55``.claude/plan/2026-09-16/pgq-expression-layer.md`` §4.3.
66"""
77
8- import os
8+ from types import SimpleNamespace
99from unittest .mock import Mock
1010from uuid import uuid4
1111
3434 SQLServerEdgeConstraint ,
3535 SQLServerGraphTableKind ,
3636)
37+ from rhosocial .activerecord .backend .impl .sqlserver .protocols import SQLServerGraphSupport
38+ from rhosocial .activerecord .testsuite .utils import requires_protocol , skip_test_if_protocol_unsupported
3739
3840
3941@pytest .fixture
@@ -353,27 +355,32 @@ def test_create_table_edge_with_constraint(self, major):
353355 )
354356
355357
358+ @pytest .fixture
359+ def check_graph_protocol_requirements (request , sqlserver_backend ):
360+ subject = SimpleNamespace (__backend__ = sqlserver_backend )
361+ for marker in request .node .iter_markers ("requires_protocol" ):
362+ protocol_class , method_name = marker .args [0 ]
363+ skip_test_if_protocol_unsupported (subject , protocol_class , method_name )
364+
365+
356366@pytest .mark .requires_sqlserver
357- @pytest .mark .skipif (
358- not os .environ .get ("SQLSERVER_GRAPH_TEST_CONNECTION_STRING" ),
359- reason = "SQLSERVER_GRAPH_TEST_CONNECTION_STRING must target a disposable SQL Server 2019+ database" ,
360- )
367+ @pytest .mark .usefixtures ("check_graph_protocol_requirements" )
368+ @requires_protocol (SQLServerGraphSupport , "supports_sql_graph" )
369+ @requires_protocol (SQLServerGraphSupport , "supports_graph_table_kind" )
370+ @requires_protocol (SQLServerGraphSupport , "supports_graph_pseudo_columns" )
371+ @requires_protocol (SQLServerGraphSupport , "supports_shortest_path" )
372+ @requires_protocol (SQLServerGraphSupport , "supports_graph_path_aggregates" )
373+ @requires_protocol (SQLServerGraphSupport , "supports_edge_constraints" )
361374class TestGraphExecution :
362375 @pytest .mark .parametrize ("maximum" , [None , 3 ])
363376 @pytest .mark .parametrize ("direction" , [SQLServerGraphDirection .RIGHT , SQLServerGraphDirection .LEFT ])
364377 @pytest .mark .parametrize ("segment_count" , [1 , 2 ])
365- def test_shortest_path_and_edge_constraint (self , maximum , direction , segment_count ):
366- import pyodbc
367-
368- connection = pyodbc .connect (
369- os .environ ["SQLSERVER_GRAPH_TEST_CONNECTION_STRING" ], autocommit = False , timeout = 5
370- )
378+ def test_shortest_path_and_edge_constraint (self , sqlserver_backend , maximum , direction , segment_count ):
379+ backend = sqlserver_backend
380+ dialect = backend .dialect
381+ transaction = backend .transaction_manager
382+ transaction .begin ()
371383 try :
372- cursor = connection .cursor ()
373- major = int (cursor .execute ("SELECT SERVERPROPERTY('ProductMajorVersion')" ).fetchone ()[0 ])
374- if major < 15 :
375- pytest .skip ("SQL Graph shortest paths and edge constraints require SQL Server 2019+" )
376- dialect = SQLServerDialect (version = (major , 0 , 0 ))
377384 suffix = uuid4 ().hex
378385 node_name , edge_name = f"graph_node_{ suffix } " , f"graph_edge_{ suffix } "
379386 node = dialect .format_identifier (node_name )
@@ -382,10 +389,10 @@ def test_shortest_path_and_edge_constraint(self, maximum, direction, segment_cou
382389 edge_kind = SQLServerAsGraphTableExpression (dialect , SQLServerGraphTableKind .EDGE ).to_sql ()[0 ]
383390 constraint_sql , params = SQLServerEdgeConstraint (dialect , node_name , node_name ).to_sql ()
384391 assert params == ()
385- cursor .execute (f"CREATE TABLE { node } (ID INT NOT NULL) { node_kind } " )
386- cursor .execute (f"CREATE TABLE { edge } ({ constraint_sql } ) { edge_kind } " )
387- cursor .execute (f"INSERT INTO { node } (ID) VALUES (1), (2), (3), (4), (5)" )
388- cursor .execute (
392+ backend .execute (f"CREATE TABLE { node } (ID INT NOT NULL) { node_kind } " )
393+ backend .execute (f"CREATE TABLE { edge } ({ constraint_sql } ) { edge_kind } " )
394+ backend .execute (f"INSERT INTO { node } (ID) VALUES (1), (2), (3), (4), (5)" )
395+ backend .execute (
389396 f"INSERT INTO { edge } ($from_id, $to_id) "
390397 f"SELECT a.$node_id, b.$node_id FROM { node } a, { node } b WHERE b.ID = a.ID + 1"
391398 )
@@ -408,20 +415,19 @@ def test_shortest_path_and_edge_constraint(self, maximum, direction, segment_cou
408415 )
409416 match_sql , params = predicate .to_sql ()
410417 start_id = 1 if direction == SQLServerGraphDirection .RIGHT else 5
411- cursor .execute (
412- f"SELECT LAST_VALUE([Person{ segment_count + 1 } ].ID) WITHIN GROUP (GRAPH PATH) "
413- f"FROM { ', ' .join (tables )} WHERE { match_sql } AND [Person1].ID = ? " ,
418+ result = backend .execute (
419+ f"SELECT LAST_VALUE([Person{ segment_count + 1 } ].ID) WITHIN GROUP (GRAPH PATH) AS [endpoint_id] "
420+ f"FROM { ', ' .join (tables )} WHERE { match_sql } AND [Person1].ID = { dialect . p () } " ,
414421 params + (start_id ,),
415422 )
416- actual = {row [0 ] for row in cursor .fetchall ()}
423+ assert result .data is not None
424+ actual = {row ["endpoint_id" ] for row in result .data }
417425 hops = min (4 // segment_count , maximum or 4 )
418426 step = segment_count if start_id == 1 else - segment_count
419427 assert actual == {start_id + step * iteration for iteration in range (1 , hops + 1 )}
420428 finally :
421- try :
422- connection .rollback ()
423- finally :
424- connection .close ()
429+ if transaction .is_active :
430+ transaction .rollback ()
425431
426432
427433class TestCorePgqStillRejected :
0 commit comments