@tosin2013/mcp-shrimp-task-manager
Version:
Enhanced MCP Shrimp Task Manager with comprehensive LLM integration. A task management tool built for AI Agents, emphasizing chain-of-thought, reflection, and style consistency. Features real GPT-4 ↔ MCP tools communication, comprehensive testing pipeline
255 lines • 11.2 kB
JavaScript
/**
* Tests for the Specification Manager component
*
* This file contains tests for the Specification Manager component of the Idea Honing Tool.
*/
import { expect, use } from 'chai';
import * as sinon from 'sinon';
import sinonChai from 'sinon-chai';
// Use sinon-chai plugin
use(sinonChai);
// Import the component to test
import { createSpecification, getSpecification, updateSpecification, searchSpecifications, rebuildSearchIndex } from '../components/specification-manager.js';
// Import dependencies for mocking
import * as fileSystem from '../utils/file-system.js';
import * as templateEngine from '../components/template-engine.js';
import * as searchIndexer from '../utils/search-indexer.js';
// Test suite
describe('Specification Manager', () => {
// Setup sandbox for stubs and mocks
let sandbox;
// Sample data for tests
const sampleSpecId = '12345';
const sampleTitle = 'Test Specification';
const sampleDescription = 'This is a test specification';
const sampleAuthor = 'Test Author';
const sampleTemplate = {
name: 'test-template',
content: '# {{title}}\n\n{{description}}\n\n## Requirements\n\n## Design\n\n## Implementation',
sections: [
{
id: 'overview',
title: 'Overview',
placeholder: 'Provide an overview',
required: true,
order: 1
},
{
id: 'requirements',
title: 'Requirements',
placeholder: 'List requirements',
required: true,
order: 2
}
]
};
const sampleSpecification = {
id: sampleSpecId,
title: sampleTitle,
sections: [
{
id: 'overview',
title: 'Overview',
content: sampleDescription,
order: 1
},
{
id: 'requirements',
title: 'Requirements',
content: 'Test requirements',
order: 2
}
],
metadata: {
authors: [sampleAuthor],
status: 'draft',
tags: ['test'],
relatedSpecs: []
},
version: 1,
changeHistory: [],
projectId: 'default',
featureId: '',
createdAt: new Date(),
updatedAt: new Date()
};
// Setup before each test
beforeEach(() => {
// Create a sandbox for stubs
sandbox = sinon.createSandbox();
// Stub file system operations
sandbox.stub(fileSystem, 'saveSpecification').resolves();
sandbox.stub(fileSystem, 'loadSpecification').resolves(sampleSpecification);
sandbox.stub(fileSystem, 'createBackup').resolves();
sandbox.stub(fileSystem, 'saveChangeRecord').resolves();
sandbox.stub(fileSystem, 'listSpecifications').resolves([sampleSpecId]);
// Stub template engine operations
sandbox.stub(templateEngine, 'loadTemplate').resolves(sampleTemplate);
sandbox.stub(templateEngine, 'createSectionsFromTemplate').returns(sampleSpecification.sections);
sandbox.stub(templateEngine, 'renderTemplate').returns(sampleTemplate.content);
// Stub search indexer operations
sandbox.stub(searchIndexer, 'indexSpecification').resolves();
sandbox.stub(searchIndexer, 'buildSearchIndex').resolves();
sandbox.stub(searchIndexer, 'searchSpecifications').resolves([
{
id: sampleSpecId,
title: sampleTitle,
description: sampleDescription,
projectId: 'default',
featureId: '',
status: 'draft',
relevance: 1.0,
matchingSections: ['Overview'],
matchingKeywords: ['test']
}
]);
});
// Cleanup after each test
afterEach(() => {
// Restore all stubs
sandbox.restore();
});
// Test cases
describe('createSpecification', () => {
it('should create a new specification with the provided data', async () => {
// Call the function
const result = await createSpecification(sampleTitle, sampleDescription, sampleAuthor);
// Verify the result
expect(result).to.be.an('object');
expect(result.title).to.equal(sampleTitle);
expect(result.metadata.authors).to.include(sampleAuthor);
expect(result.version).to.equal(1);
// Verify that the file system operations were called
expect(fileSystem.saveSpecification).to.have.been.calledOnce;
expect(fileSystem.saveChangeRecord).to.have.been.calledOnce;
// Verify that the search indexer was called
expect(searchIndexer.indexSpecification).to.have.been.calledOnce;
});
it('should handle errors during specification creation', async () => {
// Restore the stub and create a new one that throws an error
sandbox.restore();
sandbox.stub(fileSystem, 'saveSpecification').throws(new Error('Test error'));
// Call the function and expect it to throw
try {
await createSpecification(sampleTitle, sampleDescription, sampleAuthor);
// If we get here, the test should fail
expect.fail('Expected an error to be thrown');
}
catch (error) {
// Verify that the error was thrown
expect(error).to.be.an('error');
expect(error.message).to.include('Failed to create specification');
}
});
});
describe('getSpecification', () => {
it('should retrieve a specification by ID', async () => {
// Call the function
const result = await getSpecification(sampleSpecId);
// Verify the result
expect(result).to.be.an('object');
expect(result.id).to.equal(sampleSpecId);
expect(result.title).to.equal(sampleTitle);
// Verify that the file system operations were called
expect(fileSystem.loadSpecification).to.have.been.calledOnce;
expect(fileSystem.loadSpecification).to.have.been.calledWith(sampleSpecId);
});
it('should handle errors during specification retrieval', async () => {
// Restore the stub and create a new one that throws an error
sandbox.restore();
sandbox.stub(fileSystem, 'loadSpecification').throws(new Error('Test error'));
// Call the function and expect it to throw
try {
await getSpecification(sampleSpecId);
// If we get here, the test should fail
expect.fail('Expected an error to be thrown');
}
catch (error) {
// Verify that the error was thrown
expect(error).to.be.an('error');
expect(error.message).to.include('Failed to retrieve specification');
}
});
});
describe('updateSpecification', () => {
it('should update a specification with the provided data', async () => {
// Create update data
const updates = {
title: 'Updated Title',
metadata: {
status: 'review',
tags: ['test', 'updated'],
authors: [sampleAuthor],
relatedSpecs: []
}
};
// Call the function
const result = await updateSpecification(sampleSpecId, updates, sampleAuthor, 'Updated title');
// Verify the result
expect(result).to.be.an('object');
expect(result.title).to.equal(updates.title);
if (updates.metadata?.status) {
expect(result.metadata.status).to.equal(updates.metadata.status);
}
expect(result.version).to.equal(2); // Version should be incremented
// Verify that the file system operations were called
expect(fileSystem.loadSpecification).to.have.been.calledOnce;
expect(fileSystem.createBackup).to.have.been.calledOnce;
expect(fileSystem.saveSpecification).to.have.been.calledOnce;
// Verify that the search indexer was called
expect(searchIndexer.indexSpecification).to.have.been.calledOnce;
});
});
describe('searchSpecifications', () => {
it('should search for specifications matching a query', async () => {
// Call the function
const results = await searchSpecifications('test');
// Verify the results
expect(results).to.be.an('array');
expect(results.length).to.be.greaterThan(0);
expect(results[0].id).to.equal(sampleSpecId);
expect(results[0].title).to.equal(sampleTitle);
// Verify that the search indexer was called
expect(searchIndexer.searchSpecifications).to.have.been.calledOnce;
expect(searchIndexer.searchSpecifications).to.have.been.calledWith('test');
});
it('should handle errors during search and fall back to basic search', async () => {
// Restore the stub and create a new one that throws an error
sandbox.restore();
sandbox.stub(searchIndexer, 'searchSpecifications').throws(new Error('Test error'));
sandbox.stub(fileSystem, 'listSpecifications').resolves([sampleSpecId]);
sandbox.stub(fileSystem, 'loadSpecification').resolves(sampleSpecification);
// Call the function
const results = await searchSpecifications('test');
// Verify the results
expect(results).to.be.an('array');
expect(results.length).to.be.greaterThan(0);
expect(results[0].id).to.equal(sampleSpecId);
});
});
describe('rebuildSearchIndex', () => {
it('should rebuild the search index', async () => {
// Call the function
await rebuildSearchIndex();
// Verify that the search indexer was called
expect(searchIndexer.buildSearchIndex).to.have.been.calledOnce;
});
it('should handle errors during index rebuilding', async () => {
// Restore the stub and create a new one that throws an error
sandbox.restore();
sandbox.stub(searchIndexer, 'buildSearchIndex').throws(new Error('Test error'));
// Call the function and expect it to throw
try {
await rebuildSearchIndex();
// If we get here, the test should fail
expect.fail('Expected an error to be thrown');
}
catch (error) {
// Verify that the error was thrown
expect(error).to.be.an('error');
expect(error.message).to.include('Failed to rebuild search index');
}
});
});
});
//# sourceMappingURL=specification-manager.test.js.map