import { readFileSync } from 'node:fs' import { describe, expect, it } from 'vitest' import { BUNDLED_AGENT_STATE_RULES_VERSION, LIVE_UPDATABLE_AGENT_STATE_RULE_IDS, parseAgentStateRulesBundle } from '../../src/main/runtime/agent-state-rules/agent-state-rules-bundle.ts' import { BUNDLED_AGENT_STATE_RULE_FILES } from '../../src/main/runtime/agent-state-rules/agent-state-rules-catalog.ts' import { agentStateRulesDownloadUrl } from '../../src/main/runtime/agent-state-rules/agent-state-rules-live-update.ts' import { AGENT_STATE_RULES_ENGINE_VERSION } from '../../src/main/runtime/agent-state-rules/agent-state-rules-schema.ts' import { AGENT_STATE_RULES_ASSET, buildAgentStateRulesBundle, promoteAgentStateRules, publishAgentStateRules } from './agent-state-rules-bundle.mjs' import { agentStateRulesTag } from './release-tag-patterns.mjs' const REPO = 'stablyai/orca' const NEXT = agentStateRulesTag(1, 'next') const STABLE = agentStateRulesTag(1, 'stable') describe('agent state rules bundle build', () => { it('builds a bundle the app accepts, under the bundled version and engine', () => { const text = buildAgentStateRulesBundle() const parsed = parseAgentStateRulesBundle(text, 'live-updatable') expect(parsed.ok).toBe(true) const bundle = JSON.parse(text) expect(bundle.engineVersion).toBe(AGENT_STATE_RULES_ENGINE_VERSION) expect(bundle.version).toBe(BUNDLED_AGENT_STATE_RULES_VERSION) expect(bundle.files).toEqual( BUNDLED_AGENT_STATE_RULE_FILES.filter((file) => LIVE_UPDATABLE_AGENT_STATE_RULE_IDS.has(file.id) ) ) expect(bundle.bundledOnly).toBeUndefined() expect(JSON.parse(buildAgentStateRulesBundle({ bundledOnly: true })).bundledOnly).toBe(true) }) it('publishes to the exact URL the app fetches', () => { for (const channel of ['next', 'stable']) { expect(agentStateRulesDownloadUrl(channel)).toBe( `https://github.com/${REPO}/releases/download/${agentStateRulesTag(AGENT_STATE_RULES_ENGINE_VERSION, channel)}/${AGENT_STATE_RULES_ASSET}` ) } }) }) /** A `gh` stand-in over an in-memory set of releases, recording each call. */ function fakeGh(releases = {}) { const calls = [] const gh = (args) => { calls.push(args) const [, verb, tag] = args if (verb === 'download') { return tag in releases ? { status: 0, stdout: releases[tag], stderr: '' } : { status: 1, stdout: '', stderr: 'release not found' } } if (verb === 'upload' || verb === 'create') { expect(args[3].endsWith(`/${AGENT_STATE_RULES_ASSET}`)).toBe(true) releases[tag] = readFileSync(args[3], 'utf8') } return { status: 0, stdout: '', stderr: '' } } return { gh, calls, releases } } const at = (version, engineVersion = 1) => `${JSON.stringify({ version, engineVersion, files: [] })}\n` describe('publishAgentStateRules', () => { it("creates the engine's channel release as a prerelease that can never be Latest", () => { const fake = fakeGh() expect( publishAgentStateRules({ repo: REPO, channel: 'next', text: at(2), target: 'abc', gh: fake.gh }) ).toEqual({ tag: NEXT, version: 2 }) expect(fake.calls.find((args) => args[1] === 'create')).toEqual( expect.arrayContaining([NEXT, '--prerelease', '--latest=false', '--target', 'abc']) ) expect(fake.releases[NEXT]).toBe(at(2)) }) it('replaces the asset in place on an existing release, keeping the tag', () => { const fake = fakeGh({ [NEXT]: at(1) }) publishAgentStateRules({ repo: REPO, channel: 'next', text: at(2), target: 'abc', gh: fake.gh }) expect(fake.calls.map((args) => args[1])).toEqual(['download', 'upload']) expect(fake.calls[1]).toContain('--clobber') expect(fake.releases[NEXT]).toBe(at(2)) }) it.each([1, 2])('refuses version %s over a published 2, uploading nothing', (version) => { const fake = fakeGh({ [NEXT]: at(2) }) expect(() => publishAgentStateRules({ repo: REPO, channel: 'next', text: at(version), target: 'abc', gh: fake.gh }) ).toThrow('bump agent-state-rules-release.json') expect(fake.calls.map((args) => args[1])).toEqual(['download']) }) it('fails when the release exists but its download fails', () => { const gh = () => ({ status: 1, stdout: '', stderr: 'no assets match the file pattern' }) expect(() => publishAgentStateRules({ repo: REPO, channel: 'next', text: at(2), target: 'abc', gh }) ).toThrow('no assets match') }) it('promotes the identical next bytes to stable', () => { const nextText = JSON.stringify({ version: 3, engineVersion: 1, files: [] }, null, 2) const fake = fakeGh({ [NEXT]: nextText, [STABLE]: at(2) }) promoteAgentStateRules({ repo: REPO, engineVersion: 1, target: 'abc', gh: fake.gh }) expect(fake.releases[STABLE]).toBe(nextText) }) it('refuses to promote before next exists', () => { const fake = fakeGh() expect(() => promoteAgentStateRules({ repo: REPO, engineVersion: 1, target: 'abc', gh: fake.gh }) ).toThrow('has not been published') }) })