UNPKG

@boundless-oss/atlas

Version:

Atlas - MCP Server for comprehensive startup project management

178 lines (153 loc) 6.25 kB
import { describe, it, expect, beforeAll, afterAll, beforeEach, vi } from 'vitest'; import express, { Application } from 'express'; import request from 'supertest'; import { setupAgileAPI } from '../../api/agile.js'; import { SQLiteManager } from '../../../storage/sqlite-manager.js'; import { randomUUID } from 'crypto'; import path from 'path'; import fs from 'fs/promises'; // Mock the ensureDatabaseReady function before imports let testDb: SQLiteManager; vi.mock('../../../storage/sqlite-manager.js', async () => { const actual = await vi.importActual('../../../storage/sqlite-manager.js') as any; return { ...actual, ensureDatabaseReady: () => { if (!testDb) { throw new Error('Test database not initialized'); } return Promise.resolve(testDb); } }; }); describe('Agile API Integration Tests', () => { let app: Application; const testDbPath = '.atlas/test-agile-integration.db'; beforeAll(async () => { // Ensure test directory exists await fs.mkdir(path.dirname(testDbPath), { recursive: true }); // Initialize test database testDb = new SQLiteManager(testDbPath); await testDb.initialize(); // Setup Express app app = express(); app.use(express.json()); setupAgileAPI(app, null); }); afterAll(async () => { // Clean up test database await testDb.close(); try { await fs.unlink(testDbPath); } catch (e) { // Ignore if file doesn't exist } }); beforeEach(async () => { // Clear data in correct order to respect foreign keys await testDb.run('DELETE FROM agile_stories'); await testDb.run('DELETE FROM agile_sprints'); await testDb.run('DELETE FROM projects'); // Create default project for tests await testDb.run(` INSERT INTO projects (id, name, description, config) VALUES ('default', 'Default Test Project', 'Project for integration tests', '{}') `); }); describe('Sprint Board 404 Regression Test', () => { it('should find individual sprints that appear in the list endpoint', async () => { const sprintId = 'sprint-' + randomUUID(); const sprintName = 'Test Sprint for 404 Bug'; // Insert a test sprint directly into the database const insertResult = await testDb.run(` INSERT INTO agile_sprints ( id, project_id, name, goal, status, start_date, end_date, duration, team, story_points_planned, story_points_completed, stories_total, stories_completed, velocity, created_at, updated_at ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) `, [ sprintId, 'default', sprintName, 'Test goal', 'active', Date.now(), Date.now() + 14 * 24 * 60 * 60 * 1000, 14, '["Developer 1", "Developer 2"]', 20, 10, 5, 2, 10, Date.now(), Date.now() ]); expect(insertResult.success).toBe(true); // Step 1: Verify the sprint appears in the list endpoint const listResponse = await request(app).get('/api/agile/sprints'); expect(listResponse.status).toBe(200); expect(listResponse.body.success).toBe(true); expect(listResponse.body.data.sprints).toHaveLength(1); const sprintInList = listResponse.body.data.sprints[0]; expect(sprintInList.id).toBe(sprintId); expect(sprintInList.name).toBe(sprintName); expect(sprintInList.status).toBe('active'); // Step 2: Verify the individual sprint endpoint also finds it // This is the regression test - it should NOT return 404 const individualResponse = await request(app).get(`/api/agile/sprints/${sprintId}`); expect(individualResponse.status).toBe(200); expect(individualResponse.body.success).toBe(true); expect(individualResponse.body.data).toBeDefined(); expect(individualResponse.body.data.id).toBe(sprintId); expect(individualResponse.body.data.name).toBe(sprintName); expect(individualResponse.body.data.status).toBe('active'); // Verify field mapping is correct expect(individualResponse.body.data.startDate).toBeDefined(); expect(individualResponse.body.data.endDate).toBeDefined(); expect(individualResponse.body.data.team).toEqual(['Developer 1', 'Developer 2']); expect(individualResponse.body.data.storyPointsPlanned).toBe(20); // Note: storyPointsCompleted is recalculated based on actual stories // Since we haven't added any stories, it should be 0 expect(individualResponse.body.data.storyPointsCompleted).toBe(0); }); it('should handle complex sprint IDs with hyphens and UUIDs', async () => { // Test with a complex ID format similar to what was failing const complexSprintId = 'sprint-' + randomUUID(); await testDb.run(` INSERT INTO agile_sprints ( id, project_id, name, goal, status, start_date, end_date, duration, team, created_at, updated_at ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) `, [ complexSprintId, 'default', 'Sprint with Complex ID', 'Test complex IDs', 'active', Date.now(), Date.now() + 14 * 24 * 60 * 60 * 1000, 14, '[]', Date.now(), Date.now() ]); // Should successfully retrieve the sprint with complex ID const response = await request(app).get(`/api/agile/sprints/${complexSprintId}`); expect(response.status).toBe(200); expect(response.body.success).toBe(true); expect(response.body.data.id).toBe(complexSprintId); }); it('should properly return 404 for truly non-existent sprints', async () => { // Ensure we're testing a sprint that really doesn't exist const response = await request(app).get('/api/agile/sprints/definitely-does-not-exist'); expect(response.status).toBe(404); expect(response.body.success).toBe(false); expect(response.body.error).toBe('Sprint not found'); expect(response.body.sprintId).toBe('definitely-does-not-exist'); }); }); });