diff options
| author | tanaka takashi <isazaka@gmail.com> | 2026-07-17 12:15:41 +0900 |
|---|---|---|
| committer | tanaka takashi <isazaka@gmail.com> | 2026-07-17 12:15:41 +0900 |
| commit | d57577637c1c1a16176d63d5a89e9eec3c1e4d7c (patch) | |
| tree | 55087498ab17601845446174a2f41f2490a53820 /tests/mcp_protocol_test.py | |
| parent | dda043b0ec9d8d9e5da7fe51fc48791086417a9b (diff) | |
fix: sessions paginationとhealth診断を改善
- sessions cursorのエンコード不具合と長いsession IDのバッファ問題を修正
- response budgetに基づくsessionsページングを追加
- get_system_healthにsession_countを追加
- metadata_jsonのフィールド別エラー診断を追加
- CRUD互換性とDebug/Release/ASanテストを確認
Diffstat (limited to 'tests/mcp_protocol_test.py')
| -rw-r--r-- | tests/mcp_protocol_test.py | 168 |
1 files changed, 167 insertions, 1 deletions
diff --git a/tests/mcp_protocol_test.py b/tests/mcp_protocol_test.py index f5a5c86..c206515 100644 --- a/tests/mcp_protocol_test.py +++ b/tests/mcp_protocol_test.py @@ -109,6 +109,7 @@ def main() -> None: data_dir = Path(tempfile.mkdtemp(prefix="tiny-convmem-p0-")) server = Server(args.server.resolve(), data_dir) next_id = 1 + expected_session_count = 0 try: response = server.send( request( @@ -200,6 +201,9 @@ def main() -> None: assert tools["search_messages"]["inputSchema"]["properties"]["limit"]["type"] == "integer" assert tools["delete_message"]["inputSchema"]["required"] == ["message_id"] assert tools["update_message"]["inputSchema"]["required"] == ["message_id"] + empty_health = tool_payload(tool_call(server, next_id, "get_system_health", {})) + next_id += 1 + assert empty_health["session_count"] == 0 message_ids: list[str] = [] for index in range(20): @@ -213,6 +217,71 @@ def main() -> None: ) next_id += 1 message_ids.append(payload["message"]["message_id"]) + + metadata_regression = tool_payload( + tool_call( + server, + next_id, + "write_message", + { + "session_id": "metadata-regression", + "role": "assistant", + "content": "metadata combination", + "metadata_json": json.dumps( + { + "tags": ["regression"], + "memory_tier": "long", + "importance": 90, + "summary": "valid metadata combination", + } + ), + }, + ) + )["message"] + next_id += 1 + assert metadata_regression["metadata"]["tags"] == ["regression"] + assert metadata_regression["metadata"]["memory_tier"] == "long" + assert metadata_regression["metadata"]["importance"] == 90 + assert metadata_regression["metadata"]["summary"] == "valid metadata combination" + + invalid_metadata_cases = [ + (json.dumps({"unexpected": True}), "unknown field"), + (json.dumps({"memory_tier": "archive"}), "memory_tier"), + (json.dumps({"importance": 101}), "importance"), + (json.dumps({"summary": "x" * 4097}), "summary"), + (json.dumps({"tags": [1]}), "tags"), + ("{", "JSON object"), + ] + for metadata_json, expected_diagnostic in invalid_metadata_cases: + write_error = tool_payload( + tool_call( + server, + next_id, + "write_message", + { + "session_id": "metadata-invalid", + "role": "user", + "content": "must not be written", + "metadata_json": metadata_json, + }, + ), + True, + ) + next_id += 1 + assert write_error["error"]["code"] == "VALIDATION_ERROR" + assert expected_diagnostic in write_error["error"]["message"] + update_error = tool_payload( + tool_call( + server, + next_id, + "update_message", + {"message_id": message_ids[0], "metadata_json": metadata_json}, + ), + True, + ) + next_id += 1 + assert update_error["error"]["code"] == "VALIDATION_ERROR" + assert expected_diagnostic in update_error["error"]["message"] updated = tool_payload( tool_call( server, @@ -273,6 +342,63 @@ def main() -> None: io_after_views = tool_payload(tool_call(server, next_id, "get_system_health", {}))["io"] next_id += 1 assert io_after_views == io_before + + paged_session_ids = ["session-page-a", "session-page-b", "s" * 256] + for session_id in paged_session_ids: + tool_payload( + tool_call( + server, + next_id, + "write_message", + {"session_id": session_id, "role": "user", "content": "session page item"}, + ) + ) + next_id += 1 + complete_sessions = tool_payload( + tool_call(server, next_id, "read_messages", {"view": "sessions", "limit": 1000}) + )["sessions"] + next_id += 1 + expected_session_ids = [item["session_id"] for item in complete_sessions] + traversed_session_ids: list[str] = [] + session_cursor: str | None = None + first_session_cursor: str | None = None + while True: + arguments: dict[str, Any] = {"view": "sessions", "limit": 1} + if session_cursor: + arguments["cursor"] = session_cursor + session_page = tool_payload( + tool_call(server, next_id, "read_messages", arguments) + ) + next_id += 1 + assert len(session_page["sessions"]) == 1 + traversed_session_ids.append(session_page["sessions"][0]["session_id"]) + session_cursor = session_page.get("nextCursor") + if first_session_cursor is None: + first_session_cursor = session_cursor + if not session_cursor: + break + assert len(traversed_session_ids) <= len(expected_session_ids) + assert traversed_session_ids == expected_session_ids + assert first_session_cursor + tampered_cursor = first_session_cursor[:-1] + ( + "A" if first_session_cursor[-1] != "A" else "B" + ) + assert tool_payload( + tool_call( + server, + next_id, + "read_messages", + {"view": "sessions", "limit": 1, "cursor": tampered_cursor}, + ), + True, + )["error"]["code"] == "VALIDATION_ERROR" + next_id += 1 + health_with_sessions = tool_payload( + tool_call(server, next_id, "get_system_health", {}) + ) + next_id += 1 + expected_session_count = len(expected_session_ids) + assert health_with_sessions["session_count"] == expected_session_count relation_message = tool_payload( tool_call( server, @@ -429,6 +555,46 @@ def main() -> None: next_id += 1 assert 0 < len(large_page["history"]) < 6 and large_page.get("nextCursor") assert server.last_response_size <= 8 * 1024 * 1024 + + large_session_ids = [f"large-session-{index}" for index in range(5)] + for session_id in large_session_ids: + tool_payload( + tool_call( + server, + next_id, + "write_message", + {"session_id": session_id, "role": "user", "content": large_content}, + ) + ) + next_id += 1 + budget_session_ids: list[str] = [] + budget_cursor: str | None = None + budget_page_count = 0 + while True: + arguments = {"view": "sessions", "limit": 1000} + if budget_cursor: + arguments["cursor"] = budget_cursor + budget_page = tool_payload( + tool_call(server, next_id, "read_messages", arguments) + ) + next_id += 1 + budget_page_count += 1 + assert budget_page["sessions"] + assert server.last_response_size <= 8 * 1024 * 1024 + budget_session_ids.extend(item["session_id"] for item in budget_page["sessions"]) + budget_cursor = budget_page.get("nextCursor") + if not budget_cursor: + break + assert budget_page_count <= 10 + assert budget_page_count > 1 + assert len(budget_session_ids) == len(set(budget_session_ids)) + assert set(large_session_ids).issubset(budget_session_ids) + health_after_budget = tool_payload( + tool_call(server, next_id, "get_system_health", {}) + ) + next_id += 1 + expected_session_count = len(budget_session_ids) + assert health_after_budget["session_count"] == expected_session_count assert tool_payload( tool_call( server, @@ -559,6 +725,7 @@ def main() -> None: ) next_id += 1 assert resumed_health["status"] == "OK" + assert resumed_health["session_count"] == expected_session_count finally: restarted.close() finally: @@ -567,4 +734,3 @@ def main() -> None: if __name__ == "__main__": main() - |
