1+ # pyright: reportPrivateUsage=false
12"""Regression tests for adapter ADK SQL template ownership."""
23
34import ast
45import importlib
56import inspect
7+ from unittest .mock import MagicMock
68
79import pytest
810
11+ from sqlspec .adapters .cockroach_psycopg .adk import (
12+ CockroachPsycopgAsyncADKMemoryStore ,
13+ CockroachPsycopgAsyncADKStore ,
14+ CockroachPsycopgSyncADKMemoryStore ,
15+ CockroachPsycopgSyncADKStore ,
16+ )
17+ from sqlspec .adapters .mysqlconnector .adk import (
18+ MysqlConnectorAsyncADKMemoryStore ,
19+ MysqlConnectorAsyncADKStore ,
20+ MysqlConnectorSyncADKMemoryStore ,
21+ MysqlConnectorSyncADKStore ,
22+ )
23+ from sqlspec .adapters .oracledb .adk import (
24+ JSONStorageType ,
25+ OracleAsyncADKMemoryStore ,
26+ OracleAsyncADKStore ,
27+ OracleSyncADKMemoryStore ,
28+ OracleSyncADKStore ,
29+ )
30+ from sqlspec .adapters .psycopg .adk import (
31+ PsycopgAsyncADKMemoryStore ,
32+ PsycopgAsyncADKStore ,
33+ PsycopgSyncADKMemoryStore ,
34+ PsycopgSyncADKStore ,
35+ )
36+
37+
38+ def _mock_config (adk_config : dict [str , object ]) -> MagicMock :
39+ config = MagicMock ()
40+ config .extension_config = {"adk" : adk_config }
41+ return config
42+
43+
44+ async def _session_ddl (store : object ) -> dict [str , str ]:
45+ return {
46+ "sessions" : await store ._sessions_table_ddl (), # type: ignore[attr-defined]
47+ "events" : await store ._events_table_ddl (), # type: ignore[attr-defined]
48+ "app_state" : await store ._app_states_table_ddl (), # type: ignore[attr-defined]
49+ "user_state" : await store ._user_states_table_ddl (), # type: ignore[attr-defined]
50+ "metadata" : await store ._metadata_table_ddl (), # type: ignore[attr-defined]
51+ "seed" : await store ._metadata_seed_sql (), # type: ignore[attr-defined]
52+ }
53+
54+
55+ def _sync_session_ddl (store : object ) -> dict [str , str ]:
56+ return {
57+ "sessions" : store ._sessions_table_ddl (), # type: ignore[attr-defined]
58+ "events" : store ._events_table_ddl (), # type: ignore[attr-defined]
59+ "app_state" : store ._app_states_table_ddl (), # type: ignore[attr-defined]
60+ "user_state" : store ._user_states_table_ddl (), # type: ignore[attr-defined]
61+ "metadata" : store ._metadata_table_ddl (), # type: ignore[attr-defined]
62+ "seed" : store ._metadata_seed_sql (), # type: ignore[attr-defined]
63+ }
64+
965
1066@pytest .mark .parametrize (
1167 "module_name" ,
@@ -28,3 +84,149 @@ def test_adk_ddl_methods_reference_module_templates(module_name: str) -> None:
2884 inline_templates = [node for owner in sql_owners for node in ast .walk (owner ) if isinstance (node , ast .JoinedStr )]
2985
3086 assert not inline_templates
87+
88+
89+ @pytest .mark .anyio
90+ async def test_psycopg_templates_bind_identically_for_sync_and_async_stores () -> None :
91+ config = _mock_config ({
92+ "session_table" : "agent_session" ,
93+ "events_table" : "agent_event" ,
94+ "memory_table" : "agent_memory" ,
95+ "owner_id_column" : "tenant_id UUID REFERENCES tenant(id)" ,
96+ "enable_event_generated_columns" : True ,
97+ "enable_covering_indexes" : True ,
98+ })
99+ async_store = PsycopgAsyncADKStore (config )
100+ sync_store = PsycopgSyncADKStore (config )
101+ async_memory = PsycopgAsyncADKMemoryStore (config )
102+ sync_memory = PsycopgSyncADKMemoryStore (config )
103+
104+ async_ddl = await _session_ddl (async_store )
105+ sync_ddl = _sync_session_ddl (sync_store )
106+ async_memory_ddl = await async_memory ._memory_table_ddl ()
107+ sync_memory_ddl = sync_memory ._memory_table_ddl ()
108+
109+ assert async_ddl == sync_ddl
110+ assert async_memory_ddl == sync_memory_ddl
111+ assert "tenant_id UUID REFERENCES tenant(id)," in async_ddl ["sessions" ]
112+ assert "tenant_id UUID REFERENCES tenant(id)," in async_memory_ddl
113+ assert "author_gc VARCHAR(256) GENERATED ALWAYS AS (event_data->>'author') STORED" in async_ddl ["events" ]
114+ assert "node_path_gc TEXT GENERATED ALWAYS AS (event_data->'node_info'->>'path') STORED" in async_ddl ["events" ]
115+ assert "ON agent_event(session_id, timestamp ASC) INCLUDE (invocation_id)" in async_ddl ["events" ]
116+
117+
118+ @pytest .mark .anyio
119+ async def test_cockroach_templates_bind_identically_for_sync_and_async_stores () -> None :
120+ config = _mock_config ({
121+ "session_table" : "agent_session" ,
122+ "events_table" : "agent_event" ,
123+ "memory_table" : "agent_memory" ,
124+ "owner_id_column" : "tenant_id UUID" ,
125+ "table_locality" : "LOCALITY GLOBAL" ,
126+ "enable_hash_sharded_indexes" : True ,
127+ "hash_shard_bucket_count" : 8 ,
128+ "enable_storing_indexes" : True ,
129+ "enable_memory_trigram_index" : True ,
130+ })
131+ async_store = CockroachPsycopgAsyncADKStore (config )
132+ sync_store = CockroachPsycopgSyncADKStore (config )
133+ async_memory = CockroachPsycopgAsyncADKMemoryStore (config )
134+ sync_memory = CockroachPsycopgSyncADKMemoryStore (config )
135+
136+ async_ddl = await _session_ddl (async_store )
137+ sync_ddl = _sync_session_ddl (sync_store )
138+ async_memory_ddl = await async_memory ._memory_table_ddl ()
139+ sync_memory_ddl = sync_memory ._memory_table_ddl ()
140+
141+ assert async_ddl == sync_ddl
142+ assert async_memory_ddl == sync_memory_ddl
143+ assert "tenant_id UUID," in async_ddl ["sessions" ]
144+ assert "LOCALITY GLOBAL" in async_ddl ["sessions" ]
145+ assert "USING HASH WITH (bucket_count = 8)" in async_ddl ["events" ]
146+ assert "STORING (invocation_id, event_data)" in async_ddl ["events" ]
147+ assert "ON agent_memory USING GIN (content_text gin_trgm_ops)" in async_memory_ddl
148+
149+
150+ @pytest .mark .anyio
151+ async def test_mysqlconnector_templates_bind_identically_for_sync_and_async_stores () -> None :
152+ config = _mock_config ({
153+ "session_table" : "agent_session" ,
154+ "events_table" : "agent_event" ,
155+ "memory_table" : "agent_memory" ,
156+ "owner_id_column" : "tenant_id BIGINT" ,
157+ "enable_event_generated_columns" : True ,
158+ "enable_covering_indexes" : True ,
159+ "events_table_options" : "COMMENT='agent-events'" ,
160+ "memory_table_options" : "COMMENT='agent-memory'" ,
161+ })
162+ async_store = MysqlConnectorAsyncADKStore (config )
163+ sync_store = MysqlConnectorSyncADKStore (config )
164+ async_memory = MysqlConnectorAsyncADKMemoryStore (config )
165+ sync_memory = MysqlConnectorSyncADKMemoryStore (config )
166+
167+ async_ddl = await _session_ddl (async_store )
168+ sync_ddl = _sync_session_ddl (sync_store )
169+ async_memory_ddl = await async_memory ._memory_table_ddl ()
170+ sync_memory_ddl = sync_memory ._memory_table_ddl ()
171+
172+ assert async_ddl == sync_ddl
173+ assert async_memory_ddl == sync_memory_ddl
174+ assert "tenant_id BIGINT," in async_ddl ["sessions" ]
175+ assert "tenant_id BIGINT," in async_memory_ddl
176+ assert "author_gc VARCHAR(256) GENERATED ALWAYS AS" in async_ddl ["events" ]
177+ assert "INDEX idx_agent_event_session (session_id, timestamp ASC, invocation_id)" in async_ddl ["events" ]
178+ assert "COMMENT='agent-events'" in async_ddl ["events" ]
179+ assert "COMMENT='agent-memory'" in async_memory_ddl
180+
181+
182+ @pytest .mark .anyio
183+ async def test_oracle_templates_bind_identically_for_every_json_storage_type () -> None :
184+ config = _mock_config ({
185+ "session_table" : "agent_session" ,
186+ "events_table" : "agent_event" ,
187+ "memory_table" : "agent_memory" ,
188+ "owner_id_column" : "tenant_id NUMBER" ,
189+ "partitioning" : {"strategy" : "hash" , "partition_count" : 8 },
190+ })
191+ async_store = OracleAsyncADKStore (config )
192+ sync_store = OracleSyncADKStore (config )
193+ async_memory = OracleAsyncADKMemoryStore (config )
194+ sync_memory = OracleSyncADKMemoryStore (config )
195+ expected_columns = {
196+ JSONStorageType .JSON_NATIVE : ("state JSON NOT NULL" , "event_data JSON NOT NULL" , "content_json JSON" ),
197+ JSONStorageType .BLOB_JSON : (
198+ "state BLOB CHECK (state IS JSON) NOT NULL" ,
199+ "event_data BLOB CHECK (event_data IS JSON) NOT NULL" ,
200+ "content_json BLOB CHECK (content_json IS JSON)" ,
201+ ),
202+ JSONStorageType .BLOB_PLAIN : ("state BLOB NOT NULL" , "event_data BLOB NOT NULL" , "content_json BLOB" ),
203+ }
204+
205+ for storage_type , (state_column , event_column , memory_column ) in expected_columns .items ():
206+ async_session_ddl = async_store ._sessions_table_ddl_for_type (storage_type )
207+ sync_session_ddl = sync_store ._sessions_table_ddl_for_type (storage_type )
208+ async_events_ddl = async_store ._events_table_ddl_for_type (storage_type )
209+ sync_events_ddl = sync_store ._events_table_ddl_for_type (storage_type )
210+ async_memory_ddl = async_memory ._memory_table_ddl_for_type (storage_type )
211+ sync_memory_ddl = sync_memory ._memory_table_ddl_for_type (storage_type )
212+
213+ assert async_session_ddl == sync_session_ddl
214+ assert async_events_ddl == sync_events_ddl
215+ assert async_store ._app_states_table_ddl_for_type (storage_type ) == sync_store ._app_states_table_ddl_for_type (
216+ storage_type
217+ )
218+ assert async_store ._user_states_table_ddl_for_type (storage_type ) == sync_store ._user_states_table_ddl_for_type (
219+ storage_type
220+ )
221+ assert async_memory_ddl == sync_memory_ddl
222+ assert state_column in async_session_ddl
223+ assert event_column in async_events_ddl
224+ assert memory_column in async_memory_ddl
225+ assert (
226+ "update_time TIMESTAMP WITH TIME ZONE DEFAULT SYSTIMESTAMP NOT NULL, tenant_id NUMBER" in async_session_ddl
227+ )
228+ assert "tenant_id NUMBER," in async_memory_ddl
229+ assert "PARTITION BY HASH (id) PARTITIONS 8" in async_session_ddl
230+
231+ assert await async_store ._metadata_table_ddl () == sync_store ._metadata_table_ddl ()
232+ assert await async_store ._metadata_seed_sql () == sync_store ._metadata_seed_sql ()
0 commit comments