1// Package contract tests for the framework reload budget: counting, the2// refusal threshold, the settled-boot reset, and storage-less degradation.3import {4 noteReloadAttempt,5 type ReloadCounterStorage,6 resetReloadAttempts,7} from "./reload-guard.ts";8import { anchorAppId } from "./data-sink.ts";9import { assert } from "./test-assert.ts";1011const test = (globalThis as unknown as {12 Deno: { test(name: string, body: () => void | Promise<void>): void };13}).Deno.test;1415function memoryStorage(): ReloadCounterStorage {16 const map = new Map<string, string>();17 return {18 getItem: (key) => map.get(key) ?? null,19 setItem: (key, value) => void map.set(key, value),20 removeItem: (key) => void map.delete(key),21 };22}2324test("two consecutive reloads are allowed and the third is refused", () => {25 const storage = memoryStorage();26 const first = noteReloadAttempt(storage);27 assert(first.allowed && first.count === 1, "the first framework reload proceeds");28 const second = noteReloadAttempt(storage);29 assert(second.allowed && second.count === 2, "the second framework reload proceeds");30 const third = noteReloadAttempt(storage);31 assert(!third.allowed && third.count === 3, "an unbroken third reload is a cycle");32});3334test("a settled boot resets the budget", () => {35 const storage = memoryStorage();36 noteReloadAttempt(storage);37 noteReloadAttempt(storage);38 resetReloadAttempts(storage);39 const next = noteReloadAttempt(storage);40 assert(next.allowed && next.count === 1, "the budget restarts after a settled boot");41});4243test("a garbage counter value restarts the count instead of poisoning it", () => {44 const storage = memoryStorage();45 storage.setItem(`lofi:reload-count:${anchorAppId}`, "not a number");46 const attempt = noteReloadAttempt(storage);47 assert(attempt.allowed && attempt.count === 1, "garbage must count as zero prior attempts");48});4950test("without session storage the budget degrades open", () => {51 const attempt = noteReloadAttempt(null);52 assert(attempt.allowed, "storage-less environments must not be blocked");53 resetReloadAttempts(null);54});