mirror of
https://github.com/stablyai/orca.git
synced 2026-11-15 10:27:33 +00:00
* refactor(tooling): organize test runtime and maintenance owners * test(tooling): preserve relocated runtime fixture dependency edges * refactor(tooling): preserve xterm routing owner checks * ci(tooling): include routing owner in sparse checkout * test(tooling): preserve copied workflow dependency closures * fix(tooling): normalize hashed JSON assets in release parity * fix(tooling): distinguish asset aliases from literal references * fix(tooling): frame normalized asset reference payloads * fix(tooling): normalize decoded JSON asset references * refactor(tooling): organize build, CI and release owners * fix(tooling): align moved generator and workflow path contracts * fix(tooling): update generated plugin bundle owner banners * test(tooling): create relocated mobile verifier fixture directories * test(tooling): copy headless detector dependency closure * test(tooling): align native batch policy fixtures with owner path * fix(tooling): link moved reliability command to historical evidence * fix(tooling): preserve nested sandbox owners and canonical guide source * fix(tooling): target release version owners in checkout fixture
134 lines
5.4 KiB
JavaScript
134 lines
5.4 KiB
JavaScript
import { describe, expect, it } from 'vitest'
|
||
import {
|
||
createHourlyBuildVersion,
|
||
formatHourlyReleaseName,
|
||
getHourlyBuildIdentity,
|
||
nextHourlyBuildNumber
|
||
} from './hourly-build-version.mjs'
|
||
import { compareAppVersions } from '../../../../src/shared/app-version'
|
||
|
||
describe('createHourlyBuildVersion', () => {
|
||
it('stamps the version with a zero-padded UTC timestamp', () => {
|
||
expect(createHourlyBuildVersion('1.4.160', new Date('2026-07-28T04:05:00Z'))).toBe(
|
||
'1.4.160-hourly.202607280405'
|
||
)
|
||
})
|
||
|
||
// RC-tail hourlies outrank RC; drop tail so routine checks cannot offer untested builds over RC/stable.
|
||
it('drops an in-flight rc tail so hourlies never outrank the rc series', () => {
|
||
const version = createHourlyBuildVersion('1.4.160-rc.3', new Date('2026-07-28T14:00:00Z'))
|
||
expect(version).toBe('1.4.160-hourly.202607281400')
|
||
expect(compareAppVersions(version, '1.4.160-rc.3')).toBeLessThan(0)
|
||
})
|
||
|
||
it('rejects invalid input', () => {
|
||
expect(() => createHourlyBuildVersion('nope', new Date())).toThrow(/valid semver/)
|
||
expect(() => createHourlyBuildVersion('1.4.160', new Date('nope'))).toThrow(/invalid/)
|
||
})
|
||
})
|
||
|
||
describe('formatHourlyReleaseName', () => {
|
||
const name = (iso, buildNumber = 1, commit = 'e698241abcde') =>
|
||
formatHourlyReleaseName('1.4.163-hourly.x', buildNumber, commit, new Date(iso))
|
||
|
||
it('renders version, number, Pacific timestamp, and short sha', () => {
|
||
expect(name('2026-07-31T20:54:00Z')).toBe('1.4.163 • 01 • Jul 31, 1:54PM • e698241')
|
||
})
|
||
|
||
// Tag is UTC, title Pacific; test both DST offsets to catch seasonal failures.
|
||
it('follows the Pacific offset across DST', () => {
|
||
expect(name('2026-01-15T02:30:00Z')).toBe('1.4.163 • 01 • Jan 14, 6:30PM • e698241')
|
||
expect(name('2026-07-31T07:00:00Z')).toBe('1.4.163 • 01 • Jul 31, 12:00AM • e698241')
|
||
})
|
||
|
||
it('renders noon as 12', () => {
|
||
expect(name('2026-07-31T19:00:00Z')).toContain('12:00PM')
|
||
})
|
||
|
||
it('pads to two digits and grows past them', () => {
|
||
expect(name('2026-07-31T20:54:00Z', 9)).toContain(' • 09 • ')
|
||
expect(name('2026-07-31T20:54:00Z', 42)).toContain(' • 42 • ')
|
||
expect(name('2026-07-31T20:54:00Z', 1234)).toContain(' • 1234 • ')
|
||
})
|
||
|
||
it('rejects a build number that is not a positive integer', () => {
|
||
expect(() => name('2026-07-31T20:54:00Z', 0)).toThrow(/positive integer/)
|
||
expect(() => name('2026-07-31T20:54:00Z', -1)).toThrow(/positive integer/)
|
||
expect(() => name('2026-07-31T20:54:00Z', 1.5)).toThrow(/positive integer/)
|
||
expect(() => name('2026-07-31T20:54:00Z', Number.NaN)).toThrow(/positive integer/)
|
||
})
|
||
|
||
it('rejects an invalid timestamp', () => {
|
||
expect(() => formatHourlyReleaseName('1.4.163', 1, 'abcdefg', new Date('nope'))).toThrow(
|
||
/invalid/
|
||
)
|
||
})
|
||
})
|
||
|
||
describe('nextHourlyBuildNumber', () => {
|
||
const titles = [
|
||
'1.4.163 • 01 • Jul 31, 1:54PM • e698241',
|
||
'1.4.163 • 02 • Jul 31, 2:54PM • aaaaaaa',
|
||
'1.4.163 • 37 • Aug 01, 9:00AM • bbbbbbb'
|
||
]
|
||
|
||
it('continues the series for the version being built', () => {
|
||
expect(nextHourlyBuildNumber('1.4.163', titles)).toBe(38)
|
||
})
|
||
|
||
// New patch starts at 01 instead of inheriting unrelated prior-patch build count.
|
||
it('restarts at 1 when the base version moves', () => {
|
||
expect(nextHourlyBuildNumber('1.4.164', titles)).toBe(1)
|
||
expect(
|
||
nextHourlyBuildNumber('1.4.164', [...titles, '1.4.164 • 01 • Aug 02, 8:00AM • ccccccc'])
|
||
).toBe(2)
|
||
})
|
||
|
||
// Retention pruning shrinks count; max avoids reissuing numbers.
|
||
it('takes the highest number, not the count', () => {
|
||
expect(nextHourlyBuildNumber('1.4.163', ['1.4.163 • 09 • Jul 31, 1:54PM • e698241'])).toBe(10)
|
||
})
|
||
|
||
it('starts at 1 with no history at all', () => {
|
||
expect(nextHourlyBuildNumber('1.4.163')).toBe(1)
|
||
expect(nextHourlyBuildNumber('1.4.163', [])).toBe(1)
|
||
})
|
||
|
||
// Legacy raw-tag titles must not make 1.4.16 match 1.4.163’s series.
|
||
it('ignores titles that are not this version', () => {
|
||
expect(nextHourlyBuildNumber('1.4.16', ['1.4.163 • 37 • Aug 01, 9:00AM • bbbbbbb'])).toBe(1)
|
||
expect(nextHourlyBuildNumber('1.4.163', ['v1.4.163-hourly.202607311354', null, ''])).toBe(1)
|
||
})
|
||
})
|
||
|
||
describe('getHourlyBuildIdentity', () => {
|
||
// Deleted stable release must not reset hourlies below installed 1.4.203; retained/channel tags keep floor.
|
||
it('stays on the already-shipped hourly base after a buggy main release is unpublished', () => {
|
||
const identity = getHourlyBuildIdentity(new Date('2026-09-14T20:00:00Z'), {
|
||
packageVersion: '1.4.201',
|
||
publishedVersions: [
|
||
'v1.4.201',
|
||
'v1.4.202',
|
||
'v1.4.202-hourly.202609141912',
|
||
'v1.4.203-hourly.202609140417'
|
||
],
|
||
releaseNames: [
|
||
'1.4.202 • 14 • Sep 14, 12:12PM • 875b86d',
|
||
'1.4.203 • 04 • Sep 13, 9:17PM • 2ce252f'
|
||
]
|
||
})
|
||
expect(identity.version).toBe('1.4.203-hourly.202609142000')
|
||
expect(identity.buildNumber).toBe(5)
|
||
})
|
||
|
||
it('keeps a newer package version as the hourly base floor', () => {
|
||
const identity = getHourlyBuildIdentity(new Date('2026-09-14T20:00:00Z'), {
|
||
packageVersion: '1.4.214',
|
||
publishedVersions: ['v1.4.202', 'v1.4.203-hourly.202609140417'],
|
||
releaseNames: ['1.4.203 • 04 • Sep 13, 9:17PM • 2ce252f']
|
||
})
|
||
expect(identity.version).toBe('1.4.214-hourly.202609142000')
|
||
expect(identity.buildNumber).toBe(1)
|
||
})
|
||
})
|