mirror of
https://github.com/stablyai/orca.git
synced 2026-10-30 16:47:34 +00:00
* test: exercise hook and decorative title limits * test: reject non-string launch preferences * test: preserve live builds on release rejection * test: verify restored child and expiry session state * test: strengthen mobile transport lifecycle coverage --------- Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
280 lines
9.9 KiB
JavaScript
280 lines
9.9 KiB
JavaScript
import { createHash } from 'node:crypto'
|
|
import { mkdirSync, mkdtempSync, readFileSync, rmSync, symlinkSync, writeFileSync } from 'node:fs'
|
|
import { tmpdir } from 'node:os'
|
|
import { dirname, join } from 'node:path'
|
|
import { runProcessSync } from '@orca/process-host'
|
|
import { afterEach, describe, expect, it } from 'vitest'
|
|
import {
|
|
javascriptInventory,
|
|
packReleaseJavascript,
|
|
releaseBuildIdentity,
|
|
releaseJavascriptConfiguration,
|
|
restoreReleaseJavascript
|
|
} from './release-javascript-artifact.mjs'
|
|
|
|
const directories = []
|
|
const configuration = {
|
|
sourceSha: 'a'.repeat(40),
|
|
version: '1.2.3-rc.4',
|
|
packageManager: 'pnpm@12.14.0',
|
|
identity: 'rc',
|
|
diagnosticsTokenUrl: 'https://example.test/diagnostics',
|
|
telemetryKeySha256: 'b'.repeat(64)
|
|
}
|
|
|
|
afterEach(() => {
|
|
for (const directory of directories.splice(0)) {
|
|
rmSync(directory, { recursive: true, force: true })
|
|
}
|
|
})
|
|
|
|
function fixture() {
|
|
const root = mkdtempSync(join(tmpdir(), 'orca-release-javascript-test-'))
|
|
directories.push(root)
|
|
for (const file of [
|
|
'cli/index.js',
|
|
'main/index.js',
|
|
'preload/index.js',
|
|
'renderer/index.html',
|
|
'renderer/.vite/manifest.json',
|
|
'renderer/.orca-web-client-files.json',
|
|
'web/web-index.html',
|
|
'mobile-web/manifest.json',
|
|
'package.json',
|
|
'renderer/wasm/viewer.wasm',
|
|
'main/index.js.map'
|
|
]) {
|
|
const destination = join(root, 'out', file)
|
|
mkdirSync(dirname(destination), { recursive: true })
|
|
writeFileSync(destination, file === 'cli/index.js' ? '#!/usr/bin/env node\n' : file)
|
|
}
|
|
const artifactDir = join(root, '.build', 'artifact')
|
|
return { root, artifactDir, configuration }
|
|
}
|
|
|
|
function configurationFixture(version = '1.2.3') {
|
|
const root = mkdtempSync(join(tmpdir(), 'orca-javascript-configuration-test-'))
|
|
directories.push(root)
|
|
const hooks = join(root, 'empty-hooks')
|
|
mkdirSync(hooks)
|
|
writeFileSync(
|
|
join(root, 'package.json'),
|
|
JSON.stringify({ version, packageManager: 'pnpm@12.14.0' })
|
|
)
|
|
for (const args of [
|
|
['init', '--quiet'],
|
|
[
|
|
'-c',
|
|
'user.name=Build test',
|
|
'-c',
|
|
'user.email=build@example.test',
|
|
'-c',
|
|
'commit.gpgsign=false',
|
|
'-c',
|
|
`core.hooksPath=${hooks}`,
|
|
'commit',
|
|
'--allow-empty',
|
|
'--quiet',
|
|
'-m',
|
|
'Build fixture'
|
|
]
|
|
]) {
|
|
const result = runProcessSync({ program: 'git', args, cwd: root, timeoutMs: 10_000 })
|
|
expect(result.code, result.stderr).toBe(0)
|
|
}
|
|
const env = {
|
|
ORCA_BUILD_IDENTITY: 'stable',
|
|
ORCA_DIAGNOSTICS_TOKEN_URL: 'https://example.test/diagnostics',
|
|
ORCA_POSTHOG_WRITE_KEY: 'release-key'
|
|
}
|
|
return { root, env }
|
|
}
|
|
|
|
describe('release JavaScript configuration', () => {
|
|
it.each([
|
|
['1.2.3', 'stable'],
|
|
['1.2.3-rc.4.perf', 'rc']
|
|
])('preserves official build checks for %s', (version, identity) => {
|
|
const { root, env } = configurationFixture(version)
|
|
const result = releaseJavascriptConfiguration(root, { ...env, ORCA_BUILD_IDENTITY: identity })
|
|
expect(result.identity).toBe(identity)
|
|
expect(result.telemetryKeySha256).toBe(
|
|
createHash('sha256').update(env.ORCA_POSTHOG_WRITE_KEY).digest('hex')
|
|
)
|
|
expect(result.sourceSha).toMatch(/^[a-f0-9]{40}$/)
|
|
})
|
|
|
|
it('requires explicit development mode and unset official constants', () => {
|
|
const { root, env } = configurationFixture('0.0.0-adhoc.test')
|
|
const devEnv = {
|
|
...env,
|
|
ORCA_RELEASE_JAVASCRIPT_BUILD_IDENTITY: 'dev',
|
|
ORCA_BUILD_IDENTITY: '',
|
|
ORCA_POSTHOG_WRITE_KEY: ''
|
|
}
|
|
expect(releaseJavascriptConfiguration(root, devEnv)).toMatchObject({
|
|
identity: 'dev',
|
|
version: '0.0.0-adhoc.test',
|
|
telemetryKeySha256: null,
|
|
diagnosticsTokenUrl: env.ORCA_DIAGNOSTICS_TOKEN_URL
|
|
})
|
|
expect(() =>
|
|
releaseJavascriptConfiguration(root, { ...devEnv, ORCA_BUILD_IDENTITY: 'stable' })
|
|
).toThrow('must not set an official build identity')
|
|
expect(() =>
|
|
releaseJavascriptConfiguration(root, { ...devEnv, ORCA_POSTHOG_WRITE_KEY: 'release-key' })
|
|
).toThrow('must not set a release telemetry key')
|
|
expect(() =>
|
|
releaseJavascriptConfiguration(root, {
|
|
...devEnv,
|
|
ORCA_RELEASE_JAVASCRIPT_BUILD_IDENTITY: ''
|
|
})
|
|
).toThrow('Invalid desktop release tag')
|
|
})
|
|
|
|
it('does not downgrade an official build when its identity or telemetry key is missing', () => {
|
|
const { root, env } = configurationFixture()
|
|
expect(() => releaseJavascriptConfiguration(root, { ...env, ORCA_BUILD_IDENTITY: '' })).toThrow(
|
|
'Release build identity differs from version'
|
|
)
|
|
expect(() =>
|
|
releaseJavascriptConfiguration(root, { ...env, ORCA_POSTHOG_WRITE_KEY: '' })
|
|
).toThrow('Missing release telemetry key')
|
|
expect(() =>
|
|
releaseJavascriptConfiguration(root, { ...env, ORCA_RELEASE_JAVASCRIPT_BUILD_IDENTITY: 'rc' })
|
|
).toThrow('Invalid JavaScript build identity')
|
|
expect(() =>
|
|
releaseJavascriptConfiguration(root, {
|
|
...env,
|
|
ORCA_RELEASE_JAVASCRIPT_BUILD_IDENTITY: 'typo'
|
|
})
|
|
).toThrow('Invalid JavaScript build identity')
|
|
})
|
|
|
|
it('checks the source checkout and diagnostics endpoint in development mode', () => {
|
|
const { root, env } = configurationFixture()
|
|
const devEnv = {
|
|
...env,
|
|
ORCA_RELEASE_JAVASCRIPT_BUILD_IDENTITY: 'dev',
|
|
ORCA_BUILD_IDENTITY: '',
|
|
ORCA_POSTHOG_WRITE_KEY: ''
|
|
}
|
|
expect(() =>
|
|
releaseJavascriptConfiguration(root, {
|
|
...devEnv,
|
|
ORCA_RELEASE_JAVASCRIPT_SOURCE_SHA: 'b'.repeat(40)
|
|
})
|
|
).toThrow('Release checkout differs from bundle source')
|
|
expect(() =>
|
|
releaseJavascriptConfiguration(root, { ...devEnv, ORCA_DIAGNOSTICS_TOKEN_URL: '' })
|
|
).toThrow('Missing release diagnostics URL')
|
|
})
|
|
})
|
|
|
|
describe('release JavaScript transfer', () => {
|
|
it('restores development bundles and rejects them for an official build', () => {
|
|
const options = fixture()
|
|
const devConfiguration = { ...configuration, identity: 'dev', telemetryKeySha256: null }
|
|
packReleaseJavascript({ ...options, configuration: devConfiguration })
|
|
expect(() => restoreReleaseJavascript(options)).toThrow(
|
|
'JavaScript build configuration differs'
|
|
)
|
|
expect(
|
|
restoreReleaseJavascript({ ...options, configuration: devConfiguration }).configuration
|
|
).toEqual(devConfiguration)
|
|
})
|
|
|
|
it('preserves hidden manifests, source maps and portable WASM while replacing stale outputs', () => {
|
|
const options = fixture()
|
|
const before = javascriptInventory(join(options.root, 'out'))
|
|
packReleaseJavascript(options)
|
|
writeFileSync(join(options.root, 'out', 'stale.js'), 'old')
|
|
restoreReleaseJavascript(options)
|
|
expect(javascriptInventory(join(options.root, 'out'))).toEqual(before)
|
|
})
|
|
|
|
it.each([
|
|
'sourceSha',
|
|
'version',
|
|
'packageManager',
|
|
'identity',
|
|
'diagnosticsTokenUrl',
|
|
'telemetryKeySha256'
|
|
])('rejects a different %s before replacing the current build', (field) => {
|
|
const options = fixture()
|
|
packReleaseJavascript(options)
|
|
writeFileSync(join(options.root, 'out', 'main', 'index.js'), 'current build')
|
|
writeFileSync(join(options.root, 'out', 'current-only.js'), 'current only')
|
|
const current = javascriptInventory(join(options.root, 'out'))
|
|
expect(() =>
|
|
restoreReleaseJavascript({
|
|
...options,
|
|
configuration: { ...configuration, [field]: 'different' }
|
|
})
|
|
).toThrow('JavaScript build configuration differs')
|
|
expect(javascriptInventory(join(options.root, 'out'))).toEqual(current)
|
|
})
|
|
|
|
it('rejects a damaged archive before replacing the current build', () => {
|
|
const options = fixture()
|
|
packReleaseJavascript(options)
|
|
writeFileSync(join(options.artifactDir, 'release-javascript.tar.gz'), 'damaged')
|
|
expect(() => restoreReleaseJavascript(options)).toThrow('JavaScript archive differs')
|
|
expect(readFileSync(join(options.root, 'out', 'main', 'index.js'), 'utf8')).toBe(
|
|
'main/index.js'
|
|
)
|
|
})
|
|
|
|
it('rejects a file inventory that no longer describes the archive', () => {
|
|
const options = fixture()
|
|
const manifest = packReleaseJavascript(options)
|
|
writeFileSync(join(options.root, 'out', 'main', 'index.js'), 'current build')
|
|
writeFileSync(join(options.root, 'out', 'current-only.js'), 'current only')
|
|
const current = javascriptInventory(join(options.root, 'out'))
|
|
manifest.files[0].sha256 = 'c'.repeat(64)
|
|
writeFileSync(join(options.artifactDir, 'manifest.json'), JSON.stringify(manifest))
|
|
expect(() => restoreReleaseJavascript(options)).toThrow('JavaScript file inventory differs')
|
|
expect(javascriptInventory(join(options.root, 'out'))).toEqual(current)
|
|
})
|
|
|
|
it.each([
|
|
'addon.node',
|
|
'launcher.exe',
|
|
'library.dll',
|
|
'library.dylib',
|
|
'library.so.1',
|
|
'relay/relay.js'
|
|
])('keeps %s out of the portable artifact', (file) => {
|
|
const options = fixture()
|
|
const destination = join(options.root, 'out', file)
|
|
mkdirSync(dirname(destination), { recursive: true })
|
|
writeFileSync(destination, 'host output')
|
|
expect(() => packReleaseJavascript(options)).toThrow(/native binary|host output/)
|
|
})
|
|
|
|
it.skipIf(process.platform === 'win32')(
|
|
'rejects symlinks instead of following files outside the build',
|
|
() => {
|
|
const options = fixture()
|
|
const outside = join(options.root, 'outside.js')
|
|
writeFileSync(outside, 'outside the build')
|
|
symlinkSync(outside, join(options.root, 'out', 'linked.js'))
|
|
expect(() => packReleaseJavascript(options)).toThrow('contains a symlink')
|
|
}
|
|
)
|
|
|
|
it('refuses incomplete build output', () => {
|
|
const options = fixture()
|
|
rmSync(join(options.root, 'out', 'preload', 'index.js'))
|
|
expect(() => packReleaseJavascript(options)).toThrow(
|
|
'Missing JavaScript output: preload/index.js'
|
|
)
|
|
})
|
|
|
|
it('classifies stable and suffixed RC versions and rejects other tag families', () => {
|
|
expect(releaseBuildIdentity('v1.2.3')).toBe('stable')
|
|
expect(releaseBuildIdentity('v1.2.3-rc.4.perf')).toBe('rc')
|
|
expect(() => releaseBuildIdentity('mobile-v1.2.3')).toThrow('Invalid desktop release tag')
|
|
})
|
|
})
|