[Init] Initial commit - NetMesh terminal manager
Some checks failed
build-packages / resolve bundled mosh-client (push) Has been cancelled
build-packages / resolve bundled et-client (push) Has been cancelled
build-packages / build-macos (push) Has been cancelled
build-packages / build-windows (push) Has been cancelled
build-packages / build-linux-x64 (push) Has been cancelled
build-packages / build-linux-arm64 (push) Has been cancelled
build-packages / release (push) Has been cancelled
build-packages / update Nix release metadata (push) Has been cancelled
build-packages / bump homebrew tap (push) Has been cancelled
test / lint-and-test (push) Has been cancelled
AI automation / Route event (push) Has been cancelled
AI automation / Hand reopened issue to maintainers (push) Has been cancelled
AI automation / Clean source issue state (push) Has been cancelled
AI automation / Reconcile handoffs (push) Has been cancelled
AI automation / Classify issue (push) Has been cancelled
AI automation / Claude Code smoke (push) Has been cancelled
AI automation / Review issue follow-up (push) Has been cancelled
AI automation / Publish issue follow-up (push) Has been cancelled
AI automation / Implement with Claude Code (push) Has been cancelled
AI automation / Publish implement PR (push) Has been cancelled
AI automation / Continue queued issue comments (push) Has been cancelled
AI automation / Codex review loop (push) Has been cancelled
AI automation / Publish Codex fix (push) Has been cancelled
AI automation / Clear Codex dispatch marker (push) Has been cancelled
AI automation / Own PR re-request Codex (push) Has been cancelled
AI automation / External PR re-request Codex (push) Has been cancelled
AI automation / Poll Codex reaction / retry (push) Has been cancelled
build-et-binaries / build-linux-x64 (push) Has been cancelled
build-et-binaries / build-linux-arm64 (push) Has been cancelled
build-et-binaries / build-macos-universal (push) Has been cancelled
build-et-binaries / build-windows-x64 (push) Has been cancelled
build-et-binaries / release (push) Has been cancelled
Some checks failed
build-packages / resolve bundled mosh-client (push) Has been cancelled
build-packages / resolve bundled et-client (push) Has been cancelled
build-packages / build-macos (push) Has been cancelled
build-packages / build-windows (push) Has been cancelled
build-packages / build-linux-x64 (push) Has been cancelled
build-packages / build-linux-arm64 (push) Has been cancelled
build-packages / release (push) Has been cancelled
build-packages / update Nix release metadata (push) Has been cancelled
build-packages / bump homebrew tap (push) Has been cancelled
test / lint-and-test (push) Has been cancelled
AI automation / Route event (push) Has been cancelled
AI automation / Hand reopened issue to maintainers (push) Has been cancelled
AI automation / Clean source issue state (push) Has been cancelled
AI automation / Reconcile handoffs (push) Has been cancelled
AI automation / Classify issue (push) Has been cancelled
AI automation / Claude Code smoke (push) Has been cancelled
AI automation / Review issue follow-up (push) Has been cancelled
AI automation / Publish issue follow-up (push) Has been cancelled
AI automation / Implement with Claude Code (push) Has been cancelled
AI automation / Publish implement PR (push) Has been cancelled
AI automation / Continue queued issue comments (push) Has been cancelled
AI automation / Codex review loop (push) Has been cancelled
AI automation / Publish Codex fix (push) Has been cancelled
AI automation / Clear Codex dispatch marker (push) Has been cancelled
AI automation / Own PR re-request Codex (push) Has been cancelled
AI automation / External PR re-request Codex (push) Has been cancelled
AI automation / Poll Codex reaction / retry (push) Has been cancelled
build-et-binaries / build-linux-x64 (push) Has been cancelled
build-et-binaries / build-linux-arm64 (push) Has been cancelled
build-et-binaries / build-macos-universal (push) Has been cancelled
build-et-binaries / build-windows-x64 (push) Has been cancelled
build-et-binaries / release (push) Has been cancelled
This commit is contained in:
65
application/state/useCloudSync.staleUnlock.test.tsx
Normal file
65
application/state/useCloudSync.staleUnlock.test.tsx
Normal file
@@ -0,0 +1,65 @@
|
||||
import test from 'node:test';
|
||||
import assert from 'node:assert/strict';
|
||||
import React from 'react';
|
||||
import { createDomRenderer, flushEffects, installDomEnvironment } from '../../components/test-support/renderReactDom';
|
||||
import { EncryptionService } from '../../infrastructure/services/EncryptionService';
|
||||
import type { SyncManagerState } from '../../infrastructure/services/CloudSyncManager';
|
||||
import type { SyncPayload } from '../../domain/sync';
|
||||
|
||||
test('manual sync does not clear a newly shared password when an older unlock is superseded', async (t) => {
|
||||
const env = installDomEnvironment();
|
||||
const previousStorage = Object.getOwnPropertyDescriptor(globalThis, "localStorage");
|
||||
Object.defineProperty(globalThis, "localStorage", { configurable: true, value: env.window.localStorage });
|
||||
t.after(() => {
|
||||
if (previousStorage) Object.defineProperty(globalThis, "localStorage", previousStorage);
|
||||
else Reflect.deleteProperty(globalThis, "localStorage");
|
||||
});
|
||||
let sharedPassword: string | undefined;
|
||||
let clears = 0;
|
||||
Object.defineProperty(env.window, 'netcatty', { configurable: true, value: {
|
||||
cloudSyncGetSessionPassword: async () => sharedPassword,
|
||||
cloudSyncClearSessionPassword: async () => { clears += 1; sharedPassword = undefined; },
|
||||
} });
|
||||
const { getCloudSyncManager } = await import('../../infrastructure/services/CloudSyncManager');
|
||||
const manager = getCloudSyncManager();
|
||||
await manager.setupMasterKey('old-fixture-password');
|
||||
manager.lock();
|
||||
const newConfig = await EncryptionService.createMasterKeyConfig('new-fixture-password');
|
||||
const { useCloudSync } = await import('./useCloudSync');
|
||||
let current!: ReturnType<typeof useCloudSync>;
|
||||
const Probe = () => { current = useCloudSync(); return null; };
|
||||
const renderer = await createDomRenderer(env.document);
|
||||
const originalUnlock = EncryptionService.unlockMasterKey;
|
||||
let release!: () => void;
|
||||
let started!: () => void;
|
||||
const gate = new Promise<void>((resolve) => { release = resolve; });
|
||||
const entered = new Promise<void>((resolve) => { started = resolve; });
|
||||
try {
|
||||
await renderer.render(<Probe />);
|
||||
await flushEffects();
|
||||
EncryptionService.unlockMasterKey = async (...args) => {
|
||||
started();
|
||||
await gate;
|
||||
return originalUnlock(...args);
|
||||
};
|
||||
sharedPassword = 'old-fixture-password';
|
||||
const result = current.syncNow({} as SyncPayload).then(() => null, (error: unknown) => error);
|
||||
await entered;
|
||||
const state = Reflect.get(manager, 'state') as SyncManagerState;
|
||||
state.masterKeyConfig = newConfig;
|
||||
Reflect.get(manager, 'bumpSyncSecurityGeneration').call(manager);
|
||||
sharedPassword = 'new-fixture-password';
|
||||
release();
|
||||
const error = await result;
|
||||
assert.equal(clears, 0, 'stale work must not erase the new shared password');
|
||||
assert.equal(sharedPassword, 'new-fixture-password');
|
||||
assert.ok(error instanceof Error);
|
||||
assert.match(error.message, /changed while unlocking/i);
|
||||
} finally {
|
||||
release();
|
||||
EncryptionService.unlockMasterKey = originalUnlock;
|
||||
await renderer.unmount();
|
||||
manager.destroy();
|
||||
env.cleanup();
|
||||
}
|
||||
});
|
||||
Reference in New Issue
Block a user