test: fault + lifecycle scenario fan-out (PR-2)#91
Merged
Conversation
|
🎉 PR Validation ✅ PASSED Commit: Checks:
Ready to merge! ✨ |
vreshch
marked this pull request as ready for review
July 8, 2026 23:26
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
PR-2: fault + lifecycle scenario fan-out on the assembled-plugin harness
Builds on the PR-1 headless harness (
test/fakes/*+test/integration/first-sync.test.ts, #90). Adds 14 ranked integration scenarios that boot the REALAgentageMemoryPluginagainst the fakes and assert on BOTH vault state and fake-couch state (and HTTP call shape where it matters). Couch-only plugin, no git channel. Fully deterministic - ticks are driven explicitly (tick()/ a bounded macrotaskdrain/settle), no sleeps, no real network.Scenarios (highest data-loss risk first)
delete-conflict.test.ts(data-loss guards)dropLeafmid-pull -> round aborts, no vault write, cursor frozen at 0;restoreLeaf+ a clean pull converges to the full note.DELETE ?rev=; a pull-applied remote delete does NOT bounce back out as a phantom local deletion.sync-roundtrip.test.ts4. Local edit -> live push writes leaves (
_bulk_docs) BEFORE the file doc (PUT).5. Remote change pulls into the vault; the suppress guard + content-rev cache stop it echoing back as a redundant push.
resilience.test.ts6. Couch 401 mid-replication -> couch-token invalidate + re-mint + retry; sync completes, pull still delivers.
7. Expired OAuth access token ->
refresh_tokengrant + rotate before the couch JWT is minted; session stays alive.8. Rejected live push queues,
pendingCountreflects it, the nexttick()flushes it.9. Paged pull cursor (
since/limit) persists across a simulated reload: rebuild from the savedcouchStateresumes at the cursor, no re-pull from seq 0.lifecycle.test.ts10. Non-couch memory -> explicit
errorsync state (red dot) + the server-flip message, never a silent no-op, never a write.11. Sign-out tears the controller down -> a later
tick()is inert (zero couch traffic).12. Memory switch repoints the controller to a NEW db; the old db is never reused (proven with two independent in-memory couch stores).
13. Connect flow: discovery -> DCR -> authorize -> token exchange; dot goes green with a memory selected.
management.test.ts(lower risk)14. Memory list maps
entries -> files/folderCount -> folders/empty;createVaultdegrades to "not available on the server yet" on 404 / 405 / 503, then a healthy POST still creates.Plugin lines now exercised through the assembled plugin (unit tests never reach these)
Both files are coverage-excluded (
src/main.ts,src/couch/couch-sync.ts) and had NO direct test driving them end to end:main.ts:syncNowchannel routing (channelForVault->couchSyncNowvs the NOT_ON_COUCH error branch),wireCouchEventslive vault handlers (modify/create/delete pushing throughcouchChannel),selectVault/disconnectcontroller lifecycle (couchChannel.for/clear),buildCouchSyncwiring,refreshStatussync-state transitions,listVaults/createVaultmapping + degrade branches.couch-sync.ts:syncNow/pushAll/pullOnceend to end,reassemblemissing-leaf throw + cursor-freeze,tombstoneconflict-abandon +DELETE ?revpath,pushFileLive/removeFilequeue-on-failure +flushPending/tickdrain, the 401 re-mint retry inreq, and the echosuppressguard inwriteVault.Harness extensions (existing fakes + tests stay green)
fake-couch.ts:restoreLeaf(undodropLeaf) +deleteRemote(a remote-origin tombstone a pull applies).fake-auth-server.ts:failNextManagement(status)so/api/memoriesGET/POST can return a one-shot server gap.router.ts+boot.ts: route couch requests by db to a per-dbFakeCouch, withBootOptions.extraCouchadvertising extra couch memories (backs the memory-switch test). Single-memory path unchanged.test/integration/_helpers.ts: shared boot/sign-in/edit/delete/tick/drain/settle + private-field accessors (couchActive,syncStateOf,expireAccessToken).Verify
npm run verifyfully green: type-check + lint (0 errors; the 1 warning is the pre-existingsettings-tab.tsobsidianmd advisory) + format:check + 125 tests (23 files) + build + check:hosts/docs/bundle.No real plugin bug surfaced
Every fault path behaved correctly. Two sharp edges worth noting (design-correct, not bugs, not papered over):
failNext(503)on a live push lands on the leadinggetDoc, which tolerates a non-200 as "doc absent" and pushes anyway - so scenario 8 faults the actual write (failLeafOnBulk) to exercise the queue. Comment in the test documents this.reconcileDeletionshas no "known rev" to detect the stale delete. In the tested flow this is fine (the tombstone abandons and drops the rev, soreconcileDeletionsthen skips the path). Flagged here as a boundary to watch, not a reproduced fault.