项目文件夹

文件
wehub-resource-sync b3a7f98e5a
CI / E2E Cloudflare (4/8) (push) Failing after 0s
CI / E2E Cloudflare (8/8) (push) Failing after 1s
CI / Lint (push) Failing after 1s
Auto Extract / Extract (push) Failing after 4s
CI / Version Check (push) Failing after 9s
CI / Integration Tests (push) Failing after 1s
CI / E2E tests (1/8) (push) Failing after 1s
CI / E2E tests (2/8) (push) Failing after 2s
CI / E2E tests (3/8) (push) Failing after 2s
CI / Browser Tests (push) Failing after 1s
CI / E2E tests (5/8) (push) Failing after 1s
CI / E2E Cloudflare (2/8) (push) Failing after 2s
CI / Typecheck (push) Failing after 1s
CI / Changeset Validation (push) Failing after 2s
CI / E2E Cloudflare (5/8) (push) Failing after 1s
CI / E2E Cloudflare (6/8) (push) Failing after 1s
CI / E2E Cloudflare (7/8) (push) Failing after 1s
CodeQL / Analyze (javascript-typescript) (push) Failing after 1s
Format / Format (push) Failing after 0s
CodeQL / Analyze (actions) (push) Failing after 4s
CI / E2E tests (4/8) (push) Failing after 1s
CI / E2E tests (6/8) (push) Failing after 1s
CI / E2E tests (7/8) (push) Failing after 2s
CI / E2E tests (8/8) (push) Failing after 1s
CI / E2E Cloudflare (1/8) (push) Failing after 1s
CI / E2E Cloudflare (3/8) (push) Failing after 2s
Preview Releases / Publish Preview (push) Failing after 0s
zizmor / Run zizmor (push) Failing after 1s
Release / Release (push) Failing after 2s
CI / Smoke Tests (push) Failing after 5m36s
CI / Tests (push) Failing after 6m36s
Release / Sync Templates (push) Has been skipped
CI / E2E Tests (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 12:23:53 +08:00

124 行
4.4 KiB
TypeScript

此文件含有模棱两可的 Unicode 字符
此文件含有可能会与其他字符混淆的 Unicode 字符。 如果您是想特意这样的,可以安全地忽略该警告。 使用 Escape 按钮显示他们。
import { mkdir, mkdtemp, readFile, rm, writeFile } from "node:fs/promises";
import { tmpdir } from "node:os";
import { dirname, join } from "node:path";
import { afterEach, beforeEach, describe, expect, it } from "vitest";
import { createTarball, MAX_FILE_SIZE } from "../src/bundle/utils.js";
import { extractManifestFromTarballForTest } from "../src/commands/publish.js";
/**
* Round-trip the tarball-extraction step from the publish CLI: build a real
* gzipped tarball on disk, read its bytes, and feed them through the same
* function the publish flow calls. This exercises the bundle-size-cap wiring
* end-to-end (decompression + per-file accounting + total accounting) which
* the pure `validateBundleSize` unit tests cannot.
*/
describe("extractManifestFromTarball (publish CLI)", () => {
let stagingDir: string;
let outDir: string;
beforeEach(async () => {
stagingDir = await mkdtemp(join(tmpdir(), "emdash-tar-staging-"));
outDir = await mkdtemp(join(tmpdir(), "emdash-tar-out-"));
});
afterEach(async () => {
await rm(stagingDir, { recursive: true, force: true });
await rm(outDir, { recursive: true, force: true });
});
async function buildTarball(files: Record<string, string | Uint8Array>): Promise<Uint8Array> {
for (const [name, body] of Object.entries(files)) {
const fullPath = join(stagingDir, name);
await mkdir(dirname(fullPath), { recursive: true });
await writeFile(fullPath, body);
}
const tarballPath = join(outDir, "test.tar.gz");
await createTarball(stagingDir, tarballPath);
return new Uint8Array(await readFile(tarballPath));
}
const minimalManifest = JSON.stringify({
id: "test-plugin",
version: "1.0.0",
capabilities: [],
allowedHosts: [],
storage: {},
hooks: [],
routes: [],
admin: {},
});
it("returns the manifest for a tarball within all caps", async () => {
const bytes = await buildTarball({
"manifest.json": minimalManifest,
"backend.js": "export default {};\n",
});
const manifest = await extractManifestFromTarballForTest(bytes);
expect(manifest.id).toBe("test-plugin");
expect(manifest.version).toBe("1.0.0");
});
it("rejects a tarball with a single file over the per-file cap", async () => {
const bytes = await buildTarball({
"manifest.json": minimalManifest,
"backend.js": "x".repeat(MAX_FILE_SIZE + 1),
});
await expect(extractManifestFromTarballForTest(bytes)).rejects.toThrow(
/violates bundle size caps[\s\S]*backend\.js/,
);
});
it("rejects a tarball whose total decompressed size exceeds the bundle cap", async () => {
// Three files at exactly MAX_FILE_SIZE — each within per-file cap, but
// total (3 × 128 KB = 384 KB) exceeds the 256 KB total cap.
const bytes = await buildTarball({
"manifest.json": minimalManifest,
"a.js": "a".repeat(MAX_FILE_SIZE),
"b.js": "b".repeat(MAX_FILE_SIZE),
"c.js": "c".repeat(MAX_FILE_SIZE),
});
await expect(extractManifestFromTarballForTest(bytes)).rejects.toThrow(
/violates bundle size caps[\s\S]*Bundle size/,
);
});
it("rejects a tarball with too many files even when each is tiny", async () => {
const files: Record<string, string> = { "manifest.json": minimalManifest };
for (let i = 0; i < 25; i++) files[`f-${String(i).padStart(2, "0")}.js`] = "x";
const bytes = await buildTarball(files);
await expect(extractManifestFromTarballForTest(bytes)).rejects.toThrow(
/violates bundle size caps[\s\S]*contains \d+ files/,
);
});
it("does not count non-file tar entries (symlinks etc.) toward the file cap", async () => {
// Hand-build a tarball with 25 symlink entries plus one real file.
// Symlinks have type "2" and size 0 in USTAR. The file cap is 20, so
// counting symlinks would reject this; the filter should only see one
// real file (manifest.json) and accept it.
const { packTar } = await import("modern-tar");
const { gzipSync } = await import("node:zlib");
const symlinkEntries = Array.from({ length: 25 }, (_, i) => ({
header: {
name: `link-${i}`,
size: 0,
type: "symlink" as const,
linkname: "manifest.json",
},
body: new Uint8Array(0),
}));
const tarBytes = await packTar([
{
header: { name: "manifest.json", size: minimalManifest.length, type: "file" as const },
body: new TextEncoder().encode(minimalManifest),
},
...symlinkEntries,
]);
const gzipped = gzipSync(tarBytes);
const manifest = await extractManifestFromTarballForTest(new Uint8Array(gzipped));
expect(manifest.id).toBe("test-plugin");
});
});