mirror of
https://github.com/samsonjs/vibetunnel.git
synced 2026-04-27 15:17:38 +00:00
326 lines
10 KiB
Swift
326 lines
10 KiB
Swift
import Foundation
|
|
import Testing
|
|
@testable import VibeTunnel
|
|
|
|
@Suite("TerminalData Tests", .tags(.models))
|
|
struct TerminalDataTests {
|
|
// MARK: - TerminalEvent Tests
|
|
|
|
@Test("Parse header event")
|
|
func parseHeaderEvent() throws {
|
|
let headerJSON = """
|
|
{
|
|
"version": 2,
|
|
"width": 80,
|
|
"height": 24,
|
|
"timestamp": 1234567890.5,
|
|
"duration": 120.5,
|
|
"command": "/bin/bash",
|
|
"title": "Test Session",
|
|
"env": {"TERM": "xterm-256color", "SHELL": "/bin/bash"}
|
|
}
|
|
"""
|
|
|
|
let event = TerminalEvent(from: headerJSON)
|
|
#expect(event != nil)
|
|
|
|
if case .header(let header) = event {
|
|
#expect(header.version == 2)
|
|
#expect(header.width == 80)
|
|
#expect(header.height == 24)
|
|
#expect(header.timestamp == 1_234_567_890.5)
|
|
#expect(header.duration == 120.5)
|
|
#expect(header.command == "/bin/bash")
|
|
#expect(header.title == "Test Session")
|
|
#expect(header.env?["TERM"] == "xterm-256color")
|
|
#expect(header.env?["SHELL"] == "/bin/bash")
|
|
} else {
|
|
Issue.record("Expected header event but got \(String(describing: event))")
|
|
}
|
|
}
|
|
|
|
@Test("Parse minimal header event")
|
|
func parseMinimalHeaderEvent() throws {
|
|
let headerJSON = """
|
|
{
|
|
"version": 2,
|
|
"width": 80,
|
|
"height": 24
|
|
}
|
|
"""
|
|
|
|
let event = TerminalEvent(from: headerJSON)
|
|
#expect(event != nil)
|
|
|
|
if case .header(let header) = event {
|
|
#expect(header.version == 2)
|
|
#expect(header.width == 80)
|
|
#expect(header.height == 24)
|
|
#expect(header.timestamp == nil)
|
|
#expect(header.duration == nil)
|
|
#expect(header.command == nil)
|
|
#expect(header.title == nil)
|
|
#expect(header.env == nil)
|
|
} else {
|
|
Issue.record("Expected header event but got \(String(describing: event))")
|
|
}
|
|
}
|
|
|
|
@Test("Parse output event")
|
|
func parseOutputEvent() throws {
|
|
let outputJSON = "[1.5, \"o\", \"Hello, world!\\r\\n\"]"
|
|
|
|
let event = TerminalEvent(from: outputJSON)
|
|
#expect(event != nil)
|
|
|
|
if case .output(let timestamp, let data) = event {
|
|
#expect(timestamp == 1.5)
|
|
#expect(data == "Hello, world!\r\n")
|
|
} else {
|
|
Issue.record("Expected output event but got \(String(describing: event))")
|
|
}
|
|
}
|
|
|
|
@Test("Parse resize event")
|
|
func parseResizeEvent() throws {
|
|
let resizeJSON = "[2.5, \"r\", \"80x25\"]"
|
|
|
|
let event = TerminalEvent(from: resizeJSON)
|
|
#expect(event != nil)
|
|
|
|
if case .resize(let timestamp, let dimensions) = event {
|
|
#expect(timestamp == 2.5)
|
|
#expect(dimensions == "80x25")
|
|
} else {
|
|
Issue.record("Expected resize event but got \(String(describing: event))")
|
|
}
|
|
}
|
|
|
|
@Test("Parse exit event")
|
|
func parseExitEvent() throws {
|
|
let exitJSON = "[\"exit\", 0, \"test-session-123\"]"
|
|
|
|
let event = TerminalEvent(from: exitJSON)
|
|
#expect(event != nil)
|
|
|
|
if case .exit(let code, let sessionId) = event {
|
|
#expect(code == 0)
|
|
#expect(sessionId == "test-session-123")
|
|
} else {
|
|
Issue.record("Expected exit event but got \(String(describing: event))")
|
|
}
|
|
}
|
|
|
|
@Test("Parse exit event with non-zero code")
|
|
func parseExitEventNonZero() throws {
|
|
let exitJSON = "[\"exit\", 127, \"error-session\"]"
|
|
|
|
let event = TerminalEvent(from: exitJSON)
|
|
#expect(event != nil)
|
|
|
|
if case .exit(let code, let sessionId) = event {
|
|
#expect(code == 127)
|
|
#expect(sessionId == "error-session")
|
|
} else {
|
|
Issue.record("Expected exit event but got \(String(describing: event))")
|
|
}
|
|
}
|
|
|
|
@Test("Invalid JSON returns nil")
|
|
func invalidJSON() throws {
|
|
let invalidJSON = "not valid json"
|
|
let event = TerminalEvent(from: invalidJSON)
|
|
#expect(event == nil)
|
|
}
|
|
|
|
@Test("Invalid event type returns nil")
|
|
func invalidEventType() throws {
|
|
let invalidEvent = "[1.0, \"x\", \"unknown type\"]"
|
|
let event = TerminalEvent(from: invalidEvent)
|
|
#expect(event == nil)
|
|
}
|
|
|
|
@Test("Missing array elements returns nil")
|
|
func missingArrayElements() throws {
|
|
let incompleteEvent = "[1.0, \"o\"]"
|
|
let event = TerminalEvent(from: incompleteEvent)
|
|
#expect(event == nil)
|
|
}
|
|
|
|
@Test("Wrong data types in array returns nil")
|
|
func wrongDataTypes() throws {
|
|
let wrongTypes = "[\"not-a-number\", \"o\", \"data\"]"
|
|
let event = TerminalEvent(from: wrongTypes)
|
|
#expect(event == nil)
|
|
}
|
|
|
|
@Test("Empty string returns nil")
|
|
func emptyString() throws {
|
|
let event = TerminalEvent(from: "")
|
|
#expect(event == nil)
|
|
}
|
|
|
|
@Test("Array with wrong exit format returns nil")
|
|
func wrongExitFormat() throws {
|
|
// Wrong first element
|
|
let wrongExit1 = "[\"not-exit\", 0, \"session\"]"
|
|
#expect(TerminalEvent(from: wrongExit1) == nil)
|
|
|
|
// Wrong second element type
|
|
let wrongExit2 = "[\"exit\", \"not-a-number\", \"session\"]"
|
|
#expect(TerminalEvent(from: wrongExit2) == nil)
|
|
|
|
// Wrong third element type
|
|
let wrongExit3 = "[\"exit\", 0, 123]"
|
|
#expect(TerminalEvent(from: wrongExit3) == nil)
|
|
|
|
// Too few elements
|
|
let wrongExit4 = "[\"exit\", 0]"
|
|
#expect(TerminalEvent(from: wrongExit4) == nil)
|
|
|
|
// Too many elements
|
|
let wrongExit5 = "[\"exit\", 0, \"session\", \"extra\"]"
|
|
#expect(TerminalEvent(from: wrongExit5) == nil)
|
|
}
|
|
|
|
// MARK: - AsciinemaHeader Tests
|
|
|
|
@Test("Encode and decode header")
|
|
func encodeDecodeHeader() throws {
|
|
let header = AsciinemaHeader(
|
|
version: 2,
|
|
width: 120,
|
|
height: 40,
|
|
timestamp: 1_234_567_890.123,
|
|
duration: 300.5,
|
|
command: "/bin/zsh",
|
|
title: "My Terminal Session",
|
|
env: ["TERM": "xterm-256color", "USER": "testuser"]
|
|
)
|
|
|
|
let encoder = JSONEncoder()
|
|
let data = try encoder.encode(header)
|
|
|
|
let decoder = JSONDecoder()
|
|
let decodedHeader = try decoder.decode(AsciinemaHeader.self, from: data)
|
|
|
|
#expect(decodedHeader.version == header.version)
|
|
#expect(decodedHeader.width == header.width)
|
|
#expect(decodedHeader.height == header.height)
|
|
#expect(decodedHeader.timestamp == header.timestamp)
|
|
#expect(decodedHeader.duration == header.duration)
|
|
#expect(decodedHeader.command == header.command)
|
|
#expect(decodedHeader.title == header.title)
|
|
#expect(decodedHeader.env?["TERM"] == header.env?["TERM"])
|
|
#expect(decodedHeader.env?["USER"] == header.env?["USER"])
|
|
}
|
|
|
|
// MARK: - TerminalInput Tests
|
|
|
|
@Test("Create input from text")
|
|
func createInputFromText() {
|
|
let input = TerminalInput(text: "Hello, world!")
|
|
#expect(input.text == "Hello, world!")
|
|
}
|
|
|
|
@Test("Create input from special keys")
|
|
func createInputFromSpecialKeys() {
|
|
// Arrow keys
|
|
let arrowUp = TerminalInput(specialKey: .arrowUp)
|
|
#expect(arrowUp.text == "\u{001B}[A")
|
|
|
|
let arrowDown = TerminalInput(specialKey: .arrowDown)
|
|
#expect(arrowDown.text == "\u{001B}[B")
|
|
|
|
let arrowRight = TerminalInput(specialKey: .arrowRight)
|
|
#expect(arrowRight.text == "\u{001B}[C")
|
|
|
|
let arrowLeft = TerminalInput(specialKey: .arrowLeft)
|
|
#expect(arrowLeft.text == "\u{001B}[D")
|
|
|
|
// Special keys
|
|
let escape = TerminalInput(specialKey: .escape)
|
|
#expect(escape.text == "\u{001B}")
|
|
|
|
let enter = TerminalInput(specialKey: .enter)
|
|
#expect(enter.text == "\r")
|
|
|
|
let tab = TerminalInput(specialKey: .tab)
|
|
#expect(tab.text == "\t")
|
|
|
|
// Control keys
|
|
let ctrlC = TerminalInput(specialKey: .ctrlC)
|
|
#expect(ctrlC.text == "\u{0003}")
|
|
|
|
let ctrlD = TerminalInput(specialKey: .ctrlD)
|
|
#expect(ctrlD.text == "\u{0004}")
|
|
|
|
let ctrlZ = TerminalInput(specialKey: .ctrlZ)
|
|
#expect(ctrlZ.text == "\u{001A}")
|
|
|
|
let ctrlL = TerminalInput(specialKey: .ctrlL)
|
|
#expect(ctrlL.text == "\u{000C}")
|
|
|
|
let ctrlA = TerminalInput(specialKey: .ctrlA)
|
|
#expect(ctrlA.text == "\u{0001}")
|
|
|
|
let ctrlE = TerminalInput(specialKey: .ctrlE)
|
|
#expect(ctrlE.text == "\u{0005}")
|
|
|
|
// Web compatibility keys
|
|
let ctrlEnter = TerminalInput(specialKey: .ctrlEnter)
|
|
#expect(ctrlEnter.text == "ctrl_enter")
|
|
|
|
let shiftEnter = TerminalInput(specialKey: .shiftEnter)
|
|
#expect(shiftEnter.text == "shift_enter")
|
|
}
|
|
|
|
@Test("Encode and decode terminal input")
|
|
func encodeDecodeTerminalInput() throws {
|
|
let input = TerminalInput(text: "test input")
|
|
|
|
let encoder = JSONEncoder()
|
|
let data = try encoder.encode(input)
|
|
|
|
let decoder = JSONDecoder()
|
|
let decodedInput = try decoder.decode(TerminalInput.self, from: data)
|
|
|
|
#expect(decodedInput.text == input.text)
|
|
}
|
|
|
|
// MARK: - TerminalResize Tests
|
|
|
|
@Test("Create and encode terminal resize")
|
|
func createEncodeTerminalResize() throws {
|
|
let resize = TerminalResize(cols: 100, rows: 50)
|
|
#expect(resize.cols == 100)
|
|
#expect(resize.rows == 50)
|
|
|
|
let encoder = JSONEncoder()
|
|
let data = try encoder.encode(resize)
|
|
|
|
let decoder = JSONDecoder()
|
|
let decodedResize = try decoder.decode(TerminalResize.self, from: data)
|
|
|
|
#expect(decodedResize.cols == resize.cols)
|
|
#expect(decodedResize.rows == resize.rows)
|
|
}
|
|
|
|
@Test("Edge cases for terminal dimensions")
|
|
func terminalDimensionEdgeCases() throws {
|
|
// Minimum dimensions
|
|
let minResize = TerminalResize(cols: 1, rows: 1)
|
|
#expect(minResize.cols == 1)
|
|
#expect(minResize.rows == 1)
|
|
|
|
// Large dimensions
|
|
let largeResize = TerminalResize(cols: 999, rows: 999)
|
|
#expect(largeResize.cols == 999)
|
|
#expect(largeResize.rows == 999)
|
|
|
|
// Zero dimensions (though typically invalid in practice)
|
|
let zeroResize = TerminalResize(cols: 0, rows: 0)
|
|
#expect(zeroResize.cols == 0)
|
|
#expect(zeroResize.rows == 0)
|
|
}
|
|
}
|