Files
ci/src/core/escalation.test.ts
T
Jon Chery 4a58aa1657 refactor(rebrand): rename & rebrand CI → CIAgent across all source and test files
- Type renames: CIConfig → CIAgentConfig, DEFAULT_CI_CONFIG → DEFAULT_CIAGENT_CONFIG
- Type renames: CiMetadata → CIAgentMetadata, ParsedCiCommit → ParsedCIAgentCommit
- Function renames: initCI → initCIAgent, isCIInitialized → isCIAgentInitialized
- Function renames: extractCiBlock → extractCIAgentBlock, parseCiBlock → parseCIAgentBlock
- Class renames: CiFiles → CIAgentFiles
- Import paths: ci-files.js → ciagent-files.js
- Directory paths: .ci/ → .ciagent/ across all source and test files
- Check names: ".ci directory exists" → ".ciagent directory exists"
- Check names: "CI config valid" → "CIAgent config valid"
- Temp dir names: ci-*-test- → ciagent-*-test-
- CLI examples: "ci init" → "ciagent init"
- Fix deepMerge infinite recursion bug in config.ts
- ---ci---/---/ci--- block markers preserved unchanged
- All 31 test suites, 370 tests passing

---ci---
phase: 1
milestone: v0.5
plan: 07
task: 07-01-01
status: execute
---/ci---
2026-05-29 18:01:13 +00:00

119 lines
4.2 KiB
TypeScript

import * as fs from "node:fs";
import * as path from "node:path";
import * as os from "node:os";
import { EscalationProtocol, EscalationInput } from "../core/escalation.js";
import { DEFAULT_CIAGENT_CONFIG } from "../types/config.js";
describe("EscalationProtocol", () => {
let tempDir: string;
let protocol: EscalationProtocol;
beforeEach(() => {
tempDir = fs.mkdtempSync(path.join(os.tmpdir(), "ciagent-escalation-test-"));
const noAutoCommitConfig = {
...DEFAULT_CIAGENT_CONFIG,
git: { ...DEFAULT_CIAGENT_CONFIG.git, auto_commit: false },
};
protocol = new EscalationProtocol(noAutoCommitConfig, tempDir);
});
afterEach(() => {
protocol.clearAllTimers();
fs.rmSync(tempDir, { recursive: true, force: true });
});
const baseInput: EscalationInput = {
type: "irreversible_action",
phase: "1",
description: "Deploy to staging environment",
context: "Phase 1 backend is complete, all tests pass",
options: [
{ id: "A", label: "Deploy to staging", description: "Deploy to staging for integration testing", recommended: true },
{ id: "B", label: "Skip deployment", description: "Continue locally", recommended: false },
{ id: "C", label: "Abort phase", description: "Await manual deployment", recommended: false },
],
default_option_id: "A",
};
describe("escalate", () => {
it("creates an escalation with a generated ID", () => {
const escalation = protocol.escalate(baseInput);
expect(escalation.id).toMatch(/^E-\d{3}$/);
expect(escalation.type).toBe("irreversible_action");
expect(escalation.description).toBe("Deploy to staging environment");
expect(escalation.resolution).toBe("pending");
});
it("increments escalation IDs sequentially", () => {
const e1 = protocol.escalate(baseInput);
const e2 = protocol.escalate(baseInput);
expect(e1.id).toBe("E-001");
expect(e2.id).toBe("E-002");
});
});
describe("resolveEscalation", () => {
it("resolves a pending escalation", () => {
const escalation = protocol.escalate(baseInput);
const resolved = protocol.resolveEscalation(escalation.id, "A");
expect(resolved).not.toBeNull();
expect(resolved!.resolution).toBe("approved");
expect(resolved!.resolution_detail).toContain("A");
});
it("returns null for unknown escalation ID", () => {
const result = protocol.resolveEscalation("E-999", "A");
expect(result).toBeNull();
});
it("supports different resolution types", () => {
const escalation = protocol.escalate(baseInput);
const rejected = protocol.resolveEscalation(escalation.id, "C", "rejected");
expect(rejected!.resolution).toBe("rejected");
});
});
describe("getPendingEscalations", () => {
it("returns pending escalations", () => {
protocol.escalate(baseInput);
protocol.escalate(baseInput);
const pending = protocol.getPendingEscalations();
expect(pending).toHaveLength(2);
});
it("returns empty list when no pending escalations", () => {
expect(protocol.getPendingEscalations()).toHaveLength(0);
});
it("removes resolved escalations from pending", () => {
const e1 = protocol.escalate(baseInput);
protocol.escalate(baseInput);
protocol.resolveEscalation(e1.id, "A");
expect(protocol.getPendingEscalations()).toHaveLength(1);
});
});
describe("hasPending", () => {
it("returns true when there are pending escalations", () => {
protocol.escalate(baseInput);
expect(protocol.hasPending()).toBe(true);
});
it("returns false when no pending escalations", () => {
expect(protocol.hasPending()).toBe(false);
});
});
describe("formatEscalation", () => {
it("formats escalation for display", () => {
const escalation = protocol.escalate(baseInput);
const formatted = protocol.formatEscalation(escalation);
expect(formatted).toContain("ESCALATION");
expect(formatted).toContain("Irreversible Action");
expect(formatted).toContain("Deploy to staging environment");
expect(formatted).toContain("Options:");
expect(formatted).toContain("recommended");
expect(formatted).toContain("auto-proceed");
});
});
});