ai-agent/internal/tui/diff_test.go
admin 8dc496b626
Some checks failed
CI / test (push) Has been cancelled
Release / release (push) Failing after 4m36s
first commit
2026-03-08 15:40:34 +07:00

188 lines
5.4 KiB
Go

package tui
import (
"strings"
"testing"
)
func TestComputeDiff_Identical(t *testing.T) {
result := computeDiff("hello\nworld\n", "hello\nworld\n")
if result != nil {
t.Errorf("identical texts should return nil, got %d lines", len(result))
}
}
func TestComputeDiff_EmptyBefore(t *testing.T) {
result := computeDiff("", "line1\nline2\n")
if len(result) == 0 {
t.Fatal("expected diff lines for new file")
}
for _, line := range result {
if line.Kind != DiffAdded {
t.Errorf("new file should have only added lines, got kind %d", line.Kind)
}
}
}
func TestComputeDiff_EmptyAfter(t *testing.T) {
result := computeDiff("line1\nline2\n", "")
if len(result) == 0 {
t.Fatal("expected diff lines for deleted file")
}
for _, line := range result {
if line.Kind != DiffRemoved {
t.Errorf("deleted file should have only removed lines, got kind %d", line.Kind)
}
}
}
func TestComputeDiff_Modification(t *testing.T) {
before := "line1\nline2\nline3\n"
after := "line1\nline2-modified\nline3\n"
result := computeDiff(before, after)
if len(result) == 0 {
t.Fatal("expected diff lines for modification")
}
// Should contain removed and added lines.
var hasAdded, hasRemoved, hasContext bool
for _, line := range result {
switch line.Kind {
case DiffAdded:
hasAdded = true
if line.Content != "line2-modified" {
t.Errorf("added line should be 'line2-modified', got %q", line.Content)
}
case DiffRemoved:
hasRemoved = true
if line.Content != "line2" {
t.Errorf("removed line should be 'line2', got %q", line.Content)
}
case DiffContext:
hasContext = true
}
}
if !hasAdded || !hasRemoved {
t.Error("modification should produce both added and removed lines")
}
if !hasContext {
t.Error("modification should have context lines")
}
}
func TestComputeDiff_ContextLimiting(t *testing.T) {
// Create a file with many lines and a change in the middle.
var before, after strings.Builder
for i := 0; i < 50; i++ {
before.WriteString("line" + strings.Repeat("x", i) + "\n")
after.WriteString("line" + strings.Repeat("x", i) + "\n")
}
// Change line 25
beforeStr := strings.Replace(before.String(), "line"+strings.Repeat("x", 25), "CHANGED", 1)
afterStr := strings.Replace(after.String(), "line"+strings.Repeat("x", 25), "MODIFIED", 1)
result := computeDiff(beforeStr, afterStr)
if len(result) == 0 {
t.Fatal("expected diff lines")
}
// Should not include all 50 lines — context filtering should limit output.
if len(result) > 20 {
t.Errorf("context limiting should reduce output, got %d lines", len(result))
}
}
func TestFilterContext_EmptyInput(t *testing.T) {
result := filterContext(nil, 3)
if result != nil {
t.Errorf("empty input should return nil, got %d lines", len(result))
}
}
func TestFilterContext_AllChanges(t *testing.T) {
lines := []DiffLine{
{DiffAdded, "a"},
{DiffAdded, "b"},
{DiffRemoved, "c"},
}
result := filterContext(lines, 3)
if len(result) != 3 {
t.Errorf("all changes should be kept, got %d lines", len(result))
}
}
func TestSplitLines_Empty(t *testing.T) {
result := splitLines("")
if result != nil {
t.Errorf("empty string should return nil, got %v", result)
}
}
func TestSplitLines_TrailingNewline(t *testing.T) {
result := splitLines("a\nb\n")
if len(result) != 2 {
t.Errorf("should have 2 lines, got %d: %v", len(result), result)
}
}
func TestLcsLines_Empty(t *testing.T) {
result := lcsLines(nil, []string{"a"})
if result != nil {
t.Errorf("LCS with empty input should be nil, got %v", result)
}
}
func TestLcsLines_Basic(t *testing.T) {
a := []string{"a", "b", "c", "d"}
b := []string{"a", "c", "d", "e"}
lcs := lcsLines(a, b)
expected := []string{"a", "c", "d"}
if len(lcs) != len(expected) {
t.Fatalf("LCS length mismatch: got %v, want %v", lcs, expected)
}
for i, v := range lcs {
if v != expected[i] {
t.Errorf("LCS[%d] = %q, want %q", i, v, expected[i])
}
}
}
func TestRenderDiff_Empty(t *testing.T) {
s := NewStyles(true)
result := renderDiff(nil, s, 10)
if result != "" {
t.Errorf("empty diff should render empty, got %q", result)
}
}
func TestRenderDiff_MaxLines(t *testing.T) {
lines := []DiffLine{
{DiffAdded, "a"},
{DiffAdded, "b"},
{DiffAdded, "c"},
{DiffAdded, "d"},
{DiffAdded, "e"},
}
s := NewStyles(true)
result := renderDiff(lines, s, 3)
// Should contain "more lines" indicator.
if !strings.Contains(result, "more lines") {
t.Error("should show 'more lines' when truncating")
}
}
func TestReadFileForDiff_NoArgs(t *testing.T) {
result := readFileForDiff(nil)
if result != "" {
t.Errorf("nil args should return empty, got %q", result)
}
}
func TestReadFileForDiff_NonexistentFile(t *testing.T) {
result := readFileForDiff(map[string]any{"path": "/nonexistent/file/path"})
if result != "" {
t.Errorf("nonexistent file should return empty, got %q", result)
}
}