From f67456a1e124e97b51f955743364eb0c4c0117c2 Mon Sep 17 00:00:00 2001 From: Dakera Ops Date: Fri, 31 Jul 2026 18:48:16 +0000 Subject: [PATCH 1/4] test(ops): add AsyncDakeraClient.debug_config() parity tests (DAK-7477) Sync client had 2 tests in TestDebugConfig; async client had zero. Adds TestAsyncDebugConfig with happy-path and 403/AuthorizationError tests mirroring the sync coverage. Co-Authored-By: Paperclip --- tests/test_ops.py | 48 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/tests/test_ops.py b/tests/test_ops.py index 9bc7cda..4b2ac5f 100644 --- a/tests/test_ops.py +++ b/tests/test_ops.py @@ -617,3 +617,51 @@ def test_debug_config_forbidden_raises(self, client, mock_responses): ) with pytest.raises(AuthorizationError): client.debug_config() + + +class TestAsyncDebugConfig: + """AsyncDakeraClient.debug_config() parity tests (DAK-7477).""" + + async def test_async_debug_config_returns_env_map(self): + """Async variant GETs /debug/config and returns the env-var dict.""" + from unittest.mock import patch + + from dakera import AsyncDakeraClient + + client = AsyncDakeraClient("http://localhost:3000") + captured: dict = {} + fake_response = { + "DAKERA_ENABLE_BM25": "true", + "DAKERA_RERANKER_ENABLED": "true", + "_version": "0.11.102", + "_build_sha": "abc1234", + } + + async def fake_request(method, path, **kwargs): + captured["method"] = method + captured["path"] = path + return fake_response + + with patch.object(client, "_request", side_effect=fake_request): + result = await client.debug_config() + + assert captured["method"] == "GET" + assert captured["path"] == "/debug/config" + assert result["_version"] == "0.11.102" + assert "DAKERA_ENABLE_BM25" in result + + async def test_async_debug_config_forbidden_raises(self): + """Async variant surfaces AuthorizationError on 403 (no Admin scope).""" + from unittest.mock import patch + + from dakera import AsyncDakeraClient + from dakera.exceptions import AuthorizationError + + client = AsyncDakeraClient("http://localhost:3000") + + async def fake_request(method, path, **kwargs): + raise AuthorizationError("Forbidden: Admin scope required", status_code=403) + + with patch.object(client, "_request", side_effect=fake_request): + with pytest.raises(AuthorizationError): + await client.debug_config() From d95c0f1842dd89eab05218c55eaafbb3bee61537 Mon Sep 17 00:00:00 2001 From: Dakera Ops Date: Sat, 1 Aug 2026 02:46:13 +0000 Subject: [PATCH 2/4] fix(lint): collapse nested with into single statement (SIM117) Ruff SIM117 requires a single `with` with multiple contexts instead of nested `with` blocks. Co-Authored-By: Paperclip --- tests/test_ops.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/tests/test_ops.py b/tests/test_ops.py index 4b2ac5f..7b35693 100644 --- a/tests/test_ops.py +++ b/tests/test_ops.py @@ -662,6 +662,5 @@ async def test_async_debug_config_forbidden_raises(self): async def fake_request(method, path, **kwargs): raise AuthorizationError("Forbidden: Admin scope required", status_code=403) - with patch.object(client, "_request", side_effect=fake_request): - with pytest.raises(AuthorizationError): - await client.debug_config() + with patch.object(client, "_request", side_effect=fake_request), pytest.raises(AuthorizationError): + await client.debug_config() From 97dae6e3d7e6e7efebbcdf04dc8b0580ecd011e3 Mon Sep 17 00:00:00 2001 From: Dakera Ops Date: Sat, 1 Aug 2026 02:48:08 +0000 Subject: [PATCH 3/4] test(vectors): add AsyncDakeraClient.export_vectors() cursor parity tests export_vectors cursor pagination had sync coverage but no async tests. Adds TestAsyncExportVectorsCursor with two tests: cursor forwarded in request body, and cursor omitted when None. Co-Authored-By: Paperclip --- tests/test_memory_extended.py | 48 +++++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/tests/test_memory_extended.py b/tests/test_memory_extended.py index 62c0f33..6f2a86b 100644 --- a/tests/test_memory_extended.py +++ b/tests/test_memory_extended.py @@ -1318,3 +1318,51 @@ def test_store_memory_valid_from_combined_with_other_fields(self, client, mock_r assert req_body.get("tags") == ["history"] assert req_body.get("session_id") == "sess-1" assert req_body.get("ttl_seconds") == 86400 + + +class TestAsyncExportVectorsCursor: + """AsyncDakeraClient.export_vectors() cursor pagination parity (DAK-7424 follow-up).""" + + async def test_async_export_vectors_forwards_cursor(self): + """Async variant forwards cursor in the request body for pagination.""" + from unittest.mock import patch + + from dakera import AsyncDakeraClient + + client = AsyncDakeraClient("http://localhost:3000") + captured: dict = {} + + async def fake_request(method, path, data=None, **kwargs): + captured["method"] = method + captured["path"] = path + captured["data"] = data or {} + return {"vectors": [{"id": "v-1", "values": [0.1, 0.2]}], "next_cursor": "cursor-page2"} + + with patch.object(client, "_request", side_effect=fake_request): + result = await client.export_vectors("test-ns", cursor="cursor-abc", limit=50) + + assert captured["method"] == "POST" + assert "test-ns" in captured["path"] + assert captured["data"]["cursor"] == "cursor-abc" + assert captured["data"]["limit"] == 50 + assert result["next_cursor"] == "cursor-page2" + assert len(result["vectors"]) == 1 + + async def test_async_export_vectors_omits_cursor_when_none(self): + """Async variant does not include cursor in request body when not provided.""" + from unittest.mock import patch + + from dakera import AsyncDakeraClient + + client = AsyncDakeraClient("http://localhost:3000") + captured: dict = {} + + async def fake_request(method, path, data=None, **kwargs): + captured["data"] = data or {} + return {"vectors": [], "next_cursor": None} + + with patch.object(client, "_request", side_effect=fake_request): + await client.export_vectors("test-ns", limit=10) + + assert "cursor" not in captured["data"] + assert captured["data"]["limit"] == 10 From 5b9f00ba14c2836287aad2dd968b3805e9c2de9c Mon Sep 17 00:00:00 2001 From: Dakera Ops Date: Sat, 1 Aug 2026 02:49:12 +0000 Subject: [PATCH 4/4] fix(lint): wrap long with statement in parentheses (E501) Collapsed with statement was 107 chars, exceeding the 100-char limit. Use parenthesized multi-context with (valid Python 3.10+). Co-Authored-By: Paperclip --- tests/test_ops.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/tests/test_ops.py b/tests/test_ops.py index 7b35693..0c9970c 100644 --- a/tests/test_ops.py +++ b/tests/test_ops.py @@ -662,5 +662,8 @@ async def test_async_debug_config_forbidden_raises(self): async def fake_request(method, path, **kwargs): raise AuthorizationError("Forbidden: Admin scope required", status_code=403) - with patch.object(client, "_request", side_effect=fake_request), pytest.raises(AuthorizationError): + with ( + patch.object(client, "_request", side_effect=fake_request), + pytest.raises(AuthorizationError), + ): await client.debug_config()