Select committed validation inputs by module
This commit is contained in:
@@ -78,6 +78,209 @@ func TestReadCandidateTreeUsesCapturedCommitAndEnumeratesGitEntries(t *testing.T
|
||||
}
|
||||
}
|
||||
|
||||
func TestReadCandidateTreeForScopeExcludesNestedModulesAndRetainsFullLookup(t *testing.T) {
|
||||
repository := newTreeRepository(t)
|
||||
writeTreeFile(t, repository, "COPYRIGHT", []byte("copyright\n"))
|
||||
writeTreeFile(t, repository, "LICENSE", []byte("license\n"))
|
||||
writeTreeFile(t, repository, "docs/oss/attribution.md", []byte("attribution\n"))
|
||||
writeTreeFile(t, repository, "go.mod", []byte("module example.com/root\n"))
|
||||
writeTreeFile(t, repository, "root.go", []byte("package root\n"))
|
||||
writeTreeFile(t, repository, "parent/go.mod", []byte("module example.com/parent\n"))
|
||||
writeTreeFile(t, repository, "parent/main.go", []byte("package parent\n"))
|
||||
writeTreeFile(t, repository, "parent/vendor/library/go.mod", []byte("not a boundary\n"))
|
||||
writeTreeFile(t, repository, "parent/vendor/library/source.go", []byte("package library\n"))
|
||||
writeTreeFile(t, repository, "parent/nested/go.mod", []byte("module example.com/nested\n"))
|
||||
writeTreeFile(t, repository, "parent/nested/source.go", []byte("package nested\n"))
|
||||
writeTreeFile(t, repository, "sibling/go.mod", []byte("module example.com/sibling\n"))
|
||||
writeTreeFile(t, repository, "sibling/source.go", []byte("package sibling\n"))
|
||||
commit := commitCandidateTree(t, repository)
|
||||
writeTreeFile(t, repository, "parent/worktree-only.go", []byte("package parent\n"))
|
||||
|
||||
tree, err := ReadCandidateTreeForScope(context.Background(), repository, commit, TreeScope{
|
||||
ModuleDir: filepath.Join(repository, "parent"),
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
cleanupCandidateTree(t, tree)
|
||||
var got []string
|
||||
for _, entry := range tree.InputEntries() {
|
||||
got = append(got, entry.Path)
|
||||
}
|
||||
want := []string{
|
||||
"COPYRIGHT", "LICENSE", "docs/oss/attribution.md", "parent/go.mod", "parent/main.go",
|
||||
"parent/vendor/library/go.mod", "parent/vendor/library/source.go",
|
||||
}
|
||||
if strings.Join(got, "\n") != strings.Join(want, "\n") {
|
||||
t.Fatalf("selected inputs = %q, want %q", got, want)
|
||||
}
|
||||
if _, ok := tree.Entry("parent/nested/source.go"); !ok {
|
||||
t.Fatal("full tree lookup lost an excluded nested module destination")
|
||||
}
|
||||
destination, err := tree.ResolveDestination(context.Background(), "parent/main.go", "../sibling/source.go")
|
||||
if err != nil || destination.Kind != DestinationEntry || destination.Path != "sibling/source.go" {
|
||||
t.Fatalf("excluded sibling destination = %#v, %v", destination, err)
|
||||
}
|
||||
nestedDestination, err := tree.ResolveDestination(context.Background(), "parent/main.go", "nested/source.go")
|
||||
if err != nil || nestedDestination.Kind != DestinationEntry || nestedDestination.Path != "parent/nested/source.go" {
|
||||
t.Fatalf("excluded nested-module destination = %#v, %v", nestedDestination, err)
|
||||
}
|
||||
if tree.blobReader != nil || tree.blobReads != 0 {
|
||||
t.Fatalf("metadata-only selection read blob content: reader=%v reads=%d", tree.blobReader != nil, tree.blobReads)
|
||||
}
|
||||
}
|
||||
|
||||
func TestReadCandidateTreeForRootScopeExcludesNestedModules(t *testing.T) {
|
||||
repository := newTreeRepository(t)
|
||||
writeTreeFile(t, repository, "go.mod", []byte("module example.com/root\n"))
|
||||
writeTreeFile(t, repository, "root.go", []byte("package root\n"))
|
||||
writeTreeFile(t, repository, "nested/go.mod", []byte("module example.com/nested\n"))
|
||||
writeTreeFile(t, repository, "nested/source.go", []byte("package nested\n"))
|
||||
writeTreeFile(t, repository, "vendor/library/go.mod", []byte("not a boundary\n"))
|
||||
writeTreeFile(t, repository, "vendor/library/source.go", []byte("package library\n"))
|
||||
commit := commitCandidateTree(t, repository)
|
||||
|
||||
tree, err := ReadCandidateTreeForScope(context.Background(), repository, commit, TreeScope{ModuleDir: repository})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
cleanupCandidateTree(t, tree)
|
||||
want := []string{"go.mod", "root.go", "vendor/library/go.mod", "vendor/library/source.go"}
|
||||
if got := inputPaths(tree); strings.Join(got, "\n") != strings.Join(want, "\n") {
|
||||
t.Fatalf("root module inputs = %q, want %q", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestReadCandidateTreeSharedFilesReplaceDefaultsAndDeduplicate(t *testing.T) {
|
||||
repository := newTreeRepository(t)
|
||||
writeTreeFile(t, repository, "LICENSE", []byte("default\n"))
|
||||
writeTreeFile(t, repository, "module/go.mod", []byte("module example.com/module\n"))
|
||||
writeTreeFile(t, repository, "module/source.go", []byte("package module\n"))
|
||||
writeTreeFile(t, repository, "sibling/shared.txt", []byte("selected explicitly\n"))
|
||||
writeTreeFile(t, repository, "literal/*.md", []byte("literal wildcard path\n"))
|
||||
commit := commitCandidateTree(t, repository)
|
||||
moduleDir := filepath.Join(repository, "module")
|
||||
|
||||
selected, err := ReadCandidateTreeForScope(context.Background(), repository, commit, TreeScope{
|
||||
ModuleDir: moduleDir,
|
||||
SharedFiles: []string{"sibling/shared.txt", "module/source.go", "sibling/shared.txt"},
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
cleanupCandidateTree(t, selected)
|
||||
var got []string
|
||||
for _, entry := range selected.InputEntries() {
|
||||
got = append(got, entry.Path)
|
||||
}
|
||||
want := []string{"module/go.mod", "module/source.go", "sibling/shared.txt"}
|
||||
if strings.Join(got, "\n") != strings.Join(want, "\n") {
|
||||
t.Fatalf("explicit shared inputs = %q, want %q", got, want)
|
||||
}
|
||||
|
||||
empty, err := ReadCandidateTreeForScope(context.Background(), repository, commit, TreeScope{
|
||||
ModuleDir: moduleDir,
|
||||
SharedFiles: []string{},
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
cleanupCandidateTree(t, empty)
|
||||
if got := inputPaths(empty); strings.Join(got, "\n") != "module/go.mod\nmodule/source.go" {
|
||||
t.Fatalf("explicit empty input set = %q", got)
|
||||
}
|
||||
|
||||
literal, err := ReadCandidateTreeForScope(context.Background(), repository, commit, TreeScope{
|
||||
ModuleDir: moduleDir,
|
||||
SharedFiles: []string{"literal/*.md"},
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
cleanupCandidateTree(t, literal)
|
||||
if got := inputPaths(literal); strings.Join(got, "\n") != "literal/*.md\nmodule/go.mod\nmodule/source.go" {
|
||||
t.Fatalf("literal wildcard path inputs = %q", got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestReadCandidateTreeDefaultsAreOptionalAndExplicitMissingFilesFail(t *testing.T) {
|
||||
repository := newTreeRepository(t)
|
||||
writeTreeFile(t, repository, "module/go.mod", []byte("module example.com/module\n"))
|
||||
writeTreeFile(t, repository, "module/source.go", []byte("package module\n"))
|
||||
writeTreeFile(t, repository, "LICENSE", []byte("license\n"))
|
||||
commit := commitCandidateTree(t, repository)
|
||||
moduleDir := filepath.Join(repository, "module")
|
||||
|
||||
defaults, err := ReadCandidateTreeForScope(context.Background(), repository, commit, TreeScope{ModuleDir: moduleDir})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
cleanupCandidateTree(t, defaults)
|
||||
if got := inputPaths(defaults); strings.Join(got, "\n") != "LICENSE\nmodule/go.mod\nmodule/source.go" {
|
||||
t.Fatalf("default shared inputs = %q", got)
|
||||
}
|
||||
|
||||
writeTreeFile(t, repository, "missing.md", []byte("present only in the worktree\n"))
|
||||
_, err = ReadCandidateTreeForScope(context.Background(), repository, commit, TreeScope{
|
||||
ModuleDir: moduleDir,
|
||||
SharedFiles: []string{"missing.md"},
|
||||
})
|
||||
var failure *Failure
|
||||
if !errors.As(err, &failure) || failure.Check != SharedFiles || len(failure.Findings) != 1 || failure.Findings[0].Path != "missing.md" {
|
||||
t.Fatalf("missing explicit shared file error = %#v, want path-specific shared-files failure", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRunBuildsInputsOnlyForChecksOrExplicitSharedFiles(t *testing.T) {
|
||||
repository := newTreeRepository(t)
|
||||
writeTreeFile(t, repository, "go.mod", []byte("module example.com/validation-tree\n\ngo 1.25.0\n"))
|
||||
writeTreeFile(t, repository, "main.go", []byte("package validationtree\n"))
|
||||
commit := commitCandidateTree(t, repository)
|
||||
gitPath, err := exec.LookPath("git")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
bin := filepath.Join(t.TempDir(), "bin")
|
||||
if err := os.MkdirAll(bin, 0o755); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
logPath := filepath.Join(t.TempDir(), "tree-reads")
|
||||
wrapper := "#!/bin/sh\nREAL_GIT='" + gitPath + "'\nLOG='" + logPath + "'\nfor ARG in \"$@\"; do [ \"$ARG\" = ls-tree ] && echo read >> \"$LOG\"; done\nexec \"$REAL_GIT\" \"$@\"\n"
|
||||
if err := os.WriteFile(filepath.Join(bin, "git"), []byte(wrapper), 0o755); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
t.Setenv("PATH", bin+string(os.PathListSeparator)+os.Getenv("PATH"))
|
||||
|
||||
if err := Run(context.Background(), Request{RepositoryDir: repository, Commit: commit}); err != nil {
|
||||
t.Fatalf("metadata-only Run(): %v", err)
|
||||
}
|
||||
if _, err := os.Stat(logPath); !errors.Is(err, os.ErrNotExist) {
|
||||
t.Fatalf("default-only request constructed file inputs: stat error=%v", err)
|
||||
}
|
||||
|
||||
err = Run(context.Background(), Request{
|
||||
RepositoryDir: repository,
|
||||
Commit: commit,
|
||||
Settings: Settings{SharedFiles: []string{"missing.txt"}},
|
||||
})
|
||||
var failure *Failure
|
||||
if !errors.As(err, &failure) || failure.Check != SharedFiles || len(failure.Findings) != 1 || failure.Findings[0].Path != "missing.txt" {
|
||||
t.Fatalf("explicit missing shared file Run() error = %#v", err)
|
||||
}
|
||||
if data, err := os.ReadFile(logPath); err != nil || len(data) == 0 {
|
||||
t.Fatalf("explicit shared-file request did not construct committed file inputs: %q, %v", data, err)
|
||||
}
|
||||
}
|
||||
|
||||
func inputPaths(tree *CandidateTree) []string {
|
||||
entries := tree.InputEntries()
|
||||
paths := make([]string, 0, len(entries))
|
||||
for _, entry := range entries {
|
||||
paths = append(paths, entry.Path)
|
||||
}
|
||||
return paths
|
||||
}
|
||||
|
||||
func TestReadFileReturnsOwnedBytesAndCachesOnlySymlinkTargets(t *testing.T) {
|
||||
repository := newTreeRepository(t)
|
||||
writeTreeFile(t, repository, "source.txt", []byte("committed content"))
|
||||
|
||||
Reference in New Issue
Block a user