mirror of
https://github.com/Kilo-Org/kilocode.git
synced 2026-09-18 17:49:59 +08:00
test(vscode): extract SSE payload unwrapper and add parsing tests
This commit is contained in:
@@ -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")
|
||||
})
|
||||
})
|
||||
Reference in New Issue
Block a user