Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
61 changes: 61 additions & 0 deletions internal/engine/engine_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1893,6 +1893,67 @@ func TestReportOverviewTextShowsIncidentAndAuthorityCues(t *testing.T) {
}
}

func TestReportOverviewTextWrapsAuthoritySummary(t *testing.T) {
tools := map[string]int{}
for i := 0; i < 10; i++ {
tools[fmt.Sprintf("terminal-very-long-authority-tool-%02d", i)] = 1
}
sessions := []Session{{
Name: "wide-authority",
Health: 90,
Metrics: Metrics{
SourceTool: "codex_cli",
ModelUsed: "gpt-5.1",
ToolUsage: tools,
ToolAuthority: map[string]int{
ToolAuthorityReadOnlyFiles: 11,
ToolAuthorityTestOrBuild: 12,
ToolAuthorityWriteFiles: 13,
ToolAuthorityPackageInstall: 14,
ToolAuthorityNetworkAccess: 15,
ToolAuthorityShellExec: 16,
ToolAuthorityGitWrite: 17,
ToolAuthorityExternalPublish: 18,
ToolAuthorityUnknown: 19,
},
HighestAuthority: ToolAuthorityUnknown,
},
}}

out := ReportOverview(ComputeOverview(sessions), sessions)
for _, want := range []string{
"Tool authority",
"Highest category",
"Authority category counts",
"High-authority tools",
} {
if !strings.Contains(out, want) {
t.Fatalf("text overview missing %q:\n%s", want, out)
}
}

var authorityLines []string
inAuthority := false
for _, line := range strings.Split(out, "\n") {
if strings.Contains(line, "── Tool authority ──") {
inAuthority = true
continue
}
if inAuthority && strings.TrimSpace(line) == "" {
break
}
if inAuthority {
authorityLines = append(authorityLines, line)
if got := utf8.RuneCountInString(line); got > 100 {
t.Fatalf("authority line too wide (%d): %q\n%s", got, line, out)
}
}
}
if len(authorityLines) < 5 {
t.Fatalf("expected wrapped authority evidence lines, got %d:\n%s", len(authorityLines), out)
}
}

func TestReportOverviewTextTruncatesUnicodeSafely(t *testing.T) {
longName := "x" + strings.Repeat("项目", 24) + "-异常会话"
longTool := "x" + strings.Repeat("工具", 32)
Expand Down
52 changes: 48 additions & 4 deletions internal/engine/report.go
Original file line number Diff line number Diff line change
Expand Up @@ -1159,10 +1159,14 @@ func ReportOverview(ov Overview, sessions []Session) string {
wf(" %s: %s", i18n.T("report_highest_authority"), authority.Highest)
}
if len(authority.Counts) > 0 {
wf(" %s: %s", i18n.T("report_authority_category_counts"), textAuthorityCounts(authority.Counts))
for _, line := range textWrappedKeyValues(i18n.T("report_authority_category_counts"), textAuthorityCountValues(authority.Counts), 96) {
wf(" %s", line)
}
}
if len(authority.HighTools) > 0 {
wf(" %s: %s", i18n.T("report_high_authority_tools"), textCell(strings.Join(authority.HighTools, ", "), 68))
for _, line := range textWrappedKeyValues(i18n.T("report_high_authority_tools"), textToolValues(authority.HighTools), 96) {
wf(" %s", line)
}
}
w("")
}
Expand Down Expand Up @@ -1225,12 +1229,52 @@ func ReportOverview(ov Overview, sessions []Session) string {
return b.String()
}

func textAuthorityCounts(items []authorityCount) string {
func textAuthorityCountValues(items []authorityCount) []string {
parts := make([]string, 0, len(items))
for _, item := range items {
parts = append(parts, fmt.Sprintf("%s=%d", item.Category, item.Count))
}
return strings.Join(parts, ", ")
return parts
}

func textToolValues(items []string) []string {
parts := make([]string, 0, len(items))
for _, item := range items {
parts = append(parts, textCell(item, 40))
}
return parts
}

func textWrappedKeyValues(label string, values []string, limit int) []string {
if len(values) == 0 {
return []string{label + ":"}
}
prefix := label + ": "
continuation := strings.Repeat(" ", utf8.RuneCountInString(label)+2)
lines := make([]string, 0, 1)
current := prefix
for _, value := range values {
separator := ""
if current != prefix && current != continuation {
separator = ", "
}
next := separator + value
if utf8.RuneCountInString(current)+utf8.RuneCountInString(next) > limit && current != prefix && current != continuation {
lines = append(lines, current)
current = continuation + value
continue
}
if utf8.RuneCountInString(current)+utf8.RuneCountInString(next) > limit {
valueLimit := limit - utf8.RuneCountInString(current)
if valueLimit < 4 {
valueLimit = 4
}
next = textCell(value, valueLimit)
}
current += next
}
lines = append(lines, current)
return lines
}

func textCell(value string, limit int) string {
Expand Down