import { ArrayProcessingServer } from './server-class.js';
// Test utilities
class TestRunner {
constructor() {
this.tests = [];
this.passed = 0;
this.failed = 0;
}
test(name, fn) {
this.tests.push({ name, fn });
}
async run() {
console.log('Running Loop MCP Server Unit Tests...\n');
for (const test of this.tests) {
try {
await test.fn();
this.passed++;
console.log(`✅ ${test.name}`);
} catch (error) {
this.failed++;
console.log(`❌ ${test.name}`);
console.log(` Error: ${error.message}`);
}
}
console.log(`\nTest Summary: ${this.passed} passed, ${this.failed} failed\n`);
return this.failed === 0;
}
}
// Assertion helpers
function assertEqual(actual, expected, message = '') {
if (JSON.stringify(actual) !== JSON.stringify(expected)) {
throw new Error(`Assertion failed: ${message}\nExpected: ${JSON.stringify(expected)}\nActual: ${JSON.stringify(actual)}`);
}
}
function assertContains(text, substring, message = '') {
if (!text.includes(substring)) {
throw new Error(`Assertion failed: ${message}\nExpected "${text}" to contain "${substring}"`);
}
}
// Main test suite
async function runTests() {
const runner = new TestRunner();
// Test 1: Initialize array with default batch size
runner.test('Initialize array with default batch size', () => {
const server = new ArrayProcessingServer();
const result = server.initializeArray({
array: [1, 2, 3, 4, 5],
task: 'Test task',
});
assertContains(result.content[0].text, 'Array initialized with 5 items');
assertContains(result.content[0].text, 'Batch size: 1');
assertEqual(server.processingState.batchSize, 1);
assertEqual(server.processingState.array, [1, 2, 3, 4, 5]);
});
// Test 2: Initialize array with custom batch size
runner.test('Initialize array with custom batch size', () => {
const server = new ArrayProcessingServer();
const result = server.initializeArray({
array: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
task: 'Test task',
batchSize: 3,
});
assertContains(result.content[0].text, 'Array initialized with 10 items');
assertContains(result.content[0].text, 'Batch size: 3');
assertEqual(server.processingState.batchSize, 3);
});
// Test 3: Initialize with empty array
runner.test('Initialize with empty array', () => {
const server = new ArrayProcessingServer();
const result = server.initializeArray({
array: [],
task: 'Test task',
});
assertContains(result.content[0].text, 'Error: Please provide a non-empty array');
});
// Test 4: Get next item when not initialized
runner.test('Get next item when not initialized', () => {
const server = new ArrayProcessingServer();
const result = server.getNextItem();
assertContains(result.content[0].text, 'Error: Array not initialized');
});
// Test 5: Get next item
runner.test('Get next item works', () => {
const server = new ArrayProcessingServer();
server.initializeArray({
array: ['a', 'b', 'c'],
task: 'Process letters',
});
const result = server.getNextItem();
const data = JSON.parse(result.content[0].text);
assertEqual(data.item, 'a');
assertEqual(data.index, 0);
assertEqual(data.total, 3);
assertEqual(data.remaining, 3);
});
// Test 6: Get next item when all processed
runner.test('Get next item when all processed', () => {
const server = new ArrayProcessingServer();
server.initializeArray({
array: ['a'],
task: 'Process',
});
server.processingState.currentIndex = 1; // Simulate processed
const result = server.getNextItem();
assertEqual(result.content[0].text, 'All items have been processed.');
});
// Test 7: Get next batch when not initialized
runner.test('Get next batch when not initialized', () => {
const server = new ArrayProcessingServer();
const result = server.getNextBatch();
assertContains(result.content[0].text, 'Error: Array not initialized');
});
// Test 8: Get next batch
runner.test('Get next batch with custom batch size', () => {
const server = new ArrayProcessingServer();
server.initializeArray({
array: [10, 20, 30, 40, 50, 60, 70],
task: 'Process numbers',
batchSize: 3,
});
const result = server.getNextBatch();
const data = JSON.parse(result.content[0].text);
assertEqual(data.items, [10, 20, 30]);
assertEqual(data.startIndex, 0);
assertEqual(data.endIndex, 2);
assertEqual(data.batchSize, 3);
assertEqual(data.remaining, 7);
});
// Test 9: Get next batch when all processed
runner.test('Get next batch when all processed', () => {
const server = new ArrayProcessingServer();
server.initializeArray({
array: [1, 2, 3],
task: 'Process',
batchSize: 2,
});
server.processingState.currentIndex = 3; // Simulate all processed
const result = server.getNextBatch();
assertEqual(result.content[0].text, 'All items have been processed.');
});
// Test 10: Store result when not initialized
runner.test('Store result when not initialized', () => {
const server = new ArrayProcessingServer();
const result = server.storeResult({ result: 'test' });
assertContains(result.content[0].text, 'Error: Array not initialized');
});
// Test 11: Store single result
runner.test('Store single result', () => {
const server = new ArrayProcessingServer();
server.initializeArray({
array: ['test'],
task: 'Process string',
});
server.getNextItem();
const result = server.storeResult({
result: 'PROCESSED',
});
assertContains(result.content[0].text, 'Result stored');
assertContains(result.content[0].text, 'All items processed');
assertEqual(server.processingState.results.length, 1);
assertEqual(server.processingState.results[0].result, 'PROCESSED');
});
// Test 12: Store result when no more items
runner.test('Store result when no more items', () => {
const server = new ArrayProcessingServer();
server.initializeArray({
array: ['test'],
task: 'Process',
});
server.processingState.currentIndex = 1; // Already processed
const result = server.storeResult({ result: 'test' });
assertContains(result.content[0].text, 'Error: No more items to process');
});
// Test 13: Store batch results
runner.test('Store batch results', () => {
const server = new ArrayProcessingServer();
server.initializeArray({
array: [1, 2, 3, 4, 5, 6],
task: 'Square numbers',
batchSize: 3,
});
server.getNextBatch();
const result = server.storeResult({
result: [1, 4, 9],
});
assertContains(result.content[0].text, 'Result stored');
assertContains(result.content[0].text, '3 items remaining');
assertEqual(server.processingState.results.length, 3);
assertEqual(server.processingState.currentIndex, 3);
});
// Test 14: Store oversized batch results
runner.test('Store oversized batch results', () => {
const server = new ArrayProcessingServer();
server.initializeArray({
array: [1, 2, 3],
task: 'Test',
batchSize: 2,
});
const result = server.storeResult({
result: [10, 20, 30, 40, 50], // More results than items
});
// Should only store 3 results (all remaining items)
assertEqual(server.processingState.results.length, 3);
assertEqual(server.processingState.results.map(r => r.result), [10, 20, 30]);
});
// Test 15: Get all results when not initialized
runner.test('Get all results when not initialized', () => {
const server = new ArrayProcessingServer();
const result = server.getAllResults();
assertContains(result.content[0].text, 'Error: Array not initialized');
});
// Test 16: Get all results when not complete
runner.test('Get all results when not complete', () => {
const server = new ArrayProcessingServer();
server.initializeArray({
array: [1, 2, 3],
task: 'Test',
});
const result = server.getAllResults();
assertContains(result.content[0].text, 'Error: Processing not complete');
assertContains(result.content[0].text, '3 items remaining');
});
// Test 17: Get all results with summary
runner.test('Get all results with summary', () => {
const server = new ArrayProcessingServer();
server.initializeArray({
array: [1, 2],
task: 'Double',
});
server.storeResult({ result: 2 });
server.storeResult({ result: 4 });
const result = server.getAllResults({ summarize: true });
const data = JSON.parse(result.content[0].text);
assertEqual(data.processed, 2);
assertEqual(data.results.length, 2);
assertEqual(data.summary.totalItems, 2);
assertEqual(data.summary.task, 'Double');
assertContains(data.summary.completedAt, 'T'); // ISO date
});
// Test 18: Get all results without summary
runner.test('Get all results without summary', () => {
const server = new ArrayProcessingServer();
server.initializeArray({
array: [1],
task: 'Test',
});
server.storeResult({ result: 'done' });
const result = server.getAllResults();
const data = JSON.parse(result.content[0].text);
assertEqual(data.processed, 1);
assertEqual(data.results.length, 1);
assertEqual(data.summary, undefined);
});
// Test 19: Reset
runner.test('Reset processing state', () => {
const server = new ArrayProcessingServer();
server.initializeArray({
array: [1, 2, 3],
task: 'Test',
batchSize: 5,
});
const result = server.reset();
assertContains(result.content[0].text, 'Processing state has been reset');
assertEqual(server.processingState.array, []);
assertEqual(server.processingState.isInitialized, false);
assertEqual(server.processingState.batchSize, 1);
});
// Test 20: Complete flow with mixed operations
runner.test('Complete flow with mixed operations', () => {
const server = new ArrayProcessingServer();
// Initialize
server.initializeArray({
array: ['a', 'b', 'c', 'd', 'e'],
task: 'Uppercase',
batchSize: 2,
});
// Get single item
const item1 = server.getNextItem();
const data1 = JSON.parse(item1.content[0].text);
assertEqual(data1.item, 'a');
// Store single result
server.storeResult({ result: 'A' });
// Get batch (should get b, c)
const batch1 = server.getNextBatch();
const batchData1 = JSON.parse(batch1.content[0].text);
assertEqual(batchData1.items, ['b', 'c']);
// Store batch results
server.storeResult({ result: ['B', 'C'] });
// Get remaining as batch
const batch2 = server.getNextBatch();
const batchData2 = JSON.parse(batch2.content[0].text);
assertEqual(batchData2.items, ['d', 'e']);
// Store final results
server.storeResult({ result: ['D', 'E'] });
// Get all results
const allResults = server.getAllResults();
const finalData = JSON.parse(allResults.content[0].text);
assertEqual(finalData.results.map(r => r.result), ['A', 'B', 'C', 'D', 'E']);
});
// Test 21: Validate non-array input
runner.test('Initialize with non-array input', () => {
const server = new ArrayProcessingServer();
const result = server.initializeArray({
array: "not an array",
task: 'Test task',
});
assertContains(result.content[0].text, 'Error: Please provide a non-empty array');
});
// Run all tests
const success = await runner.run();
process.exit(success ? 0 : 1);
}
// Run the test suite
runTests().catch(error => {
console.error('Test suite failed:', error);
process.exit(1);
});