Contents of smoke-test.mjs:
1: import fetch from 'node-fetch';
2: import WebSocket from 'ws';
3: import assert from 'assert';
4: 
5: const API_URL = 'http://localhost:3000';
6: const WS_URL = 'ws://localhost:3000';
7: 
8: const GOOGLE_API_KEY = process.env.GOOGLE_API_KEY;
9: const GEMINI_MODEL = process.env.GEMINI_MODEL || 'gemini-1.5-flash';
10: 
11: async function healthCheck() {
12:     console.log('Running health check...');
13:     const res = await fetch(`${API_URL}/api/incidents`);
14:     assert.strictEqual(res.status, 200, 'Health check failed: Expected 200 status');
15:     console.log('Health check passed.');
16: }
17: 
18: async function testIncidentsCrud() {
19:     console.log('Running incidents CRUD test...');
20:     // Create
21:     const newIncident = { title: 'Test Incident', description: 'This is a test incident.' };
22:     const createRes = await fetch(`${API_URL}/api/incidents`, {
23:         method: 'POST',
24:         headers: { 'Content-Type': 'application/json' },
25:         body: JSON.stringify(newIncident)
26:     });
27:     const createdIncident = await createRes.json();
28:     assert.strictEqual(createRes.status, 201, 'Create incident failed');
29:     assert.strictEqual(createdIncident.title, newIncident.title, 'Created incident title mismatch');
30:     console.log('Incident created.');
31: 
32:     // Read
33:     const getRes = await fetch(`${API_URL}/api/incidents`);
34:     const incidents = await getRes.json();
35:     assert.strictEqual(getRes.status, 200, 'Get incidents failed');
36:     assert.ok(incidents.some(inc => inc.id === createdIncident.id), 'Created incident not found in list');
37:     console.log('Incidents read.');
38: 
39:     // Update
40:     const updatedTitle = 'Updated Test Incident';
41:     const updateRes = await fetch(`${API_URL}/api/incidents/${createdIncident.id}`, {
42:         method: 'PUT',
43:         headers: { 'Content-Type': 'application/json' },
44:         body: JSON.stringify({ title: updatedTitle, status: 'resolved' })
45:     });
46:     const updatedIncident = await updateRes.json();
47:     assert.strictEqual(updateRes.status, 200, 'Update incident failed');
48:     assert.strictEqual(updatedIncident.title, updatedTitle, 'Updated incident title mismatch');
49:     assert.strictEqual(updatedIncident.status, 'resolved', 'Updated incident status mismatch');
50:     console.log('Incident updated.');
51: 
52:     // Delete
53:     const deleteRes = await fetch(`${API_URL}/api/incidents/${createdIncident.id}`, {
54:         method: 'DELETE'
55:     });
56:     assert.strictEqual(deleteRes.status, 204, 'Delete incident failed');
57:     const getAfterDeleteRes = await fetch(`${API_URL}/api/incidents`);
58:     const incidentsAfterDelete = await getAfterDeleteRes.json();
59:     assert.ok(!incidentsAfterDelete.some(inc => inc.id === createdIncident.id), 'Deleted incident found in list');
60:     console.log('Incident deleted.');
61:     console.log('Incidents CRUD test passed.');
62: }
63: 
64: async function testJobs() {
65:     console.log('Running jobs test...');
66:     const newJob = { title: 'Test Job', description: 'This is a test job.' };
67:     const createRes = await fetch(`${API_URL}/api/jobs`, {
68:         method: 'POST',
69:         headers: { 'Content-Type': 'application/json' },
70:         body: JSON.stringify(newJob)
71:     });
72:     const createdJob = await createRes.json();
73:     assert.strictEqual(createRes.status, 201, 'Create job failed');
74:     assert.strictEqual(createdJob.status, 'pending', 'Job initial status mismatch');
75:     console.log('Job created.');
76: 
77:     // Wait for job to complete (simulated 5 seconds)
78:     console.log('Waiting for job to complete...');
79:     await new Promise(resolve => setTimeout(resolve, 6000)); // 5s job + buffer
80: 
81:     const getRes = await fetch(`${API_URL}/api/jobs`);
82:     const jobs = await getRes.json();
83:     const completedJob = jobs.find(job => job.id === createdJob.id);
84:     assert.ok(completedJob, 'Completed job not found');
85:     assert.strictEqual(completedJob.status, 'completed', 'Job did not complete');
86:     console.log('Job completed test passed.');
87: }
88: 
89: async function testWebSocket() {
90:     console.log('Running WebSocket test...');
91:     return new Promise((resolve, reject) => {
92:         const ws = new WebSocket(WS_URL);
93:         let incidentCreated = false;
94:         let jobCreated = false;
95: 
96:         ws.onopen = async () => {
97:             console.log('WebSocket connected.');
98:             // Create an incident to trigger a broadcast
99:             await fetch(`${API_URL}/api/incidents`, {
100:                 method: 'POST',
101:                 headers: { 'Content-Type': 'application/json' },
102:                 body: JSON.stringify({ title: 'WS Incident', description: 'Test WS' })
103:             });
104:             // Create a job to trigger a broadcast
105:             await fetch(`${API_URL}/api/jobs`, {
106:                 method: 'POST',
107:                 headers: { 'Content-Type': 'application/json' },
108:                 body: JSON.stringify({ title: 'WS Job', description: 'Test WS Job' })
109:             });
110:         };
111: 
112:         ws.onmessage = (event) => {
113:             const data = JSON.parse(event.data);
114:             if (data.type === 'incidentCreated' && data.incident.title === 'WS Incident') {
115:                 incidentCreated = true;
116:                 console.log('WebSocket incidentCreated received.');
117:             }
118:             if (data.type === 'jobCreated' && data.job.title === 'WS Job') {
119:                 jobCreated = true;
120:                 console.log('WebSocket jobCreated received.');
121:             }
122: 
123:             if (incidentCreated && jobCreated) {
124:                 console.log('WebSocket test passed.');
125:                 ws.close();
126:                 resolve();
127:             }
128:         };
129: 
130:         ws.onerror = (error) => {
131:             console.error('WebSocket error:', error);
132:             reject(error);
133:         };
134: 
135:         ws.onclose = () => {
136:             if (!incidentCreated || !jobCreated) {
137:                 reject(new Error('WebSocket test failed: Did not receive all expected messages.'));
138:             }
139:         };
140:     });
141: }
142: 
143: async function testGeminiSummarize() {
144:     console.log('Running Gemini summarization test...');
145:     if (!GOOGLE_API_KEY) {
146:         console.log('Skipping Gemini summarization test: GOOGLE_API_KEY not set.');
147:         return;
148:     }
149: 
150:     const textToSummarize = 'The system experienced a critical outage due to a database connection failure. This impacted all user-facing services for approximately 30 minutes. Root cause analysis is ongoing.';
151:     const res = await fetch(`${API_URL}/api/assist/summarize`, {
152:         method: 'POST',
153:         headers: { 'Content-Type': 'application/json' },
154:         body: JSON.stringify({ text: textToSummarize })
155:     });
156:     const data = await res.json();
157: 
158:     assert.strictEqual(res.status, 200, `Gemini summarization failed: Expected 200, got ${res.status}`);
159:     assert.ok(data.summary, 'Summary field missing');
160:     assert.ok(typeof data.summary === 'string', 'Summary is not a string');
161:     assert.ok(data.summary.length > 10, 'Summary is too short');
162:     console.log('Gemini summarization test passed.');
163: }
164: 
165: import { spawn } from 'child_process';
166: 
167: async function runTests() {
168:     try {
169:         // Start the server in a child process (assuming `npm start` runs it)
170:         const serverProcess = spawn('npm', ['start']);
171: 
172:         serverProcess.stdout.on('data', (data) => {
173:             console.log(`Server: ${data}`);
174:         });
175: 
176:         serverProcess.stderr.on('data', (data) => {
177:             console.error(`Server Error: ${data}`);
178:         });
179: 
180:         // Give the server some time to start
181:         console.log('Waiting for server to start...');
182:         await new Promise(resolve => setTimeout(resolve, 5000));
183: 
184:         await healthCheck();
185:         await testIncidentsCrud();
186:         await testJobs();
187:         await testWebSocket();
188:         await testGeminiSummarize();
189: 
190:         console.log('All smoke tests passed!');
191:     } catch (error) {
192:         console.error('Smoke test failed:', error);
193:         process.exit(1);
194:     } finally {
195:         // Ensure the server process is killed
196:         if (serverProcess) {
197:             serverProcess.kill();
198:             console.log('Server process terminated.');
199:         }
200:     }
201: }
202: 
203: runTests();
204: 