import test from "node:test"; import assert from "node:assert/strict"; import { R2_OBJECT_ENVELOPE_MAX_BODY_BYTES, R2_OBJECT_MAX_BYTES, R2_OBJECT_PUT_MAX_BODY_BYTES, base64Ceiling, tooLargeMessage, } from "../object-limits.js"; test("the cap is 100 MiB", () => { assert.equal(R2_OBJECT_MAX_BYTES, 100 * 1024 * 1024); }); // The cap is set independently of email's 25 MiB. This pins that they are not // the same number, so a future edit that "unifies" them fails here and has to // read the design note rather than silently coupling two unrelated limits. test("the cap is not email's attachment cap", () => { assert.notEqual(R2_OBJECT_MAX_BYTES, 25 * 1024 * 1024); }); test("base64Ceiling matches base64's 4-out-per-3-in expansion, rounded up", () => { assert.equal(base64Ceiling(0), 0); assert.equal(base64Ceiling(1), 4); assert.equal(base64Ceiling(3), 4); assert.equal(base64Ceiling(4), 8); assert.equal(base64Ceiling(6), 8); }); // The ceiling must never sit below what a legal at-cap object actually encodes // to, or a legitimate put would be rejected by its own encoding. test("base64Ceiling is never below Node's own base64 length", () => { for (const n of [0, 1, 2, 3, 4, 5, 100, 1023, 4096]) { const encoded = Buffer.alloc(n).toString("base64").length; assert.ok( base64Ceiling(n) >= encoded, `base64Ceiling(${n}) = ${base64Ceiling(n)} < ${encoded}`, ); } }); test("the put body ceiling admits a cap-sized object plus an envelope allowance", () => { assert.equal( R2_OBJECT_PUT_MAX_BODY_BYTES, base64Ceiling(R2_OBJECT_MAX_BYTES) + 64 * 1024, ); assert.ok(R2_OBJECT_PUT_MAX_BODY_BYTES > base64Ceiling(R2_OBJECT_MAX_BYTES)); }); // The allowance is for a key and JSON punctuation, not for smuggling a bigger // object: it must stay far below the cap it guards. test("the envelope allowance is negligible against the cap", () => { const allowance = R2_OBJECT_PUT_MAX_BODY_BYTES - base64Ceiling(R2_OBJECT_MAX_BYTES); assert.ok(allowance < R2_OBJECT_MAX_BYTES / 1000); }); test("the envelope limit is 64 KiB", () => { assert.equal(R2_OBJECT_ENVELOPE_MAX_BODY_BYTES, 64 * 1024); }); // The sizing rationale, pinned rather than left in prose. An object key is at // most 1,024 bytes and a bucket name at most 63 characters, so even a key whose // every byte takes a six-character \uXXXX escape leaves an order of magnitude of // headroom. A future edit that shrinks this toward a real envelope fails here. test("the envelope limit clears a worst-case escaped envelope with headroom", () => { const worstCase = 63 + 1024 * 6 + 64; // bucket + fully escaped key + punctuation assert.ok( R2_OBJECT_ENVELOPE_MAX_BODY_BYTES > worstCase * 5, `${R2_OBJECT_ENVELOPE_MAX_BODY_BYTES} leaves too little headroom over ${worstCase}`, ); }); // The counterpart to "the cap is not email's attachment cap" above, and the // opposite ruling for the opposite reason. That pair must stay apart because the // two numbers track different constraints and would drift for different reasons. // These two are one fact — how large can an envelope legitimately get — asked once // by put's allowance and once by the metadata routes' whole body, so they must // move together. This pins the composition; the test above pins the value. test("put's body ceiling is composed of the same envelope constant", () => { assert.equal( R2_OBJECT_PUT_MAX_BODY_BYTES, base64Ceiling(R2_OBJECT_MAX_BYTES) + R2_OBJECT_ENVELOPE_MAX_BODY_BYTES, ); }); test("the over-cap message names the object and the limit", () => { const msg = tooLargeMessage("photo.jpg", 150 * 1024 * 1024); assert.match(msg, /photo\.jpg/); assert.match(msg, /150\.0 MiB/); assert.match(msg, /100\.0 MiB/); }); // Code review — MiB alone rounds, so an object one byte over the cap rendered as // "100.0 MiB, which exceeds the 100.0 MiB limit": a message that contradicts // itself and reads as a bug rather than a rejection. The exact bytes are what // disambiguate it, so they are pinned rather than left to formatting taste. test("the message stays legible for an object one byte over the cap", () => { const msg = tooLargeMessage("photo.jpg", R2_OBJECT_MAX_BYTES + 1); assert.match(msg, /104857601 bytes/); assert.match(msg, /104857600 bytes/); assert.notEqual( msg.match(/100\.0 MiB \(104857601 bytes\)/), null, "the actual size must carry its exact byte count", ); });