Skip to content

syncEngine: tests for restore-deleted-files + fault-isolation fix - #259

Open
philips-clanker wants to merge 3 commits into
mainfrom
sync-engine-tests-and-fix
Open

philips-clanker wants to merge 3 commits into
mainfrom
sync-engine-tests-and-fix

Conversation

@philips-clanker

Copy link
Copy Markdown
Collaborator

Stacked on #258 — this branch contains that PR's commit plus two follow-ups. Merge #258 first; until then GitHub shows its commit in the diff here too. Review only the last two commits:

  • 2874004 syncEngine: add direct tests for runDeviceSync
  • f3891f2 syncEngine: don't abort the whole sync run when a vault file can't be read

Why

#258 adds real re-partitioning logic to runDeviceSync, which previously had no direct test coverage (the file was deliberately thin and relied on deviceSync.ts's unit tests). Reviewing it surfaced one robustness regression, fixed here.

Tests (src/syncEngine.test.ts, new)

Uses an in-memory fake vault plus vi.mock for obsidian (a real TFile class, for the instanceof checks), scanDeviceSupernoteTree, and fetchFromDevice; manifest fixtures are built with the real buildSyncRecord/hashBytes so they stay honest. Covers:

  • restoring a locally deleted file whose device copy is unchanged (re-downloaded at the exact same path, counts, manifest refresh)
  • restoring into a deleted subfolder (folders recreated)
  • intact unchanged files left untouched and un-fetched
  • locally edited unchanged files still flagged as skip-conflict, never clobbered or re-downloaded
  • a mixed run partitioning each file into exactly one bucket (new / restored / intact / edited-conflict / filtered-out)
  • a failed restore download reported per-file without aborting the run

Fix: one unreadable vault file aborted the entire sync run

The #258 restore pass called currentHash() unguarded, so a single readBinary error (e.g. IO failure on any unchanged vault file) threw out of runDeviceSync — no files synced, no manifest persisted. Both adjacent passes already isolate per-file failures: the toSync loop catches into result.failed, and the drift-check loop catches and logs. The test demonstrated the error escaping (EIO out of currentHash) before the fix.

The fix guards the pass the same way: on a read error, log it and conservatively keep the listing in the unchanged bucket — never attempt to restore over a file we couldn't inspect — and let the drift-check loop report it on its own guarded pass.

Validation

  • Full suite: 15 files / 277 tests passing (270 on main + 7 new)
  • tsc --noEmit clean, npm run lint clean (same single pre-existing warning as main)

Follow-up idea, not included here: the restore pass and the drift-check loop each read+hash surviving unchanged files, so they're read twice per run; merging the two passes would halve vault I/O for large vaults.

agonzalezreyes and others added 3 commits September 4, 2026 18:31
runDeviceSync had no direct coverage: the file was deliberately thin and
relied on deviceSync.ts's unit tests for the decision logic. The pending
restore-deleted-files change (PR #258) adds real re-partitioning logic to
the orchestration layer, which needs its own tests.

Uses an in-memory fake vault plus vi.mock for obsidian (a real TFile
class, for the instanceof checks), scanDeviceSupernoteTree, and
fetchFromDevice; manifest fixtures are built with the real
buildSyncRecord/hashBytes so they stay honest. Covers:

- restoring a locally deleted file whose device copy is unchanged
  (deleted file re-downloaded at the same path, counts, manifest refresh)
- restoring into a deleted subfolder (folders recreated)
- intact unchanged files left untouched and un-fetched
- locally edited unchanged files still flagged as skip-conflict,
  never clobbered or re-downloaded
- a mixed run partitioning each file into exactly one bucket
- a failed restore download reported per-file without aborting the run

One test is skipped for now: fault isolation when an unchanged vault
file can't be read. It encodes the expected per-file degradation, but
the re-partition pass calls currentHash() unguarded, so it currently
fails with the error escaping runDeviceSync entirely. To be unskipped
by the fix in the next commit.
… read

The deleted-file restore pass called currentHash() unguarded, so one
unreadable vault file (e.g. an IO error on readBinary) threw out of
runDeviceSync and aborted the entire sync run — no files synced, no
manifest persisted. Both adjacent passes already isolate per-file
failures: the toSync loop catches into result.failed, and the
drift-check loop catches and logs.

Guard the restore pass the same way: on a read error, log it and
conservatively keep the listing in the unchanged bucket — never attempt
to restore over a file we couldn't inspect — and let the drift-check
loop report it on its own guarded pass.

Unskips the fault-isolation test from the previous commit, which
demonstrated the error escaping runDeviceSync (EIO out of currentHash)
before this fix.
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