diff --git a/scripts/microgenerator/generate.py b/scripts/microgenerator/generate.py index 61f47d6c1..da52febe0 100644 --- a/scripts/microgenerator/generate.py +++ b/scripts/microgenerator/generate.py @@ -389,7 +389,31 @@ def process_structure( def _should_include_class(class_name: str, class_filters: Dict[str, Any]) -> bool: - """Checks if a class should be included based on filter criteria.""" + """Determines if a class should be included based on name filters. + + Filters are defined in the configuration file and passed in the + `class_filters` dictionary. + + Args: + class_name: The name of the class to check. + class_filters: A dictionary containing filter rules: + "include_suffixes": List of suffixes. If provided, the class name + MUST end with one of these suffixes. + "exclude_suffixes": List of suffixes. If provided, the class name + MUST NOT end with any of these suffixes. + + Returns: + bool: True if the class should be included, False otherwise. + + Example: + >>> filters = {"include_suffixes": ["Client"], "exclude_suffixes": ["BaseClient"]} + >>> _should_include_class("DatasetClient", filters) + True + >>> _should_include_class("BaseClient", filters) + False + >>> _should_include_class("SomeOtherClass", filters) + False + """ if class_filters.get("include_suffixes"): if not class_name.endswith(tuple(class_filters["include_suffixes"])): return False @@ -400,7 +424,31 @@ def _should_include_class(class_name: str, class_filters: Dict[str, Any]) -> boo def _should_include_method(method_name: str, method_filters: Dict[str, Any]) -> bool: - """Checks if a method should be included based on filter criteria.""" + """Determines if a method should be included based on name filters. + + Filters are defined in the configuration file and passed in the + `method_filters` dictionary. + + Args: + method_name: The name of the method to check. + method_filters: A dictionary containing filter rules: + "include_prefixes": List of prefixes. If provided, the method name + MUST start with one of these prefixes. + "exclude_prefixes": List of prefixes. If provided, the method name + MUST NOT start with any of these prefixes. + + Returns: + bool: True if the method should be included, False otherwise. + + Example: + >>> filters = {"include_prefixes": ["get_", "list_"], "exclude_prefixes": ["_internal_"]} + >>> _should_include_method("get_dataset", filters) + True + >>> _should_include_method("create_dataset", filters) + False + >>> _should_include_method("_internal_get_dataset", filters) + False + """ if method_filters.get("include_prefixes"): if not any( method_name.startswith(p) for p in method_filters["include_prefixes"] diff --git a/scripts/microgenerator/tests/unit/test_generate_filters.py b/scripts/microgenerator/tests/unit/test_generate_filters.py new file mode 100644 index 000000000..3882237bf --- /dev/null +++ b/scripts/microgenerator/tests/unit/test_generate_filters.py @@ -0,0 +1,129 @@ +import pytest +from scripts.microgenerator.generate import ( + _should_include_class, + _should_include_method, +) + + +# Tests for _should_include_class +@pytest.mark.parametrize( + "class_name, filters, expected", + [ + pytest.param("MyClass", {}, True, id="No filters"), + pytest.param( + "DatasetClient", + {"include_suffixes": ["Client", "Service"]}, + True, + id="Include suffix match", + ), + pytest.param( + "MyClass", + {"include_suffixes": ["Client", "Service"]}, + False, + id="Include suffix no match", + ), + pytest.param( + "MyBase", + {"exclude_suffixes": ["Base", "Util"]}, + False, + id="Exclude suffix match", + ), + pytest.param( + "MyClass", + {"exclude_suffixes": ["Base", "Util"]}, + True, + id="Exclude suffix no match", + ), + pytest.param( + "DatasetClient", + {"include_suffixes": ["Client"], "exclude_suffixes": ["BaseClient"]}, + True, + id="Mix include/exclude match", + ), + pytest.param( + "BaseClient", + {"include_suffixes": ["Client"], "exclude_suffixes": ["BaseClient"]}, + False, + id="Mix include/exclude no match", + ), + pytest.param( + "MyClass", + {"include_suffixes": [], "exclude_suffixes": []}, + True, + id="Empty filters", + ), + ], +) +def test_should_include_class(class_name, filters, expected): + assert _should_include_class(class_name, filters) is expected + + +# Tests for _should_include_method +@pytest.mark.parametrize( + "method_name, filters, expected", + [ + pytest.param("my_method", {}, True, id="No filters"), + pytest.param( + "get_dataset", + {"include_prefixes": ["get_", "list_"]}, + True, + id="Include prefix match (get)", + ), + pytest.param( + "list_jobs", + {"include_prefixes": ["get_", "list_"]}, + True, + id="Include prefix match (list)", + ), + pytest.param( + "create_dataset", + {"include_prefixes": ["get_", "list_"]}, + False, + id="Include prefix no match", + ), + pytest.param( + "_private_method", + {"exclude_prefixes": ["_", "internal_"]}, + False, + id="Exclude prefix match (private)", + ), + pytest.param( + "internal_helper", + {"exclude_prefixes": ["_", "internal_"]}, + False, + id="Exclude prefix match (internal)", + ), + pytest.param( + "get_dataset", + {"exclude_prefixes": ["_", "internal_"]}, + True, + id="Exclude prefix no match", + ), + pytest.param( + "get_dataset", + {"include_prefixes": ["get_"], "exclude_prefixes": ["get_internal_"]}, + True, + id="Mix include/exclude match", + ), + pytest.param( + "get_internal_status", + {"include_prefixes": ["get_"], "exclude_prefixes": ["get_internal_"]}, + False, + id="Mix include/exclude no match (exclude wins)", + ), + pytest.param( + "list_datasets", + {"include_prefixes": ["get_"], "exclude_prefixes": ["get_internal_"]}, + False, + id="Mix include/exclude no match (include fails)", + ), + pytest.param( + "my_method", + {"include_prefixes": [], "exclude_prefixes": []}, + True, + id="Empty filters", + ), + ], +) +def test_should_include_method(method_name, filters, expected): + assert _should_include_method(method_name, filters) is expected