loop-session.test.ts 2.7 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586
  1. import { describe, expect, test } from 'bun:test';
  2. import {
  3. compactAttempt,
  4. createLoopSession,
  5. type LoopDefinition,
  6. loopDirname,
  7. } from './loop-session';
  8. function testDef(overrides?: Partial<LoopDefinition>): LoopDefinition {
  9. return {
  10. goal: 'test goal',
  11. successCriteria: 'it works',
  12. success: { type: 'test', command: 'bun test' },
  13. maxAttempts: 3,
  14. executeAgent: 'fixer',
  15. verifyAgent: 'oracle',
  16. ...overrides,
  17. };
  18. }
  19. describe('loopDirname', () => {
  20. test('creates human-readable dir name with short ID', () => {
  21. const name = loopDirname('loop-mqwo5ddt', 'Fix typescript errors');
  22. expect(name).toBe('fix-typescript-errors-mqwo5ddt');
  23. });
  24. test('slugifies the goal text', () => {
  25. const name = loopDirname('loop-abc-123', 'Fix TypeScript & ESLint errors!');
  26. expect(name).toBe('fix-typescript-eslint-errors-123');
  27. });
  28. test('truncates long goals', () => {
  29. const longGoal = 'a'.repeat(50);
  30. const name = loopDirname('xyz-999', longGoal);
  31. expect(name.length).toBeLessThan(60);
  32. });
  33. });
  34. describe('createLoopSession', () => {
  35. test('creates a session with executing phase and attempt 1', () => {
  36. const def = testDef();
  37. const session = createLoopSession(def, 'loop-test-1');
  38. expect(session.loopID).toBe('loop-test-1');
  39. expect(session.definition).toBe(def);
  40. expect(session.currentPhase).toBe('executing');
  41. expect(session.attempts).toBe(1);
  42. expect(session.history).toEqual([]);
  43. expect(session.activeJobID).toBeUndefined();
  44. expect(session.manualReviewPending).toBe(false);
  45. expect(session.historyDir).toContain('test-goal');
  46. });
  47. });
  48. describe('compactAttempt', () => {
  49. test('formats a passed attempt', () => {
  50. const result = compactAttempt({
  51. attemptNumber: 1,
  52. executionResult: 'bun test',
  53. verificationResult: { passed: true, reason: 'all green' },
  54. });
  55. expect(result).toContain('## Attempt 1');
  56. expect(result).toContain('**Outcome:** PASS');
  57. expect(result).toContain('### Execution Result');
  58. });
  59. test('formats a failed attempt with reason', () => {
  60. const result = compactAttempt({
  61. attemptNumber: 2,
  62. executionResult: 'bun test',
  63. verificationResult: { passed: false, reason: 'tests failed' },
  64. });
  65. expect(result).toContain('## Attempt 2');
  66. expect(result).toContain('FAIL: tests failed');
  67. });
  68. test('includes artifacts when present', () => {
  69. const result = compactAttempt({
  70. attemptNumber: 1,
  71. executionResult: 'built',
  72. verificationResult: { passed: true, reason: 'ok' },
  73. artifactPaths: ['src/output.ts', 'src/output.test.ts'],
  74. });
  75. expect(result).toContain('artifacts: src/output.ts, src/output.test.ts');
  76. });
  77. });