import { AsyncAdoptionScope } from '../src/lib/asyncOwnership.ts'; import { INITIAL_LOCAL_NETWORK_SHARING_SNAPSHOT, LocalNetworkSharingAsyncScope, type LocalNetworkSharingSnapshot, displayedSharingTarget, isLocalNetworkSharingActive, localNetworkSharingNotice, newestLocalNetworkSharingSnapshot, } from '../src/lib/localNetworkSharing.ts'; const assert = (condition: boolean, message: string) => { if (!condition) throw new Error(message); }; const assertEquals = (actual: T, expected: T, message: string) => { if (JSON.stringify(actual) !== JSON.stringify(expected)) { throw new Error( `${message}: expected ${JSON.stringify(expected)}, got ${JSON.stringify(actual)}`, ); } }; const snapshot = ( revision: number, overrides: Partial = {}, ): LocalNetworkSharingSnapshot => ({ ...INITIAL_LOCAL_NETWORK_SHARING_SNAPSHOT, revision, persistenceProblem: null, ...overrides, }); Deno.test('sharing bootstrap remains visibly fail-closed if listener and query fail', () => { assert(!displayedSharingTarget(INITIAL_LOCAL_NETWORK_SHARING_SNAPSHOT), 'fallback switch'); assert( !isLocalNetworkSharingActive(INITIAL_LOCAL_NETWORK_SHARING_SNAPSHOT), 'fallback network admission', ); assertEquals( localNetworkSharingNotice(INITIAL_LOCAL_NETWORK_SHARING_SNAPSHOT), 'Local network sharing is off because its setting could not be loaded.', 'fallback copy', ); }); Deno.test('delayed sharing query cannot overwrite a newer listener event', () => { const delayedQuery = snapshot(1, { enabled: false, phase: 'disabled' }); const event = snapshot(2, { enabled: true, phase: 'enabled' }); const afterEvent = newestLocalNetworkSharingSnapshot( INITIAL_LOCAL_NETWORK_SHARING_SNAPSHOT, event, ); assertEquals( newestLocalNetworkSharingSnapshot(afterEvent, delayedQuery), event, 'newer listener event must win', ); assertEquals( newestLocalNetworkSharingSnapshot(event, { ...event, enabled: false }), event, 'equal revisions must not rewrite state', ); }); Deno.test('pending policy target is distinct from effective network activity', () => { const waiting = snapshot(1, { enabled: true, phase: 'waitingForGameDirectory', }); assert(displayedSharingTarget(waiting), 'waiting policy should remain checked'); assert(!isLocalNetworkSharingActive(waiting), 'waiting policy must not claim active sharing'); const disabling = snapshot(2, { enabled: true, pendingTarget: false, phase: 'enabled', }); assert(!displayedSharingTarget(disabling), 'pending explicit off target should drive switch'); assert( !isLocalNetworkSharingActive(disabling), 'an admitted off target must close network actions before the core event arrives', ); }); Deno.test('sharing notices use exact redacted state copy', () => { assertEquals( localNetworkSharingNotice(snapshot(1, { enabled: false, phase: 'disabled' })), 'Local network sharing is off. Nearby devices cannot browse or request games from this library.', 'disabled copy', ); assertEquals( localNetworkSharingNotice(snapshot(2, { phase: 'waitingForGameDirectory' })), 'Local network sharing will start after you choose a game folder.', 'waiting copy', ); assertEquals( localNetworkSharingNotice(snapshot(3, { enabled: true, pendingTarget: false, phase: 'enabled', })), 'Stopping Local network sharing…', 'pending-off copy before core Disabling event', ); assertEquals( localNetworkSharingNotice(snapshot(4, { enabled: false, phase: 'disabled', persistenceProblem: 'load', })), 'Local network sharing is off because its setting could not be loaded.', 'load failure copy', ); assertEquals( localNetworkSharingNotice(snapshot(5, { enabled: false, phase: 'disabled', persistenceProblem: 'save', })), 'Local network sharing is off, but this setting could not be saved.', 'save failure copy', ); }); Deno.test('sharing mutations execute in admission order', async () => { const adoption = new AsyncAdoptionScope(); const scope = new LocalNetworkSharingAsyncScope(() => {}, adoption); const order: string[] = []; let releaseFirst = () => {}; const firstGate = new Promise(resolve => { releaseFirst = resolve; }); const first = scope.applyMutationIfActive(async () => { order.push('first-start'); await firstGate; order.push('first-finish'); return 'first'; }, value => order.push(`${value}-apply`)); const second = scope.applyMutationIfActive(async () => { order.push('second-start'); return 'second'; }, value => order.push(`${value}-apply`)); await Promise.resolve(); assertEquals(order, ['first-start'], 'second mutation must remain queued'); releaseFirst(); await Promise.all([first, second]); assertEquals( order, ['first-start', 'first-finish', 'first-apply', 'second-start', 'second-apply'], 'mutation order', ); await scope.dispose(); }); Deno.test('failed mutation does not poison its admitted successor', async () => { const adoption = new AsyncAdoptionScope(); const scope = new LocalNetworkSharingAsyncScope(() => {}, adoption); const order: string[] = []; const first = scope.applyMutationIfActive(async () => { order.push('first'); throw new Error('injected'); }, () => order.push('unexpected-apply')); const second = scope.applyMutationIfActive(async () => { order.push('second'); return true; }, () => order.push('second-apply')); await first.catch(() => false); assert(await second, 'successor should still publish'); assertEquals(order, ['first', 'second', 'second-apply'], 'successor ordering'); await scope.dispose(); }); Deno.test('sharing scope drains admitted mutation and rejects late work on dispose', async () => { const adoption = new AsyncAdoptionScope(); const scope = new LocalNetworkSharingAsyncScope(() => {}, adoption); let release = () => {}; const gate = new Promise(resolve => { release = resolve; }); let published = false; const admitted = scope.applyMutationIfActive(async () => { await gate; return true; }, () => { published = true; }); const disposal = scope.dispose(); assert( !await scope.applyMutationIfActive(async () => true, () => {}), 'dispose must close mutation admission', ); release(); await Promise.all([admitted, disposal]); assert(!published, 'disposed scope must suppress late React publication'); });