diff --git a/package.json b/package.json index 68100c13..25a807d2 100644 --- a/package.json +++ b/package.json @@ -23,7 +23,7 @@ "test:unit:heavy": "vitest run --config vitest.heavy.config.ts", "test:phase1-conformance": "node ./scripts/phase1-conformance.mjs --lock ./phase1-conformance.lock.json --scenario all", "test:e2e": "playwright test", - "test:native-e2e": "cargo test --manifest-path src-tauri/Cargo.toml --features phase1-conformance --test phase1_native_rpc", + "test:native-e2e": "cargo test --manifest-path src-tauri/Cargo.toml --features phase1-conformance --test phase1_native_rpc -- --test-threads=1", "test:contract-canary": "node ./scripts/contract-canary.mjs", "cargo:check": "cargo check --manifest-path src-tauri/Cargo.toml --all-targets", "cargo:check:windows-gnu": "cargo check --manifest-path src-tauri/Cargo.toml --target x86_64-pc-windows-gnu --all-targets", diff --git a/src-tauri/tests/phase1_native_rpc.rs b/src-tauri/tests/phase1_native_rpc.rs index 6ad15828..513322af 100644 --- a/src-tauri/tests/phase1_native_rpc.rs +++ b/src-tauri/tests/phase1_native_rpc.rs @@ -1366,7 +1366,10 @@ mod windows_cleanup { credential_account(TARGET_A), credential_account(TARGET_B), ] { - assert!(!entry_present(&service, &account)); + assert!( + !entry_present(&service, &account), + "cleanup reported success but {account} survived in {service}" + ); } assert!(entry_present(&service, &credential_account(UNRELATED))); assert!(entry_present(&unrelated_service, &unrelated_account)); @@ -1409,8 +1412,15 @@ mod windows_cleanup { let final_grant = rpc.issue(&[TARGET_A]); assert_eq!(rpc.cleanup(&final_grant)["ok"], true); - assert!(!entry_present(&service, INSTALLATION_ACCOUNT)); - assert!(!entry_present(&service, &credential_account(TARGET_A))); + for account in [ + INSTALLATION_ACCOUNT.to_owned(), + credential_account(TARGET_A), + ] { + assert!( + !entry_present(&service, &account), + "final cleanup reported success but {account} survived in {service}" + ); + } assert!(entry_present(&service, &credential_account(UNRELATED))); assert!(entry_present(&unrelated_service, &unrelated_account)); rpc.shutdown(); diff --git a/src/phase1-windows-supervisor.test.ts b/src/phase1-windows-supervisor.test.ts index c0ccdf00..04cdff18 100644 --- a/src/phase1-windows-supervisor.test.ts +++ b/src/phase1-windows-supervisor.test.ts @@ -151,12 +151,16 @@ async function cleanupRoot(owned: OwnedRoot) { } expect(processIsOpenable(pid)).toBe(false); } + // A target binary that has just exited can stay locked for a moment while + // Windows releases its image section, and Defender scans a freshly copied + // executable. Node retries EPERM and EBUSY only when asked (#446). + const removal = { recursive: true, force: true, maxRetries: 20, retryDelay: 100 } as const; for (const entry of readdirSync(owned.path)) { - rmSync(resolve(owned.path, entry), { recursive: true, force: true }); + rmSync(resolve(owned.path, entry), removal); } expect(readdirSync(owned.path)).toEqual([]); assertFleetHelperUnchanged(); - rmSync(owned.path, { recursive: true, force: true }); + rmSync(owned.path, removal); roots.splice(roots.indexOf(owned), 1); }