Files
orca/config/scripts/daemon-protocol-facts.test.mjs
T
OrcaWinandm4air 2a83c9536f ci(daemon): gate PRs on daemon protocol crossing from the newest release (#24089)
Lands daemon-protocol-facts.mjs from the Windows update diagnostic branch with a
stricter parser, and adds check-daemon-protocol-crossing.mjs (rule R1): the working
tree must attach the newest release tag's daemon. Rollback crossing is reported only.
Runs in the cross-version-wire job, which already has full tags; tag selection moves
to config/scripts/stable-release-tags.mjs so both use one rule.

Co-authored-by: m4air <m4air@m4airs-Air.localdomain>
2026-09-30 23:23:28 -07:00

91 lines
3.2 KiB
JavaScript

import { readFileSync } from 'node:fs'
import { join, resolve } from 'node:path'
import { describe, expect, it } from 'vitest'
import {
DAEMON_PROTOCOL_SOURCE_PATH,
canAttach,
crossingRequirements,
parseDaemonProtocolFacts
} from './daemon-protocol-facts.mjs'
const projectDir = resolve(import.meta.dirname, '../..')
function source(current, previous) {
return [
`export const PROTOCOL_VERSION = ${current}`,
'export const OTHER_DAEMON_PROTOCOL_VERSION = 5',
`export const PREVIOUS_DAEMON_PROTOCOL_VERSIONS = [${previous}] as const`
].join('\n')
}
const facts = (protocolVersion, previousProtocolVersions) => ({
protocolVersion,
previousProtocolVersions
})
describe('parseDaemonProtocolFacts', () => {
it('reads the working tree declarations and keeps the append-only range', () => {
const parsed = parseDaemonProtocolFacts(
readFileSync(join(projectDir, DAEMON_PROTOCOL_SOURCE_PATH), 'utf8')
)
expect(parsed.protocolVersion).toBeGreaterThan(1)
expect(parsed.previousProtocolVersions).toEqual(
Array.from({ length: parsed.protocolVersion - 1 }, (_, index) => index + 1)
)
})
it('parses multi-line lists and type annotations', () => {
const text = [
'// PROTOCOL_VERSION = 99 in a comment is ignored',
'export const PROTOCOL_VERSION: number = 4',
'export const PREVIOUS_DAEMON_PROTOCOL_VERSIONS: readonly number[] = [',
' 1, 2,',
' 3,',
']'
].join('\n')
expect(parseDaemonProtocolFacts(text)).toEqual(facts(4, [1, 2, 3]))
})
it.each([
['a missing PROTOCOL_VERSION', 'export const PREVIOUS_DAEMON_PROTOCOL_VERSIONS = [1]'],
['a missing previous list', 'export const PROTOCOL_VERSION = 2'],
['a non-literal PROTOCOL_VERSION', source('NEXT_VERSION', '1')],
['a spread in the previous list', source(3, '...LEGACY, 2')],
['a comment in the previous list', source(3, '1, // legacy\n 2')],
['an empty previous list past v1', source(3, '')],
['a previous version at or above current', source(3, '1, 2, 3')],
['a duplicate declaration', `${source(3, '1, 2')}\nexport const PROTOCOL_VERSION = 4`],
[
'a list moved behind a constant',
'export const PROTOCOL_VERSION = 3\nexport const PREVIOUS_DAEMON_PROTOCOL_VERSIONS = LEGACY'
]
])('fails loudly on %s', (_name, text) => {
expect(() => parseDaemonProtocolFacts(text, 'fixture.ts')).toThrow(/fixture\.ts/)
})
})
describe('protocol crossing', () => {
const release = facts(37, [36, 35])
it('lets a same-version or listing candidate adopt the release daemon', () => {
expect(canAttach(facts(37, [36, 35]), release)).toBe(true)
expect(canAttach(facts(38, [37, 36, 35]), release)).toBe(true)
})
it('rejects a candidate that dropped the release version', () => {
expect(canAttach(facts(38, [36, 35]), release)).toBe(false)
})
it('cannot roll back past a protocol bump', () => {
expect(canAttach(release, facts(38, [37, 36, 35]))).toBe(false)
expect(canAttach(release, facts(36, [35]))).toBe(true)
})
it('describes the requirements in both directions', () => {
expect(crossingRequirements(release)).toMatchObject({
upgrade: 'candidate speaks 37 or lists 37 as previous',
rollback: expect.stringContaining('35..37')
})
})
})