import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest'
import { createPinia, setActivePinia } from 'pinia'
import { useWebSocket } from '@/composables/useWebSocket.js'
class MockWebSocket extends EventTarget {
constructor(url) {
super()
this.url = url
this.readyState = WebSocket.CONNECTING
this.sent = []
MockWebSocket.last = this
}
send(data) { this.sent.push(data) }
close() {
this.readyState = WebSocket.CLOSED
this.dispatchEvent(new Event('close'))
}
_open() {
this.readyState = WebSocket.OPEN
this.dispatchEvent(new Event('open'))
}
_msg(data) {
this.dispatchEvent(new MessageEvent('message', { data: JSON.stringify(data) }))
}
}
// Stub global WebSocket before each test
beforeEach(() => {
vi.stubGlobal('WebSocket', MockWebSocket)
setActivePinia(createPinia())
})
afterEach(() => {
vi.unstubAllGlobals()
})
describe('useWebSocket', () => {
it('connect opens socket', () => {
const ws = useWebSocket()
ws.connect('s1')
expect(MockWebSocket.last).toBeTruthy()
expect(MockWebSocket.last.url).toContain('/ws/sessions/s1')
})
it('connected flag after open', () => {
const ws = useWebSocket()
ws.connect('s1')
MockWebSocket.last._open()
expect(ws.connected.value).toBe(true)
expect(ws.reconnecting.value).toBe(false)
})
it('send delivers JSON when open', () => {
const ws = useWebSocket()
ws.connect('s1')
MockWebSocket.last._open()
ws.send({ type: 'message', content: 'hi' })
expect(MockWebSocket.last.sent).toHaveLength(1)
expect(JSON.parse(MockWebSocket.last.sent[0])).toEqual({ type: 'message', content: 'hi' })
})
it('disconnect sets destroyed and clears state', () => {
const ws = useWebSocket()
ws.connect('s1')
MockWebSocket.last._open()
ws.disconnect()
expect(ws.connected.value).toBe(false)
})
it('dispatches stream_start to chat store', () => {
const ws = useWebSocket()
ws.connect('s1')
MockWebSocket.last._open()
MockWebSocket.last._msg({ type: 'stream_start' })
// No crash = dispatched; deeper assertions need chat store spy
expect(ws.connected.value).toBe(true)
})
it('dispatches session_sync', () => {
const ws = useWebSocket()
ws.connect('s1')
MockWebSocket.last._open()
MockWebSocket.last._msg({ type: 'session_sync' })
expect(ws.connected.value).toBe(true)
})
it('reconnect schedules on close', () => {
vi.useFakeTimers()
const ws = useWebSocket()
ws.connect('s1')
MockWebSocket.last._open()
MockWebSocket.last.close()
expect(ws.reconnecting.value).toBe(true)
vi.advanceTimersByTime(3000)
// Should have created a new MockWebSocket instance
expect(MockWebSocket.last.readyState).toBe(WebSocket.CONNECTING)
vi.useRealTimers()
})
})