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
26 changes: 26 additions & 0 deletions apps/desktop/src/main/__tests__/app-update-activity.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import assert from 'node:assert/strict';
import { describe, test } from 'node:test';
import { hasInterruptibleUpdateWork } from '../app-update-activity.js';

describe('App update activity', () => {
test('guards every kind of work that app shutdown interrupts', () => {
const cases = [
{ name: 'idle', session: false, automation: false, shells: 0, expected: false },
{ name: 'session turn', session: true, automation: false, shells: 0, expected: true },
{ name: 'Automation fire', session: false, automation: true, shells: 0, expected: true },
{ name: 'background shell', session: false, automation: false, shells: 1, expected: true },
];

for (const input of cases) {
assert.equal(
hasInterruptibleUpdateWork({
sessionActivities: { hasActive: () => input.session },
automationScheduler: { hasInFlight: () => input.automation },
shellRuns: { liveCount: () => input.shells },
}),
input.expected,
input.name,
);
}
});
});
98 changes: 98 additions & 0 deletions apps/desktop/src/main/__tests__/app-update-install.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
import assert from 'node:assert/strict';
import { describe, test } from 'node:test';
import type {
AppUpdateInstallRequest,
AppUpdateInstallResult,
} from '../../preload/bridge-contract.js';
import {
isAppUpdateInstallFailure,
requestDownloadedAppUpdate,
} from '../../renderer/app-update-install.js';

describe('requestDownloadedAppUpdate', () => {
test('identifies asynchronous install failures from update status', () => {
assert.equal(isAppUpdateInstallFailure({
state: 'error',
currentVersion: '1.0.0',
latestVersion: '1.1.0',
operation: 'install',
message: 'signature rejected',
}), true);
assert.equal(isAppUpdateInstallFailure({
state: 'error',
currentVersion: '1.0.0',
latestVersion: '1.1.0',
operation: 'download',
message: 'offline',
}), false);
});

test('installs immediately when main reports no active work', async () => {
const requests: AppUpdateInstallRequest[] = [];
const outcome = await requestDownloadedAppUpdate({
installUpdate: async (request) => {
requests.push(request);
return { ok: true };
},
confirmActiveTasks: async () => {
throw new Error('confirmation should not open');
},
});

assert.deepEqual(requests, [{ allowInterruptActiveTasks: false }]);
assert.deepEqual(outcome, { kind: 'install-started' });
});

test('leaves the downloaded update pending when the user cancels', async () => {
const requests: AppUpdateInstallRequest[] = [];
let confirmed = false;
const outcome = await requestDownloadedAppUpdate({
installUpdate: async (request) => {
requests.push(request);
return { ok: false, reason: 'active_tasks' };
},
confirmActiveTasks: async () => {
confirmed = true;
return false;
},
});

assert.equal(confirmed, true);
assert.deepEqual(requests, [{ allowInterruptActiveTasks: false }]);
assert.deepEqual(outcome, { kind: 'cancelled' });
});

test('sends explicit interruption authority only after confirmation', async () => {
const requests: AppUpdateInstallRequest[] = [];
const results: AppUpdateInstallResult[] = [
{ ok: false, reason: 'active_tasks' },
{ ok: true },
];
const outcome = await requestDownloadedAppUpdate({
installUpdate: async (request) => {
requests.push(request);
return results.shift()!;
},
confirmActiveTasks: async () => true,
});

assert.deepEqual(requests, [
{ allowInterruptActiveTasks: false },
{ allowInterruptActiveTasks: true },
]);
assert.deepEqual(outcome, { kind: 'install-started' });
});

test('returns a terminal failure from the authorized install attempt', async () => {
const results: AppUpdateInstallResult[] = [
{ ok: false, reason: 'active_tasks' },
{ ok: false, reason: 'install_failed' },
];
const outcome = await requestDownloadedAppUpdate({
installUpdate: async () => results.shift()!,
confirmActiveTasks: async () => true,
});

assert.deepEqual(outcome, { kind: 'failed', reason: 'install_failed' });
});
});
Loading
Loading