orchestrata_tools.test_generator.combined_generator

Reference › orchestrata_tools › test_generator › combined_generator

CombinedTestGenerator unifies the signature-based generators of this package behind one entry point. It is built from an ASTIndex and an optional TestMatcher (matcher), and creates a stub generator, an interface generator, a branch generator and a behavioral generator. It is re-exported by the package, and tests/test_orchestrata_tools_test_generator.py covers it.

generate_for_method returns a dictionary keyed by kind: always a stub, plus branch and behavioral when those generators produce output. generate_for_class takes a class: a protocol or ABC gets an interface test, a plain class gets class stubs, and each public method then contributes its branch and behavioral tests, with the per-method stub skipped when class stubs already cover it. generate_for_gaps asks the matcher for untested public methods and returns one stub file per module, named from the module path. It raises ValueError if no matcher was given.

At a glance

Source src/orchestrata_tools/test_generator/combined_generator.py
Lines of code 90
Classes 1
Functions 0
Imports (internal) orchestrata_tools.models.ast_models, orchestrata_tools.parser.indexer, orchestrata_tools.test_analyzer.matcher, orchestrata_tools.test_generator.behavioral_tests, orchestrata_tools.test_generator.branch_tests, orchestrata_tools.test_generator.interface_tests, orchestrata_tools.test_generator.stub_generator
Imported by orchestrata_tools.test_generator
Tests that import it tests/test_orchestrata_tools.py, tests/test_orchestrata_tools_test_generator.py

Class diagram

Class diagram orchestrata_tools.test_generator.combined_generator

Dependency diagram

Dependency diagram orchestrata_tools.test_generator.combined_generator

API

Classes

CombinedTestGenerator

Line 18

Generate comprehensive test suites using all available strategies.

Method Signature Summary
__init__ __init__(index: ASTIndex, matcher: TestMatcher | None = None)
generate_for_method generate_for_method(method: MethodInfo) -> dict[str, str] Generate all applicable tests for a single method.
generate_for_class generate_for_class() -> dict[str, str] Generate all applicable tests for a class.
generate_for_gaps generate_for_gaps() -> dict[str, str] Generate tests for all uncovered public methods.

Source

Permalink: https://gitlab.com/MSamy/orchestrata-ultimate-architecture/-/blob/a4cbc2bdf/src/orchestrata_tools/test_generator/combined_generator.py

"""Unified test generation combining all strategies."""

from __future__ import annotations

from typing import TYPE_CHECKING

from orchestrata_tools.test_generator.behavioral_tests import BehavioralTestGenerator
from orchestrata_tools.test_generator.branch_tests import BranchTestGenerator
from orchestrata_tools.test_generator.interface_tests import InterfaceTestGenerator
from orchestrata_tools.test_generator.stub_generator import TestStubGenerator

if TYPE_CHECKING:
    from orchestrata_tools.models.ast_models import ClassInfo, MethodInfo
    from orchestrata_tools.parser.indexer import ASTIndex
    from orchestrata_tools.test_analyzer.matcher import TestMatcher


class CombinedTestGenerator:
    """Generate comprehensive test suites using all available strategies."""

    def __init__(self, index: ASTIndex, matcher: TestMatcher | None = None):
        self.index = index
        self.matcher = matcher
        self.stub_gen = TestStubGenerator()
        self.iface_gen = InterfaceTestGenerator()
        self.branch_gen = BranchTestGenerator()
        self.behavior_gen = BehavioralTestGenerator()

    def generate_for_method(self, method: MethodInfo) -> dict[str, str]:
        """Generate all applicable tests for a single method."""
        result = {}
        result["stub"] = self.stub_gen.generate_stub(method)
        branch = self.branch_gen.generate_for_method(method)
        if branch:
            result["branch"] = branch
        behavioral = self.behavior_gen.generate_from_docstring(method)
        if behavioral:
            result["behavioral"] = behavioral
        return result

    def generate_for_class(self, cls: ClassInfo) -> dict[str, str]:
        """Generate all applicable tests for a class."""
        result = {}
        if cls.is_protocol:
            result["interface"] = self.iface_gen.generate_for_protocol(cls)
        elif cls.is_abstract:
            result["interface"] = self.iface_gen.generate_for_abc(cls)
        else:
            result["stub"] = self.stub_gen.generate_class_stubs(cls)

        already_stubbed = "stub" in result
        for method in cls.methods:
            if method.name.startswith("_"):
                continue
            method_tests = self.generate_for_method(method)
            for key, val in method_tests.items():
                if key == "stub" and already_stubbed:
                    # generate_class_stubs() above already emitted a stub
                    # for every public method of a plain class; re-merging
                    # the per-method stub here duplicated every generated
                    # test. Protocol/ABC classes never pre-seed "stub", so
                    # they still get one from generate_for_method here.
                    continue
                if key not in result:
                    result[key] = ""
                result[key] += val + "\n"
        return result

    def generate_for_gaps(self) -> dict[str, str]:
        """Generate tests for all uncovered public methods."""
        if self.matcher is None:
            raise ValueError("matcher required for gap generation")

        untested = self.matcher.find_untested_methods(self.index)
        files: dict[str, list[str]] = {}

        for method in untested:
            stub = self.stub_gen.generate_stub(method)
            module_path = method.module_path.replace(".", "/")
            filename = f"test_{module_path}.py"
            if filename not in files:
                files[filename] = [
                    '"""Generated test stubs for uncovered methods."""',
                    "",
                    "import pytest",
                    "",
                ]
            files[filename].append(stub)

        return {k: "\n".join(v) for k, v in files.items()}