refactor(tests): derive the two duplicated test helpers into tests/helpers.py (#4276)
CI & Build / Python lint (push) Successful in 2s
CI & Build / Plugin hooks (push) Successful in 9s
CI & Build / integration (push) Successful in 48s
CI & Build / TypeScript typecheck (push) Successful in 52s
CI & Build / Python tests (push) Successful in 1m37s
CI & Build / Build & push image (push) Successful in 15s
CI & Build / Python lint (push) Successful in 2s
CI & Build / Plugin hooks (push) Successful in 9s
CI & Build / integration (push) Successful in 48s
CI & Build / TypeScript typecheck (push) Successful in 52s
CI & Build / Python tests (push) Successful in 1m37s
CI & Build / Build & push image (push) Successful in 15s
Derive-first, ahead of the tests shape pay-down. An AST pass over all 2628 definitions under `tests/` found exactly three helper bodies duplicated across files. Two are real copies and are consolidated here; the third is not, and is left alone. `need_tools(*tools)` — byte-identical in three hook-test modules, each skipping when `jq`/`awk`/`git` is absent from PATH. Now snippet #4277. The `import shutil` each file carried existed only to serve it and goes with it. `rule_row(rule_id)` — byte-identical in two integration modules, reading a Rule back through a SEPARATE session so the assertion is about what Postgres holds rather than what the writing session's identity map remembers. Now snippet #4278. Its imports are lazy, because `tests/helpers.py` is imported by unit tests that have no database, which is the same reason `plugin_config` defers its service imports. NOT consolidated: `_side(uid, k, d="")` in test_services_plugin_context and test_write_path_trigger. The body is identical but it closes over a module-local `stored` dict, so it is not self-contained and "moving" it would mean inventing a parameter neither call site wants. That is convention plumbing — two tests independently writing the same one-line side_effect — and it is dismissed in the ledger rather than lifted. Worth recording for the next pass: a repeated NAME is not a family. `_row` is defined in five modules and only two of those share a body; the other three (test_list_rows_brief, test_calibration_stamp, test_shape_ledger) build entirely different objects. Grouping by name would have consolidated three things that have nothing in common. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01821k5B3Ysecp9fNYs92Kuy
This commit is contained in:
@@ -418,3 +418,55 @@ def writepath_cfg(**over):
|
||||
}
|
||||
cfg.update(over)
|
||||
return cfg
|
||||
|
||||
|
||||
def need_tools(*tools):
|
||||
"""Skip unless every named executable is on PATH.
|
||||
|
||||
For the hook tests, which drive `plugin/hooks/*.sh` through a real shell
|
||||
and therefore depend on whatever that shell reaches for — `jq`, `awk`,
|
||||
`git`, `curl`. Those are present on the CI image and routinely absent from
|
||||
a developer's box, and the honest answer there is "not exercised", not a
|
||||
failure: a red test would say the hook is broken when nothing about the
|
||||
hook was ever run.
|
||||
|
||||
A skip rather than a stub on purpose. Stubbing `jq` would test the stub —
|
||||
these tests exist precisely because the shell's behaviour is the thing in
|
||||
question (#2932), so anything short of the real tool proves nothing.
|
||||
|
||||
Consolidated 2026-09-21 from three byte-identical copies in
|
||||
test_contract_around_the_change, test_hook_json_reader and
|
||||
test_session_slippage_readout.
|
||||
"""
|
||||
import shutil
|
||||
|
||||
import pytest
|
||||
|
||||
for t in tools:
|
||||
if shutil.which(t) is None:
|
||||
pytest.skip(f"hook runtime tool {t!r} not installed")
|
||||
|
||||
|
||||
async def rule_row(rule_id: int):
|
||||
"""Read a Rule straight from Postgres, outside whatever session the code
|
||||
under test used.
|
||||
|
||||
THE POINT IS THE SEPARATE SESSION. An integration test that asserts on the
|
||||
object the service just returned is asserting on that session's identity
|
||||
map, which can hold a value the database never accepted — a column the
|
||||
write never reached, a default the ORM supplied rather than the schema.
|
||||
Opening a new session forces a real read and is the only way `verify_with`
|
||||
being NULL rather than "" is distinguishable at all (milestone 312).
|
||||
|
||||
Import is lazy because this module is imported by unit tests that have no
|
||||
database and must not pay for one — the same reason `plugin_config` defers
|
||||
its service imports.
|
||||
|
||||
Consolidated 2026-09-21 from two byte-identical copies in
|
||||
test_integration_rule_move and test_integration_rule_verification.
|
||||
"""
|
||||
from scribe.models import async_session
|
||||
from scribe.models.rulebook import Rule
|
||||
|
||||
async with async_session() as s:
|
||||
return await s.get(Rule, rule_id)
|
||||
|
||||
Reference in New Issue
Block a user