import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest' /** * Unit tests for useExchangeStream memory management (max 1000 cached messages). * Validates: Requirement 7.1 - client-side message cap enforcement * * We test the appendMessage logic directly by extracting it into a testable pattern * since the composable relies on Vue reactivity and EventSource which require mocking. */ const MAX_CACHED_MESSAGES = 1000 interface CwmpExchange { deviceId: string direction: 'CPE→ACS' | 'ACS→CPE' timestamp: string sessionId: string cwmpMethod: string rawXml: string sequenceNumber: number createdAt?: string } /** * Simulates the appendMessage logic from useExchangeStream.ts * This is the exact same logic used in the composable. */ function appendMessage(messages: CwmpExchange[], exchange: CwmpExchange): CwmpExchange[] { let result = [...messages, exchange] if (result.length > MAX_CACHED_MESSAGES) { result = result.slice(-MAX_CACHED_MESSAGES) } return result } function createExchange(sequenceNumber: number): CwmpExchange { return { deviceId: 'test-device', direction: 'CPE→ACS', timestamp: new Date(Date.now() + sequenceNumber * 1000).toISOString(), sessionId: 'session-1', cwmpMethod: 'Inform', rawXml: ``, sequenceNumber, } } describe('useExchangeStream memory management', () => { describe('appendMessage cap enforcement', () => { it('should allow messages up to MAX_CACHED_MESSAGES without discarding', () => { let messages: CwmpExchange[] = [] for (let i = 1; i <= MAX_CACHED_MESSAGES; i++) { messages = appendMessage(messages, createExchange(i)) } expect(messages.length).toBe(MAX_CACHED_MESSAGES) expect(messages[0].sequenceNumber).toBe(1) expect(messages[messages.length - 1].sequenceNumber).toBe(MAX_CACHED_MESSAGES) }) it('should discard oldest message when exceeding the cap', () => { let messages: CwmpExchange[] = [] // Fill to cap for (let i = 1; i <= MAX_CACHED_MESSAGES; i++) { messages = appendMessage(messages, createExchange(i)) } // Add one more (1001) messages = appendMessage(messages, createExchange(MAX_CACHED_MESSAGES + 1)) expect(messages.length).toBe(MAX_CACHED_MESSAGES) // Oldest (seq 1) should be discarded, newest starts at seq 2 expect(messages[0].sequenceNumber).toBe(2) expect(messages[messages.length - 1].sequenceNumber).toBe(MAX_CACHED_MESSAGES + 1) }) it('should always keep the most recent messages when adding many past the cap', () => { let messages: CwmpExchange[] = [] // Add 1500 messages (500 over the cap) const totalMessages = 1500 for (let i = 1; i <= totalMessages; i++) { messages = appendMessage(messages, createExchange(i)) } expect(messages.length).toBe(MAX_CACHED_MESSAGES) // Should keep messages 501 through 1500 expect(messages[0].sequenceNumber).toBe(totalMessages - MAX_CACHED_MESSAGES + 1) expect(messages[messages.length - 1].sequenceNumber).toBe(totalMessages) }) it('should handle empty initial state', () => { let messages: CwmpExchange[] = [] messages = appendMessage(messages, createExchange(1)) expect(messages.length).toBe(1) expect(messages[0].sequenceNumber).toBe(1) }) it('should never exceed MAX_CACHED_MESSAGES regardless of input pattern', () => { let messages: CwmpExchange[] = [] // Rapid burst of 2000 messages for (let i = 1; i <= 2000; i++) { messages = appendMessage(messages, createExchange(i)) expect(messages.length).toBeLessThanOrEqual(MAX_CACHED_MESSAGES) } expect(messages.length).toBe(MAX_CACHED_MESSAGES) }) it('should preserve chronological order after cap enforcement', () => { let messages: CwmpExchange[] = [] for (let i = 1; i <= 1200; i++) { messages = appendMessage(messages, createExchange(i)) } // Verify all remaining messages are in ascending order for (let i = 1; i < messages.length; i++) { expect(messages[i].sequenceNumber).toBeGreaterThan(messages[i - 1].sequenceNumber) expect(messages[i].timestamp > messages[i - 1].timestamp).toBe(true) } }) it('should correctly handle cap at exactly MAX_CACHED_MESSAGES boundary', () => { let messages: CwmpExchange[] = [] // Add exactly MAX_CACHED_MESSAGES messages for (let i = 1; i <= MAX_CACHED_MESSAGES; i++) { messages = appendMessage(messages, createExchange(i)) } expect(messages.length).toBe(MAX_CACHED_MESSAGES) expect(messages[0].sequenceNumber).toBe(1) // Add one more to go over by 1 messages = appendMessage(messages, createExchange(MAX_CACHED_MESSAGES + 1)) expect(messages.length).toBe(MAX_CACHED_MESSAGES) expect(messages[0].sequenceNumber).toBe(2) }) }) })