import fs from "node:fs"; import os from "node:os"; import path from "node:path"; import JSZip from "jszip"; import request from "supertest"; import { afterAll, beforeAll, describe, expect, it } from "vitest"; import { createApp } from "../src/app"; const app = createApp(); let token: string; let printId: string; let fileUrl: string; function auth(req: request.Test) { return req.set("Authorization", `Bearer ${token}`); } async function bambuProject(): Promise { const zip = new JSZip(); zip.file( "3D/3dmodel.model", ` BambuStudio-02.07.01.57 `, ); zip.file( "3D/Objects/object_1.model", ` `, ); zip.file("Metadata/project_settings.config", JSON.stringify({ wall_loops: "3", filament_colour: ["#FF0000"] })); return zip.generateAsync({ type: "nodebuffer" }); } async function fetchApplication(query = "?normalize=1"): Promise { const res = await auth(request(app).get(`/api${fileUrl}${query}`)) .buffer(true) .parse((r, cb) => { const chunks: Buffer[] = []; r.on("data", (c: Buffer) => chunks.push(c)); r.on("end", () => cb(null, Buffer.concat(chunks))); }); expect(res.status).toBe(200); const xml = await (await JSZip.loadAsync(res.body as Buffer)).file("3D/3dmodel.model")!.async("string"); return /([^<]*) { const email = `normalize-test-${Date.now()}@example.com`; const registered = await request(app) .post("/api/register") .send({ displayName: "Normalize Test", email, password: "password123" }); if (registered.status !== 200) throw new Error(`register failed: ${registered.status}`); token = registered.body.token; const file = path.join(os.tmpdir(), `normalize-${Date.now()}.3mf`); fs.writeFileSync(file, await bambuProject()); const upload = await auth(request(app).post("/api/upload")).attach("files", file); fs.rmSync(file, { force: true }); if (upload.status !== 200) throw new Error(`upload failed: ${upload.status}`); printId = upload.body.prints[0].id; fileUrl = upload.body.prints[0].plates[0].url; }); afterAll(async () => { if (printId) await auth(request(app).delete(`/api/print/${printId}`)); }); describe("GET plate file with ?normalize=1", () => { it("serves the normalized copy when asked", async () => { expect(await fetchApplication()).toBe("Thingport"); }); it("serves the original without the flag, so downloads and the viewer are unaffected", async () => { expect(await fetchApplication("")).toMatch(/^BambuStudio/); }); it("ignores other flag values", async () => { expect(await fetchApplication("?normalize=auto")).toMatch(/^BambuStudio/); }); it("leaves non-3MF plates untouched", async () => { const file = path.join(os.tmpdir(), `normalize-${Date.now()}.stl`); fs.writeFileSync(file, "solid x endsolid"); const upload = await auth(request(app).post("/api/upload")).attach("files", file); fs.rmSync(file, { force: true }); const stl = upload.body.prints[0]; try { const res = await auth(request(app).get(`/api${stl.plates[0].url}?normalize=1`)).responseType("blob"); expect(res.status).toBe(200); expect(Buffer.from(res.body as Buffer).toString()).toBe("solid x endsolid"); } finally { await auth(request(app).delete(`/api/print/${stl.id}`)); } }); }); describe("POST plate normalize status", () => { const normalizeUrl = () => `/api${fileUrl.replace(/\/file\/[^/]+$/, "/normalize")}`; it("reports ready once the copy exists", async () => { let status = ""; for (let i = 0; i < 50 && status !== "ready"; i++) { const res = await auth(request(app).post(normalizeUrl())); expect(res.status).toBe(200); status = res.body.status; expect(["preparing", "ready"]).toContain(status); if (status !== "ready") await new Promise((r) => setTimeout(r, 100)); } expect(status).toBe("ready"); }); it("rejects plates that aren't plain 3MF projects", async () => { const file = path.join(os.tmpdir(), `normalize-${Date.now()}.stl`); fs.writeFileSync(file, "solid x endsolid"); const upload = await auth(request(app).post("/api/upload")).attach("files", file); fs.rmSync(file, { force: true }); const stl = upload.body.prints[0]; try { const url = `/api${stl.plates[0].url.replace(/\/file\/[^/]+$/, "/normalize")}`; expect((await auth(request(app).post(url))).status).toBe(400); } finally { await auth(request(app).delete(`/api/print/${stl.id}`)); } }); it("404s for someone else's plate", async () => { const other = await request(app) .post("/api/register") .send({ displayName: "Other", email: `normalize-other-${Date.now()}@example.com`, password: "password123" }); const res = await request(app).post(normalizeUrl()).set("Authorization", `Bearer ${other.body.token}`); expect(res.status).toBe(404); }); });