Skip to content

Add unit tests for osism/main.py (OsismApp entry point) - #2382

Merged
ideaship merged 2 commits into
mainfrom
implement/issue-2361-test-main
Jul 1, 2026
Merged

Add unit tests for osism/main.py (OsismApp entry point)#2382
ideaship merged 2 commits into
mainfrom
implement/issue-2361-test-main

Conversation

@berendt

@berendt berendt commented Jun 16, 2026

Copy link
Copy Markdown
Member

Summary

Adds tests/unit/test_main.py, the first coverage for osism/main.py — the
osism console-script entry point. The module defines OsismApp (the cliff
App subclass that wires the osism.commands entry-point namespace and
replaces loguru's default sink) and main() (the osism = osism.main:main
console script).

Closes #2361

Changes (in commit order)

  1. Add unit tests for OsismApp and main in osism/main.py — creates
    tests/unit/test_main.py:

    • OsismApp.__init__ — logging setup: logger.remove() is called once and
      logger.add() is called once with sys.stderr, level="INFO",
      colorize=True, and the <green>{time:…}</green> format string. The
      loguru logger is patched (mock_logger fixture) before every
      construction so the process-global sink is never mutated for real.
    • OsismApp.__init__ — cliff wiring: command_manager.namespace == "osism.commands", deferred_help is True, parser.description == "OSISM manager interface", construction succeeds when
      osism.main.__version__ is patched to None (the pbr fallback), and
      app.run(["--version"]) raises SystemExit(0).
    • main() — constructs the app exactly once, forwards argv unchanged,
      returns the app's exit code (42 and 0), and forwards an empty argv
      list (asserted against a mocked app only — the real app.run([])
      would enter cliff's interactive shell). A module docstring documents the
      frozen argv=sys.argv[1:] default so every test passes argv explicitly.
  2. Exclude main entry guard in main.py from coverage — adds
    # pragma: no cover to the if __name__ == "__main__": guard. That line
    invokes the real main()/app.run() with the process argv and cannot be
    exercised in-process, so without the exclusion coverage tops out at
    ~94.4%. With it, pytest --cov=osism.main reaches 100%, satisfying the
    ≥ 95 % Definition of Done. This commit is separable if a maintainer prefers
    a [coverage:report] exclude_lines block in setup.cfg instead.

Verification

  • flake8 tests/unit/test_main.py osism/main.py — clean.
  • black --check tests/unit/test_main.py osism/main.py — clean.
  • pytest is left to the Zuul python-osism-unit-tests job per the repo's
    local workflow (pipenv venv lives outside the tree).

Notes

  • The issue describes construction as "cheap" because cliff/stevedore loads
    command modules lazily. With the pinned stevedore==5.8.0, CommandManager
    actually imports the osism.commands.* tree eagerly; construction still
    succeeds in the unit environment (main deps installed, tests/conftest.py
    stubs ansible, stevedore skips per-plugin import errors), so every
    assertion holds. The test comments avoid repeating the "cheap construction"
    claim.

🤖 Generated with Claude Code

@berendt
berendt force-pushed the implement/issue-2361-test-main branch from fa2f7a6 to 03f8961 Compare June 16, 2026 06:55
@berendt
berendt marked this pull request as ready for review June 16, 2026 06:56

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - I've reviewed your changes and they look great!


Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

berendt added 2 commits June 16, 2026 09:31
Cover the osism console-script entry point, which had no tests:

- OsismApp.__init__: loguru's default handler is removed and a single
  stderr sink is added with level INFO, colorize, and the green-time
  format; the cliff App is wired to the osism.commands namespace, with
  deferred help, the "OSISM manager interface" description, a version
  passed through from the package (including the None fallback), and a
  working --version action that exits 0.
- main: constructs the app once, forwards argv unchanged, and returns
  the app's exit code; the empty-argv edge is checked against a mocked
  app only.

The loguru logger is patched before every construction so the
process-global sink configuration is never mutated by the suite.

Assisted-by: Claude:claude-opus-4-8
Signed-off-by: Christian Berendt <berendt@osism.tech>
The "if __name__ == '__main__'" script entry calls the real main()
(and thus app.run with the process argv), so it cannot be exercised
from an in-process unit test. Mark it "# pragma: no cover" so
"pytest --cov=osism.main" reaches 100% and meets the >= 95% bar.

Assisted-by: Claude:claude-opus-4-8
Signed-off-by: Christian Berendt <berendt@osism.tech>
@berendt
berendt force-pushed the implement/issue-2361-test-main branch from 03f8961 to a30eb6d Compare June 16, 2026 07:31
@berendt

berendt commented Jun 16, 2026

Copy link
Copy Markdown
Member Author

Fix Report (iteration 1)

Per check

  • python-osism-unit-tests
    • Category: test
    • Root cause: test_version_option_exits_zero asserted "osism" in capsys.readouterr().out, but cliff renders the --version line as "{App.NAME} {version}" where App.NAME = os.path.splitext(os.path.basename(sys.argv[0]))[0] (cliff app.py:60, used verbatim at app.py:147). Under the test runner sys.argv[0] is the pytest binary, so the output is "pytest <version>" and never contains "osism". The assertion encoded a false assumption — "osism" only appears when the program is launched via the osism console script, not under pytest.
    • Fix: tests/unit/test_main.py — import __version__ from osism.main and assert str(__version__) in capsys.readouterr().out (the package version main.py actually wires into the App via version=__version__), with a comment explaining cliff's prog-name behavior. Production main.py is correct and unchanged.
    • Local verification: not reproducible in this environment — the only venv carrying the deps (/Users/berendt/tmp/venv) has a dangling python symlink to a removed homebrew interpreter, and no other interpreter has cliff/loguru/pbr; consistent with the project's "let CI run tests" convention. Diagnosis was confirmed by reading the exact cliff (app.py:60,147) and argparse (_VersionAction.__call__ → prints "{App.NAME} {version}" to stdout, then exits 0) sources. Relying on CI for the final green run.
    • Regression test: n/a — the fix is inside the test code itself.

Diff summary

  • Files: tests/unit/test_main.py
  • Approx lines added/removed: +5/-1
  • Commit strategy: folded into f2d96b3 "Add unit tests for OsismApp and main in osism/main.py" (now 6843cd2) via git commit --fixup + git rebase --autosquash; the change lives entirely in the file that commit introduced.

Status

STATUS: DONE_WITH_CONCERNS

(Fix is high-confidence and grounded in direct source reading of cliff/argparse, but I could not execute pytest locally to see green — the local venv is broken and no interpreter here has the dependencies. The one concern a human should note: local verification was impossible, so confirmation depends on the next CI run.)


Fix report generated by planwerk-review fix with Claude Code

@berendt
berendt requested a review from ideaship June 16, 2026 08:18
@berendt berendt moved this from New to Ready for review in Human Board Jul 1, 2026
@ideaship
ideaship merged commit 0200a64 into main Jul 1, 2026
3 checks passed
@ideaship
ideaship deleted the implement/issue-2361-test-main branch July 1, 2026 09:56
@github-project-automation github-project-automation Bot moved this from Ready for review to Done in Human Board Jul 1, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Archived in project

Development

Successfully merging this pull request may close these issues.

Unit tests for osism/main.py — OsismApp entry point

3 participants