Read files from captured Git commits
This commit is contained in:
125
internal/validation/tree_test.go
Normal file
125
internal/validation/tree_test.go
Normal file
@@ -0,0 +1,125 @@
|
||||
package validation
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"errors"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestReadCandidateTreeUsesCapturedCommitAndEnumeratesGitEntries(t *testing.T) {
|
||||
repository := newTreeRepository(t)
|
||||
writeTreeFile(t, repository, "README.md", []byte("captured\n"))
|
||||
writeTreeFile(t, repository, "vendor/library/go.mod", []byte("module vendor.example\n"))
|
||||
writeTreeFile(t, repository, "nested/module/go.mod", []byte("module nested.example\n"))
|
||||
writeTreeFile(t, repository, "binary.dat", []byte{0, 1, 255, 2})
|
||||
unusualPath := "space tab\tline\nname.txt"
|
||||
writeTreeFile(t, repository, unusualPath, []byte("unusual path\n"))
|
||||
if err := os.Symlink("README.md", filepath.Join(repository, "readme-link")); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
submoduleCommit := newTreeRepository(t)
|
||||
writeTreeFile(t, submoduleCommit, "sub.txt", []byte("submodule\n"))
|
||||
gitTree(t, submoduleCommit, "add", "--all")
|
||||
gitTree(t, submoduleCommit, "commit", "-m", "create submodule source")
|
||||
submoduleOID := strings.TrimSpace(gitTree(t, submoduleCommit, "rev-parse", "HEAD"))
|
||||
gitTree(t, repository, "add", "--all")
|
||||
gitTree(t, repository, "update-index", "--add", "--cacheinfo", "160000,"+submoduleOID+",submodule")
|
||||
gitTree(t, repository, "commit", "-m", "capture candidate files")
|
||||
commit := strings.TrimSpace(gitTree(t, repository, "rev-parse", "HEAD"))
|
||||
|
||||
writeTreeFile(t, repository, "README.md", []byte("worktree changed\n"))
|
||||
statusBefore := gitTree(t, repository, "status", "--porcelain=v1", "--untracked-files=all")
|
||||
tree, err := ReadCandidateTree(context.Background(), repository, commit)
|
||||
if err != nil {
|
||||
t.Fatalf("ReadCandidateTree() error = %v", err)
|
||||
}
|
||||
entries := make(map[string]gitTreeEntry, len(tree.Entries()))
|
||||
for _, entry := range tree.Entries() {
|
||||
entries[entry.Path] = gitTreeEntry{mode: entry.Mode, kind: entry.Type, size: entry.Size, hasSize: entry.HasSize}
|
||||
}
|
||||
for _, path := range []string{"vendor/library/go.mod", "nested/module/go.mod", unusualPath} {
|
||||
if _, ok := entries[path]; !ok {
|
||||
t.Errorf("committed tree is missing %q", path)
|
||||
}
|
||||
}
|
||||
if got := entries["binary.dat"]; got.size != 4 || !got.hasSize {
|
||||
t.Errorf("binary metadata = %#v, want size 4", got)
|
||||
}
|
||||
if got := entries["readme-link"]; got.mode != "120000" || got.kind != "blob" || !got.hasSize {
|
||||
t.Errorf("symlink metadata = %#v", got)
|
||||
}
|
||||
if got := entries["submodule"]; got.mode != "160000" || got.kind != "commit" || got.hasSize {
|
||||
t.Errorf("gitlink metadata = %#v", got)
|
||||
}
|
||||
contents, err := tree.ReadFile(context.Background(), "README.md")
|
||||
if err != nil || string(contents) != "captured\n" {
|
||||
t.Fatalf("captured README = %q, error = %v", contents, err)
|
||||
}
|
||||
binary, err := tree.ReadFile(context.Background(), "binary.dat")
|
||||
if err != nil || !bytes.Equal(binary, []byte{0, 1, 255, 2}) {
|
||||
t.Fatalf("captured binary = %v, error = %v", binary, err)
|
||||
}
|
||||
unusual, err := tree.ReadFile(context.Background(), unusualPath)
|
||||
if err != nil || string(unusual) != "unusual path\n" {
|
||||
t.Fatalf("unusual path contents = %q, error = %v", unusual, err)
|
||||
}
|
||||
if _, err := tree.ReadFile(context.Background(), "submodule"); err == nil {
|
||||
t.Fatal("ReadFile() returned submodule commit contents")
|
||||
}
|
||||
statusAfter := gitTree(t, repository, "status", "--porcelain=v1", "--untracked-files=all")
|
||||
if statusAfter != statusBefore {
|
||||
t.Fatalf("tree inspection changed worktree state: before %q, after %q", statusBefore, statusAfter)
|
||||
}
|
||||
}
|
||||
|
||||
func TestReadCandidateTreePreservesGitProcessError(t *testing.T) {
|
||||
repository := newTreeRepository(t)
|
||||
_, err := ReadCandidateTree(context.Background(), repository, strings.Repeat("f", 40))
|
||||
var exitErr *exec.ExitError
|
||||
if !errors.As(err, &exitErr) {
|
||||
t.Fatalf("ReadCandidateTree() error = %v, want Git process error preserved", err)
|
||||
}
|
||||
}
|
||||
|
||||
type gitTreeEntry struct {
|
||||
mode string
|
||||
kind string
|
||||
size int64
|
||||
hasSize bool
|
||||
}
|
||||
|
||||
func newTreeRepository(t *testing.T) string {
|
||||
t.Helper()
|
||||
repository := t.TempDir()
|
||||
gitTree(t, repository, "init", "-b", "main")
|
||||
gitTree(t, repository, "config", "user.name", "ReleaseKit Test")
|
||||
gitTree(t, repository, "config", "user.email", "releasekit@example.invalid")
|
||||
return repository
|
||||
}
|
||||
|
||||
func writeTreeFile(t *testing.T, repository, name string, content []byte) {
|
||||
t.Helper()
|
||||
path := filepath.Join(repository, filepath.FromSlash(name))
|
||||
if err := os.MkdirAll(filepath.Dir(path), 0o755); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := os.WriteFile(path, content, 0o644); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func gitTree(t *testing.T, repository string, args ...string) string {
|
||||
t.Helper()
|
||||
command := exec.Command("git", args...)
|
||||
command.Dir = repository
|
||||
output, err := command.CombinedOutput()
|
||||
if err != nil {
|
||||
t.Fatalf("git %s: %v\n%s", strings.Join(args, " "), err, output)
|
||||
}
|
||||
return string(output)
|
||||
}
|
||||
Reference in New Issue
Block a user