test(vscode): extract SSE payload unwrapper and add parsing tests

This commit is contained in:
Igor Šćekić
2026-02-20 12:50:21 +01:00
parent d9f7d602ee
commit 6bf7d99d5a
3 changed files with 83 additions and 3 deletions
@@ -1,5 +1,6 @@
import EventSource from "eventsource"
import type { ServerConfig, SSEEvent } from "./types"
import { unwrapSSEPayload } from "./sse-utils"
// Type definitions for handlers
export type SSEEventHandler = (event: SSEEvent) => void
@@ -67,9 +68,8 @@ export class SSEClient {
console.log("[Kilo New] SSE: 📨 Received message event:", messageEvent.data)
try {
const raw = JSON.parse(messageEvent.data)
// Global endpoint wraps events as { directory, payload: { type, properties } }
const event = (raw.payload ?? raw) as SSEEvent
if (!event.type) {
const event = unwrapSSEPayload(raw)
if (!event) {
console.warn("[Kilo New] SSE: ⚠️ Received event without type:", raw)
return
}
@@ -0,0 +1,16 @@
import type { SSEEvent } from "./types"
/**
* Unwrap an SSE message payload.
* The global /global/event endpoint wraps events as { directory, payload: SSEEvent }.
* Direct event endpoints return the SSEEvent directly.
* Returns null if the parsed data has no `type` field (malformed or unknown event).
*/
export function unwrapSSEPayload(raw: unknown): SSEEvent | null {
if (!raw || typeof raw !== "object") return null
const event = ((raw as { payload?: SSEEvent }).payload ?? raw) as SSEEvent
if (!event || typeof event !== "object" || !("type" in event)) {
return null
}
return event
}
@@ -0,0 +1,64 @@
import { describe, it, expect } from "bun:test"
import { unwrapSSEPayload } from "../../src/services/cli-backend/sse-utils"
describe("unwrapSSEPayload", () => {
it("unwraps global endpoint payload wrapper", () => {
const raw = {
directory: "/workspace",
payload: { type: "session.created", properties: { info: {} } },
}
const event = unwrapSSEPayload(raw)
expect(event?.type).toBe("session.created")
})
it("returns direct event when no payload wrapper", () => {
const raw = { type: "server.connected", properties: {} }
const event = unwrapSSEPayload(raw)
expect(event?.type).toBe("server.connected")
})
it("returns null when no type field in direct event", () => {
const raw = { properties: {} }
expect(unwrapSSEPayload(raw)).toBeNull()
})
it("returns null when payload wrapper exists but has no type", () => {
const raw = { directory: "/workspace", payload: { properties: {} } }
expect(unwrapSSEPayload(raw)).toBeNull()
})
it("returns null for null input", () => {
expect(unwrapSSEPayload(null)).toBeNull()
})
it("returns null for empty object", () => {
expect(unwrapSSEPayload({})).toBeNull()
})
it("returns null for non-object input", () => {
expect(unwrapSSEPayload("string")).toBeNull()
expect(unwrapSSEPayload(42)).toBeNull()
})
it("uses payload over root when both have type", () => {
const raw = {
type: "root-type",
payload: { type: "payload-type", properties: {} },
}
const event = unwrapSSEPayload(raw)
expect(event?.type).toBe("payload-type")
})
it("handles nested event types correctly", () => {
const raw = {
payload: {
type: "message.updated",
properties: {
info: { id: "m1", sessionID: "s1", role: "assistant", time: { created: 0 } },
},
},
}
const event = unwrapSSEPayload(raw)
expect(event?.type).toBe("message.updated")
})
})