MCPcopy Create free account
hub / github.com/codehamr/codehamr / TestToolCallRoundTripExecutesBash

Function TestToolCallRoundTripExecutesBash

internal/tui/tui_test.go:1030–1080  ·  view source on GitHub ↗
(t *testing.T)

Source from the content-addressed store, hash-verified

1028func TestToolCallRoundTripExecutesBash(t *testing.T) {
1029 // Turn 1: bash tool call. Turn 2: plain content, no tool call. A turn ends
1030 // when the assistant stops emitting tool calls (handleStreamClosed →
1031 // finalizeTurn → endTurn).
1032 turn := 0
1033 m := newTestModel(t, func(w http.ResponseWriter, _ *http.Request) {
1034 w.Header().Set("Content-Type", "text/event-stream")
1035 turn++
1036 switch turn {
1037 case 1:
1038 fmt.Fprintf(w, "data: %s\n\n", `{"type":"response.output_item.done","output_index":0,"item":{"type":"function_call","call_id":"c1","name":"bash","arguments":"{\"cmd\":\"echo HAMMER\"}"}}`)
1039 fmt.Fprintf(w, "data: %s\n\n", `{"type":"response.completed","response":{"usage":{"output_tokens":5}}}`)
1040 default:
1041 fmt.Fprintf(w, "data: %s\n\n", `{"type":"response.output_text.delta","delta":"echoed HAMMER for you"}`)
1042 fmt.Fprintf(w, "data: %s\n\n", `{"type":"response.completed","response":{"usage":{"output_tokens":1}}}`)
1043 }
1044 })
1045 mm, cmd := m.submit("run echo", "run echo", promptEntry{display: "run echo"})
1046 out, _ := drain(mm, cmd)
1047 final := out.(Model)
1048
1049 if turn != 2 {
1050 t.Fatalf("expected 2 LLM turns, got %d", turn)
1051 }
1052 // history: user, assistant(bash call), tool(bash result), assistant(content)
1053 if len(final.history) != 4 {
1054 t.Fatalf("history wrong: %d messages", len(final.history))
1055 }
1056 if final.history[2].Role != "tool" || !strings.Contains(final.history[2].Content, "HAMMER") {
1057 t.Fatalf("tool result missing: %+v", final.history[2])
1058 }
1059 if !strings.Contains(stripANSI(final.scroll.String()), "echoed HAMMER for you") {
1060 t.Fatalf("final assistant content missing from scroll: %q", final.scroll.String())
1061 }
1062 // No tool calls → idle, control back to the user.
1063 if final.phase.active() {
1064 t.Fatalf("turn ending with no tool calls must return to idle, phase=%v", final.phase)
1065 }
1066 // The frozen run summary must sum tokens across both LLM rounds, not
1067 // overwrite. Round 1 reports usage.completion_tokens=5, round 2 reports 1.
1068 // finalizeTurn freezes turnTokens into lastTokens (the avg-rate divisor). Sum = 6.
1069 if final.lastTokens != 6 {
1070 t.Fatalf("per-turn tokens should sum across rounds (5+1), got %d", final.lastTokens)
1071 }
1072 // A clean finish (no tool calls) freezes the ✓ outcome for the idle footer.
1073 if final.lastOutcome != outcomeDone {
1074 t.Fatalf("clean finish should freeze outcomeDone, got %v", final.lastOutcome)
1075 }
1076}
1077
1078// TestToolArgsStreamBumpsEstimateAndPhase: a tool-call argument fragment (a file
1079// streaming into write_file) ticks the live token estimate AND flips the phase
1080// to "generating", so the counter doesn't freeze through a long file write: the
1081// bug where only chat content and reasoning were counted, not tool arguments.
1082func TestToolArgsStreamBumpsEstimateAndPhase(t *testing.T) {
1083 m := newTestModel(t, func(http.ResponseWriter, *http.Request) {})

Callers

nothing calls this directly

Calls 6

newTestModelFunction · 0.85
drainFunction · 0.85
stripANSIFunction · 0.85
submitMethod · 0.80
StringMethod · 0.80
activeMethod · 0.80

Tested by

no test coverage detected