- 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
126 lines
3.3 KiB
TypeScript
126 lines
3.3 KiB
TypeScript
import { describe, expect, it } from "vitest";
|
|
|
|
import { Capsule, type CapsuleCreateOptions } from "../../src/capsule.js";
|
|
import { DEFAULT_BASE_URL } from "../../src/config.js";
|
|
|
|
const baseUrl = process.env.WRENN_BASE_URL ?? DEFAULT_BASE_URL;
|
|
const apiKey = process.env.WRENN_API_KEY;
|
|
const template = process.env.WRENN_TEST_TEMPLATE ?? "minimal";
|
|
const waitTimeoutMs = Number(process.env.WRENN_TEST_WAIT_TIMEOUT_MS ?? 120_000);
|
|
const describeWithApiKey = apiKey ? describe : describe.skip;
|
|
|
|
const clientOpts: CapsuleCreateOptions = { baseUrl };
|
|
if (apiKey) clientOpts.apiKey = apiKey;
|
|
|
|
describeWithApiKey("Capsule live integration", () => {
|
|
it(
|
|
"creates, waits for, inspects, pings, and destroys a live capsule",
|
|
async () => {
|
|
let capsule: Capsule | undefined;
|
|
|
|
try {
|
|
capsule = await Capsule.create(template, {
|
|
...clientOpts,
|
|
timeout_sec: 60,
|
|
});
|
|
|
|
expect(capsule.id).toBeTypeOf("string");
|
|
expect(capsule.id.length).toBeGreaterThan(0);
|
|
|
|
const ready = await capsule.waitForReady({
|
|
intervalMs: 2_000,
|
|
timeoutMs: waitTimeoutMs,
|
|
});
|
|
expect(ready).toMatchObject({ id: capsule.id, status: "running" });
|
|
|
|
const connected = Capsule.connect(capsule.id, clientOpts);
|
|
const info = await connected.getInfo();
|
|
expect(info.id).toBe(capsule.id);
|
|
|
|
await expect(connected.ping()).resolves.toBeUndefined();
|
|
|
|
const metrics = await connected.getMetrics({ range: "10m" });
|
|
expect(metrics).toBeTypeOf("object");
|
|
|
|
await connected.close();
|
|
await connected[Symbol.asyncDispose]();
|
|
|
|
await Capsule.destroy(capsule.id, clientOpts);
|
|
capsule = undefined;
|
|
} finally {
|
|
if (capsule) {
|
|
await Capsule.destroy(capsule.id, clientOpts).catch(() => undefined);
|
|
}
|
|
}
|
|
},
|
|
waitTimeoutMs + 30_000,
|
|
);
|
|
|
|
it(
|
|
"pauses and resumes a live capsule through high-level methods",
|
|
async () => {
|
|
let capsule: Capsule | undefined;
|
|
|
|
try {
|
|
capsule = await Capsule.create(template, {
|
|
...clientOpts,
|
|
timeout_sec: 60,
|
|
});
|
|
|
|
await capsule.waitForReady({
|
|
intervalMs: 2_000,
|
|
timeoutMs: waitTimeoutMs,
|
|
});
|
|
|
|
const paused = await capsule.pause();
|
|
expect(paused).toMatchObject({ id: capsule.id });
|
|
expect(paused.status).toBe("paused");
|
|
|
|
const resumed = await capsule.resume({
|
|
intervalMs: 2_000,
|
|
timeoutMs: waitTimeoutMs,
|
|
wait: true,
|
|
});
|
|
expect(resumed).toMatchObject({ id: capsule.id, status: "running" });
|
|
} finally {
|
|
if (capsule) {
|
|
await Capsule.destroy(capsule.id, clientOpts).catch(() => undefined);
|
|
}
|
|
}
|
|
},
|
|
waitTimeoutMs * 2 + 30_000,
|
|
);
|
|
|
|
it(
|
|
"destroys an owned live capsule when async disposed",
|
|
async () => {
|
|
let capsuleId: string | undefined;
|
|
|
|
try {
|
|
const capsule = await Capsule.create(template, {
|
|
...clientOpts,
|
|
timeout_sec: 60,
|
|
});
|
|
capsuleId = capsule.id;
|
|
|
|
await capsule.waitForReady({
|
|
intervalMs: 2_000,
|
|
timeoutMs: waitTimeoutMs,
|
|
});
|
|
|
|
await capsule[Symbol.asyncDispose]();
|
|
|
|
await expect(
|
|
Capsule.connect(capsuleId, clientOpts).getInfo(),
|
|
).resolves.toMatchObject({ id: capsuleId, status: "stopped" });
|
|
capsuleId = undefined;
|
|
} finally {
|
|
if (capsuleId) {
|
|
await Capsule.destroy(capsuleId, clientOpts).catch(() => undefined);
|
|
}
|
|
}
|
|
},
|
|
waitTimeoutMs + 30_000,
|
|
);
|
|
});
|