feat: add delete post tests

This commit is contained in:
Vlad Stan 2023-02-03 15:45:23 +02:00
parent b0be06580a
commit 01764c155e
4 changed files with 111 additions and 11 deletions

View file

@ -1,11 +1,10 @@
import asyncio
import json import json
from typing import Any, Callable, List, Union from typing import Any, Callable, List
from fastapi import WebSocket from fastapi import WebSocket
from loguru import logger from loguru import logger
from .crud import create_event, get_events from .crud import create_event, delete_events, get_events
from .models import NostrEvent, NostrEventType, NostrFilter from .models import NostrEvent, NostrEventType, NostrFilter
@ -23,8 +22,7 @@ class NostrClientManager:
async def broadcast_event(self, source: "NostrClientConnection", event: NostrEvent): async def broadcast_event(self, source: "NostrClientConnection", event: NostrEvent):
for client in self.clients: for client in self.clients:
if client != source: if client != source:
sent = await client.notify_event(event) await client.notify_event(event)
print("### sent", sent, event.id)
class NostrClientConnection: class NostrClientConnection:
@ -38,13 +36,12 @@ class NostrClientConnection:
await self.websocket.accept() await self.websocket.accept()
while True: while True:
json_data = await self.websocket.receive_text() json_data = await self.websocket.receive_text()
print('### received', json_data) print('### received: ', json_data)
try: try:
data = json.loads(json_data) data = json.loads(json_data)
resp = await self.__handle_message(data) resp = await self.__handle_message(data)
for r in resp: for r in resp:
print('### sent query', json.dumps(r))
await self.websocket.send_text(json.dumps(r)) await self.websocket.send_text(json.dumps(r))
except Exception as e: except Exception as e:
logger.warning(e) logger.warning(e)
@ -53,7 +50,6 @@ class NostrClientConnection:
for filter in self.filters: for filter in self.filters:
if filter.matches(event): if filter.matches(event):
resp = event.serialize_response(filter.subscription_id) resp = event.serialize_response(filter.subscription_id)
print('### sent notify', json.dumps(resp))
await self.websocket.send_text(json.dumps(resp)) await self.websocket.send_text(json.dumps(resp))
return True return True
return False return False
@ -76,18 +72,28 @@ class NostrClientConnection:
return [] return []
async def __handle_event(self, e: "NostrEvent"): async def __handle_event(self, e: NostrEvent):
resp_nip20: List[Any] = ["ok", e.id] resp_nip20: List[Any] = ["ok", e.id]
try: try:
e.check_signature() e.check_signature()
await create_event("111", e) await create_event("111", e)
await self.broadcast_event(self, e) await self.broadcast_event(self, e)
if e.is_delete_event():
await self.__delete_event(e)
resp_nip20 += [True, ""] resp_nip20 += [True, ""]
except Exception as ex: except Exception as ex:
resp_nip20 += [False, f"error: failed to create event"] resp_nip20 += [False, f"error: failed to create event"]
await self.websocket.send_text(json.dumps(resp_nip20)) await self.websocket.send_text(json.dumps(resp_nip20))
async def __delete_event(self, event: NostrEvent):
# NIP 09
filter = NostrFilter(authors=[event.pubkey])
filter.ids = [t[1] for t in event.tags if t[0] == "e"]
events_to_delete = await get_events("111", filter, False)
ids = [e.id for e in events_to_delete]
await delete_events("111", ids)
async def __handle_request(self, subscription_id: str, filter: NostrFilter) -> List: async def __handle_request(self, subscription_id: str, filter: NostrFilter) -> List:
filter.subscription_id = subscription_id filter.subscription_id = subscription_id
self.remove_filter(subscription_id) self.remove_filter(subscription_id)

View file

@ -48,6 +48,9 @@ class NostrEvent(BaseModel):
id = hashlib.sha256(data.encode()).hexdigest() id = hashlib.sha256(data.encode()).hexdigest()
return id return id
def is_delete_event(self) -> bool:
return self.kind == 5
def check_signature(self): def check_signature(self):
event_id = self.event_id event_id = self.event_id
if self.id != event_id: if self.id != event_id:

View file

