package api
import (
"context"
"os/exec"
"path/filepath"
"strings"
"testing"
"craftdesign.group/quarry/internal/artifact"
"craftdesign.group/quarry/internal/index"
"craftdesign.group/quarry/internal/ops"
"craftdesign.group/quarry/internal/zitcli"
)
// A check-in past largeDiffLines is answered by its file list and each
// file's counts, with no hunks computed; file= narrows zit's diff to
// that one path; unified=true is still the whole patch, for a script.
func TestALargeCheckinIsDiffedFileByFile(t *testing.T) {
if _, err := exec.LookPath("zit"); err != nil {
t.Skip("zit not on PATH")
}
dir := t.TempDir()
zrun(t, dir, "init")
zwrite(t, dir, "a.txt", "one\n")
zwrite(t, dir, "b.txt", "two\n")
zrun(t, dir, "add", "a.txt", "b.txt")
zrun(t, dir, "commit", "-m", "first", "--user", "u")
base := ztip(t, dir, "trunk")
zwrite(t, dir, "a.txt", "one\nmore a\n")
zwrite(t, dir, "b.txt", "TWO\n")
zrun(t, dir, "commit", "-m", "second", "--user", "u")
tip := ztip(t, dir, "trunk")
ix, err := index.Open(filepath.Join(t.TempDir(), "index.db"))
if err != nil {
t.Fatal(err)
}
t.Cleanup(func() { ix.Close() })
if err := ix.FoldNow([]index.Parsed{
{Hash: base, Art: &artifact.Artifact{Type: artifact.Manifest, Date: "2026-10-30T10:00:00", User: "u", Comment: "first"},
Changes: []string{"a.txt", "b.txt"}},
{Hash: tip, Art: &artifact.Artifact{Type: artifact.Manifest, Date: "2026-10-30T11:00:00", User: "u", Comment: "second", Parents: []string{base}},
Changes: []string{"a.txt", "b.txt"}, Lines: map[string]index.LineCount{"a.txt": {Added: 1}, "b.txt": {Added: 1, Removed: 1}}},
}); err != nil {
t.Fatal(err)
}
r := ops.NewRegistry()
if err := RegisterAll(r, Deps{Version: "v", Repos: NewRepos(map[string]*Repo{"test": {Index: ix, Zit: zitcli.New(filepath.Join(dir, ".zit"))}})}); err != nil {
t.Fatal(err)
}
defer func(n int) { largeDiffLines = n }(largeDiffLines)
largeDiffLines = 2
diffOf := func(in diffGetIn) diffGetOut {
t.Helper()
in.Repo, in.Hash = "test", tip
out, err := r.Invoke(context.Background(), "checkin.diff", in)
if err != nil {
t.Fatal(err)
}
return out.(diffGetOut)
}
whole := diffOf(diffGetIn{})
if !whole.Large || len(whole.Files) != 2 || len(whole.Structured) != 0 || whole.Counts["b.txt"].Removed != 1 || whole.TotalLines != 3 {
t.Errorf("FAIL: a large check-in answered large=%v files=%d hunks=%d counts=%v total=%d",
whole.Large, len(whole.Files), len(whole.Structured), whole.Counts, whole.TotalLines)
}
one := diffOf(diffGetIn{File: "b.txt"})
if one.Large || len(one.Structured) != 1 || one.Structured[0].Path != "b.txt" {
t.Errorf("FAIL: file=b.txt answered large=%v with %d files", one.Large, len(one.Structured))
}
if raw := diffOf(diffGetIn{Unified: true}); raw.Large || !strings.Contains(raw.Unified, "a.txt") || !strings.Contains(raw.Unified, "b.txt") {
t.Error("FAIL: unified=true no longer answers the whole patch")
}
largeDiffLines = 20000
if small := diffOf(diffGetIn{}); small.Large || len(small.Structured) != 2 {
t.Errorf("FAIL: a small check-in answered large=%v with %d files", small.Large, len(small.Structured))
}
}