"use strict";
/**
 * test_runtime_health_backoff — Story 6.5 regression suite.
 *
 * Three scenarios:
 *   1. Exponential schedule: failure N produces delay ≈ 10s * 2^(N-1)
 *      with a 1h cap; explicit unit test on the pure function.
 *   2. markFailure + markHealthy: failures accumulate, until extends with
 *      backoff, markHealthy resets to failures=0 and clears
 *      permanentlyDown.
 *   3. Retry budget breach: more than MAX_FAILURES failures within the
 *      window flips permanentlyDown=true and isCoolingDown()=true.
 *
 * Prints TEST_RUNTIME_HEALTH_BACKOFF_OK on full pass.
 */
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    var desc = Object.getOwnPropertyDescriptor(m, k);
    if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
      desc = { enumerable: true, get: function() { return m[k]; } };
    }
    Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
    Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
    o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
    var ownKeys = function(o) {
        ownKeys = Object.getOwnPropertyNames || function (o) {
            var ar = [];
            for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
            return ar;
        };
        return ownKeys(o);
    };
    return function (mod) {
        if (mod && mod.__esModule) return mod;
        var result = {};
        if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
        __setModuleDefault(result, mod);
        return result;
    };
})();
Object.defineProperty(exports, "__esModule", { value: true });
const fs = __importStar(require("fs"));
const os = __importStar(require("os"));
const path = __importStar(require("path"));
const RuntimeHealthMonitor_1 = require("./orchestrator/RuntimeHealthMonitor");
function assert(cond, msg) {
    if (!cond)
        throw new Error(`assertion failed: ${msg}`);
}
function mkTmp() {
    return fs.mkdtempSync(path.join(os.tmpdir(), 'rh-test-'));
}
function rmTmp(dir) {
    try {
        fs.rmSync(dir, { recursive: true, force: true });
    }
    catch { /* ignore */ }
}
// ─────────────────────────────────────────────────────────────
// Scenario 1 — pure backoff function
// ─────────────────────────────────────────────────────────────
function scenarioBackoffFunction() {
    assert((0, RuntimeHealthMonitor_1.exponentialBackoffDelayMs)(1) === RuntimeHealthMonitor_1.BASE_DELAY_MS, 'failure 1 = base');
    assert((0, RuntimeHealthMonitor_1.exponentialBackoffDelayMs)(2) === RuntimeHealthMonitor_1.BASE_DELAY_MS * 2, 'failure 2 = 2x');
    assert((0, RuntimeHealthMonitor_1.exponentialBackoffDelayMs)(3) === RuntimeHealthMonitor_1.BASE_DELAY_MS * 4, 'failure 3 = 4x');
    assert((0, RuntimeHealthMonitor_1.exponentialBackoffDelayMs)(4) === RuntimeHealthMonitor_1.BASE_DELAY_MS * 8, 'failure 4 = 8x');
    // Cap at MAX_DELAY_MS — failure count above the cap stays capped
    assert((0, RuntimeHealthMonitor_1.exponentialBackoffDelayMs)(100) === RuntimeHealthMonitor_1.MAX_DELAY_MS, 'failure 100 = cap');
    assert((0, RuntimeHealthMonitor_1.exponentialBackoffDelayMs)(0) === RuntimeHealthMonitor_1.BASE_DELAY_MS, 'failure 0 = base (defensive)');
    console.log('✅ scenario 1: exponential backoff function (base + cap + clamp)');
}
// ─────────────────────────────────────────────────────────────
// Scenario 2 — failure accumulation + recovery
// ─────────────────────────────────────────────────────────────
function scenarioAccumulateAndRecover() {
    const tmp = mkTmp();
    try {
        const monitor = new RuntimeHealthMonitor_1.RuntimeHealthMonitor(tmp);
        monitor.markFailure('codex', 'timeout');
        const after1 = monitor.get('codex');
        assert(after1?.failures === 1, 'failures after first call = 1');
        assert(after1.until !== undefined, 'until must be set');
        assert(after1.permanentlyDown !== true, 'must not be permanently down after one failure');
        assert(monitor.isCoolingDown('codex') === true, 'must be cooling down');
        monitor.markFailure('codex', 'timeout again');
        const after2 = monitor.get('codex');
        assert(after2?.failures === 2, 'failures after second call = 2');
        // Until extends — second failure has a larger backoff than first
        const after1UntilMs = new Date(after1.until).getTime();
        const after2UntilMs = new Date(after2.until).getTime();
        assert(after2UntilMs > after1UntilMs, `second until must be later: ${after2UntilMs} vs ${after1UntilMs}`);
        // markHealthy resets
        monitor.markHealthy('codex');
        const recovered = monitor.get('codex');
        assert(recovered?.failures === 0, 'failures reset on markHealthy');
        assert(recovered.permanentlyDown !== true, 'permanentlyDown cleared on markHealthy');
        assert(monitor.isCoolingDown('codex') === false, 'no longer cooling down after markHealthy');
        console.log('✅ scenario 2: accumulate failures + markHealthy resets');
    }
    finally {
        rmTmp(tmp);
    }
}
// ─────────────────────────────────────────────────────────────
// Scenario 3 — retry budget exhausted → permanentlyDown
// ─────────────────────────────────────────────────────────────
function scenarioBudgetExhausted() {
    const tmp = mkTmp();
    try {
        const monitor = new RuntimeHealthMonitor_1.RuntimeHealthMonitor(tmp);
        // Drive failures past MAX_FAILURES
        for (let i = 0; i < RuntimeHealthMonitor_1.MAX_FAILURES + 2; i++) {
            monitor.markFailure('gemini', `failure ${i + 1}`);
        }
        const final = monitor.get('gemini');
        assert(final?.failures === RuntimeHealthMonitor_1.MAX_FAILURES + 2, `expected ${RuntimeHealthMonitor_1.MAX_FAILURES + 2} failures, got ${final?.failures}`);
        assert(final.permanentlyDown === true, 'must be permanently down past budget');
        assert(monitor.isCoolingDown('gemini') === true, 'permanentlyDown implies cooling down');
        assert(final.until === undefined, 'permanentlyDown means no scheduled retry');
        // markHealthy reverts even from permanentlyDown (manual reset surface)
        monitor.markHealthy('gemini');
        assert(monitor.get('gemini')?.permanentlyDown !== true, 'manual reset clears permanentlyDown');
        console.log('✅ scenario 3: retry budget exhausted → permanentlyDown + manual reset');
    }
    finally {
        rmTmp(tmp);
    }
}
// ─────────────────────────────────────────────────────────────
// Driver
// ─────────────────────────────────────────────────────────────
function main() {
    console.log('🧪 test_runtime_health_backoff — Story 6.5 regression suite');
    scenarioBackoffFunction();
    scenarioAccumulateAndRecover();
    scenarioBudgetExhausted();
    console.log('');
    console.log('TEST_RUNTIME_HEALTH_BACKOFF_OK');
}
try {
    main();
}
catch (err) {
    console.error('❌ test_runtime_health_backoff FAILED:', err instanceof Error ? err.message : err);
    process.exit(1);
}
