84 lines
3.5 KiB
TypeScript
84 lines
3.5 KiB
TypeScript
import { buildFakeTorrent, RAR5_SAMPLE_BASE64, SEVENZIP_SUBTITLE_BASE64, ZIMUKU_CAPTCHA } from "@umt/testkit";
|
|
import { zipSync } from "fflate";
|
|
import { describe, expect, it } from "vitest";
|
|
import { runSandbox, SandboxError } from "../../src/sandbox/client.ts";
|
|
import { b64encode } from "../../src/sandbox/jobs.ts";
|
|
|
|
// 沙箱(docs/adr/ADR-005-sandbox.md):子进程撤销全部权限;卡死超时强杀;内存超限强杀;任务错误原样带回
|
|
const enc = (s: string) => new TextEncoder().encode(s);
|
|
|
|
describe("沙箱子进程", () => {
|
|
it("正常返回;子进程里读文件被拒绝", async () => {
|
|
expect(await runSandbox("self.test", { mode: "ok" })).toEqual({ ok: true });
|
|
await expect(runSandbox("self.test", { mode: "io" })).rejects.toMatchObject({ kind: "job", message: /NotCapable/ });
|
|
});
|
|
|
|
it("卡死:超时后强杀,不拖住调用方", async () => {
|
|
const t0 = Date.now();
|
|
const e = await runSandbox("self.test", { mode: "hang" }, { timeoutMs: 800 }).catch((x) => x);
|
|
expect(e).toBeInstanceOf(SandboxError);
|
|
expect(e.kind).toBe("timeout");
|
|
expect(Date.now() - t0).toBeLessThan(5000);
|
|
});
|
|
|
|
it("吃内存:超过上限强杀", async () => {
|
|
const e = await runSandbox("self.test", { mode: "memory" }, { memoryMb: 128, timeoutMs: 20_000 }).catch((x) => x);
|
|
expect(e).toBeInstanceOf(SandboxError);
|
|
expect(e.kind).toBe("memory");
|
|
});
|
|
|
|
it("任务自己抛错:kind = job,带原因", async () => {
|
|
await expect(runSandbox("self.test", { mode: "throw" })).rejects.toMatchObject({
|
|
kind: "job",
|
|
message: /任务自己抛的错/,
|
|
});
|
|
});
|
|
|
|
it("字幕:zip 解压、语言与季集;种子:文件清单", async () => {
|
|
const zip = zipSync({ "a/Show.S01E03.chs.srt": enc("1\n00:00:01,000 --> 00:00:02,000\n你好\n") });
|
|
const r = await runSandbox("subtitle.parse", { files: [{ name: "x.zip", base64: b64encode(zip) }] });
|
|
expect(r.items[0]).toMatchObject({
|
|
name: "a/Show.S01E03.chs.srt",
|
|
archive: "x.zip",
|
|
language: "zh-Hans",
|
|
episode: 3,
|
|
});
|
|
const t = buildFakeTorrent({ name: "Show.S01E01.mkv" });
|
|
const f = await runSandbox("torrent.parse", { base64: b64encode(t.bytes) });
|
|
expect(f.files.map((x) => x.path)).toEqual(["Show.S01E01.mkv"]);
|
|
expect(f.hash).toBe(t.infoHash);
|
|
});
|
|
|
|
it("rar / 7z 在沙箱里解压(撤销权限后 WASM 仍可用);非字幕条目跳过", async () => {
|
|
const r = await runSandbox("subtitle.parse", {
|
|
files: [
|
|
{ name: "pack.7z", base64: SEVENZIP_SUBTITLE_BASE64 },
|
|
{ name: "texts.rar", base64: RAR5_SAMPLE_BASE64 },
|
|
],
|
|
});
|
|
expect(r.items).toEqual([
|
|
expect.objectContaining({ name: "s7/a.chs.srt", archive: "pack.7z", language: "zh-Hans" }),
|
|
]);
|
|
expect(r.skipped).toEqual([{ name: "texts.rar", reason: "压缩包里没有字幕文件" }]);
|
|
});
|
|
|
|
it("字幕库验证码:真实样本识别正确", async () => {
|
|
expect(await runSandbox("captcha.solve", { kind: "zimuku", bmpBase64: ZIMUKU_CAPTCHA.bmpBase64 })).toEqual({
|
|
code: ZIMUKU_CAPTCHA.code,
|
|
});
|
|
});
|
|
});
|
|
|
|
describe("压缩包解压(进程内,直接测解压模块)", () => {
|
|
it("rar 列出全部条目", async () => {
|
|
const { extractArchive } = await import("../../src/sandbox/archives.ts");
|
|
const out = await extractArchive(
|
|
"rar",
|
|
Uint8Array.from(atob(RAR5_SAMPLE_BASE64), (c) => c.charCodeAt(0)),
|
|
() => true,
|
|
);
|
|
expect(Object.keys(out).sort()).toEqual(["stest1.txt", "stest2.txt"]);
|
|
await expect(extractArchive("rar", new Uint8Array([1, 2, 3]), () => true)).rejects.toThrow();
|
|
});
|
|
});
|