149 lines
5.3 KiB
Python
149 lines
5.3 KiB
Python
import pytest
|
|
|
|
from traderai.memory import MemoryStore
|
|
from traderai.negotiations import NegotiationSyncService, extract_negotiation_hash
|
|
from traderai.tools import ToolRegistry
|
|
|
|
|
|
class FakeNegotiationUEX:
|
|
def __init__(self):
|
|
self.list_calls = []
|
|
self.message_calls = []
|
|
self.posts = []
|
|
|
|
async def list_negotiations(self, id=None, id_listing=None, hash=None):
|
|
self.list_calls.append({"id": id, "id_listing": id_listing, "hash": hash})
|
|
data = [
|
|
{
|
|
"id": 11,
|
|
"hash": "open-hash",
|
|
"id_listing": 101,
|
|
"listing_slug": "rgl-open",
|
|
"listing_title": "RGL Set",
|
|
"advertiser_username": "seller_a",
|
|
"client_username": "pilot_hudson",
|
|
"date_modified": 1_780_975_053,
|
|
},
|
|
{
|
|
"id": 12,
|
|
"hash": "closed-recent",
|
|
"id_listing": 102,
|
|
"listing_slug": "rgl-closed",
|
|
"listing_title": "Closed Deal",
|
|
"advertiser_username": "seller_b",
|
|
"client_username": "pilot_hudson",
|
|
"date_modified": 1_780_975_053,
|
|
"date_closed": 1_780_975_054,
|
|
},
|
|
]
|
|
if hash:
|
|
data = [item for item in data if item["hash"] == hash]
|
|
return {"status": "ok", "negotiations": data}
|
|
|
|
async def get_negotiation_messages(self, hash=None, id_negotiation=None):
|
|
self.message_calls.append({"hash": hash, "id_negotiation": id_negotiation})
|
|
return {
|
|
"status": "ok",
|
|
"messages": [
|
|
{
|
|
"id": 201,
|
|
"negotiation_hash": hash,
|
|
"user_username": "seller_a" if hash == "open-hash" else "seller_b",
|
|
"user_name": "Seller",
|
|
"message": "Still available.",
|
|
"date_added": 1_780_975_053,
|
|
}
|
|
],
|
|
}
|
|
|
|
async def send_negotiation_message(self, **payload):
|
|
self.posts.append({"kind": "message", **payload})
|
|
return {"status": "ok", "posted": self.posts[-1]}
|
|
|
|
async def close_negotiation(self, **payload):
|
|
self.posts.append({"kind": "close", **payload})
|
|
return {"status": "ok", "posted": self.posts[-1]}
|
|
|
|
|
|
def test_extract_negotiation_hash_handles_uex_redirects():
|
|
assert extract_negotiation_hash("https://uexcorp.space/marketplace/negotiate/hash/abc-123") == "abc-123"
|
|
assert extract_negotiation_hash("/marketplace/negotiate/hash/def-456") == "def-456"
|
|
assert extract_negotiation_hash("/marketplace/item/info/foo") is None
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_startup_sync_keeps_open_and_recent_threads(tmp_path):
|
|
memory = MemoryStore(str(tmp_path / "memory.sqlite3"))
|
|
memory.set_profile("uex_user", {"username": "pilot_hudson"})
|
|
service = NegotiationSyncService(memory, FakeNegotiationUEX())
|
|
|
|
result = await service.startup_sync()
|
|
negotiations = memory.list_negotiations(limit=10)
|
|
|
|
assert result["count"] == 2
|
|
assert {item["hash"] for item in negotiations} == {"open-hash", "closed-recent"}
|
|
detail = memory.get_negotiation("open-hash")
|
|
assert detail["messages"][0]["body"] == "Still available."
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_notification_refresh_targets_only_changed_negotiation(tmp_path):
|
|
memory = MemoryStore(str(tmp_path / "memory.sqlite3"))
|
|
memory.set_profile("uex_user", {"username": "pilot_hudson"})
|
|
fake = FakeNegotiationUEX()
|
|
service = NegotiationSyncService(memory, fake)
|
|
await service.startup_sync()
|
|
fake.message_calls.clear()
|
|
|
|
await service.handle_notifications(
|
|
[
|
|
{
|
|
"id": 99,
|
|
"message": "seller_a: ping",
|
|
"redir": "https://uexcorp.space/marketplace/negotiate/hash/open-hash",
|
|
"date_added": 1_780_975_060,
|
|
"date_read": 0,
|
|
}
|
|
]
|
|
)
|
|
|
|
assert fake.message_calls == [{"hash": "open-hash", "id_negotiation": None}]
|
|
assert memory.get_negotiation("open-hash")["unread_count"] == 1
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_manual_send_refreshes_local_thread(tmp_path):
|
|
memory = MemoryStore(str(tmp_path / "memory.sqlite3"))
|
|
memory.set_profile("uex_user", {"username": "pilot_hudson"})
|
|
fake = FakeNegotiationUEX()
|
|
service = NegotiationSyncService(memory, fake)
|
|
await service.startup_sync()
|
|
|
|
result = await service.manual_send_message("open-hash", "I can buy tonight.")
|
|
|
|
assert result["posted"]["kind"] == "message"
|
|
assert fake.message_calls[-1]["hash"] == "open-hash"
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_draft_negotiation_close_creates_pending_action(tmp_path):
|
|
memory = MemoryStore(str(tmp_path / "memory.sqlite3"))
|
|
registry = ToolRegistry(FakeNegotiationUEX(), memory=memory)
|
|
|
|
result = await registry.draft_negotiation_close(
|
|
hash="open-hash",
|
|
deal_closed=True,
|
|
deal_value=1_000_000,
|
|
currency="UEC",
|
|
clarity_rating=5,
|
|
speed_rating=5,
|
|
respect_rating=5,
|
|
fairness_rating=4,
|
|
comment="Smooth trade",
|
|
)
|
|
|
|
pending = result["pending_action"]
|
|
assert pending["endpoint"] == "marketplace_negotiations_close"
|
|
assert pending["payload"]["deal_closed"] == 1
|
|
assert pending["payload"]["is_production"] == 1
|