Skip to content

fix(desktop): fix Windows FUSE overwrite race and bin E2E test#271

Merged
FSM1 merged 5 commits into
mainfrom
fix/desktop-e2e-windows
Mar 5, 2026
Merged

fix(desktop): fix Windows FUSE overwrite race and bin E2E test#271
FSM1 merged 5 commits into
mainfrom
fix/desktop-e2e-windows

Conversation

@FSM1

@FSM1 FSM1 commented Mar 5, 2026

Copy link
Copy Markdown
Owner

Summary

  • Fix Windows FUSE file overwrite race condition — PowerShell's Set-Content performs truncate-then-write as two separate handle cycles, producing two background IPFS uploads for the same inode. The stale 0-byte upload could overwrite the correct CID because write_generation was hardcoded to 0 on Windows, so drain_upload_completions accepted both. Now write_generation is properly incremented (matching macOS/Linux) so stale uploads are rejected.
  • Fix bin E2E test checking non-existent API field — Test 5 checked recycleBinIpnsName from /vault/config, but this field is derived client-side (via HKDF from user's private key) and never exposed by the API. Changed to verify bin publish via desktop log output.
  • Pass DESKTOP_LOG env var to Windows E2E test runner so the bin test can read the desktop log file.

Fixes 4 test failures in Desktop E2E (Windows):

  1. Overwrite file (I/O device error) — stale upload race
  2. Rename file (file in use) — cascading from overwrite
  3. Delete file (not found) — cascading from rename
  4. Bin entry verification (missing field) — wrong verification approach

Test plan

  • Desktop E2E (Windows) CI passes: overwrite, rename, delete tests
  • Desktop E2E (Windows) CI passes: recycle bin Test 5
  • Desktop E2E (macOS) still passes (unchanged code paths)
  • Desktop E2E (Linux) still passes (unchanged code paths)

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Bug Fixes

    • Improve file truncation/overwrite: sizes, content IDs, and upload generation are updated; background uploads include and honor write generation to avoid caching stale uploads.
    • Prevent stale upload plaintext from being moved into cache unless generation matches current file state.
  • Tests

    • Desktop E2E exposes a DESKTOP_LOG path for log-based recycle-bin verification (with fallback if log missing).
    • Allow re-uploading same file in upload tests and add resilient quota reads for recycle-bin tests.

FSM1 and others added 2 commits March 5, 2026 04:28
When TC08 uploads v2 of a file with the same disk path as v1, Chromium
skips the change event because the input value hasn't changed. Clearing
input.value before setInputFiles ensures the change event always fires.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Entire-Checkpoint: bd5084e0c4c4
Two issues caused Desktop E2E Windows CI failures:

1. **File overwrite race condition**: PowerShell's `Set-Content` does
   open→truncate→close then open→write→close, creating two background
   IPFS uploads for the same inode. The stale 0-byte upload could
   overwrite the correct CID because `write_generation` was never
   incremented on Windows (always 0), so `drain_upload_completions`
   accepted both uploads. Fix: increment `write_generation` in
   `handle_set_file_size`, `handle_overwrite`, and the cleanup flush
   code, matching the macOS/Linux pattern. This ensures stale uploads
   are rejected.

2. **Bin test checking non-existent API field**: Test 5 in
   `test-recycle-bin.ps1` checked `recycleBinIpnsName` from
   `/vault/config`, but this field is derived client-side via HKDF
   and never exposed by the API. Fix: verify bin entry creation by
   checking the desktop log for "Bin entry published" instead.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Entire-Checkpoint: f5d35d1b5d65
@coderabbitai

coderabbitai Bot commented Mar 5, 2026

Copy link
Copy Markdown

Caution

Review failed

Pull request was closed or merged during review

Walkthrough

Track and propagate write generations for size-affecting FUSE ops; prevent caching stale upload data; expose desktop log path to Windows E2E step and switch recycle-bin Test 5 to log-based verification when available; clear file-input before re-upload in E2E helper and add a safe quota-store reader in recycle-bin tests.

Changes

Cohort / File(s) Summary
Windows FUSE write logic
apps/desktop/src-tauri/src/fuse/windows/write_ops.rs, apps/desktop/src-tauri/src/fuse/mod.rs
Increment inode.write_generation on size-affecting ops (truncate/overwrite/set_file_size); clear file CID and reset size on zero-length truncation; propagate captured write_generation into background upload messages; only move plaintext to cache when upload generation matches inode generation.
Desktop E2E workflow & recycle-bin script
.github/workflows/e2e-desktop.yml, tests/e2e-desktop/scripts/test-recycle-bin.ps1
Export DESKTOP_LOG in Windows E2E step; Test 5 now checks desktop log for "Bin entry published" when DESKTOP_LOG is present (falls back to pass/skip behavior if log unavailable), removing the prior IPNS/vault verification path.
E2E upload helper & recycle-bin tests
tests/e2e/page-objects/file-browser/upload-zone.page.ts, tests/e2e/tests/recycle-bin.spec.ts
Pre-clear hidden file input (el.value = '') before setInputFiles to allow re-uploading same file; add a safe helper getQuotaUsedBytes(page) to read Zustand quota store defensively and replace direct window store accesses.
Manifest / metadata
package.json
Minor manifest changes referenced by tests (metadata adjustments included in diff).

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title directly summarizes the two main fixes in this changeset: addressing a Windows FUSE overwrite race condition and fixing the Desktop E2E recycle-bin test.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch fix/desktop-e2e-windows

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@codecov

codecov Bot commented Mar 5, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 0% with 6 lines in your changes missing coverage. Please review.
✅ Project coverage is 48.06%. Comparing base (15a7ece) to head (94838ec).
⚠️ Report is 2 commits behind head on main.

Files with missing lines Patch % Lines
apps/desktop/src-tauri/src/fuse/mod.rs 0.00% 6 Missing ⚠️
Additional details and impacted files

Impacted file tree graph

@@            Coverage Diff             @@
##             main     #271      +/-   ##
==========================================
+ Coverage   47.95%   48.06%   +0.11%     
==========================================
  Files         109      110       +1     
  Lines        8360     9050     +690     
  Branches      652      652              
==========================================
+ Hits         4009     4350     +341     
- Misses       4179     4528     +349     
  Partials      172      172              
Flag Coverage Δ
api 84.69% <ø> (ø)
crypto 84.69% <ø> (ø)
desktop 28.64% <0.00%> (+2.74%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

Files with missing lines Coverage Δ
apps/desktop/src-tauri/src/fuse/mod.rs 20.88% <0.00%> (+20.88%) ⬆️

... and 9 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Copilot AI 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.

Pull request overview

This PR fixes two Windows Desktop E2E test failures: a WinFsp file overwrite race condition (stale 0-byte IPFS upload overwriting the correct file CID) and a misconfigured bin test that checked a non-existent API field.

Changes:

  • Windows WinFsp write_generation is now properly incremented in handle_overwrite, handle_set_file_size(0), and the cleanup flush path — matching the existing macOS/Linux behavior and preventing stale uploads from drain_upload_completions accepting a 0-byte CID.
  • The bin E2E test (Test 5) now verifies publish via desktop log inspection instead of checking the non-existent recycleBinIpnsName API field.
  • DESKTOP_LOG env var is exported in the Windows CI workflow so the PowerShell test script can locate the log file.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 2 comments.

File Description
apps/desktop/src-tauri/src/fuse/windows/write_ops.rs Core fix: adds write_generation increments in overwrite/truncate/flush paths, and passes actual generation (not 0) to UploadComplete
tests/e2e-desktop/scripts/test-recycle-bin.ps1 Replaces broken API-field check with desktop-log-based bin publish verification
.github/workflows/e2e-desktop.yml Exports DESKTOP_LOG so the PowerShell test script can read the desktop binary log
tests/e2e/page-objects/file-browser/upload-zone.page.ts Clears file input value before setInputFiles to ensure re-upload of same path triggers change event

You can also share your feedback on Copilot code review. Take the survey.

Comment thread apps/desktop/src-tauri/src/fuse/windows/write_ops.rs
Comment thread tests/e2e/page-objects/file-browser/upload-zone.page.ts
FSM1 and others added 2 commits March 5, 2026 13:08
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Entire-Checkpoint: af52ba0136c1
- Guard pending_content→content_cache move with generation check in
  drain_upload_completions to prevent cache pollution from stale uploads
- Clear file input value in uploadFiles() for consistency with uploadFile()

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Entire-Checkpoint: 61bc5416fb00

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
tests/e2e/tests/recycle-bin.spec.ts (1)

355-360: Quota store is intentionally not exposed; quota assertions are best-effort as designed.

The review comment correctly identifies that quota is not registered in __ZUSTAND_STORES__ (only auth, vault, folder, sync are exposed in apps/web/src/main.tsx). This makes the quota assertions at lines 360, 416, and 454 unreachable—they always return null due to missing store, and the guards handle this correctly.

This is by design. The test explicitly documents this fragility (lines 350–352: "window.ZUSTAND_STORES may not be exposed"), and the learning confirms quota validation is intentionally supplementary—the primary assertion is that permanent delete completes without crashing. Encryption overhead and IPFS chunking make deterministic freed-byte tracking unreliable in E2E tests, so the store was intentionally excluded from exposure.

Optional improvement: Either expose useQuotaStore in __ZUSTAND_STORES__ if quota validation becomes important, or simplify the test by removing the dead-code branches to reduce confusion.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/e2e/tests/recycle-bin.spec.ts` around lines 355 - 360, The test relies
on a quota store lookup via __ZUSTAND_STORES__.quota which is intentionally not
exposed, making the quota assertions in recycle-bin.spec.ts unreachable; either
(a) expose useQuotaStore/quota in the global __ZUSTAND_STORES__ registry (add
the quota entry where other stores are registered so the lookup in
recycle-bin.spec.ts finds the store), or (b) remove/simplify the dead guarded
assertions that reference __ZUSTAND_STORES__.quota (and related checks at the
locations noted) so the test only performs the primary “permanent delete
completes” assertion; update the test comments to reflect the chosen approach.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@tests/e2e/tests/recycle-bin.spec.ts`:
- Around line 355-360: The test relies on a quota store lookup via
__ZUSTAND_STORES__.quota which is intentionally not exposed, making the quota
assertions in recycle-bin.spec.ts unreachable; either (a) expose
useQuotaStore/quota in the global __ZUSTAND_STORES__ registry (add the quota
entry where other stores are registered so the lookup in recycle-bin.spec.ts
finds the store), or (b) remove/simplify the dead guarded assertions that
reference __ZUSTAND_STORES__.quota (and related checks at the locations noted)
so the test only performs the primary “permanent delete completes” assertion;
update the test comments to reflect the chosen approach.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 3891d59d-ea81-4901-916e-da141bd7b362

📥 Commits

Reviewing files that changed from the base of the PR and between 49ef139 and aea4e31.

📒 Files selected for processing (1)
  • tests/e2e/tests/recycle-bin.spec.ts

Copilot AI 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.

Pull request overview

Copilot reviewed 6 out of 6 changed files in this pull request and generated 2 comments.


You can also share your feedback on Copilot code review. Take the survey.

Comment thread tests/e2e-desktop/scripts/test-recycle-bin.ps1
Comment thread tests/e2e/tests/recycle-bin.spec.ts Outdated
- Document DESKTOP_LOG env var in test-recycle-bin.ps1 header
- Extract duplicated Zustand quota store access into getQuotaUsedBytes helper

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Entire-Checkpoint: 4a56f1f17ac9
@FSM1 FSM1 enabled auto-merge (squash) March 5, 2026 12:26
@FSM1 FSM1 requested a review from Copilot March 5, 2026 12:30
@FSM1 FSM1 merged commit 42bbdd7 into main Mar 5, 2026
21 of 22 checks passed
@FSM1 FSM1 deleted the fix/desktop-e2e-windows branch March 5, 2026 12:31

Copilot AI 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.

Pull request overview

Copilot reviewed 6 out of 6 changed files in this pull request and generated 1 comment.


You can also share your feedback on Copilot code review. Take the survey.

if (quotaDecreased !== null) {
expect(quotaDecreased).toBe(true);
if (quotaUsed !== null) {
expect(typeof quotaUsed === 'number').toBe(true);

Copilot AI Mar 5, 2026

Copy link

Choose a reason for hiding this comment

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

The assertion at line 370 is a tautological no-op. The getQuotaUsedBytes helper returns number | null from its type signature, and by the time line 370 executes, quotaUsed has already been confirmed to be non-null by the surrounding if (quotaUsed !== null) guard. Therefore typeof quotaUsed === 'number' is always true, and expect(typeof quotaUsed === 'number').toBe(true) never fails under any circumstances. The intent of this secondary assertion (verifying the store is functional and actually holds a numeric value) is preserved by the null-check alone. Consider either removing this inner assertion entirely, or replacing it with a more meaningful check such as expect(quotaUsed).toBeGreaterThanOrEqual(0).

Suggested change
expect(typeof quotaUsed === 'number').toBe(true);
expect(quotaUsed).toBeGreaterThanOrEqual(0);

Copilot uses AI. Check for mistakes.
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