2025-07-23 03:01:01 +08:00
|
|
|
import { RuntimeContext } from '@mastra/core/runtime-context';
|
|
|
|
import { beforeEach, describe, expect, it } from 'vitest';
|
|
|
|
import type { DocsAgentContext } from '../../../context/docs-agent-context';
|
|
|
|
import { checkOffTodoList } from './check-off-todo-list-tool';
|
|
|
|
|
|
|
|
describe('checkOffTodoList', () => {
|
|
|
|
let runtimeContext: RuntimeContext<DocsAgentContext>;
|
|
|
|
|
|
|
|
beforeEach(() => {
|
|
|
|
runtimeContext = new RuntimeContext<DocsAgentContext>();
|
|
|
|
});
|
|
|
|
|
2025-07-29 02:41:07 +08:00
|
|
|
it('should check off a single todo item successfully', async () => {
|
2025-07-23 03:01:01 +08:00
|
|
|
const initialTodoList = `## Todo List
|
|
|
|
- [ ] Write unit tests
|
|
|
|
- [ ] Implement feature
|
|
|
|
- [ ] Review code`;
|
|
|
|
|
|
|
|
runtimeContext.set('todoList', initialTodoList);
|
|
|
|
|
|
|
|
const result = await checkOffTodoList.execute({
|
2025-07-29 02:41:07 +08:00
|
|
|
context: { todoItems: ['Write unit tests'] },
|
2025-07-23 03:01:01 +08:00
|
|
|
runtimeContext,
|
|
|
|
});
|
|
|
|
|
|
|
|
expect(result.success).toBe(true);
|
|
|
|
expect(result.updatedTodoList).toContain('- [x] Write unit tests');
|
|
|
|
expect(result.updatedTodoList).toContain('- [ ] Implement feature');
|
|
|
|
expect(result.updatedTodoList).toContain('- [ ] Review code');
|
2025-07-29 02:41:07 +08:00
|
|
|
expect(result.message).toBe('Successfully checked off all 1 items');
|
|
|
|
expect(result.checkedOffItems).toEqual(['Write unit tests']);
|
|
|
|
expect(result.failedItems).toEqual([]);
|
2025-07-23 03:01:01 +08:00
|
|
|
|
|
|
|
// Verify context was updated
|
|
|
|
const updatedContext = runtimeContext.get('todoList');
|
|
|
|
expect(updatedContext).toBe(result.updatedTodoList);
|
|
|
|
});
|
|
|
|
|
2025-07-29 02:41:07 +08:00
|
|
|
it('should check off multiple todo items successfully', async () => {
|
|
|
|
const initialTodoList = `## Todo List
|
|
|
|
- [ ] Write unit tests
|
|
|
|
- [ ] Implement feature
|
|
|
|
- [ ] Review code
|
|
|
|
- [ ] Deploy to production`;
|
|
|
|
|
|
|
|
runtimeContext.set('todoList', initialTodoList);
|
|
|
|
|
|
|
|
const result = await checkOffTodoList.execute({
|
|
|
|
context: { todoItems: ['Write unit tests', 'Review code'] },
|
|
|
|
runtimeContext,
|
|
|
|
});
|
|
|
|
|
|
|
|
expect(result.success).toBe(true);
|
|
|
|
expect(result.updatedTodoList).toContain('- [x] Write unit tests');
|
|
|
|
expect(result.updatedTodoList).toContain('- [ ] Implement feature');
|
|
|
|
expect(result.updatedTodoList).toContain('- [x] Review code');
|
|
|
|
expect(result.updatedTodoList).toContain('- [ ] Deploy to production');
|
|
|
|
expect(result.message).toBe('Successfully checked off all 2 items');
|
|
|
|
expect(result.checkedOffItems).toEqual(['Write unit tests', 'Review code']);
|
|
|
|
expect(result.failedItems).toEqual([]);
|
|
|
|
});
|
|
|
|
|
|
|
|
it('should handle partial success when some items are not found', async () => {
|
|
|
|
const initialTodoList = `## Todo List
|
|
|
|
- [ ] Write unit tests
|
|
|
|
- [ ] Implement feature
|
|
|
|
- [ ] Review code`;
|
|
|
|
|
|
|
|
runtimeContext.set('todoList', initialTodoList);
|
|
|
|
|
|
|
|
const result = await checkOffTodoList.execute({
|
|
|
|
context: { todoItems: ['Write unit tests', 'Non-existent task', 'Review code'] },
|
|
|
|
runtimeContext,
|
|
|
|
});
|
|
|
|
|
|
|
|
expect(result.success).toBe(true);
|
|
|
|
expect(result.updatedTodoList).toContain('- [x] Write unit tests');
|
|
|
|
expect(result.updatedTodoList).toContain('- [ ] Implement feature');
|
|
|
|
expect(result.updatedTodoList).toContain('- [x] Review code');
|
|
|
|
expect(result.message).toBe('Successfully checked off 2 out of 3 items');
|
|
|
|
expect(result.checkedOffItems).toEqual(['Write unit tests', 'Review code']);
|
|
|
|
expect(result.failedItems).toEqual(['Non-existent task']);
|
|
|
|
});
|
|
|
|
|
2025-07-23 03:01:01 +08:00
|
|
|
it('should return error when todo list is not found in context', async () => {
|
|
|
|
const result = await checkOffTodoList.execute({
|
2025-07-29 02:41:07 +08:00
|
|
|
context: { todoItems: ['Some task', 'Another task'] },
|
2025-07-23 03:01:01 +08:00
|
|
|
runtimeContext,
|
|
|
|
});
|
|
|
|
|
|
|
|
expect(result.success).toBe(false);
|
|
|
|
expect(result.updatedTodoList).toBe('');
|
|
|
|
expect(result.message).toBe('No todo list found in context');
|
2025-07-29 02:41:07 +08:00
|
|
|
expect(result.checkedOffItems).toEqual([]);
|
|
|
|
expect(result.failedItems).toEqual(['Some task', 'Another task']);
|
2025-07-23 03:01:01 +08:00
|
|
|
});
|
|
|
|
|
2025-07-29 02:41:07 +08:00
|
|
|
it('should return error when no items could be checked off', async () => {
|
2025-07-23 03:01:01 +08:00
|
|
|
const todoList = `## Todo List
|
|
|
|
- [ ] Write unit tests
|
|
|
|
- [ ] Implement feature`;
|
|
|
|
|
|
|
|
runtimeContext.set('todoList', todoList);
|
|
|
|
|
|
|
|
const result = await checkOffTodoList.execute({
|
2025-07-29 02:41:07 +08:00
|
|
|
context: { todoItems: ['Non-existent task', 'Another missing task'] },
|
2025-07-23 03:01:01 +08:00
|
|
|
runtimeContext,
|
|
|
|
});
|
|
|
|
|
|
|
|
expect(result.success).toBe(false);
|
|
|
|
expect(result.updatedTodoList).toBe(todoList);
|
|
|
|
expect(result.message).toBe(
|
2025-07-29 02:41:07 +08:00
|
|
|
'No items were checked off - they may not exist or are already checked'
|
2025-07-23 03:01:01 +08:00
|
|
|
);
|
2025-07-29 02:41:07 +08:00
|
|
|
expect(result.checkedOffItems).toEqual([]);
|
|
|
|
expect(result.failedItems).toEqual(['Non-existent task', 'Another missing task']);
|
2025-07-23 03:01:01 +08:00
|
|
|
});
|
|
|
|
|
2025-07-29 02:41:07 +08:00
|
|
|
it('should not check off already checked items', async () => {
|
2025-07-23 03:01:01 +08:00
|
|
|
const todoList = `## Todo List
|
|
|
|
- [x] Write unit tests
|
2025-07-29 02:41:07 +08:00
|
|
|
- [ ] Implement feature
|
|
|
|
- [ ] Review code`;
|
|
|
|
|
|
|
|
runtimeContext.set('todoList', todoList);
|
|
|
|
|
|
|
|
const result = await checkOffTodoList.execute({
|
|
|
|
context: { todoItems: ['Write unit tests', 'Implement feature'] },
|
|
|
|
runtimeContext,
|
|
|
|
});
|
|
|
|
|
|
|
|
expect(result.success).toBe(true);
|
|
|
|
expect(result.updatedTodoList).toContain('- [x] Write unit tests');
|
|
|
|
expect(result.updatedTodoList).toContain('- [x] Implement feature');
|
|
|
|
expect(result.message).toBe('Successfully checked off 1 out of 2 items');
|
|
|
|
expect(result.checkedOffItems).toEqual(['Implement feature']);
|
|
|
|
expect(result.failedItems).toEqual(['Write unit tests']);
|
|
|
|
});
|
|
|
|
|
|
|
|
it('should handle empty array of todo items', async () => {
|
|
|
|
const todoList = `## Todo List
|
|
|
|
- [ ] Write unit tests`;
|
2025-07-23 03:01:01 +08:00
|
|
|
|
|
|
|
runtimeContext.set('todoList', todoList);
|
|
|
|
|
|
|
|
const result = await checkOffTodoList.execute({
|
2025-07-29 02:41:07 +08:00
|
|
|
context: { todoItems: [] },
|
2025-07-23 03:01:01 +08:00
|
|
|
runtimeContext,
|
|
|
|
});
|
|
|
|
|
|
|
|
expect(result.success).toBe(false);
|
|
|
|
expect(result.updatedTodoList).toBe(todoList);
|
2025-07-29 02:41:07 +08:00
|
|
|
expect(result.checkedOffItems).toEqual([]);
|
|
|
|
expect(result.failedItems).toEqual([]);
|
2025-07-23 03:01:01 +08:00
|
|
|
});
|
|
|
|
|
|
|
|
it('should handle first occurrence when there are duplicates', async () => {
|
|
|
|
const todoList = `## Todo List
|
2025-07-29 02:41:07 +08:00
|
|
|
- [ ] Write unit tests
|
|
|
|
- [ ] Write unit tests
|
|
|
|
- [ ] Implement feature`;
|
2025-07-23 03:01:01 +08:00
|
|
|
|
|
|
|
runtimeContext.set('todoList', todoList);
|
|
|
|
|
|
|
|
const result = await checkOffTodoList.execute({
|
2025-07-29 02:41:07 +08:00
|
|
|
context: { todoItems: ['Write unit tests'] },
|
2025-07-23 03:01:01 +08:00
|
|
|
runtimeContext,
|
|
|
|
});
|
|
|
|
|
|
|
|
expect(result.success).toBe(true);
|
2025-07-29 02:41:07 +08:00
|
|
|
// Only the first occurrence should be checked off
|
|
|
|
const lines = result.updatedTodoList.split('\n');
|
|
|
|
expect(lines[1]).toBe('- [x] Write unit tests');
|
|
|
|
expect(lines[2]).toBe('- [ ] Write unit tests');
|
|
|
|
expect(result.checkedOffItems).toEqual(['Write unit tests']);
|
2025-07-23 03:01:01 +08:00
|
|
|
});
|
|
|
|
|
|
|
|
it('should validate input schema', () => {
|
2025-07-29 02:41:07 +08:00
|
|
|
const validInput = { todoItems: ['Test task', 'Another task'] };
|
2025-07-23 03:01:01 +08:00
|
|
|
const parsed = checkOffTodoList.inputSchema.parse(validInput);
|
|
|
|
expect(parsed).toEqual(validInput);
|
|
|
|
|
2025-07-29 02:41:07 +08:00
|
|
|
// Empty array should be valid
|
|
|
|
const emptyInput = { todoItems: [] };
|
|
|
|
const emptyParsed = checkOffTodoList.inputSchema.parse(emptyInput);
|
|
|
|
expect(emptyParsed).toEqual(emptyInput);
|
|
|
|
|
|
|
|
expect(() => {
|
|
|
|
checkOffTodoList.inputSchema.parse({ todoItems: 'not an array' });
|
|
|
|
}).toThrow();
|
|
|
|
|
2025-07-23 03:01:01 +08:00
|
|
|
expect(() => {
|
2025-07-29 02:41:07 +08:00
|
|
|
checkOffTodoList.inputSchema.parse({ todoItems: [123, 456] });
|
2025-07-23 03:01:01 +08:00
|
|
|
}).toThrow();
|
|
|
|
|
|
|
|
expect(() => {
|
|
|
|
checkOffTodoList.inputSchema.parse({});
|
|
|
|
}).toThrow();
|
|
|
|
});
|
|
|
|
|
|
|
|
it('should validate output schema', () => {
|
|
|
|
const validOutput = {
|
|
|
|
success: true,
|
|
|
|
updatedTodoList: '- [x] Done',
|
|
|
|
message: 'Success',
|
2025-07-29 02:41:07 +08:00
|
|
|
checkedOffItems: ['Done'],
|
|
|
|
failedItems: [],
|
2025-07-23 03:01:01 +08:00
|
|
|
};
|
|
|
|
const parsed = checkOffTodoList.outputSchema.parse(validOutput);
|
|
|
|
expect(parsed).toEqual(validOutput);
|
|
|
|
|
|
|
|
const minimalOutput = {
|
|
|
|
success: false,
|
|
|
|
updatedTodoList: '',
|
2025-07-29 02:41:07 +08:00
|
|
|
checkedOffItems: [],
|
|
|
|
failedItems: ['Failed item'],
|
2025-07-23 03:01:01 +08:00
|
|
|
};
|
|
|
|
const minimalParsed = checkOffTodoList.outputSchema.parse(minimalOutput);
|
|
|
|
expect(minimalParsed).toEqual(minimalOutput);
|
|
|
|
});
|
|
|
|
});
|