@oh-my-pi/pi-coding-agent 5.7.67 → 5.7.69

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (53) hide show
  1. package/CHANGELOG.md +9 -0
  2. package/README.md +6 -6
  3. package/package.json +8 -7
  4. package/src/migrations.ts +1 -34
  5. package/src/vendor/photon/index.js +4 -2
  6. package/src/vendor/photon/photon_rs_bg.wasm.b64.js +1 -0
  7. package/src/core/python-executor-display.test.ts +0 -42
  8. package/src/core/python-executor-lifecycle.test.ts +0 -99
  9. package/src/core/python-executor-mapping.test.ts +0 -41
  10. package/src/core/python-executor-per-call.test.ts +0 -49
  11. package/src/core/python-executor-session.test.ts +0 -103
  12. package/src/core/python-executor-streaming.test.ts +0 -77
  13. package/src/core/python-executor-timeout.test.ts +0 -35
  14. package/src/core/python-executor.lifecycle.test.ts +0 -139
  15. package/src/core/python-executor.result.test.ts +0 -49
  16. package/src/core/python-executor.test.ts +0 -180
  17. package/src/core/python-kernel-display.test.ts +0 -54
  18. package/src/core/python-kernel-env.test.ts +0 -138
  19. package/src/core/python-kernel-session.test.ts +0 -87
  20. package/src/core/python-kernel-ws.test.ts +0 -104
  21. package/src/core/python-kernel.lifecycle.test.ts +0 -249
  22. package/src/core/python-kernel.test.ts +0 -461
  23. package/src/core/python-modules.test.ts +0 -102
  24. package/src/core/python-prelude.test.ts +0 -140
  25. package/src/core/settings-manager-python.test.ts +0 -23
  26. package/src/core/streaming-output.test.ts +0 -26
  27. package/src/core/system-prompt.python.test.ts +0 -17
  28. package/src/core/tools/index.test.ts +0 -212
  29. package/src/core/tools/python-execution.test.ts +0 -68
  30. package/src/core/tools/python-fallback.test.ts +0 -72
  31. package/src/core/tools/python-renderer.test.ts +0 -36
  32. package/src/core/tools/python-tool-mode.test.ts +0 -43
  33. package/src/core/tools/python.test.ts +0 -121
  34. package/src/core/tools/schema-validation.test.ts +0 -530
  35. package/src/core/tools/web-scrapers/academic.test.ts +0 -239
  36. package/src/core/tools/web-scrapers/business.test.ts +0 -82
  37. package/src/core/tools/web-scrapers/dev-platforms.test.ts +0 -254
  38. package/src/core/tools/web-scrapers/documentation.test.ts +0 -85
  39. package/src/core/tools/web-scrapers/finance-media.test.ts +0 -144
  40. package/src/core/tools/web-scrapers/git-hosting.test.ts +0 -272
  41. package/src/core/tools/web-scrapers/media.test.ts +0 -138
  42. package/src/core/tools/web-scrapers/package-managers-2.test.ts +0 -199
  43. package/src/core/tools/web-scrapers/package-managers.test.ts +0 -171
  44. package/src/core/tools/web-scrapers/package-registries.test.ts +0 -259
  45. package/src/core/tools/web-scrapers/research.test.ts +0 -107
  46. package/src/core/tools/web-scrapers/security.test.ts +0 -103
  47. package/src/core/tools/web-scrapers/social-extended.test.ts +0 -192
  48. package/src/core/tools/web-scrapers/social.test.ts +0 -259
  49. package/src/core/tools/web-scrapers/stackexchange.test.ts +0 -120
  50. package/src/core/tools/web-scrapers/standards.test.ts +0 -122
  51. package/src/core/tools/web-scrapers/wikipedia.test.ts +0 -73
  52. package/src/core/tools/web-scrapers/youtube.test.ts +0 -198
  53. package/src/discovery/helpers.test.ts +0 -131