@ -98,6 +98,38 @@
"28c96b6e80681c18a690e0e0dc6ca4e72b9d291d1d2576bc8949a07bb4bee225", "28c96b6e80681c18a690e0e0dc6ca4e72b9d291d1d2576bc8949a07bb4bee225",
true, true,
"" ""
],
"delete_post01": [
"EVENT",
{
"kind": 5,
"content": "deleted",
"tags": [
[
"e",
"05741bda9079cdf66f3be977a4d31287366470d1337b1aeb09506da4fbf7cd85"
],
[
"e",
"mock-id",
""
],
[
"e",
"bb34749ffd3eb0e393e54cc90b61a7dd5f34108d4931467861d20281c0b7daea"
]
],
"created_at": 1675427798,
"pubkey": "0b29ecc73ba400e5b4bd1e4cb0d8f524e9958345749197ca21c8da38d0622816",
"id": "2751f2ee0f894268c61300c5b1a1a434f49a33a467a6f4516f10a82a1848f093",
"sig": "8e972ba7f1ce9d11ba5d49fdd48db4a92ea999790eb604e6a7f01868a26a70a8e96e1f9e104d8f77a5aa7f29e94119e33117b4cc8a5ff9e50ec8c23eeccd94e9"
}
],
"delete_post01_response": [
"ok",
"2751f2ee0f894268c61300c5b1a1a434f49a33a467a6f4516f10a82a1848f093",
true,
""
] ]
}, },
"bob": { "bob": {
@ -254,6 +286,19 @@
], ],
"limit": 400 "limit": 400
} }
],
"subscribe_to_delete_from_alice": [
"REQ",
"notifications:delete",
{
"kinds": [
5
],
"authors": [
"0b29ecc73ba400e5b4bd1e4cb0d8f524e9958345749197ca21c8da38d0622816"
],
"limit": 400
}
] ]
} }
} }

View file

@ -1,5 +1,5 @@
import asyncio import asyncio
from json import dumps from json import dumps, loads
import pytest import pytest
from fastapi import WebSocket from fastapi import WebSocket
@ -56,6 +56,9 @@ async def test_alice_and_bob():
await alice_writes_to_bob(ws_alice, ws_bob) await alice_writes_to_bob(ws_alice, ws_bob)
await alice_deletes_post01__bob_is_notified(ws_alice, ws_bob)
def init_clients(): def init_clients():
client_manager = NostrClientManager() client_manager = NostrClientManager()
@ -230,7 +233,7 @@ async def bob_writes_to_alice(ws_alice: MockWebSocket, ws_bob: MockWebSocket):
), "Alice: Wrong direct message received" ), "Alice: Wrong direct message received"
async def alice_writes_to_bob(ws_alice, ws_bob): async def alice_writes_to_bob(ws_alice: MockWebSocket, ws_bob: MockWebSocket):
ws_alice.sent_messages.clear() ws_alice.sent_messages.clear()
ws_bob.sent_messages.clear() ws_bob.sent_messages.clear()
@ -262,3 +265,46 @@ async def alice_writes_to_bob(ws_alice, ws_bob):
assert ws_bob.sent_messages[1] == dumps( assert ws_bob.sent_messages[1] == dumps(
["EOSE", "notifications:d685447c43c7c18dbbea61923cf0b63e1ab46bed"] ["EOSE", "notifications:d685447c43c7c18dbbea61923cf0b63e1ab46bed"]
), "Bob: Received all stored events" ), "Bob: Received all stored events"
async def alice_deletes_post01__bob_is_notified(ws_alice: MockWebSocket, ws_bob:MockWebSocket):
ws_bob.sent_messages.clear()
await ws_bob.wire_mock_data(bob["request_posts_alice"])
await asyncio.sleep(0.1)
assert (
len(ws_bob.sent_messages) == 3
), "Bob: Expected two posts from Alice plus and EOSE"
ws_alice.sent_messages.clear()
ws_bob.sent_messages.clear()
await ws_bob.wire_mock_data(bob["subscribe_to_delete_from_alice"])
await asyncio.sleep(0.1)
await ws_alice.wire_mock_data(alice["delete_post01"])
await asyncio.sleep(0.1)
assert (
len(ws_alice.sent_messages) == 1
), "Alice: Expected confirmation for delete post01"
assert ws_alice.sent_messages[0] == dumps(
alice["delete_post01_response"]
), "Alice: Wrong confirmation for delete post01"
assert len(ws_bob.sent_messages) == 2, "Bob: Expects 2 messages for delete post01"
assert ws_bob.sent_messages[0] == dumps(
["EOSE", "notifications:delete"]
), "Bob: Expect no delete notification on subscribe"
assert loads(ws_bob.sent_messages[1]) == [
"EVENT",
"notifications:delete",
alice["delete_post01"][1],
], "Bob: Expect delete notification later on"
ws_bob.sent_messages.clear()
await ws_bob.wire_mock_data(bob["request_posts_alice"])
await asyncio.sleep(0.1)
assert (
len(ws_bob.sent_messages) == 2
), "Bob: Expected one posts from Alice plus and EOSE"