from __future__ import annotations import pytest from adapter.matrix.store import ( STAGED_ATTACHMENTS_PREFIX, add_staged_attachment, clear_pending_confirm, clear_staged_attachments, get_pending_confirm, get_platform_chat_id, get_room_meta, get_room_state, get_skills_message_id, get_staged_attachments, get_user_meta, next_chat_id, next_platform_chat_id, remove_staged_attachment_at, set_pending_confirm, set_platform_chat_id, set_room_meta, set_room_state, set_skills_message_id, set_user_meta, ) from core.store import InMemoryStore @pytest.fixture def store() -> InMemoryStore: return InMemoryStore() async def test_room_meta_roundtrip(store: InMemoryStore): meta = { "room_type": "chat", "chat_id": "C1", "display_name": "Чат 1", "matrix_user_id": "@alice:m.org", } await set_room_meta(store, "!r:m.org", meta) assert await get_room_meta(store, "!r:m.org") == meta async def test_room_meta_roundtrip_with_platform_chat_id(store: InMemoryStore): meta = { "chat_id": "C1", "matrix_user_id": "@alice:example.org", "platform_chat_id": "chat-platform-1", } await set_room_meta(store, "!r:m.org", meta) saved = await get_room_meta(store, "!r:m.org") assert saved is not None assert saved["platform_chat_id"] == "chat-platform-1" async def test_platform_chat_id_helpers_roundtrip(store: InMemoryStore): meta = { "chat_id": "C1", "matrix_user_id": "@alice:example.org", "display_name": "Research", } await set_room_meta(store, "!r:m.org", meta) await set_platform_chat_id(store, "!r:m.org", "chat-platform-1") assert await get_platform_chat_id(store, "!r:m.org") == "chat-platform-1" assert await get_room_meta(store, "!r:m.org") == { "chat_id": "C1", "matrix_user_id": "@alice:example.org", "display_name": "Research", "platform_chat_id": "chat-platform-1", } async def test_room_meta_missing(store: InMemoryStore): assert await get_room_meta(store, "!nonexistent:m.org") is None async def test_user_meta_roundtrip(store: InMemoryStore): meta = { "platform_user_id": "usr-1", "display_name": "Alice", "space_id": None, "settings_room_id": None, "next_chat_index": 1, } await set_user_meta(store, "@alice:m.org", meta) assert await get_user_meta(store, "@alice:m.org") == meta async def test_room_state_roundtrip(store: InMemoryStore): await set_room_state(store, "!r:m.org", "idle") assert await get_room_state(store, "!r:m.org") == "idle" await set_room_state(store, "!r:m.org", "waiting_response") assert await get_room_state(store, "!r:m.org") == "waiting_response" async def test_room_state_default_idle(store: InMemoryStore): assert await get_room_state(store, "!unknown:m.org") == "idle" async def test_next_chat_id_increments(store: InMemoryStore): uid = "@alice:m.org" await set_user_meta(store, uid, {"next_chat_index": 1}) assert await next_chat_id(store, uid) == "C1" assert await next_chat_id(store, uid) == "C2" assert await next_chat_id(store, uid) == "C3" async def test_next_platform_chat_id_increments(store: InMemoryStore): assert await next_platform_chat_id(store) == "1" assert await next_platform_chat_id(store) == "2" assert await next_platform_chat_id(store) == "3" async def test_skills_message_roundtrip(store: InMemoryStore): await set_skills_message_id(store, "!room", "$event") assert await get_skills_message_id(store, "!room") == "$event" async def test_pending_confirm_roundtrip(store: InMemoryStore): assert await get_pending_confirm(store, "!room:m.org") is None meta = {"action_id": "test", "description": "Do thing"} await set_pending_confirm(store, "!room:m.org", meta) assert await get_pending_confirm(store, "!room:m.org") == meta await clear_pending_confirm(store, "!room:m.org") assert await get_pending_confirm(store, "!room:m.org") is None async def test_staged_attachments_roundtrip(store: InMemoryStore): room_id = "!room:m.org" user_id = "@alice:m.org" assert await get_staged_attachments(store, room_id, user_id) == [] first = {"id": "att-1", "name": "screenshot.png"} second = {"id": "att-2", "name": "invoice.pdf"} await add_staged_attachment(store, room_id, user_id, first) await add_staged_attachment(store, room_id, user_id, second) assert await get_staged_attachments(store, room_id, user_id) == [ first, second, ] @pytest.mark.parametrize( "stored_value", [ None, "not-a-dict", [], 123, ], ) async def test_staged_attachments_invalid_container_state_returns_empty_list( store: InMemoryStore, stored_value, ): room_id = "!room:m.org" user_id = "@alice:m.org" await store.set(f"{STAGED_ATTACHMENTS_PREFIX}{room_id}:{user_id}", stored_value) assert await get_staged_attachments(store, room_id, user_id) == [] async def test_staged_attachments_filters_invalid_entries(store: InMemoryStore): room_id = "!room:m.org" user_id = "@alice:m.org" valid_one = {"id": "att-1", "name": "alpha.png"} valid_two = {"id": "att-2", "name": "beta.pdf"} await store.set( f"{STAGED_ATTACHMENTS_PREFIX}{room_id}:{user_id}", { "attachments": [ valid_one, "bad-entry", None, {"id": "ignored"}, valid_two, ] }, ) assert await get_staged_attachments(store, room_id, user_id) == [ valid_one, {"id": "ignored"}, valid_two, ] async def test_staged_attachments_are_scoped_by_room_and_user(store: InMemoryStore): room_a = "!room-a:m.org" room_b = "!room-b:m.org" user_a = "@alice:m.org" user_b = "@bob:m.org" attachment_a = {"id": "att-a", "name": "alpha.png"} attachment_b = {"id": "att-b", "name": "beta.png"} attachment_c = {"id": "att-c", "name": "gamma.png"} await add_staged_attachment(store, room_a, user_a, attachment_a) await add_staged_attachment(store, room_a, user_b, attachment_b) await add_staged_attachment(store, room_b, user_a, attachment_c) assert await get_staged_attachments(store, room_a, user_a) == [attachment_a] assert await get_staged_attachments(store, room_a, user_b) == [attachment_b] assert await get_staged_attachments(store, room_b, user_a) == [attachment_c] assert await get_staged_attachments(store, room_b, user_b) == [] async def test_remove_staged_attachment_at_by_zero_based_index( store: InMemoryStore, ): room_id = "!room:m.org" user_id = "@alice:m.org" first = {"id": "att-1", "name": "first.png"} second = {"id": "att-2", "name": "second.png"} third = {"id": "att-3", "name": "third.png"} await add_staged_attachment(store, room_id, user_id, first) await add_staged_attachment(store, room_id, user_id, second) await add_staged_attachment(store, room_id, user_id, third) assert await remove_staged_attachment_at(store, room_id, user_id, 1) == second assert await get_staged_attachments(store, room_id, user_id) == [first, third] assert await remove_staged_attachment_at(store, room_id, user_id, 99) is None assert await remove_staged_attachment_at(store, room_id, user_id, -1) is None async def test_clear_staged_attachments(store: InMemoryStore): room_id = "!room:m.org" user_id = "@alice:m.org" await add_staged_attachment(store, room_id, user_id, {"id": "att-1"}) await add_staged_attachment(store, room_id, user_id, {"id": "att-2"}) await clear_staged_attachments(store, room_id, user_id) assert await get_staged_attachments(store, room_id, user_id) == []