Files
cc-switch/tests/lib/skillErrorReason.test.ts
T
Jason 83f0155f3c fix(skills): spool repo archives to disk and lift the 128 MiB download cap
Skill discovery, install, check-updates and update all download the
whole GitHub repo ZIP. The compressed body was collected into a Vec in
memory and capped at 128 MiB, so repos whose docs/ images dwarf the
skills themselves (K-Dense-AI/scientific-agent-skills: 235 MiB ZIP,
277 MiB uncompressed, 20 MiB of skills) failed before extraction even
though they fit the 512 MiB extraction budget and 30k entry cap. The
download cap also reused ARCHIVE_TOO_LARGE, so the UI blamed the
extraction limit.

Stream the body chunk by chunk into an anonymous temp file (deleted on
drop, including error paths), still counting actually received bytes,
and open the ZipArchive from that file. With memory no longer the
constraint, align the download cap with the extraction budget, since a
larger compressed body could not be extracted anyway. Report the
download cap under its own ARCHIVE_DOWNLOAD_TOO_LARGE code with
four-locale text. Extraction budget, entry cap and path/symlink
protections are unchanged.

Refs #8421
2026-10-16 19:29:24 +08:00

49 lines
1.6 KiB
TypeScript

import { describe, expect, it } from "vitest";
import type { TFunction } from "i18next";
import { skillErrorReason } from "@/lib/errors/skillErrorParser";
const t = ((key: string, options?: Record<string, unknown>) =>
options && Object.keys(options).length
? `${key}:${JSON.stringify(options)}`
: key) as unknown as TFunction;
describe("skillErrorReason", () => {
it("uses the HTTP explanation for rate-limited downloads", () => {
const error = JSON.stringify({
code: "DOWNLOAD_FAILED",
context: { status: "403" },
suggestion: "http403",
});
expect(skillErrorReason(error, t)).toBe("skills.error.http403");
});
it("uses the error code text with its context otherwise", () => {
const error = JSON.stringify({
code: "DOWNLOAD_TIMEOUT",
context: { owner: "a", name: "b", timeout: "60" },
suggestion: "checkNetwork",
});
expect(skillErrorReason(error, t)).toBe(
'skills.error.downloadTimeout:{"owner":"a","name":"b","timeout":"60"}',
);
});
it("tells an oversized download apart from the extraction limit", () => {
// #8421:下载体超限曾复用 ARCHIVE_TOO_LARGE,被说成「解压后超过上限」
const error = JSON.stringify({
code: "ARCHIVE_DOWNLOAD_TOO_LARGE",
context: { limit_mb: "512" },
});
expect(skillErrorReason(error, t)).toBe(
'skills.error.archiveDownloadTooLarge:{"limit_mb":"512"}',
);
});
it("keeps the first line of an unstructured error", () => {
expect(skillErrorReason("connection reset\nstack…", t)).toBe(
"connection reset",
);
expect(skillErrorReason("", t)).toBe("common.error");
});
});