test: lock release snippet guard negative fixtures
This commit is contained in:
@@ -123,6 +123,7 @@ TRAINING_M41_RELEASE_INDEX_GUARD_TEST_INVENTORY = (
|
||||
"test_training_m59_release_index_required_snippet_inventory_source_shape_preserves_literal_order",
|
||||
"test_training_m60_release_index_source_shape_fixture_guard_inventory_self_lock_is_current",
|
||||
"test_training_m61_release_index_source_shape_fixture_guard_methods_constant_source_shape_is_locked",
|
||||
"test_training_m62_release_index_source_shape_fixture_guard_methods_reject_dynamic_constant_sources",
|
||||
"test_training_m53_release_index_doc_category_coverage_is_exact",
|
||||
"test_training_case_product_doc_reflects_current_m40_maturity",
|
||||
)
|
||||
@@ -589,6 +590,9 @@ class DocumentationConsistencyTests(unittest.TestCase):
|
||||
"required_snippet_inventory_source_shape_fixture_constant_source_lock": (
|
||||
"test_training_m61_release_index_source_shape_fixture_guard_methods_constant_source_shape_is_locked"
|
||||
),
|
||||
"required_snippet_inventory_source_shape_fixture_constant_negative_sources": (
|
||||
"test_training_m62_release_index_source_shape_fixture_guard_methods_reject_dynamic_constant_sources"
|
||||
),
|
||||
"per_doc_helper_exact_category_coverage": (
|
||||
"test_training_m53_release_index_doc_category_coverage_is_exact"
|
||||
),
|
||||
@@ -746,6 +750,7 @@ class DocumentationConsistencyTests(unittest.TestCase):
|
||||
"test_training_m59_release_index_required_snippet_inventory_source_shape_preserves_literal_order",
|
||||
"test_training_m60_release_index_source_shape_fixture_guard_inventory_self_lock_is_current",
|
||||
"test_training_m61_release_index_source_shape_fixture_guard_methods_constant_source_shape_is_locked",
|
||||
"test_training_m62_release_index_source_shape_fixture_guard_methods_reject_dynamic_constant_sources",
|
||||
"test_training_m53_release_index_doc_category_coverage_is_exact",
|
||||
"test_training_case_product_doc_reflects_current_m40_maturity",
|
||||
)
|
||||
@@ -824,18 +829,11 @@ class DocumentationConsistencyTests(unittest.TestCase):
|
||||
self.assertEqual(loader.errors, [])
|
||||
self.assertEqual(suite.countTestCases(), 1)
|
||||
|
||||
def test_training_m61_release_index_source_shape_fixture_guard_methods_constant_source_shape_is_locked(
|
||||
self,
|
||||
) -> None:
|
||||
@staticmethod
|
||||
def _release_index_source_shape_fixture_guard_method_names_from_source(
|
||||
module_source: str,
|
||||
) -> tuple[str, ...]:
|
||||
constant_name = "TRAINING_M41_RELEASE_INDEX_SOURCE_SHAPE_FIXTURE_GUARD_METHODS"
|
||||
expected_source_shape_fixture_guards = (
|
||||
"test_training_m55_release_index_required_snippet_inventory_source_shape_is_locked",
|
||||
"test_training_m56_release_index_required_snippet_inventory_source_shape_rejects_dynamic_fixtures",
|
||||
"test_training_m57_release_index_required_snippet_inventory_source_shape_diagnostics_are_locked",
|
||||
"test_training_m58_release_index_required_snippet_inventory_source_shape_accepts_literal_fixtures",
|
||||
"test_training_m59_release_index_required_snippet_inventory_source_shape_preserves_literal_order",
|
||||
)
|
||||
module_source = Path(__file__).read_text(encoding="utf-8")
|
||||
module_tree = ast.parse(module_source)
|
||||
|
||||
constant_assignments = tuple(
|
||||
@@ -847,43 +845,124 @@ class DocumentationConsistencyTests(unittest.TestCase):
|
||||
and node.targets[0].id == constant_name
|
||||
)
|
||||
|
||||
self.assertEqual(len(constant_assignments), 1)
|
||||
if len(constant_assignments) != 1:
|
||||
raise AssertionError(
|
||||
"source shape fixture guard methods must have one assignment"
|
||||
)
|
||||
|
||||
constant_source = constant_assignments[0].value
|
||||
self.assertIsInstance(constant_source, ast.Tuple)
|
||||
self.assertTrue(constant_source.elts)
|
||||
self.assertTrue(
|
||||
all(
|
||||
isinstance(element, ast.Constant)
|
||||
and isinstance(element.value, str)
|
||||
for element in constant_source.elts
|
||||
)
|
||||
disallowed_source_nodes = (
|
||||
ast.Call,
|
||||
ast.DictComp,
|
||||
ast.GeneratorExp,
|
||||
ast.List,
|
||||
ast.ListComp,
|
||||
ast.Name,
|
||||
ast.SetComp,
|
||||
ast.Starred,
|
||||
)
|
||||
self.assertIsNone(
|
||||
next(
|
||||
(
|
||||
node
|
||||
for node in ast.walk(constant_source)
|
||||
if isinstance(
|
||||
node,
|
||||
(
|
||||
ast.Call,
|
||||
ast.DictComp,
|
||||
ast.GeneratorExp,
|
||||
ast.List,
|
||||
ast.ListComp,
|
||||
ast.Name,
|
||||
ast.SetComp,
|
||||
),
|
||||
)
|
||||
),
|
||||
None,
|
||||
)
|
||||
dynamic_source_node = next(
|
||||
(
|
||||
node
|
||||
for node in ast.walk(constant_source)
|
||||
if isinstance(node, disallowed_source_nodes)
|
||||
),
|
||||
None,
|
||||
)
|
||||
if dynamic_source_node is not None:
|
||||
dynamic_node = type(dynamic_source_node).__name__
|
||||
raise AssertionError(
|
||||
"source shape fixture guard methods use dynamic source node "
|
||||
f"{dynamic_node}"
|
||||
)
|
||||
|
||||
if not isinstance(constant_source, ast.Tuple):
|
||||
raise AssertionError(
|
||||
"source shape fixture guard methods must be a literal tuple assignment"
|
||||
)
|
||||
|
||||
if not constant_source.elts or not all(
|
||||
isinstance(element, ast.Constant) and isinstance(element.value, str)
|
||||
for element in constant_source.elts
|
||||
):
|
||||
raise AssertionError(
|
||||
"source shape fixture guard methods must use literal method names"
|
||||
)
|
||||
|
||||
try:
|
||||
method_names = ast.literal_eval(constant_source)
|
||||
except (SyntaxError, ValueError) as exc:
|
||||
raise AssertionError(
|
||||
"source shape fixture guard methods must use literal method names"
|
||||
) from exc
|
||||
|
||||
if not all(isinstance(method_name, str) for method_name in method_names):
|
||||
raise AssertionError(
|
||||
"source shape fixture guard methods must use literal method names"
|
||||
)
|
||||
|
||||
return method_names
|
||||
|
||||
def test_training_m61_release_index_source_shape_fixture_guard_methods_constant_source_shape_is_locked(
|
||||
self,
|
||||
) -> None:
|
||||
expected_source_shape_fixture_guards = (
|
||||
"test_training_m55_release_index_required_snippet_inventory_source_shape_is_locked",
|
||||
"test_training_m56_release_index_required_snippet_inventory_source_shape_rejects_dynamic_fixtures",
|
||||
"test_training_m57_release_index_required_snippet_inventory_source_shape_diagnostics_are_locked",
|
||||
"test_training_m58_release_index_required_snippet_inventory_source_shape_accepts_literal_fixtures",
|
||||
"test_training_m59_release_index_required_snippet_inventory_source_shape_preserves_literal_order",
|
||||
)
|
||||
module_source = Path(__file__).read_text(encoding="utf-8")
|
||||
|
||||
self.assertEqual(
|
||||
ast.literal_eval(constant_source),
|
||||
self._release_index_source_shape_fixture_guard_method_names_from_source(
|
||||
module_source
|
||||
),
|
||||
expected_source_shape_fixture_guards,
|
||||
)
|
||||
|
||||
def test_training_m62_release_index_source_shape_fixture_guard_methods_reject_dynamic_constant_sources(
|
||||
self,
|
||||
) -> None:
|
||||
constant_name = "TRAINING_M41_RELEASE_INDEX_SOURCE_SHAPE_FIXTURE_GUARD_METHODS"
|
||||
first_method = (
|
||||
"test_training_m55_release_index_required_snippet_inventory_source_shape_is_locked"
|
||||
)
|
||||
second_method = (
|
||||
"test_training_m56_release_index_required_snippet_inventory_source_shape_rejects_dynamic_fixtures"
|
||||
)
|
||||
invalid_sources = {
|
||||
"tuple_call": (
|
||||
f"{constant_name} = tuple([{first_method!r}, {second_method!r}])",
|
||||
"dynamic source node Call",
|
||||
),
|
||||
"sorted_call": (
|
||||
f"{constant_name} = sorted(({first_method!r}, {second_method!r}))",
|
||||
"dynamic source node Call",
|
||||
),
|
||||
"name_alias": (
|
||||
f"fixture_guard_methods = ({first_method!r}, {second_method!r})\n"
|
||||
f"{constant_name} = fixture_guard_methods",
|
||||
"dynamic source node Name",
|
||||
),
|
||||
"list_literal": (
|
||||
f"{constant_name} = [{first_method!r}, {second_method!r}]",
|
||||
"dynamic source node List",
|
||||
),
|
||||
"list_comprehension": (
|
||||
f"{constant_name} = [method for method in ({first_method!r}, {second_method!r})]",
|
||||
"dynamic source node ListComp",
|
||||
),
|
||||
}
|
||||
|
||||
for fixture_name, (source, error_snippet) in invalid_sources.items():
|
||||
with self.subTest(fixture_name=fixture_name):
|
||||
with self.assertRaisesRegex(AssertionError, re.escape(error_snippet)):
|
||||
self._release_index_source_shape_fixture_guard_method_names_from_source(
|
||||
source
|
||||
)
|
||||
|
||||
def test_training_m47_release_index_per_doc_helper_inventory_self_lock_is_current(self) -> None:
|
||||
helper_name = "_release_index_missing_required_snippets_by_doc"
|
||||
docs_guard_name = "test_training_readmes_expose_m1_m40_release_index_without_raw_test_modules"
|
||||
|
||||
Reference in New Issue
Block a user