clone

push a directory of html from the terminal and get a four-character url back. a deno cli on cloudflare

feat/site-feedback
nzip / cli / lib / publish_test.ts
publish_test.ts 3.5K
1import type { CommitResponse } from "@nzip/shared";2import type { Bundle } from "./bundle.ts";3import { publishBundle } from "./publish.ts";45function assert(value: unknown, message = "assertion failed"): asserts value {6 if (!value) throw new Error(message);7}8const bundle: Bundle = {9 manifest: { v: 1, files: {} },10 blobs: new Map([["one", new Uint8Array([1])], ["two", new Uint8Array([2])]]),11 totalBytes: 2,12 warnings: [],13};1415Deno.test("publish waits for in-flight uploads and never commits after an upload failure", async () => {16 let drained = false;17 let committed = false;18 let release!: () => void;19 const pending = new Promise<void>((resolve) => {20 release = resolve;21 });22 const result = publishBundle(23 {24 prepare: () => Promise.resolve({ manifestHash: "hash", missing: ["one", "two"] }),25 uploadBlob: async (hash) => {26 if (hash === "one") {27 release();28 throw new Error("upload failed");29 }30 await pending;31 drained = true;32 return { ok: true };33 },34 commit: () => {35 committed = true;36 return Promise.resolve({} as CommitResponse);37 },38 },39 bundle,40 { target: { vault: "home" } },41 );42 try {43 await result;44 throw new Error("expected failure");45 } catch (error) {46 assert(error instanceof Error && error.message === "upload failed");47 }48 assert(drained && !committed);49});5051Deno.test("publish cancellation between upload and commit prevents commit", async () => {52 const controller = new AbortController();53 let committed = false;54 try {55 await publishBundle(56 {57 prepare: () => Promise.resolve({ manifestHash: "hash", missing: ["one"] }),58 uploadBlob: () => {59 controller.abort();60 return Promise.resolve({ ok: true });61 },62 commit: () => {63 committed = true;64 return Promise.resolve({} as CommitResponse);65 },66 },67 bundle,68 { target: { vault: "home" } },69 { signal: controller.signal },70 );71 throw new Error("expected cancellation");72 } catch (error) {73 assert(error instanceof DOMException && error.name === "AbortError");74 }75 assert(!committed);76});7778Deno.test("progress observer failures do not misreport an accepted commit", async () => {79 const result = await publishBundle(80 {81 prepare: () => Promise.resolve({ manifestHash: "hash", missing: [] }),82 uploadBlob: () => {83 throw new Error("not needed");84 },85 commit: () => Promise.resolve({ address: "2001" } as CommitResponse),86 },87 bundle,88 { target: { vault: "home" } },89 {90 onProgress: () => {91 throw new Error("observer disconnected");92 },93 },94 );95 assert(result.address === "2001");96});9798Deno.test("publish fails loudly when an older server ignores the feedback flag", async () => {99 const bundle = {100 manifest: { v: 1 as const, files: {} },101 blobs: new Map<string, Uint8Array>(),102 totalBytes: 0,103 warnings: [],104 };105 const api = {106 prepare: () => Promise.resolve({ manifestHash: "a".repeat(64), missing: [] }),107 uploadBlob: () => Promise.resolve({ ok: true as const }),108 commit: () => Promise.resolve({ address: "2001", protected: true } as CommitResponse),109 };110 let message = "";111 try {112 await publishBundle(api, bundle, { target: { vault: "home" }, feedback: true });113 } catch (error) {114 message = (error as Error).message;115 }116 if (!message.includes("ignored feedback=true")) {117 throw new Error(`expected an old-server error, got ${JSON.stringify(message)}`);118 }119});