Ctrl K
ComunidadGuías…

tests/service-budget.test.ts

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
import assert from 'node:assert/strict';
import { mkdir, mkdtemp } from 'node:fs/promises';
import { resolve } from 'node:path';
import test from 'node:test';
import { FileServiceStore } from '../src/adapters/storage/service-store.js';
import { CodexAgentTeam } from '../src/adapters/codex/team.js';
import { DockerCodexAgent } from '../src/adapters/codex/docker-agent.js';
import { ServiceBudget } from '../src/application/service-budget.js';
import { configSchema } from '../src/domain/config.js';
import { configDigest, pipelineId } from '../src/domain/identity.js';
import { serviceSchema } from '../src/domain/service.js';
import { AutomationPausedError } from '../src/shared/control.js';

async function store() {
  await mkdir('.cache/tests', { recursive: true });
  return new FileServiceStore(await mkdtemp(resolve('.cache/tests/service-budget-')));
}
test('parallel model reservations cannot exceed the daily allowance and unknown usage survives restart', async () => {
  const data = await store(), config = serviceSchema.parse({ limits: { dailyCalls: 2, dailyTokens: 200 } });
  const budget = new ServiceBudget(data, 'owner/repo', config, 100);
  const results = await Promise.allSettled([budget.reserve(), budget.reserve(), budget.reserve()]);
  assert.equal(results.filter(item => item.status === 'fulfilled').length, 2);
  assert.equal(results.filter(item => item.status === 'rejected').length, 1);
  const first = results[0]!; assert.equal(first.status, 'fulfilled');
  if (first.status === 'fulfilled') await first.value.settle(30);
  const resumed = new ServiceBudget(data, 'owner/repo', config, 100);
  assert.deepEqual((await resumed.status()).used, { calls: 2, tokens: 130, tasks: 0 });
  assert.equal((await resumed.status()).unknownCalls, 1);
  await assert.rejects(resumed.reserve(), AutomationPausedError);
});
test('midnight accounting charges a late settlement to its original day and retains over-limit actual usage', async () => {
  const data = await store(), config = serviceSchema.parse({ limits: { dailyTokens: 100 } });
  let time = Date.parse('2026-10-08T23:59:59Z');
  const budget = new ServiceBudget(data, 'owner/repo', config, 100, () => time);
  const call = await budget.reserve();
  time += 2000; await call.settle(150);
  const ledger = (await data.ledger('owner/repo'))!;
  assert.equal(ledger.days['2026-10-08']?.tokens, 150);
  assert.equal((await budget.status()).used.tokens, 0);
  const next = await budget.reserve(); await next.settle(150);
  await assert.rejects(budget.reserve(), /token limit/);
  assert.equal((await budget.status()).used.tokens, 150);
});
test('operator resume clears the circuit while preserving consumed daily task and model allowances', async () => {
  const data = await store(), config = serviceSchema.parse({ limits: { dailyTasks: 1, failureThreshold: 2 } });
  const budget = new ServiceBudget(data, 'owner/repo', config, 100);
  await budget.startTask(); const call = await budget.reserve(); await call.settle(12);
  await budget.outcome('failure'); await budget.outcome('failure');
  assert.ok((await budget.status()).reasons.some(reason => reason.includes('circuit')));
  await data.requestControl('owner/repo', 'pause');
  await assert.rejects(budget.reserve(), /operator/);
  await data.requestControl('owner/repo', 'resume');
  const status = await budget.status();
  assert.equal(status.failures, 0); assert.deepEqual(status.used, { calls: 1, tokens: 12, tasks: 1 });
  await assert.rejects(budget.startTask(), /task limit/);
  // An already-admitted task can finish within the remaining call/token allowance.
  const continuing = await budget.reserve(); await continuing.settle(8);
  assert.equal((await budget.status()).used.calls, 2);
});
test('service operational settings do not invalidate pinned execution evidence', () => {
  const original = configSchema.parse({ repository: 'owner/repo' });
  const service = configSchema.parse({ ...original, service: { progress: true } });
  const input = { baseSha: 'a'.repeat(40), headSha: 'b'.repeat(40), description: 'validation' };
  assert.equal(configDigest(original), configDigest(service));
  assert.equal(pipelineId(input, original), pipelineId(input, service));
  assert.notEqual(configDigest(service), configDigest({ ...service, publish: true }));
});

test('Codex role agents and execution forks all consult the shared guard before starting a container', async () => {
  await mkdir('.cache/tests', { recursive: true });
  const root = await mkdtemp(resolve('.cache/tests/model-guard-'));
  const config = configSchema.parse({ repository: 'owner/repo', agent: { enabled: true, image: 'repopilot-test-no-execution:invalid' },
    iteration: { collaboration: {} } });
  let reservations = 0;
  const guard = { reserve: async () => { reservations++; throw new AutomationPausedError('synthetic policy denial'); } };
  const originalKey = process.env.OPENAI_API_KEY;
  process.env.OPENAI_API_KEY = 'synthetic-placeholder';
  try {
    const snapshot = new Map([['src/a.ts', 'export const value = 1;']]);
    const single = new DockerCodexAgent(config.agent, root, undefined, undefined, undefined, guard);
    const team = new CodexAgentTeam(config.agent, config.iteration!.collaboration, root, undefined, guard);
    await assert.rejects(single.forkExecution().review(snapshot, snapshot, 'guard validation'), AutomationPausedError);
    await assert.rejects(team.forRole('tester').plan(snapshot, snapshot, 'guard validation'), AutomationPausedError);
    await assert.rejects(team.forkExecution().forRole!('developer').repair(snapshot, snapshot, 'guard validation'), AutomationPausedError);
    assert.equal(reservations, 3);
  } finally { if (originalKey === undefined) delete process.env.OPENAI_API_KEY; else process.env.OPENAI_API_KEY = originalKey; }
});