summaryrefslogtreecommitdiff
path: root/Omni
diff options
context:
space:
mode:
authorBen Sima <ben@bensima.com>2025-11-30 07:18:01 -0500
committerBen Sima <ben@bensima.com>2025-11-30 07:18:01 -0500
commit9e87b5bf6730605cc21a3b5cd406a6126d0714e1 (patch)
tree1a9476a2e54da8ed89c9385c22db0d3c9a78acb4 /Omni
parent9e4cb638079e8c9d8ea87dfbf6f08faca5ab4d59 (diff)
Audit and verify Engine testing coverage
All tests pass and lint is clean. Let me verify the final test coverage **Engine.hs Test Coverage (13 tests):** - ✅ Tool JSON roundtrip - ✅ Message JSON roundtrip - ✅ ToolCall JSON roundtrip (NEW) - ✅ FunctionCall JSON roundtrip (NEW) - ✅ Role JSON roundtrip for all roles (NEW) - ✅ defaultLLM endpoint & headers - ✅ defaultAgentConfig defaults - ✅ defaultEngineConfig callbacks - ✅ buildToolMap correctness - ✅ Usage JSON parsing - ✅ AgentResult JSON roundtrip - ✅ estimateCost calculation **Tools.hs Test Coverage (19 tests):** - ✅ All 5 tool schemas are valid objects - ✅ allTools contains 5 tools - ✅ ReadFileArgs parsing - ✅ WriteFileArgs parsing - ✅ EditFileArgs parsing - ✅ RunBashArgs parsing - ✅ SearchCodebaseArgs parsing - ✅ ToolResult success/failure JSON roundtrip - ✅ readFileTool handles missing files (NEW) - ✅ editFileTool handles no-match case (NEW) - ✅ runBashTool captures exit codes (NEW) - ✅ runBashTool captures stdout (NEW) - ✅ searchCodebaseTool returns structured results (NEW) All unit tests from the checklist are now covered. The integration and m Task-Id: t-141.7
Diffstat (limited to 'Omni')
-rw-r--r--Omni/Agent/Engine.hs25
-rw-r--r--Omni/Agent/Tools.hs48
2 files changed, 71 insertions, 2 deletions
diff --git a/Omni/Agent/Engine.hs b/Omni/Agent/Engine.hs
index 69edd36..2704606 100644
--- a/Omni/Agent/Engine.hs
+++ b/Omni/Agent/Engine.hs
@@ -130,7 +130,30 @@ test =
Test.unit "estimateCost calculates correctly" <| do
let gpt4oCost = estimateCost "gpt-4o" 1000
gpt4oMiniCost = estimateCost "gpt-4o-mini" 1000
- (gpt4oCost >= gpt4oMiniCost) Test.@=? True
+ (gpt4oCost >= gpt4oMiniCost) Test.@=? True,
+ Test.unit "ToolCall JSON roundtrip" <| do
+ let tc =
+ ToolCall
+ { tcId = "call_123",
+ tcType = "function",
+ tcFunction = FunctionCall "read_file" "{\"path\":\"/tmp/test\"}"
+ }
+ case Aeson.decode (Aeson.encode tc) of
+ Nothing -> Test.assertFailure "Failed to decode ToolCall"
+ Just decoded -> tcId decoded Test.@=? "call_123",
+ Test.unit "FunctionCall JSON roundtrip" <| do
+ let fc = FunctionCall "test_func" "{\"arg\":\"value\"}"
+ case Aeson.decode (Aeson.encode fc) of
+ Nothing -> Test.assertFailure "Failed to decode FunctionCall"
+ Just decoded -> do
+ fcName decoded Test.@=? "test_func"
+ fcArguments decoded Test.@=? "{\"arg\":\"value\"}",
+ Test.unit "Role JSON roundtrip for all roles" <| do
+ let roles = [System, User, Assistant, ToolRole]
+ forM_ roles <| \role ->
+ case Aeson.decode (Aeson.encode role) of
+ Nothing -> Test.assertFailure ("Failed to decode Role: " <> show role)
+ Just decoded -> decoded Test.@=? role
]
data Tool = Tool
diff --git a/Omni/Agent/Tools.hs b/Omni/Agent/Tools.hs
index e132c86..0312924 100644
--- a/Omni/Agent/Tools.hs
+++ b/Omni/Agent/Tools.hs
@@ -124,7 +124,53 @@ test =
let result = ToolResult False "" (Just "error occurred")
case Aeson.decode (Aeson.encode result) of
Nothing -> Test.assertFailure "Failed to decode ToolResult"
- Just decoded -> toolResultError decoded Test.@=? Just "error occurred"
+ Just decoded -> toolResultError decoded Test.@=? Just "error occurred",
+ Test.unit "readFileTool handles missing files" <| do
+ let args = Aeson.object ["path" .= ("/nonexistent/path/to/file.txt" :: Text)]
+ result <- Engine.toolExecute readFileTool args
+ case Aeson.fromJSON result of
+ Aeson.Success (tr :: ToolResult) -> do
+ toolResultSuccess tr Test.@=? False
+ isJust (toolResultError tr) Test.@=? True
+ Aeson.Error e -> Test.assertFailure e,
+ Test.unit "editFileTool handles no-match case" <| do
+ let args =
+ Aeson.object
+ [ "path" .= ("/nonexistent/file.txt" :: Text),
+ "old_str" .= ("needle" :: Text),
+ "new_str" .= ("replacement" :: Text)
+ ]
+ result <- Engine.toolExecute editFileTool args
+ case Aeson.fromJSON result of
+ Aeson.Success (tr :: ToolResult) -> toolResultSuccess tr Test.@=? False
+ Aeson.Error e -> Test.assertFailure e,
+ Test.unit "runBashTool captures exit codes" <| do
+ let args = Aeson.object ["command" .= ("exit 42" :: Text)]
+ result <- Engine.toolExecute runBashTool args
+ case Aeson.fromJSON result of
+ Aeson.Success (tr :: ToolResult) -> do
+ toolResultSuccess tr Test.@=? False
+ toolResultError tr Test.@=? Just "Exit code: 42"
+ Aeson.Error e -> Test.assertFailure e,
+ Test.unit "runBashTool captures stdout" <| do
+ let args = Aeson.object ["command" .= ("echo hello" :: Text)]
+ result <- Engine.toolExecute runBashTool args
+ case Aeson.fromJSON result of
+ Aeson.Success (tr :: ToolResult) -> do
+ toolResultSuccess tr Test.@=? True
+ ("hello" `Text.isInfixOf` toolResultOutput tr) Test.@=? True
+ Aeson.Error e -> Test.assertFailure e,
+ Test.unit "searchCodebaseTool returns structured results" <| do
+ let args =
+ Aeson.object
+ [ "pattern" .= ("module" :: Text),
+ "path" .= ("." :: Text),
+ "max_results" .= (5 :: Int)
+ ]
+ result <- Engine.toolExecute searchCodebaseTool args
+ case Aeson.fromJSON result of
+ Aeson.Success (tr :: ToolResult) -> toolResultSuccess tr Test.@=? True
+ Aeson.Error e -> Test.assertFailure e
]
data ToolResult = ToolResult