Skip to main content
Glama
v1.7.test.js10.1 kB
const request = require('supertest'); const { createApp } = require('./server.js'); const path = require('path'); const fs = require('fs').promises; const DB_PATH = path.join(__dirname, 'data', 'tpc.db'); describe('v1.7 SQLite Migration - Migration Tests', () => { let app; let db; beforeAll(async () => { const instance = await createApp(); app = instance.app; db = instance.db; }); test('Migration loads data from JSON files on first run', async () => { const plansResponse = await request(app).get('/plans'); expect(plansResponse.status).toBe(200); expect(Array.isArray(plansResponse.body)).toBe(true); expect(plansResponse.body.length).toBe(10); // From plans.json const thoughtsResponse = await request(app).get('/thoughts'); expect(thoughtsResponse.status).toBe(200); expect(Array.isArray(thoughtsResponse.body)).toBe(true); expect(thoughtsResponse.body.length).toBe(6); // From current thoughts.json (initial + design decisions) }); afterAll(() => { db.close(); }); }); describe('v1.7 SQLite Migration - Regression Tests', () => { let app; let db; beforeEach(async () => { const instance = await createApp({ skipMigration: true }); app = instance.app; db = instance.db; }); afterEach(() => { db.close(); }); test('Data persists after write/read in DB session', async () => { const newPlanRes = await request(app) .post('/plans') .send({ title: 'Persistent Plan', description: 'Test desc' }) .expect(201); const planId = newPlanRes.body.id; const getResponse = await request(app).get(`/plans/${planId}`); expect(getResponse.status).toBe(200); expect(getResponse.body.id).toBe(planId); expect(getResponse.body.title).toBe('Persistent Plan'); }); test('Concurrent requests do not corrupt data', async () => { const planPromises = [ request(app).post('/plans').send({ title: 'Concurrent 1', description: 'Desc 1' }), request(app).post('/plans').send({ title: 'Concurrent 2', description: 'Desc 2' }), request(app).post('/plans').send({ title: 'Concurrent 3', description: 'Desc 3' }) ]; const responses = await Promise.all(planPromises); expect(responses.every(r => r.status === 201)).toBe(true); const getPlans = await request(app).get('/plans'); expect(getPlans.body.length).toBe(3); }); test('POST /thoughts creates and persists thought', async () => { const response = await request(app) .post('/thoughts') .send({ content: 'Test thought v1.7' }) .expect(201); expect(response.body.content).toBe('Test thought v1.7'); expect(response.body.id).toBeDefined(); expect(response.body.timestamp).toBeDefined(); const getThoughts = await request(app).get('/thoughts'); expect(getThoughts.body.length).toBe(1); expect(getThoughts.body[0].content).toBe('Test thought v1.7'); }); test('POST /thoughts with plan_id links to plan', async () => { const planRes = await request(app) .post('/plans') .send({ title: 'Linked Plan', description: 'Desc' }) .expect(201); const planId = planRes.body.id; const thoughtRes = await request(app) .post('/thoughts') .send({ content: 'Linked thought', plan_id: planId.toString() }) .expect(201); expect(thoughtRes.body.plan_id).toBe(planId.toString()); const linkedThoughts = await request(app).get(`/plans/${planId}/thoughts`); expect(linkedThoughts.body.length).toBe(1); expect(linkedThoughts.body[0].content).toBe('Linked thought'); }); test('GET /thoughts returns all thoughts sorted by timestamp ASC', async () => { await request(app).post('/thoughts').send({ content: 'First' }); await new Promise(resolve => setTimeout(resolve, 10)); await request(app).post('/thoughts').send({ content: 'Second' }); const response = await request(app).get('/thoughts'); expect(response.body.length).toBe(2); expect(response.body[0].content).toBe('First'); expect(response.body[1].content).toBe('Second'); }); test('POST /plans creates plan with default status', async () => { const response = await request(app) .post('/plans') .send({ title: 'New Plan v1.7', description: 'Test desc' }) .expect(201); expect(response.body.status).toBe('proposed'); expect(response.body.id).toBe(1); }); test('GET /plans returns all plans sorted by timestamp ASC', async () => { await request(app).post('/plans').send({ title: 'Plan A', description: 'Desc A' }); await new Promise(resolve => setTimeout(resolve, 10)); await request(app).post('/plans').send({ title: 'Plan B', description: 'Desc B' }); const response = await request(app).get('/plans'); expect(response.body.length).toBe(2); expect(response.body[0].title).toBe('Plan A'); }); test('GET /plans/:id returns specific plan with changelog parsed', async () => { const postRes = await request(app) .post('/plans') .send({ title: 'Detailed Plan', description: 'Desc' }) .expect(201); const id = postRes.body.id; await request(app) .patch(`/plans/${id}/changelog`) .send({ change: 'Test change' }) .expect(200); const getRes = await request(app).get(`/plans/${id}`); expect(getRes.body.title).toBe('Detailed Plan'); expect(Array.isArray(getRes.body.changelog)).toBe(true); expect(getRes.body.changelog.length).toBe(1); expect(getRes.body.changelog[0].change).toBe('Test change'); }); test('PATCH /plans/:id updates status', async () => { const postRes = await request(app) .post('/plans') .send({ title: 'Status Plan', description: 'Desc' }) .expect(201); const id = postRes.body.id; await request(app) .patch(`/plans/${id}`) .send({ status: 'in_progress' }) .expect(200); const getRes = await request(app).get(`/plans/${id}`); expect(getRes.body.status).toBe('in_progress'); }); test('PATCH /plans/:id/changelog appends entry', async () => { const postRes = await request(app) .post('/plans') .send({ title: 'Changelog Plan', description: 'Desc' }) .expect(201); const id = postRes.body.id; const patchRes = await request(app) .patch(`/plans/${id}/changelog`) .send({ change: 'First change' }) .expect(200); expect(patchRes.body.changelog.length).toBe(1); expect(patchRes.body.changelog[0].change).toBe('First change'); await request(app) .patch(`/plans/${id}/changelog`) .send({ change: 'Second change' }) .expect(200); const getRes = await request(app).get(`/plans/${id}`); expect(getRes.body.changelog.length).toBe(2); }); test('GET /plans/:id/thoughts returns linked thoughts', async () => { const postPlan = await request(app) .post('/plans') .send({ title: 'Thoughts Plan', description: 'Desc' }) .expect(201); const planId = postPlan.body.id; await request(app) .post('/thoughts') .send({ content: 'Unlinked' }) .expect(201); await request(app) .post('/thoughts') .send({ content: 'Linked', plan_id: planId.toString() }) .expect(201); const response = await request(app).get(`/plans/${planId}/thoughts`); expect(response.body.length).toBe(1); expect(response.body[0].content).toBe('Linked'); }); test('GET /context returns incomplete plans and last 10 thoughts DESC', async () => { // Create completed plan await request(app) .post('/plans') .send({ title: 'Completed', description: 'Desc' }); const completedId = 1; await request(app).patch(`/plans/${completedId}`).send({ status: 'completed' }); // Incomplete await request(app) .post('/plans') .send({ title: 'Incomplete 1', description: 'Desc' }); await request(app) .post('/plans') .send({ title: 'Incomplete 2', description: 'Desc' }); // Create 12 thoughts with delays for timestamps for (let i = 0; i < 12; i++) { await request(app).post('/thoughts').send({ content: `Thought ${i}` }); if (i < 11) await new Promise(resolve => setTimeout(resolve, 1)); } const response = await request(app).get('/context'); expect(response.body.incompletePlans.length).toBe(2); expect(response.body.last10Thoughts.length).toBe(10); expect(response.body.last10Thoughts[0].content).toBe('Thought 11'); // Most recent expect(response.body.last10Thoughts[9].content).toBe('Thought 2'); // Oldest in last10 }); test('Error handling: Invalid inputs return 400', async () => { await request(app) .post('/thoughts') .send({ content: '' }) .expect(400); await request(app) .post('/plans') .send({ title: '', description: 'Desc' }) .expect(400); const planRes = await request(app) .post('/plans') .send({ title: 'Invalid Status Plan', description: 'Desc' }) .expect(201); const id = planRes.body.id; await request(app) .patch(`/plans/${id}`) .send({ status: 'invalid' }) .expect(400); await request(app) .patch(`/plans/${id}/changelog`) .send({ change: '' }) .expect(400); }); test('Non-existent resources return 404', async () => { await request(app).get('/plans/999').expect(404); const thoughtsRes = await request(app).get('/plans/999/thoughts'); expect(thoughtsRes.status).toBe(200); expect(thoughtsRes.body).toEqual([]); await request(app).patch('/plans/999').send({ status: 'proposed' }).expect(404); await request(app).patch('/plans/999/changelog').send({ change: 'test' }).expect(404); }); test('Empty DB returns empty arrays', async () => { const plansRes = await request(app).get('/plans'); expect(plansRes.body).toEqual([]); const thoughtsRes = await request(app).get('/thoughts'); expect(thoughtsRes.body).toEqual([]); const contextRes = await request(app).get('/context'); expect(contextRes.body.incompletePlans).toEqual([]); expect(contextRes.body.last10Thoughts).toEqual([]); }); });

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/suttonwilliamd/tpc-server'

If you have feedback or need assistance with the MCP directory API, please join our Discord server