.. module:: Testing Testing ========================= The project ships with two complementary test suites that together give 100% statement and branch coverage of ``core_extensions``. .. code-block:: text tests/ ├── unit/ │ ├── cli/ │ │ └── tests_aliased_group.py # AliasedGroup unit tests │ └── decorators/ │ └── tests_retry.py # SimpleRetry unit tests └── integration/ ├── test_cli_integration.py # AliasedGroup end-to-end tests └── test_retry_integration.py # SimpleRetry end-to-end tests Unit Tests ----------------------------------------------------------------------------------- Unit tests cover each class in isolation. External collaborators (loggers, tenacity internals) are replaced with mocks so that every branch of the production code can be exercised independently and deterministically. **AliasedGroup** (``tests/unit/cli/tests_aliased_group.py``) Covers every public method of ``AliasedGroup``: ``command()``, ``group()``, ``add_command()``, ``format_commands()``, and ``get_command()``. Edge cases include bare-decorator usage (no parentheses), alias collision detection, hidden commands, subgroups with ``cls=AliasedGroup``, and the full ``--help`` output format. **SimpleRetry** (``tests/unit/decorators/tests_retry.py``) Covers ``__init__`` validation (conflicting kwargs, ``max_delay < base_delay``), ``create_decorator`` (empty tuple guard), and the ``after_error`` / ``before_sleep`` callbacks in isolation using ``Mock`` objects. Run unit tests: .. code-block:: bash pytest tests/unit/ Run a single unit test file: .. code-block:: bash pytest tests/unit/cli/tests_aliased_group.py pytest tests/unit/decorators/tests_retry.py .. note:: Unit test files follow a ``tests_*.py`` naming convention. The project's ``pyproject.toml`` configures pytest with ``python_files = ["tests_*.py", "test_*.py"]`` so that both patterns are discovered automatically when passing a directory path. Integration Tests ----------------------------------------------------------------------------------- Integration tests exercise the full public API end-to-end with real dependencies — Click's ``CliRunner`` for the CLI and tenacity's live retry machinery for the decorator — using no mocks of internal collaborators. **AliasedGroup** (``tests/integration/test_cli_integration.py``) Builds a realistic multi-command CLI application and drives it through Click's ``CliRunner``: - Canonical command names and every registered alias - Arguments and options passed through aliases without mutation - Subgroup accessed via parent alias (``i``, ``infrastructure``) with its own nested commands reachable by their nested aliases (``prov``, ``down``) - Commands registered imperatively via ``add_command()`` with aliases - ``--help`` output: aliases rendered inline, hidden commands absent, alias annotation on the correct row - Duplicate alias registration raises ``ValueError`` - Unknown command returns a non-zero exit code **SimpleRetry** (``tests/integration/test_retry_integration.py``) Runs the complete tenacity retry cycle against live decorated functions: - Transient failure → success, verifying attempt count - All attempts exhausted: ``reraise=True`` propagates the original exception; ``reraise=False`` raises ``tenacity.RetryError`` - Multiple exception types in the tuple each trigger retry independently - Unregistered exception type propagates immediately without retry - Live log records checked for exception type, message, function name, and upcoming sleep duration (not cumulative) - ``reraise`` override at decoration time overrides the instance default - Two decorators from the same ``SimpleRetry`` instance are independent - Return value preserved on a first-attempt success Run integration tests: .. code-block:: bash pytest tests/integration/ Run a single integration test file: .. code-block:: bash pytest tests/integration/test_cli_integration.py pytest tests/integration/test_retry_integration.py Running All Tests ----------------------------------------------------------------------------------- Run both suites with a single command: .. code-block:: bash pytest tests/ Or using the helper script: .. code-block:: bash python manager.py run-tests Coverage ----------------------------------------------------------------------------------- Generate a branch-coverage report for all ``core_extensions`` modules: .. code-block:: bash pytest tests/ \ --cov=core_extensions \ --cov-branch \ --cov-report=term-missing Or using the helper script: .. code-block:: bash python manager.py run-coverage The ``@t.overload`` stubs are excluded from coverage tracking via ``.coveragerc`` (``exclude_also = @.*overload``) because overload definitions are never executed at runtime — they exist solely for static type checkers. Expected output: .. code-block:: text Name Stmts Miss Branch BrPart Cover -------------------------------------------------------------------------- core_extensions/__init__.py 0 0 0 0 100% core_extensions/cli/__init__.py 2 0 0 0 100% core_extensions/cli/aliased_group.py 87 0 48 0 100% core_extensions/decorators/__init__.py 2 0 0 0 100% core_extensions/decorators/retry.py 41 0 12 0 100% -------------------------------------------------------------------------- TOTAL 132 0 60 0 100%