Add tmux stale-surface routing regression tests

This commit is contained in:
Lawrence Chen 2026-03-21 03:04:53 -07:00
parent 5051123c14
commit f0fb098d3b
No known key found for this signature in database
2 changed files with 404 additions and 0 deletions

View file

@ -1024,6 +1024,125 @@ final class CLINotifyProcessIntegrationTests: XCTestCase {
)
}
@MainActor
func testNotifyInTmuxPrefersCallerTTYOverStaleValidSurfaceID() throws {
let cliPath = try bundledCLIPath()
let socketPath = makeSocketPath("notify-tmux-tty")
let listenerFD = try bindUnixSocket(at: socketPath)
let state = MockSocketServerState()
let callerTTY = "/dev/ttys777"
let workspaceId = "11111111-1111-1111-1111-111111111111"
let callerSurface = "22222222-2222-2222-2222-222222222222"
let staleSurface = "33333333-3333-3333-3333-333333333333"
defer {
Darwin.close(listenerFD)
unlink(socketPath)
}
let serverHandled = startMockServer(listenerFD: listenerFD, state: state) { line in
if line == "notify_target \(workspaceId) \(callerSurface) Notification||" {
return "OK"
}
if line == "notify_target \(workspaceId) \(staleSurface) Notification||" {
return "OK"
}
guard let data = line.data(using: .utf8),
let payload = try? JSONSerialization.jsonObject(with: data, options: []) as? [String: Any],
let id = payload["id"] as? String,
let method = payload["method"] as? String else {
return "ERROR: Unexpected command \(line)"
}
let params = payload["params"] as? [String: Any] ?? [:]
switch method {
case "surface.list":
let requestedWorkspace = params["workspace_id"] as? String
if requestedWorkspace == workspaceId {
return self.v2Response(
id: id,
ok: true,
result: [
"surfaces": [
[
"id": callerSurface,
"ref": "surface:1",
"index": 0,
"focused": false
],
[
"id": staleSurface,
"ref": "surface:2",
"index": 1,
"focused": true
]
]
]
)
}
case "debug.terminals":
return self.v2Response(
id: id,
ok: true,
result: [
"count": 2,
"terminals": [
[
"workspace_id": workspaceId,
"surface_id": callerSurface,
"tty": callerTTY
],
[
"workspace_id": workspaceId,
"surface_id": staleSurface,
"tty": "/dev/ttys778"
]
]
]
)
default:
break
}
return self.v2Response(
id: id,
ok: false,
error: ["code": "unexpected", "message": "Unexpected method \(method)"]
)
}
var environment = ProcessInfo.processInfo.environment
environment["CMUX_SOCKET_PATH"] = socketPath
environment["CMUX_WORKSPACE_ID"] = workspaceId
environment["CMUX_SURFACE_ID"] = staleSurface
environment["CMUX_CLI_TTY_NAME"] = callerTTY
environment["TMUX"] = "/tmp/tmux-current,123,0"
environment["CMUX_CLI_SENTRY_DISABLED"] = "1"
environment["CMUX_CLAUDE_HOOK_SENTRY_DISABLED"] = "1"
let result = runProcess(
executablePath: cliPath,
arguments: ["notify"],
environment: environment,
timeout: 5
)
wait(for: [serverHandled], timeout: 5)
XCTAssertFalse(result.timedOut, result.stderr)
XCTAssertEqual(result.status, 0, result.stderr)
XCTAssertEqual(result.stdout, "OK\n")
XCTAssertTrue(result.stderr.isEmpty, result.stderr)
XCTAssertTrue(
state.commands.contains("notify_target \(workspaceId) \(callerSurface) Notification||"),
"Expected notify_target to use caller tty surface in tmux, saw \(state.commands)"
)
XCTAssertFalse(
state.commands.contains("notify_target \(workspaceId) \(staleSurface) Notification||"),
"Stale env surface should not win inside tmux, saw \(state.commands)"
)
}
@MainActor
func testTriggerFlashPrefersCallerTTYOverFocusedSurfaceWhenCallerIDsAreStale() throws {
let cliPath = try bundledCLIPath()
@ -1179,4 +1298,147 @@ final class CLINotifyProcessIntegrationTests: XCTestCase {
"Focused surface should not win over caller tty, saw \(state.commands)"
)
}
@MainActor
func testTriggerFlashInTmuxPrefersCallerTTYOverStaleValidSurfaceID() throws {
let cliPath = try bundledCLIPath()
let socketPath = makeSocketPath("flash-tmux-tty")
let listenerFD = try bindUnixSocket(at: socketPath)
let state = MockSocketServerState()
let callerTTY = "/dev/ttys777"
let workspaceId = "11111111-1111-1111-1111-111111111111"
let callerSurface = "22222222-2222-2222-2222-222222222222"
let staleSurface = "33333333-3333-3333-3333-333333333333"
defer {
Darwin.close(listenerFD)
unlink(socketPath)
}
let serverHandled = startMockServer(listenerFD: listenerFD, state: state) { line in
guard let data = line.data(using: .utf8),
let payload = try? JSONSerialization.jsonObject(with: data, options: []) as? [String: Any],
let id = payload["id"] as? String,
let method = payload["method"] as? String else {
return self.v2Response(
id: "unknown",
ok: false,
error: ["code": "unexpected", "message": "Unexpected payload"]
)
}
let params = payload["params"] as? [String: Any] ?? [:]
switch method {
case "surface.list":
let requestedWorkspace = params["workspace_id"] as? String
if requestedWorkspace == workspaceId {
return self.v2Response(
id: id,
ok: true,
result: [
"surfaces": [
[
"id": callerSurface,
"ref": "surface:1",
"index": 0,
"focused": false
],
[
"id": staleSurface,
"ref": "surface:2",
"index": 1,
"focused": true
]
]
]
)
}
case "debug.terminals":
return self.v2Response(
id: id,
ok: true,
result: [
"count": 2,
"terminals": [
[
"workspace_id": workspaceId,
"surface_id": callerSurface,
"tty": callerTTY
],
[
"workspace_id": workspaceId,
"surface_id": staleSurface,
"tty": "/dev/ttys778"
]
]
]
)
case "surface.trigger_flash":
let requestedWorkspace = params["workspace_id"] as? String
let requestedSurface = params["surface_id"] as? String
if requestedWorkspace == workspaceId,
(requestedSurface == callerSurface || requestedSurface == staleSurface) {
return self.v2Response(id: id, ok: true, result: [:])
}
default:
break
}
return self.v2Response(
id: id,
ok: false,
error: ["code": "unexpected", "message": "Unexpected method \(method)"]
)
}
var environment = ProcessInfo.processInfo.environment
environment["CMUX_SOCKET_PATH"] = socketPath
environment["CMUX_WORKSPACE_ID"] = workspaceId
environment["CMUX_SURFACE_ID"] = staleSurface
environment["CMUX_CLI_TTY_NAME"] = callerTTY
environment["TMUX"] = "/tmp/tmux-current,123,0"
environment["CMUX_CLI_SENTRY_DISABLED"] = "1"
environment["CMUX_CLAUDE_HOOK_SENTRY_DISABLED"] = "1"
let result = runProcess(
executablePath: cliPath,
arguments: ["trigger-flash"],
environment: environment,
timeout: 5
)
wait(for: [serverHandled], timeout: 5)
XCTAssertFalse(result.timedOut, result.stderr)
XCTAssertEqual(result.status, 0, result.stderr)
XCTAssertEqual(result.stdout, "OK\n")
XCTAssertTrue(result.stderr.isEmpty, result.stderr)
XCTAssertTrue(
state.commands.contains { command in
guard let data = command.data(using: .utf8),
let payload = try? JSONSerialization.jsonObject(with: data, options: []) as? [String: Any],
let method = payload["method"] as? String,
method == "surface.trigger_flash" else {
return false
}
let params = payload["params"] as? [String: Any] ?? [:]
return (params["workspace_id"] as? String) == workspaceId
&& (params["surface_id"] as? String) == callerSurface
},
"Expected trigger-flash to use caller tty surface in tmux, saw \(state.commands)"
)
XCTAssertFalse(
state.commands.contains { command in
guard let data = command.data(using: .utf8),
let payload = try? JSONSerialization.jsonObject(with: data, options: []) as? [String: Any],
let method = payload["method"] as? String,
method == "surface.trigger_flash" else {
return false
}
let params = payload["params"] as? [String: Any] ?? [:]
return (params["workspace_id"] as? String) == workspaceId
&& (params["surface_id"] as? String) == staleSurface
},
"Stale env surface should not win inside tmux, saw \(state.commands)"
)
}
}