1package tinyclient23import (4 "strings"5 "testing"6)78func TestChatActivityViewScopesDecisionsAndPreservesJobSummary(t *testing.T) {9 actor := strings.Repeat("a", 64)10 other := strings.Repeat("b", 64)11 jobs := map[string]any{"items": []any{map[string]any{"id": strings.Repeat("1", 64), "requester": other, "status": "working", "content": "Indexing files"}}}12 approvals := map[string]any{"items": []any{13 map[string]any{"id": strings.Repeat("2", 64), "asker": other, "asked": []any{actor}, "subject": "Publish result", "state": "open", "kind": "4550"},14 map[string]any{"id": strings.Repeat("3", 64), "asker": other, "asked": []any{other}, "subject": "Private request", "state": "open", "kind": "4550"},15 }}16 items := chatActivityView(jobs, approvals, actor, "/chat/activity", "general")17 if len(items) != 2 {18 t.Fatalf("got %d activity items, want job and addressed approval", len(items))19 }20 if items[0].Type != "job" || items[0].Status != "working" || items[0].Title != "Indexing files" {21 t.Fatalf("unexpected job card: %#v", items[0])22 }23 if items[1].Type != "approval" || !items[1].CanDecide || items[1].ID != strings.Repeat("2", 64) {24 t.Fatalf("unexpected approval card: %#v", items[1])25 }26}2728func TestChatActivityViewDisablesAnsweredApproval(t *testing.T) {29 actor := strings.Repeat("a", 64)30 approvals := map[string]any{"items": []any{map[string]any{"id": strings.Repeat("2", 64), "asker": actor, "asked": []any{actor}, "content": "Done", "state": "answered"}}}31 items := chatActivityView(nil, approvals, actor, "/chat/activity", "general")32 if len(items) != 1 || items[0].CanDecide {33 t.Fatalf("answered approval remained actionable: %#v", items)34 }35}3637func TestChatActivityAskerCannotApproveOwnRequest(t *testing.T) {38 actor := strings.Repeat("a", 64)39 approvals := map[string]any{"items": []any{map[string]any{"id": strings.Repeat("2", 64), "asker": actor, "asked": []string{actor, strings.Repeat("b", 64)}, "state": "open"}}}40 items := chatActivityView(nil, approvals, actor, "/chat/activity", "main")41 if len(items) != 1 || items[0].CanDecide {42 t.Fatalf("asker allowed to decide: %#v", items)43 }44}4546func TestChatActivityPreservesAnsweredQuestion(t *testing.T) {47 actor := strings.Repeat("a", 64)48 other := strings.Repeat("b", 64)49 approvals := map[string]any{"items": []any{map[string]any{50 "id": strings.Repeat("4", 64), "asker": other, "asked": []string{actor},51 "type": "question", "subject": "Which preview?", "state": "answered",52 "answer": map[string]any{"decision": "replied", "content": "Use the dark preview."},53 }}}54 items := chatActivityView(nil, approvals, actor, "/chat/activity", "general")55 if len(items) != 1 || items[0].Answer != "Use the dark preview." || items[0].CanDecide {56 t.Fatalf("answered question = %#v", items)57 }58}5960func TestChatActivityViewCarriesNativeOptions(t *testing.T) {61 actor := strings.Repeat("a", 64)62 other := strings.Repeat("b", 64)63 approvals := map[string]any{"items": []any{map[string]any{64 "id": strings.Repeat("5", 64), "asker": other, "asked": []string{actor}, "kind": 9,65 "type": "question", "state": "open", "selection": "multiple",66 "options": []any{map[string]any{"id": "continue", "label": "Continue playing"}, map[string]any{"id": "stop", "label": "Stop here"}},67 }}}68 items := chatActivityView(nil, approvals, actor, "/chat/activity", "main")69 if len(items) != 1 || !items[0].Native || !items[0].Multiple || len(items[0].Options) != 2 || items[0].Options[1].Label != "Stop here" {70 t.Fatalf("native options = %#v", items)71 }72}7374func TestChatActivityShowsChoiceLabelsAndExplanation(t *testing.T) {75 actor, other := strings.Repeat("a", 64), strings.Repeat("b", 64)76 for _, tt := range []struct{ name, content, want string }{77 {"choice", "c1", "No, something looks wrong"},78 {"choices", `["c0","c1"]`, "Yes, it looks right\nNo, something looks wrong"},79 {"explanation", `{"choices":["c1"],"text":"Keep it in this thread."}`, "No, something looks wrong\n\nKeep it in this thread."},80 {"custom", `{"text":"A different answer."}`, "A different answer."},81 {"unknown", `{"choices":["unknown"],"text":"Details."}`, `{"choices":["unknown"],"text":"Details."}`},82 } {83 t.Run(tt.name, func(t *testing.T) {84 approvals := map[string]any{"items": []any{map[string]any{85 "id": strings.Repeat("5", 64), "asker": other, "asked": []string{actor}, "kind": 9,86 "type": "question", "interaction": "question", "state": "answered", "selection": "single",87 "options": []any{map[string]any{"id": "c0", "label": "Yes, it looks right"}, map[string]any{"id": "c1", "label": "No, something looks wrong"}},88 "answer": map[string]any{"decision": "replied", "content": tt.content},89 }}}90 items := chatActivityView(nil, approvals, actor, "/chat/activity", "general")91 if len(items) != 1 || items[0].Answer != tt.want || items[0].CanDecide {92 t.Fatalf("answered question = %#v; want %q", items, tt.want)93 }94 })95 }96}