test: audit suite for meaningfulness, reach 100% coverage #66

Merged
trex Bot merged 2 commits from dev-test-suite-audit into main 2026-11-03 16:47:04 -06:00

Why

The suite looked green but proved little:

  • 8 empty tests (pass bodies) covering is_preme, is_cat_ignored, is_tracker_blank, is_protected_tracker, is_not_protected_tracker, is_protected_under_ratio, is_protected_over_ratio, is_non_protected_tor.
  • 2 tests made live network calls to https://thunder.jonb.io/ping/<uuid> — a real healthcheck endpoint with production side effects.
  • test_write_csv only asserted os.path.exists.
  • CI never ran the main suite — .gitea/workflows/test.yml only ran test_komodo_deploy.

What changed

File Change
qbt_fakes.py new — shared fakes: FakeQbt, FakeQbtClient, FakeAPIError, StubPushover, FailingPushover, torrent() factory
test_qlist.py new — tag/tracker/ratio predicates, boundary cases
test_qprocess.py new — file deletion, dry-run, error paths
test_qlogging_extra.py new — summary formatting, pushover/apprise dispatch, logging setup
test_apprise.py new — env/TOML config resolution, send paths
test_qbt.py new — loads hyphenated qbit-maid.py via importlib, covers the __main__ entry point
test_qbitmaid.py rewritten — pipeline tests against FakeQbt
test_write_csv.py rewritten — header-once, append, round-trip content in temp dirs
.coveragerc new — source scope + report config
.gitea/workflows/test.yml new unit-tests job: installs requests qbittorrent-api coverage (mirroring the Dockerfile) and runs the full suite with --fail-under=100

Verification

Ran 142 tests in 14.035s
OK
Name               Stmts   Miss  Cover
AppriseClient.py      35      0   100%
qbit-maid.py          95      0   100%
qlist.py              63      0   100%
qlogging.py          188      0   100%
qprocess.py           59      0   100%
TOTAL                440      0   100%

Mutation check: 15/15 caught (boundary flips, guard removal, separator/emoji/html/priority changes, csv header, delete-files flag).

Suite time for the unit tests dropped from ~14s to 0.035s once the live network calls were stubbed.

Bugs found — pinned, not fixed

Each is covered by a test with a KNOWN BUG docstring so a fix will flip the test:

  1. qbit-maid.py:92 — self.ignored_tags = self.config["ignored_domains"]; the [ignored_tags] section is never read, so tag-based ignoring silently does nothing.
  2. qlist.is_ignored_tag — substring match, not tag match: 'ipt' matches 'iptorrents', 'save' matches 'unsaved'.
  3. qbit-maid.py:120 — self.po.send_message(...) but the vendored pushover module has no module-level send_message; a qBittorrent auth failure raises AttributeError instead of notifying.
  4. qlogging.list_first_tor — indexes torrent_list[0] unconditionally; an instance with zero torrents raises IndexError and aborts the run before any summary is sent.
  5. qlist.build_tor_list — the blank-tag block and the tracker branch both call torrents_add_tags, so non-premature new torrents get tagged twice.
  6. AppriseClient.__init__ — bare except: KeyError swallows the DOCKER KeyError and skips the rest of the try block, so toml_path is only read when DOCKER and all five env vars are set.
  7. qlogging.setup_logging — removes old handlers without closing them, leaking a file descriptor per call.

Also noted, not changed: config.toml.example is missing the [telemetry] section that qbit-maid.py:82 requires, so a config copied from the example crashes on startup; and requirements.txt pins qbittorrent_api==2022.5.32, which cannot import on Python 3.13 (the Dockerfile ignores it and installs unpinned).

Happy to fix any of these — say which.

