* Make in-venv pip-fallback test independent of the runner's environment test_pip_install_fallback_chain_propagates_failure_in_venv simulated the in-venv case by probing the real interpreter (sys.prefix != sys.base_prefix). That assumes the test runner is itself inside a venv. CI runs pytest with no venv, so venv_check reported not-in-venv, the negated guard flipped, the --user branch fired, and the assertion failed. Make venv_check exit 0 directly to simulate the in-venv condition deterministically, mirroring the outside-venv companion test. * Stop agent-tool import shims from leaking into the admin-gate test test_function_call_non_object_args and test_unknown_tool_calls stub heavy DB/auth deps at import time to load the real agent-tool stack, but they popped src.tool_execution and left core.auth stubbed without restoring. Popping and re-importing src.tool_execution rebinds the src package's tool_execution attribute, so test_edit_file's later 'import src.tool_execution as te' resolved to a different module object than the one execute_tool_block lives in. The monkeypatch on _owner_is_admin then missed, the non-admin edit_file gate never fired, and the edit went through (exit_code 0). Stop touching src.tool_execution and restore the heavy stubs after import. Verified the full suite is green on Linux (Python 3.11, matching CI). --------- Co-authored-by: Alexandre Teixeira <111787685+alteixeira20@users.noreply.github.com>
Test Suite Notes
Purpose
This file documents the shared test helpers and the review expectations that go
with them. The suite is being refactored incrementally, so this is a working
reference for that effort - not a claim that the suite is already fully
organized. Read it before adding a new helper or before reviewing a PR that
touches tests/helpers/.
For the broader rules - test taxonomy, determinism/isolation rules, the
behavioral-vs-source-text policy, and helper/factory extraction rules - see
TESTING_STANDARD.md. This file is the concrete helper
reference; that file is the standard the refactor works toward.
Running focused subsets (taxonomy markers)
tests/conftest.py tags every test at collection time with two markers derived
from its filename by tests/_taxonomy.py: an area_* marker (e.g.
area_security) and a finer sub_* marker (e.g. sub_owner_scope). This adds
markers only - it moves no files and changes no test behavior. Use them to run a
focused slice:
python3 -m pytest -m area_security
python3 -m pytest -m "area_services and sub_cookbook"
Areas are security, routes, services, cli, js, helpers, unit, and
uncategorized. Classification is conservative and token-based: a file that
matches no area keyword falls back to area_uncategorized with its filename as
the sub-area. The area_* names are registered in pyproject.toml; the dynamic
sub_* names are registered before collection by pytest_configure in
tests/conftest.py, so unknown-mark warnings still flag genuine typos.
For common focused runs, use tests/run_focus.py. It validates area and
sub-area names, accepts sub-areas with or without the sub_ prefix, and passes
extra pytest arguments after --:
python3 tests/run_focus.py --area security
python3 tests/run_focus.py --area services --sub-area cookbook
python3 tests/run_focus.py --sub-area sub_cookbook
python3 tests/run_focus.py --keyword taxonomy
python3 tests/run_focus.py --last-failed
python3 tests/run_focus.py --dry-run --area services --sub-area cookbook
python3 tests/run_focus.py --area services -- --maxfail=1 -q
Core principles
- Keep PRs small and homogeneous: one kind of change per PR.
- Prefer explicit local setup over hidden global fixtures.
- Avoid expanding the root
conftest.pyunless absolutely necessary. - Do not mix file moves with logic changes in the same PR.
- Do not weaken tests with
skip/xfailjust to make CI pass. - Validate the focused files you changed, plus any neighboring or order-sensitive groups they interact with.
Helper conventions
The helpers below live under tests/helpers/. They exist to remove repeated
boilerplate that already appeared across multiple tests. Reach for one only when
your test matches its intended use; do not stretch a helper to cover a new case.
tests.helpers.cli_loader.load_script
Use when a test needs to import a script under scripts/ without repeating
SourceFileLoader / importlib.util boilerplate.
- Intended for script/CLI tests that load a single file from
scripts/. - Not for arbitrary package imports - use a normal
importfor those. - When migrating an existing test to it, keep the existing stubs and assertions
unchanged. Any
sys.modulesstubs the script needs at import time must still be injected (e.g. viamonkeypatch) before callingload_script.
tests.helpers.import_state.clear_module
Use when a test must drop one cached module and its parent-package attribute before a fresh import.
- Clears
sys.modules[name]. - Clears the parent-package attribute when present.
- Good replacement for local
sys.modules.pop(...)+delattr(parent, child)blocks.
tests.helpers.import_state.preserve_import_state
Use when a test temporarily installs stubs into sys.modules and needs
deterministic cleanup afterward.
- Context manager: restores both
sys.modulesentries and parent-package attributes on exit (normal or exception). - Useful around module-level stubs or temporary imports.
- Prefer narrow, explicit module names over broad ones.
tests.helpers.import_state.clear_fake_database_modules
Use only for the guarded fake/stub database cleanup pattern.
- Preserves a real-looking
core.database(one with a string__file__). - Removes a fake/stub
core.databaseand the relatedsrc.databasestate. - Do not use as a general database reset fixture.
tests.helpers.import_state.clear_fake_endpoint_resolver_modules
Use only for the guarded fake/stub src.endpoint_resolver cleanup pattern.
- Preserves real resolver modules (those with a truthy
__file__). - Evicts fake/stub resolver modules and the dependent route modules that were cached against them.
- Accepts explicit extra dependent module names to evict alongside the defaults.
tests.helpers.sqlite_db.make_temp_sqlite
Use for the repeated file-backed temp sqlite setup in tests.
- Only constructs
(SessionLocal, engine, tmpfile)from the repeated block. - Does not patch modules and does not clean up the temp file.
- The caller must bind
SessionLocalexplicitly onto whatever module the code under test reads, and must keep the returned objects alive. - Do not use it as a general DB fixture framework.
What not to abstract yet
Some remaining patterns should stay as-is for now rather than being forced into helpers:
- Large mixed files such as security/review regression files.
- Setup-oriented
sys.modulesstub installers. - One-off custom module patching.
- DB/session/route setup, until it has been audited separately.
Validation expectations
Run validation locally before opening or approving a PR. Practical checks:
git diff --check- catch whitespace and conflict-marker errors.python3 -m py_compile <changed files>- confirm changed files compile.- Focused
pyteston the changed test files. pyteston neighboring or order-sensitive test groups that share import state with the changed files.grepfor the old boilerplate when replacing it, to confirm no stragglers remain.- A fresh audit worktree when changing the helpers themselves, so stale
__pycache__or import state cannot mask a regression.
Current roadmap
- Import-state cleanup - complete.
- Document helper conventions (this file).
- Audit fake DB /
SessionLocal/ route setup duplication. - Add tiny helpers only when the repeated semantics are clear.
- Start low-risk file moves only after helper conventions are documented.
- Avoid moving high-risk security/route regression files first.