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