Files
OpenResearch/scripts/ci-changes.test.mjs
T
2026-10-28 13:06:55 -07:00

104 lines
5.9 KiB
JavaScript

import test from 'node:test'
import assert from 'node:assert/strict'
import { execFileSync } from 'node:child_process'
import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from 'node:fs'
import { tmpdir } from 'node:os'
import { join } from 'node:path'
import { pathToFileURL } from 'node:url'
import { changedChecks, classifyChanges } from './ci-changes.mjs'
import { checkResults } from './ci-check-result.mjs'
const enabled = paths => Object.entries(classifyChanges(paths)).filter(([, value]) => value).map(([name]) => name)
test('frontend changes do not select Rust', () => {
assert.deepEqual(enabled(['ui/src/App.tsx', 'ui/pnpm-lock.yaml']), ['frontend'])
})
test('Rust and embedded resources select all existing Rust platforms', () => {
for (const path of ['src/main.rs', 'tests/compute_cli.rs', 'Cargo.lock', 'build.rs', 'agent-skills/orx-compute/SKILL.md', 'demo/nanochat/base/file.txt', 'SYSTEM_PROMPT.md', 'SKILL.md']) {
assert.deepEqual(enabled([path]), ['rust', 'windows', 'linux', 'macos'], path)
}
})
test('platform files select corresponding checks', () => {
assert.deepEqual(enabled(['windows/launcher/src/main.rs']), ['windows'])
assert.deepEqual(enabled(['linux/OpenResearch.png']), ['linux'])
assert.deepEqual(enabled(['macos/Info.plist']), ['rust', 'macos'])
})
test('helper tests are independent and combine with other changes', () => {
assert.deepEqual(enabled(['scripts/desktop.js']), ['dev_slot'])
assert.deepEqual(enabled(['scripts/dev-slot.mjs', 'scripts/test_orx_chart.py', 'scripts/release_notes.py', 'ui/package.json']), ['frontend', 'dev_slot', 'chart', 'release_notes'])
})
test('docs skip builds, workflow changes and unknown files select all', () => {
assert.deepEqual(enabled(['README.md', 'docs/linux.md', 'LICENSE', '.github/readme-assets/banner.png', 'macos/DISTRIBUTION.md', 'windows/README.md', 'ui/README.md']), [])
for (const path of ['.github/workflows/ci.yml', 'scripts/ci-changes.mjs', '.cargo/config.toml', 'new-build-input']) assert.equal(enabled([path]).length, 9)
})
test('embedded chart changes also run chart tests', () => {
assert.equal(classifyChanges(['agent-skills/orx-figures/assets/orx_chart.py']).chart, true)
})
test('diff reports both deleted and added paths for renames', () => {
const calls = []
const checks = changedChecks({ base: 'base', head: 'merge' }, args => {
calls.push(args)
return args[0] === 'diff' ? 'ui/old.ts\0src/new.rs\0' : 'sha\n'
})
assert.equal(checks.frontend, true)
assert.equal(checks.rust, true)
assert.deepEqual(calls[2], ['diff', '--name-only', '--no-renames', '-z', 'base', 'merge', '--'])
})
test('unavailable comparisons and forced runs select everything', () => {
for (const options of [{}, { base: '000000' }, { base: 'missing' }, { forceAll: true }]) {
const checks = changedChecks(options, () => { throw new Error('missing commit') })
assert.equal(Object.values(checks).every(Boolean), true)
}
})
const flags = Object.fromEntries(Object.entries(classifyChanges(['ui/src/App.tsx'])).map(([key, value]) => [key, String(value)]))
const needs = { changes: { result: 'success' }, frontend: { result: 'success' }, rust: { result: 'skipped' }, windows: { result: 'skipped' }, 'linux-app': { result: 'skipped' }, helpers: { result: 'skipped' } }
test('aggregate accepts intentional skips and successful selected jobs', () => checkResults(needs, flags))
test('aggregate rejects failures, cancellations, missing and unexpectedly skipped jobs', () => {
for (const result of ['failure', 'cancelled', 'skipped', undefined]) assert.throws(() => checkResults({ ...needs, frontend: { result } }, flags))
assert.throws(() => checkResults({ ...needs, rust: { result: 'failure' } }, flags))
assert.throws(() => checkResults({ ...needs, changes: { result: 'failure' } }, flags))
assert.throws(() => checkResults(needs, { ...flags, rust: undefined }))
})
test('backend and embedded JavaScript inputs run their suites without a frontend build', () => {
for (const path of ['src/local/acp/zcode-runtime.cjs', 'src/commands/app.rs', 'agent-skills/orx-figures/assets/orx-chart.html']) {
const checks = classifyChanges([path])
assert.equal(checks.javascript, true, path)
assert.equal(checks.frontend, false, path)
}
const selected = { ...flags, frontend: 'false', javascript: 'true' }
assert.throws(() => checkResults({ ...needs, frontend: { result: 'skipped' } }, selected))
checkResults({ ...needs, frontend: { result: 'skipped' }, helpers: { result: 'success' } }, selected)
})
test('shallow checkouts fetch only the missing comparison commit', () => {
const root = mkdtempSync(join(tmpdir(), 'orx-ci-shallow-'))
const source = join(root, 'source')
const clone = join(root, 'clone')
mkdirSync(source)
const gitAt = cwd => args => execFileSync('git', args, { cwd, encoding: 'utf8', stdio: ['ignore', 'pipe', 'pipe'] }).trim()
const git = gitAt(source)
try {
git(['init', '-b', 'main'])
git(['config', 'user.name', 'CI test'])
git(['config', 'user.email', 'ci-test@example.invalid'])
writeFileSync(join(source, 'README.md'), 'base')
git(['add', '.']); git(['commit', '-m', 'base'])
const base = git(['rev-parse', 'HEAD'])
mkdirSync(join(source, 'ui'))
writeFileSync(join(source, 'ui', 'app.ts'), 'changed')
git(['add', '.']); git(['commit', '-m', 'frontend'])
git(['clone', '--depth=1', pathToFileURL(source).href, clone])
const shallow = gitAt(clone)
assert.throws(() => shallow(['cat-file', '-e', base]))
const calls = []
const checks = changedChecks({ base }, args => { calls.push(args); return shallow(args) })
assert.equal(checks.frontend, true)
assert.equal(checks.rust, false)
assert.deepEqual(calls.find(args => args[0] === 'fetch'), ['fetch', '--no-tags', '--depth=1', 'origin', base])
assert.equal(shallow(['rev-parse', '--is-shallow-repository']), 'true')
assert.equal(shallow(['rev-list', '--count', 'HEAD']), '1')
} finally {
rmSync(root, { recursive: true, force: true })
}
})