208 lines
6.4 KiB
JavaScript
208 lines
6.4 KiB
JavaScript
import test from "node:test";
|
|
import assert from "node:assert/strict";
|
|
import { access, mkdtemp, rm } from "node:fs/promises";
|
|
import { tmpdir } from "node:os";
|
|
import { join } from "node:path";
|
|
import { execFile, spawn } from "node:child_process";
|
|
import { promisify } from "node:util";
|
|
|
|
try {
|
|
await access(".output/server/index.mjs");
|
|
} catch {
|
|
await promisify(execFile)("npm", ["run", "build"], { stdio: "inherit" });
|
|
}
|
|
const port = 19000 + Math.floor(Math.random() * 500);
|
|
const storage = await mkdtemp(join(tmpdir(), "quickdrop-http-"));
|
|
const child = spawn(process.execPath, [".output/server/index.mjs"], {
|
|
env: {
|
|
...process.env,
|
|
NITRO_HOST: "127.0.0.1",
|
|
NITRO_PORT: String(port),
|
|
AUTH_PASSCODE: "smoke-passcode",
|
|
AUTH_SECRET: "smoke-secret-for-tests",
|
|
QUICKDROP_STORAGE_DIR: storage,
|
|
},
|
|
stdio: "ignore",
|
|
});
|
|
async function request(url, init) {
|
|
return fetch(`http://127.0.0.1:${port}${url}`, init);
|
|
}
|
|
async function waitForServer() {
|
|
for (let attempt = 0; attempt < 50; attempt++) {
|
|
try {
|
|
const response = await request("/api/auth/session");
|
|
if (response.status === 200) return;
|
|
} catch {}
|
|
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
}
|
|
throw new Error("built server did not start");
|
|
}
|
|
|
|
test("built server enforces auth and serves capability-only lease/chunk APIs", async () => {
|
|
try {
|
|
await waitForServer();
|
|
const reservationBody = JSON.stringify({
|
|
version: 2,
|
|
chunkSize: 8 * 1024 * 1024,
|
|
chunkCount: 1,
|
|
noncePrefix: "AQIDBAUGBwg",
|
|
expiresIn: "24h",
|
|
maxReads: 1,
|
|
});
|
|
assert.equal(
|
|
(
|
|
await request("/api/drops", {
|
|
method: "POST",
|
|
headers: { "content-type": "application/json" },
|
|
body: reservationBody,
|
|
})
|
|
).status,
|
|
401,
|
|
);
|
|
const login = await request("/api/auth/login", {
|
|
method: "POST",
|
|
headers: {
|
|
"content-type": "application/json",
|
|
origin: `http://127.0.0.1:${port}`,
|
|
},
|
|
body: JSON.stringify({ passcode: "smoke-passcode" }),
|
|
});
|
|
assert.equal(login.status, 204);
|
|
const cookie = login.headers.get("set-cookie").split(";", 1)[0];
|
|
const reserve = await request("/api/drops", {
|
|
method: "POST",
|
|
headers: {
|
|
"content-type": "application/json",
|
|
cookie,
|
|
origin: `http://127.0.0.1:${port}`,
|
|
},
|
|
body: reservationBody,
|
|
});
|
|
assert.equal(reserve.status, 201);
|
|
const created = await reserve.json();
|
|
const chunk = new Uint8Array(16);
|
|
const put = await request(`/api/drops/${created.id}/chunks/0`, {
|
|
method: "PUT",
|
|
headers: {
|
|
"content-type": "application/octet-stream",
|
|
cookie,
|
|
origin: `http://127.0.0.1:${port}`,
|
|
},
|
|
body: chunk,
|
|
});
|
|
assert.equal(put.status, 201);
|
|
const finalize = await request(`/api/drops/${created.id}/finalize`, {
|
|
method: "POST",
|
|
headers: {
|
|
"content-type": "application/octet-stream",
|
|
cookie,
|
|
origin: `http://127.0.0.1:${port}`,
|
|
},
|
|
body: new Uint8Array(17),
|
|
});
|
|
assert.equal(finalize.status, 200);
|
|
const descriptor = await request(`/api/drops/${created.id}`, {
|
|
headers: { "x-quickdrop-access": created.access },
|
|
});
|
|
assert.equal(descriptor.status, 200);
|
|
const first = await request(`/api/drops/${created.id}/leases`, {
|
|
method: "POST",
|
|
headers: { "x-quickdrop-access": created.access },
|
|
});
|
|
assert.equal(first.status, 201);
|
|
const lease = await first.json();
|
|
const second = await request(`/api/drops/${created.id}/leases`, {
|
|
method: "POST",
|
|
headers: { "x-quickdrop-access": created.access },
|
|
});
|
|
assert.equal(second.status, 410);
|
|
const reused = await request(`/api/drops/${created.id}/leases`, {
|
|
method: "POST",
|
|
headers: {
|
|
"x-quickdrop-access": created.access,
|
|
authorization: `Bearer ${lease.token}`,
|
|
},
|
|
});
|
|
assert.equal(reused.status, 200);
|
|
assert.equal((await reused.json()).token, lease.token);
|
|
const chunkGet = await request(`/api/drops/${created.id}/chunks/0`, {
|
|
headers: { authorization: `Bearer ${lease.token}` },
|
|
});
|
|
assert.equal(chunkGet.status, 200);
|
|
assert.equal((await chunkGet.arrayBuffer()).byteLength, 16);
|
|
const complete = await request(`/api/drops/${created.id}/complete`, {
|
|
method: "POST",
|
|
headers: { authorization: `Bearer ${lease.token}` },
|
|
});
|
|
assert.equal(complete.status, 204);
|
|
|
|
// Completion retries must also be idempotent when the drop remains (unlimited reads).
|
|
const unlimitedBody = JSON.stringify({
|
|
...JSON.parse(reservationBody),
|
|
maxReads: null,
|
|
});
|
|
const unlimitedReserve = await request("/api/drops", {
|
|
method: "POST",
|
|
headers: {
|
|
"content-type": "application/json",
|
|
cookie,
|
|
origin: `http://127.0.0.1:${port}`,
|
|
},
|
|
body: unlimitedBody,
|
|
});
|
|
assert.equal(unlimitedReserve.status, 201);
|
|
const unlimited = await unlimitedReserve.json();
|
|
assert.equal(
|
|
(
|
|
await request(`/api/drops/${unlimited.id}/chunks/0`, {
|
|
method: "PUT",
|
|
headers: {
|
|
"content-type": "application/octet-stream",
|
|
cookie,
|
|
origin: `http://127.0.0.1:${port}`,
|
|
},
|
|
body: chunk,
|
|
})
|
|
).status,
|
|
201,
|
|
);
|
|
assert.equal(
|
|
(
|
|
await request(`/api/drops/${unlimited.id}/finalize`, {
|
|
method: "POST",
|
|
headers: {
|
|
"content-type": "application/octet-stream",
|
|
cookie,
|
|
origin: `http://127.0.0.1:${port}`,
|
|
},
|
|
body: new Uint8Array(17),
|
|
})
|
|
).status,
|
|
200,
|
|
);
|
|
const unlimitedClaim = await request(`/api/drops/${unlimited.id}/leases`, {
|
|
method: "POST",
|
|
headers: { "x-quickdrop-access": unlimited.access },
|
|
});
|
|
assert.equal(unlimitedClaim.status, 201);
|
|
const unlimitedLease = await unlimitedClaim.json();
|
|
const completionInit = {
|
|
method: "POST",
|
|
headers: { authorization: `Bearer ${unlimitedLease.token}` },
|
|
};
|
|
assert.equal(
|
|
(await request(`/api/drops/${unlimited.id}/complete`, completionInit))
|
|
.status,
|
|
204,
|
|
);
|
|
assert.equal(
|
|
(await request(`/api/drops/${unlimited.id}/complete`, completionInit))
|
|
.status,
|
|
204,
|
|
);
|
|
} finally {
|
|
child.kill("SIGTERM");
|
|
await rm(storage, { recursive: true, force: true });
|
|
}
|
|
});
|