import { afterEach, describe, expect, it } from 'vitest'; import { act } from '@testing-library/react'; import { parseGatewaySnapshot } from '@/api/gateway'; import { dispatchGatewayEvent, useGatewayStore } from '@/stores/gateway'; import { useSessionStore } from '@/stores/session'; import { makeReadySnapshot } from './helpers'; afterEach(() => { useSessionStore.getState().reset(); useGatewayStore.setState({ status: 'idle', heartbeatIntervalMs: null, sessionId: null, attempts: 0, invalidated: false, }); }); describe('session store', () => { it('applies a READY snapshot: guilds, channels and session id', () => { const snapshot = parseGatewaySnapshot( makeReadySnapshot([ { id: 'g-1', name: 'Main', is_main: true, channels: [ { id: 'c-1', name: 'general', position: 0 }, { id: 'c-2', name: 'voice', type: 'voice', position: 1 }, ], }, ]), ); expect(snapshot).not.toBeNull(); act(() => { dispatchGatewayEvent({ op: 0, t: 'READY', s: 1, d: makeReadySnapshot([ { id: 'g-1', name: 'Main', is_main: true, channels: [ { id: 'c-1', name: 'general', position: 0 }, { id: 'c-2', name: 'voice', type: 'voice', position: 1 }, ], }, ]) }); }); const state = useSessionStore.getState(); expect(state.guilds).toHaveLength(1); expect(state.guilds[0]?.name).toBe('Main'); expect(state.guilds[0]?.channels.map((channel) => channel.id)).toEqual(['c-1', 'c-2']); expect(state.sessionId).toBe('session-1'); expect(state.user?.display_name).toBe('Alice'); expect(useGatewayStore.getState().status).toBe('connected'); expect(useGatewayStore.getState().heartbeatIntervalMs).toBe(45_000); }); it('keeps channels with can_send=false (only can_view=false is hidden)', () => { act(() => { dispatchGatewayEvent({ op: 0, t: 'READY', s: 2, d: makeReadySnapshot([ { id: 'g-1', name: 'Main', channels: [ { id: 'c-readonly', name: 'announcements', can_send: false }, { id: 'c-hidden', name: 'secret', can_view: false }, ], }, ]), }); }); const channels = useSessionStore.getState().guilds[0]?.channels ?? []; expect(channels.map((channel) => channel.id)).toEqual(['c-readonly', 'c-hidden']); }); it('ignores unknown events and malformed snapshots', () => { act(() => { dispatchGatewayEvent({ op: 0, t: 'SOMETHING_NEW', s: 3, d: { anything: true } }); dispatchGatewayEvent({ op: 0, t: 'READY', s: 4, d: { guilds: [] } }); }); expect(useSessionStore.getState().guilds).toEqual([]); expect(useSessionStore.getState().user).toBeNull(); }); it('drops a guild and clears the selection when leaving', () => { act(() => { dispatchGatewayEvent({ op: 0, t: 'READY', s: 5, d: makeReadySnapshot([{ id: 'g-1', name: 'Main' }]), }); useSessionStore.getState().selectChannel('g-1', null); useSessionStore.getState().removeGuild('g-1'); }); const state = useSessionStore.getState(); expect(state.guilds).toEqual([]); expect(state.selectedGuildId).toBeNull(); }); });