309 lines
8.4 KiB
TypeScript
309 lines
8.4 KiB
TypeScript
import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';
|
|
import { SequentialThinkingServer, ThoughtData } from '../lib.js';
|
|
|
|
// Mock chalk to avoid ESM issues
|
|
vi.mock('chalk', () => {
|
|
const chalkMock = {
|
|
yellow: (str: string) => str,
|
|
green: (str: string) => str,
|
|
blue: (str: string) => str,
|
|
};
|
|
return {
|
|
default: chalkMock,
|
|
};
|
|
});
|
|
|
|
describe('SequentialThinkingServer', () => {
|
|
let server: SequentialThinkingServer;
|
|
|
|
beforeEach(() => {
|
|
// Disable thought logging for tests
|
|
process.env.DISABLE_THOUGHT_LOGGING = 'true';
|
|
server = new SequentialThinkingServer();
|
|
});
|
|
|
|
// Note: Input validation tests removed - validation now happens at the tool
|
|
// registration layer via Zod schemas before processThought is called
|
|
|
|
describe('processThought - valid inputs', () => {
|
|
it('should accept valid basic thought', () => {
|
|
const input = {
|
|
thought: 'This is my first thought',
|
|
thoughtNumber: 1,
|
|
totalThoughts: 3,
|
|
nextThoughtNeeded: true
|
|
};
|
|
|
|
const result = server.processThought(input);
|
|
expect(result.isError).toBeUndefined();
|
|
|
|
const data = JSON.parse(result.content[0].text);
|
|
expect(data.thoughtNumber).toBe(1);
|
|
expect(data.totalThoughts).toBe(3);
|
|
expect(data.nextThoughtNeeded).toBe(true);
|
|
expect(data.thoughtHistoryLength).toBe(1);
|
|
});
|
|
|
|
it('should accept thought with optional fields', () => {
|
|
const input = {
|
|
thought: 'Revising my earlier idea',
|
|
thoughtNumber: 2,
|
|
totalThoughts: 3,
|
|
nextThoughtNeeded: true,
|
|
isRevision: true,
|
|
revisesThought: 1,
|
|
needsMoreThoughts: false
|
|
};
|
|
|
|
const result = server.processThought(input);
|
|
expect(result.isError).toBeUndefined();
|
|
|
|
const data = JSON.parse(result.content[0].text);
|
|
expect(data.thoughtNumber).toBe(2);
|
|
expect(data.thoughtHistoryLength).toBe(1);
|
|
});
|
|
|
|
it('should track multiple thoughts in history', () => {
|
|
const input1 = {
|
|
thought: 'First thought',
|
|
thoughtNumber: 1,
|
|
totalThoughts: 3,
|
|
nextThoughtNeeded: true
|
|
};
|
|
|
|
const input2 = {
|
|
thought: 'Second thought',
|
|
thoughtNumber: 2,
|
|
totalThoughts: 3,
|
|
nextThoughtNeeded: true
|
|
};
|
|
|
|
const input3 = {
|
|
thought: 'Final thought',
|
|
thoughtNumber: 3,
|
|
totalThoughts: 3,
|
|
nextThoughtNeeded: false
|
|
};
|
|
|
|
server.processThought(input1);
|
|
server.processThought(input2);
|
|
const result = server.processThought(input3);
|
|
|
|
const data = JSON.parse(result.content[0].text);
|
|
expect(data.thoughtHistoryLength).toBe(3);
|
|
expect(data.nextThoughtNeeded).toBe(false);
|
|
});
|
|
|
|
it('should auto-adjust totalThoughts if thoughtNumber exceeds it', () => {
|
|
const input = {
|
|
thought: 'Thought 5',
|
|
thoughtNumber: 5,
|
|
totalThoughts: 3,
|
|
nextThoughtNeeded: true
|
|
};
|
|
|
|
const result = server.processThought(input);
|
|
const data = JSON.parse(result.content[0].text);
|
|
|
|
expect(data.totalThoughts).toBe(5);
|
|
});
|
|
});
|
|
|
|
describe('processThought - branching', () => {
|
|
it('should track branches correctly', () => {
|
|
const input1 = {
|
|
thought: 'Main thought',
|
|
thoughtNumber: 1,
|
|
totalThoughts: 3,
|
|
nextThoughtNeeded: true
|
|
};
|
|
|
|
const input2 = {
|
|
thought: 'Branch A thought',
|
|
thoughtNumber: 2,
|
|
totalThoughts: 3,
|
|
nextThoughtNeeded: true,
|
|
branchFromThought: 1,
|
|
branchId: 'branch-a'
|
|
};
|
|
|
|
const input3 = {
|
|
thought: 'Branch B thought',
|
|
thoughtNumber: 2,
|
|
totalThoughts: 3,
|
|
nextThoughtNeeded: false,
|
|
branchFromThought: 1,
|
|
branchId: 'branch-b'
|
|
};
|
|
|
|
server.processThought(input1);
|
|
server.processThought(input2);
|
|
const result = server.processThought(input3);
|
|
|
|
const data = JSON.parse(result.content[0].text);
|
|
expect(data.branches).toContain('branch-a');
|
|
expect(data.branches).toContain('branch-b');
|
|
expect(data.branches.length).toBe(2);
|
|
expect(data.thoughtHistoryLength).toBe(3);
|
|
});
|
|
|
|
it('should allow multiple thoughts in same branch', () => {
|
|
const input1 = {
|
|
thought: 'Branch thought 1',
|
|
thoughtNumber: 1,
|
|
totalThoughts: 2,
|
|
nextThoughtNeeded: true,
|
|
branchFromThought: 1,
|
|
branchId: 'branch-a'
|
|
};
|
|
|
|
const input2 = {
|
|
thought: 'Branch thought 2',
|
|
thoughtNumber: 2,
|
|
totalThoughts: 2,
|
|
nextThoughtNeeded: false,
|
|
branchFromThought: 1,
|
|
branchId: 'branch-a'
|
|
};
|
|
|
|
server.processThought(input1);
|
|
const result = server.processThought(input2);
|
|
|
|
const data = JSON.parse(result.content[0].text);
|
|
expect(data.branches).toContain('branch-a');
|
|
expect(data.branches.length).toBe(1);
|
|
});
|
|
});
|
|
|
|
describe('processThought - edge cases', () => {
|
|
it('should handle very long thought strings', () => {
|
|
const input = {
|
|
thought: 'a'.repeat(10000),
|
|
thoughtNumber: 1,
|
|
totalThoughts: 1,
|
|
nextThoughtNeeded: false
|
|
};
|
|
|
|
const result = server.processThought(input);
|
|
expect(result.isError).toBeUndefined();
|
|
});
|
|
|
|
it('should handle thoughtNumber = 1, totalThoughts = 1', () => {
|
|
const input = {
|
|
thought: 'Only thought',
|
|
thoughtNumber: 1,
|
|
totalThoughts: 1,
|
|
nextThoughtNeeded: false
|
|
};
|
|
|
|
const result = server.processThought(input);
|
|
expect(result.isError).toBeUndefined();
|
|
|
|
const data = JSON.parse(result.content[0].text);
|
|
expect(data.thoughtNumber).toBe(1);
|
|
expect(data.totalThoughts).toBe(1);
|
|
});
|
|
|
|
it('should handle nextThoughtNeeded = false', () => {
|
|
const input = {
|
|
thought: 'Final thought',
|
|
thoughtNumber: 3,
|
|
totalThoughts: 3,
|
|
nextThoughtNeeded: false
|
|
};
|
|
|
|
const result = server.processThought(input);
|
|
const data = JSON.parse(result.content[0].text);
|
|
|
|
expect(data.nextThoughtNeeded).toBe(false);
|
|
});
|
|
});
|
|
|
|
describe('processThought - response format', () => {
|
|
it('should return correct response structure on success', () => {
|
|
const input = {
|
|
thought: 'Test thought',
|
|
thoughtNumber: 1,
|
|
totalThoughts: 1,
|
|
nextThoughtNeeded: false
|
|
};
|
|
|
|
const result = server.processThought(input);
|
|
|
|
expect(result).toHaveProperty('content');
|
|
expect(Array.isArray(result.content)).toBe(true);
|
|
expect(result.content.length).toBe(1);
|
|
expect(result.content[0]).toHaveProperty('type', 'text');
|
|
expect(result.content[0]).toHaveProperty('text');
|
|
});
|
|
|
|
it('should return valid JSON in response', () => {
|
|
const input = {
|
|
thought: 'Test thought',
|
|
thoughtNumber: 1,
|
|
totalThoughts: 1,
|
|
nextThoughtNeeded: false
|
|
};
|
|
|
|
const result = server.processThought(input);
|
|
|
|
expect(() => JSON.parse(result.content[0].text)).not.toThrow();
|
|
});
|
|
});
|
|
|
|
describe('processThought - with logging enabled', () => {
|
|
let serverWithLogging: SequentialThinkingServer;
|
|
|
|
beforeEach(() => {
|
|
// Enable thought logging for these tests
|
|
delete process.env.DISABLE_THOUGHT_LOGGING;
|
|
serverWithLogging = new SequentialThinkingServer();
|
|
});
|
|
|
|
afterEach(() => {
|
|
// Reset to disabled for other tests
|
|
process.env.DISABLE_THOUGHT_LOGGING = 'true';
|
|
});
|
|
|
|
it('should format and log regular thoughts', () => {
|
|
const input = {
|
|
thought: 'Test thought with logging',
|
|
thoughtNumber: 1,
|
|
totalThoughts: 3,
|
|
nextThoughtNeeded: true
|
|
};
|
|
|
|
const result = serverWithLogging.processThought(input);
|
|
expect(result.isError).toBeUndefined();
|
|
});
|
|
|
|
it('should format and log revision thoughts', () => {
|
|
const input = {
|
|
thought: 'Revised thought',
|
|
thoughtNumber: 2,
|
|
totalThoughts: 3,
|
|
nextThoughtNeeded: true,
|
|
isRevision: true,
|
|
revisesThought: 1
|
|
};
|
|
|
|
const result = serverWithLogging.processThought(input);
|
|
expect(result.isError).toBeUndefined();
|
|
});
|
|
|
|
it('should format and log branch thoughts', () => {
|
|
const input = {
|
|
thought: 'Branch thought',
|
|
thoughtNumber: 2,
|
|
totalThoughts: 3,
|
|
nextThoughtNeeded: false,
|
|
branchFromThought: 1,
|
|
branchId: 'branch-a'
|
|
};
|
|
|
|
const result = serverWithLogging.processThought(input);
|
|
expect(result.isError).toBeUndefined();
|
|
});
|
|
});
|
|
});
|