Skip to content

Commit 500c0a5

Browse files
committed
test(plugins): restore image-fallback, cache-failure, and config-precedence coverage
Amp-Thread-ID: https://jieli.app/threads/T-b2e20e80-1c45-4d61-bb27-4234f223abe0
1 parent bede1d3 commit 500c0a5

2 files changed

Lines changed: 94 additions & 0 deletions

File tree

plugins/claude-code/tests/runtime-node.test.mjs

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -276,9 +276,47 @@ test("normalizes Claude images and attachment cache behavior", async () => {
276276
assert.equal(first, "https://jieli.example.test/attachments/cached.png");
277277
assert.equal(second, "https://jieli.example.test/attachments/cached.png");
278278
assert.deepEqual(calls, [imagePath]);
279+
280+
const failingCalls = [];
281+
const failingUpload = async (path) => {
282+
failingCalls.push(path);
283+
throw new Error("backend is down");
284+
};
285+
const otherImage = join(tmp, "2.png");
286+
writeFileSync(otherImage, Buffer.from([0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a]));
287+
await assert.rejects(() => runtime.uploadAttachmentCached(otherImage, "https://jieli.example.test/", "secret", failingUpload), /backend is down/);
288+
await assert.rejects(() => runtime.uploadAttachmentCached(otherImage, "https://jieli.example.test/", "secret", failingUpload), /backend is down/);
289+
assert.deepEqual(failingCalls, [otherImage, otherImage]);
279290
});
280291
});
281292

293+
test("keeps the existing image label when the uploader fails instead of inserting a placeholder", async () => {
294+
const tmp = makeTempDir();
295+
const imagePath = join(tmp, "1.png");
296+
writeFileSync(imagePath, Buffer.from([0x89, 0x50, 0x4e, 0x47]));
297+
const transcript = join(tmp, "session.jsonl");
298+
writeJsonl(transcript, [
299+
{
300+
type: "user",
301+
uuid: "u-image-fallback",
302+
sessionId: "cc-image-fallback",
303+
message: { role: "user", content: [{ type: "text", text: "我测试下图片:\n\n[Image #1]\n你看到了什么" }, { type: "image", source: imagePath }] },
304+
},
305+
]);
306+
307+
const payload = await runtime.buildPayloadFromHook(
308+
{ session_id: "cc-image-fallback", transcript_path: transcript, cwd: "/Users/alice/work/jieli" },
309+
"https://jieli.example.test",
310+
async () => {
311+
throw new Error("backend is down");
312+
},
313+
);
314+
315+
assert.equal(payload.thread.messages.length, 1);
316+
assert.equal(payload.thread.messages[0].content, "我测试下图片:\n\n[Image #1]\n你看到了什么");
317+
assert.doesNotMatch(JSON.stringify(payload), /\[Image unavailable\]/);
318+
});
319+
282320
test("handles Claude model aliases, local command noise, loaded skills, and split assistant messages", async () => {
283321
const tmp = makeTempDir();
284322
const transcript = join(tmp, "session.jsonl");
@@ -361,6 +399,23 @@ test("configuration, upload, lock, and transcript flush helpers match hook behav
361399
assert.deepEqual(runtime.buildMissingConfigHookResponse("userpromptsubmit", ["JIELI_API_KEY"]), {});
362400
});
363401

402+
const settingsHome = makeTempDir();
403+
mkdirSync(join(settingsHome, ".config", "jieli"), { recursive: true });
404+
writeFileSync(
405+
join(settingsHome, ".config", "jieli", "settings.json"),
406+
JSON.stringify({ api_key: "settings-key", base_url: "https://settings.example.test/" }),
407+
"utf8",
408+
);
409+
await withEnv({ HOME: settingsHome, JIELI_API_KEY: undefined, CLAUDE_PLUGIN_OPTION_API_KEY: undefined, JIELI_BASE_URL: undefined }, async () => {
410+
assert.deepEqual(runtime.missingConfigVars(), []);
411+
assert.equal(runtime.requiredEnv("JIELI_API_KEY", "CLAUDE_PLUGIN_OPTION_API_KEY"), "settings-key");
412+
assert.equal(runtime.optionalEnv("JIELI_BASE_URL", "CLAUDE_PLUGIN_OPTION_BASE_URL"), "https://settings.example.test");
413+
});
414+
await withEnv({ HOME: settingsHome, JIELI_API_KEY: "env-key", JIELI_BASE_URL: "https://env.example.test/" }, async () => {
415+
assert.equal(runtime.requiredEnv("JIELI_API_KEY", "CLAUDE_PLUGIN_OPTION_API_KEY"), "env-key");
416+
assert.equal(runtime.optionalEnv("JIELI_BASE_URL", "CLAUDE_PLUGIN_OPTION_BASE_URL"), "https://env.example.test/");
417+
});
418+
364419
const { server, state } = createMockJieliServer();
365420
const baseUrl = await listen(server);
366421
state.baseUrl = baseUrl;

plugins/codex/tests/runtime-node.test.mjs

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -268,9 +268,43 @@ test("normalizes Codex repo metadata, data URL images, local image events, and a
268268
assert.equal(first, "https://jieli.example.test/attachments/cached.png");
269269
assert.equal(second, "https://jieli.example.test/attachments/cached.png");
270270
assert.deepEqual(calls, [imagePath]);
271+
272+
const failingCalls = [];
273+
const failingUpload = async (path) => {
274+
failingCalls.push(path);
275+
throw new Error("backend is down");
276+
};
277+
const otherImage = join(tmp, "2.png");
278+
writeFileSync(otherImage, Buffer.from([0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a]));
279+
await assert.rejects(() => runtime.uploadAttachmentCached(otherImage, "https://jieli.example.test/", "secret", failingUpload), /backend is down/);
280+
await assert.rejects(() => runtime.uploadAttachmentCached(otherImage, "https://jieli.example.test/", "secret", failingUpload), /backend is down/);
281+
assert.deepEqual(failingCalls, [otherImage, otherImage]);
271282
});
272283
});
273284

