package gitx import ( "os" "os/exec" "path/filepath" "strings" "testing" "sourcecraft.dev/bigbes/sr-ht-compare/core" ) // newFidelityRepo builds a two-commit bare repo whose base..head diff exercises // (a) a rename WITH an edit, (b) a binary file change, and (c) a plain text // edit. It returns reposRoot plus the base and head SHAs. func newFidelityRepo(t *testing.T) (reposRoot, base, head string) { t.Helper() reposRoot = t.TempDir() work := t.TempDir() d1 := "2024-02-01T00:00:00Z" d2 := "2024-02-02T00:00:00Z" gitTest(t, work, d1, "init", "-b", "main") // Base commit. writeFile(t, work, "a.txt", []byte("line1\nline2\nline3\nline4\n")) writeFile(t, work, "old.txt", []byte("one\ntwo\nthree\nfour\nfive\nsix\n")) writeFile(t, work, "img.bin", []byte{0x00, 0x01, 0x02, 0x03, 0x00, 0xff}) gitTest(t, work, d1, "add", "-A") gitTest(t, work, d1, "commit", "-m", "base") // Head commit: plain edit, rename+edit, binary change. writeFile(t, work, "a.txt", []byte("line1\nline2-edited\nline3\nline4\n")) gitTest(t, work, d2, "mv", "old.txt", "new.txt") writeFile(t, work, "new.txt", []byte("one\ntwo\nthree-edited\nfour\nfive\nsix\n")) writeFile(t, work, "img.bin", []byte{0xff, 0xfe, 0x00, 0x11, 0x22, 0x33}) gitTest(t, work, d2, "add", "-A") gitTest(t, work, d2, "commit", "-m", "head: rename+edit, binary change, plain edit") if err := os.MkdirAll(filepath.Join(reposRoot, "~"+fxOwner), 0o755); err != nil { t.Fatal(err) } bare := filepath.Join(reposRoot, "~"+fxOwner, "fidelity") gitTest(t, work, d2, "clone", "--bare", work, bare) base = fxRevIn(t, reposRoot, "fidelity", "main~1") head = fxRevIn(t, reposRoot, "fidelity", "main") return reposRoot, base, head } // fxRevIn is fxRev for an arbitrary repo name under fxOwner. func fxRevIn(t *testing.T, reposRoot, name, rev string) string { t.Helper() bare := filepath.Join(reposRoot, "~"+fxOwner, name) cmd := exec.Command("git", "-C", bare, "rev-parse", rev) cmd.Env = append(os.Environ(), "GIT_CONFIG_GLOBAL=/dev/null", "GIT_CONFIG_SYSTEM=/dev/null", "LC_ALL=C") out, err := cmd.CombinedOutput() if err != nil { t.Fatalf("fxRevIn %q: %v\n%s", rev, err, out) } return strings.TrimSpace(string(out)) } // TestPatchFidelity is the compatibility gate for the browser-side // parsePatchFiles(): go-git's generated patch must carry standard git headers // for renames, binary files, and text hunks. func TestPatchFidelity(t *testing.T) { reposRoot, base, head := newFidelityRepo(t) repo, err := Open(reposRoot, fxOwner, "fidelity") if err != nil { t.Fatalf("Open: %v", err) } spec := core.CompareSpec{Base: base, Head: head} patch, err := repo.Diff(ctx(), spec) if err != nil { t.Fatalf("Diff: %v", err) } text := patch.Text t.Logf("generated patch:\n%s", text) mustContain := []string{ // plain edit "diff --git a/a.txt b/a.txt", "@@", "-line2", "+line2-edited", // rename + edit "diff --git a/old.txt b/new.txt", "rename from old.txt", "rename to new.txt", // binary change "img.bin", "Binary files", } for _, want := range mustContain { if !strings.Contains(text, want) { t.Errorf("patch missing %q\n---\n%s", want, text) } } // The rename must be classified as a single R change, not add+delete. files, err := repo.DiffStat(ctx(), spec) if err != nil { t.Fatalf("DiffStat: %v", err) } byPath := map[string]FileChange{} for _, f := range files { byPath[f.Path] = f } if r, ok := byPath["new.txt"]; !ok || r.Status != "R" || r.OldPath != "old.txt" { t.Errorf("rename entry = %+v (ok=%v), want R from old.txt", byPath["new.txt"], ok) } if r := byPath["new.txt"]; r.Additions == 0 && r.Deletions == 0 { t.Errorf("rename+edit should report line changes, got %+v", r) } if b, ok := byPath["img.bin"]; !ok || !b.Binary { t.Errorf("img.bin = %+v (ok=%v), want binary", byPath["img.bin"], ok) } if a, ok := byPath["a.txt"]; !ok || a.Status != "M" { t.Errorf("a.txt = %+v (ok=%v), want M", byPath["a.txt"], ok) } }