Files
bitvid/tests/storage-cors-preservation.test.mjs
T

141 lines
6.1 KiB
JavaScript

// Real storage helper + SDK wrapper; only SDK classes and S3 transport are fake.
import test from "node:test";
import assert from "node:assert/strict";
import { registerHooks } from "node:module";
const commandNames = ["S3Client", "HeadBucketCommand", "CreateBucketCommand", "GetBucketCorsCommand", "PutBucketCorsCommand"];
const hooks = registerHooks({
resolve(specifier, context, nextResolve) {
if (specifier.startsWith("https://esm.sh/@aws-sdk/client-s3@")) {
return { url: "offline:storage-cors-sdk", shortCircuit: true };
}
if (/^https?:/.test(specifier)) throw new Error("Network forbidden in CORS fixtures");
return nextResolve(specifier, context);
},
load(url, context, nextLoad) {
if (url === "offline:storage-cors-sdk") {
return { format: "module", shortCircuit: true, source: commandNames.map(name =>
`export class ${name} { constructor(input) { this.input = input; } }`
).join("\n") };
}
return nextLoad(url, context);
},
});
// The full runner disables network imports globally. Our hook supplies the SDK locally.
globalThis.__BITVID_DISABLE_NETWORK_IMPORTS__ = false;
const { ensureS3SdkLoaded } = await import("../js/storage/s3-client.js");
await ensureS3SdkLoaded();
const { ensureBucketCors } = await import("../js/storage/s3-multipart.js");
hooks.deregister();
globalThis.__BITVID_DISABLE_NETWORK_IMPORTS__ = true;
const first = "https://first.example.invalid";
const second = "https://second.example.invalid";
const third = "https://third.example.invalid";
const custom = { ID: "read-only", AllowedOrigins: [first], AllowedMethods: ["GET"], AllowedHeaders: ["Range"], ExposeHeaders: ["ETag"], MaxAgeSeconds: 75 };
const defaults = origins => ({ AllowedHeaders: ["*"], AllowedMethods: ["GET", "HEAD", "PUT", "POST", "DELETE", "OPTIONS"], AllowedOrigins: origins, ExposeHeaders: ["ETag", "Content-Length", "Content-Range", "Accept-Ranges"], MaxAgeSeconds: 3600 });
function transport(rules, readError) {
return {
policy: rules === null ? null : { CORSRules: structuredClone(rules) }, puts: 0, calls: [],
async send(command) {
const name = command.constructor.name;
this.calls.push(name);
assert.equal(command.input.Bucket, "offline-bucket");
if (name === "HeadBucketCommand") return {};
if (name === "GetBucketCorsCommand") {
if (readError) throw readError;
if (this.policy === null) throw Object.assign(new Error("No policy"), { name: "NoSuchCORSConfiguration" });
return structuredClone(this.policy);
}
if (name === "PutBucketCorsCommand") {
this.puts++;
this.policy = structuredClone(command.input.CORSConfiguration);
return {};
}
throw new Error(`Unexpected command ${name}`);
},
};
}
const ensure = (s3, origins) => ensureBucketCors({ s3, bucket: "offline-bucket", origins });
test("adding a second origin preserves the complete existing custom policy", async () => {
const s3 = transport([custom]);
await ensure(s3, [second]);
assert.deepEqual(s3.policy, { CORSRules: [custom, defaults([second])] });
assert.equal(s3.puts, 1);
});
test("wildcard and multiple custom rules keep their fields and order", async () => {
const wildcard = { ...custom, ID: "wildcard", AllowedOrigins: ["*"], AllowedHeaders: ["X-Custom"], AllowedMethods: ["HEAD"], ExtraField: "preserve" };
const s3 = transport([wildcard, custom]);
await ensure(s3, [second]);
assert.deepEqual(s3.policy, { CORSRules: [wildcard, custom, defaults([second])] });
});
test("duplicate origins and repeated calls are idempotent without another PUT", async () => {
const s3 = transport([custom]);
await ensure(s3, [second, second, null, ""]);
const once = structuredClone(s3.policy);
assert.deepEqual(once, { CORSRules: [custom, defaults([second])] });
await ensure(s3, [second, second]);
assert.deepEqual(s3.policy, once);
assert.equal(s3.puts, 1);
});
test("equivalent default rules cover origins across rules regardless of array order", async () => {
const existing = [defaults([first]), { ...defaults([second]), AllowedMethods: [...defaults([]).AllowedMethods].reverse() }];
const s3 = transport(existing);
await ensure(s3, [first, second]);
assert.deepEqual(s3.policy, { CORSRules: existing });
assert.equal(s3.puts, 0);
});
test("similar rules with different non-origin permissions do not cover a new request", async () => {
for (const override of [
{ AllowedMethods: ["GET"] },
{ AllowedHeaders: ["Range"] },
{ ExposeHeaders: ["ETag"] },
{ MaxAgeSeconds: 75 },
{ ExtraField: "preserve" },
]) {
const existing = { ...defaults([second]), ...override };
const s3 = transport([existing]);
await ensure(s3, [second]);
assert.deepEqual(s3.policy, { CORSRules: [existing, defaults([second])] });
assert.equal(s3.puts, 1);
}
});
test("only uncovered origins receive a separate rule", async () => {
const existing = [defaults([first]), custom];
const s3 = transport(existing);
await ensure(s3, [first, second, third]);
assert.deepEqual(s3.policy, { CORSRules: [...existing, defaults([second, third])] });
});
test("an explicit absent policy creates defaults and then skips repeated writes", async () => {
const s3 = transport(null);
await ensure(s3, [first]);
assert.deepEqual(s3.policy, { CORSRules: [defaults([first])] });
await ensure(s3, [first]);
assert.equal(s3.puts, 1);
});
test("all other policy read failures reject unchanged without PUT", async () => {
for (const error of [Object.assign(new Error("Denied"), { name: "AccessDenied" }), Object.assign(new Error("Not found"), { name: "NoSuchBucket", $metadata: { httpStatusCode: 404 } }), new Error("Transport failed")]) {
const s3 = transport([custom], error);
await assert.rejects(ensure(s3, [second]), actual => actual === error);
assert.deepEqual(s3.policy, { CORSRules: [custom] });
assert.equal(s3.puts, 0);
}
});
test("empty or missing origins perform no IO and preserve policy", async () => {
for (const origins of [[], undefined, [null, "", false]]) {
const s3 = transport([custom]);
await ensure(s3, origins);
assert.deepEqual(s3.calls, []);
assert.deepEqual(s3.policy, { CORSRules: [custom] });
}
});