Skip to main content
Module

x/kv_toolbox/blob.test.ts

Utilities for working with Deno KV πŸ¦•πŸ—οΈ
Go to Latest
File
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558
import { assert, assertEquals, assertRejects, setup, teardown, timingSafeEqual,} from "./_test_util.ts";
import { get, getAsBlob, getAsJSON, getAsResponse, getAsStream, getMeta, remove, set, toBlob, toJSON, toValue,} from "./blob.ts";import { keys } from "./keys.ts";
Deno.test({ name: "set - sets a blob value", async fn() { const kv = await setup(); const blob = new Uint8Array(65_536); window.crypto.getRandomValues(blob); const res = await set(kv, ["hello"], blob); assert(res.ok); assert(res.versionstamp); const actual = await keys(kv, { prefix: ["hello"] }); assertEquals(actual, [ ["hello", "__kv_toolbox_blob__", 1], ["hello", "__kv_toolbox_blob__", 2], ["hello", "__kv_toolbox_meta__"], ]); return teardown(); },});
Deno.test({ name: "set - sets a blob value as a stream", async fn() { const kv = await setup(); const data = new Uint8Array(65_536); window.crypto.getRandomValues(data); const blob = new Blob([data]); const res = await set(kv, ["hello"], blob.stream()); assert(res.ok); const actual = await keys(kv, { prefix: ["hello"] }); assertEquals(actual, [ ["hello", "__kv_toolbox_blob__", 1], ["hello", "__kv_toolbox_blob__", 2], ["hello", "__kv_toolbox_meta__"], ]); return teardown(); },});
Deno.test({ name: "set - sets a blob value as a blob", async fn() { const kv = await setup(); const data = new Uint8Array(65_536); window.crypto.getRandomValues(data); const blob = new Blob([data], { type: "application/octet-stream" }); const res = await set(kv, ["hello"], blob); assert(res.ok); const actual = await keys(kv, { prefix: ["hello"] }); assertEquals(actual, [ ["hello", "__kv_toolbox_blob__", 1], ["hello", "__kv_toolbox_blob__", 2], ["hello", "__kv_toolbox_meta__"], ]); const metaEntry = await kv.get(["hello", "__kv_toolbox_meta__"]); assertEquals(metaEntry.value, { kind: "blob", type: "application/octet-stream", size: 65_536, }); return teardown(); },});
Deno.test({ name: "set - sets a blob value as a file", async fn() { const kv = await setup(); const data = new Uint8Array(65_536); window.crypto.getRandomValues(data); const blob = new File([data], "test.bin", { type: "application/octet-stream", lastModified: 12345678, }); const res = await set(kv, ["hello"], blob); assert(res.ok); const actual = await keys(kv, { prefix: ["hello"] }); assertEquals(actual, [ ["hello", "__kv_toolbox_blob__", 1], ["hello", "__kv_toolbox_blob__", 2], ["hello", "__kv_toolbox_meta__"], ]); const metaEntry = await kv.get(["hello", "__kv_toolbox_meta__"]); assertEquals(metaEntry.value, { kind: "file", type: "application/octet-stream", name: "test.bin", lastModified: 12345678, size: 65_536, }); return teardown(); },});
Deno.test({ name: "set - replacing value sizes keys properly", async fn() { const kv = await setup(); const blob = new Uint8Array(65_536); window.crypto.getRandomValues(blob); const res = await set(kv, ["hello"], blob); assert(res.ok); const actual = await keys(kv, { prefix: ["hello"] }); assertEquals(actual, [ ["hello", "__kv_toolbox_blob__", 1], ["hello", "__kv_toolbox_blob__", 2], ["hello", "__kv_toolbox_meta__"], ]); const blob2 = blob.slice(0, 1_000); await set(kv, ["hello"], blob2); const actual2 = await keys(kv, { prefix: ["hello"] }); assertEquals(actual2, [ ["hello", "__kv_toolbox_blob__", 1], ["hello", "__kv_toolbox_meta__"], ]); return teardown(); },});
Deno.test({ name: "set - large blob", async fn() { const kv = await setup(); const blob = await Deno.readFile("./_fixtures/png-1mb.png"); const res = await set(kv, ["hello"], blob); assert(res.ok); const actual = await get(kv, ["hello"]); assert(actual.value); assert(timingSafeEqual(actual.value, blob)); return teardown(); },});
Deno.test({ name: "set - very large blob", async fn() { const kv = await setup(); const blob = await Deno.readFile("./_fixtures/mp4-7mb.mp4"); const res = await set(kv, ["hello"], blob); assert(res.ok); const actual = await get(kv, ["hello"]); assert(actual.value); assert(timingSafeEqual(actual.value, blob)); return teardown(); },});
Deno.test({ name: "set - rejects TypeError with invalid value", async fn() { const kv = await setup(); const blob = "kv-toolbox".repeat(1000); await assertRejects( // @ts-ignore to make the test type check () => set(kv, ["hello"], blob), "Blob must be typed array, array buffer, ReadableStream, Blob, or File", ); return teardown(); },});
Deno.test({ name: "get - assembles blob value as array buffer", async fn() { const kv = await setup(); const blob = new Uint8Array(65_536); window.crypto.getRandomValues(blob); await set(kv, ["hello"], blob); const actual = await get(kv, ["hello"]); assert(actual.value); assert(timingSafeEqual(actual.value, blob)); return teardown(); },});
Deno.test({ name: "get - option stream streams blob value", async fn() { const kv = await setup(); const blob = new Uint8Array(65_536); window.crypto.getRandomValues(blob); await set(kv, ["hello"], blob); const entry = await get(kv, ["hello"], { stream: true }); assert(entry.value); let count = 0; for await (const _ of entry.value) { count++; } assertEquals(count, 2); return teardown(); },});
Deno.test({ name: "getAsStream - streams blob value", async fn() { const kv = await setup(); const blob = new Uint8Array(65_536); window.crypto.getRandomValues(blob); await set(kv, ["hello"], blob); const stream = getAsStream(kv, ["hello"]); let count = 0; for await (const _ of stream) { count++; } assertEquals(count, 2); return teardown(); },});
Deno.test({ name: "getAsResponse - set response correctly", async fn() { const kv = await setup(); const blob = new Blob( [`<DOCTYPE! html><html><body>Hello!</body></html>`], { type: "text/html" }, ); await set(kv, ["index.html"], blob); const actual = await getAsResponse(kv, ["index.html"]); assertEquals(actual.headers.get("content-type"), "text/html"); assertEquals(actual.headers.get("content-length"), "47"); assertEquals([...actual.headers].length, 2); assertEquals(actual.status, 200); assertEquals(actual.statusText, "OK"); assertEquals( await actual.text(), `<DOCTYPE! html><html><body>Hello!</body></html>`, ); return teardown(); },});
Deno.test({ name: "getAsResponse - missing entry", async fn() { const kv = await setup(); const actual = await getAsResponse(kv, ["index.html"]); assertEquals([...actual.headers].length, 0); assertEquals(actual.status, 404); assertEquals(actual.statusText, "Not Found"); return teardown(); },});
Deno.test({ name: "getAsResponse - missing entry uses options", async fn() { const kv = await setup(); const actual = await getAsResponse(kv, ["index.html"], { notFoundBody: "not found", notFoundHeaders: { "content-type": "text/plain" }, }); assertEquals(actual.headers.get("content-type"), "text/plain"); assertEquals([...actual.headers].length, 1); assertEquals(actual.status, 404); assertEquals(actual.statusText, "Not Found"); assertEquals(await actual.text(), "not found"); return teardown(); },});
Deno.test({ name: "getAsResponse - processes headers init", async fn() { const kv = await setup(); const blob = new Blob( [`<DOCTYPE! html><html><body>Hello!</body></html>`], { type: "text/html" }, ); await set(kv, ["index.html"], blob); const actual = await getAsResponse(kv, ["index.html"], { headers: { "X-KV-Toolbox": "custom" }, }); assertEquals(actual.headers.get("content-type"), "text/html"); assertEquals(actual.headers.get("content-length"), "47"); assertEquals(actual.headers.get("x-kv-toolbox"), "custom"); assertEquals([...actual.headers].length, 3); return teardown(); },});
Deno.test({ name: "getAsResponse - contentDisposition is true", async fn() { const kv = await setup(); const data = await Deno.readFile("./_fixtures/png-1mb.png"); const stats = await Deno.stat("./_fixtures/png-1mb.png"); const file = new File([data], "png-1mb.png", { lastModified: stats.mtime?.getTime(), type: "image/png", }); await set(kv, ["hello"], file); const actual = await getAsResponse(kv, ["hello"], { contentDisposition: true, }); assertEquals(actual.headers.get("content-type"), "image/png"); assertEquals(actual.headers.get("content-length"), "1050986"); assertEquals( actual.headers.get("content-disposition"), 'attachment; filename="png-1mb.png"', ); assertEquals([...actual.headers].length, 3); return teardown(); },});
Deno.test({ name: "getAsJSON - handles array buffer like", async fn() { const kv = await setup(); const u8 = new Uint8Array(65_536); window.crypto.getRandomValues(u8); await set(kv, ["hello"], u8); const json = await getAsJSON(kv, ["hello"]); assert(json); assertEquals(json.parts.length, 2); assertEquals(json.meta, { kind: "buffer", size: 65_536 }); return teardown(); },});
Deno.test({ name: "getAsJSON - handles blob", async fn() { const kv = await setup(); const u8 = new Uint8Array(65_536); window.crypto.getRandomValues(u8); await set( kv, ["hello"], new Blob([u8], { type: "application/octet-stream" }), ); const json = await getAsJSON(kv, ["hello"]); assert(json); assertEquals(json.parts.length, 2); assertEquals(json.meta, { kind: "blob", type: "application/octet-stream", size: 65_536, }); return teardown(); },});
Deno.test({ name: "getAsJSON - handles file", async fn() { const kv = await setup(); const u8 = new Uint8Array(65_536); window.crypto.getRandomValues(u8); await set( kv, ["hello"], new File([u8], "test.bin", { type: "application/octet-stream", lastModified: 1711349710546, }), ); const json = await getAsJSON(kv, ["hello"]); assert(json); assertEquals(json.parts.length, 2); assertEquals(json.meta, { kind: "file", type: "application/octet-stream", name: "test.bin", lastModified: 1711349710546, size: 65_536, }); return teardown(); },});
Deno.test({ name: "getMeta", async fn() { const kv = await setup(); const u8 = new Uint8Array(65_536); window.crypto.getRandomValues(u8); await set(kv, ["hello"], u8); const meta = await getMeta(kv, ["hello"]); assert(meta); assertEquals(meta.value, { kind: "buffer", size: 65_536 }); return teardown(); },});
Deno.test({ name: "toJSON/toValue - File", async fn() { const u8 = new Uint8Array(65_536); window.crypto.getRandomValues(u8); const json = await toJSON( new File([u8], "test.bin", { type: "application/octet-stream", lastModified: 1711349710546, }), ); assertEquals(json.meta, { kind: "file", type: "application/octet-stream", name: "test.bin", lastModified: 1711349710546, }); assertEquals(json.parts.length, 2); const value = toValue(json); assert(value instanceof File); assertEquals((await value.arrayBuffer()).byteLength, 65_536); assertEquals(value.name, "test.bin"); assertEquals(value.lastModified, 1711349710546); assertEquals(value.type, "application/octet-stream"); },});
Deno.test({ name: "toJSON/toValue - Blob", async fn() { const u8 = new Uint8Array(65_536); window.crypto.getRandomValues(u8); const json = await toJSON( new Blob([u8], { type: "application/octet-stream" }), ); assertEquals(json.meta, { kind: "blob", type: "application/octet-stream" }); assertEquals(json.parts.length, 2); const value = toValue(json); assert(value instanceof Blob); assert(!(value instanceof File)); assertEquals((await value.arrayBuffer()).byteLength, 65_536); assertEquals(value.type, "application/octet-stream"); },});
Deno.test({ name: "toJSON/toValue - buffer", async fn() { const u8 = new Uint8Array(65_536); window.crypto.getRandomValues(u8); const json = await toJSON(u8); assertEquals(json.meta, { kind: "buffer" }); assertEquals(json.parts.length, 2); const value = toValue(json); assert(value instanceof Uint8Array); assertEquals(value.byteLength, 65_536); },});
Deno.test({ name: "set/get - files", async fn() { const kv = await setup(); const data = await Deno.readFile("./_fixtures/png-1mb.png"); const stats = await Deno.stat("./_fixtures/png-1mb.png"); const file = new File([data], "png-1mb.png", { lastModified: stats.mtime?.getTime(), type: "image/png", }); await set(kv, ["hello"], file); const actual = await get(kv, ["hello"], { blob: true }); assert(actual.value instanceof File); assertEquals(actual.value.name, "png-1mb.png"); assertEquals(actual.value.type, "image/png"); assertEquals(actual.value.lastModified, file.lastModified); assert( timingSafeEqual( await actual.value.arrayBuffer(), await file.arrayBuffer(), ), ); return teardown(); },});
Deno.test({ name: "set/getAsBlob - files", async fn() { const kv = await setup(); const data = await Deno.readFile("./_fixtures/png-1mb.png"); const stats = await Deno.stat("./_fixtures/png-1mb.png"); const file = new File([data], "png-1mb.png", { lastModified: stats.mtime?.getTime(), type: "image/png", }); await set(kv, ["hello"], file); const actual = await getAsBlob(kv, ["hello"]); assert(actual instanceof File); assertEquals(actual.name, "png-1mb.png"); assertEquals(actual.type, "image/png"); assertEquals(actual.lastModified, file.lastModified); assert( timingSafeEqual(await actual.arrayBuffer(), await file.arrayBuffer()), ); return teardown(); },});
Deno.test({ name: "remove - deletes a blob value", async fn() { const kv = await setup(); const data = new Uint8Array(65_536); window.crypto.getRandomValues(data); await set(kv, ["hello"], new Blob([data])); assertEquals((await keys(kv, { prefix: ["hello"] })).length, 3); await remove(kv, ["hello"]); assertEquals((await keys(kv, { prefix: ["hello"] })).length, 0); return teardown(); },});
Deno.test({ name: "remove - non-existent key is a noop", async fn() { const kv = await setup(); await remove(kv, ["hello"]); return teardown(); },});
Deno.test({ name: "toBlob() - default type", async fn() { const actual = toBlob("some sort of string"); assertEquals(actual.type, "text/plain"); assertEquals(await actual.text(), "some sort of string"); },});
Deno.test({ name: "toBlob() - provided type", fn() { const actual = toBlob("some sort of string", "text/html"); assertEquals(actual.type, "text/html"); },});