211 lines
6.4 KiB
TypeScript
211 lines
6.4 KiB
TypeScript
import assert from "node:assert/strict";
|
|
import { spawn } from "node:child_process";
|
|
import { mkdir, readFile, writeFile, copyFile } from "node:fs/promises";
|
|
import { createClient } from "@supabase/supabase-js";
|
|
process.loadEnvFile(".env.local");
|
|
const db = createClient(
|
|
process.env.NEXT_PUBLIC_SUPABASE_URL!,
|
|
process.env.SUPABASE_SECRET_KEY!,
|
|
{ auth: { persistSession: false } },
|
|
);
|
|
const account = JSON.parse(await readFile(".data/test-account.json", "utf8"));
|
|
const origin = "http://127.0.0.1:3100",
|
|
directory = `.data/api-smoke-${Date.now()}`;
|
|
await mkdir(directory, { recursive: true });
|
|
const manifest = JSON.parse(
|
|
await readFile(
|
|
".cache/paper-proof/plugins/MineGeo/export/manifest.json",
|
|
"utf8",
|
|
),
|
|
);
|
|
manifest.name = "Private API regression";
|
|
manifest.serverName = "MineGeo API regression";
|
|
await writeFile(`${directory}/manifest.json`, JSON.stringify(manifest));
|
|
await copyFile(
|
|
".cache/paper-proof/plugins/MineGeo/export/terrain.json.gz",
|
|
`${directory}/terrain.json.gz`,
|
|
);
|
|
const server = spawn(
|
|
process.execPath,
|
|
[
|
|
"node_modules/next/dist/bin/next",
|
|
"start",
|
|
"--hostname",
|
|
"127.0.0.1",
|
|
"--port",
|
|
"3100",
|
|
],
|
|
{ stdio: ["ignore", "pipe", "pipe"], windowsHide: true },
|
|
);
|
|
let serverLog = "";
|
|
server.stdout.on("data", (data) => (serverLog += data));
|
|
server.stderr.on("data", (data) => (serverLog += data));
|
|
const stopped = new Promise<void>((resolve) =>
|
|
server.on("exit", () => resolve()),
|
|
);
|
|
const request = (path: string, body?: unknown, authenticated = true) =>
|
|
fetch(origin + "/api/v1/" + path, {
|
|
method: body === undefined ? "GET" : "POST",
|
|
headers: {
|
|
...(authenticated ? { authorization: "Bearer " + account.token } : {}),
|
|
"content-type": "application/json",
|
|
},
|
|
body: body === undefined ? undefined : JSON.stringify(body),
|
|
});
|
|
let datasetId: string | undefined, serverId: string | undefined;
|
|
try {
|
|
let ready = false;
|
|
for (let i = 0; i < 60; i++) {
|
|
try {
|
|
const response = await fetch(origin + "/api/v1/health");
|
|
if (response.ok) {
|
|
ready = true;
|
|
break;
|
|
}
|
|
} catch {}
|
|
await new Promise((r) => setTimeout(r, 1000));
|
|
}
|
|
assert.ok(ready, "Production server and database must become healthy");
|
|
for (const path of [
|
|
"/",
|
|
"/servers",
|
|
"/downloads",
|
|
"/fonts/VT323-Regular.ttf",
|
|
"/images/example-map.webp",
|
|
"/tools/plugin/minegeo-plugin-1.21.11.jar",
|
|
"/tools/mod/minegeo-fabric-1.21.11.jar",
|
|
]) {
|
|
const response = await fetch(origin + path);
|
|
assert.equal(response.status, 200, path);
|
|
const bytes = await response.arrayBuffer();
|
|
assert.ok(bytes.byteLength > 0);
|
|
if (path === "/") {
|
|
const html = new TextDecoder().decode(bytes);
|
|
assert.ok(html.includes("blocky world"));
|
|
assert.ok(html.includes("COMMUNITY MAPS"));
|
|
}
|
|
console.log("HTTP 200 " + path);
|
|
}
|
|
const child = spawn(
|
|
"java",
|
|
[
|
|
"--class-path",
|
|
"public/tools/plugin/minegeo-plugin-1.21.11.jar",
|
|
"tests/java/UploadSmoke.java",
|
|
origin,
|
|
directory,
|
|
".data/test-account.json",
|
|
],
|
|
{ stdio: ["ignore", "pipe", "pipe"], windowsHide: true },
|
|
);
|
|
let javaLog = "";
|
|
child.stdout.on("data", (data) => {
|
|
javaLog += data;
|
|
process.stdout.write(data);
|
|
});
|
|
child.stderr.on("data", (data) => (javaLog += data));
|
|
const code = await new Promise<number | null>((resolve) =>
|
|
child.on("exit", resolve),
|
|
);
|
|
assert.equal(code, 0, javaLog);
|
|
const ledger = JSON.parse(
|
|
await readFile(`${directory}/.upload-state.json`, "utf8"),
|
|
);
|
|
datasetId = ledger.datasetId;
|
|
const draftResponse = await request("datasets/" + datasetId);
|
|
assert.equal(draftResponse.status, 200);
|
|
const draft = await draftResponse.json();
|
|
serverId = draft.server_id;
|
|
assert.ok(["processing", "draft"].includes(draft.state));
|
|
if (draft.state === "processing")
|
|
assert.ok(
|
|
draft.previews.every((p: any) => p.image === null),
|
|
"Pending media must not be signed",
|
|
);
|
|
const anonymous = await request("datasets/" + datasetId, undefined, false);
|
|
assert.ok(anonymous.status >= 400, "Private draft must reject guests");
|
|
if (draft.state === "processing") {
|
|
const cancel = await request("datasets/" + datasetId + "/cancel", {});
|
|
assert.equal(cancel.status, 200, await cancel.text());
|
|
}
|
|
// A never-finalized upload has no queue row: cancellation must still work.
|
|
const created = await request("uploads", { manifest });
|
|
assert.equal(created.status, 200);
|
|
const empty = await created.json();
|
|
const cancelled = await request(
|
|
"datasets/" + empty.datasetId + "/cancel",
|
|
{},
|
|
);
|
|
assert.equal(cancelled.status, 200, await cancelled.text());
|
|
const cleanup = await db
|
|
.from("datasets")
|
|
.delete()
|
|
.eq("id", empty.datasetId)
|
|
.eq("owner_id", account.id);
|
|
if (cleanup.error) throw cleanup.error;
|
|
await writeFile(
|
|
"artifacts-api-smoke.json",
|
|
JSON.stringify(
|
|
{
|
|
passed: true,
|
|
checks: [
|
|
"production homepage/catalog/downloads/assets",
|
|
"Java uploader to real Supabase Storage",
|
|
"private draft before rendered assets exist",
|
|
"guests denied private drafts",
|
|
"cancel queued processing",
|
|
"cancel unfinished upload without a queue row",
|
|
],
|
|
},
|
|
null,
|
|
2,
|
|
),
|
|
);
|
|
console.log("REAL STORAGE / API SMOKE PASSED");
|
|
} finally {
|
|
if (!datasetId) {
|
|
try {
|
|
datasetId = JSON.parse(
|
|
await readFile(`${directory}/.upload-state.json`, "utf8"),
|
|
).datasetId;
|
|
} catch {}
|
|
}
|
|
if (datasetId) {
|
|
const record = await db
|
|
.from("datasets")
|
|
.select("server_id")
|
|
.eq("id", datasetId)
|
|
.eq("owner_id", account.id)
|
|
.maybeSingle();
|
|
serverId = record.data?.server_id ?? serverId;
|
|
await db.storage
|
|
.from("minegeo-raw")
|
|
.remove(
|
|
manifest.assets.map((a: any) => `${account.id}/${datasetId}/${a.path}`),
|
|
);
|
|
const deleted = await db
|
|
.from("datasets")
|
|
.delete()
|
|
.eq("id", datasetId)
|
|
.eq("owner_id", account.id);
|
|
if (deleted.error)
|
|
console.error("Test dataset cleanup: " + deleted.error.message);
|
|
}
|
|
if (serverId) {
|
|
const remaining = await db
|
|
.from("datasets")
|
|
.select("id")
|
|
.eq("server_id", serverId)
|
|
.limit(1);
|
|
if (remaining.data?.length === 0)
|
|
await db
|
|
.from("catalog_servers")
|
|
.delete()
|
|
.eq("id", serverId)
|
|
.eq("name", "MineGeo API regression");
|
|
}
|
|
server.kill();
|
|
await stopped;
|
|
await writeFile(`${directory}/server.log`, serverLog);
|
|
}
|