feat: add delete post tests
This commit is contained in:
parent
b0be06580a
commit
01764c155e
4 changed files with 111 additions and 11 deletions
|
|
@ -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)
|
||||||
|
|
|
||||||
|
|
@ -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:
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -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"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue