Files
goodbuddy/src/shared/agent-installation-contracts.test.ts
T
mesalogo 1698d9fd3e fix: keep valid older agent packages usable after desktop updates
Accept package-declared OpenCode, Node and koffi versions instead of desktop release pins. Preserve signature, hash, protocol and package-internal version consistency checks.

Validate both cached 0.11.18 packages and Linux x64 installation, bootstrap, health and stop. Related regression suite: 326 passed, 1 skipped; unrelated full-workspace check failures are recorded in the remote Host design.

Release note: Desktop updates no longer reject intact older Agent packages solely because their bundled runtime versions differ.
2026-09-16 19:04:54 +08:00

192 lines
4.4 KiB
TypeScript

import { describe, expect, it } from 'vitest'
import {
agentBundleManifestSchema,
agentReleaseKeyRegistrySchema,
agentRuntimeLockSchema
} from './agent-installation-contracts'
const digest = 'a'.repeat(64)
function manifest() {
return {
formatVersion: 1,
product: 'GoodBuddy',
agentVersion: '0.11.0',
platform: 'linux',
arch: 'x64',
protocol: { major: 1, minor: 0 },
signingKeyId: 'test-key',
entrypoint: {
path: 'goodbuddy-agent',
runtimePath: 'node',
scriptPath: 'lib/agent.cjs'
},
files: [
{
path: 'goodbuddy-agent',
size: 1,
sha256: digest,
mode: '0755'
},
{
path: 'node',
size: 1,
sha256: digest,
mode: '0755'
},
{
path: 'lib/agent.cjs',
size: 1,
sha256: digest,
mode: '0644'
},
{
path: 'licenses/license.txt',
size: 1,
sha256: digest,
mode: '0644'
}
],
licenses: [
{
package: 'fixture',
version: '1.0.0',
spdx: 'MIT',
path: 'licenses/license.txt'
}
]
} as const
}
describe('Agent installation contracts', () => {
it('accepts a complete strict manifest', () => {
expect(agentBundleManifestSchema.parse(manifest())).toMatchObject({
arch: 'x64'
})
expect(() =>
agentBundleManifestSchema.parse({
...manifest(),
agentVersion: 'release_10'
})
).toThrow()
})
it('rejects unsafe, duplicate, undeclared, and incorrectly-modeled files', () => {
expect(() =>
agentBundleManifestSchema.parse({
...manifest(),
files: [
...manifest().files,
{
path: '../outside',
size: 1,
sha256: digest,
mode: '0644'
}
]
})
).toThrow()
expect(() =>
agentBundleManifestSchema.parse({
...manifest(),
files: [...manifest().files, manifest().files[0]]
})
).toThrow()
expect(() =>
agentBundleManifestSchema.parse({
...manifest(),
licenses: [
{
package: 'fixture',
version: '1.0.0',
spdx: 'MIT',
path: 'licenses/missing.txt'
}
]
})
).toThrow()
expect(() =>
agentBundleManifestSchema.parse({
...manifest(),
files: manifest().files.map((file) =>
file.path === 'goodbuddy-agent'
? { ...file, mode: '0644' }
: file
)
})
).toThrow()
})
it('rejects duplicate keys and extra registry fields', () => {
const key = {
keyId: 'test-key',
publicKeySpkiBase64: 'AAAA',
environment: 'test'
}
expect(() =>
agentReleaseKeyRegistrySchema.parse({
formatVersion: 1,
keys: [key, key],
revocations: [],
unknown: true
})
).toThrow()
})
it('rejects Base64 encodings with non-zero padding bits', () => {
expect(() =>
agentReleaseKeyRegistrySchema.parse({
formatVersion: 1,
keys: [{
keyId: 'test-key',
publicKeySpkiBase64: 'ZE==',
environment: 'test'
}],
revocations: []
})
).toThrow('canonical Base64')
})
it.each([
['24.19.0', '3.1.4'],
['24.18.0', '3.1.3']
])('accepts package Node %s and koffi %s versions while requiring valid targets and source', (nodeVersion, koffiVersion) => {
const target = {
archive: 'node.tar.gz',
sha256: digest,
binaryPath: 'node/bin/node',
licensePath: 'node/LICENSE'
}
expect(
agentRuntimeLockSchema.parse({
formatVersion: 1,
agentVersion: '0.11.0',
protocol: { major: 1, minor: 0 },
node: {
version: nodeVersion,
source: `https://nodejs.org/dist/v${nodeVersion}/`,
targets: {
'linux-x64': target,
'linux-arm64': target
}
},
koffi: { version: koffiVersion }
}).koffi.version
).toBe(koffiVersion)
expect(() =>
agentRuntimeLockSchema.parse({
formatVersion: 1,
agentVersion: '0.11.0',
protocol: { major: 1, minor: 0 },
node: {
version: '24.18.0',
source: 'http://nodejs.org/',
targets: { 'linux-x64': target }
},
koffi: { version: '3.1.3' }
})
).toThrow()
})
})