Skip to content

Commit d46f46c

Browse files
committed
test(graph): reuse scenario fixtures and protocol capability checks
1 parent 9f7823d commit d46f46c

1 file changed

Lines changed: 34 additions & 28 deletions

File tree

‎tests/rhosocial/activerecord_sqlserver_test/feature/backend/dialect/test_sqlserver_sql_graph.py‎

Lines changed: 34 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
``.claude/plan/2026-09-16/pgq-expression-layer.md`` §4.3.
66
"""
77

8-
import os
8+
from types import SimpleNamespace
99
from unittest.mock import Mock
1010
from uuid import uuid4
1111

@@ -34,6 +34,8 @@
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")
361374
class 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

427433
class TestCorePgqStillRejected:

0 commit comments

Comments
 (0)