Files
orca/config/scripts/release-mac-build-workflow-dispatch.test.mjs
T
OrcaWinandOrcaWin 2eb0bb637d test: strengthen hook, release, and mobile transport coverage (#33031)
* 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>
2026-10-23 21:53:27 -07:00

428 lines
14 KiB
JavaScript

import { describe, expect, it, vi } from 'vitest'
import {
executeReleaseMacBuildMode,
expectedReleaseMacBuildRunTitle,
readReleaseMacBuildWorkflowOptions,
runReleaseMacBuildWorkflow
} from './run-release-mac-build-workflow.mjs'
const baseOptions = {
apiBaseUrl: 'https://api.github.test',
pollSeconds: 1,
ref: 'main',
releaseRunId: '777',
repo: 'stablyai/orca',
tag: 'v1.2.3-rc.4',
timeoutMinutes: 2,
token: 'token',
workflow: 'release-mac-build.yml'
}
describe('release mac build workflow dispatch', () => {
it('passes the shared JavaScript commit only when the parent produced a bundle', async () => {
const { fetch, requests } = createGitHubFetch([
jsonResponse(200, { workflow_run_id: 123 }),
jsonResponse(200, { id: 123, status: 'completed', conclusion: 'success' })
])
await runReleaseMacBuildWorkflow(
{ ...baseOptions, javascriptSourceSha: 'a'.repeat(40) },
{ fetch, now: () => Date.parse('2026-06-30T12:00:00Z'), sleep: vi.fn() }
)
expect(requests[0].body.inputs).toEqual({
release_run_id: '777',
tag: 'v1.2.3-rc.4',
javascript_source_sha: 'a'.repeat(40)
})
})
it('dispatches the mac workflow and waits for the returned run id', async () => {
const { fetch, requests } = createGitHubFetch([
jsonResponse(200, {
html_url: 'https://github.test/stablyai/orca/actions/runs/123',
workflow_run_id: 123
}),
jsonResponse(200, {
conclusion: 'success',
html_url: 'https://github.test/stablyai/orca/actions/runs/123',
id: 123,
status: 'completed'
})
])
const completedRun = await runReleaseMacBuildWorkflow(baseOptions, {
fetch,
now: () => Date.parse('2026-06-30T12:00:00Z'),
sleep: vi.fn()
})
expect(completedRun.conclusion).toBe('success')
expect(requests[0].method).toBe('POST')
expect(requests[0].path).toBe(
'/repos/stablyai/orca/actions/workflows/release-mac-build.yml/dispatches'
)
expect(requests[0].body).toEqual({
inputs: {
release_run_id: '777',
tag: 'v1.2.3-rc.4'
},
ref: 'main'
})
expect(requests[1].path).toBe('/repos/stablyai/orca/actions/runs/123')
})
it('finds the dispatched run by release tag and parent run id when GitHub returns no body', async () => {
const runTitle = expectedReleaseMacBuildRunTitle(baseOptions)
const { fetch, requests } = createGitHubFetch([
jsonResponse(204, null),
jsonResponse(200, {
workflow_runs: [
{
created_at: '2026-06-30T11:59:00Z',
display_title: runTitle,
id: 122
},
{
created_at: '2026-06-30T12:00:01Z',
display_title: runTitle,
html_url: 'https://github.test/stablyai/orca/actions/runs/124',
id: 124
}
]
}),
jsonResponse(200, {
conclusion: 'success',
html_url: 'https://github.test/stablyai/orca/actions/runs/124',
id: 124,
status: 'completed'
})
])
const completedRun = await runReleaseMacBuildWorkflow(baseOptions, {
fetch,
now: () => Date.parse('2026-06-30T12:00:05Z'),
sleep: vi.fn()
})
expect(completedRun.id).toBe(124)
expect(requests[1].path).toBe(
'/repos/stablyai/orca/actions/workflows/release-mac-build.yml/runs'
)
expect(requests[1].query.get('event')).toBe('workflow_dispatch')
})
it('fails when the isolated mac workflow does not succeed', async () => {
const { fetch } = createGitHubFetch([
jsonResponse(200, {
html_url: 'https://github.test/stablyai/orca/actions/runs/125',
workflow_run_id: 125
}),
jsonResponse(200, {
conclusion: 'failure',
html_url: 'https://github.test/stablyai/orca/actions/runs/125',
id: 125,
status: 'completed'
})
])
await expect(
runReleaseMacBuildWorkflow(baseOptions, {
fetch,
now: () => Date.parse('2026-06-30T12:00:00Z'),
sleep: vi.fn()
})
).rejects.toThrow(/concluded failure/)
})
it('does not accept a combined-mode success first observed after its polling deadline', async () => {
let currentTime = Date.parse('2026-06-30T12:00:00Z')
const { fetch, requests } = createGitHubFetch([
jsonResponse(200, { workflow_run_id: 123 }),
jsonResponse(200, { id: 123, status: 'in_progress', conclusion: null }),
jsonResponse(200, { id: 123, status: 'completed', conclusion: 'success' })
])
await expect(
runReleaseMacBuildWorkflow(baseOptions, {
fetch,
now: () => currentTime,
sleep: async () => {
currentTime += 3 * 60 * 1000
}
})
).rejects.toThrow(/Timed out after 2m/)
expect(requests.map((request) => request.method)).toEqual(['POST', 'GET'])
})
it('reads required workflow settings from the GitHub Actions environment', () => {
const options = readReleaseMacBuildWorkflowOptions({
GITHUB_REPOSITORY: 'stablyai/orca',
GITHUB_RUN_ID: '987',
GITHUB_TOKEN: 'token',
RELEASE_MAC_BUILD_REF: 'main',
RELEASE_MAC_BUILD_TAG: 'v1.2.3'
})
expect(options.releaseRunId).toBe('987')
expect(options.workflow).toBe('release-mac-build.yml')
expect(options.pollSeconds).toBe(30)
expect(options.timeoutMinutes).toBe(90)
})
})
const splitEnvironment = {
GITHUB_REPOSITORY: 'stablyai/orca',
GITHUB_RUN_ID: '777',
GITHUB_TOKEN: 'token',
GITHUB_OUTPUT: '/tmp/mac-build-output',
RELEASE_MAC_BUILD_REF: 'main',
RELEASE_MAC_BUILD_TAG: 'v1.2.3-rc.4',
RELEASE_MAC_BUILD_RUN_ID: '123',
RELEASE_MAC_BUILD_WORKFLOW_SHA: 'a'.repeat(40),
RELEASE_MAC_BUILD_RUN_ATTEMPT: '1',
RELEASE_MAC_BUILD_WAIT_DEADLINE_MS: String(Date.parse('2026-06-30T13:30:00Z'))
}
const splitRun = {
id: 123,
run_attempt: 1,
head_sha: 'a'.repeat(40),
head_branch: 'main',
path: '.github/workflows/release-mac-build.yml',
event: 'workflow_dispatch',
display_title: 'Mac release build v1.2.3-rc.4 (777)',
html_url: 'https://github.test/stablyai/orca/actions/runs/123',
updated_at: '2026-06-30T12:10:00Z',
status: 'completed',
conclusion: 'success'
}
describe('release mac build split dispatch and verification', () => {
it('waits for the exact dispatched run metadata to become consistent', async () => {
const { fetch, requests } = createGitHubFetch([
jsonResponse(200, { workflow_run_id: 123 }),
jsonResponse(200, { ...splitRun, display_title: 'Release macOS Build', run_attempt: 0 }),
jsonResponse(200, splitRun)
])
const sleep = vi.fn()
const appendOutput = vi.fn()
await expect(
executeReleaseMacBuildMode('dispatch', splitEnvironment, { fetch, sleep, appendOutput })
).resolves.toMatchObject({ id: 123 })
expect(requests.map((request) => request.method)).toEqual(['POST', 'GET', 'GET'])
expect(requests.slice(1).map((request) => request.path)).toEqual([
'/repos/stablyai/orca/actions/runs/123',
'/repos/stablyai/orca/actions/runs/123'
])
expect(sleep).toHaveBeenCalledWith(5_000)
expect(appendOutput).toHaveBeenCalledOnce()
})
it('rejects persistently mismatched metadata without redispatching or recording outputs', async () => {
const { fetch, requests } = createGitHubFetch([
jsonResponse(200, { workflow_run_id: 123 }),
...Array.from({ length: 7 }, () =>
jsonResponse(200, { ...splitRun, display_title: 'Another release' })
)
])
const sleep = vi.fn()
const appendOutput = vi.fn()
await expect(
executeReleaseMacBuildMode('dispatch', splitEnvironment, { fetch, sleep, appendOutput })
).rejects.toThrow(/does not match/)
expect(requests.filter((request) => request.method === 'POST')).toHaveLength(1)
expect(sleep).toHaveBeenCalledTimes(6)
expect(appendOutput).not.toHaveBeenCalled()
})
it('returns a live run identity without holding a runner while macOS builds', async () => {
const live = { ...splitRun, status: 'in_progress', conclusion: null }
const { fetch, requests } = createGitHubFetch([
jsonResponse(200, { workflow_run_id: 123, html_url: live.html_url }),
jsonResponse(200, live)
])
const appendOutput = vi.fn()
const result = await executeReleaseMacBuildMode('dispatch', splitEnvironment, {
fetch,
appendOutput,
now: () => Date.parse('2026-06-30T12:00:00Z')
})
expect(result.status).toBe('in_progress')
expect(requests.map((request) => request.method)).toEqual(['POST', 'GET'])
expect(requests[0].headers['X-GitHub-Api-Version']).toBe('2026-03-10')
expect(appendOutput).toHaveBeenCalledWith(
splitEnvironment.GITHUB_OUTPUT,
`run_id=123\nworkflow_sha=${splitRun.head_sha}\nrun_attempt=1\nwait_deadline_ms=${splitEnvironment.RELEASE_MAC_BUILD_WAIT_DEADLINE_MS}\n`
)
})
it.each([null, {}, { workflow_run_id: 0 }, { workflow_run_id: -1 }, { workflow_run_id: 1.5 }])(
'fails closed when a dispatch response cannot identify its exact child: %j',
async (response) => {
const { fetch, requests } = createGitHubFetch([
jsonResponse(response == null ? 204 : 200, response),
jsonResponse(200, { workflow_runs: [splitRun] })
])
const appendOutput = vi.fn()
await expect(
executeReleaseMacBuildMode('dispatch', splitEnvironment, {
fetch,
appendOutput,
now: () => Date.parse('2026-06-30T12:00:00Z')
})
).rejects.toThrow(/refusing ambiguous discovery/)
expect(requests.map((request) => request.method)).toEqual(['POST'])
expect(appendOutput).not.toHaveBeenCalled()
}
)
it('verifies the recorded run without issuing another dispatch', async () => {
const { fetch, requests } = createGitHubFetch([jsonResponse(200, splitRun)])
const result = await executeReleaseMacBuildMode('wait', splitEnvironment, {
fetch,
now: () => Date.parse('2026-06-30T12:11:00Z')
})
expect(result.id).toBe(123)
expect(requests.map((request) => request.method)).toEqual(['GET'])
})
it.each(['failure', 'cancelled', 'timed_out', 'action_required', null])(
'blocks publication for the terminal conclusion %s',
async (conclusion) => {
const { fetch } = createGitHubFetch([jsonResponse(200, { ...splitRun, conclusion })])
await expect(
executeReleaseMacBuildMode('wait', splitEnvironment, {
fetch,
now: () => Date.parse('2026-06-30T12:11:00Z')
})
).rejects.toThrow(/concluded/)
}
)
it.each([
['id', 124],
['head_sha', 'b'.repeat(40)],
['head_branch', 'other'],
['run_attempt', 2],
['event', 'pull_request'],
['path', '.github/workflows/other.yml'],
['display_title', 'Mac release build v1.2.4 (777)']
])('rejects a changed %s before inspecting success', async (field, value) => {
const { fetch } = createGitHubFetch([jsonResponse(200, { ...splitRun, [field]: value })])
await expect(
executeReleaseMacBuildMode('wait', splitEnvironment, {
fetch,
now: () => Date.parse('2026-06-30T12:11:00Z')
})
).rejects.toThrow(/does not match/)
})
it('accepts a run completed within its original deadline after a slow Windows build', async () => {
const { fetch } = createGitHubFetch([jsonResponse(200, splitRun)])
await expect(
executeReleaseMacBuildMode('wait', splitEnvironment, {
fetch,
now: () => Date.parse('2026-06-30T17:00:00Z')
})
).resolves.toMatchObject({ conclusion: 'success' })
})
it('rejects a successful run that exceeded its original deadline', async () => {
const { fetch } = createGitHubFetch([
jsonResponse(200, { ...splitRun, updated_at: '2026-06-30T13:31:00Z' })
])
await expect(
executeReleaseMacBuildMode('wait', splitEnvironment, {
fetch,
now: () => Date.parse('2026-06-30T17:00:00Z')
})
).rejects.toThrow(/exceeded its original deadline/)
})
it('permits only the first late wait-only observation', async () => {
let currentTime = Date.parse('2026-06-30T13:29:59Z')
const { fetch, requests } = createGitHubFetch([
jsonResponse(200, { ...splitRun, status: 'in_progress', conclusion: null }),
jsonResponse(200, splitRun)
])
await expect(
executeReleaseMacBuildMode('wait', splitEnvironment, {
fetch,
now: () => currentTime,
sleep: async () => {
currentTime += 2_000
}
})
).rejects.toThrow(/Timed out/)
expect(requests).toHaveLength(1)
})
it('does not reset the polling deadline after the Windows build finishes', async () => {
const { fetch, requests } = createGitHubFetch([
jsonResponse(200, { ...splitRun, status: 'in_progress', conclusion: null })
])
const sleep = vi.fn()
await expect(
executeReleaseMacBuildMode('wait', splitEnvironment, {
fetch,
sleep,
now: () => Date.parse('2026-06-30T17:00:00Z')
})
).rejects.toThrow(/Timed out/)
expect(requests).toHaveLength(1)
expect(sleep).not.toHaveBeenCalled()
})
it.each([
['RELEASE_MAC_BUILD_RUN_ID', ''],
['RELEASE_MAC_BUILD_RUN_ID', '../456'],
['RELEASE_MAC_BUILD_WORKFLOW_SHA', ''],
['RELEASE_MAC_BUILD_RUN_ATTEMPT', ''],
['RELEASE_MAC_BUILD_WAIT_DEADLINE_MS', '']
])('fails closed without valid %s', async (field, value) => {
const fetch = vi.fn()
await expect(
executeReleaseMacBuildMode(
'wait',
{
...splitEnvironment,
[field]: value
},
{ fetch }
)
).rejects.toThrow()
expect(fetch).not.toHaveBeenCalled()
})
})
function createGitHubFetch(responses) {
const requests = []
const fetch = vi.fn(async (rawUrl, init) => {
const url = new URL(rawUrl)
const response = responses.shift()
if (response == null) {
throw new Error(`Unexpected request: ${init.method} ${rawUrl}`)
}
requests.push({
body: init.body == null ? undefined : JSON.parse(init.body),
method: init.method,
headers: init.headers,
path: url.pathname,
query: url.searchParams
})
return response
})
return { fetch, requests }
}
function jsonResponse(status, body) {
return {
ok: status >= 200 && status < 300,
status,
async text() {
return body == null ? '' : JSON.stringify(body)
}
}
}