/** * @fileoverview Tests for Ralph Wiggum configuration parser * * Tests parsing of .claude/ralph-loop.local.md and CLAUDE.md * for Ralph loop configuration. */ import { describe, it, expect } from 'vitest'; import { parseRalphLoopConfigFromContent, extractCompletionPhraseFromContent, } from '../src/ralph-config.js'; describe('parseRalphLoopConfigFromContent', () => { describe('valid YAML frontmatter', () => { it('should parse complete config', () => { const content = `--- enabled: true iteration: 5 max-iterations: 50 completion-promise: "COMPLETE" --- # Original Prompt Build a REST API... `; const config = parseRalphLoopConfigFromContent(content); expect(config).not.toBeNull(); expect(config!.enabled).toBe(true); expect(config!.iteration).toBe(5); expect(config!.maxIterations).toBe(50); expect(config!.completionPromise).toBe('COMPLETE'); }); it('should parse config without quotes around values', () => { const content = `--- enabled: true iteration: 10 max-iterations: 100 completion-promise: TESTS_PASS --- `; const config = parseRalphLoopConfigFromContent(content); expect(config).not.toBeNull(); expect(config!.completionPromise).toBe('TESTS_PASS'); }); it('should parse config with hyphenated completion promise', () => { const content = `--- enabled: true completion-promise: "TESTS-PASS" --- `; const config = parseRalphLoopConfigFromContent(content); expect(config).not.toBeNull(); expect(config!.completionPromise).toBe('TESTS-PASS'); }); it('should handle partial config (only enabled)', () => { const content = `--- enabled: true --- `; const config = parseRalphLoopConfigFromContent(content); expect(config).not.toBeNull(); expect(config!.enabled).toBe(true); expect(config!.iteration).toBe(0); expect(config!.maxIterations).toBeNull(); expect(config!.completionPromise).toBeNull(); }); it('should handle disabled state', () => { const content = `--- enabled: false iteration: 25 max-iterations: 50 completion-promise: "DONE" --- `; const config = parseRalphLoopConfigFromContent(content); expect(config).not.toBeNull(); expect(config!.enabled).toBe(false); expect(config!.iteration).toBe(25); }); it('should convert completion promise to uppercase', () => { const content = `--- completion-promise: "lower_case_phrase" --- `; const config = parseRalphLoopConfigFromContent(content); expect(config).not.toBeNull(); expect(config!.completionPromise).toBe('LOWER_CASE_PHRASE'); }); it('should handle whitespace in values', () => { const content = `--- enabled: true completion-promise: "COMPLETE" iteration: 5 --- `; const config = parseRalphLoopConfigFromContent(content); expect(config).not.toBeNull(); expect(config!.completionPromise).toBe('COMPLETE'); expect(config!.iteration).toBe(5); }); }); describe('invalid content', () => { it('should return null for content without frontmatter', () => { const content = `# Just a markdown file No YAML frontmatter here. `; const config = parseRalphLoopConfigFromContent(content); expect(config).toBeNull(); }); it('should return null for malformed frontmatter (missing closing)', () => { const content = `--- enabled: true # No closing --- `; const config = parseRalphLoopConfigFromContent(content); expect(config).toBeNull(); }); it('should return null for empty content', () => { const config = parseRalphLoopConfigFromContent(''); expect(config).toBeNull(); }); it('should return null for empty frontmatter', () => { const content = `--- --- Content here. `; const config = parseRalphLoopConfigFromContent(content); // Empty frontmatter (nothing between ---) returns null // because there's nothing useful to extract expect(config).toBeNull(); }); }); describe('edge cases', () => { it('should handle zero iteration', () => { const content = `--- iteration: 0 --- `; const config = parseRalphLoopConfigFromContent(content); expect(config).not.toBeNull(); expect(config!.iteration).toBe(0); }); it('should handle invalid iteration as zero', () => { const content = `--- iteration: not-a-number --- `; const config = parseRalphLoopConfigFromContent(content); expect(config).not.toBeNull(); expect(config!.iteration).toBe(0); }); it('should handle invalid max-iterations as null', () => { const content = `--- max-iterations: infinite --- `; const config = parseRalphLoopConfigFromContent(content); expect(config).not.toBeNull(); expect(config!.maxIterations).toBeNull(); }); it('should handle mixed valid and invalid values', () => { const content = `--- enabled: true iteration: abc max-iterations: 50 completion-promise: "VALID-PHRASE" --- `; const config = parseRalphLoopConfigFromContent(content); expect(config).not.toBeNull(); expect(config!.enabled).toBe(true); expect(config!.iteration).toBe(0); // Invalid falls back to 0 expect(config!.maxIterations).toBe(50); expect(config!.completionPromise).toBe('VALID-PHRASE'); }); }); }); describe('extractCompletionPhraseFromContent', () => { describe('standard patterns', () => { it('should extract simple completion phrase', () => { const content = `Output COMPLETE when done.`; const phrase = extractCompletionPhraseFromContent(content); expect(phrase).toBe('COMPLETE'); }); it('should extract phrase with underscores', () => { const content = `ALL_TASKS_DONE`; const phrase = extractCompletionPhraseFromContent(content); expect(phrase).toBe('ALL_TASKS_DONE'); }); it('should extract phrase with hyphens', () => { const content = `Output TESTS-PASS when all tests green.`; const phrase = extractCompletionPhraseFromContent(content); expect(phrase).toBe('TESTS-PASS'); }); it('should extract phrase with numbers', () => { const content = `TASK_123`; const phrase = extractCompletionPhraseFromContent(content); expect(phrase).toBe('TASK_123'); }); it('should extract phrase with mixed characters', () => { const content = `TASK-123_COMPLETE`; const phrase = extractCompletionPhraseFromContent(content); expect(phrase).toBe('TASK-123_COMPLETE'); }); }); describe('whitespace handling', () => { it('should handle whitespace inside tags', () => { const content = ` COMPLETE `; const phrase = extractCompletionPhraseFromContent(content); expect(phrase).toBe('COMPLETE'); }); it('should handle newlines around phrase', () => { const content = ` DONE `; // The pattern does match across lines since \s* includes newlines // This is acceptable - multi-line promises are unusual but still valid const phrase = extractCompletionPhraseFromContent(content); expect(phrase).toBe('DONE'); }); }); describe('case handling', () => { it('should convert lowercase to uppercase', () => { const content = `complete`; const phrase = extractCompletionPhraseFromContent(content); expect(phrase).toBe('COMPLETE'); }); it('should convert mixed case to uppercase', () => { const content = `TestsPass`; const phrase = extractCompletionPhraseFromContent(content); expect(phrase).toBe('TESTSPASS'); }); }); describe('multiple occurrences', () => { it('should return first occurrence', () => { const content = ` First: FIRST_PHRASE Second: SECOND_PHRASE Third: THIRD_PHRASE `; const phrase = extractCompletionPhraseFromContent(content); expect(phrase).toBe('FIRST_PHRASE'); }); }); describe('in backticks', () => { it('should extract phrase from within backticks', () => { const content = 'Output `COMPLETE` when done.'; const phrase = extractCompletionPhraseFromContent(content); expect(phrase).toBe('COMPLETE'); }); it('should extract phrase from code block', () => { const content = ` \`\`\` DONE \`\`\` `; const phrase = extractCompletionPhraseFromContent(content); expect(phrase).toBe('DONE'); }); }); describe('no match cases', () => { it('should return null when no pattern found', () => { const content = `Just some regular text without any promise tags.`; const phrase = extractCompletionPhraseFromContent(content); expect(phrase).toBeNull(); }); it('should return null for empty content', () => { const phrase = extractCompletionPhraseFromContent(''); expect(phrase).toBeNull(); }); it('should return null for malformed tags', () => { const content = `INCOMPLETE`; const phrase = extractCompletionPhraseFromContent(content); expect(phrase).toBeNull(); }); it('should return null for empty promise', () => { const content = ``; const phrase = extractCompletionPhraseFromContent(content); expect(phrase).toBeNull(); }); it('should return null for promise with only whitespace', () => { const content = ` `; const phrase = extractCompletionPhraseFromContent(content); expect(phrase).toBeNull(); }); }); describe('real-world CLAUDE.md examples', () => { it('should extract from typical CLAUDE.md content', () => { const content = ` # CLAUDE.md ## Task Build a REST API for user management. ## Completion Criteria - All endpoints working - Tests passing - Documentation complete Output COMPLETE when all criteria met. `; const phrase = extractCompletionPhraseFromContent(content); expect(phrase).toBe('COMPLETE'); }); it('should extract from Ralph Loop section', () => { const content = ` ## Ralph Wiggum Loop Completion phrase: TIME_COMPLETE Keep working until minimum duration reached. `; const phrase = extractCompletionPhraseFromContent(content); expect(phrase).toBe('TIME_COMPLETE'); }); }); });