mirror of
https://github.com/Shik3i/KoalaSync.git
synced 2026-08-30 12:29:27 +00:00
88 lines
5.0 KiB
JavaScript
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()');
|
|
});
|
|
});
|