import { expect, test } from "bun:test"; import { generateServiceWorker } from "../src/index.ts"; function worker(fetcher: (r:any)=>Promise, failStore = false) { const listeners: Record = {}; const saved: string[] = []; const cache = {match:async()=>undefined,put:async(r:any)=>{if(failStore)throw Error("quota");saved.push(r.url)}}; const self = {location:{origin:"https://account.example.test"},addEventListener:(name:string,fn:Function)=>listeners[name]=fn,skipWaiting(){},clients:{claim:async()=>{}}}; new Function("self","caches","fetch",generateServiceWorker())(self,{open:async()=>cache,keys:async()=>[],delete:async()=>true},fetcher); return {saved, async fetch(path:string, mode="cors") { let result:Promise|undefined; const req = new Request(new URL(path,self.location.origin)); Object.defineProperty(req,"mode",{value:mode}); listeners.fetch({request:req,respondWith:(r:Promise)=>result=r,waitUntil:()=>{}}); return result; }}; } test("handoffs and API calls bypass service worker interception", async()=>{ const w=worker(async()=>{throw Error("must not intercept")}); expect(await w.fetch("/organization-add?sso_code=one-time")).toBeUndefined(); expect(await w.fetch("/api/account")).toBeUndefined(); }); test("private responses and navigations never enter cache", async()=>{ const w=worker(async()=>new Response("private",{headers:{"cache-control":"private, no-store"}})); expect(await (await w.fetch("/profile"))?.text()).toBe("private"); expect(w.saved).toEqual([]); const nav=worker(async()=>new Response("personal page")); await nav.fetch("/profile","navigate"); expect(nav.saved).toEqual([]); }); test("cache quota errors do not turn successful requests into network errors", async()=>{ const w=worker(async()=>new Response("asset"),true); expect(await (await w.fetch("/app.js"))?.text()).toBe("asset"); }); test("offline without a cached fallback always returns a Response", async()=>{ const w=worker(async()=>{throw Error("offline")}); expect((await w.fetch("/profile","navigate"))?.status).toBe(503); }); test("public static assets remain cacheable", async()=>{ const w=worker(async()=>new Response("asset",{headers:{"cache-control":"public, max-age=3600"}})); await w.fetch("/app.js"); expect(w.saved).toHaveLength(1); });