feat: align SDK with updated OpenAPI spec and add missing unit tests
- Update generated types from new openapi.yaml (capsule stats, usage, metrics, pause/resume lifecycle, host/channel management, auth flow) - Add Capsule pause/resume/ping/getMetrics lifecycle methods - Add Capsule.waitForReady abort signal support - Add PtyManager.connect and PtySession disposal - Fix HttpClient empty-body response handling (content-length: 0) - Add streamProcess() to CommandManager for background process streams - Add integration tests for capsule lifecycle, git, and PTY features - Add unit tests for AsyncQueue error paths, PtySession.close, Git.checkout without create, Git.add single string, Notebook.execCell error case, and PtyStartOptions fields
This commit is contained in:
@ -3,6 +3,7 @@ import type { AddressInfo } from "node:net";
|
||||
import { afterEach, describe, expect, it, vi } from "vitest";
|
||||
import { WebSocketServer } from "ws";
|
||||
|
||||
import { AsyncQueue } from "../src/_shared/async-queue.js";
|
||||
import { HttpClient } from "../src/_shared/http.js";
|
||||
import { WsConnection } from "../src/_shared/websocket.js";
|
||||
import { resolveConfig } from "../src/config.js";
|
||||
@ -233,6 +234,57 @@ describe("HttpClient", () => {
|
||||
|
||||
await assertion;
|
||||
});
|
||||
|
||||
it("downloads binary response bodies as ReadableStream", async () => {
|
||||
const body = new ReadableStream({
|
||||
start(controller) {
|
||||
controller.enqueue(new Uint8Array([1, 2, 3]));
|
||||
controller.close();
|
||||
},
|
||||
});
|
||||
vi.stubGlobal(
|
||||
"fetch",
|
||||
vi.fn(async () => new Response(body, { status: 200 })),
|
||||
);
|
||||
|
||||
const client = new HttpClient({ baseUrl: "https://api.example.com" });
|
||||
const stream = await client.download("/v1/file", { path: "/a.txt" });
|
||||
|
||||
expect(stream).toBeInstanceOf(ReadableStream);
|
||||
const reader = stream.getReader();
|
||||
const { value } = await reader.read();
|
||||
expect(value).toEqual(new Uint8Array([1, 2, 3]));
|
||||
});
|
||||
|
||||
it("handles content-length zero as empty response", async () => {
|
||||
vi.stubGlobal(
|
||||
"fetch",
|
||||
vi.fn(
|
||||
async () =>
|
||||
new Response(null, {
|
||||
status: 200,
|
||||
headers: { "content-length": "0" },
|
||||
}),
|
||||
),
|
||||
);
|
||||
|
||||
const client = new HttpClient({ baseUrl: "https://api.example.com" });
|
||||
const result = await client.get("/v1/empty");
|
||||
|
||||
expect(result).toBeUndefined();
|
||||
});
|
||||
|
||||
it("handles empty text body as undefined", async () => {
|
||||
vi.stubGlobal(
|
||||
"fetch",
|
||||
vi.fn(async () => new Response("", { status: 200 })),
|
||||
);
|
||||
|
||||
const client = new HttpClient({ baseUrl: "https://api.example.com" });
|
||||
const result = await client.get("/v1/blank");
|
||||
|
||||
expect(result).toBeUndefined();
|
||||
});
|
||||
});
|
||||
|
||||
describe("WsConnection", () => {
|
||||
@ -263,7 +315,8 @@ describe("WsConnection", () => {
|
||||
await expect(receivedByServer).resolves.toEqual({ type: "start" });
|
||||
expect(messages).toEqual([{ type: "ready" }]);
|
||||
|
||||
connection.close();
|
||||
await connection[Symbol.asyncDispose]();
|
||||
expect(connection.isClosed).toBe(true);
|
||||
server.close();
|
||||
});
|
||||
|
||||
@ -285,3 +338,37 @@ describe("WsConnection", () => {
|
||||
vi.useRealTimers();
|
||||
});
|
||||
});
|
||||
|
||||
describe("AsyncQueue", () => {
|
||||
it("rejects waiting consumers when failed", async () => {
|
||||
const queue = new AsyncQueue<string>();
|
||||
const pending = queue.next();
|
||||
|
||||
queue.fail(new Error("socket died"));
|
||||
|
||||
await expect(pending).rejects.toThrow("socket died");
|
||||
await expect(queue.next()).rejects.toThrow("socket died");
|
||||
});
|
||||
|
||||
it("ends the queue from return and resolves as done", async () => {
|
||||
const queue = new AsyncQueue<string>();
|
||||
|
||||
const result = await queue.return();
|
||||
|
||||
expect(result).toEqual({ done: true, value: undefined });
|
||||
await expect(queue.next()).resolves.toEqual({
|
||||
done: true,
|
||||
value: undefined,
|
||||
});
|
||||
});
|
||||
|
||||
it("propagates error through throw and rejects future reads", async () => {
|
||||
const queue = new AsyncQueue<string>();
|
||||
const error = new Error("consumer threw");
|
||||
|
||||
const result = queue.throw(error);
|
||||
|
||||
await expect(result).rejects.toThrow("consumer threw");
|
||||
await expect(queue.next()).rejects.toThrow("consumer threw");
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user