import asyncio from memory_system_api.clients import EverOSMemorySystemClient, OpenVikingMemorySystemClient class FakeStore: def __init__(self): self.users = {} self.accounts = {} self.sessions = [] self.tasks = [] def get_user_key(self, user_id: str) -> str | None: return self.users.get(user_id) def save_user_key(self, user_id: str, user_key: str, account_id: str = "admin") -> None: self.users[user_id] = user_key def get_account_key(self, account_id: str) -> str | None: return self.accounts.get(account_id) def save_account_key(self, account_id: str, admin_user_id: str, account_key: str) -> None: self.accounts[account_id] = account_key def account_key_matches(self, account_id: str, account_key: str) -> bool: return self.accounts.get(account_id) == account_key def user_key_matches(self, user_id: str, user_key: str) -> bool: return self.users.get(user_id) == user_key def save_session(self, user_id: str, session_id: str) -> None: self.sessions.append((user_id, session_id)) def save_task(self, user_id: str, session_id: str, task_id: str, archive_uri: str | None) -> None: self.tasks.append((user_id, session_id, task_id, archive_uri)) class FakeResponse: def __init__(self, status_code: int, data: dict): self.status_code = status_code self._data = data def json(self) -> dict: return self._data def raise_for_status(self) -> None: if self.status_code >= 400: raise AssertionError(f"unexpected status {self.status_code}") class FakeAsyncClient: def __init__(self, calls: list, responses: list[FakeResponse], api_key: str, headers: dict): self.calls = calls self.responses = responses self.api_key = api_key self.headers = headers async def __aenter__(self): return self async def __aexit__(self, exc_type, exc, tb): return False async def post(self, path: str, json: dict | None = None, files: dict | None = None) -> FakeResponse: if files and "file" in files: uploaded = files["file"] files = {"file": uploaded[0] if isinstance(uploaded, tuple) else uploaded} self.calls.append(("post", self.api_key, self.headers, path, json, files)) return self.responses.pop(0) async def get(self, path: str, params: dict | None = None) -> FakeResponse: self.calls.append(("get", self.api_key, self.headers, path, params)) return self.responses.pop(0) async def delete(self, path: str, params: dict | None = None) -> FakeResponse: self.calls.append(("delete", self.api_key, self.headers, path, params)) return self.responses.pop(0) def test_openviking_rejects_unknown_user_credentials(): store = FakeStore() client = OpenVikingMemorySystemClient(store=store) try: client.credential_for_user("tom", "missing-key") except PermissionError as exc: assert "Invalid user key" in str(exc) else: raise AssertionError("expected PermissionError") def test_openviking_accepts_matching_user_credentials(): store = FakeStore() store.save_user_key("tom", "tom-key") client = OpenVikingMemorySystemClient(store=store) client.root_key = "root-key" credential = client.credential_for_user("tom", "tom-key", agent_id="sess-1") assert credential.api_key == "tom-key" assert credential.account_id == "tom_account" assert credential.user_id == "tom" assert credential.agent_id == "sess-1" def test_openviking_client_uses_x_api_key_for_user_keys(): client = OpenVikingMemorySystemClient(store=FakeStore()) client.root_key = "root-key" http_client = client._client("tom-key") try: assert http_client.headers["X-API-Key"] == "tom-key" assert "Authorization" not in http_client.headers finally: asyncio.run(http_client.aclose()) def test_openviking_create_user_creates_isolated_admin_account(): store = FakeStore() client = OpenVikingMemorySystemClient(store=store) client.root_key = "root-key" calls = [] responses = [ FakeResponse( 200, { "status": "ok", "result": { "account_id": "userA_account", "admin_user_id": "userA", "user_key": "userA-key", }, }, ), ] client._client = lambda api_key, extra_headers=None, json_content_type=True: FakeAsyncClient( # type: ignore[method-assign] calls, responses, api_key, extra_headers or {}, ) result = asyncio.run(client.create_user("userA")) assert result == { "status": "ok", "result": { "account_id": "userA_account", "admin_user_id": "userA", "user_key": "userA-key", }, } assert store.accounts == {"userA_account": "userA-key"} assert store.users == {"userA": "userA-key"} assert calls == [ ( "post", "root-key", {}, "/api/v1/admin/accounts", {"account_id": "userA_account", "admin_user_id": "userA"}, None, ), ] def test_openviking_create_user_creates_account_even_when_admin_workspace_exists(): store = FakeStore() store.save_account_key("admin", "admin", "admin-key") client = OpenVikingMemorySystemClient(store=store) client.root_key = "root-key" calls = [] responses = [ FakeResponse( 200, { "status": "ok", "result": { "account_id": "userB_account", "admin_user_id": "userB", "user_key": "userB-key", }, }, ) ] client._client = lambda api_key, extra_headers=None, json_content_type=True: FakeAsyncClient( # type: ignore[method-assign] calls, responses, api_key, extra_headers or {}, ) result = asyncio.run(client.create_user("userB")) assert result == { "status": "ok", "result": { "account_id": "userB_account", "admin_user_id": "userB", "user_key": "userB-key", }, } assert store.accounts == {"admin": "admin-key", "userB_account": "userB-key"} assert store.users == {"userB": "userB-key"} assert calls == [ ( "post", "root-key", {}, "/api/v1/admin/accounts", {"account_id": "userB_account", "admin_user_id": "userB"}, None, ) ] def test_openviking_user_key_auth_is_used_for_session_create(): client = OpenVikingMemorySystemClient(store=FakeStore()) client.root_key = "root-key" calls = [] responses = [FakeResponse(200, {"status": "ok", "result": {"session_id": "sess-2"}})] client._client = lambda api_key, extra_headers=None, json_content_type=True: FakeAsyncClient( # type: ignore[method-assign] calls, responses, api_key, extra_headers or {}, ) credential = client.user_credential("tom-key", "tom", agent_id="sess-2") result = asyncio.run(client.ensure_session(credential, "sess-2")) assert result == {"status": "ok", "result": {"session_id": "sess-2"}} assert client.store.sessions == [("tom", "sess-2")] assert calls == [ ( "post", "tom-key", {}, "/api/v1/sessions", {"session_id": "sess-2"}, None, ) ] def test_openviking_find_uses_current_identity_memory_scope(): client = OpenVikingMemorySystemClient(store=FakeStore()) calls = [] responses = [FakeResponse(200, {"status": "ok", "result": {"memories": []}})] client._client = lambda api_key, extra_headers=None, json_content_type=True: FakeAsyncClient( # type: ignore[method-assign] calls, responses, api_key, extra_headers or {}, ) credential = client.user_credential("tom-key", "tom", agent_id="sess-1") result = asyncio.run(client.find(credential, "咖啡", 5)) assert result == {"status": "ok", "result": {"memories": []}} assert calls == [ ( "post", "tom-key", {}, "/api/v1/search/find", {"query": "咖啡", "target_uri": "viking://user/tom/memories/", "limit": 5}, None, ) ] def test_openviking_search_uses_fixed_user_memory_target_with_level_and_score_threshold(): client = OpenVikingMemorySystemClient(store=FakeStore()) calls = [] responses = [FakeResponse(200, {"status": "ok", "result": {"memories": []}})] client._client = lambda api_key, extra_headers=None, json_content_type=True: FakeAsyncClient( # type: ignore[method-assign] calls, responses, api_key, extra_headers or {}, ) credential = client.user_credential("tom-key", "tom", agent_id="sess-1") result = asyncio.run(client.search(credential, "咖啡", 5, level=3, score_threshold=0.7)) assert result == {"status": "ok", "result": {"memories": []}} assert calls == [ ( "post", "tom-key", {}, "/api/v1/search/search", { "query": "咖啡", "target_uri": "viking://user/memories", "limit": 5, "level": 3, "score_threshold": 0.7, }, None, ) ] def test_openviking_search_accepts_custom_target_uri(): client = OpenVikingMemorySystemClient(store=FakeStore()) calls = [] responses = [FakeResponse(200, {"status": "ok", "result": {"memories": []}})] client._client = lambda api_key, extra_headers=None, json_content_type=True: FakeAsyncClient( # type: ignore[method-assign] calls, responses, api_key, extra_headers or {}, ) credential = client.user_credential("tom-key", "tom", agent_id="sess-1") result = asyncio.run(client.search( credential, "咖啡", 5, level=3, score_threshold=0.7, target_uri="viking://user/custom/memories", )) assert result == {"status": "ok", "result": {"memories": []}} assert calls == [ ( "post", "tom-key", {}, "/api/v1/search/search", { "query": "咖啡", "target_uri": "viking://user/custom/memories", "limit": 5, "level": 3, "score_threshold": 0.7, }, None, ) ] def test_openviking_profile_search_uses_user_memory_target_and_level(): client = OpenVikingMemorySystemClient(store=FakeStore()) calls = [] responses = [FakeResponse(200, {"status": "ok", "result": {"memories": []}})] client._client = lambda api_key, extra_headers=None, json_content_type=True: FakeAsyncClient( # type: ignore[method-assign] calls, responses, api_key, extra_headers or {}, ) credential = client.user_credential("tom-key", "tom") result = asyncio.run(client.search_profile_memories(credential, "我想喝东西", 10, 2)) assert result == {"status": "ok", "result": {"memories": []}} assert calls == [ ( "post", "tom-key", {}, "/api/v1/search/search", { "query": "我想喝东西", "limit": 10, "level": 2, "target_uri": "viking://user/memories", }, None, ) ] def test_openviking_get_session_context_uses_user_key_auth(): client = OpenVikingMemorySystemClient(store=FakeStore()) calls = [] responses = [ FakeResponse( 200, { "status": "ok", "result": { "latest_archive_overview": "# Working Memory", "messages": [], }, }, ) ] client._client = lambda api_key, extra_headers=None, json_content_type=True: FakeAsyncClient( # type: ignore[method-assign] calls, responses, api_key, extra_headers or {}, ) credential = client.user_credential("tom-key", "tom", agent_id="sess-1") result = asyncio.run(client.get_session_context(credential, "sess-1")) assert result == { "status": "ok", "result": { "latest_archive_overview": "# Working Memory", "messages": [], }, } assert calls == [ ( "get", "tom-key", {}, "/api/v1/sessions/sess-1/context", None, ) ] def test_openviking_commit_keeps_no_recent_live_messages(): client = OpenVikingMemorySystemClient(store=FakeStore()) calls = [] responses = [ FakeResponse( 200, { "status": "ok", "result": { "status": "accepted", "task_id": "task-1", "archive_uri": "viking://session/tom/sess-1/history/archive_001", }, }, ) ] client._client = lambda api_key, extra_headers=None, json_content_type=True: FakeAsyncClient( # type: ignore[method-assign] calls, responses, api_key, extra_headers or {}, ) credential = client.user_credential("tom-key", "tom") result = asyncio.run(client.commit_session(credential, "sess-1")) assert result == { "status": "ok", "result": { "status": "accepted", "task_id": "task-1", "archive_uri": "viking://session/tom/sess-1/history/archive_001", }, } assert client.store.tasks == [("tom", "sess-1", "task-1", "viking://session/tom/sess-1/history/archive_001")] assert calls == [ ( "post", "tom-key", {}, "/api/v1/sessions/sess-1/commit", {"keep_recent_count": 0}, None, ) ] def test_openviking_upload_temp_file_posts_multipart(tmp_path): path = tmp_path / "report.pdf" path.write_bytes(b"pdf-bytes") client = OpenVikingMemorySystemClient(store=FakeStore()) calls = [] responses = [ FakeResponse( 200, {"status": "ok", "result": {"temp_file_id": "upload_report.pdf"}}, ) ] client._client = lambda api_key, extra_headers=None, json_content_type=True: FakeAsyncClient( # type: ignore[method-assign] calls, responses, api_key, extra_headers or {}, ) credential = client.user_credential("tom-key", "tom") result = asyncio.run(client.upload_temp_file(credential, path)) assert result == {"status": "ok", "result": {"temp_file_id": "upload_report.pdf"}} assert calls == [ ( "post", "tom-key", {}, "/api/v1/resources/temp_upload", None, {"file": "report.pdf"}, ) ] def test_openviking_add_resource_posts_url_payload(): client = OpenVikingMemorySystemClient(store=FakeStore()) calls = [] responses = [FakeResponse(200, {"status": "ok", "result": {"uri": "viking://resources/tom/images/photo.png"}})] client._client = lambda api_key, extra_headers=None, json_content_type=True: FakeAsyncClient( # type: ignore[method-assign] calls, responses, api_key, extra_headers or {}, ) credential = client.user_credential("tom-key", "tom") result = asyncio.run( client.add_resource( credential, path="https://example.com/photo.png", to="viking://resources/tom/images/photo.png", reason="上传远程图片", wait=True, directly_upload_media=True, ) ) assert result == {"status": "ok", "result": {"uri": "viking://resources/tom/images/photo.png"}} assert calls == [ ( "post", "tom-key", {}, "/api/v1/resources", { "path": "https://example.com/photo.png", "to": "viking://resources/tom/images/photo.png", "reason": "上传远程图片", "wait": True, "directly_upload_media": True, }, None, ) ] def test_openviking_delete_resource_sends_uri_and_recursive_flag(): client = OpenVikingMemorySystemClient(store=FakeStore()) calls = [] responses = [FakeResponse(200, {"status": "ok", "result": {"estimated_deleted_count": 4}})] client._client = lambda api_key, extra_headers=None, json_content_type=True: FakeAsyncClient( # type: ignore[method-assign] calls, responses, api_key, extra_headers or {}, ) credential = client.user_credential("tom-key", "tom") result = asyncio.run( client.delete_resource( credential, uri="viking://resources/tom/files/report.pdf", recursive=True, ) ) assert result == {"status": "ok", "result": {"estimated_deleted_count": 4}} assert calls == [ ( "delete", "tom-key", {}, "/api/v1/fs", {"uri": "viking://resources/tom/files/report.pdf", "recursive": "true"}, ) ] def test_openviking_list_memories_calls_fs_ls_with_recursive_flag(): client = OpenVikingMemorySystemClient(store=FakeStore()) calls = [] responses = [FakeResponse(200, {"status": "ok", "result": {"children": []}})] client._client = lambda api_key, extra_headers=None, json_content_type=True: FakeAsyncClient( # type: ignore[method-assign] calls, responses, api_key, extra_headers or {}, ) credential = client.user_credential("tom-key", "tom") result = asyncio.run( client.list_memories( credential, uri="viking://user/memories", recursive=True, ) ) assert result == {"status": "ok", "result": {"children": []}} assert calls == [ ( "get", "tom-key", {}, "/api/v1/fs/ls", {"uri": "viking://user/memories", "recursive": "true"}, ) ] def test_openviking_read_memory_calls_content_read(): client = OpenVikingMemorySystemClient(store=FakeStore()) calls = [] responses = [FakeResponse(200, {"status": "ok", "result": {"content": "# Python"}})] client._client = lambda api_key, extra_headers=None, json_content_type=True: FakeAsyncClient( # type: ignore[method-assign] calls, responses, api_key, extra_headers or {}, ) credential = client.user_credential("tom-key", "tom") result = asyncio.run(client.read_memory(credential, "viking://user/memories/preferences/python.md")) assert result == {"status": "ok", "result": {"content": "# Python"}} assert calls == [ ( "get", "tom-key", {}, "/api/v1/content/read", {"uri": "viking://user/memories/preferences/python.md"}, ) ] def test_openviking_write_memory_posts_content_write_mode_and_wait(): client = OpenVikingMemorySystemClient(store=FakeStore()) calls = [] responses = [FakeResponse(200, {"status": "ok", "result": {"uri": "viking://user/memories/profile.md"}})] client._client = lambda api_key, extra_headers=None, json_content_type=True: FakeAsyncClient( # type: ignore[method-assign] calls, responses, api_key, extra_headers or {}, ) credential = client.user_credential("tom-key", "tom") result = asyncio.run( client.write_memory( credential, uri="viking://user/memories/profile.md", content="# Profile\n\nLikes Python.", mode="replace", wait=True, ) ) assert result == {"status": "ok", "result": {"uri": "viking://user/memories/profile.md"}} assert calls == [ ( "post", "tom-key", {}, "/api/v1/content/write", { "uri": "viking://user/memories/profile.md", "content": "# Profile\n\nLikes Python.", "mode": "replace", "wait": True, }, None, ) ] def test_openviking_delete_memory_defaults_non_recursive(): client = OpenVikingMemorySystemClient(store=FakeStore()) calls = [] responses = [FakeResponse(200, {"status": "ok", "result": {"estimated_deleted_count": 1}})] client._client = lambda api_key, extra_headers=None, json_content_type=True: FakeAsyncClient( # type: ignore[method-assign] calls, responses, api_key, extra_headers or {}, ) credential = client.user_credential("tom-key", "tom") result = asyncio.run( client.delete_memory( credential, uri="viking://user/memories/preferences/python.md", ) ) assert result == {"status": "ok", "result": {"estimated_deleted_count": 1}} assert calls == [ ( "delete", "tom-key", {}, "/api/v1/fs", {"uri": "viking://user/memories/preferences/python.md", "recursive": "false"}, ) ] def test_everos_assistant_payload_does_not_use_user_id_as_sender(): client = EverOSMemorySystemClient() payload = client.build_message_payload( user_id="tom", session_id="sess-1", role="assistant", content="我记住了", ) message = payload["messages"][0] assert message["role"] == "assistant" assert message["sender_id"] != "tom" assert message["sender_name"] != "tom" def test_everos_user_payload_uses_user_id_as_sender(): client = EverOSMemorySystemClient() payload = client.build_message_payload( user_id="tom", session_id="sess-1", role="user", content="我喜欢拿铁", ) message = payload["messages"][0] assert message["role"] == "user" assert message["sender_id"] == "tom" assert message["sender_name"] == "tom" def test_everos_append_message_posts_current_memory_add_contract(): client = EverOSMemorySystemClient() calls = [] responses = [FakeResponse(200, {"request_id": "req-1", "data": {"status": "accumulated", "message_count": 1}})] client._client = lambda: FakeAsyncClient( # type: ignore[method-assign] calls, responses, client.api_key or "", {}, ) result = asyncio.run(client.append_message("tom", "sess-1", "user", "我喜欢拿铁")) assert result == {"request_id": "req-1", "data": {"status": "accumulated", "message_count": 1}} assert calls[0][0] == "post" assert calls[0][3] == "/api/v1/memory/add" payload = calls[0][4] assert payload["session_id"] == "sess-1" assert "user_id" not in payload assert payload["messages"][0]["sender_id"] == "tom" assert payload["messages"][0]["role"] == "user" assert payload["messages"][0]["content"] == "我喜欢拿铁" def test_everos_flush_posts_current_memory_flush_contract(): client = EverOSMemorySystemClient() calls = [] responses = [FakeResponse(200, {"request_id": "req-1", "data": {"status": "extracted"}})] client._client = lambda: FakeAsyncClient( # type: ignore[method-assign] calls, responses, client.api_key or "", {}, ) result = asyncio.run(client.flush("tom", "sess-1")) assert result == {"request_id": "req-1", "data": {"status": "extracted"}} assert calls == [ ( "post", client.api_key or "", {}, "/api/v1/memory/flush", {"session_id": "sess-1"}, None, ) ] def test_everos_search_posts_current_memory_search_contract(): client = EverOSMemorySystemClient() calls = [] responses = [FakeResponse(200, {"request_id": "req-1", "data": {"episodes": []}})] client._client = lambda: FakeAsyncClient( # type: ignore[method-assign] calls, responses, client.api_key or "", {}, ) result = asyncio.run(client.search("tom", "sess-1", "牛奶在哪里", "hybrid", 7)) assert result == {"request_id": "req-1", "data": {"episodes": []}} assert calls == [ ( "post", client.api_key or "", {}, "/api/v1/memory/search", { "user_id": "tom", "query": "牛奶在哪里", "method": "hybrid", "top_k": 7, "include_profile": True, "filters": {"session_id": "sess-1"}, }, None, ) ] def test_everos_get_profile_posts_current_memory_get_contract(): client = EverOSMemorySystemClient() calls = [] responses = [FakeResponse(200, {"request_id": "req-1", "data": {"profiles": []}})] client._client = lambda: FakeAsyncClient( # type: ignore[method-assign] calls, responses, client.api_key or "", {}, ) result = asyncio.run(client.get_profile("tom")) assert result == {"request_id": "req-1", "data": {"profiles": []}} assert calls == [ ( "post", client.api_key or "", {}, "/api/v1/memory/get", { "user_id": "tom", "memory_type": "profile", "page": 1, "page_size": 20, }, None, ) ]