import assert from "node:assert/strict"; import { test } from "node:test"; import { createHash, randomUUID } from "node:crypto"; import { createServer } from "node:http"; import sharp from "sharp"; import { createClient } from "@supabase/supabase-js"; import { datasetSchema, faces } from "../shared/protocol"; import type { Database } from "../shared/database"; import { processFabricImages } from "../lib/fabric-images"; import { processFabricJob } from "../lib/fabric-jobs"; async function fixture() { const map = await sharp({ create: { width: 32, height: 32, channels: 3, background: "#349d66" }, }) .png() .toBuffer(); const face = await sharp({ create: { width: 256, height: 256, channels: 3, background: "#548fda" }, }) .png() .toBuffer(); const viewpoints = Array.from({ length: 5 }, (_, i) => ({ id: `p${i}`, x: i, y: 64, z: i, })); const files = new Map([ ["map.png", map], ...viewpoints.flatMap((p) => faces.map((f) => [`${p.id}/${f}.png`, face] as [string, Buffer]), ), ]); const manifest = datasetSchema.parse({ format: "minegeo-dataset/v1", minecraftVersion: "26.2", dimension: "minecraft:overworld", source: "fabric", name: "Image worker fixture", serverName: "Local", bounds: { minX: 0, minZ: 0, maxX: 32, maxZ: 32 }, north: "-z", viewpoints, assets: [...files].map(([path, bytes]) => ({ path, bytes: bytes.length, sha256: createHash("sha256").update(bytes).digest("hex"), })), }); return { files, manifest }; } test("mod images become a lossless map, six WebP faces per location, and previews", async () => { const { manifest, files } = await fixture(); const images = new Map(); const progress: number[] = []; await processFabricImages(manifest, { signal: new AbortController().signal, async download(path) { return files.get(path)!; }, async save(path, bytes) { images.set(path, bytes); }, async progress(value) { progress.push(value); }, }); assert.equal(images.size, 36); for (const point of manifest.viewpoints) { for (const face of faces) { const meta = await sharp( images.get(`${point.id}/${face}.webp`), ).metadata(); assert.equal(meta.format, "webp"); assert.equal(meta.width, 256); assert.equal(meta.height, 256); } assert.equal( (await sharp(images.get(`${point.id}/preview.webp`)).metadata()).width, 512, ); } assert.deepEqual( await sharp(images.get("map.webp")).raw().toBuffer(), await sharp(files.get("map.png")).raw().toBuffer(), ); assert.equal(progress.at(-1), 95); assert.ok(progress.every((p, i) => i === 0 || p > progress[i - 1])); }); test("map-only refresh processes only the captured map", async () => { const { manifest, files } = await fixture(); const saved: string[] = [], downloaded: string[] = []; await processFabricImages(manifest, { mapOnly: true, signal: new AbortController().signal, async download(path) { downloaded.push(path); return files.get(path)!; }, async save(path) { saved.push(path); }, async progress() {}, }); assert.deepEqual(downloaded, ["map.png"]); assert.deepEqual(saved, ["map.webp"]); }); test("corrupt assets, non-square faces, and cancelled work cannot complete", async () => { const { manifest, files } = await fixture(); const io = { signal: new AbortController().signal, async download(path: string) { return files.get(path)!; }, async save() {}, async progress() {}, }; await assert.rejects( processFabricImages(manifest, { ...io, async download() { return Buffer.from("corrupt"); }, }), /checksum\/size mismatch/, ); const rectangular = await sharp({ create: { width: 512, height: 256, channels: 3, background: "white" }, }) .png() .toBuffer(); files.set("p0/px.png", rectangular); const asset = manifest.assets.find((a) => a.path === "p0/px.png")!; asset.bytes = rectangular.length; asset.sha256 = createHash("sha256").update(rectangular).digest("hex"); await assert.rejects( processFabricImages(manifest, io), /must be square PNGs/, ); const controller = new AbortController(); controller.abort(new Error("Cancelled fixture")); await assert.rejects( processFabricImages(manifest, { ...io, signal: controller.signal }), /Cancelled fixture/, ); }); // Exercise the actual Supabase client against a local Storage/PostgREST service. // No credentials, game clients, or real user data are involved. test("Vercel worker claims the existing queue job, stores images, and completes through the existing RPC", async (t) => { const { files, manifest } = await fixture(); const owner = randomUUID(), datasetId = randomUUID(); let job: Record = { id: randomUUID(), dataset_id: datasetId, source_dataset_id: null, kind: "upload", state: "queued", attempts: 0, progress: 0, lease_until: null, }; let completed = 0; const saved = new Map(); const server = createServer(async (req, res) => { const url = new URL(req.url!, "http://localhost"), chunks = []; for await (const chunk of req) chunks.push(chunk); const bytes = Buffer.concat(chunks); function json(data: unknown) { res.setHeader("Content-Type", "application/json"); res.end(JSON.stringify(data)); } if (url.pathname === "/rest/v1/upload_jobs") { if (req.method === "PATCH") { const body = JSON.parse(bytes.toString()); assert.equal(url.searchParams.get("id"), `eq.${job.id}`); if (body.lease_owner) assert.equal(url.searchParams.get("state"), "eq.queued"); job = { ...job, ...body }; } return json([job]); } if (url.pathname === "/rest/v1/datasets") return json({ owner_id: owner, source: "fabric" }); if (url.pathname === "/rest/v1/dataset_inputs") return json({ manifest }); if (url.pathname === "/rest/v1/rpc/complete_job") { const body = JSON.parse(bytes.toString()); assert.equal(body.p_job, job.id); assert.equal(body.p_worker, job.lease_owner); assert.equal(body.p_count, 5); assert.equal(body.p_cover, "p0"); assert.equal(saved.size, 36); completed++; job.state = "complete"; return json(null); } const raw = `/storage/v1/object/minegeo-raw/${owner}/${datasetId}/`; if (url.pathname.startsWith(raw)) return res.end(files.get(url.pathname.slice(raw.length))); const media = `/storage/v1/object/minegeo-media/${datasetId}/`; if (url.pathname.startsWith(media)) { assert.equal(req.headers["x-upsert"], "true"); assert.equal((await sharp(bytes).metadata()).format, "webp"); saved.set(url.pathname.slice(media.length), bytes); return json({ Key: url.pathname, Id: randomUUID() }); } res.statusCode = 404; json({ error: url.pathname }); }); await new Promise((resolve) => server.listen(0, "127.0.0.1", resolve)); t.after(() => new Promise((resolve) => server.close(() => resolve()))); const client = createClient( `http://127.0.0.1:${(server.address() as any).port}`, "local-fixture-only", { auth: { persistSession: false, autoRefreshToken: false } }, ); assert.equal(await processFabricJob(client, datasetId), "complete"); assert.equal(job.state, "complete"); assert.equal(job.attempts, 1); assert.equal(completed, 1); });