Files
MorphDoc/packages/font-pack-builder/tests/builder.test.ts
T

158 lines
5.0 KiB
TypeScript

import { createHash } from "node:crypto";
import {
copyFile,
mkdir,
mkdtemp,
readFile,
rm,
writeFile
} from "node:fs/promises";
import { tmpdir } from "node:os";
import { join } from "node:path";
import { fileURLToPath } from "node:url";
import { afterEach, describe, expect, it } from "vitest";
import { buildFontPack, verifyFontPack } from "../src/index.js";
const repositoryRoot = fileURLToPath(new URL("../../../", import.meta.url));
const katexFontRoot = join(repositoryRoot, "node_modules", "katex", "dist", "fonts");
const temporaryDirectories: string[] = [];
function hash(content: Uint8Array | string) {
return createHash("sha256").update(content).digest("hex");
}
async function temporaryDirectory() {
const directory = await mkdtemp(join(tmpdir(), "mdtp-font-pack-builder-"));
temporaryDirectories.push(directory);
return directory;
}
async function createFixture(root: string) {
const sourceRoot = join(root, "source");
const recipeRoot = join(root, "recipe");
const outputRoot = join(root, "output", "root");
const reportPath = join(root, "output", "report.json");
await Promise.all([mkdir(sourceRoot, { recursive: true }), mkdir(recipeRoot, { recursive: true })]);
const webName = "KaTeX_Main-Regular.woff2";
const docxName = "KaTeX_Main-Regular.ttf";
await Promise.all([
copyFile(join(katexFontRoot, webName), join(sourceRoot, webName)),
copyFile(join(katexFontRoot, docxName), join(sourceRoot, docxName))
]);
const [web, docx] = await Promise.all([
readFile(join(sourceRoot, webName)),
readFile(join(sourceRoot, docxName))
]);
const license = "Test Font License\n";
const notice = "# Test font\n";
await Promise.all([
writeFile(join(recipeRoot, "LICENSE.txt"), license, "utf8"),
writeFile(join(recipeRoot, "SOURCE.md"), notice, "utf8")
]);
const recipe = {
recipeVersion: 1,
manifestVersion: 1,
id: "test-serif",
version: "1.0.0",
name: "测试字体包",
description: "构建器测试",
internalFamily: "KaTeX_Main",
license: "Test",
licenseSource: "LICENSE.txt",
licensePath: "LICENSE.txt",
licenseBytes: Buffer.byteLength(license),
licenseSha256: hash(license),
noticeSource: "SOURCE.md",
noticePath: "SOURCE.md",
compatibility: {
minimumAppVersion: "0.6.0",
maximumAppVersionExclusive: "0.7.0"
},
faces: [
{
id: "main-regular",
targets: ["KaTeX_Main"],
weight: 400,
style: "normal",
web: {
source: webName,
path: `fonts/${webName}`,
bytes: web.byteLength,
sha256: hash(web)
},
docx: {
source: docxName,
path: `fonts/${docxName}`,
bytes: docx.byteLength,
sha256: hash(docx)
}
}
]
};
const recipePath = join(recipeRoot, "recipe.json");
await writeFile(recipePath, `${JSON.stringify(recipe, null, 2)}\n`, "utf8");
return { sourceRoot, recipeRoot, outputRoot, reportPath, recipePath, recipe };
}
afterEach(async () => {
await Promise.all(
temporaryDirectories.splice(0).map((directory) =>
rm(directory, { recursive: true, force: true })
)
);
});
describe("字体包构建器", () => {
it("从冻结资源构建可注册、可重复验证的字体包", async () => {
const fixture = await createFixture(await temporaryDirectory());
const first = await buildFontPack(fixture);
expect(first.unchanged).toBe(false);
expect(first.packId).toBe("test-serif");
expect(first.faces[0]).toMatchObject({
family: "KaTeX_Main",
weight: 400,
permission: "installable"
});
expect(JSON.parse(await readFile(fixture.reportPath, "utf8"))).toMatchObject({
packId: "test-serif",
packVersion: "1.0.0"
});
const second = await buildFontPack(fixture);
expect(second.unchanged).toBe(true);
expect(second.packFingerprint).toBe(first.packFingerprint);
const verified = await verifyFontPack({
root: fixture.outputRoot,
appVersion: "0.6.0",
packId: "test-serif",
packVersion: "1.0.0"
});
expect(verified.faces).toHaveLength(1);
});
it("拒绝哈希不匹配的冻结字体源", async () => {
const fixture = await createFixture(await temporaryDirectory());
const webPath = join(fixture.sourceRoot, "KaTeX_Main-Regular.woff2");
const content = await readFile(webPath);
content[content.byteLength - 1] = (content[content.byteLength - 1] ?? 0) ^ 1;
await writeFile(webPath, content);
await expect(buildFontPack(fixture)).rejects.toThrow("SHA-256");
});
it("拒绝覆盖内容不同的同版本发行包", async () => {
const fixture = await createFixture(await temporaryDirectory());
await buildFontPack(fixture);
const changedRecipe = {
...fixture.recipe,
description: "同版本内容发生变化"
};
await writeFile(
fixture.recipePath,
`${JSON.stringify(changedRecipe, null, 2)}\n`,
"utf8"
);
await expect(buildFontPack(fixture)).rejects.toThrow("拒绝覆盖");
});
});