mirror of
https://github.com/samsonjs/vibetunnel.git
synced 2026-06-26 05:09:30 +00:00
198 lines
No EOL
5.7 KiB
Swift
198 lines
No EOL
5.7 KiB
Swift
import Foundation
|
|
@testable import VibeTunnel
|
|
|
|
// This file combines the mock classes needed for BufferWebSocketClientTests
|
|
|
|
/// Mock WebSocket implementation for testing
|
|
@MainActor
|
|
class MockWebSocket: WebSocketProtocol {
|
|
weak var delegate: WebSocketDelegate?
|
|
|
|
// State tracking
|
|
private(set) var isConnected = false
|
|
private(set) var lastConnectURL: URL?
|
|
private(set) var lastConnectHeaders: [String: String]?
|
|
private(set) var sentMessages: [WebSocketMessage] = []
|
|
private(set) var pingCount = 0
|
|
private(set) var disconnectCalled = false
|
|
private(set) var lastDisconnectCode: URLSessionWebSocketTask.CloseCode?
|
|
private(set) var lastDisconnectReason: Data?
|
|
|
|
// Control test behavior
|
|
var shouldFailConnection = false
|
|
var connectionError: Error?
|
|
var shouldFailSend = false
|
|
var sendError: Error?
|
|
var shouldFailPing = false
|
|
var pingError: Error?
|
|
|
|
// Message simulation
|
|
private var messageQueue: [WebSocketMessage] = []
|
|
private var messageDeliveryTask: Task<Void, Never>?
|
|
|
|
func connect(to url: URL, with headers: [String: String]) async throws {
|
|
lastConnectURL = url
|
|
lastConnectHeaders = headers
|
|
|
|
if shouldFailConnection {
|
|
let error = connectionError ?? WebSocketError.connectionFailed
|
|
throw error
|
|
}
|
|
|
|
isConnected = true
|
|
delegate?.webSocketDidConnect(self)
|
|
|
|
// Start delivering queued messages
|
|
startMessageDelivery()
|
|
}
|
|
|
|
func send(_ message: WebSocketMessage) async throws {
|
|
guard isConnected else {
|
|
throw WebSocketError.connectionFailed
|
|
}
|
|
|
|
if shouldFailSend {
|
|
throw sendError ?? WebSocketError.connectionFailed
|
|
}
|
|
|
|
sentMessages.append(message)
|
|
}
|
|
|
|
func sendPing() async throws {
|
|
guard isConnected else {
|
|
throw WebSocketError.connectionFailed
|
|
}
|
|
|
|
if shouldFailPing {
|
|
throw pingError ?? WebSocketError.connectionFailed
|
|
}
|
|
|
|
pingCount += 1
|
|
}
|
|
|
|
func disconnect(with code: URLSessionWebSocketTask.CloseCode, reason: Data?) {
|
|
disconnectCalled = true
|
|
lastDisconnectCode = code
|
|
lastDisconnectReason = reason
|
|
|
|
if isConnected {
|
|
isConnected = false
|
|
messageDeliveryTask?.cancel()
|
|
messageDeliveryTask = nil
|
|
delegate?.webSocketDidDisconnect(self, closeCode: code, reason: reason)
|
|
}
|
|
}
|
|
|
|
// MARK: - Test Helpers
|
|
|
|
/// Simulate receiving a message from the server
|
|
func simulateMessage(_ message: WebSocketMessage) {
|
|
guard isConnected else { return }
|
|
messageQueue.append(message)
|
|
}
|
|
|
|
/// Simulate multiple messages
|
|
func simulateMessages(_ messages: [WebSocketMessage]) {
|
|
guard isConnected else { return }
|
|
messageQueue.append(contentsOf: messages)
|
|
}
|
|
|
|
/// Simulate a connection error
|
|
func simulateError(_ error: Error) {
|
|
guard isConnected else { return }
|
|
delegate?.webSocket(self, didFailWithError: error)
|
|
}
|
|
|
|
/// Simulate server disconnection
|
|
func simulateDisconnection(closeCode: URLSessionWebSocketTask.CloseCode = .abnormalClosure, reason: Data? = nil) {
|
|
guard isConnected else { return }
|
|
isConnected = false
|
|
messageDeliveryTask?.cancel()
|
|
messageDeliveryTask = nil
|
|
delegate?.webSocketDidDisconnect(self, closeCode: closeCode, reason: reason)
|
|
}
|
|
|
|
/// Clear all tracked state
|
|
func reset() {
|
|
isConnected = false
|
|
lastConnectURL = nil
|
|
lastConnectHeaders = nil
|
|
sentMessages.removeAll()
|
|
pingCount = 0
|
|
disconnectCalled = false
|
|
lastDisconnectCode = nil
|
|
lastDisconnectReason = nil
|
|
messageQueue.removeAll()
|
|
messageDeliveryTask?.cancel()
|
|
messageDeliveryTask = nil
|
|
}
|
|
|
|
/// Find sent messages by type
|
|
func sentStringMessages() -> [String] {
|
|
sentMessages.compactMap { message in
|
|
if case .string(let text) = message {
|
|
return text
|
|
}
|
|
return nil
|
|
}
|
|
}
|
|
|
|
func sentDataMessages() -> [Data] {
|
|
sentMessages.compactMap { message in
|
|
if case .data(let data) = message {
|
|
return data
|
|
}
|
|
return nil
|
|
}
|
|
}
|
|
|
|
/// Find sent JSON messages
|
|
func sentJSONMessages() -> [[String: Any]] {
|
|
sentStringMessages().compactMap { string in
|
|
guard let data = string.data(using: .utf8),
|
|
let json = try? JSONSerialization.jsonObject(with: data) as? [String: Any] else {
|
|
return nil
|
|
}
|
|
return json
|
|
}
|
|
}
|
|
|
|
private func startMessageDelivery() {
|
|
messageDeliveryTask = Task { [weak self] in
|
|
while !Task.isCancelled {
|
|
guard let self = self else { break }
|
|
|
|
if !messageQueue.isEmpty {
|
|
let message = messageQueue.removeFirst()
|
|
await MainActor.run {
|
|
self.delegate?.webSocket(self, didReceiveMessage: message)
|
|
}
|
|
}
|
|
|
|
// Small delay to simulate network latency
|
|
try? await Task.sleep(nanoseconds: 10_000_000) // 10ms
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Mock WebSocket factory for testing
|
|
@MainActor
|
|
class MockWebSocketFactory: WebSocketFactory {
|
|
private(set) var createdWebSockets: [MockWebSocket] = []
|
|
|
|
func createWebSocket() -> WebSocketProtocol {
|
|
let webSocket = MockWebSocket()
|
|
createdWebSockets.append(webSocket)
|
|
return webSocket
|
|
}
|
|
|
|
var lastCreatedWebSocket: MockWebSocket? {
|
|
createdWebSockets.last
|
|
}
|
|
|
|
func reset() {
|
|
createdWebSockets.forEach { $0.reset() }
|
|
createdWebSockets.removeAll()
|
|
}
|
|
} |