takt/src/__tests__/escape.test.ts

184 lines
6.5 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* Unit tests for template escaping and placeholder replacement
*
* Tests escapeTemplateChars and replaceTemplatePlaceholders functions.
*/
import { describe, it, expect } from 'vitest';
import {
escapeTemplateChars,
replaceTemplatePlaceholders,
} from '../core/piece/instruction/escape.js';
import { makeMovement, makeInstructionContext } from './test-helpers.js';
describe('escapeTemplateChars', () => {
it('should replace curly braces with full-width equivalents', () => {
expect(escapeTemplateChars('{hello}')).toBe('hello');
});
it('should handle multiple braces', () => {
expect(escapeTemplateChars('{{nested}}')).toBe('nested');
});
it('should return unchanged string when no braces', () => {
expect(escapeTemplateChars('no braces here')).toBe('no braces here');
});
it('should handle empty string', () => {
expect(escapeTemplateChars('')).toBe('');
});
it('should handle braces in code snippets', () => {
const input = 'function foo() { return { a: 1 }; }';
const expected = 'function foo() return a: 1 ; ';
expect(escapeTemplateChars(input)).toBe(expected);
});
});
describe('replaceTemplatePlaceholders', () => {
it('should replace {task} placeholder', () => {
const step = makeMovement();
const ctx = makeInstructionContext({ task: 'implement feature X' });
const template = 'Your task is: {task}';
const result = replaceTemplatePlaceholders(template, step, ctx);
expect(result).toBe('Your task is: implement feature X');
});
it('should escape braces in task content', () => {
const step = makeMovement();
const ctx = makeInstructionContext({ task: 'fix {bug} in code' });
const template = '{task}';
const result = replaceTemplatePlaceholders(template, step, ctx);
expect(result).toBe('fix bug in code');
});
it('should replace {iteration} and {max_movements}', () => {
const step = makeMovement();
const ctx = makeInstructionContext({ iteration: 3, maxMovements: 20 });
const template = 'Iteration {iteration}/{max_movements}';
const result = replaceTemplatePlaceholders(template, step, ctx);
expect(result).toBe('Iteration 3/20');
});
it('should replace {movement_iteration}', () => {
const step = makeMovement();
const ctx = makeInstructionContext({ movementIteration: 5 });
const template = 'Movement run #{movement_iteration}';
const result = replaceTemplatePlaceholders(template, step, ctx);
expect(result).toBe('Movement run #5');
});
it('should replace {previous_response} when passPreviousResponse is true', () => {
const step = makeMovement({ passPreviousResponse: true });
const ctx = makeInstructionContext({
previousOutput: {
persona: 'coder',
status: 'done',
content: 'previous output text',
timestamp: new Date(),
},
});
const template = 'Previous: {previous_response}';
const result = replaceTemplatePlaceholders(template, step, ctx);
expect(result).toBe('Previous: previous output text');
});
it('should prefer preprocessed previous response text when provided', () => {
const step = makeMovement({ passPreviousResponse: true });
const ctx = makeInstructionContext({
previousOutput: {
persona: 'coder',
status: 'done',
content: 'raw previous output',
timestamp: new Date(),
},
previousResponseText: 'processed previous output',
});
const template = 'Previous: {previous_response}';
const result = replaceTemplatePlaceholders(template, step, ctx);
expect(result).toBe('Previous: processed previous output');
});
it('should replace {previous_response} with empty string when no previous output', () => {
const step = makeMovement({ passPreviousResponse: true });
const ctx = makeInstructionContext();
const template = 'Previous: {previous_response}';
const result = replaceTemplatePlaceholders(template, step, ctx);
expect(result).toBe('Previous: ');
});
it('should not replace {previous_response} when passPreviousResponse is false', () => {
const step = makeMovement({ passPreviousResponse: false });
const ctx = makeInstructionContext({
previousOutput: {
persona: 'coder',
status: 'done',
content: 'should not appear',
timestamp: new Date(),
},
});
const template = 'Previous: {previous_response}';
const result = replaceTemplatePlaceholders(template, step, ctx);
expect(result).toBe('Previous: {previous_response}');
});
it('should replace {user_inputs} with joined inputs', () => {
const step = makeMovement();
const ctx = makeInstructionContext({ userInputs: ['input 1', 'input 2', 'input 3'] });
const template = 'Inputs: {user_inputs}';
const result = replaceTemplatePlaceholders(template, step, ctx);
expect(result).toBe('Inputs: input 1\ninput 2\ninput 3');
});
it('should replace {report_dir} with report directory', () => {
const step = makeMovement();
const ctx = makeInstructionContext({ reportDir: '/tmp/reports/run-1' });
const template = 'Reports: {report_dir}';
const result = replaceTemplatePlaceholders(template, step, ctx);
expect(result).toBe('Reports: /tmp/reports/run-1');
});
it('should replace {report:filename} with full path', () => {
const step = makeMovement();
const ctx = makeInstructionContext({ reportDir: '/tmp/reports' });
const template = 'Read {report:review.md} and {report:plan.md}';
const result = replaceTemplatePlaceholders(template, step, ctx);
expect(result).toBe('Read /tmp/reports/review.md and /tmp/reports/plan.md');
});
it('should handle template with multiple different placeholders', () => {
const step = makeMovement();
const ctx = makeInstructionContext({
task: 'test task',
iteration: 2,
maxMovements: 5,
movementIteration: 1,
reportDir: '/reports',
});
const template = '{task} - iter {iteration}/{max_movements} - mv {movement_iteration} - dir {report_dir}';
const result = replaceTemplatePlaceholders(template, step, ctx);
expect(result).toBe('test task - iter 2/5 - mv 1 - dir /reports');
});
it('should leave unreplaced placeholders when reportDir is undefined', () => {
const step = makeMovement();
const ctx = makeInstructionContext({ reportDir: undefined });
const template = 'Dir: {report_dir} File: {report:test.md}';
const result = replaceTemplatePlaceholders(template, step, ctx);
expect(result).toBe('Dir: {report_dir} File: {report:test.md}');
});
});