Ctrl K
ComunidadGuías…

tests/implementation.test.ts

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

Archivo original ↗Preguntar a KiBot
import assert from 'node:assert/strict';
import test from 'node:test';
import { runPipeline } from '../src/application/pipeline.js';
import { configSchema } from '../src/domain/config.js';
import type { Agent } from '../src/ports/agent.js';
import type { Runner } from '../src/ports/runner.js';
import { answer, result, store, testCase } from './helpers.js';

const criterion = 'Negative quantities produce a validation error.';
const base = new Map([['src/a.ts', 'BROKEN']]);
const input = { base, head: base, baseSha: 'a'.repeat(40), headSha: 'a'.repeat(40), description: criterion,
  implementation: { mode: 'feature' as const, acceptance: [criterion], allowedPaths: ['src', 'test'] } };
const config = configSchema.parse({ repository: 'owner/repo', agent: { enabled: true, repair: true, maxAttempts: 3 } });
function model(): Agent { return {
  review: async () => answer(),
  plan: async () => ({ ...answer([{ path: 'test/generated.test.js', content: 'test("works", () => {});' }]),
    scenarios: [{ name: 'works', requirement: criterion, requirementQuote: criterion, kind: 'new_behavior', testFile: 'test/generated.test.js' }] }),
  repair: async () => answer([{ path: 'src/a.ts', content: 'GOOD' }])
}; }
const runner: Runner = { run: async files => {
  const cases = [testCase()];
  if (files.has('test/generated.test.js')) cases.push(testCase('test/generated.test.js', files.get('src/a.ts') === 'GOOD' ? 'passed' : 'failed'));
  return result(cases.some(c => c.status === 'failed') ? 'failed' : 'passed', cases);
} };

test('feature implementation demonstrates missing behavior before changing production and preserves frozen acceptance tests', async () => {
  const report = await runPipeline(input, config, await store(), runner, model());
  assert.equal(report.status, 'verified');
  assert.equal(report.tests.base.status, 'passed');
  assert.equal(report.evidence.find(e => e.phase === 'planned-base')?.result.status, 'failed');
  assert.equal(report.testStability?.status, 'stable');
  assert.equal(report.tests.repaired?.status, 'passed');
  assert.deepEqual(report.changes.map(c => c.path).sort(), ['src/a.ts', 'test/generated.test.js']);
});

test('feature planning refuses uncited acceptance, omitted acceptance and tests outside allowed paths', async () => {
  for (const variant of ['quote', 'coverage', 'scope']) {
    const m = model();
    if (variant === 'quote') { const plan = m.plan; m.plan = async (...args) => { const a = await plan(...args); a.scenarios[0]!.requirementQuote = 'Unrelated requirements'; return a; }; }
    const implementation = { ...input.implementation,
      acceptance: variant === 'coverage' ? [criterion, 'Zero quantities must also be rejected.'] : [criterion],
      allowedPaths: variant === 'scope' ? ['src'] : ['src', 'test'] };
    m.repair = async () => { assert.fail('Invalid plan must never reach repair'); };
    const report = await runPipeline({ ...input, implementation }, config, await store(), runner, m);
    assert.equal(report.status, 'error', variant); assert.equal(report.attempts, 0);
  }
});

test('feature repairs cannot expand scope or modify frozen tests', async () => {
  for (const path of ['other/a.ts', 'test/generated.test.js']) {
    const m = model(); m.repair = async () => answer([{ path, content: 'GOOD' }]);
    const report = await runPipeline(input, config, await store(), runner, m);
    assert.equal(report.status, 'needs_attention'); assert.deepEqual(report.changes, []);
    assert.match(report.repairs[0]!.reason ?? '', /allowed paths|frozen tests/);
  }
});

test('identical failed patches terminate instead of consuming all repair attempts', async () => {
  const m = model(); let calls = 0;
  m.repair = async () => { calls++; return answer([{ path: 'src/a.ts', content: 'STILL_BROKEN' }]); };
  const report = await runPipeline(input, config, await store(), runner, m);
  assert.equal(calls, 2); assert.equal(report.status, 'needs_attention');
  assert.equal(report.evidence.filter(e => e.phase === 'repair').length, 1);
  assert.match(report.repairs[1]!.reason ?? '', /Repeated patch/);
});

test('already passing feature criteria verify new acceptance tests without production changes', async () => {
  const m = model(); m.repair = async () => { assert.fail('Already passing tests cannot justify a repair'); };
  const green: Runner = { run: async files => result('passed', [testCase(), ...(files.has('test/generated.test.js') ? [testCase('test/generated.test.js')] : [])]) };
  const report = await runPipeline(input, config, await store(), green, m);
  assert.equal(report.status, 'verified'); assert.equal(report.attempts, 0);
  assert.deepEqual(report.changes.map(c => c.path), ['test/generated.test.js']);
  assert.equal(report.tests.repaired?.status, 'passed');
});



test('already passing acceptance tests cannot bypass static policy review', async () => {
  const m = model(), plan = m.plan;
  m.plan = async (...args) => {
    const planned = await plan(...args); planned.changes[0]!.content = 'test("works", () => { forbidden(); });'; return planned;
  };
  m.repair = async () => { assert.fail('Passing acceptance tests cannot authorize production repair'); };
  const policyBase = new Map([...base, ['.repopilot/policy.json', JSON.stringify({ rules: [{ id: 'forbidden',
    kind: 'forbid-call', callee: 'forbidden', message: 'Forbidden operation' }] })]]);
  const green: Runner = { run: async files => result('passed', [testCase(), ...(files.has('test/generated.test.js') ? [testCase('test/generated.test.js')] : [])]) };
  const report = await runPipeline({ ...input, base: policyBase, head: policyBase }, config, await store(), green, m);
  assert.equal(report.status, 'needs_attention'); assert.deepEqual(report.changes, []); assert.equal(report.attempts, 0);
  assert.match(report.notes.join('\n'), /policy review/);
});

test('bugfix mode cannot accept all-green tests as proof that a reported bug was reproduced', async () => {
  const m = model(), plan = m.plan;
  m.plan = async (...args) => { const planned = await plan(...args); planned.scenarios[0]!.kind = 'regression'; return planned; };
  m.repair = async () => { assert.fail('Unreproduced bug must not request a repair'); };
  const green: Runner = { run: async files => result('passed', [testCase(), ...(files.has('test/generated.test.js') ? [testCase('test/generated.test.js')] : [])]) };
  const report = await runPipeline({ ...input, implementation: { ...input.implementation, mode: 'bugfix' } }, config, await store(), green, m);
  assert.equal(report.status, 'needs_attention'); assert.deepEqual(report.changes, []); assert.equal(report.attempts, 0);
});