Add a documentation-only test layout inventory for the first low-risk split of the flat tests directory. Records the current 28-file area_cli set, including tests/test_research_cli_status.py, and documents validation/non-goals for the future mechanical move. Closes #3712 Part of #2523
8.4 KiB
Test Layout Inventory
Purpose
Inventory for the first low-risk split of the flat tests/ directory
(issue #3712, parent #2523). This document only records what should move
first and why; it moves nothing. The actual move is a separate, mechanical
PR that relocates the listed files verbatim and changes no test content.
The target layout and category definitions come from
TESTING_STANDARD.md; the collection-time markers
come from _taxonomy.py, which classifies by filename
tokens only (paths are ignored, except the tests/helpers/ rule). A file
keeps its area_*/sub_* markers when moved into a subdirectory, and
conftest.py discovers marker names recursively (rglob), so a move does not
disturb marker registration or focused selection.
Current low-risk candidate groups
Groups whose tests need no route/app setup and no real DB/session setup:
- CLI / script tests (
area_cli, 28 files) - loadscripts/entry points viatests.helpers.cli_loader.load_script; DB access is stubbed withtests.helpers.db_stubs(SessionLocalis a plain stub attribute). NoTestClient, no FastAPI app import, no SQLite files. - Helper self-tests (
area_helpers) - e.g.test_helpers_import_state.py,test_db_stubs_helper.py. Safe but tiny (two files), and they test the shared helpers from the #3685 audit (merged) that the rest of the suite depends on; little payoff as a first slice. - Pure unit / parsing tests (
area_unit) -*_nonstring.py,*_nondict.py, parsing tests. Large and heterogeneous; some touch provider/session modules, so the boundary is less crisp. - Static checks - e.g.
test_readme_ascii_fenced.py,test_docs_no_orphan_images.py. Safe but tiny anduncategorizedin the taxonomy, so a move buys little and matches no existing marker.
Not candidates for the first move (per #3712 guidance): security/owner-scope tests, route/API tests, DB/session-heavy tests, auth/session concurrency tests, and the taxonomy/runner infrastructure tests that changed recently (#3491, #3556, #3659, #3711).
Recommended first move
CLI / script tests → tests/cli/
Why this group over the alternatives:
- Lowest coupling: every file imports only the script under test (via
cli_loader) plustests.helpersstubs - no app, no routes, no real DB. - Crisp, machine-checkable boundary: the set is exactly the files classified
area_cliby_taxonomy.py, so before/after selection counts can be compared mechanically. - Already the planned target dir for this category in
TESTING_STANDARD.md(tests/cli/). - Absolute imports (
from tests.helpers...) and unique basenames mean no import-order or module-name collisions after the move. - Lower risk than helper self-tests (tiny group, little payoff), unit tests (fuzzy boundary), or anything security/route/session-shaped.
Files included in the first move
The 28 files classified area_cli (verified against _taxonomy.py):
Note: this inventory was refreshed against current dev after tests/test_research_cli_status.py was added to the area_cli set.
tests/test_calendar_cli_name.pytests/test_contacts_cli_rows.pytests/test_cookbook_cli_state.pytests/test_docs_cli_content_length.pytests/test_gallery_cli_album_count.pytests/test_gallery_cli_preview.pytests/test_logs_cli_resolve_nonstring.pytests/test_mail_cli_read_empty_fetch.pytests/test_mail_cli_recipients.pytests/test_mcp_cli_env_serialize.pytests/test_mcp_cli_json.pytests/test_memory_cli_rows.pytests/test_notes_cli_items.pytests/test_personal_cli_rows.pytests/test_preset_cli_invalid_entries.pytests/test_preset_cli_set_corrupt_entry.pytests/test_preset_cli_store.pytests/test_research_cli_preview.pytests/test_research_cli_status_filter.pytests/test_research_cli_status.pytests/test_research_cli_store.pytests/test_sessions_cli.pytests/test_signature_cli_export.pytests/test_skills_cli_preview.pytests/test_skills_cli_rows.pytests/test_tasks_cli_preview.pytests/test_theme_cli_store.pytests/test_webhook_cli_mask.py
Files intentionally excluded
tests/test_backup_cli_security.py- classifies asarea_security(security outranks cli in the taxonomy); moving it intotests/cli/would make the directory disagree with its marker. It belongs with the security group in a later phase.tests/test_run_focus.py,tests/test_taxonomy.py- taxonomy/runner infrastructure tests, recently changed (#3556, #3659); they also pin flat-layout paths (e.g.tests/test_auth_config_lock_concurrency.pyintest_run_focus.py), so they stay put.- Script-like but
uncategorizedfiles -test_pr_blocker_audit.py,test_update_database_script.py,test_windows_update_script.py,test_setup_admin_user.py,test_amd_gpu_check_args.py,test_hwfit_*.py. They exercisescripts/too, but moving them would maketests/cli/diverge from thearea_climarker set. Reclassify or move them in a later, separate slice. - Everything else (security, routes, services, unit, js, helpers) - out of scope for the first move by design.
How this was verified
Read-only checks, run from the repo root on this branch. Note the real API is
classify_test_path (there is no classify_test_file).
# Compute the area_cli set and confirm test_backup_cli_security.py is
# area_security. Expected: 28 files, then "security".
.venv/bin/python - <<'PY'
from pathlib import Path
from tests._taxonomy import classify_test_path
cli = [p for p in sorted(Path("tests").glob("test_*.py"))
if classify_test_path(p).area == "cli"]
print(len(cli))
for p in cli:
print(p)
print(classify_test_path("tests/test_backup_cli_security.py").area)
PY
# Coupling check across the CLI files. Expected: the only hits are
# "SessionLocal" as stub attribute names passed to tests.helpers.db_stubs;
# no TestClient, FastAPI, create_app, sqlite, or dependency_overrides.
rg -n "TestClient|FastAPI|create_app|SessionLocal|sqlite|dependency_overrides" \
tests/test_*cli*.py tests/test_sessions_cli.py
# Hard-coded flat paths to the exact CLI files outside tests/. Expected: no matches.
.venv/bin/python - <<'PY2' > /tmp/area_cli_paths.txt
from pathlib import Path
from tests._taxonomy import classify_test_path
for path in sorted(Path("tests").glob("test_*.py")):
if classify_test_path(path).area == "cli":
print(path)
PY2
rg -n -F -f /tmp/area_cli_paths.txt .github scripts docs \
tests/README.md tests/TESTING_STANDARD.md pyproject.toml 2>/dev/null || true
Also checked by reading the code: tests/conftest.py registers sub-markers
from a recursive rglob scan, and tests/_taxonomy.py classifies by filename
tokens only (plus the tests/helpers/ directory rule), so the markers of the
28 files do not change when they move into tests/cli/.
Validation for the future move PR
Run with the project venv (.venv/bin/python); system python3 may miss
pinned deps. Before the move, record the baseline; after, compare:
# Selection must match the 28 files before and after the move.
.venv/bin/python tests/run_focus.py --dry-run --area cli
.venv/bin/python -m pytest -m area_cli -q
# Moved files pass when targeted directly.
.venv/bin/python -m pytest tests/cli/ -q
# Whole-suite collection still succeeds (catches import/path breakage).
.venv/bin/python -m pytest --collect-only -q
# Taxonomy/runner infrastructure is unaffected.
.venv/bin/python -m pytest tests/test_taxonomy.py tests/test_run_focus.py -q
# No stale flat-path references to the moved files. Expected: no matches
# outside tests/cli/ itself.
.venv/bin/python - <<'PY2' > /tmp/area_cli_paths.txt
from pathlib import Path
from tests._taxonomy import classify_test_path
for path in sorted(Path("tests").glob("test_*.py")):
if classify_test_path(path).area == "cli":
print(path)
PY2
rg -n -F -f /tmp/area_cli_paths.txt .github scripts docs \
tests/README.md tests/TESTING_STANDARD.md pyproject.toml 2>/dev/null || true
Pass criteria: identical test counts for -m area_cli before/after, zero
collection errors, and no changes outside the moved files.
Non-goals
- No file moves, renames, or deletions in this PR.
- No changes to
conftest.py,_taxonomy.py,run_focus.py, helpers, markers, CI workflows, or production code. - No recommendation to split the whole suite at once; later groups get their own inventory-then-move slices.