summaryrefslogtreecommitdiff
path: root/internal/api/logs_test.go
diff options
context:
space:
mode:
authorPeter Stone <thepeterstone@gmail.com>2026-03-08 20:40:41 +0000
committerPeter Stone <thepeterstone@gmail.com>2026-03-08 20:40:41 +0000
commit7914153d3e65cec7a178e7454c9d4addbbbbdd3f (patch)
tree88fca0d0204a69ffcb4249bf40f7f79f8000f31f /internal/api/logs_test.go
parent417034be7f745062901a940d1a021f6d85be496e (diff)
api: extend executions and log streaming endpoints
- handleListRecentExecutions: add since/limit/task_id query params - handleStreamLogs: tighten SSE framing and cleanup Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Diffstat (limited to 'internal/api/logs_test.go')
-rw-r--r--internal/api/logs_test.go71
1 files changed, 71 insertions, 0 deletions
diff --git a/internal/api/logs_test.go b/internal/api/logs_test.go
index 52fa168..6c6be05 100644
--- a/internal/api/logs_test.go
+++ b/internal/api/logs_test.go
@@ -1,6 +1,7 @@
package api
import (
+ "encoding/json"
"errors"
"net/http"
"net/http/httptest"
@@ -293,6 +294,76 @@ func TestHandleStreamTaskLogs_TerminalExecution_EmitsEventsAndDone(t *testing.T)
}
}
+// TestEmitLogLine_ToolUse_EmitsNameField verifies that emitLogLine emits a tool_use SSE event
+// with a "name" field matching the tool name so the web UI can display it as "[ToolName]".
+func TestEmitLogLine_ToolUse_EmitsNameField(t *testing.T) {
+ line := []byte(`{"type":"assistant","message":{"content":[{"type":"tool_use","name":"Bash","input":{"command":"ls -la"}}]}}`)
+
+ w := httptest.NewRecorder()
+ emitLogLine(w, w, line)
+
+ body := w.Body.String()
+ var found bool
+ for _, chunk := range strings.Split(body, "\n\n") {
+ chunk = strings.TrimSpace(chunk)
+ if !strings.HasPrefix(chunk, "data: ") {
+ continue
+ }
+ jsonStr := strings.TrimPrefix(chunk, "data: ")
+ var e map[string]interface{}
+ if err := json.Unmarshal([]byte(jsonStr), &e); err != nil {
+ continue
+ }
+ if e["type"] == "tool_use" {
+ if e["name"] != "Bash" {
+ t.Errorf("tool_use event name: want Bash, got %v", e["name"])
+ }
+ if e["input"] == nil {
+ t.Error("tool_use event input: expected non-nil")
+ }
+ found = true
+ }
+ }
+ if !found {
+ t.Errorf("no tool_use event found in SSE output:\n%s", body)
+ }
+}
+
+// TestEmitLogLine_Cost_EmitsTotalCostField verifies that emitLogLine emits a cost SSE event
+// with a numeric "total_cost" field so the web UI can display it correctly.
+func TestEmitLogLine_Cost_EmitsTotalCostField(t *testing.T) {
+ line := []byte(`{"type":"result","total_cost_usd":0.0042}`)
+
+ w := httptest.NewRecorder()
+ emitLogLine(w, w, line)
+
+ body := w.Body.String()
+ var found bool
+ for _, chunk := range strings.Split(body, "\n\n") {
+ chunk = strings.TrimSpace(chunk)
+ if !strings.HasPrefix(chunk, "data: ") {
+ continue
+ }
+ jsonStr := strings.TrimPrefix(chunk, "data: ")
+ var e map[string]interface{}
+ if err := json.Unmarshal([]byte(jsonStr), &e); err != nil {
+ continue
+ }
+ if e["type"] == "cost" {
+ if e["total_cost"] == nil {
+ t.Error("cost event total_cost: expected non-nil numeric field")
+ }
+ if v, ok := e["total_cost"].(float64); !ok || v != 0.0042 {
+ t.Errorf("cost event total_cost: want 0.0042, got %v", e["total_cost"])
+ }
+ found = true
+ }
+ }
+ if !found {
+ t.Errorf("no cost event found in SSE output:\n%s", body)
+ }
+}
+
// TestHandleStreamTaskLogs_RunningExecution_LiveTails verifies that a RUNNING execution is
// live-tailed and a done event is emitted once it transitions to a terminal state.
func TestHandleStreamTaskLogs_RunningExecution_LiveTails(t *testing.T) {