// Test script: Exam events through Kafka → push-gateway → WebSocket (v2) // Verifies that the 3 exam events (ExamExtended, ExamForceSubmitted, // ExamQuestionReordered) traverse the new edu.notify.notification.sent topic // and reach the WebSocket client with correct event_type passthrough. // // Usage: node services/push-gateway/scripts/test-exam-events.mjs import WebSocket from "ws"; import { Kafka } from "kafkajs"; const WS_URL = "ws://localhost:8081/ws?token=dev-token"; const KAFKA_BROKER = "localhost:9092"; const TOPIC = "edu.notify.notification.sent"; const USER_ID = "dev-user"; const examEvents = [ { event_type: "ExamExtended", data: { examId: "exam-001", extendedMinutes: 15, reason: "special needs" }, }, { event_type: "ExamForceSubmitted", data: { examId: "exam-001", reason: "time_expired", submissionId: "sub-001" }, }, { event_type: "ExamQuestionReordered", data: { examId: "exam-001", questionIds: ["q-3", "q-1", "q-2"] }, }, ]; console.log("[exam-test] connecting WebSocket to", WS_URL); const ws = new WebSocket(WS_URL); const received = []; ws.on("open", async () => { console.log("[exam-test] WebSocket connected, publishing 3 exam events..."); const kafka = new Kafka({ brokers: [KAFKA_BROKER] }); const producer = kafka.producer(); await producer.connect(); for (const ev of examEvents) { const payload = { event_id: `evt-exam-${ev.event_type}-${Date.now()}`, user_id: USER_ID, event_type: ev.event_type, channel: "ws", title: `Exam event: ${ev.event_type}`, content: `Verification of ${ev.event_type} passthrough`, data: ev.data, broadcast: false, occurred_at: Date.now(), }; await producer.send({ topic: TOPIC, messages: [{ value: JSON.stringify(payload) }], }); console.log(`[exam-test] published ${ev.event_type}`); } await producer.disconnect(); // Wait up to 15 seconds for all 3 messages. const deadline = Date.now() + 15000; while (Date.now() < deadline && received.length < 3) { await new Promise((r) => setTimeout(r, 200)); } if (received.length < 3) { console.error(`[exam-test] FAIL: only ${received.length}/3 messages received`); process.exit(1); } const got = received.map((m) => m.event).sort(); const want = examEvents.map((e) => e.event_type).sort(); if (JSON.stringify(got) === JSON.stringify(want)) { console.log("[exam-test] PASS: all 3 exam events received with correct event_type"); console.log("[exam-test] events:", got.join(", ")); process.exit(0); } else { console.error("[exam-test] FAIL: event mismatch"); console.error(" got:", got); console.error(" want:", want); process.exit(1); } }); ws.on("message", (data) => { const msg = JSON.parse(data.toString()); console.log("[exam-test] received:", JSON.stringify(msg)); received.push(msg); }); ws.on("error", (err) => { console.error("[exam-test] WebSocket error:", err.message); process.exit(1); }); setTimeout(() => { console.error("[exam-test] FAIL: overall timeout 20s"); process.exit(1); }, 20000);