Files
KanbanGit/native/tests/updates.test.js
T
Twyco 20f9314659
CI/CD / preconditions (pull_request) Successful in 2s
Native JavaScript checks / Native launcher and release tests (pull_request) Successful in 1m0s
CI/CD / test_and_check_app (pull_request) Successful in 7m2s
CI/CD / Publish immutable image (pull_request) Skipped
macOS checks / Native Rust tests and unsigned build (pull_request) Successful in 1m35s
feat(KAN-65): add signed macOS updates and DMG distribution
2026-09-27 21:40:23 +02:00

123 lines
3.5 KiB
JavaScript

import assert from 'node:assert/strict';
import { Buffer } from 'node:buffer';
import { signedFixture } from './updater-fixture.mjs';
import test from 'node:test';
import {
compareVersions,
updateManifest,
verifyUpdateSignature,
} from '../update-manifest.mjs';
import { updatePresentation } from '../ui/updates-state.js';
test('compares stable versions numerically and rejects prerelease or malformed versions', () => {
assert.equal(compareVersions('1.10.0', '1.9.9'), 1);
assert.equal(compareVersions('1.0.0', '2.0.0'), -1);
assert.equal(compareVersions('1.0.0', '1.0.0'), 0);
for (const invalid of [
'v1.0.0',
'1.0.0-beta',
'01.0.0',
'1.0.0+2',
'',
'1.2',
]) {
assert.throws(() => compareVersions(invalid, '1.0.0'));
}
});
test('verifies minisign archive and trusted comment with the permanent key', () => {
const bytes = Buffer.from('signed app');
const fixture = signedFixture(bytes);
assert.doesNotThrow(() =>
verifyUpdateSignature(bytes, fixture.signature, fixture.publicKey),
);
assert.throws(
() =>
verifyUpdateSignature(
Buffer.from('tampered app'),
fixture.signature,
fixture.publicKey,
),
/signature/,
);
assert.throws(
() =>
verifyUpdateSignature(
bytes,
fixture.signature,
signedFixture(bytes).publicKey,
),
/signature/,
);
for (const signature of [
'',
'invalid',
Buffer.from(
Buffer.from(fixture.signature, 'base64')
.toString()
.replace('timestamp:123456', 'timestamp:999999'),
).toString('base64'),
]) {
assert.throws(
() => verifyUpdateSignature(bytes, signature, fixture.publicKey),
/signature/,
);
}
});
test('manifest selects the exact supported architecture and embeds release notes', () => {
const metadata = {
version: '1.2.3',
target: 'aarch64-apple-darwin',
updaterFilename: 'KanbanGit.app.tar.gz',
};
assert.deepEqual(
updateManifest(
metadata,
'https://release.example/1.2.3',
'signed',
'Release notes',
),
{
version: '1.2.3',
notes: 'Release notes',
platforms: {
'darwin-aarch64': {
url: 'https://release.example/1.2.3/KanbanGit.app.tar.gz',
signature: 'signed',
},
},
},
);
assert.throws(() =>
updateManifest(
{ ...metadata, target: 'android' },
'https://example.com',
'signed',
),
);
});
test('update controls prevent concurrent operations and allow failed downloads to retry', () => {
for (const phase of [
'checking',
'downloading',
'verifying',
'installing',
'restarting',
]) {
const view = updatePresentation({ phase, version: '2.0.0' });
assert.equal(view.busy, true);
assert.equal(view.canInstall, false);
}
assert.equal(
updatePresentation({ phase: 'available', version: '2.0.0' }).canInstall,
true,
);
assert.equal(
updatePresentation({ phase: 'error', version: '2.0.0' }).canInstall,
true,
);
assert.equal(
updatePresentation({ phase: 'current', version: null }).canInstall,
false,
);
});