package app import ( "strings" "testing" "github.com/hjbdev/patterm/internal/preset" ) func TestParseSummarizerResponseAllowsWrappedJSON(t *testing.T) { resp, err := parseSummarizerResponse("log\n{\"summary\":\"Waiting for tests\",\"state\":\"WORKING\"}\n") if err != nil { t.Fatalf("parseSummarizerResponse: %v", err) } if resp.Summary != "Waiting for tests" || summaryIdleState(resp.State) != StateWorking { t.Fatalf("response = %+v", resp) } } func TestCompactSummaryTextDropsControlAndRedundantWhitespace(t *testing.T) { got := compactSummaryText("hello\x00 world \n\n\n\x1b[31mred\x1b[0m\n") if strings.ContainsRune(got, '\x00') { t.Fatalf("control byte survived: %q", got) } if strings.Contains(got, "\n\n\n") { t.Fatalf("redundant blanks survived: %q", got) } if strings.Contains(got, "\x1b") { t.Fatalf("ansi survived: %q", got) } } func TestWrapSidebarSummaryKeepsWordBoundaries(t *testing.T) { got := wrapSidebarSummary("alpha beta gamma delta", 12) want := []string{"alpha beta", "gamma delta"} if len(got) != len(want) { t.Fatalf("lines = %#v", got) } for i := range want { if got[i] != want[i] { t.Fatalf("line %d = %q want %q", i, got[i], want[i]) } } long := wrapSidebarSummary("supercalifragilistic short", 8) if len(long) == 0 || !strings.HasSuffix(long[0], "…") { t.Fatalf("long word should clip with ellipsis: %#v", long) } } func TestSummaryManagerArmsOnlyTrackedTopLevelAgents(t *testing.T) { sess := NewSession(t.TempDir(), "test") c := newChildEntry("a1", "agent", KindAgent, []string{"fake"}, nil, "", "", "") running := StatusRunning c.status.Store(&running) sess.children[c.ID] = c sess.order = append(sess.order, c.ID) cfg := defaultSettings().AutoSummary m := newSummaryManager(sess, t.TempDir(), preset.Set{}, func() autoSummarySettings { return cfg.clone() }, nil, nil) m.ObserveHumanInput(c.ID, []byte("please summarize")) if got := m.Summary(c.ID); got.Text != "" { t.Fatalf("untracked agent should not update summary state: %+v", got) } m.RegisterChild(c) m.ObserveHumanInput(c.ID, []byte("please summarize")) m.ObserveOutput(c.ID) m.mu.Lock() e := m.entries[c.ID] m.mu.Unlock() if e == nil || !e.armed || !e.dirty { t.Fatalf("tracked top-level agent not armed/dirty: %+v", e) } sub := newChildEntry("a2", "sub", KindAgent, []string{"fake"}, nil, c.ID, "", "") sub.status.Store(&running) m.RegisterChild(sub) m.ObserveHumanInput(sub.ID, []byte("please summarize")) m.mu.Lock() _, ok := m.entries[sub.ID] m.mu.Unlock() if ok { t.Fatal("sub-agent should not get a summary entry") } }