## Why The suite looked green but proved little: - **8 empty tests** (`pass` bodies) covering `is_preme`, `is_cat_ignored`, `is_tracker_blank`, `is_protected_tracker`, `is_not_protected_tracker`, `is_protected_under_ratio`, `is_protected_over_ratio`, `is_non_protected_tor`. - **2 tests made live network calls** to `https://thunder.jonb.io/ping/<uuid>` — a real healthcheck endpoint with production side effects. - `test_write_csv` only asserted `os.path.exists`. - **CI never ran the main suite** — `.gitea/workflows/test.yml` only ran `test_komodo_deploy`. ## What changed | File | Change | |---|---| | `qbt_fakes.py` | new — shared fakes: `FakeQbt`, `FakeQbtClient`, `FakeAPIError`, `StubPushover`, `FailingPushover`, `torrent()` factory | | `test_qlist.py` | new — tag/tracker/ratio predicates, boundary cases | | `test_qprocess.py` | new — file deletion, dry-run, error paths | | `test_qlogging_extra.py` | new — summary formatting, pushover/apprise dispatch, logging setup | | `test_apprise.py` | new — env/TOML config resolution, send paths | | `test_qbt.py` | new — loads hyphenated `qbit-maid.py` via `importlib`, covers the `__main__` entry point | | `test_qbitmaid.py` | rewritten — pipeline tests against `FakeQbt` | | `test_write_csv.py` | rewritten — header-once, append, round-trip content in temp dirs | | `.coveragerc` | new — source scope + report config | | `.gitea/workflows/test.yml` | new `unit-tests` job: installs `requests qbittorrent-api coverage` (mirroring the Dockerfile) and runs the full suite with `--fail-under=100` | ## Verification ``` Ran 142 tests in 14.035s OK Name Stmts Miss Cover AppriseClient.py 35 0 100% qbit-maid.py 95 0 100% qlist.py 63 0 100% qlogging.py 188 0 100% qprocess.py 59 0 100% TOTAL 440 0 100% ``` Mutation check: **15/15 caught** (boundary flips, guard removal, separator/emoji/html/priority changes, csv header, delete-files flag). Suite time for the unit tests dropped from ~14s to **0.035s** once the live network calls were stubbed. ## Bugs found — pinned, not fixed Each is covered by a test with a `KNOWN BUG` docstring so a fix will flip the test: 1. `qbit-maid.py:92` — `self.ignored_tags = self.config["ignored_domains"]`; the `[ignored_tags]` section is never read, so tag-based ignoring silently does nothing. 2. `qlist.is_ignored_tag` — substring match, not tag match: `'ipt'` matches `'iptorrents'`, `'save'` matches `'unsaved'`. 3. `qbit-maid.py:120` — `self.po.send_message(...)` but the vendored `pushover` module has no module-level `send_message`; a qBittorrent auth failure raises `AttributeError` instead of notifying. 4. `qlogging.list_first_tor` — indexes `torrent_list[0]` unconditionally; an instance with zero torrents raises `IndexError` and aborts the run before any summary is sent. 5. `qlist.build_tor_list` — the blank-tag block and the tracker branch both call `torrents_add_tags`, so non-premature new torrents get tagged twice. 6. `AppriseClient.__init__` — bare `except: KeyError` swallows the `DOCKER` KeyError and skips the rest of the try block, so `toml_path` is only read when `DOCKER` **and** all five env vars are set. 7. `qlogging.setup_logging` — removes old handlers without closing them, leaking a file descriptor per call. Also noted, not changed: `config.toml.example` is missing the `[telemetry]` section that `qbit-maid.py:82` requires, so a config copied from the example crashes on startup; and `requirements.txt` pins `qbittorrent_api==2022.5.32`, which cannot import on Python 3.13 (the Dockerfile ignores it and installs unpinned). Happy to fix any of these — say which.
trex Bot added 1 commit 2026-11-03 15:35:04 -06:00
test: audit suite for meaningfulness, reach 100% coverage
test / unit-tests (push) Failing after 4s
test / komodo-deploy-script (push) Successful in 16s
test / unit-tests (pull_request) Failing after 3s
test / komodo-deploy-script (pull_request) Successful in 16s
ci / docker (push) Successful in 1m37s
925f9619be
The suite had 8 empty `pass` tests, two tests that made live network
calls to a production healthcheck endpoint, and a write_csv test that
only asserted the file existed. CI ran only test_komodo_deploy, so the
main suite never executed at all.

- Split tests by module: test_qlist, test_qprocess, test_qlogging_extra,
  test_apprise, test_qbt; rewrote test_qbitmaid and test_write_csv.
- qbt_fakes.py: shared fakes (FakeQbt, FakeQbtClient, FakeAPIError,
  StubPushover, FailingPushover, torrent factory).
- test_qbt.py loads the hyphenated qbit-maid.py via importlib and covers
  the __main__ entry point.
- Replaced the live-network tests with a RecordingRequests stub
  (suite time 14s -> 0.035s).
- .coveragerc + CI unit-tests job running the full suite with
  --fail-under=100.

127 unit tests, 100% statement coverage on all 5 first-party modules
(440 stmts), 15/15 mutations caught.

Seven pre-existing bugs are pinned by tests with KNOWN BUG docstrings
and left unfixed pending a decision.
trex Bot added 1 commit 2026-11-03 15:35:39 -06:00
ci: fix unit-tests job on the Debian-packaged runner pip
ci / docker (push) Successful in 23s
test / unit-tests (pull_request) Successful in 5s
test / komodo-deploy-script (pull_request) Successful in 16s
test / unit-tests (push) Successful in 6s
test / komodo-deploy-script (push) Successful in 17s
ab46ea1fb2
The runner image ships pip 24.0 from the Debian package, so
`pip install --upgrade pip` aborts with "Cannot uninstall pip 24.0,
RECORD file not found" and the job failed after 4s. Drop the upgrade
and install with --break-system-packages.
trex Bot merged commit 0c02cd40b0 into main 2026-11-03 16:47:04 -06:00
trex Bot deleted branch dev-test-suite-audit 2026-11-03 16:47:04 -06:00
Sign in to join this conversation.