54 lines
3.6 KiB
JavaScript
54 lines
3.6 KiB
JavaScript
import assert from "node:assert/strict";
|
|
import { createHash } from "node:crypto";
|
|
import { mkdtemp, mkdir, readFile, readdir, rm, writeFile } from "node:fs/promises";
|
|
import os from "node:os";
|
|
import path from "node:path";
|
|
import test from "node:test";
|
|
import { gunzipSync } from "node:zlib";
|
|
import { prepareContentObjects } from "./content-objects.mjs";
|
|
import { selectBootstrapContent } from "./prepare-bootstrap.mjs";
|
|
import { createContentManifest } from "./publish-content.mjs";
|
|
import contentPolicy from "../../shared/content-policy.json" with { type: "json" };
|
|
|
|
test("ships one lossless object for identical assets and verifies original bytes", async () => {
|
|
const directory = await mkdtemp(path.join(os.tmpdir(), "healer-content-compression-"));
|
|
try {
|
|
const sourcePath = path.join(directory, "original.glb");
|
|
const original = Buffer.from("original geometry, textures, and animation\n".repeat(4096));
|
|
await writeFile(sourcePath, original);
|
|
const common = { sourcePath, bytes: original.length, sha256: createHash("sha256").update(original).digest("hex"), extension: ".glb" };
|
|
const files = ["first", "second"].map((id) => ({ ...common, logicalPath: `assets/creatures/${id}/model.glb`, packId: `dungeon:${id}` }));
|
|
const result = await prepareContentObjects(files, { directory: path.join(directory, "objects"), baseUrl: "/content/bundled" });
|
|
assert.equal(result.uniqueObjects, 1);
|
|
assert.equal(result.uniqueBytes, original.length);
|
|
assert.equal(result.logicalBytes, original.length * 2);
|
|
assert.ok(result.storedBytes < original.length / 10);
|
|
assert.equal((await readdir(path.join(directory, "objects"))).length, 1);
|
|
assert.deepEqual(result.files[0].download, result.files[1].download);
|
|
assert.ok(result.files[0].download.url.endsWith(".gzip"), "avoid Android's automatic .gz expansion");
|
|
const stored = await readFile(path.join(directory, "objects", path.basename(result.files[0].download.url)));
|
|
assert.deepEqual(gunzipSync(stored), original);
|
|
assert.deepEqual(await readFile(sourcePath), original);
|
|
const again = await prepareContentObjects(files, { directory: path.join(directory, "objects"), baseUrl: "/content/bundled" });
|
|
assert.equal(again.writtenObjects, 0);
|
|
assert.equal(createContentManifest(result.files).version, createContentManifest(again.files).version);
|
|
await writeFile(sourcePath, "changed");
|
|
await assert.rejects(prepareContentObjects(files, { directory: path.join(directory, "objects"), baseUrl: "/content/bundled" }), /changed during packaging/);
|
|
} finally { await rm(directory, { recursive: true, force: true }); }
|
|
});
|
|
|
|
test("bundles only starter packs and their exact dependencies", () => {
|
|
const ids = [...contentPolicy.bootstrapPackIds, "dungeon:second", "shared:creatures", "manastorm:shared-map", "manastorm:optional-map", "characters:extra"];
|
|
const files = ids.map((packId, index) => ({ logicalPath: `assets/${index}.glb`, packId, sha256: String(index).repeat(64), bytes: 100, extension: ".glb" }));
|
|
const result = selectBootstrapContent(files, [
|
|
{ id: "dungeon:second", dependencies: [] },
|
|
]);
|
|
assert.deepEqual(result.files.map((file) => file.packId), contentPolicy.bootstrapPackIds);
|
|
const packs = new Map(result.packDefinitions.map((pack) => [pack.id, pack]));
|
|
assert.deepEqual([...packs.keys()].sort(), [...contentPolicy.bootstrapPackIds].sort());
|
|
assert.equal([...packs.values()].every((pack) => pack.bundledOnAndroid), true);
|
|
assert.equal(packs.has("dungeon:second"), false);
|
|
assert.equal(packs.has("manastorm:shared-map"), false);
|
|
assert.equal(packs.has("manastorm:optional-map"), false);
|
|
});
|