package main import ( "testing" "time" ) var testNow = time.Date(2026, 3, 23, 12, 0, 30, 0, time.UTC) func TestDetectState_EndTurnText(t *testing.T) { msgs := []JSONLMessage{{ Type: "assistant", Timestamp: "2026-03-23T12:00:00Z", Message: &MessagePayload{ Role: "assistant", Content: []ContentBlock{{Type: "text", Text: "Done."}}, StopReason: "end_turn", }, }} state := DetectState(msgs, testNow) if state != NeedsInput { t.Errorf("expected NeedsInput, got %v", state) } } func TestDetectState_ToolUseAskUser(t *testing.T) { msgs := []JSONLMessage{{ Type: "assistant", Timestamp: "2026-03-23T12:00:00Z", Message: &MessagePayload{ Role: "assistant", Content: []ContentBlock{ {Type: "text", Text: "Let me ask..."}, {Type: "tool_use", Name: "AskUserQuestion"}, }, StopReason: "tool_use", }, }} state := DetectState(msgs, testNow) if state != NeedsInput { t.Errorf("expected NeedsInput, got %v", state) } } func TestDetectState_ToolUseOther(t *testing.T) { msgs := []JSONLMessage{{ Type: "assistant", Timestamp: "2026-03-23T12:00:00Z", Message: &MessagePayload{ Role: "assistant", Content: []ContentBlock{ {Type: "tool_use", Name: "Read"}, }, StopReason: "tool_use", }, }} state := DetectState(msgs, testNow) if state != Working { t.Errorf("expected Working, got %v", state) } } func TestDetectState_Progress(t *testing.T) { msgs := []JSONLMessage{{ Type: "progress", Timestamp: "2026-03-23T12:00:00Z", Data: &ProgressData{Type: "agent_progress"}, }} state := DetectState(msgs, testNow) if state != Working { t.Errorf("expected Working, got %v", state) } } func TestDetectState_ToolResult(t *testing.T) { msgs := []JSONLMessage{{ Type: "user", Timestamp: "2026-03-23T12:00:00Z", Message: &MessagePayload{ Role: "user", Content: []ContentBlock{{Type: "tool_result"}}, }, }} state := DetectState(msgs, testNow) if state != Working { t.Errorf("expected Working, got %v", state) } } func TestDetectState_IdleThreshold(t *testing.T) { // Message timestamp > 60s in the past msgs := []JSONLMessage{{ Type: "assistant", Timestamp: "2026-03-23T11:58:00Z", // 2min30s before testNow Message: &MessagePayload{ Role: "assistant", Content: []ContentBlock{{Type: "text", Text: "Done."}}, StopReason: "end_turn", }, }} state := DetectState(msgs, testNow) if state != Idle { t.Errorf("expected Idle, got %v", state) } } func TestDetectState_EmptyMessages(t *testing.T) { state := DetectState(nil, testNow) if state != Unknown { t.Errorf("expected Unknown, got %v", state) } } func TestExtractPreview(t *testing.T) { msgs := []JSONLMessage{ { Type: "user", Message: &MessagePayload{ Role: "user", Content: []ContentBlock{{Type: "text", Text: "do something"}}, }, }, { Type: "assistant", Message: &MessagePayload{ Role: "assistant", Content: []ContentBlock{{Type: "text", Text: "Voici le resultat\nLigne 2\nLigne 3\nLigne 4\nLigne 5"}}, }, }, } preview := ExtractPreview(msgs) want := "Voici le resultat\nLigne 2\nLigne 3" if preview != want { t.Errorf("ExtractPreview = %q, want %q", preview, want) } } func TestExtractPreview_NoAssistant(t *testing.T) { msgs := []JSONLMessage{{ Type: "user", Message: &MessagePayload{ Role: "user", Content: []ContentBlock{{Type: "text", Text: "hello"}}, }, }} preview := ExtractPreview(msgs) if preview != "" { t.Errorf("expected empty preview, got %q", preview) } } func TestExtractPreview_LongText(t *testing.T) { // Text longer than 200 chars should be truncated longText := "" for i := 0; i < 100; i++ { longText += "abcde " } msgs := []JSONLMessage{{ Type: "assistant", Message: &MessagePayload{ Role: "assistant", Content: []ContentBlock{{Type: "text", Text: longText}}, }, }} preview := ExtractPreview(msgs) if len(preview) > 203 { // 200 + "..." t.Errorf("preview too long: %d chars", len(preview)) } if preview[len(preview)-3:] != "..." { t.Errorf("expected truncation marker '...', got %q", preview[len(preview)-3:]) } }