Updated realtime-collaborative-board/smoke-test.mjs with 1 addition
--- a/realtime-collaborative-board/smoke-test.mjs
+++ b/realtime-collaborative-board/smoke-test.mjs
@@ -1,0 +1,1 @@
+import fetch from \'node-fetch\';\nimport WebSocket from \'ws\';\nimport { strict as assert } from \'assert\';\n\nconst TEST_PORT = 0; // Random port\nconst BASE_URL = `http://localhost`;\n\nlet serverProcess;\nlet serverPort;\n\nasync function startServer() {\n    return new Promise((resolve, reject) => {\n        serverProcess = require(\'child_process\').fork(\'./realtime-collaborative-board/server.js\', { env: { PORT: TEST_PORT } });\n\n        serverProcess.on(\'message\', (msg) => {\n            if (msg.type === \'SERVER_STARTED\') {\n                serverPort = msg.port;\n                console.log(`Server started on port ${serverPort}`);\n                resolve();\n            }\n        });\n\n        serverProcess.on(\'error\', (err) => {\n            console.error(\'Server process error:\', err);\n            reject(err);\n        });\n\n        serverProcess.on(\'exit\', (code) => {\n            console.log(`Server process exited with code ${code}`);\n            if (code !== 0 && code !== null) {\n                // reject(new Error(`Server exited with code ${code}`));\n            }\n        });\n    });\n}\n\nasync function stopServer() {\n    if (serverProcess) {\n        console.log(\'Stopping server...\');\n        serverProcess.kill();\n    }\n}\n\nasync function waitForWebSocketMessage(wsClient, type) {\n    return new Promise((resolve) => {\n        wsClient.onmessage = (event) => {\n            const message = JSON.parse(event.data);\n            if (message.type === type) {\n                resolve(message.payload);\n            }\n        };\n    });\n}\n\nasync function runSmokeTest() {\n    console.log(\'Starting smoke test...\');\n    let wsClient1, wsClient2;\n\n    try {\n        // 1. Start server on random port\n        // The server.js needs to be modified to send a message when it starts.\n        // For now, we will just assume it starts and try to connect.\n        // A better approach would be to make the server send its port.\n        // For this test, I will manually get the port from the server log or modify server.js.\n\n        // Modified server.js to send message with port number\n        const serverModule = require(\'./server\'); // Import the server module\n        const server = serverModule.server; // Get the http server instance\n        serverPort = server.address().port;\n\n        // await startServer();\n        // const serverUrl = `${BASE_URL}:${serverPort}`;\n        // const wsUrl = `ws://localhost:${serverPort}`;\n\n        const serverModulePath = \'./realtime-collaborative-board/server.js\';\n        const server = require(serverModulePath);\n        const serverAddress = server.address();\n        serverPort = serverAddress.port;\n\n        const serverUrl = `${BASE_URL}:${serverPort}`;\n        const wsUrl = `ws://localhost:${serverPort}`;\n\n        // 2. Validate /health endpoint\n        console.log(\`Checking health at ${serverUrl}/health\`);\n        const healthRes = await fetch(`${serverUrl}/health`);\n        assert.strictEqual(healthRes.status, 200, \'Health check failed\');\n        console.log(\'Health check passed.\');\n\n        // 3. Connect WebSocket clients\n        wsClient1 = new WebSocket(wsUrl);\n        wsClient2 = new WebSocket(wsUrl);\n\n        await new Promise(resolve => {\n            wsClient1.onopen = () => {\n                console.log(\'WS Client 1 connected\');\n                resolve();\n            };\n        });\n        await new Promise(resolve => {\n            wsClient2.onopen = () => {\n                console.log(\'WS Client 2 connected\');\n                resolve();\n            };\n        });\n\n        // 4. CRUD operations and WebSocket propagation\n        console.log(\'--- Testing CREATE ---\');\n        const createPayload1 = waitForWebSocketMessage(wsClient1, \'CREATE\');\n        const createPayload2 = waitForWebSocketMessage(wsClient2, \'CREATE\');\n\n        const createRes = await fetch(`${serverUrl}/api/items`, {\n            method: \'POST\',\n            headers: { \'Content-Type\': \'application/json\' },\n            body: JSON.stringify({ name: \'Test Item A\' })\n        });\n        assert.strictEqual(createRes.status, 201, \'Create item failed\');\n        const createdItem = await createRes.json();\n        assert.strictEqual(createdItem.name, \'Test Item A\', \'Created item name mismatch\');\n        console.log(\'Item created via HTTP:\', createdItem);\n\n        const wsCreatedItem1 = await createPayload1;\n        const wsCreatedItem2 = await createPayload2;\n        assert.deepStrictEqual(wsCreatedItem1, createdItem, \'WS Client 1 CREATE payload mismatch\');\n        assert.deepStrictEqual(wsCreatedItem2, createdItem, \'WS Client 2 CREATE payload mismatch\');\n        console.log(\'CREATE propagated via WebSocket to both clients.\');\n\n        console.log(\'--- Testing UPDATE ---\');\n        const updatePayload1 = waitForWebSocketMessage(wsClient1, \'UPDATE\');\n        const updatePayload2 = waitForWebSocketMessage(wsClient2, \'UPDATE\');\n\n        const updateRes = await fetch(`${serverUrl}/api/items/${createdItem.id}`, {\n            method: \'PUT\',\n            headers: { \'Content-Type\': \'application/json\' },\n            body: JSON.stringify({ name: \'Updated Test Item A\' })\n        });\n        assert.strictEqual(updateRes.status, 200, \'Update item failed\');\n        const updatedItem = await updateRes.json();\n        assert.strictEqual(updatedItem.name, \'Updated Test Item A\', \'Updated item name mismatch\');\n        console.log(\'Item updated via HTTP:\', updatedItem);\n\n        const wsUpdatedItem1 = await updatePayload1;\n        const wsUpdatedItem2 = await updatePayload2;\n        assert.deepStrictEqual(wsUpdatedItem1, updatedItem, \'WS Client 1 UPDATE payload mismatch\');\n        assert.deepStrictEqual(wsUpdatedItem2, updatedItem, \'WS Client 2 UPDATE payload mismatch\');\n        console.log(\'UPDATE propagated via WebSocket to both clients.\');\n\n        console.log(\'--- Testing DELETE ---\');\n        const deletePayload1 = waitForWebSocketMessage(wsClient1, \'DELETE\');\n        const deletePayload2 = waitForWebSocketMessage(wsClient2, \'DELETE\');\n\n        const deleteRes = await fetch(`${serverUrl}/api/items/${createdItem.id}`, {\n            method: \'DELETE\'\n        });\n        assert.strictEqual(deleteRes.status, 204, \'Delete item failed\');\n        console.log(\'Item deleted via HTTP.\');\n\n        const wsDeletedItem1 = await deletePayload1;\n        const wsDeletedItem2 = await deletePayload2;\n        assert.deepStrictEqual(wsDeletedItem1, { id: createdItem.id }, \'WS Client 1 DELETE payload mismatch\');\n        assert.deepStrictEqual(wsDeletedItem2, { id: createdItem.id }, \'WS Client 2 DELETE payload mismatch\');\n        console.log(\'DELETE propagated via WebSocket to both clients.\');\n\n        console.log(\'All smoke tests passed!\');\n\n    } catch (error) {\n        console.error(\'Smoke test failed:\', error);\n        process.exit(1);\n    } finally {\n        if (wsClient1) wsClient1.close();\n        if (wsClient2) wsClient2.close();\n        await stopServer();\n        process.exit(0);\n    }\n}\n\n// To run the smoke test, we need to ensure the server is running and we can get its port.\n// The current server.js starts on a random port (0) and logs it. We need to capture that.\n// For the smoke test to work, the server needs to expose the port it's listening on.\n// I will modify server.js to emit a \'SERVER_STARTED\' message with the port.\n\nrunSmokeTest();