test: matcher base-chain validation, casefolded table comparison
This commit is contained in:
@@ -687,14 +687,26 @@ def test_dao_matrix_test_file_raw_probes_are_only_constraint_probes() -> None:
|
|||||||
matcher_ok = False
|
matcher_ok = False
|
||||||
if call.args:
|
if call.args:
|
||||||
matcher = call.args[0]
|
matcher = call.args[0]
|
||||||
# Accept a Name IntegrityError or an Attribute chain
|
matcher_ok = _is_integrity_error_node(matcher)
|
||||||
# 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
|
yield sub, matcher_ok
|
||||||
|
|
||||||
|
def _is_integrity_error_node(matcher) -> bool:
|
||||||
|
"""True for an AST chain of Name/Attribute ending exactly at
|
||||||
|
IntegrityError: every Attribute base must itself be a Name
|
||||||
|
or Attribute (a Call base like __import__('x') fails)."""
|
||||||
|
node = matcher
|
||||||
|
while isinstance(node, ast.Attribute):
|
||||||
|
node = node.value
|
||||||
|
if not isinstance(node, ast.Name):
|
||||||
|
return False
|
||||||
|
final = matcher
|
||||||
|
while isinstance(final, ast.Attribute):
|
||||||
|
final = final.attr
|
||||||
|
return (
|
||||||
|
isinstance(final, str)
|
||||||
|
and ast.unparse(matcher).split(".")[-1] == "IntegrityError"
|
||||||
|
)
|
||||||
|
|
||||||
# INSERT OR variant / quoted / qualified / schema-qualified
|
# INSERT OR variant / quoted / qualified / schema-qualified
|
||||||
# names: normalize the SQL so ANY spelling of an insert into
|
# names: normalize the SQL so ANY spelling of an insert into
|
||||||
# the table counts as a table insert and requires the raises
|
# the table counts as a table insert and requires the raises
|
||||||
@@ -710,11 +722,13 @@ def test_dao_matrix_test_file_raw_probes_are_only_constraint_probes() -> None:
|
|||||||
return False
|
return False
|
||||||
target = insert_match.group(2)
|
target = insert_match.group(2)
|
||||||
# Tokenize the target's leading identifier: strip quotes
|
# Tokenize the target's leading identifier: strip quotes
|
||||||
# and brackets, take the LAST dot-separated segment.
|
# and brackets, take the LAST dot-separated segment,
|
||||||
|
# casefolded so 'CHILDREN' cannot bypass the lowercase
|
||||||
|
# table name the scan looks for.
|
||||||
identifier = re.split(r"[\s(]", target, maxsplit=1)[0]
|
identifier = re.split(r"[\s(]", target, maxsplit=1)[0]
|
||||||
segments = re.split(r"\.", identifier)
|
segments = re.split(r"\.", identifier)
|
||||||
last = segments[-1].strip("`'\"[]")
|
last = segments[-1].strip("`'\"[]")
|
||||||
return last == table
|
return last.casefold() == table.casefold()
|
||||||
|
|
||||||
for insert, rendered in raw_statements:
|
for insert, rendered in raw_statements:
|
||||||
if not any_table.search(rendered):
|
if not any_table.search(rendered):
|
||||||
|
|||||||
Reference in New Issue
Block a user