"""Step definitions for PostgreSQLAnalyzer coverage-boost scenarios. Targets uncovered lines in postgresql_analyzer.py: - Lines 112-116: exception handler in analyze() - Line 135: duplicate CREATE SCHEMA deduplication - Line 198: _extract_tables with tables_seen=None - Line 285: CREATE VIEW without trailing semicolon - Lines 297-302: schema-qualified CREATE VIEW with uko:contains """ from __future__ import annotations from unittest.mock import patch from behave import given, then, use_step_matcher, when from behave.runner import Context use_step_matcher("re") from cleveragents.domain.models.acms.postgresql_analyzer import ( # noqa: E402 PostgreSQLAnalyzer, ) __all__: list[str] = [] _SAMPLE_URI = "uko://test/coverage-boost" # --------------------------------------------------------------------------- # Given # --------------------------------------------------------------------------- @given(r"a fresh PostgreSQLAnalyzer instance") def step_given_fresh_postgresql_analyzer(context: Context) -> None: context.analyzer = PostgreSQLAnalyzer() context.triples = [] # --------------------------------------------------------------------------- # When # --------------------------------------------------------------------------- @when(r"I analyze DDL with duplicate schemas:") def step_when_analyze_ddl_duplicate_schemas(context: Context) -> None: context.triples = context.analyzer.analyze(context.text, _SAMPLE_URI) @when(r"I analyze DDL that triggers an internal parse error") def step_when_analyze_ddl_with_parse_error(context: Context) -> None: """Force an exception inside _extract_tables to exercise lines 112-116.""" with ( patch.object( context.analyzer, "_extract_tables", side_effect=RuntimeError("simulated parse failure"), ), # Capture the log output to verify the warning was emitted. patch( "cleveragents.domain.models.acms.postgresql_analyzer.logger" ) as mock_logger, ): context.triples = context.analyzer.analyze( "CREATE SCHEMA testschema;\nCREATE TABLE t (id INT);\n", _SAMPLE_URI, ) context.mock_logger = mock_logger @when(r"I call _extract_tables directly without tables_seen on:") def step_when_call_extract_tables_directly(context: Context) -> None: """Call _extract_tables with tables_seen=None to hit line 198.""" context.triples = context.analyzer._extract_tables( context.text, _SAMPLE_URI, set(), None, ) @when(r"I analyze DDL with a view missing its semicolon:") def step_when_analyze_ddl_view_no_semicolon(context: Context) -> None: context.triples = context.analyzer.analyze(context.text, _SAMPLE_URI) @when(r"I analyze DDL with a schema-qualified view:") def step_when_analyze_ddl_schema_qualified_view(context: Context) -> None: context.triples = context.analyzer.analyze(context.text, _SAMPLE_URI) # --------------------------------------------------------------------------- # Then # --------------------------------------------------------------------------- @then(r"exactly (?P\d+) Schema type triple should exist") def step_then_exactly_n_schema_triples(context: Context, count: str) -> None: count_int = int(count) actual = sum( 1 for t in context.triples if t.predicate == "rdf:type" and t.object_uri == "uko-data:Schema" ) assert actual == count_int, ( f"Expected {count_int} Schema type triple(s), got {actual}.\n" f"Triples: {context.triples}" ) @then( r'the result triples should contain predicate "(?P[^"]+)" with value "(?P[^"]+)"' ) def step_then_result_contains_pred_value(context: Context, pred: str, val: str) -> None: found = any(t.predicate == pred and t.object_value == val for t in context.triples) assert found, ( f"No triple with predicate={pred!r} object_value={val!r}.\n" f"Triples: {context.triples}" ) @then( r'the result triples should contain predicate "(?P[^"]+)" with uri "(?P[^"]+)"' ) def step_then_result_contains_pred_uri(context: Context, pred: str, uri: str) -> None: found = any(t.predicate == pred and t.object_uri == uri for t in context.triples) assert found, ( f"No triple with predicate={pred!r} object_uri={uri!r}.\n" f"Triples: {context.triples}" ) @then(r'the result triples should contain predicate "(?P[^"]+)" present') def step_then_result_contains_pred(context: Context, pred: str) -> None: found = any(t.predicate == pred for t in context.triples) assert found, f"No triple with predicate={pred!r}.\nTriples: {context.triples}" @then(r"the partial results should be returned without raising") def step_then_partial_results_returned(context: Context) -> None: # analyze() should have caught the exception and returned a list assert isinstance(context.triples, list), ( f"Expected a list, got {type(context.triples).__name__}" ) @then(r"the parse error should have been logged as a warning") def step_then_parse_error_logged(context: Context) -> None: mock_logger = context.mock_logger mock_logger.warning.assert_called_once() call_args = mock_logger.warning.call_args assert "parse error" in call_args[0][0].lower(), ( f"Expected 'parse error' in warning message, got: {call_args[0][0]!r}" ) @then(r'the direct extraction should produce Table triples for "(?P[^"]+)"') def step_then_direct_extraction_produces_table( context: Context, table_name: str ) -> None: has_table_type = any( t.predicate == "rdf:type" and t.object_uri == "uko-data:Table" for t in context.triples ) has_label = any( t.predicate == "rdfs:label" and t.object_value == table_name for t in context.triples ) assert has_table_type, ( f"No rdf:type uko-data:Table triple found.\nTriples: {context.triples}" ) assert has_label, ( f"No rdfs:label={table_name!r} triple found.\nTriples: {context.triples}" ) @then(r'the viewDefinition value should contain "(?P[^"]+)"') def step_then_view_definition_contains(context: Context, fragment: str) -> None: found = any( t.predicate == "uko-data:viewDefinition" and fragment in t.object_value for t in context.triples ) assert found, ( f"No uko-data:viewDefinition triple containing {fragment!r}.\n" f"Triples: {context.triples}" ) @then( r'the result triples should contain predicate "(?P[^"]+)" ' r'linking schema "(?P[^"]+)" to view "(?P[^"]+)"' ) def step_then_result_contains_schema_view_link( context: Context, pred: str, schema: str, view: str ) -> None: found = any( t.predicate == pred and schema in t.subject_uri and view in t.object_uri for t in context.triples ) assert found, ( f"No triple with predicate={pred!r} linking schema containing " f"{schema!r} to view containing {view!r}.\n" f"Triples: {context.triples}" ) # Reset step matcher to parse (default) so subsequent step files are not affected use_step_matcher("parse")