Ctrl K
ComunidadGuías…

tests/post-merge.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 { evaluateGoals } from '../src/application/evaluation.js';
import { trackPostMerge } from '../src/application/post-merge.js';
import type { AutomationDependencies } from '../src/application/automation.js';
import { executeGoals } from '../src/cli/commands/goals.js';
import { configSchema } from '../src/domain/config.js';
import type { GoalState } from '../src/domain/iteration.js';
import type { IterationStore } from '../src/ports/iteration.js';
import { pr } from './helpers.js';

function goal(id = 'a'.repeat(24)): GoalState {
  return { schemaVersion: 1, id, repository: 'owner/repo', configHash: 'config',
    spec: { title: 'Implement benchmark behavior', objective: 'Implement benchmark behavior safely.', mode: 'feature',
      acceptance: [{ id: 'works', text: 'The benchmark behavior works correctly.' }], allowedPaths: ['src'],
      evaluation: { suite: 'core-suite', case: 'feature-case', profile: 'codex-default' } },
    branch: 'main', sha: pr.base.sha, createdAt: '2026-01-01T00:00:00Z', updatedAt: '2026-01-01T00:00:00Z',
    status: 'published', steps: [{ id: 'works', title: 'Implement benchmark behavior', acceptanceIds: ['works'], dependsOn: [] }],
    completed: ['works'], changes: [], reports: [], rounds: 2, calls: 3, tokens: 100, elapsedMs: 500,
    notes: [], publication: 'b'.repeat(24), pullRequestUrl: 'https://github.com/owner/repo/pull/1' };
}

function memoryStore(state: GoalState): IterationStore {
  return { read: async id => id === state.id ? state : undefined, save: async next => { Object.assign(state, next); },
    list: async () => [state], pause: async () => {}, paused: async () => false, unpause: async () => {},
    remember: async () => {}, experiences: async () => [] };
}

test('post-merge tracking waits for merge evidence, verifies the merge commit and records regressions', async () => {
  const state = goal(), goals = memoryStore(state), merged = 'c'.repeat(40);
  let pull = { ...pr, head: { ...pr.head, ref: `autofix/goal-${state.id}/${state.publication}` },
    state: 'open', merged_at: null as string | null, merge_commit_sha: null as string | null };
  let checks = [{ name: 'tests', status: 'completed', conclusion: 'success' as string | null }], issueState = 'open';
  const github = { target: async () => ({ branch: 'main', sha: pr.base.sha }), issue: async () => ({ number: 7, title: 'Issue title', body: '', state: issueState }),
    issues: async () => [], feedback: async () => { throw new Error('Unexpected feedback'); }, updatePull: async () => undefined,
    recoverPull: async () => undefined, propose: async () => '', outcome: async () => ({ pr: pull, checks }) };
  const deps = { repository: 'owner/repo', requiredChecks: ['tests'], requireIssueClosed: true, goals, github };
  assert.equal((await trackPostMerge(state.id, deps))?.status, 'waiting_for_merge');

  state.spec.issue = 7;
  pull = { ...pull, state: 'closed', merged_at: '2026-01-02T00:00:00Z', merge_commit_sha: merged };
  checks = [{ name: 'tests', status: 'in_progress', conclusion: null }];
  assert.equal((await trackPostMerge(state.id, deps))?.status, 'observing');

  checks = [{ name: 'tests', status: 'completed', conclusion: 'failure' }];
  assert.equal((await trackPostMerge(state.id, deps))?.status, 'regressed');
  assert.match(state.notes.at(-1)!, /Post-merge regression/);
  const notes = state.notes.length;
  await trackPostMerge(state.id, deps);
  assert.equal(state.notes.length, notes, 'repeated polling must not duplicate regression notes');

  checks = [{ name: 'tests', status: 'completed', conclusion: 'success' }]; issueState = 'closed';
  const healthy = await trackPostMerge(state.id, deps);
  assert.equal(healthy?.status, 'healthy'); assert.equal(healthy?.mergeSha, merged);
});

test('evaluation reports deterministic evidence by suite and profile', () => {
  const healthy = goal(), failed = goal('b'.repeat(24));
  healthy.postMerge = { pull: 1, status: 'healthy', mergeSha: 'c'.repeat(40), checkedAt: '2026-01-02T00:00:00Z', checks: [], reasons: [] };
  failed.spec.evaluation = { suite: 'core-suite', case: 'bug-case', profile: 'codex-default' };
  failed.status = 'needs_attention'; failed.completed = [];
  const report = evaluateGoals([failed, healthy], 'core-suite');
  assert.equal(report.records.length, 2); assert.equal(report.profiles[0]?.verificationRate, 0.5);
  assert.equal(report.profiles[0]?.postMergePassRate, 1); assert.equal(report.profiles[0]?.averageCalls, 3);
  assert.equal(report.digest, evaluateGoals([healthy, failed], 'core-suite').digest);
  assert.deepEqual(report.duplicateKeys, []); assert.deepEqual(report.inconsistentCases, []);
});

test('iteration switches a merged goal from PR maintenance to post-merge tracking', async () => {
  const state = goal(), goals = memoryStore(state), merge = 'd'.repeat(40);
  const config = configSchema.parse({ repository: state.repository, iteration: { postMerge: { requiredChecks: ['tests'] } } });
  const owned = { ...pr, head: { ...pr.head, ref: `autofix/goal-${state.id}/${state.publication}` }, state: 'closed',
    merged_at: '2026-01-02T00:00:00Z', merge_commit_sha: merge };
  const deps: AutomationDependencies = { config, goals, signal: new AbortController().signal,
    store: {} as AutomationDependencies['store'], repository: {} as AutomationDependencies['repository'],
    github: { listPulls: async () => [], pull: async () => owned, current: async () => undefined, publish: async () => undefined,
      target: async () => ({ branch: 'main', sha: pr.base.sha }), issue: async () => { throw new Error('Unexpected Issue read'); },
      issues: async () => [], feedback: async () => { throw new Error('Unexpected feedback'); }, updatePull: async () => undefined,
      recoverPull: async () => undefined, propose: async () => '',
      outcome: async () => ({ pr: owned, checks: [{ name: 'tests', status: 'completed', conclusion: 'success' }] }) } };
  const output: string[] = [];
  assert.equal(await executeGoals('iterate', undefined, undefined, { once: true }, deps,
    { write: value => output.push(value), error: value => assert.fail(value) }), 0);
  assert.equal(state.postMerge?.status, 'healthy');
  assert.equal(JSON.parse(output[0]!).mergeSha, merge);
});