| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120 |
- import { describe, expect, spyOn, test } from 'bun:test';
- import * as fs from 'node:fs';
- import {
- compactAttempt,
- createLoopSession,
- type LoopDefinition,
- loopDirname,
- writeHistoryFile,
- } from './loop-session';
- function testDef(overrides?: Partial<LoopDefinition>): LoopDefinition {
- return {
- goal: 'test goal',
- successCriteria: 'it works',
- success: { type: 'test', command: 'bun test' },
- maxAttempts: 3,
- executeAgent: 'fixer',
- verifyAgent: 'oracle',
- ...overrides,
- };
- }
- describe('loopDirname', () => {
- test('creates human-readable dir name with short ID', () => {
- const name = loopDirname('loop-mqwo5ddt', 'Fix typescript errors');
- expect(name).toBe('fix-typescript-errors-mqwo5ddt');
- });
- test('slugifies the goal text', () => {
- const name = loopDirname('loop-abc-123', 'Fix TypeScript & ESLint errors!');
- expect(name).toBe('fix-typescript-eslint-errors-123');
- });
- test('truncates long goals', () => {
- const longGoal = 'a'.repeat(50);
- const name = loopDirname('xyz-999', longGoal);
- expect(name.length).toBeLessThan(60);
- });
- });
- describe('createLoopSession', () => {
- test('creates a session with executing phase and attempt 1', () => {
- const def = testDef();
- const session = createLoopSession(def, 'loop-test-1');
- expect(session.loopID).toBe('loop-test-1');
- expect(session.definition).toBe(def);
- expect(session.currentPhase).toBe('executing');
- expect(session.attempts).toBe(1);
- expect(session.history).toEqual([]);
- expect(session.activeJobID).toBeUndefined();
- expect(session.manualReviewPending).toBe(false);
- expect(session.historyDir).toContain('test-goal');
- });
- });
- describe('compactAttempt', () => {
- test('formats a passed attempt', () => {
- const result = compactAttempt({
- attemptNumber: 1,
- executionResult: 'bun test',
- verificationResult: { passed: true, reason: 'all green' },
- });
- expect(result).toContain('## Attempt 1');
- expect(result).toContain('**Outcome:** PASS');
- expect(result).toContain('### Execution Result');
- });
- test('formats a failed attempt with reason', () => {
- const result = compactAttempt({
- attemptNumber: 2,
- executionResult: 'bun test',
- verificationResult: { passed: false, reason: 'tests failed' },
- });
- expect(result).toContain('## Attempt 2');
- expect(result).toContain('FAIL: tests failed');
- });
- test('includes artifacts when present', () => {
- const result = compactAttempt({
- attemptNumber: 1,
- executionResult: 'built',
- verificationResult: { passed: true, reason: 'ok' },
- artifactPaths: ['src/output.ts', 'src/output.test.ts'],
- });
- expect(result).toContain('artifacts: src/output.ts, src/output.test.ts');
- });
- });
- describe('writeHistoryFile', () => {
- test('uses the attempt number for the history filename', () => {
- const mkdirSpy = spyOn(fs, 'mkdirSync').mockImplementation(() => undefined);
- const writeSpy = spyOn(fs, 'writeFileSync').mockImplementation(
- () => undefined,
- );
- const session = createLoopSession(testDef(), 'loop-test-1');
- session.attempts = 99;
- session.history.push({
- attemptNumber: 4,
- executionResult: 'bun test',
- verificationResult: { passed: true, reason: 'ok' },
- });
- try {
- writeHistoryFile(session);
- expect(mkdirSpy).toHaveBeenCalledWith(session.historyDir, {
- recursive: true,
- });
- expect(writeSpy).toHaveBeenCalledWith(
- expect.stringContaining('history-004.md'),
- expect.stringContaining('## Attempt 4'),
- { encoding: 'utf-8' },
- );
- } finally {
- mkdirSpy.mockRestore();
- writeSpy.mockRestore();
- }
- });
- });
|