Skip to content

test(subscription): pin auto-switch chain skips .credentials.enc import (#606) - #800

Merged
vybe merged 5 commits into
devfrom
AndriiPasternak31/issue-606-plan
May 14, 2026
Merged

test(subscription): pin auto-switch chain skips .credentials.enc import (#606)#800
vybe merged 5 commits into
devfrom
AndriiPasternak31/issue-606-plan

Conversation

@AndriiPasternak31

Copy link
Copy Markdown
Contributor

Summary

Adds a chain-level regression test for #606 that pins the SUB-003 auto-switch path against silently regressing the lifecycle.py:155 subscription_mode short-circuit.

#606 was originally fixed in #612/#663 — this PR adds the missing chain-level coverage so a future refactor of _perform_auto_switch or _restart_agent cannot silently bypass the guard.

Why this test, when a leaf test already exists

tests/unit/test_inject_assigned_credentials.py::test_subscription_mode_skips_import_with_clear_reason covers the short-circuit in isolation. It would still pass if a future refactor of _perform_auto_switch quietly stopped reaching inject_assigned_credentials at all — leaving #606 silently un-fixed.

This new test enters at the smallest entry point that still exercises the real lifecycle injection chain (subscription_auto_switch._restart_agent) and asserts:

  1. _restart_agent returns "success" (the chain ran end-to-end)
  2. import_to_agent was never awaited (the file-import path was not reached)
  3. inject_assigned_credentials returned reason == "subscription_mode" specifically — proving the line-155 short-circuit fired, NOT the #421 container_already_running skip (which does not apply on the auto-switch path because _restart_agent explicitly stops the container before start_agent_internal)

The third assertion is load-bearing: without it, a refactor that coalesced the short-circuit into the #421 branch would silently pass.

Test plan

  • uv run pytest tests/unit/test_subscription_auto_switch_no_cred_import.py -v → 1 passed
  • No production code changes — test-only PR
  • CI green on dev

Refs

Co-Authored-By: Claude noreply@anthropic.com

@AndriiPasternak31
AndriiPasternak31 marked this pull request as draft May 12, 2026 00:49
@AndriiPasternak31
AndriiPasternak31 marked this pull request as ready for review May 12, 2026 01:13
@AndriiPasternak31
AndriiPasternak31 force-pushed the AndriiPasternak31/issue-606-plan branch from ea3a129 to 191959c Compare May 12, 2026 02:04
@AndriiPasternak31
AndriiPasternak31 changed the base branch from dev to AndriiPasternak31/lint-baseline-orphan-test May 12, 2026 02:04

@vybe vybe left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This PR requires the following changes before merge:

  • Retarget base branch to dev — the current base (AndriiPasternak31/lint-baseline-orphan-test) was merged to dev as PR #796 earlier today. Change base via the GitHub UI: Edit → Base: dev.

Code quality, tests, and docs are all clean. This is the only blocker.

@AndriiPasternak31
AndriiPasternak31 changed the base branch from AndriiPasternak31/lint-baseline-orphan-test to dev May 12, 2026 22:10
@AndriiPasternak31

Copy link
Copy Markdown
Contributor Author

Hi Eugene — base is already dev (auto-retargeted when #796 landed and the intermediate branch AndriiPasternak31/lint-baseline-orphan-test was deleted). Current state: base=dev, mergeable=MERGEABLE. Re-requesting review.

AndriiPasternak31 and others added 5 commits May 14, 2026 15:14
…rt (#606)

Adds a chain-level regression test for #606 entered at
`subscription_auto_switch._restart_agent` — pins that the SUB-003
auto-switch path reaches the `lifecycle.py:155` `subscription_mode`
short-circuit and never re-enters the file-based credential import.

The existing leaf-level test
(`test_inject_assigned_credentials.py::test_subscription_mode_skips_import_with_clear_reason`)
covers the short-circuit in isolation but would not catch a future
refactor of `_perform_auto_switch` or `_restart_agent` that quietly
bypassed the guard. The new test asserts on `reason == "subscription_mode"`
specifically (not just `status == "skipped"`) so a refactor that moved the
short-circuit into the `#421` `container_already_running` branch would
fail explicitly.

Test-only — no production code changes.

Refs #606

Co-Authored-By: Claude <noreply@anthropic.com>
…uto-switch test

session-tab.md: step 6 documents the new `_ResumeLock(..., session.id, ...)` signature
and the cold-turn key shape `session_lock:cold:{session_id}` (#779). Step 5 drops the
"which the lock skips by design" claim about cold turns — no longer accurate.

subscription-auto-switch.md: adds `tests/unit/test_subscription_auto_switch_no_cred_import.py`
to the Tests table — chain-level regression for #606 that pins the SUB-003 auto-switch
path reaches the `lifecycle.py:155` subscription_mode short-circuit and never re-enters
file-based credential import.

Refs #779
Refs #606

Co-Authored-By: Claude <noreply@anthropic.com>
The #606 chain-level regression test added in 967b874 introduced 17 new
`tests/lint_sys_modules.py` violations — `sys.modules[...] = ...`,
`.update(...)`, `.pop(...)`, `.setdefault(...)` writes at both module-
import time (inside `_preload_credential_encryption`, `_load_lifecycle`,
`_load_auto_switch`) and inside the test body and `_reset` fixture.

`monkeypatch.setitem` is fixture-scoped and structurally cannot rewind
the stubs planted during module import. Adopt the named snapshot/restore
escape hatch documented in `tests/lint_sys_modules.py` (precedent:
`tests/unit/test_telegram_webhook_backfill.py`):

- Add top-level `_STUBBED_MODULE_NAMES` listing the 19 module slots this
  file stubs across its four loader paths.
- Add autouse `_restore_sys_modules` fixture that snapshots those slots
  at test setup and restores them at teardown.

This bounds the actual #762 concern (cross-file pollution within the
same pytest session) without forcing a structurally-impossible rewrite
of the import-time loaders.

Verification:
- `python tests/lint_sys_modules.py` — target file now reports 0
  violations (was 17). The remaining `test_cleanup_unreachable_orphan.py`
  failure is a pre-existing unrelated case.
- `pytest tests/unit/test_subscription_auto_switch_no_cred_import.py` —
  passes.
- `pytest tests/unit/test_inject_assigned_credentials.py` — passes,
  confirming the snapshot/restore doesn't leak into the sibling leaf
  test that also loads `lifecycle.py`.

Test-only — no production code changes.

Refs #606
Refs #762

Co-Authored-By: Claude <noreply@anthropic.com>
…ssage

The lint at `tests/lint_sys_modules.py` documents three ways out of a
bare-`sys.modules` violation: `monkeypatch.setitem` / `delitem`, moving
to `conftest.py`, and the `_STUBBED_MODULE_NAMES` + `_restore_sys_modules`
named-helper pair (precedent: `tests/unit/test_telegram_webhook_backfill.py`).
Only the first two are surfaced in the per-line error message.

For tests that load real backend modules in isolation with their heavy
deps stubbed at import time, neither of the documented options applies:

- `monkeypatch.*` is fixture-scoped and structurally cannot rewind a stub
  planted before any test runs (the stubs are needed during `spec_from_file_location`
  + `exec_module`, which executes at module-import time).
- `conftest.py` would leak the stubs to every test in the directory, the
  exact #762 failure mode the lint exists to prevent.

The named-helper escape hatch is the right answer for this case, and the
lint already exempts it — but discoverability is poor. PR #783 and #606
both hit the lint, read the error, and re-discovered the escape hatch
independently after digging into the script's docstring. Surface it in
the error directly so the next author sees all three options at first
encounter, not after archaeology.

No behavioral change — pure error-text expansion. The lint's exit codes,
detection logic, and baseline mechanics are unchanged. The committed
baseline file is unaffected.

Refs #762

Co-Authored-By: Claude <noreply@anthropic.com>
`tests/unit/test_cleanup_unreachable_orphan.py` (added in 79a6481,
PR #783) plants three import-time stubs in `sys.modules`:

  - `docker` (line 64) — the unit venv doesn't ship the docker package
  - `services.docker_service` (line 76) — eager-imported by `services/__init__.py`
  - `database` (line 79) — hit transitively by cleanup_service

The lint+baseline shipped in #791 (3043631) merged ~4 hours after #783,
but `#791`'s baseline file omits this entry — almost certainly generated
against a pre-#783 dev state and never refreshed before merge. Result:
`lint-sys-modules` job in `backend-unit-test.yml` has been red on dev
since both PRs landed, and the failure travels to every PR branched off
that state — including #606's PR.

Apply the same named-helper escape hatch used by the #606 file and the
original `tests/unit/test_telegram_webhook_backfill.py` precedent: a
top-level `_STUBBED_MODULE_NAMES` list naming the three slots, plus an
autouse `_restore_sys_modules` fixture that snapshots them at test setup
and restores at teardown. Bounds the cross-file pollution to this test's
own scope — the exact invariant #762's lint protects.

Verification:
- `python tests/lint_sys_modules.py` — clean: 223 violations in 65 files,
  baseline allows 223, no new violations.
- `pytest tests/test_lint_sys_modules.py` — all 25 tests pass, including
  `test_committed_baseline_matches_current_repo_state` (was failing).
- `pytest tests/unit/test_cleanup_unreachable_orphan.py` — passes.

Test-only — no production code changes. Cross-PR fix: clears CI on this
branch and on dev. The underlying "stale baseline at PR merge"
question (why didn't #791's own CI catch this?) is a separate concern
worth a follow-up issue.

Refs #762
Refs #783

Co-Authored-By: Claude <noreply@anthropic.com>
@vybe
vybe force-pushed the AndriiPasternak31/issue-606-plan branch from 191959c to 63aab5b Compare May 14, 2026 14:14

@vybe vybe left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Rebase landed clean — shared baseline commit auto-skipped by Git. Five PR-specific commits intact. Chain-level regression test design is solid. LGTM.

@vybe
vybe merged commit 4639a22 into dev May 14, 2026
9 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants