clone

a deno framework for local-first web apps that open instantly, work offline and sync when users choose

fix/175-template-quality-pass
lofi / package / runtime / auth-secret-lock_test.ts
auth-secret-lock_test.ts 3.0K
1import {2 type AuthSecretLockManager,3 authSecretLockName,4 withAuthSecretLock,5} from "./auth-secret-lock.ts";6import { assert } from "./test-assert.ts";78// An in-memory exclusive lock manager: requests on one name run one at a9// time, in arrival order, like tabs on one origin.10function memoryLocks(): AuthSecretLockManager {11 const queues = new Map<string, Promise<unknown>>();12 return {13 request(name, _options, callback) {14 const previous = queues.get(name) ?? Promise.resolve();15 const run = previous.then(() => callback(undefined));16 queues.set(name, run.catch(() => undefined));17 return run;18 },19 };20}2122// A store with the vendor's first-visit shape: a non-atomic check-then-write23// that yields between the check and the write, so unserialized concurrent24// callers mint rival secrets.25function checkThenWriteStore() {26 const state = { stored: null as string | null, minted: 0, active: 0, overlapped: false };27 return {28 state,29 async getOrCreate(): Promise<string> {30 state.active += 1;31 if (state.active > 1) state.overlapped = true;32 const checked = state.stored;33 await new Promise((resolve) => setTimeout(resolve, 1));34 let value = checked;35 if (value === null) {36 state.minted += 1;37 value = `secret-${state.minted}`;38 state.stored = value;39 }40 state.active -= 1;41 return value;42 },43 };44}4546Deno.test("concurrent first-boot creations serialize to one secret", async () => {47 const locks = memoryLocks();48 const store = checkThenWriteStore();49 const [first, second] = await Promise.all([50 withAuthSecretLock("app-1", store.getOrCreate, locks),51 withAuthSecretLock("app-1", store.getOrCreate, locks),52 ]);53 assert(!store.state.overlapped, "secret-store operations overlapped under the lock");54 assert(store.state.minted === 1, `expected one minted secret, received ${store.state.minted}`);55 assert(first === "secret-1" && second === "secret-1", "tabs resolved different secrets");56});5758Deno.test("unserialized check-then-write mints rival secrets (control)", async () => {59 const store = checkThenWriteStore();60 const [first, second] = await Promise.all([store.getOrCreate(), store.getOrCreate()]);61 assert(store.state.minted === 2, "control run was expected to race");62 assert(first !== second, "control run was expected to mint rival secrets");63});6465Deno.test("without a lock manager the operation runs directly", async () => {66 const store = checkThenWriteStore();67 const secret = await withAuthSecretLock("app-1", store.getOrCreate, undefined);68 assert(secret === "secret-1", "fallback path did not run the operation");69});7071Deno.test("lock names are app-scoped and normalized", () => {72 assert(authSecretLockName("demo.app/1") === "lofi-auth-secret-demo-app-1", "unexpected name");73 assert(74 authSecretLockName("a") !== authSecretLockName("b"),75 "different apps must not share a lock",76 );77 assert(authSecretLockName("") === "lofi-auth-secret-app", "empty app id needs a stable name");78});