Skip to content

Commit aba111c

Browse files
committed
test: add unit tests for stream.delta and stream.aborted hooks (t1306)
1 parent caff7d1 commit aba111c

File tree

1 file changed

+112
-0
lines changed

1 file changed

+112
-0
lines changed
Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
import { describe, expect, test } from "bun:test"
2+
import { SessionProcessor } from "../../src/session/processor"
3+
4+
describe("session.processor.StreamAbortedError", () => {
5+
test("stores reason and partial content", () => {
6+
const err = new SessionProcessor.StreamAbortedError("bad content", "partial text here")
7+
expect(err.reason).toBe("bad content")
8+
expect(err.partialContent).toBe("partial text here")
9+
expect(err.name).toBe("StreamAbortedError")
10+
expect(err.message).toBe("Stream aborted by plugin: bad content")
11+
})
12+
13+
test("is an instance of Error", () => {
14+
const err = new SessionProcessor.StreamAbortedError("test", "")
15+
expect(err).toBeInstanceOf(Error)
16+
expect(err).toBeInstanceOf(SessionProcessor.StreamAbortedError)
17+
})
18+
19+
test("has a stack trace", () => {
20+
const err = new SessionProcessor.StreamAbortedError("reason", "content")
21+
expect(err.stack).toBeDefined()
22+
expect(err.stack).toContain("StreamAbortedError")
23+
})
24+
25+
test("handles empty reason and content", () => {
26+
const err = new SessionProcessor.StreamAbortedError("", "")
27+
expect(err.reason).toBe("")
28+
expect(err.partialContent).toBe("")
29+
expect(err.message).toBe("Stream aborted by plugin: ")
30+
})
31+
})
32+
33+
describe("stream hook type contracts", () => {
34+
test("stream.delta input shape matches expected fields", () => {
35+
// Verify the hook input type has the required fields
36+
const input = {
37+
sessionID: "sess-1",
38+
messageID: "msg-1",
39+
type: "text-delta" as const,
40+
delta: "hello",
41+
accumulated: "hello world",
42+
}
43+
expect(input.sessionID).toBe("sess-1")
44+
expect(input.type).toBe("text-delta")
45+
expect(input.delta).toBe("hello")
46+
expect(input.accumulated).toBe("hello world")
47+
})
48+
49+
test("stream.delta supports all three delta types", () => {
50+
const types = ["text-delta", "reasoning-delta", "tool-input-delta"] as const
51+
for (const t of types) {
52+
const input = {
53+
sessionID: "s",
54+
messageID: "m",
55+
type: t,
56+
delta: "d",
57+
accumulated: "a",
58+
}
59+
expect(input.type).toBe(t)
60+
}
61+
})
62+
63+
test("stream.delta output defaults to no-abort", () => {
64+
const output = { abort: false, reason: "" }
65+
expect(output.abort).toBe(false)
66+
expect(output.reason).toBe("")
67+
})
68+
69+
test("stream.aborted input shape matches expected fields", () => {
70+
const input = {
71+
sessionID: "sess-1",
72+
messageID: "msg-1",
73+
reason: "content policy violation",
74+
partialContent: "some partial output",
75+
}
76+
expect(input.reason).toBe("content policy violation")
77+
expect(input.partialContent).toBe("some partial output")
78+
})
79+
80+
test("stream.aborted output defaults to no-retry", () => {
81+
const output = { retry: false, injectMessage: "" }
82+
expect(output.retry).toBe(false)
83+
expect(output.injectMessage).toBe("")
84+
})
85+
86+
test("stream.aborted output supports retry with message injection", () => {
87+
const output = {
88+
retry: true,
89+
injectMessage: "Please avoid SQL statements in your response.",
90+
}
91+
expect(output.retry).toBe(true)
92+
expect(output.injectMessage).toContain("SQL")
93+
})
94+
})
95+
96+
describe("STREAM_ABORT_MAX_RETRIES constant", () => {
97+
test("StreamAbortedError can be caught and inspected in a retry loop", () => {
98+
const maxRetries = 3
99+
let retries = 0
100+
const errors: SessionProcessor.StreamAbortedError[] = []
101+
102+
while (retries < maxRetries) {
103+
const err = new SessionProcessor.StreamAbortedError(`attempt ${retries + 1}`, `content-${retries}`)
104+
errors.push(err)
105+
retries++
106+
}
107+
108+
expect(errors).toHaveLength(3)
109+
expect(errors[0].reason).toBe("attempt 1")
110+
expect(errors[2].partialContent).toBe("content-2")
111+
})
112+
})

0 commit comments

Comments
 (0)