Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 30 additions & 9 deletions apps/desktop/src/main/__tests__/main-startup-lifetime.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,14 +56,41 @@ test('retains process lifetime before a standalone startup dialog can close', ()
);
const windowAllClosed = bootSource.slice(
windowAllClosedStart,
bootSource.indexOf('app.on("before-quit"', windowAllClosedStart),
bootSource.indexOf('powerMonitor.on("resume"', windowAllClosedStart),
);
assert.match(
windowAllClosed,
/process\.platform !== "darwin" && !isBrowserMessageBoxPresentationActive\(\) &&\s*!isDesktopStartupInProgress\(\)/u,
);
});

test('registers one shared quit cleanup before the initial Host handoff', () => {
const hostStart = bootSource.indexOf('runtimeHostManager = await startLocalRuntimeHostManager');
const quitRegistration = bootSource.indexOf('app.on("before-quit", quitCoordinator.handleBeforeQuit)');
const workBoardDeclaration = bootSource.indexOf('let workBoardIpc:');
assert.ok(workBoardDeclaration >= 0 && workBoardDeclaration < quitRegistration);
assert.ok(quitRegistration >= 0 && quitRegistration < hostStart);
assert.equal(bootSource.match(/createAppQuitCoordinator\(\{/gu)?.length, 1);
assert.equal(bootSource.match(/app\.on\("before-quit"/gu)?.length, 1);
assert.match(bootSource, /cleanup: closeRuntimeHostDesktop/u);
assert.match(bootSource, /return runtimeHostDesktopShutdown \?\?= disposeRuntimeHostDesktop\(\)/u);
assert.match(bootSource, /workBoardIpc\?\.close\(\)/u);
});

test('drains startup resources before cancellation quit or fatal presentation', () => {
const callbackStart = bootSource.indexOf('onFatalError: (error, target) => {');
const callback = bootSource.slice(callbackStart, bootSource.indexOf('\n);', callbackStart));
assert.ok(callback.indexOf('if (!runtimeHostManager) return;') < callback.indexOf('app.quit()'));

const hostStart = bootSource.indexOf('runtimeHostManager = await startLocalRuntimeHostManager');
const failure = bootSource.slice(hostStart, bootSource.indexOf('// Runtime Host is the only', hostStart));
const cleanup = failure.indexOf('await closeRuntimeHostDesktop()');
assert.ok(cleanup >= 0 && cleanup < failure.indexOf('app.quit()'));
assert.ok(cleanup < failure.indexOf('throw error'));
assert.doesNotMatch(failure, /retireOwnedLocalHost|forceTerminate/u);
assert.match(bootSource, /await runtimeHostPeerMeshComponent\?\.close\(\)[\s\S]*await runtimeHostPeerEndpointOwner\?\.close\(\)/u);
});

test('presents startup before Host boot and hands off only when the main window is shown', () => {
const ready = mainSource.indexOf("console.log('[startup] app ready')");
const presentation = mainSource.indexOf('showDesktopStartupProgress(', ready);
Expand All @@ -79,27 +106,21 @@ test('resolves persisted locale before first post-settings recovery prompt', ()
rendererRecoveryStart,
bootSource.indexOf('resolveBrowserDialogParent =', rendererRecoveryStart),
);
const hostRecoveryStart = bootSource.indexOf('prompt: async (input)');
const hostRecovery = bootSource.slice(
hostRecoveryStart,
bootSource.indexOf('}).catch((error: unknown)', hostRecoveryStart),
);
const defaultHostRecoveryStart = bootSource.indexOf(
'async function promptForDefaultRuntimeHostRecovery',
);
const defaultHostRecovery = bootSource.slice(defaultHostRecoveryStart);

assert.match(rendererRecovery, /const locale = await desktopLocale\.resolve\(\)/u);
assert.match(hostRecovery, /const locale = await desktopLocale\.resolve\(\)/u);
assert.match(bootSource, /handoffSurface: createDesktopHostHandoffSurface\(\(\) => desktopLocale\.resolve\(\)\)/u);
assert.match(defaultHostRecovery, /const locale = await desktopLocale\.resolve\(\)/u);
assert.doesNotMatch(rendererRecovery, /desktopLocale\.current\(\)/u);
assert.doesNotMatch(hostRecovery, /desktopLocale\.current\(\)/u);
assert.doesNotMatch(defaultHostRecovery, /resolveSystemUiLocale/u);
});

test('lets the Runtime Host migrate its State Root before Desktop opens shared tables', () => {
const hostStart = bootSource.indexOf(
'runtimeHostManager = await startDesktopRuntimeHostWithRecovery',
'runtimeHostManager = await startLocalRuntimeHostManager',
);
const workBoardOpen = bootSource.indexOf(
'store: createWorkBoardStore(workspaceRoot',
Expand Down
52 changes: 0 additions & 52 deletions apps/desktop/src/main/__tests__/native-diagnostic-dialog.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@ import {
showFatalStartupError,
showMainRendererProcessGoneDialog,
showMessageBoxWithDiagnostics,
showRuntimeHostStartupRecoveryDialog,
} from '../native-diagnostic-dialog.js';

const diagnosticEnvironment = () => ({
Expand Down Expand Up @@ -193,54 +192,3 @@ test('main Renderer loss keeps Copy Diagnostics auxiliary to recovery', async ()
assert.match(clipboard, /Recent main-process logs \(1\)/);
assert.doesNotMatch(clipboard, /very-secret-token/);
});

test('managed Host recovery preserves the workspace and confirms active-work interruption', async () => {
let shown: MessageBoxOptions | undefined;
const decision = await showRuntimeHostStartupRecoveryDialog(
{
startupError: new Error('managed service unavailable'),
repairError: new Error('service update failed'),
activeTasks: true,
},
{
locale: 'en',
copyDiagnostics() {},
showMessageBox: async (options): Promise<MessageBoxReturnValue> => {
shown = options;
return { response: 0, checkboxChecked: false };
},
},
);

assert.equal(decision, 'repair');
assert.deepEqual(shown?.buttons, [
'Repair and Restart Host',
'Exit',
'Copy Diagnostics',
]);
assert.equal(shown?.defaultId, shown?.cancelId);
assert.match(shown?.detail ?? '', /workspace, Host identity, credentials, and settings/);
assert.match(shown?.detail ?? '', /automatic update compatibility cannot be confirmed/);
assert.match(shown?.detail ?? '', /interrupt that work/);
assert.match(shown?.detail ?? '', /Copy diagnostics to inspect the details/);
assert.doesNotMatch(shown?.detail ?? '', /service update failed/);

let unknownShown: MessageBoxOptions | undefined;
const unknownDecision = await showRuntimeHostStartupRecoveryDialog(
{
startupError: new Error('managed service unavailable'),
repairError: new Error('safe repair could not verify Host activity'),
activeTasks: false,
},
{
locale: 'en',
copyDiagnostics() {},
showMessageBox: async (options): Promise<MessageBoxReturnValue> => {
unknownShown = options;
return { response: 1, checkboxChecked: false };
},
},
);
assert.equal(unknownDecision, 'exit');
assert.equal(unknownShown?.defaultId, unknownShown?.cancelId);
});
141 changes: 102 additions & 39 deletions apps/desktop/src/main/__tests__/runtime-host-desktop-candidate.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ import type {
ConnectOrSpawnRuntimeHostInput,
RuntimeHostConnection,
} from '@maka/runtime-host/client';
import { RuntimeHostOperationError } from '@maka/runtime-host/client';
import { RuntimeHostOperationError, runHostHandoff } from '@maka/runtime-host/client';
import {
SESSION_CONTINUITY_SCHEMA_VERSION,
type ClientCapabilityCallFrame,
Expand All @@ -60,7 +60,7 @@ import { RuntimeHostSessionObservationRegistry } from '../runtime-host-session-o
import { RuntimeHostReconnectingIpcMain } from '../runtime-host-reconnecting-ipc-main.js';
import { desktopSessionResourceKey } from '../../shared/runtime-host-identity.js';
import { waitFor as pollFor } from '@maka/core/test-only/async-primitives';
import { startDesktopRuntimeHostWithRecovery } from '../runtime-host-startup-recovery.js';
import { canRepairManagedRuntimeHostStartup } from '../runtime-host-startup-recovery.js';

const TEST_HOST_ID = 'a'.repeat(64);
const TEST_TARGET_EPOCH = 'test-target-epoch';
Expand Down Expand Up @@ -113,52 +113,115 @@ test('updates a protocol-compatible managed Host before exposing a candidate ove
const updated = connectionHarness('updated');
let starts = 0;
let repairs = 0;
const candidate = await startDesktopRuntimeHostWithRecovery({
start: async () => {
const host = starts++ === 0 ? old : updated;
const result = await startDesktopRuntimeHostCandidate({
...deps(ipc),
workspaceRoot: root,
rootPath: root,
candidateEntrypoint: 'unused.js',
candidateLaunchBarrier: {
connect: async () => ({
kind: 'connected',
connection: host.connection,
registration: { lifecycleMode: 'supervised', pid: 123 },
}),
},
} as unknown as DesktopRuntimeHostCandidateStartInput);
assert.equal(result.kind, 'ready');
if (result.kind !== 'ready') throw new Error('Expected a ready candidate');
return result.candidate;
},
repair: async (authority) => {
repairs += 1;
assert.deepEqual(authority, { allowManualUpdate: false, allowInterruptActiveTasks: false });
assert.equal(old.closeCalls, 1, 'release the old connection before managed update');
assert.equal(old.capabilityRegistrations, 0, 'do not expose capabilities before storage admission');
assert.equal(ipc.size, 0);
const preserved = new DatabaseSync(databasePath, { readOnly: true });
const candidate = await runHostHandoff({
observe: async () => {
try {
assert.equal(preserved.prepare("SELECT version FROM operational_schema_migrations WHERE scope = 'usage'").get()?.version, 6);
assert.equal(preserved.prepare("SELECT record_json FROM usage_model_call_attempts WHERE attempt_id = 'retained'").get()?.record_json, '{}');
} finally {
preserved.close();
const host = starts++ === 0 ? old : updated;
const result = await startDesktopRuntimeHostCandidate({
...deps(ipc),
workspaceRoot: root,
rootPath: root,
candidateEntrypoint: 'unused.js',
candidateLaunchBarrier: {
connect: async () => ({
kind: 'connected',
connection: host.connection,
registration: { lifecycleMode: 'supervised', pid: 123 },
}),
},
} as unknown as DesktopRuntimeHostCandidateStartInput);
assert.equal(result.kind, 'ready');
if (result.kind !== 'ready')
throw new Error('Expected a ready candidate');
return { kind: 'ready', value: result.candidate };
} catch (error) {
assert.ok(
error instanceof Error && canRepairManagedRuntimeHostStartup(error),
);
return {
kind: 'blocked',
blocker: {
identity: 'managed-schema-before',
target: { name: 'Local', location: 'local' },
reason: 'repair',
mayExitNaturally: false,
activity: {
connections: 0,
activeOperations: 0,
processUptimeSeconds: 1,
residencies: [],
},
replacement: {
kind: 'repair',
canReplaceIdle: true,
canInterrupt: true,
execute: async (authority) => {
repairs += 1;
assert.equal(authority, 'refuse_active_work');
assert.equal(
old.closeCalls,
1,
'release the old connection before managed update',
);
assert.equal(
old.capabilityRegistrations,
0,
'do not expose capabilities before storage admission',
);
assert.equal(ipc.size, 0);
const preserved = new DatabaseSync(databasePath, {
readOnly: true,
});
try {
assert.equal(
preserved
.prepare(
"SELECT version FROM operational_schema_migrations WHERE scope = 'usage'",
)
.get()?.version,
6,
);
assert.equal(
preserved
.prepare(
"SELECT record_json FROM usage_model_call_attempts WHERE attempt_id = 'retained'",
)
.get()?.record_json,
'{}',
);
} finally {
preserved.close();
}
// Simulate the updated owning Host, not Desktop, performing migration.
acquireOperationalStateDatabase(root).close();
return { kind: 'completed' };
},
},
},
};
}
// Simulate the updated owning Host, not Desktop, performing migration.
acquireOperationalStateDatabase(root).close();
return { kind: 'repaired' };
},
prompt: async () => { throw new Error('No prompt needed for an idle automatically updatable Host'); },
openSurface: () => ({
update: (view) => assert.equal(view.state, 'progress', 'idle repair needs no consent'),
close: () => {},
}),
});
t.after(() => candidate.close());
assert.equal(starts, 2);
assert.equal(repairs, 1);
assert.equal(updated.capabilityRegistrations, 1);
const current = acquireOperationalStateDatabase(root, { schemaMigration: 'require_current' });
const current = acquireOperationalStateDatabase(root, {
schemaMigration: 'require_current',
});
try {
assert.equal(current.database.prepare("SELECT session_id FROM usage_model_call_attempts WHERE attempt_id = 'retained'").get()?.session_id, 'deleted-session');
assert.equal(
current.database
.prepare(
"SELECT session_id FROM usage_model_call_attempts WHERE attempt_id = 'retained'",
)
.get()?.session_id,
'deleted-session',
);
} finally {
current.close();
}
Expand Down
Loading
Loading