@@ -1,180 +0,0 @@
1
- import { afterEach, describe, expect, it, vi } from "bun:test";
2
- import { rmSync } from "node:fs";
3
- import {
4
- disposeAllKernelSessions,
5
- executePythonWithKernel,
6
- getPreludeDocs,
7
- type PythonKernelExecutor,
8
- resetPreludeDocsCache,
9
- warmPythonEnvironment,
10
- } from "./python-executor";
11
- import { type KernelExecuteOptions, type KernelExecuteResult, type PreludeHelper, PythonKernel } from "./python-kernel";
12
- import { DEFAULT_MAX_BYTES } from "./tools/truncate";
13
-
14
- class FakeKernel implements PythonKernelExecutor {
15
- private result: KernelExecuteResult;
16
- private onExecute: (options?: KernelExecuteOptions) => void;
17
-
18
- constructor(result: KernelExecuteResult, onExecute: (options?: KernelExecuteOptions) => void) {
19
- this.result = result;
20
- this.onExecute = onExecute;
21
- }
22
-
23
- async execute(_code: string, options?: KernelExecuteOptions): Promise<KernelExecuteResult> {
24
- this.onExecute(options);
25
- return this.result;
26
- }
27
- }
28
-
29
- describe("executePythonWithKernel", () => {
30
- it("captures text and display outputs", async () => {
31
- const kernel = new FakeKernel(
32
- { status: "ok", cancelled: false, timedOut: false, stdinRequested: false },
33
- (options) => {
34
- options?.onChunk?.("hello\n");
35
- options?.onDisplay?.({ type: "json", data: { foo: "bar" } });
36
- },
37
- );
38
-
39
- const result = await executePythonWithKernel(kernel, "print('hello')");
40
-
41
- expect(result.exitCode).toBe(0);
42
- expect(result.output).toContain("hello");
43
- expect(result.displayOutputs).toHaveLength(1);
44
- });
45
-
46
- it("marks stdin request as error", async () => {
47
- const kernel = new FakeKernel(
48
- { status: "ok", cancelled: false, timedOut: false, stdinRequested: true },
49
- () => {},
50
- );
51
-
52
- const result = await executePythonWithKernel(kernel, "input('prompt')");
53
-
54
- expect(result.exitCode).toBe(1);
55
- expect(result.stdinRequested).toBe(true);
56
- expect(result.output).toContain("Kernel requested stdin; interactive input is not supported.");
57
- });
58
-
59
- it("maps error status to exit code 1", async () => {
60
- const kernel = new FakeKernel(
61
- { status: "error", cancelled: false, timedOut: false, stdinRequested: false },
62
- (options) => {
63
- options?.onChunk?.("Traceback\n");
64
- },
65
- );
66
-
67
- const result = await executePythonWithKernel(kernel, "raise ValueError('nope')");
68
-
69
- expect(result.exitCode).toBe(1);
70
- expect(result.cancelled).toBe(false);
71
- expect(result.output).toContain("Traceback");
72
- });
73
-
74
- it("sanitizes streamed chunks", async () => {
75
- const kernel = new FakeKernel(
76
- { status: "ok", cancelled: false, timedOut: false, stdinRequested: false },
77
- (options) => {
78
- options?.onChunk?.("\u001b[31mred\r\n");
79
- },
80
- );
81
-
82
- const result = await executePythonWithKernel(kernel, "print('red')");
83
-
84
- expect(result.output).toBe("red\n");
85
- });
86
-
87
- it("returns cancelled result with timeout annotation", async () => {
88
- const kernel = new FakeKernel(
89
- { status: "ok", cancelled: true, timedOut: true, stdinRequested: false },
90
- (options) => {
91
- options?.onChunk?.("partial output\n");
92
- },
93
- );
94
-
95
- const result = await executePythonWithKernel(kernel, "while True: pass", { timeout: 4100 });
96
-
97
- expect(result.exitCode).toBeUndefined();
98
- expect(result.cancelled).toBe(true);
99
- expect(result.output).toContain("Command timed out after 4 seconds");
100
- });
101
-
102
- it("returns cancelled result without timeout annotation", async () => {
103
- const kernel = new FakeKernel(
104
- { status: "ok", cancelled: true, timedOut: false, stdinRequested: false },
105
- (options) => {
106
- options?.onChunk?.("cancelled output\n");
107
- },
108
- );
109
-
110
- const result = await executePythonWithKernel(kernel, "while True: pass");
111
-
112
- expect(result.exitCode).toBeUndefined();
113
- expect(result.cancelled).toBe(true);
114
- expect(result.output).toContain("cancelled output");
115
- expect(result.output).not.toContain("Command timed out");
116
- });
117
-
118
- it("truncates large output and stores full output file", async () => {
119
- const largeOutput = `${"x".repeat(DEFAULT_MAX_BYTES + 1024)}TAIL`;
120
- const kernel = new FakeKernel(
121
- { status: "ok", cancelled: false, timedOut: false, stdinRequested: false },
122
- (options) => {
123
- options?.onChunk?.(largeOutput);
124
- },
125
- );
126
-
127
- const result = await executePythonWithKernel(kernel, "print('big')");
128
-
129
- expect(result.truncated).toBe(true);
130
- expect(result.fullOutputPath).toBeDefined();
131
- expect(result.output).toContain("TAIL");
132
-
133
- const fullText = await Bun.file(result.fullOutputPath as string).text();
134
- expect(fullText).toBe(largeOutput);
135
-
136
- rmSync(result.fullOutputPath as string, { force: true });
137
- });
138
- });
139
-
140
- afterEach(async () => {
141
- await disposeAllKernelSessions();
142
- resetPreludeDocsCache();
143
- vi.restoreAllMocks();
144
- });
145
-
146
- describe("warmPythonEnvironment", () => {
147
- it("caches prelude docs on warmup", async () => {
148
- const previousSkip = process.env.OMP_PYTHON_SKIP_CHECK;
149
- process.env.OMP_PYTHON_SKIP_CHECK = "1";
150
- const docs: PreludeHelper[] = [
151
- {
152
- name: "read",
153
- signature: "(path)",
154
- docstring: "Read file contents.",
155
- category: "File I/O",
156
- },
157
- ];
158
- const kernel = {
159
- introspectPrelude: vi.fn().mockResolvedValue(docs),
160
- ping: vi.fn().mockResolvedValue(true),
161
- isAlive: () => true,
162
- shutdown: vi.fn().mockResolvedValue(undefined),
163
- };
164
- const startSpy = vi.spyOn(PythonKernel, "start").mockResolvedValue(kernel as unknown as PythonKernel);
165
-
166
- const result = await warmPythonEnvironment("/tmp/test", "session-1");
167
-
168
- expect(result.ok).toBe(true);
169
- expect(result.docs).toEqual(docs);
170
- expect(getPreludeDocs()).toEqual(docs);
171
- expect(kernel.introspectPrelude).toHaveBeenCalledTimes(1);
172
-
173
- startSpy.mockRestore();
174
- if (previousSkip === undefined) {
175
- delete process.env.OMP_PYTHON_SKIP_CHECK;
176
- } else {
177
- process.env.OMP_PYTHON_SKIP_CHECK = previousSkip;
178
- }
179
- });
180
- });
@@ -1,54 +0,0 @@
1
- import { describe, expect, it } from "bun:test";
2
- import { type KernelDisplayOutput, PythonKernel } from "./python-kernel";
3
-
4
- const renderDisplay = (
5
- PythonKernel as unknown as {
6
- prototype: {
7
- renderDisplay: (content: Record<string, unknown>) => {
8
- text: string;
9
- outputs: KernelDisplayOutput[];
10
- };
11
- };
12
- }
13
- ).prototype.renderDisplay;
14
-
15
- describe("PythonKernel display rendering", () => {
16
- it("normalizes text/plain output and returns no display outputs", () => {
17
- const { text, outputs } = renderDisplay.call({} as PythonKernel, {
18
- data: { "text/plain": "hello" },
19
- });
20
-
21
- expect(text).toBe("hello\n");
22
- expect(outputs).toHaveLength(0);
23
- });
24
-
25
- it("collects image and json display outputs without text", () => {
26
- const { text, outputs } = renderDisplay.call({} as PythonKernel, {
27
- data: { "image/png": "abc", "application/json": { foo: "bar" } },
28
- });
29
-
30
- expect(text).toBe("");
31
- expect(outputs).toEqual([
32
- { type: "image", data: "abc", mimeType: "image/png" },
33
- { type: "json", data: { foo: "bar" } },
34
- ]);
35
- });
36
-
37
- it("converts text/html to markdown", () => {
38
- const { text, outputs } = renderDisplay.call({} as PythonKernel, {
39
- data: { "text/html": "<p><strong>Hello</strong></p>" },
40
- });
41
-
42
- expect(outputs).toHaveLength(0);
43
- expect(text).toBe("**Hello**\n");
44
- });
45
-
46
- it("combines text/plain with json output", () => {
47
- const { text, outputs } = renderDisplay.call({} as PythonKernel, {
48
- data: { "text/plain": "value", "application/json": { ok: true } },
49
- });
50
-
51
- expect(text).toBe("value\n");
52
- expect(outputs).toEqual([{ type: "json", data: { ok: true } }]);
53
- });
54
- });
@@ -1,138 +0,0 @@
1
- import { afterEach, beforeEach, describe, expect, it, vi } from "bun:test";
2
- import * as shell from "../utils/shell";
3
- import * as shellSnapshot from "../utils/shell-snapshot";
4
- import { PythonKernel } from "./python-kernel";
5
- import { PYTHON_PRELUDE } from "./python-prelude";
6
-
7
- class FakeWebSocket {
8
- static OPEN = 1;
9
- static CLOSED = 3;
10
- readyState = FakeWebSocket.OPEN;
11
- binaryType = "arraybuffer";
12
- url: string;
13
- onopen?: () => void;
14
- onerror?: (event: unknown) => void;
15
- onclose?: () => void;
16
- onmessage?: (event: { data: ArrayBuffer }) => void;
17
-
18
- constructor(url: string) {
19
- this.url = url;
20
- queueMicrotask(() => {
21
- this.onopen?.();
22
- });
23
- }
24
-
25
- send(_data: ArrayBuffer) {}
26
-
27
- close() {
28
- this.readyState = FakeWebSocket.CLOSED;
29
- this.onclose?.();
30
- }
31
- }
32
-
33
- describe("PythonKernel.start (local gateway)", () => {
34
- const originalEnv = { ...process.env };
35
- const originalFetch = globalThis.fetch;
36
- const originalWebSocket = globalThis.WebSocket;
37
-
38
- beforeEach(() => {
39
- process.env.BUN_ENV = "test";
40
- delete process.env.OMP_PYTHON_GATEWAY_URL;
41
- delete process.env.OMP_PYTHON_GATEWAY_TOKEN;
42
- globalThis.WebSocket = FakeWebSocket as unknown as typeof WebSocket;
43
- });
44
-
45
- afterEach(() => {
46
- for (const key of Object.keys(process.env)) {
47
- if (!(key in originalEnv)) {
48
- delete process.env[key];
49
- }
50
- }
51
- for (const [key, value] of Object.entries(originalEnv)) {
52
- process.env[key] = value;
53
- }
54
- globalThis.fetch = originalFetch;
55
- globalThis.WebSocket = originalWebSocket;
56
- vi.restoreAllMocks();
57
- });
58
-
59
- it("filters environment variables before spawning gateway", async () => {
60
- const fetchSpy = vi.fn(async (input: string | URL, init?: RequestInit) => {
61
- const url = typeof input === "string" ? input : input.toString();
62
- if (url.endsWith("/api/kernelspecs")) {
63
- return new Response(JSON.stringify({}), { status: 200 });
64
- }
65
- if (url.endsWith("/api/kernels") && init?.method === "POST") {
66
- return new Response(JSON.stringify({ id: "kernel-1" }), { status: 201 });
67
- }
68
- return new Response("", { status: 200 });
69
- });
70
- globalThis.fetch = fetchSpy as unknown as typeof fetch;
71
-
72
- const shellSpy = vi.spyOn(shell, "getShellConfig").mockResolvedValue({
73
- shell: "/bin/bash",
74
- args: ["-lc"],
75
- env: {
76
- PATH: "/bin",
77
- HOME: "/home/test",
78
- OPENAI_API_KEY: "secret",
79
- UNSAFE_TOKEN: "nope",
80
- OMP_CUSTOM: "1",
81
- LC_ALL: "en_US.UTF-8",
82
- },
83
- prefix: undefined,
84
- });
85
- const snapshotSpy = vi.spyOn(shellSnapshot, "getOrCreateSnapshot").mockResolvedValue(null);
86
- const whichSpy = vi.spyOn(Bun, "which").mockReturnValue("/usr/bin/python");
87
-
88
- let spawnEnv: Record<string, string | undefined> | undefined;
89
- let spawnArgs: string[] | undefined;
90
- const spawnSpy = vi.spyOn(Bun, "spawn").mockImplementation(((...args: unknown[]) => {
91
- const [cmd, options] = args as [string[] | { cmd: string[] }, { env?: Record<string, string | undefined> }?];
92
- spawnArgs = Array.isArray(cmd) ? cmd : cmd.cmd;
93
- spawnEnv = options?.env;
94
- return { pid: 1234, exited: Promise.resolve(0) } as unknown as Bun.Subprocess;
95
- }) as unknown as typeof Bun.spawn);
96
-
97
- const executeSpy = vi
98
- .spyOn(PythonKernel.prototype, "execute")
99
- .mockResolvedValue({ status: "ok", cancelled: false, timedOut: false, stdinRequested: false });
100
-
101
- const kernel = await PythonKernel.start({ cwd: "/tmp/project", env: { CUSTOM_VAR: "ok" } });
102
-
103
- const createCall = fetchSpy.mock.calls.find(([input, init]) => {
104
- const url = typeof input === "string" ? input : input.toString();
105
- return url.endsWith("/api/kernels") && init?.method === "POST";
106
- });
107
- expect(createCall).toBeDefined();
108
- if (createCall) {
109
- expect(JSON.parse(String(createCall[1]?.body ?? "{}"))).toEqual({ name: "python3" });
110
- }
111
-
112
- expect(spawnArgs).toContain("kernel_gateway");
113
- expect(spawnEnv?.PATH).toBe("/bin");
114
- expect(spawnEnv?.HOME).toBe("/home/test");
115
- expect(spawnEnv?.OMP_CUSTOM).toBe("1");
116
- expect(spawnEnv?.LC_ALL).toBe("en_US.UTF-8");
117
- expect(spawnEnv?.CUSTOM_VAR).toBe("ok");
118
- expect(spawnEnv?.OPENAI_API_KEY).toBeUndefined();
119
- expect(spawnEnv?.UNSAFE_TOKEN).toBeUndefined();
120
- expect(spawnEnv?.PYTHONPATH).toBe("/tmp/project");
121
-
122
- expect(executeSpy).toHaveBeenCalledWith(
123
- PYTHON_PRELUDE,
124
- expect.objectContaining({
125
- silent: true,
126
- storeHistory: false,
127
- }),
128
- );
129
-
130
- await kernel.shutdown();
131
-
132
- shellSpy.mockRestore();
133
- snapshotSpy.mockRestore();
134
- whichSpy.mockRestore();
135
- spawnSpy.mockRestore();
136
- executeSpy.mockRestore();
137
- });
138
- });
@@ -1,87 +0,0 @@
1
- import { afterEach, beforeEach, describe, expect, it } from "bun:test";
2
- import { disposeAllKernelSessions, executePython } from "./python-executor";
3
- import type { KernelExecuteOptions, KernelExecuteResult } from "./python-kernel";
4
- import { PythonKernel } from "./python-kernel";
5
-
6
- class FakeKernel {
7
- executeCalls = 0;
8
- shutdownCalls = 0;
9
- alive = true;
10
- readonly id: string;
11
-
12
- constructor(id: string) {
13
- this.id = id;
14
- }
15
-
16
- async execute(_code: string, options?: KernelExecuteOptions): Promise<KernelExecuteResult> {
17
- this.executeCalls += 1;
18
- options?.onChunk?.("ok\n");
19
- return { status: "ok", cancelled: false, timedOut: false, stdinRequested: false };
20
- }
21
-
22
- async shutdown(): Promise<void> {
23
- this.shutdownCalls += 1;
24
- this.alive = false;
25
- }
26
-
27
- isAlive(): boolean {
28
- return this.alive;
29
- }
30
-
31
- async ping(): Promise<boolean> {
32
- return this.alive;
33
- }
34
- }
35
-
36
- describe("executePython kernel reuse", () => {
37
- const originalStart = PythonKernel.start;
38
- let startCalls = 0;
39
- let kernels: FakeKernel[] = [];
40
-
41
- beforeEach(() => {
42
- process.env.OMP_PYTHON_SKIP_CHECK = "1";
43
- startCalls = 0;
44
- kernels = [];
45
- PythonKernel.start = (async () => {
46
- startCalls += 1;
47
- const kernel = new FakeKernel(`kernel-${startCalls}`);
48
- kernels.push(kernel);
49
- return kernel as unknown as PythonKernel;
50
- }) as typeof PythonKernel.start;
51
- });
52
-
53
- afterEach(async () => {
54
- PythonKernel.start = originalStart;
55
- await disposeAllKernelSessions();
56
- });
57
-
58
- it("reuses kernels for session mode", async () => {
59
- await executePython("print('one')", { cwd: "/tmp", sessionId: "session-a", kernelMode: "session" });
60
- await executePython("print('two')", { cwd: "/tmp", sessionId: "session-a", kernelMode: "session" });
61
-
62
- expect(startCalls).toBe(1);
63
- expect(kernels[0]?.executeCalls).toBe(2);
64
- });
65
-
66
- it("creates and disposes per-call kernels", async () => {
67
- await executePython("print('one')", { cwd: "/tmp", kernelMode: "per-call" });
68
- await executePython("print('two')", { cwd: "/tmp", kernelMode: "per-call" });
69
-
70
- expect(startCalls).toBe(2);
71
- expect(kernels[0]?.shutdownCalls).toBe(1);
72
- expect(kernels[1]?.shutdownCalls).toBe(1);
73
- });
74
-
75
- it("resets the session kernel when requested", async () => {
76
- await executePython("print('one')", { cwd: "/tmp", sessionId: "session-b", kernelMode: "session" });
77
- await executePython("print('two')", {
78
- cwd: "/tmp",
79
- sessionId: "session-b",
80
- kernelMode: "session",
81
- reset: true,
82
- });
83
-
84
- expect(startCalls).toBe(2);
85
- expect(kernels[0]?.shutdownCalls).toBe(1);
86
- });
87
- });
@@ -1,104 +0,0 @@
1
- import { describe, expect, it } from "bun:test";
2
- import { deserializeWebSocketMessage, type JupyterMessage, serializeWebSocketMessage } from "./python-kernel";
3
-
4
- const encoder = new TextEncoder();
5
-
6
- function buildFrame(message: Omit<JupyterMessage, "buffers">, buffers: Uint8Array[] = []): ArrayBuffer {
7
- const msgBytes = encoder.encode(JSON.stringify(message));
8
- const offsetCount = 1 + buffers.length;
9
- const headerSize = 4 + offsetCount * 4;
10
-
11
- let totalSize = headerSize + msgBytes.length;
12
- for (const buffer of buffers) {
13
- totalSize += buffer.length;
14
- }
15
-
16
- const frame = new ArrayBuffer(totalSize);
17
- const view = new DataView(frame);
18
- const bytes = new Uint8Array(frame);
19
-
20
- view.setUint32(0, offsetCount, true);
21
- view.setUint32(4, headerSize, true);
22
- bytes.set(msgBytes, headerSize);
23
-
24
- let offset = headerSize + msgBytes.length;
25
- for (let i = 0; i < buffers.length; i++) {
26
- view.setUint32(4 + (i + 1) * 4, offset, true);
27
- bytes.set(buffers[i], offset);
28
- offset += buffers[i].length;
29
- }
30
-
31
- return frame;
32
- }
33
-
34
- describe("deserializeWebSocketMessage", () => {
35
- it("parses offset tables and buffers", () => {
36
- const message = {
37
- channel: "iopub",
38
- header: {
39
- msg_id: "msg-1",
40
- session: "session-1",
41
- username: "omp",
42
- date: "2024-01-01T00:00:00Z",
43
- msg_type: "stream",
44
- version: "5.5",
45
- },
46
- parent_header: {},
47
- metadata: {},
48
- content: { text: "hello" },
49
- };
50
- const buffer = new Uint8Array([1, 2, 3]);
51
- const frame = buildFrame(message, [buffer]);
52
-
53
- const parsed = deserializeWebSocketMessage(frame);
54
-
55
- expect(parsed).not.toBeNull();
56
- expect(parsed?.header.msg_id).toBe("msg-1");
57
- expect(parsed?.content).toEqual({ text: "hello" });
58
- expect(parsed?.buffers?.[0]).toEqual(buffer);
59
- });
60
-
61
- it("returns null for invalid frames", () => {
62
- const headerSize = 8;
63
- const bytes = encoder.encode("not-json");
64
- const frame = new ArrayBuffer(headerSize + bytes.length);
65
- const view = new DataView(frame);
66
- const data = new Uint8Array(frame);
67
- view.setUint32(0, 1, true);
68
- view.setUint32(4, headerSize, true);
69
- data.set(bytes, headerSize);
70
-
71
- expect(deserializeWebSocketMessage(frame)).toBeNull();
72
- const emptyFrame = new ArrayBuffer(4);
73
- new DataView(emptyFrame).setUint32(0, 0, true);
74
- expect(deserializeWebSocketMessage(emptyFrame)).toBeNull();
75
- });
76
- });
77
-
78
- describe("serializeWebSocketMessage", () => {
79
- it("round trips message payloads", () => {
80
- const message: JupyterMessage = {
81
- channel: "shell",
82
- header: {
83
- msg_id: "msg-2",
84
- session: "session-2",
85
- username: "omp",
86
- date: "2024-02-01T00:00:00Z",
87
- msg_type: "execute_request",
88
- version: "5.5",
89
- },
90
- parent_header: { parent: "root" },
91
- metadata: { tag: "meta" },
92
- content: { code: "print('hi')" },
93
- buffers: [new Uint8Array([9, 8, 7])],
94
- };
95
-
96
- const frame = serializeWebSocketMessage(message);
97
- const parsed = deserializeWebSocketMessage(frame);
98
-
99
- expect(parsed).not.toBeNull();
100
- expect(parsed?.header.msg_type).toBe("execute_request");
101
- expect(parsed?.content).toEqual({ code: "print('hi')" });
102
- expect(parsed?.buffers?.[0]).toEqual(new Uint8Array([9, 8, 7]));
103
- });
104
- });