Skip to content

test: design record on test fidelity + DD15 real-ddev migration loops - #87

Merged
steffenmaechtel merged 2 commits into
masterfrom
feature/design-test-fidelity
Sep 18, 2026
Merged

steffenmaechtel merged 2 commits into
masterfrom
feature/design-test-fidelity

Conversation

@steffenmaechtel

Copy link
Copy Markdown
Owner

What

Two related pieces, motivated by the recurring "green tests, broken production" pattern (most recently b14a198: ddev consumed the ddev-migrate export loop's stdin — one database dump per run, only visible with real ddev on a real user's machine):

1. Design record: docs/design/test-environment-fidelity.md

Reference on why kit bugs pass unit + e2e and still hit real terminals:

  • the three execution environments (user terminal / agent session / unit-e2e) and their differences (TTY, stdin semantics, rc-file loading, isatty branches, signals) — bugs survive in the gaps
  • the four bug classes with kit incidents (stdin drains, rc-file/hook installation, isatty prompts, signal/job control)
  • test-double terminology (Fowler/Meszaros) and the kit's fidelity rule: a double must replicate every observable behavior the code under test can touch, including stdio side effects
  • closing patterns: behavioral doubles + contract lock, real-binary tiers, stdin-variant matrix, PTY harnesses (script -qec, pexpect, expect, tmux), session recording
  • framework options (shellspec, bats-core) assessed — explicitly optional, nothing blocks on them
  • checklist for new kit code + glossary

Indexed in docs/README.md.

2. DD15: both ddev-migrate loops driven with REAL ddev (tests/e2e/run-ddev.sh)

Implements §4.2 of the record — the stdin bug lived in a loop the real-binary suite never drove. Two projects per loop (the bug only becomes visible from the second project on):

  • export loop, dev side: real ddev config as the dev user (real registry, dev-owned .ddev), real ddev-migrate export. Starts fail by design (dev owns no daemon in the container) — asserts the loop reaches both projects instead of breaking on the first failing child.
  • import loop, opencode side (green path): fabricated dump directory in the exact layout a real export finalizes, real ddev-migrate import; asserts both databases land (per-project mark tables verified via export-db).

Mutation-verified: stripping </dev/null from the import loop's ddev call flips the checks to first-only import — the production failure mode.

Documented caveat (code + both design records): a failing real ddev never reaches its stdin probing, so a fail-path scenario proves loop survival only — the drain itself needs the success path (import part), while the unit DDEV_EAT_STDIN fake keeps the export call-site contract locked.

Verification

  • make e2e-ddev: 70 passed (was 59), 0 failed, 0 skipped — including a mutation run (2 expected FAILs) and a final green run after revert
  • unit 23/23, make lint (ShellCheck), make check-version green
  • docs updated in the same PR: ddev-e2e-test.md (status delta, DD0–DD15, 70 checks), test-environment-fidelity.md §4.2 (shipped note + caveat)

Why bugs pass unit + e2e and still hit real user terminals (stdin
drains, rc-file hooks, isatty branches, signals), test double
fidelity lessons from b14a198, patterns (behavioral doubles,
real-binary tiers, stdin matrix, PTY harnesses), and a glossary.
test-environment-fidelity.md §4.2: the b14a198 stdin-drain bug lived
in loops the real-binary suite never drove. New e2e-ddev section DD15
(two projects per loop — the bug only shows from the second on):

- export loop, dev side: real 'ddev config' as dev (registry + dev-owned
  .ddev), real ddev-migrate export; starts fail by design (dev has no
  daemon) — asserts the loop REACHES both projects instead of breaking
  on the first failing child (loop survival).
- import loop, opencode side (green path): fabricated dump dir in the
  real export layout, real ddev-migrate import; asserts both databases
  land (mark tables via export-db).

Mutation-verified: stripping </dev/null from the import loop flips the
checks to first-only import. Learned caveat (documented in code + both
design records): a FAILING real ddev never reaches its stdin probing —
the drain itself is only provable on the success path; the export part
therefore covers loop survival, the unit DDEV_EAT_STDIN fake keeps the
export call-site contract.

e2e-ddev 70 (was 59); unit 23, lint green.

@steffenmaechtel steffenmaechtel left a comment

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

Reviewed

@steffenmaechtel
steffenmaechtel merged commit f72c246 into master Sep 18, 2026
3 checks passed
@steffenmaechtel
steffenmaechtel deleted the feature/design-test-fidelity branch September 18, 2026 22:10
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.

1 participant