Files
KoalaSync/extension/episode-lobby-background.test.mjs
T

88 lines
5.0 KiB
JavaScript

import fs from 'node:fs';
import path from 'node:path';
import { fileURLToPath } from 'node:url';
import { describe, expect, it } from 'vitest';
const extensionDir = path.dirname(fileURLToPath(import.meta.url));
const backgroundSource = fs.readFileSync(path.join(extensionDir, 'background.js'), 'utf8');
function sourceBetween(startNeedle, endNeedle) {
const start = backgroundSource.indexOf(startNeedle);
const end = backgroundSource.indexOf(endNeedle, start + startNeedle.length);
expect(start).toBeGreaterThan(-1);
expect(end).toBeGreaterThan(start);
return backgroundSource.slice(start, end);
}
describe('episode lobby completion races', () => {
it('does not read the cleared lobby after a remote ready completes it', () => {
const handler = sourceBetween('case EVENTS.EPISODE_READY:', 'case EVENTS.EPISODE_LOBBY_CANCEL:');
const snapshotIndex = handler.indexOf('const readyPeers = [...lobby.readyPeers]');
const roomUpdateIndex = handler.indexOf('currentRoom.activeLobby.readyPeers = readyPeers');
const completionIndex = handler.indexOf('checkEpisodeLobbyCompletion()');
expect(snapshotIndex).toBeGreaterThan(-1);
expect(roomUpdateIndex).toBeGreaterThan(snapshotIndex);
expect(completionIndex).toBeGreaterThan(roomUpdateIndex);
expect(handler.slice(completionIndex)).not.toContain('episodeLobby.readyPeers');
});
it('revalidates the lobby after awaiting settings for a local ready', () => {
const handler = sourceBetween("message.type === 'EPISODE_READY_LOCAL'", "message.type === 'TITLE_PRIVACY_CHANGED'");
const awaitIndex = handler.indexOf('const settings = await getSettings()');
const guardIndex = handler.indexOf('if (!isCurrentLobbyContext() || settings.roomId !== lobbyRoomId)');
const mutationIndex = handler.indexOf('lobby.readyPeers.push(peerId)');
expect(handler).toContain('const lobby = episodeLobby');
expect(handler).toContain('const isCurrentLobbyContext = () => episodeLobby === lobby');
expect(awaitIndex).toBeGreaterThan(-1);
expect(guardIndex).toBeGreaterThan(awaitIndex);
expect(mutationIndex).toBeGreaterThan(guardIndex);
});
it('clears the persisted room lobby when completion starts Force Sync', () => {
const execute = sourceBetween('function executeEpisodeLobby()', 'function checkEpisodeLobbyCompletion()');
const roomClearIndex = execute.indexOf('currentRoom.activeLobby = null');
const lobbyClearIndex = execute.indexOf('clearEpisodeLobbyState()');
expect(roomClearIndex).toBeGreaterThan(-1);
expect(lobbyClearIndex).toBeGreaterThan(roomClearIndex);
expect(execute).toContain('chrome.storage.session.set({ currentRoom })');
});
it('validates restored and authoritative lobby state before using readyPeers', () => {
expect(backgroundSource).toContain('function normalizeEpisodeLobby(value, fallbackCreatedAt = Date.now(), allowedPeerIds = null)');
expect(backgroundSource).toContain('data.currentRoom.activeLobby,');
expect(backgroundSource).toContain('const authoritativeLobby = normalizeEpisodeLobby(');
expect(backgroundSource).toContain('new Set(currentRoom.peers.map(candidate => candidate.peerId))');
});
it('rejects stale or unknown ready senders and correlates new ready frames to the lobby', () => {
const remoteReady = sourceBetween('case EVENTS.EPISODE_READY:', 'case EVENTS.EPISODE_LOBBY_CANCEL:');
expect(remoteReady).toContain('const senderPresent = currentRoom?.peers?.some');
expect(remoteReady).toContain('!sameEpisode(data.expectedTitle, lobby.expectedTitle)');
const localReady = sourceBetween("message.type === 'EPISODE_READY_LOCAL'", "message.type === 'TITLE_PRIVACY_CHANGED'");
expect(localReady).toContain('expectedTitle: lobby.expectedTitle');
});
it('adopts authoritative correction data after the relay rejects a competing lobby', () => {
const remoteLobby = sourceBetween('case EVENTS.EPISODE_LOBBY:', 'case EVENTS.EPISODE_READY:');
expect(remoteLobby).toContain('data.authoritative === true && Array.isArray(data.readyPeers)');
expect(remoteLobby).toContain('currentRoom.activeLobby = incomingLobby');
});
it('counts only ready peers who still participate in lobby completion', () => {
const completion = sourceBetween('function checkEpisodeLobbyCompletion()', 'function checkEpisodeLobbyPeerDeparture()');
expect(completion).toContain('const participatingPeerIds = new Set(peers');
expect(completion).toContain('participatingPeerIds.has(candidate)');
expect(completion).toContain('readyParticipatingCount >= participatingPeerIds.size');
});
it('re-evaluates or cancels a lobby when the local peer enters solo mode', () => {
const desync = sourceBetween("message.type === 'HCM_DESYNC_STATE'", "message.type === 'LEAVE_ROOM'");
expect(desync).toContain("cancelEpisodeLobby('Initiator entered solo mode')");
expect(desync).toContain('checkEpisodeLobbyCompletion()');
});
});