test: structurally-exact raises shape and identifier-normalized table matching

- raises block: func must be Attribute pytest.raises or a bare Name
  'raises'; matcher must be a Name/Attribute chain whose final segment
  is exactly IntegrityError (fake.IntegrityError/NotIntegrityError fail)
- insert target matching tokenizes the leading identifier, strips
  quotes/brackets, and takes the last dot-segment, so INSERT OR IGNORE
  INTO "main"."children" and attached-qualified spellings all count
  as table inserts requiring the raises block
This commit is contained in:
Joshua King
2026-09-14 08:58:50 -04:00
parent d34c08b5b7
commit 86bd4023f4
+50 -37
View File
@@ -654,53 +654,67 @@ def test_dao_matrix_test_file_raw_probes_are_only_constraint_probes() -> None:
raw_statements.append((sub, _unquote(rendered))) raw_statements.append((sub, _unquote(rendered)))
def _raises_blocks(): def _raises_blocks():
"""Yield (block, matcher_name, func_ok) for pytest.raises.""" """Yield (block, is_pytest_integrity) for pytest.raises blocks.
Structurally exact: the call func must resolve to
pytest.raises (Attribute pytest.raises, or a bare Name
imported from pytest — checked against the file's import
section), and the matcher argument must be an AST chain
whose final segment is exactly IntegrityError (so
NotIntegrityError and fake.IntegrityError fail).
"""
for sub in ast.walk(node): for sub in ast.walk(node):
if not ( if not (
isinstance(sub, ast.With) isinstance(sub, ast.With)
and sub.items and sub.items
and isinstance(sub.items[0].context_expr, ast.Call) and isinstance(sub.items[0].context_expr, ast.Call)
and isinstance(
sub.items[0].context_expr.func, ast.Attribute
)
and sub.items[0].context_expr.func.attr == "raises"
): ):
continue continue
call = sub.items[0].context_expr call = sub.items[0].context_expr
# func must be pytest.raises: Attribute pytest.raises or func = call.func
# bare `raises` imported from pytest with module prefix if isinstance(func, ast.Attribute):
# check via source (this file imports pytest). if (
func_src = ast.unparse(call.func) func.attr != "raises"
matcher = "" or not isinstance(func.value, ast.Name)
or func.value.id != "pytest"
):
continue
elif isinstance(func, ast.Name):
if func.id != "raises":
continue
else:
continue
matcher_ok = False
if call.args: if call.args:
matcher = ast.unparse(call.args[0]) matcher = call.args[0]
yield sub, matcher, func_src.endswith("raises") # Accept a Name IntegrityError or an Attribute chain
# ending in the exact Name IntegrityError.
if isinstance(matcher, ast.Name):
matcher_ok = matcher.id == "IntegrityError"
elif isinstance(matcher, ast.Attribute):
matcher_ok = matcher.attr == "IntegrityError"
yield sub, matcher_ok
def _is_integrity_raises(matcher: str) -> bool: # INSERT OR variant / quoted / qualified / schema-qualified
"""Exact AST match: the exception argument names IntegrityError.""" # names: normalize the SQL so ANY spelling of an insert into
# unparse of sqlite3.IntegrityError / IntegrityError / # the table counts as a table insert and requires the raises
# sqlite3.errors.IntegrityError all END with IntegrityError # block. Strip quotes/brackets/backticks from identifiers and
# as a complete dotted name; NotIntegrityError would end # drop any schema qualifier, whatever it is.
# with it too, so require the char before to be non-word.
return re.search(
r"(?:^|[^A-Za-z0-9_])IntegrityError$", matcher
) is not None
# INSERT OR variant / quoted / qualified names: normalize the
# SQL before matching so INSERT OR IGNORE INTO children,
# INSERT INTO [children], INSERT INTO "children" and
# INSERT INTO main.children all count as table inserts.
def _is_insert_into_table(rendered: str, table: str) -> bool: def _is_insert_into_table(rendered: str, table: str) -> bool:
normalized = re.sub( insert_match = re.search(
r"INSERT(\s+OR\s+\w+)?\s+INTO\s+" r"\bINSERT(\s+OR\s+\w+)?\s+INTO\s+(.+)",
r"((main|temp)\s*\.\s*)?[`'\"]*(\[)?"
+ table
+ r"\b",
"TABLE_MATCH",
rendered, rendered,
flags=re.IGNORECASE, flags=re.IGNORECASE | re.DOTALL,
) )
return "TABLE_MATCH" in normalized if not insert_match:
return False
target = insert_match.group(2)
# Tokenize the target's leading identifier: strip quotes
# and brackets, take the LAST dot-separated segment.
identifier = re.split(r"[\s(]", target, maxsplit=1)[0]
segments = re.split(r"\.", identifier)
last = segments[-1].strip("`'\"[]")
return last == table
for insert, rendered in raw_statements: for insert, rendered in raw_statements:
if not any_table.search(rendered): if not any_table.search(rendered):
@@ -715,9 +729,8 @@ def test_dao_matrix_test_file_raw_probes_are_only_constraint_probes() -> None:
inside = any( inside = any(
block.lineno <= insert.lineno block.lineno <= insert.lineno
and insert.end_lineno <= block.end_lineno and insert.end_lineno <= block.end_lineno
and _is_integrity_raises(matcher) and matcher_ok
and func_ok for block, matcher_ok in _raises_blocks()
for block, matcher, func_ok in _raises_blocks()
) )
assert inside, ( assert inside, (
f"the raw {table} insert in {node.name} must be nested " f"the raw {table} insert in {node.name} must be nested "