test fixes

This commit is contained in:
Peter Steinberger 2025-05-30 16:25:03 +01:00
parent d4d899da85
commit 39f3c18eea
5 changed files with 854 additions and 838 deletions

View File

@ -6,208 +6,211 @@ import XCTest
// MARK: - Action Command Tests
class ActionIntegrationTests: XCTestCase {
func testPerformActionSetTextEditTextAreaValue() async throws {
let actionCommandId = "performaction-setvalue-\(UUID().uuidString)"
let queryCommandId = "query-verify-setvalue-\(UUID().uuidString)"
let textEditBundleId = "com.apple.TextEdit"
let textAreaRole = ApplicationServices.kAXTextAreaRole as String
let textToSet = "Hello from AXORC performAction test! Time: \(Date())"
// MARK: Internal
// Setup
_ = try await setupTextEditAndGetInfo()
defer { Task { await closeTextEdit() } }
func testPerformActionSetTextEditTextAreaValue() async throws {
let actionCommandId = "performaction-setvalue-\(UUID().uuidString)"
let queryCommandId = "query-verify-setvalue-\(UUID().uuidString)"
let textEditBundleId = "com.apple.TextEdit"
let textAreaRole = ApplicationServices.kAXTextAreaRole as String
let textToSet = "Hello from AXORC performAction test! Time: \(Date())"
let textAreaLocator = Locator(criteria: [Criterion(attribute: "AXRole", value: textAreaRole)])
// Setup
_ = try await setupTextEditAndGetInfo()
defer { Task { await closeTextEdit() } }
// Perform action
try await performSetValueAction(
actionCommandId: actionCommandId,
textEditBundleId: textEditBundleId,
textAreaLocator: textAreaLocator,
textToSet: textToSet
)
let textAreaLocator = Locator(criteria: [Criterion(attribute: "AXRole", value: textAreaRole)])
// Verify the value was set
try await verifyTextValue(
queryCommandId: queryCommandId,
textEditBundleId: textEditBundleId,
textAreaLocator: textAreaLocator,
expectedText: textToSet
)
}
// Perform action
try await performSetValueAction(
actionCommandId: actionCommandId,
textEditBundleId: textEditBundleId,
textAreaLocator: textAreaLocator,
textToSet: textToSet
)
func testExtractTextFromTextEditTextArea() async throws {
let setValueCommandId = "setvalue-for-extract-\(UUID().uuidString)"
let extractTextCommandId = "extracttext-textedit-textarea-\(UUID().uuidString)"
let textEditBundleId = "com.apple.TextEdit"
let textAreaRole = ApplicationServices.kAXTextAreaRole as String
let textToSetAndExtract = "Text to be extracted by AXORC. Unique: \(UUID().uuidString)"
// Setup
_ = try await setupTextEditAndGetInfo()
defer { Task { await closeTextEdit() } }
let textAreaLocator = Locator(criteria: [Criterion(attribute: "AXRole", value: textAreaRole)])
// Set text value
try await performSetValueAction(
actionCommandId: setValueCommandId,
textEditBundleId: textEditBundleId,
textAreaLocator: textAreaLocator,
textToSet: textToSetAndExtract
)
// Extract and verify text
try await extractAndVerifyText(
extractTextCommandId: extractTextCommandId,
textEditBundleId: textEditBundleId,
textAreaLocator: textAreaLocator,
expectedText: textToSetAndExtract
)
}
// MARK: - Helper Functions
private func performSetValueAction(
actionCommandId: String,
textEditBundleId: String,
textAreaLocator: Locator,
textToSet: String
) async throws {
let performActionEnvelope = CommandEnvelope(
commandId: actionCommandId,
command: .performAction,
application: textEditBundleId,
debugLogging: true,
locator: textAreaLocator,
actionName: "AXSetValue",
actionValue: AnyCodable(textToSet)
)
let response = try await executeCommand(performActionEnvelope)
XCTAssertEqual(response.commandId , actionCommandId)
XCTAssertEqual(
response.success , true,
"performAction command was not successful. Error: \(response.error?.message ?? "N/A")"
)
try await Task.sleep(for: .milliseconds(100))
}
private func verifyTextValue(
queryCommandId: String,
textEditBundleId: String,
textAreaLocator: Locator,
expectedText: String
) async throws {
let queryEnvelope = CommandEnvelope(
commandId: queryCommandId,
command: .query,
application: textEditBundleId,
attributes: ["AXValue"],
debugLogging: true,
locator: textAreaLocator
)
let response = try await executeCommand(queryEnvelope)
XCTAssertEqual(response.commandId , queryCommandId)
XCTAssertEqual(
response.success , true,
"Query (verify) command failed. Error: \(response.error?.message ?? "N/A")"
)
guard let attributes = response.data?.attributes else {
throw TestError.generic("Attributes nil in query (verify) response.")
// Verify the value was set
try await verifyTextValue(
queryCommandId: queryCommandId,
textEditBundleId: textEditBundleId,
textAreaLocator: textAreaLocator,
expectedText: textToSet
)
}
let retrievedValue = attributes["AXValue"]?.value as? String
XCTAssertEqual(
retrievedValue , expectedText,
"AXValue did not match. Expected: '\(expectedText)'. Got: '\(retrievedValue ?? "nil")'"
)
func testExtractTextFromTextEditTextArea() async throws {
let setValueCommandId = "setvalue-for-extract-\(UUID().uuidString)"
let extractTextCommandId = "extracttext-textedit-textarea-\(UUID().uuidString)"
let textEditBundleId = "com.apple.TextEdit"
let textAreaRole = ApplicationServices.kAXTextAreaRole as String
let textToSetAndExtract = "Text to be extracted by AXORC. Unique: \(UUID().uuidString)"
XCTAssertNotEqual(response.debugLogs , nil)
}
// Setup
_ = try await setupTextEditAndGetInfo()
defer { Task { await closeTextEdit() } }
private func extractAndVerifyText(
extractTextCommandId: String,
textEditBundleId: String,
textAreaLocator: Locator,
expectedText: String
) async throws {
let extractTextEnvelope = CommandEnvelope(
commandId: extractTextCommandId,
command: .extractText,
application: textEditBundleId,
debugLogging: true,
locator: textAreaLocator
)
let textAreaLocator = Locator(criteria: [Criterion(attribute: "AXRole", value: textAreaRole)])
let response = try await executeCommand(extractTextEnvelope)
// Set text value
try await performSetValueAction(
actionCommandId: setValueCommandId,
textEditBundleId: textEditBundleId,
textAreaLocator: textAreaLocator,
textToSet: textToSetAndExtract
)
XCTAssertEqual(response.commandId , extractTextCommandId)
XCTAssertEqual(
response.success , true,
"extractText command failed. Error: \(response.error?.message ?? "N/A")"
)
XCTAssertEqual(response.command , CommandType.extractText.rawValue)
guard let attributes = response.data?.attributes else {
throw TestError.generic("Attributes nil in extractText response.")
// Extract and verify text
try await extractAndVerifyText(
extractTextCommandId: extractTextCommandId,
textEditBundleId: textEditBundleId,
textAreaLocator: textAreaLocator,
expectedText: textToSetAndExtract
)
}
let extractedValue = attributes["AXValue"]?.value as? String
XCTAssertEqual(
extractedValue , expectedText,
"Extracted text did not match. Expected: '\(expectedText)'. Got: '\(extractedValue ?? "nil")'"
)
// MARK: Private
XCTAssertNotEqual(response.debugLogs , nil)
XCTAssertTrue(
response.debugLogs?
.contains { log in
log.contains("Handling extractText command") ||
log.contains("handleExtractText completed")
} == true,
"Debug logs should indicate extractText execution."
)
}
// MARK: - Helper Functions
private func executeCommand(_ command: CommandEnvelope) async throws -> QueryResponse {
let encoder = JSONEncoder()
encoder.outputFormatting = .withoutEscapingSlashes
let jsonData = try encoder.encode(command)
guard let jsonString = String(data: jsonData, encoding: String.Encoding.utf8) else {
throw TestError.generic("Failed to create JSON for command.")
private func performSetValueAction(
actionCommandId: String,
textEditBundleId: String,
textAreaLocator: Locator,
textToSet: String
) async throws {
let performActionEnvelope = CommandEnvelope(
commandId: actionCommandId,
command: .performAction,
application: textEditBundleId,
debugLogging: true,
locator: textAreaLocator,
actionName: "AXSetValue",
actionValue: AnyCodable(textToSet)
)
let response = try await executeCommand(performActionEnvelope)
XCTAssertEqual(response.commandId, actionCommandId)
XCTAssertEqual(
response.success, true,
"performAction command was not successful. Error: \(response.error?.message ?? "N/A")"
)
try await Task.sleep(for: .milliseconds(100))
}
print("Sending command: \(jsonString)")
let result = try runAXORCCommand(arguments: [jsonString])
let (output, errorOutput, exitCode) = (result.output, result.errorOutput, result.exitCode)
private func verifyTextValue(
queryCommandId: String,
textEditBundleId: String,
textAreaLocator: Locator,
expectedText: String
) async throws {
let queryEnvelope = CommandEnvelope(
commandId: queryCommandId,
command: .query,
application: textEditBundleId,
attributes: ["AXValue"],
debugLogging: true,
locator: textAreaLocator
)
XCTAssertEqual(exitCode , 0, Comment(rawValue: "Command failed. Error: \(errorOutput ?? "N/A")"))
XCTAssertEqual(
errorOutput , nil || errorOutput!.isEmpty,
Comment(rawValue: "STDERR should be empty. Got: \(errorOutput ?? "")")
)
let response = try await executeCommand(queryEnvelope)
guard let outputString = output, !outputString.isEmpty else {
throw TestError.generic("Output was nil/empty.")
XCTAssertEqual(response.commandId, queryCommandId)
XCTAssertEqual(
response.success, true,
"Query (verify) command failed. Error: \(response.error?.message ?? "N/A")"
)
guard let attributes = response.data?.attributes else {
throw TestError.generic("Attributes nil in query (verify) response.")
}
let retrievedValue = attributes["AXValue"]?.value as? String
XCTAssertEqual(
retrievedValue, expectedText,
"AXValue did not match. Expected: '\(expectedText)'. Got: '\(retrievedValue ?? "nil")'"
)
XCTAssertNotEqual(response.debugLogs, nil)
}
print("Received output: \(outputString)")
private func extractAndVerifyText(
extractTextCommandId: String,
textEditBundleId: String,
textAreaLocator: Locator,
expectedText: String
) async throws {
let extractTextEnvelope = CommandEnvelope(
commandId: extractTextCommandId,
command: .extractText,
application: textEditBundleId,
debugLogging: true,
locator: textAreaLocator
)
guard let responseData = outputString.data(using: String.Encoding.utf8) else {
throw TestError.generic("Could not convert output to data.")
let response = try await executeCommand(extractTextEnvelope)
XCTAssertEqual(response.commandId, extractTextCommandId)
XCTAssertEqual(
response.success, true,
"extractText command failed. Error: \(response.error?.message ?? "N/A")"
)
XCTAssertEqual(response.command, CommandType.extractText.rawValue)
guard let attributes = response.data?.attributes else {
throw TestError.generic("Attributes nil in extractText response.")
}
let extractedValue = attributes["AXValue"]?.value as? String
XCTAssertEqual(
extractedValue, expectedText,
"Extracted text did not match. Expected: '\(expectedText)'. Got: '\(extractedValue ?? "nil")'"
)
XCTAssertNotEqual(response.debugLogs, nil)
XCTAssertTrue(
response.debugLogs?
.contains { log in
log.contains("Handling extractText command") ||
log.contains("handleExtractText completed")
} == true,
"Debug logs should indicate extractText execution."
)
}
do {
return try JSONDecoder().decode(QueryResponse.self, from: responseData)
} catch {
throw TestError.generic("Failed to decode response: \(error.localizedDescription). JSON: \(outputString)")
private func executeCommand(_ command: CommandEnvelope) async throws -> QueryResponse {
let encoder = JSONEncoder()
encoder.outputFormatting = .withoutEscapingSlashes
let jsonData = try encoder.encode(command)
guard let jsonString = String(data: jsonData, encoding: String.Encoding.utf8) else {
throw TestError.generic("Failed to create JSON for command.")
}
print("Sending command: \(jsonString)")
let result = try runAXORCCommand(arguments: [jsonString])
let (output, errorOutput, exitCode) = (result.output, result.errorOutput, result.exitCode)
XCTAssertEqual(exitCode, 0, Comment(rawValue: "Command failed. Error: \(errorOutput ?? "N/A")"))
XCTAssertEqual(
errorOutput, nil || errorOutput!.isEmpty,
Comment(rawValue: "STDERR should be empty. Got: \(errorOutput ?? "")")
)
guard let outputString = output, !outputString.isEmpty else {
throw TestError.generic("Output was nil/empty.")
}
print("Received output: \(outputString)")
guard let responseData = outputString.data(using: String.Encoding.utf8) else {
throw TestError.generic("Could not convert output to data.")
}
do {
return try JSONDecoder().decode(QueryResponse.self, from: responseData)
} catch {
throw TestError.generic("Failed to decode response: \(error.localizedDescription). JSON: \(outputString)")
}
}
}
}

View File

@ -5,146 +5,147 @@ import XCTest
// MARK: - Application Query Tests
class ApplicationQueryTests: XCTestCase {
func testGetAllApplications() async throws {
let command = CommandEnvelope(
commandId: "test-get-all-apps",
command: .collectAll,
debugLogging: true,
locator: Locator(criteria: [Criterion(attribute: "AXRole", value: "AXApplication")]),
outputFormat: .verbose
)
func testGetAllApplications() async throws {
let command = CommandEnvelope(
commandId: "test-get-all-apps",
command: .collectAll,
debugLogging: true,
locator: Locator(criteria: [Criterion(attribute: "AXRole", value: "AXApplication")]),
outputFormat: .verbose
)
let encoder = JSONEncoder()
encoder.outputFormatting = .prettyPrinted
let jsonData = try encoder.encode(command)
guard let jsonString = String(data: jsonData, encoding: String.Encoding.utf8) else {
throw TestError.generic("Failed to create JSON")
}
let result = try runAXORCCommand(arguments: [jsonString])
XCTAssertEqual(result.exitCode , 0, "Command should succeed")
XCTAssertNotEqual(result.output , nil, "Should have output")
guard let output = result.output,
let responseData = output.data(using: String.Encoding.utf8)
else {
throw TestError.generic("No output")
}
let response = try JSONDecoder().decode(SimpleSuccessResponse.self, from: responseData)
XCTAssertEqual(response.success , true)
// TODO: Fix response type - SimpleSuccessResponse doesn't have data property
// The following code expects response.data which doesn't exist
/*
XCTAssertNotEqual(response.data?["elements"] , nil, "Should have elements")
if let elements = response.data?["elements"] as? [[String: Any]] {
XCTAssertTrue(!elements.isEmpty, "Should have at least one application")
// Check for Finder
let appTitles = elements.compactMap { element -> String? in
guard let attrs = element["attributes"] as? [String: Any] else { return nil }
return attrs["AXTitle"] as? String
}
XCTAssertTrue(appTitles.contains("Finder"), "Finder should be running")
}
*/
}
func testGetWindowsOfApplication() async throws {
await closeTextEdit()
try await Task.sleep(for: .milliseconds(500))
let (pid, _) = try await setupTextEditAndGetInfo()
defer {
if let app = NSRunningApplication.runningApplications(withBundleIdentifier: "com.apple.TextEdit").first {
app.terminate()
let encoder = JSONEncoder()
encoder.outputFormatting = .prettyPrinted
let jsonData = try encoder.encode(command)
guard let jsonString = String(data: jsonData, encoding: String.Encoding.utf8) else {
throw TestError.generic("Failed to create JSON")
}
let result = try runAXORCCommand(arguments: [jsonString])
XCTAssertEqual(result.exitCode, 0, "Command should succeed")
XCTAssertNotEqual(result.output, nil, "Should have output")
guard let output = result.output,
let responseData = output.data(using: String.Encoding.utf8)
else {
throw TestError.generic("No output")
}
let response = try JSONDecoder().decode(SimpleSuccessResponse.self, from: responseData)
XCTAssertEqual(response.success, true)
// TODO: Fix response type - SimpleSuccessResponse doesn't have data property
// The following code expects response.data which doesn't exist
/*
XCTAssertNotEqual(response.data?["elements"] , nil, "Should have elements")
if let elements = response.data?["elements"] as? [[String: Any]] {
XCTAssertTrue(!elements.isEmpty, "Should have at least one application")
// Check for Finder
let appTitles = elements.compactMap { element -> String? in
guard let attrs = element["attributes"] as? [String: Any] else { return nil }
return attrs["AXTitle"] as? String
}
XCTAssertTrue(appTitles.contains("Finder"), "Finder should be running")
}
*/
}
try await Task.sleep(for: .seconds(1))
func testGetWindowsOfApplication() async throws {
await closeTextEdit()
try await Task.sleep(for: .milliseconds(500))
// Query for windows
let command = CommandEnvelope(
commandId: "test-get-windows",
command: .query,
application: "TextEdit",
debugLogging: true,
locator: Locator(criteria: [Criterion(attribute: "AXRole", value: "AXWindow")]),
outputFormat: .verbose
)
let (pid, _) = try await setupTextEditAndGetInfo()
defer {
if let app = NSRunningApplication.runningApplications(withBundleIdentifier: "com.apple.TextEdit").first {
app.terminate()
}
}
let encoder = JSONEncoder()
let jsonData = try encoder.encode(command)
guard let jsonString = String(data: jsonData, encoding: String.Encoding.utf8) else {
throw TestError.generic("Failed to create JSON")
}
try await Task.sleep(for: .seconds(1))
let result = try runAXORCCommand(arguments: [jsonString])
// Query for windows
let command = CommandEnvelope(
commandId: "test-get-windows",
command: .query,
application: "TextEdit",
debugLogging: true,
locator: Locator(criteria: [Criterion(attribute: "AXRole", value: "AXWindow")]),
outputFormat: .verbose
)
XCTAssertEqual(result.exitCode , 0)
let encoder = JSONEncoder()
let jsonData = try encoder.encode(command)
guard let jsonString = String(data: jsonData, encoding: String.Encoding.utf8) else {
throw TestError.generic("Failed to create JSON")
}
guard let output = result.output,
let responseData = output.data(using: String.Encoding.utf8)
else {
throw TestError.generic("No output")
}
let result = try runAXORCCommand(arguments: [jsonString])
let response = try JSONDecoder().decode(SimpleSuccessResponse.self, from: responseData)
XCTAssertEqual(result.exitCode, 0)
XCTAssertEqual(response.success , true)
// TODO: Fix response type - SimpleSuccessResponse doesn't have data property
/*
if let elements = response.data?["elements"] as? [[String: Any]] {
XCTAssertTrue(!elements.isEmpty, "Should have at least one window")
guard let output = result.output,
let responseData = output.data(using: String.Encoding.utf8)
else {
throw TestError.generic("No output")
}
for window in elements {
if let attrs = window["attributes"] as? [String: Any] {
XCTAssertEqual(attrs["AXRole"] as? String , "AXWindow")
XCTAssertNotEqual(attrs["AXTitle"] , nil, "Window should have title")
let response = try JSONDecoder().decode(SimpleSuccessResponse.self, from: responseData)
XCTAssertEqual(response.success, true)
// TODO: Fix response type - SimpleSuccessResponse doesn't have data property
/*
if let elements = response.data?["elements"] as? [[String: Any]] {
XCTAssertTrue(!elements.isEmpty, "Should have at least one window")
for window in elements {
if let attrs = window["attributes"] as? [String: Any] {
XCTAssertEqual(attrs["AXRole"] as? String , "AXWindow")
XCTAssertNotEqual(attrs["AXTitle"] , nil, "Window should have title")
}
}
}
}
*/
}
func testQueryNonExistentApp() async throws {
let command = CommandEnvelope(
commandId: "test-nonexistent",
command: .query,
application: "NonExistentApp12345",
debugLogging: true,
locator: Locator(criteria: [Criterion(attribute: "AXRole", value: "AXApplication")])
)
let encoder = JSONEncoder()
let jsonData = try encoder.encode(command)
guard let jsonString = String(data: jsonData, encoding: String.Encoding.utf8) else {
throw TestError.generic("Failed to create JSON")
*/
}
let result = try runAXORCCommand(arguments: [jsonString])
func testQueryNonExistentApp() async throws {
let command = CommandEnvelope(
commandId: "test-nonexistent",
command: .query,
application: "NonExistentApp12345",
debugLogging: true,
locator: Locator(criteria: [Criterion(attribute: "AXRole", value: "AXApplication")])
)
// Command should succeed but return no elements
XCTAssertEqual(result.exitCode , 0)
let encoder = JSONEncoder()
let jsonData = try encoder.encode(command)
guard let jsonString = String(data: jsonData, encoding: String.Encoding.utf8) else {
throw TestError.generic("Failed to create JSON")
}
guard let output = result.output,
let responseData = output.data(using: String.Encoding.utf8)
else {
throw TestError.generic("No output")
}
let result = try runAXORCCommand(arguments: [jsonString])
let response = try JSONDecoder().decode(SimpleSuccessResponse.self, from: responseData)
// Command should succeed but return no elements
XCTAssertEqual(result.exitCode, 0)
if response.success {
// For non-existent app, we expect success but should check message or details
// to verify no elements were found. Since SimpleSuccessResponse doesn't
// have element data, we verify through the success status and message.
XCTAssertTrue(response.message.contains("No") || response.message.contains("not found") || response.message.isEmpty,
"Message should indicate no elements found or be empty")
guard let output = result.output,
let responseData = output.data(using: String.Encoding.utf8)
else {
throw TestError.generic("No output")
}
let response = try JSONDecoder().decode(SimpleSuccessResponse.self, from: responseData)
if response.success {
// For non-existent app, we expect success but should check message or details
// to verify no elements were found. Since SimpleSuccessResponse doesn't
// have element data, we verify through the success status and message.
XCTAssertTrue(
response.message.contains("No") || response.message.contains("not found") || response.message.isEmpty,
"Message should indicate no elements found or be empty"
)
}
}
}
}

View File

@ -5,134 +5,137 @@ import XCTest
// MARK: - Batch Command Tests
class BatchIntegrationTests: XCTestCase {
func testBatchCommandGetFocusedElementAndQuery() async throws {
let batchCommandId = "batch-textedit-\(UUID().uuidString)"
let focusedElementSubCmdId = "batch-sub-getfocused-\(UUID().uuidString)"
let querySubCmdId = "batch-sub-querytextarea-\(UUID().uuidString)"
let textEditBundleId = "com.apple.TextEdit"
let textAreaRole = ApplicationServices.kAXTextAreaRole as String
// MARK: Internal
// Setup TextEdit
_ = try await setupTextEditAndGetInfo()
defer { Task { await closeTextEdit() } }
func testBatchCommandGetFocusedElementAndQuery() async throws {
let batchCommandId = "batch-textedit-\(UUID().uuidString)"
let focusedElementSubCmdId = "batch-sub-getfocused-\(UUID().uuidString)"
let querySubCmdId = "batch-sub-querytextarea-\(UUID().uuidString)"
let textEditBundleId = "com.apple.TextEdit"
let textAreaRole = ApplicationServices.kAXTextAreaRole as String
// Create batch command
let batchCommand = createBatchCommand(
batchCommandId: batchCommandId,
focusedElementSubCmdId: focusedElementSubCmdId,
querySubCmdId: querySubCmdId,
textEditBundleId: textEditBundleId,
textAreaRole: textAreaRole
)
// Setup TextEdit
_ = try await setupTextEditAndGetInfo()
defer { Task { await closeTextEdit() } }
// Execute batch command
let batchResponse = try await executeBatchCommand(batchCommand)
// Create batch command
let batchCommand = createBatchCommand(
batchCommandId: batchCommandId,
focusedElementSubCmdId: focusedElementSubCmdId,
querySubCmdId: querySubCmdId,
textEditBundleId: textEditBundleId,
textAreaRole: textAreaRole
)
// Verify results
verifyBatchResponse(
batchResponse,
batchCommandId: batchCommandId,
focusedElementSubCmdId: focusedElementSubCmdId,
querySubCmdId: querySubCmdId,
textAreaRole: textAreaRole
)
}
// Execute batch command
let batchResponse = try await executeBatchCommand(batchCommand)
// MARK: - Helper Functions
private func createBatchCommand(
batchCommandId: String,
focusedElementSubCmdId: String,
querySubCmdId: String,
textEditBundleId: String,
textAreaRole: String
) -> CommandEnvelope {
let getFocusedElementSubCommand = CommandEnvelope(
commandId: focusedElementSubCmdId,
command: .getFocusedElement,
application: textEditBundleId,
debugLogging: true
)
let queryTextAreaSubCommand = CommandEnvelope(
commandId: querySubCmdId,
command: .query,
application: textEditBundleId,
attributes: ["AXRole", "AXValue"],
debugLogging: true,
locator: Locator(criteria: [Criterion(attribute: "AXRole", value: textAreaRole)])
)
return CommandEnvelope(
commandId: batchCommandId,
command: .batch,
application: nil,
debugLogging: true,
subCommands: [getFocusedElementSubCommand, queryTextAreaSubCommand]
)
}
private func executeBatchCommand(_ command: CommandEnvelope) async throws -> BatchOperationResponse {
let encoder = JSONEncoder()
encoder.outputFormatting = .prettyPrinted
let jsonData = try encoder.encode(command)
guard let jsonString = String(data: jsonData, encoding: String.Encoding.utf8) else {
throw TestError.generic("Failed to create JSON string for batch command.")
// Verify results
verifyBatchResponse(
batchResponse,
batchCommandId: batchCommandId,
focusedElementSubCmdId: focusedElementSubCmdId,
querySubCmdId: querySubCmdId,
textAreaRole: textAreaRole
)
}
print("Sending batch command to axorc: \(jsonString)")
let result = try runAXORCCommand(arguments: [jsonString])
let (output, errorOutput, exitCode) = (result.output, result.errorOutput, result.exitCode)
// MARK: Private
XCTAssertEqual(
exitCode , 0,
Comment(rawValue: "axorc process for batch command should exit with 0. Error: \(errorOutput ?? "N/A")")
)
XCTAssertEqual(
errorOutput , nil || errorOutput!.isEmpty,
Comment(rawValue: "STDERR should be empty. Got: \(errorOutput ?? "")")
)
// MARK: - Helper Functions
guard let outputString = output, !outputString.isEmpty else {
throw TestError.generic("Output string was nil or empty for batch command.")
}
print("Received output from axorc (batch command): \(outputString)")
private func createBatchCommand(
batchCommandId: String,
focusedElementSubCmdId: String,
querySubCmdId: String,
textEditBundleId: String,
textAreaRole: String
) -> CommandEnvelope {
let getFocusedElementSubCommand = CommandEnvelope(
commandId: focusedElementSubCmdId,
command: .getFocusedElement,
application: textEditBundleId,
debugLogging: true
)
guard let responseData = outputString.data(using: String.Encoding.utf8) else {
throw TestError.generic("Could not convert output string to data for batch command.")
let queryTextAreaSubCommand = CommandEnvelope(
commandId: querySubCmdId,
command: .query,
application: textEditBundleId,
attributes: ["AXRole", "AXValue"],
debugLogging: true,
locator: Locator(criteria: [Criterion(attribute: "AXRole", value: textAreaRole)])
)
return CommandEnvelope(
commandId: batchCommandId,
command: .batch,
application: nil,
debugLogging: true,
subCommands: [getFocusedElementSubCommand, queryTextAreaSubCommand]
)
}
return try JSONDecoder().decode(BatchOperationResponse.self, from: responseData)
private func executeBatchCommand(_ command: CommandEnvelope) async throws -> BatchOperationResponse {
let encoder = JSONEncoder()
encoder.outputFormatting = .prettyPrinted
let jsonData = try encoder.encode(command)
guard let jsonString = String(data: jsonData, encoding: String.Encoding.utf8) else {
throw TestError.generic("Failed to create JSON string for batch command.")
}
print("Sending batch command to axorc: \(jsonString)")
let result = try runAXORCCommand(arguments: [jsonString])
let (output, errorOutput, exitCode) = (result.output, result.errorOutput, result.exitCode)
XCTAssertEqual(
exitCode, 0,
Comment(rawValue: "axorc process for batch command should exit with 0. Error: \(errorOutput ?? "N/A")")
)
XCTAssertEqual(
errorOutput, nil || errorOutput!.isEmpty,
Comment(rawValue: "STDERR should be empty. Got: \(errorOutput ?? "")")
)
guard let outputString = output, !outputString.isEmpty else {
throw TestError.generic("Output string was nil or empty for batch command.")
}
print("Received output from axorc (batch command): \(outputString)")
guard let responseData = outputString.data(using: String.Encoding.utf8) else {
throw TestError.generic("Could not convert output string to data for batch command.")
}
return try JSONDecoder().decode(BatchOperationResponse.self, from: responseData)
}
private func verifyBatchResponse(
_ batchResponse: BatchOperationResponse,
batchCommandId: String,
focusedElementSubCmdId: String,
querySubCmdId: String,
textAreaRole: String
) {
XCTAssertEqual(batchResponse.commandId, batchCommandId)
XCTAssertEqual(batchResponse.success, true, "Batch command should succeed")
XCTAssertEqual(batchResponse.results.count, 2, "Expected 2 results")
// Verify first sub-command
let result1 = batchResponse.results[0]
XCTAssertEqual(result1.commandId, focusedElementSubCmdId)
XCTAssertEqual(result1.success, true, "GetFocusedElement should succeed")
XCTAssertEqual(result1.command, CommandType.getFocusedElement.rawValue)
XCTAssertNotEqual(result1.data, nil)
XCTAssertEqual(result1.data?.attributes?["AXRole"]?.value as? String, textAreaRole)
// Verify second sub-command
let result2 = batchResponse.results[1]
XCTAssertEqual(result2.commandId, querySubCmdId)
XCTAssertEqual(result2.success, true, "Query should succeed")
XCTAssertEqual(result2.command, CommandType.query.rawValue)
XCTAssertNotEqual(result2.data, nil)
XCTAssertEqual(result2.data?.attributes?["AXRole"]?.value as? String, textAreaRole)
XCTAssertNotEqual(batchResponse.debugLogs, nil)
}
}
private func verifyBatchResponse(
_ batchResponse: BatchOperationResponse,
batchCommandId: String,
focusedElementSubCmdId: String,
querySubCmdId: String,
textAreaRole: String
) {
XCTAssertEqual(batchResponse.commandId , batchCommandId)
XCTAssertEqual(batchResponse.success , true, "Batch command should succeed")
XCTAssertEqual(batchResponse.results.count , 2, "Expected 2 results")
// Verify first sub-command
let result1 = batchResponse.results[0]
XCTAssertEqual(result1.commandId , focusedElementSubCmdId)
XCTAssertEqual(result1.success , true, "GetFocusedElement should succeed")
XCTAssertEqual(result1.command , CommandType.getFocusedElement.rawValue)
XCTAssertNotEqual(result1.data , nil)
XCTAssertEqual(result1.data?.attributes?["AXRole"]?.value as? String , textAreaRole)
// Verify second sub-command
let result2 = batchResponse.results[1]
XCTAssertEqual(result2.commandId , querySubCmdId)
XCTAssertEqual(result2.success , true, "Query should succeed")
XCTAssertEqual(result2.command , CommandType.query.rawValue)
XCTAssertNotEqual(result2.data , nil)
XCTAssertEqual(result2.data?.attributes?["AXRole"]?.value as? String , textAreaRole)
XCTAssertNotEqual(batchResponse.debugLogs , nil)
}
}

View File

@ -5,248 +5,247 @@ import XCTest
// MARK: - Element Search and Navigation Tests
class ElementSearchTests: XCTestCase {
func testSearchElementsByRole() async throws {
await closeTextEdit()
try await Task.sleep(for: .milliseconds(500))
func testSearchElementsByRole() async throws {
await closeTextEdit()
try await Task.sleep(for: .milliseconds(500))
_ = try await setupTextEditAndGetInfo()
defer {
if let app = NSRunningApplication.runningApplications(withBundleIdentifier: "com.apple.TextEdit").first {
app.terminate()
_ = try await setupTextEditAndGetInfo()
defer {
if let app = NSRunningApplication.runningApplications(withBundleIdentifier: "com.apple.TextEdit").first {
app.terminate()
}
}
try await Task.sleep(for: .seconds(1))
// Search for buttons
let command = CommandEnvelope(
commandId: "test-search-buttons",
command: .query,
application: "TextEdit",
debugLogging: true,
locator: Locator(criteria: [Criterion(attribute: "AXRole", value: "AXButton")]),
outputFormat: .verbose
)
let encoder = JSONEncoder()
let jsonData = try encoder.encode(command)
guard let jsonString = String(data: jsonData, encoding: String.Encoding.utf8) else {
throw TestError.generic("Failed to create JSON")
}
let result = try runAXORCCommand(arguments: [jsonString])
XCTAssertEqual(result.exitCode, 0)
guard let output = result.output,
let responseData = output.data(using: String.Encoding.utf8)
else {
throw TestError.generic("No output")
}
let response = try JSONDecoder().decode(QueryResponse.self, from: responseData)
XCTAssertEqual(response.success, true)
if let data = response.data, let attributes = data.attributes {
// For a query response, we should find button elements
if let role = attributes["AXRole"]?.value as? String {
XCTAssertEqual(role, "AXButton", "Should find button elements")
}
}
}
try await Task.sleep(for: .seconds(1))
func testDescribeElementHierarchy() async throws {
await closeTextEdit()
try await Task.sleep(for: .milliseconds(500))
// Search for buttons
let command = CommandEnvelope(
commandId: "test-search-buttons",
command: .query,
application: "TextEdit",
debugLogging: true,
locator: Locator(criteria: [Criterion(attribute: "AXRole", value: "AXButton")]),
outputFormat: .verbose
)
_ = try await setupTextEditAndGetInfo()
defer {
if let app = NSRunningApplication.runningApplications(withBundleIdentifier: "com.apple.TextEdit").first {
app.terminate()
}
}
let encoder = JSONEncoder()
let jsonData = try encoder.encode(command)
guard let jsonString = String(data: jsonData, encoding: String.Encoding.utf8) else {
throw TestError.generic("Failed to create JSON")
try await Task.sleep(for: .seconds(1))
// Describe the application element
let command = CommandEnvelope(
commandId: "test-describe",
command: .describeElement,
application: "TextEdit",
debugLogging: true,
locator: Locator(criteria: [Criterion(attribute: "AXRole", value: "AXApplication")]),
maxElements: 3,
outputFormat: .verbose
)
let encoder = JSONEncoder()
let jsonData = try encoder.encode(command)
guard let jsonString = String(data: jsonData, encoding: String.Encoding.utf8) else {
throw TestError.generic("Failed to create JSON")
}
let result = try runAXORCCommand(arguments: [jsonString])
XCTAssertEqual(result.exitCode, 0)
guard let output = result.output,
let responseData = output.data(using: String.Encoding.utf8)
else {
throw TestError.generic("No output")
}
let response = try JSONDecoder().decode(QueryResponse.self, from: responseData)
XCTAssertEqual(response.success, true)
XCTAssertNotEqual(response.data, nil)
// Check hierarchy
if let data = response.data, let attributes = data.attributes {
if let role = attributes["AXRole"]?.value as? String {
XCTAssertEqual(role, "AXApplication", "Should find application element")
}
}
}
let result = try runAXORCCommand(arguments: [jsonString])
func testSetAndVerifyText() async throws {
await closeTextEdit()
try await Task.sleep(for: .milliseconds(500))
XCTAssertEqual(result.exitCode , 0)
_ = try await setupTextEditAndGetInfo()
defer {
if let app = NSRunningApplication.runningApplications(withBundleIdentifier: "com.apple.TextEdit").first {
app.terminate()
}
}
guard let output = result.output,
let responseData = output.data(using: String.Encoding.utf8)
else {
throw TestError.generic("No output")
try await Task.sleep(for: .seconds(1))
// Set text
let setText = CommandEnvelope(
commandId: "test-set-text",
command: .performAction,
application: "TextEdit",
debugLogging: true,
locator: Locator(criteria: [Criterion(attribute: "AXRole", value: "AXTextArea")]),
actionName: "AXSetValue",
actionValue: AnyCodable("Hello from AXorcist tests!")
)
let encoder = JSONEncoder()
var jsonData = try encoder.encode(setText)
guard let setJsonString = String(data: jsonData, encoding: String.Encoding.utf8) else {
throw TestError.generic("Failed to create JSON")
}
var result = try runAXORCCommand(arguments: [setJsonString])
XCTAssertEqual(result.exitCode, 0)
// Query to verify
let queryText = CommandEnvelope(
commandId: "test-query-text",
command: .query,
application: "TextEdit",
debugLogging: true,
locator: Locator(criteria: [Criterion(attribute: "AXRole", value: "AXTextArea")]),
outputFormat: .verbose
)
jsonData = try encoder.encode(queryText)
guard let queryJsonString = String(data: jsonData, encoding: String.Encoding.utf8) else {
throw TestError.generic("Failed to create JSON")
}
result = try runAXORCCommand(arguments: [queryJsonString])
XCTAssertEqual(result.exitCode, 0)
guard let output = result.output,
let responseData = output.data(using: String.Encoding.utf8)
else {
throw TestError.generic("No output")
}
let response = try JSONDecoder().decode(QueryResponse.self, from: responseData)
XCTAssertEqual(response.success, true)
if let data = response.data, let attributes = data.attributes {
if let value = attributes["AXValue"]?.value as? String {
XCTAssertTrue(value.contains("Hello from AXorcist tests!"), "Should find the text we set")
}
}
}
let response = try JSONDecoder().decode(QueryResponse.self, from: responseData)
func testExtractText() async throws {
await closeTextEdit()
try await Task.sleep(for: .milliseconds(500))
XCTAssertEqual(response.success , true)
_ = try await setupTextEditAndGetInfo()
defer {
if let app = NSRunningApplication.runningApplications(withBundleIdentifier: "com.apple.TextEdit").first {
app.terminate()
}
}
if let data = response.data, let attributes = data.attributes {
// For a query response, we should find button elements
if let role = attributes["AXRole"]?.value as? String {
XCTAssertEqual(role , "AXButton", "Should find button elements")
try await Task.sleep(for: .seconds(1))
// Set some text first
let setText = CommandEnvelope(
commandId: "test-set-for-extract",
command: .performAction,
application: "TextEdit",
debugLogging: true,
locator: Locator(criteria: [Criterion(attribute: "AXRole", value: "AXTextArea")]),
actionName: "AXSetValue",
actionValue: AnyCodable("This is test content.\nIt has multiple lines.\nExtract this text.")
)
let encoder = JSONEncoder()
var jsonData = try encoder.encode(setText)
guard let setJsonString = String(data: jsonData, encoding: String.Encoding.utf8) else {
throw TestError.generic("Failed to create JSON")
}
_ = try runAXORCCommand(arguments: [setJsonString])
// Extract text
let extractCommand = CommandEnvelope(
commandId: "test-extract",
command: .extractText,
application: "TextEdit",
debugLogging: true,
locator: Locator(criteria: [Criterion(attribute: "AXRole", value: "AXWindow")]),
outputFormat: .textContent
)
jsonData = try encoder.encode(extractCommand)
guard let extractJsonString = String(data: jsonData, encoding: String.Encoding.utf8) else {
throw TestError.generic("Failed to create JSON")
}
let result = try runAXORCCommand(arguments: [extractJsonString])
XCTAssertEqual(result.exitCode, 0)
guard let output = result.output,
let responseData = output.data(using: String.Encoding.utf8)
else {
throw TestError.generic("No output")
}
let response = try JSONDecoder().decode(QueryResponse.self, from: responseData)
XCTAssertEqual(response.success, true)
if let data = response.data, let attributes = data.attributes {
// For extract text commands, check for extracted text in attributes
if let extractedText = attributes["extractedText"]?.value as? String {
XCTAssertTrue(extractedText.contains("This is test content"), "Should extract the test content")
XCTAssertTrue(extractedText.contains("multiple lines"), "Should extract multiple lines")
} else if let value = attributes["AXValue"]?.value as? String {
XCTAssertTrue(value.contains("This is test content"), "Should extract the test content")
XCTAssertTrue(value.contains("multiple lines"), "Should extract multiple lines")
}
}
}
}
func testDescribeElementHierarchy() async throws {
await closeTextEdit()
try await Task.sleep(for: .milliseconds(500))
_ = try await setupTextEditAndGetInfo()
defer {
if let app = NSRunningApplication.runningApplications(withBundleIdentifier: "com.apple.TextEdit").first {
app.terminate()
}
}
try await Task.sleep(for: .seconds(1))
// Describe the application element
let command = CommandEnvelope(
commandId: "test-describe",
command: .describeElement,
application: "TextEdit",
debugLogging: true,
locator: Locator(criteria: [Criterion(attribute: "AXRole", value: "AXApplication")]),
maxElements: 3,
outputFormat: .verbose
)
let encoder = JSONEncoder()
let jsonData = try encoder.encode(command)
guard let jsonString = String(data: jsonData, encoding: String.Encoding.utf8) else {
throw TestError.generic("Failed to create JSON")
}
let result = try runAXORCCommand(arguments: [jsonString])
XCTAssertEqual(result.exitCode , 0)
guard let output = result.output,
let responseData = output.data(using: String.Encoding.utf8)
else {
throw TestError.generic("No output")
}
let response = try JSONDecoder().decode(QueryResponse.self, from: responseData)
XCTAssertEqual(response.success , true)
XCTAssertNotEqual(response.data , nil)
// Check hierarchy
if let data = response.data, let attributes = data.attributes {
if let role = attributes["AXRole"]?.value as? String {
XCTAssertEqual(role , "AXApplication", "Should find application element")
}
}
}
func testSetAndVerifyText() async throws {
await closeTextEdit()
try await Task.sleep(for: .milliseconds(500))
_ = try await setupTextEditAndGetInfo()
defer {
if let app = NSRunningApplication.runningApplications(withBundleIdentifier: "com.apple.TextEdit").first {
app.terminate()
}
}
try await Task.sleep(for: .seconds(1))
// Set text
let setText = CommandEnvelope(
commandId: "test-set-text",
command: .performAction,
application: "TextEdit",
debugLogging: true,
locator: Locator(criteria: [Criterion(attribute: "AXRole", value: "AXTextArea")]),
actionName: "AXSetValue",
actionValue: AnyCodable("Hello from AXorcist tests!")
)
let encoder = JSONEncoder()
var jsonData = try encoder.encode(setText)
guard let setJsonString = String(data: jsonData, encoding: String.Encoding.utf8) else {
throw TestError.generic("Failed to create JSON")
}
var result = try runAXORCCommand(arguments: [setJsonString])
XCTAssertEqual(result.exitCode , 0)
// Query to verify
let queryText = CommandEnvelope(
commandId: "test-query-text",
command: .query,
application: "TextEdit",
debugLogging: true,
locator: Locator(criteria: [Criterion(attribute: "AXRole", value: "AXTextArea")]),
outputFormat: .verbose
)
jsonData = try encoder.encode(queryText)
guard let queryJsonString = String(data: jsonData, encoding: String.Encoding.utf8) else {
throw TestError.generic("Failed to create JSON")
}
result = try runAXORCCommand(arguments: [queryJsonString])
XCTAssertEqual(result.exitCode , 0)
guard let output = result.output,
let responseData = output.data(using: String.Encoding.utf8)
else {
throw TestError.generic("No output")
}
let response = try JSONDecoder().decode(QueryResponse.self, from: responseData)
XCTAssertEqual(response.success , true)
if let data = response.data, let attributes = data.attributes {
if let value = attributes["AXValue"]?.value as? String {
XCTAssertTrue(value.contains("Hello from AXorcist tests!"), "Should find the text we set")
}
}
}
func testExtractText() async throws {
await closeTextEdit()
try await Task.sleep(for: .milliseconds(500))
_ = try await setupTextEditAndGetInfo()
defer {
if let app = NSRunningApplication.runningApplications(withBundleIdentifier: "com.apple.TextEdit").first {
app.terminate()
}
}
try await Task.sleep(for: .seconds(1))
// Set some text first
let setText = CommandEnvelope(
commandId: "test-set-for-extract",
command: .performAction,
application: "TextEdit",
debugLogging: true,
locator: Locator(criteria: [Criterion(attribute: "AXRole", value: "AXTextArea")]),
actionName: "AXSetValue",
actionValue: AnyCodable("This is test content.\nIt has multiple lines.\nExtract this text.")
)
let encoder = JSONEncoder()
var jsonData = try encoder.encode(setText)
guard let setJsonString = String(data: jsonData, encoding: String.Encoding.utf8) else {
throw TestError.generic("Failed to create JSON")
}
_ = try runAXORCCommand(arguments: [setJsonString])
// Extract text
let extractCommand = CommandEnvelope(
commandId: "test-extract",
command: .extractText,
application: "TextEdit",
debugLogging: true,
locator: Locator(criteria: [Criterion(attribute: "AXRole", value: "AXWindow")]),
outputFormat: .textContent
)
jsonData = try encoder.encode(extractCommand)
guard let extractJsonString = String(data: jsonData, encoding: String.Encoding.utf8) else {
throw TestError.generic("Failed to create JSON")
}
let result = try runAXORCCommand(arguments: [extractJsonString])
XCTAssertEqual(result.exitCode , 0)
guard let output = result.output,
let responseData = output.data(using: String.Encoding.utf8)
else {
throw TestError.generic("No output")
}
let response = try JSONDecoder().decode(QueryResponse.self, from: responseData)
XCTAssertEqual(response.success , true)
if let data = response.data, let attributes = data.attributes {
// For extract text commands, check for extracted text in attributes
if let extractedText = attributes["extractedText"]?.value as? String {
XCTAssertTrue(extractedText.contains("This is test content"), "Should extract the test content")
XCTAssertTrue(extractedText.contains("multiple lines"), "Should extract multiple lines")
} else if let value = attributes["AXValue"]?.value as? String {
XCTAssertTrue(value.contains("This is test content"), "Should extract the test content")
XCTAssertTrue(value.contains("multiple lines"), "Should extract multiple lines")
}
}
}
}

View File

@ -5,203 +5,213 @@ import XCTest
// MARK: - Ping Command Tests
class PingIntegrationTests: XCTestCase {
func testPingViaStdin() async throws {
let inputJSON = """
{
"command_id": "test_ping_stdin",
"command": "ping",
"payload": {
"message": "Hello from testPingViaStdin"
func testPingViaStdin() async throws {
let inputJSON = """
{
"command_id": "test_ping_stdin",
"command": "ping",
"payload": {
"message": "Hello from testPingViaStdin"
}
}
}
"""
let result = try runAXORCCommandWithStdin(
inputJSON: inputJSON,
arguments: ["--stdin"]
)
XCTAssertEqual(
result.exitCode , 0,
Comment(rawValue: "axorc command failed with status \(result.exitCode). Error: \(result.errorOutput ?? "N/A")")
)
XCTAssertEqual(
result.errorOutput , nil || result.errorOutput!.isEmpty,
Comment(rawValue: "Expected no error output, but got: \(result.errorOutput!)")
)
guard let outputString = result.output else {
XCTAssertTrue(Bool(false), Comment(rawValue: "Output was nil for ping via STDIN"))
return
}
guard let responseData = outputString.data(using: String.Encoding.utf8) else {
XCTAssertTrue(
Bool(false),
Comment(rawValue: "Failed to convert output to Data for ping via STDIN. Output: \(outputString)")
"""
let result = try runAXORCCommandWithStdin(
inputJSON: inputJSON,
arguments: ["--stdin"]
)
return
}
let decodedResponse = try JSONDecoder().decode(SimpleSuccessResponse.self, from: responseData)
XCTAssertEqual(decodedResponse.success , true)
XCTAssertEqual(
decodedResponse.message , "Ping handled by AXORCCommand. Input source: STDIN",
Comment(rawValue: "Unexpected success message: \(decodedResponse.message)")
)
XCTAssertEqual(decodedResponse.details , "Hello from testPingViaStdin")
}
func testPingViaFile() async throws {
let payloadMessage = "Hello from testPingViaFile"
let inputJSON = """
{
"command_id": "test_ping_file",
"command": "ping",
"payload": { "message": "\(payloadMessage)" }
}
"""
let tempFilePath = try createTempFile(content: inputJSON)
defer { try? FileManager.default.removeItem(atPath: tempFilePath) }
let result = try runAXORCCommand(arguments: ["--file", tempFilePath])
XCTAssertEqual(
result.exitCode , 0,
Comment(rawValue: "axorc command failed with status \(result.exitCode). Error: \(result.errorOutput ?? "N/A")")
)
XCTAssertEqual(
result.errorOutput , nil || result.errorOutput!.isEmpty,
Comment(rawValue: "Expected no error output, but got: \(result.errorOutput ?? "N/A")")
)
guard let outputString = result.output else {
XCTAssertTrue(Bool(false), Comment(rawValue: "Output was nil for ping via file"))
return
}
guard let responseData = outputString.data(using: String.Encoding.utf8) else {
XCTAssertTrue(
Bool(false),
Comment(rawValue: "Failed to convert output to Data for ping via file. Output: \(outputString)")
)
return
}
let decodedResponse = try JSONDecoder().decode(SimpleSuccessResponse.self, from: responseData)
XCTAssertEqual(decodedResponse.success , true)
XCTAssertTrue(
decodedResponse.message.lowercased().contains("file: \(tempFilePath.lowercased())"),
Comment(rawValue: "Message should contain file path. Got: \(decodedResponse.message)")
)
XCTAssertEqual(decodedResponse.details , payloadMessage)
}
func testPingViaDirectPayload() async throws {
let payloadMessage = "Hello from testPingViaDirectPayload"
let inputJSON =
"{\"command_id\":\"test_ping_direct\",\"command\":\"ping\",\"payload\":{\"message\":\"\(payloadMessage)\"}}"
let result = try runAXORCCommand(arguments: [inputJSON])
XCTAssertEqual(
result.exitCode , 0,
Comment(rawValue: "axorc command failed with status \(result.exitCode). Error: \(result.errorOutput ?? "N/A")")
)
XCTAssertEqual(
result.errorOutput , nil || result.errorOutput!.isEmpty,
Comment(rawValue: "Expected no error output, but got: \(result.errorOutput ?? "N/A")")
)
guard let outputString = result.output else {
XCTAssertTrue(Bool(false), Comment(rawValue: "Output was nil for ping via direct payload"))
return
}
guard let responseData = outputString.data(using: String.Encoding.utf8) else {
XCTAssertTrue(
Bool(false),
Comment(rawValue: "Failed to convert output to Data for ping via direct payload. Output: \(outputString)")
)
return
}
let decodedResponse = try JSONDecoder().decode(SimpleSuccessResponse.self, from: responseData)
XCTAssertEqual(decodedResponse.success , true)
XCTAssertTrue(
decodedResponse.message.contains("Direct Argument Payload"),
Comment(rawValue: "Unexpected success message: \(decodedResponse.message)")
)
XCTAssertEqual(decodedResponse.details , payloadMessage)
}
func testErrorMultipleInputMethods() async throws {
let inputJSON = """
{
"command_id": "test_error_multiple_inputs",
"command": "ping",
"payload": { "message": "This should not be processed" }
}
"""
let tempFilePath = try createTempFile(content: "{}")
defer { try? FileManager.default.removeItem(atPath: tempFilePath) }
let result = try runAXORCCommandWithStdin(
inputJSON: inputJSON,
arguments: ["--file", tempFilePath]
)
XCTAssertEqual(
result.exitCode , 0,
Comment(rawValue: "axorc command should return 0 with error on stdout. Status: \(result.exitCode). " +
"Error STDOUT: \(result.output ?? "nil"). Error STDERR: \(result.errorOutput ?? "nil")")
)
guard let outputString = result.output, !outputString.isEmpty else {
XCTAssertTrue(Bool(false), Comment(rawValue: "Output was nil or empty for multiple input methods error test"))
return
}
guard let responseData = outputString.data(using: String.Encoding.utf8) else {
XCTAssertTrue(
Bool(false),
XCTAssertEqual(
result.exitCode, 0,
Comment(
rawValue: "Failed to convert output to Data for multiple input methods error. Output: \(outputString)"
rawValue: "axorc command failed with status \(result.exitCode). Error: \(result.errorOutput ?? "N/A")"
)
)
return
}
let errorResponse = try JSONDecoder().decode(ErrorResponse.self, from: responseData)
XCTAssertEqual(errorResponse.success , false)
XCTAssertTrue(
errorResponse.error.message.contains("Multiple input flags specified"),
Comment(rawValue: "Unexpected error message: \(errorResponse.error.message)")
)
}
func testErrorNoInputProvidedForPing() async throws {
let result = try runAXORCCommand(arguments: [])
XCTAssertEqual(
result.exitCode , 0,
Comment(rawValue: "axorc should return 0 with error on stdout. Status: \(result.exitCode). " +
"Error STDOUT: \(result.output ?? "nil"). Error STDERR: \(result.errorOutput ?? "nil")")
)
guard let outputString = result.output, !outputString.isEmpty else {
XCTAssertTrue(Bool(false), Comment(rawValue: "Output was nil or empty for no input test."))
return
}
guard let responseData = outputString.data(using: String.Encoding.utf8) else {
XCTAssertTrue(
Bool(false),
Comment(rawValue: "Failed to convert output to Data for no input error. Output: \(outputString)")
XCTAssertEqual(
result.errorOutput, nil || result.errorOutput!.isEmpty,
Comment(rawValue: "Expected no error output, but got: \(result.errorOutput!)")
)
return
}
let errorResponse = try JSONDecoder().decode(ErrorResponse.self, from: responseData)
XCTAssertEqual(errorResponse.success , false)
XCTAssertEqual(
errorResponse.commandId , "input_error",
Comment(rawValue: "Expected commandId to be input_error, got \(errorResponse.commandId)")
)
XCTAssertTrue(
errorResponse.error.message.contains("No JSON input method specified"),
Comment(rawValue: "Unexpected error message for no input: \(errorResponse.error.message)")
)
}
}
guard let outputString = result.output else {
XCTAssertTrue(Bool(false), Comment(rawValue: "Output was nil for ping via STDIN"))
return
}
guard let responseData = outputString.data(using: String.Encoding.utf8) else {
XCTAssertTrue(
Bool(false),
Comment(rawValue: "Failed to convert output to Data for ping via STDIN. Output: \(outputString)")
)
return
}
let decodedResponse = try JSONDecoder().decode(SimpleSuccessResponse.self, from: responseData)
XCTAssertEqual(decodedResponse.success, true)
XCTAssertEqual(
decodedResponse.message, "Ping handled by AXORCCommand. Input source: STDIN",
Comment(rawValue: "Unexpected success message: \(decodedResponse.message)")
)
XCTAssertEqual(decodedResponse.details, "Hello from testPingViaStdin")
}
func testPingViaFile() async throws {
let payloadMessage = "Hello from testPingViaFile"
let inputJSON = """
{
"command_id": "test_ping_file",
"command": "ping",
"payload": { "message": "\(payloadMessage)" }
}
"""
let tempFilePath = try createTempFile(content: inputJSON)
defer { try? FileManager.default.removeItem(atPath: tempFilePath) }
let result = try runAXORCCommand(arguments: ["--file", tempFilePath])
XCTAssertEqual(
result.exitCode, 0,
Comment(
rawValue: "axorc command failed with status \(result.exitCode). Error: \(result.errorOutput ?? "N/A")"
)
)
XCTAssertEqual(
result.errorOutput, nil || result.errorOutput!.isEmpty,
Comment(rawValue: "Expected no error output, but got: \(result.errorOutput ?? "N/A")")
)
guard let outputString = result.output else {
XCTAssertTrue(Bool(false), Comment(rawValue: "Output was nil for ping via file"))
return
}
guard let responseData = outputString.data(using: String.Encoding.utf8) else {
XCTAssertTrue(
Bool(false),
Comment(rawValue: "Failed to convert output to Data for ping via file. Output: \(outputString)")
)
return
}
let decodedResponse = try JSONDecoder().decode(SimpleSuccessResponse.self, from: responseData)
XCTAssertEqual(decodedResponse.success, true)
XCTAssertTrue(
decodedResponse.message.lowercased().contains("file: \(tempFilePath.lowercased())"),
Comment(rawValue: "Message should contain file path. Got: \(decodedResponse.message)")
)
XCTAssertEqual(decodedResponse.details, payloadMessage)
}
func testPingViaDirectPayload() async throws {
let payloadMessage = "Hello from testPingViaDirectPayload"
let inputJSON =
"{\"command_id\":\"test_ping_direct\",\"command\":\"ping\",\"payload\":{\"message\":\"\(payloadMessage)\"}}"
let result = try runAXORCCommand(arguments: [inputJSON])
XCTAssertEqual(
result.exitCode, 0,
Comment(
rawValue: "axorc command failed with status \(result.exitCode). Error: \(result.errorOutput ?? "N/A")"
)
)
XCTAssertEqual(
result.errorOutput, nil || result.errorOutput!.isEmpty,
Comment(rawValue: "Expected no error output, but got: \(result.errorOutput ?? "N/A")")
)
guard let outputString = result.output else {
XCTAssertTrue(Bool(false), Comment(rawValue: "Output was nil for ping via direct payload"))
return
}
guard let responseData = outputString.data(using: String.Encoding.utf8) else {
XCTAssertTrue(
Bool(false),
Comment(
rawValue: "Failed to convert output to Data for ping via direct payload. Output: \(outputString)"
)
)
return
}
let decodedResponse = try JSONDecoder().decode(SimpleSuccessResponse.self, from: responseData)
XCTAssertEqual(decodedResponse.success, true)
XCTAssertTrue(
decodedResponse.message.contains("Direct Argument Payload"),
Comment(rawValue: "Unexpected success message: \(decodedResponse.message)")
)
XCTAssertEqual(decodedResponse.details, payloadMessage)
}
func testErrorMultipleInputMethods() async throws {
let inputJSON = """
{
"command_id": "test_error_multiple_inputs",
"command": "ping",
"payload": { "message": "This should not be processed" }
}
"""
let tempFilePath = try createTempFile(content: "{}")
defer { try? FileManager.default.removeItem(atPath: tempFilePath) }
let result = try runAXORCCommandWithStdin(
inputJSON: inputJSON,
arguments: ["--file", tempFilePath]
)
XCTAssertEqual(
result.exitCode, 0,
Comment(rawValue: "axorc command should return 0 with error on stdout. Status: \(result.exitCode). " +
"Error STDOUT: \(result.output ?? "nil"). Error STDERR: \(result.errorOutput ?? "nil")")
)
guard let outputString = result.output, !outputString.isEmpty else {
XCTAssertTrue(
Bool(false),
Comment(rawValue: "Output was nil or empty for multiple input methods error test")
)
return
}
guard let responseData = outputString.data(using: String.Encoding.utf8) else {
XCTAssertTrue(
Bool(false),
Comment(
rawValue: "Failed to convert output to Data for multiple input methods error. Output: \(outputString)"
)
)
return
}
let errorResponse = try JSONDecoder().decode(ErrorResponse.self, from: responseData)
XCTAssertEqual(errorResponse.success, false)
XCTAssertTrue(
errorResponse.error.message.contains("Multiple input flags specified"),
Comment(rawValue: "Unexpected error message: \(errorResponse.error.message)")
)
}
func testErrorNoInputProvidedForPing() async throws {
let result = try runAXORCCommand(arguments: [])
XCTAssertEqual(
result.exitCode, 0,
Comment(rawValue: "axorc should return 0 with error on stdout. Status: \(result.exitCode). " +
"Error STDOUT: \(result.output ?? "nil"). Error STDERR: \(result.errorOutput ?? "nil")")
)
guard let outputString = result.output, !outputString.isEmpty else {
XCTAssertTrue(Bool(false), Comment(rawValue: "Output was nil or empty for no input test."))
return
}
guard let responseData = outputString.data(using: String.Encoding.utf8) else {
XCTAssertTrue(
Bool(false),
Comment(rawValue: "Failed to convert output to Data for no input error. Output: \(outputString)")
)
return
}
let errorResponse = try JSONDecoder().decode(ErrorResponse.self, from: responseData)
XCTAssertEqual(errorResponse.success, false)
XCTAssertEqual(
errorResponse.commandId, "input_error",
Comment(rawValue: "Expected commandId to be input_error, got \(errorResponse.commandId)")
)
XCTAssertTrue(
errorResponse.error.message.contains("No JSON input method specified"),
Comment(rawValue: "Unexpected error message for no input: \(errorResponse.error.message)")
)
}
}