1import { describeCredentialOrigin, describeSyncState } from "./DeviceStatus.tsx";23// The report's contract after the sync-state integrity pass: a reader must be4// able to tell *why* nothing is syncing. Each blocked disposition names its5// cause, and the precedence puts the most specific cause first — an owner6// mismatch explains more than a store answer, a store refusal more than a7// missing sink.8Deno.test("describeSyncState names each blocked disposition first-class", () => {9 const base = {10 syncing: false,11 syncAvailable: false,12 ownerMismatch: false,13 storeState: "unchecked" as const,14 sinkUnopenable: false,15 };16 const cases: [Parameters<typeof describeSyncState>[0], string][] = [17 [{ ...base, ownerMismatch: true, syncing: true }, "another account"],18 [{ ...base, storeState: "no_schema", syncing: true }, "no schema"],19 [{ ...base, storeState: "ticket_rejected", syncing: true }, "ticket no longer accepted"],20 [{ ...base, sinkUnopenable: true }, "unopenable"],21 [{ ...base, syncing: true, syncAvailable: true }, "syncing to your account"],22 [{ ...base, syncAvailable: true }, "not yet backed up"],23 [base, "local-only"],24 ];25 for (const [input, needle] of cases) {26 const verdict = describeSyncState(input);27 if (!verdict.includes(needle)) {28 throw new Error(`disposition lost its cause: ${JSON.stringify(input)} → ${verdict}`);29 }30 }31});3233// The owner mismatch outranks every other explanation: transport is suppressed34// because of it, so store-level answers describe a connection that is not35// being attempted.36Deno.test("describeSyncState puts the owner mismatch above store answers", () => {37 const verdict = describeSyncState({38 syncing: true,39 syncAvailable: true,40 ownerMismatch: true,41 storeState: "no_schema",42 sinkUnopenable: false,43 });44 if (!verdict.includes("another account")) {45 throw new Error(`owner mismatch was outranked: ${verdict}`);46 }47});4849Deno.test("credential-origin status does not overstate API support", () => {50 const local = describeCredentialOrigin({51 status: "local-only",52 rpId: "localhost",53 action: "use the stable HTTPS origin",54 });55 if (!local.includes("local development only") || !local.includes("localhost")) {56 throw new Error(`local credential origin was overstated: ${local}`);57 }58 const stable = describeCredentialOrigin({59 status: "stable",60 rpId: "demo.lofi.host",61 action: "keep this hostname",62 });63 if (!stable.includes("stable") || !stable.includes("demo.lofi.host")) {64 throw new Error(`stable credential origin lost its hostname: ${stable}`);65 }66});