From 59dd4dcd8f9388ceed059d6df2f895c139a531e8 Mon Sep 17 00:00:00 2001 From: David Arnold Date: Thu, 10 Oct 2024 02:25:42 +0200 Subject: [PATCH] fix: test record discovery (#28067) * fix: doctype name discovery * fix: preserve dependency order --- frappe/tests/classes/integration_test_case.py | 7 ++++-- frappe/tests/classes/unit_test_case.py | 10 +++++--- frappe/tests/utils/generators.py | 25 ++++++++++--------- 3 files changed, 24 insertions(+), 18 deletions(-) diff --git a/frappe/tests/classes/integration_test_case.py b/frappe/tests/classes/integration_test_case.py index 46df7c35a0..5b7cf37d16 100644 --- a/frappe/tests/classes/integration_test_case.py +++ b/frappe/tests/classes/integration_test_case.py @@ -48,8 +48,11 @@ class IntegrationTestCase(UnitTestCase): if cls.doctype and cls.doctype not in frappe.local.test_objects: cls._newly_created_test_records += make_test_records(cls.doctype) elif not cls.doctype: - to_add, to_remove = get_missing_records_module_overrides(cls.module) - to_add.difference_update(to_remove) + to_add, ignore = get_missing_records_module_overrides(cls.module) + if ignore: + raise NotImplementedError( + f"IGNORE_TEST_RECORD_DEPENDENCIES is only implement for test modules within a doctype folder {cls.module} {cls.doctype}" + ) for doctype in to_add: cls._newly_created_test_records += make_test_records(doctype) # flush changes done so far to avoid flake diff --git a/frappe/tests/classes/unit_test_case.py b/frappe/tests/classes/unit_test_case.py index 6f27cfcc8d..8ae656f5eb 100644 --- a/frappe/tests/classes/unit_test_case.py +++ b/frappe/tests/classes/unit_test_case.py @@ -105,11 +105,13 @@ class UnitTestCase(unittest.TestCase, BaseTestCase): def _get_doctype_from_module(cls): - module_path = cls.__module__.split(".") + namespace = cls.__module__.split(".") + path = frappe.get_pymodule_path(cls.__module__) try: - doctype_index = module_path.index("doctype") - doctype_snake_case = module_path[doctype_index + 1] - json_file_path = Path(*module_path[:-1]).joinpath(f"{doctype_snake_case}.json") + doctype_index = namespace.index("doctype") + doctype_snake_case = namespace[doctype_index + 1] + # need to check json spec: todo -> ToDo (not Todo); not inferable + json_file_path = Path(path).joinpath(f"{doctype_snake_case}.json") if json_file_path.is_file(): doctype_data = json.loads(json_file_path.read_text()) return doctype_data.get("name") diff --git a/frappe/tests/utils/generators.py b/frappe/tests/utils/generators.py index 09731a1079..16c816761c 100644 --- a/frappe/tests/utils/generators.py +++ b/frappe/tests/utils/generators.py @@ -53,24 +53,25 @@ def get_missing_records_doctypes(doctype): for df in meta.get_table_fields(): link_fields.extend(frappe.get_meta(df.options).get_link_fields()) - doctype_set = {df.options for df in link_fields if df.options != "[Select]"} + unique_doctypes = dict.fromkeys(df.options for df in link_fields if df.options != "[Select]") to_add, to_remove = get_missing_records_module_overrides(test_module) - doctype_set.update(to_add) - doctype_set.difference_update(to_remove) + unique_doctypes.update(dict.fromkeys(to_add)) + if to_remove: + unique_doctypes = {k: v for k, v in unique_doctypes.items() if k not in to_remove} # Recursive depth-first traversal result = [] - for dep_doctype in doctype_set: + for dep_doctype in unique_doctypes: result.extend(get_missing_records_doctypes(dep_doctype)) result.append(doctype) return result -def get_missing_records_module_overrides(module) -> [set, set]: - to_add = set() - to_remove = set() +def get_missing_records_module_overrides(module) -> [list, list]: + to_add = [] + to_remove = [] if hasattr(module, "test_dependencies"): from frappe.deprecation_dumpster import deprecation_warning @@ -90,10 +91,10 @@ find . -name "*.py" | while read -r file; do done ```""", ) - to_add.update(set(module.test_dependencies)) + to_add += module.test_dependencies if hasattr(module, "EXTRA_TEST_RECORD_DEPENDENCIES"): - to_add.update(set(module.EXTRA_TEST_RECORD_DEPENDENCIES)) + to_add += module.EXTRA_TEST_RECORD_DEPENDENCIES if hasattr(module, "test_ignore"): from frappe.deprecation_dumpster import deprecation_warning @@ -105,7 +106,7 @@ done ```bash # Find Python files find . -name "*.py" | while read -r file; do - # Check if the file contains 'test_dependencies' at the module level + # Check if the file contains 'test_ignore' at the module level if grep -q "^test_ignore" "$file"; then # Replace 'test_ignore' with 'IGNORE_TEST_RECORD_DEPENDENCIES' sed -i 's/^test_ignore/IGNORE_TEST_RECORD_DEPENDENCIES/' "$file" @@ -114,10 +115,10 @@ find . -name "*.py" | while read -r file; do done ```""", ) - to_remove.difference_update(set(module.test_ignore)) + to_remove += module.test_ignore if hasattr(module, "IGNORE_TEST_RECORD_DEPENDENCIES"): - to_remove.difference_update(set(module.IGNORE_TEST_RECORD_DEPENDENCIES)) + to_remove += module.IGNORE_TEST_RECORD_DEPENDENCIES return to_add, to_remove