285+
test("keeps the existing image label when the Codex uploader fails instead of inserting a placeholder", async () => {
286+
const tmp = makeTempDir();
287+
const imagePath = join(tmp, "1.png");
288+
writeFileSync(imagePath, Buffer.from([0x89, 0x50, 0x4e, 0x47]));
289+
const transcript = join(tmp, "session.jsonl");
290+
writeJsonl(transcript, [
291+
{ type: "session_meta", payload: { id: "codex-image-fallback", cwd: "/Users/alice/work/jieli" } },
292+
{ type: "event_msg", payload: { type: "user_message", message: "ok [Image #1] what is this?", images: [], local_images: [imagePath], text_elements: [{ placeholder: "[Image #1]" }] } },
293+
]);
294+
295+
const payload = await runtime.buildPayloadFromHook(
296+
{ session_id: "codex-image-fallback", transcript_path: transcript },
297+
"https://jieli.example.test",
298+
async () => {
299+
throw new Error("backend is down");
300+
},
301+
);
302+
303+
assert.equal(payload.thread.messages.length, 1);
304+
assert.equal(payload.thread.messages[0].content, "ok [Image #1] what is this?");
305+
assert.doesNotMatch(JSON.stringify(payload), /\[Image unavailable\]/);
306+
});
307+
274308
test("configuration, upload, lock, session mapping, and missing transcript behavior match Codex hooks", async () => {
275309
const tmp = makeTempDir();
276310
await withEnv({ HOME: tmp, JIELI_API_KEY: undefined, JIELI_BASE_URL: undefined }, async () => {
@@ -289,6 +323,11 @@ test("configuration, upload, lock, session mapping, and missing transcript behav
289323
assert.equal(runtime.optionalEnv("JIELI_BASE_URL"), "https://jieli.example.test");
290324
});
291325

326+
await withEnv({ HOME: tmp, JIELI_API_KEY: "env-key", JIELI_BASE_URL: "https://env.example.test/" }, async () => {
327+
assert.equal(runtime.requiredEnv("JIELI_API_KEY"), "env-key");
328+
assert.equal(runtime.optionalEnv("JIELI_BASE_URL"), "https://env.example.test");
329+
});
330+
292331
await withEnv({ HOME: tmp }, async () => {
293332
runtime.writeSessionMapping("codex-map", "https://jieli.example.test/", "T-codex-map", "/tmp/session.jsonl");
294333
const path = join(tmp, ".jieli", "codex-sessions.json");

0 commit comments

Comments
 (0)