- Split app.go (2091→1378 lines) into rendering.go, events.go, init.go - Add EventRouting stream event for router arm transparency - Add session auto-naming from first user message - Add context window progress bar in status bar - Add /keys cheatsheet, /replay for resumed sessions - Add inline cost-per-turn after assistant responses - Add diff previews in fs.write/fs.edit permission prompts - Collapse tool output to 3 lines by default (ctrl+o expands) - Use AddPrefix for system context instead of InjectMessage - Handle ContentThinking and ContentToolResult in session resume - Show session title in resume picker - Add /model numeric selection snapshot safety
104 lines
3.0 KiB
Go
104 lines
3.0 KiB
Go
package tui
|
|
|
|
import (
|
|
"fmt"
|
|
"strings"
|
|
"testing"
|
|
|
|
"somegit.dev/Owlibou/gnoma/internal/message"
|
|
"somegit.dev/Owlibou/gnoma/internal/session"
|
|
)
|
|
|
|
func TestRenderContextBar_Zero(t *testing.T) {
|
|
s := session.Status{TokensUsed: 0, TokenPercent: 0}
|
|
got := renderContextBar(s)
|
|
if !strings.Contains(got, "—") {
|
|
t.Errorf("zero usage should show dash, got %q", got)
|
|
}
|
|
}
|
|
|
|
func TestRenderContextBar_Half(t *testing.T) {
|
|
s := session.Status{TokensUsed: 50000, TokenPercent: 50, TokenState: "ok"}
|
|
got := renderContextBar(s)
|
|
if !strings.Contains(got, "50%") {
|
|
t.Errorf("should contain 50%%, got %q", got)
|
|
}
|
|
// 8-wide bar at 50% → 4 filled + 4 empty
|
|
if strings.Count(got, "█") != 4 {
|
|
t.Errorf("expected 4 filled blocks, got %d in %q", strings.Count(got, "█"), got)
|
|
}
|
|
if strings.Count(got, "░") != 4 {
|
|
t.Errorf("expected 4 empty blocks, got %d in %q", strings.Count(got, "░"), got)
|
|
}
|
|
}
|
|
|
|
func TestRenderContextBar_Full(t *testing.T) {
|
|
s := session.Status{TokensUsed: 100000, TokenPercent: 100, TokenState: "critical"}
|
|
got := renderContextBar(s)
|
|
if !strings.Contains(got, "100%") {
|
|
t.Errorf("should contain 100%%, got %q", got)
|
|
}
|
|
if strings.Count(got, "█") != 8 {
|
|
t.Errorf("expected 8 filled blocks at 100%%, got %d", strings.Count(got, "█"))
|
|
}
|
|
}
|
|
|
|
func TestRenderContextBar_Warning(t *testing.T) {
|
|
s := session.Status{TokensUsed: 70000, TokenPercent: 75, TokenState: "warning"}
|
|
got := renderContextBar(s)
|
|
if !strings.Contains(got, "75%") {
|
|
t.Errorf("should contain 75%%, got %q", got)
|
|
}
|
|
}
|
|
|
|
func TestFormatTurnUsage_Basic(t *testing.T) {
|
|
u := message.Usage{InputTokens: 1500, OutputTokens: 200}
|
|
got := formatTurnUsage(u)
|
|
if !strings.Contains(got, "in: 1500") {
|
|
t.Errorf("should contain input tokens, got %q", got)
|
|
}
|
|
if !strings.Contains(got, "out: 200") {
|
|
t.Errorf("should contain output tokens, got %q", got)
|
|
}
|
|
if strings.Contains(got, "cache") {
|
|
t.Errorf("should not mention cache when zero, got %q", got)
|
|
}
|
|
}
|
|
|
|
func TestFormatTurnUsage_WithCache(t *testing.T) {
|
|
u := message.Usage{InputTokens: 5000, OutputTokens: 800, CacheReadTokens: 3000}
|
|
got := formatTurnUsage(u)
|
|
if !strings.Contains(got, "cache: 3000") {
|
|
t.Errorf("should contain cache tokens, got %q", got)
|
|
}
|
|
}
|
|
|
|
func TestDiffPreviewEdit(t *testing.T) {
|
|
got := diffPreviewEdit("old line", "new line")
|
|
if !strings.Contains(got, "- old line") {
|
|
t.Errorf("should show removed line, got %q", got)
|
|
}
|
|
if !strings.Contains(got, "+ new line") {
|
|
t.Errorf("should show added line, got %q", got)
|
|
}
|
|
}
|
|
|
|
func TestDiffPreviewWrite(t *testing.T) {
|
|
content := "line1\nline2\nline3"
|
|
got := diffPreviewWrite(content)
|
|
if !strings.Contains(got, "+ line1") {
|
|
t.Errorf("should show first line, got %q", got)
|
|
}
|
|
}
|
|
|
|
func TestDiffPreviewWrite_TruncatesLong(t *testing.T) {
|
|
lines := make([]string, 20)
|
|
for i := range lines {
|
|
lines[i] = fmt.Sprintf("line %d", i+1)
|
|
}
|
|
got := diffPreviewWrite(strings.Join(lines, "\n"))
|
|
if !strings.Contains(got, "more lines") {
|
|
t.Errorf("should truncate with '...more lines', got %q", got)
|
|
}
|
|
}
|