1import * as NodeAssert from "node:assert/strict";
2
3import * as Cause from "effect/Cause";
4import { describe, it } from "vite-plus/test";
5
6import { OpenCodeRuntimeError, openCodeRuntimeErrorDetail } from "./OpenCodeRuntime.ts";
7
8describe("openCodeRuntimeErrorDetail", () => {
9 it("formats an Effect TimeoutError constructed without a message", () => {
10 const detail = openCodeRuntimeErrorDetail(new Cause.TimeoutError());
11 NodeAssert.equal(typeof detail, "string");
12 NodeAssert.match(detail, /TimeoutError/);
13 });
14
15 it("returns the detail of an OpenCodeRuntimeError", () => {
16 const error = new OpenCodeRuntimeError({ operation: "session.abort", detail: "aborted" });
17 NodeAssert.equal(openCodeRuntimeErrorDetail(error), "aborted");
18 });
19
20 it("returns a trimmed Error message", () => {
21 NodeAssert.equal(openCodeRuntimeErrorDetail(new Error(" timed out ")), "timed out");
22 });
23
24 it("reads an Error message getter only once", () => {
25 const error = new Error();
26 let reads = 0;
27 Object.defineProperty(error, "message", {
28 get() {
29 reads += 1;
30 if (reads > 1) throw new Error("message was read twice");
31 return " timed out ";
32 },
33 });
34
35 NodeAssert.equal(openCodeRuntimeErrorDetail(error), "timed out");
36 NodeAssert.equal(reads, 1);
37 });
38
39 it("does not throw for an Error whose message is undefined", () => {
40 // Effect's TimeoutError constructed without an argument arrives this way.
41 const error = new Error("placeholder");
42 Object.defineProperty(error, "message", { value: undefined, writable: true });
43 const detail = openCodeRuntimeErrorDetail(error);
44 NodeAssert.equal(typeof detail, "string");
45 NodeAssert.ok(detail.length > 0);
46 });
47
48 it("does not throw for an Error whose message is not a string", () => {
49 const error = new Error("placeholder");
50 Object.defineProperty(error, "message", { value: { code: 42 }, writable: true });
51 const detail = openCodeRuntimeErrorDetail(error);
52 NodeAssert.equal(typeof detail, "string");
53 NodeAssert.ok(detail.length > 0);
54 });
55
56 it("falls through to the object fallback for an Error with an empty message", () => {
57 const error = Object.assign(new Error(""), { response: { status: 503 }, error: "down" });
58 NodeAssert.equal(openCodeRuntimeErrorDetail(error), 'status=503 body="down"');
59 });
60
61 it("formats SDK response shapes", () => {
62 NodeAssert.equal(
63 openCodeRuntimeErrorDetail({ response: { status: 500 }, error: { message: "boom" } }),
64 'status=500 body={"message":"boom"}',
65 );
66 });
67
68 it("stringifies primitives", () => {
69 NodeAssert.equal(openCodeRuntimeErrorDetail("plain"), "plain");
70 NodeAssert.equal(openCodeRuntimeErrorDetail(undefined), "undefined");
71 });
72});