package validation import ( "context" "errors" "path/filepath" "strings" "testing" ) func TestRunFileChecksUseSelectedInputsAndFullReferenceLookup(t *testing.T) { repository := newTreeRepository(t) writeTreeFile(t, repository, "go.mod", []byte("module example.com/root\n\ngo 1.25.0\nreplace example.com/rootdep => ./missing\n")) writeTreeFile(t, repository, "go.work", []byte("go 1.25.0\n")) writeTreeFile(t, repository, "outside.go", []byte("package outside\nfunc broken( ){}\n")) writeTreeFile(t, repository, "outside.bin", []byte(strings.Repeat("x", 160))) writeTreeFile(t, repository, "nested/go.mod", []byte("module example.com/nested\n\ngo 1.25.0\n")) writeTreeFile(t, repository, "nested/source.go", []byte("package nested\n\nfunc ready() {}\n")) writeTreeFile(t, repository, "nested/ignored.bin", []byte(strings.Repeat("x", 160))) writeTreeFile(t, repository, "nested/docs/guide.md", []byte("# Guide\n\n[details](../../sibling/docs/target.md#details)\n")) writeTreeFile(t, repository, "nested/child/go.mod", []byte("module example.com/nested/child\n")) writeTreeFile(t, repository, "nested/child/broken.go", []byte("package child\nfunc broken( ){}\n")) writeTreeFile(t, repository, "nested/child/go.work", []byte("go 1.25.0\n")) writeTreeFile(t, repository, "sibling/go.mod", []byte("module example.com/sibling\n\nreplace example.com/siblingdep => ./missing\n")) writeTreeFile(t, repository, "sibling/go.work", []byte("go 1.25.0\n")) writeTreeFile(t, repository, "sibling/broken.go", []byte("package sibling\nfunc broken( ){}\n")) writeTreeFile(t, repository, "sibling/outside.bin", []byte(strings.Repeat("x", 160))) writeTreeFile(t, repository, "sibling/docs/target.md", []byte("# Details\n\n[broken](missing.md)\n")) writeTreeFile(t, repository, "shared/broken.go", []byte("package shared\nfunc broken( ){}\n")) commit := commitCandidateTree(t, repository) installGoShim(t, "", false) moduleDir := filepath.Join(repository, "nested") settings := Settings{ GoFormat: true, ModuleDistribution: ModuleDistributionSettings{ WorkspaceFiles: true, LocalReplacements: true, }, FileSize: &FileSizeSettings{LimitBytes: int64Pointer(100), Exclusions: []string{"ignored.bin"}}, Markdown: &MarkdownSettings{EnforceCheckoutBoundary: true}, } request := Request{RepositoryDir: repository, ModuleDir: moduleDir, Commit: commit, Settings: settings} if err := Run(context.Background(), request); err != nil { t.Fatalf("selected module checks = %v, want out-of-scope violations ignored and cross-module anchor resolved", err) } request.Settings.FileSize.Exclusions = nil err := Run(context.Background(), request) assertScopedFailure(t, err, FileSize, "nested/ignored.bin") writeTreeFile(t, repository, "nested/broken.go", []byte("package nested\nfunc broken( ){}\n")) request.Commit = commitCandidateTree(t, repository) request.Settings = Settings{GoFormat: true} assertScopedFailure(t, Run(context.Background(), request), GoFormat, "nested/broken.go") writeTreeFile(t, repository, "nested/go.work", []byte("go 1.25.0\n")) request.Commit = commitCandidateTree(t, repository) request.Settings = Settings{ModuleDistribution: ModuleDistributionSettings{WorkspaceFiles: true}} assertScopedFailure(t, Run(context.Background(), request), WorkspaceFiles, "nested/go.work") writeTreeFile(t, repository, "nested/go.mod", []byte("module example.com/nested\n\ngo 1.25.0\nreplace example.com/nesteddep => ./missing\n")) request.Commit = commitCandidateTree(t, repository) request.Settings = Settings{ModuleDistribution: ModuleDistributionSettings{LocalReplacements: true}} assertScopedFailure(t, Run(context.Background(), request), LocalReplacements, "nested/go.mod") writeTreeFile(t, repository, "nested/docs/guide.md", []byte("# Guide\n\n[details](../../sibling/docs/target.md#details)\n\n[missing](absent.md)\n")) request.Commit = commitCandidateTree(t, repository) request.Settings = Settings{Markdown: &MarkdownSettings{}} assertScopedFailure(t, Run(context.Background(), request), MarkdownLinks, "nested/docs/guide.md") request.Settings = Settings{GoFormat: true, SharedFiles: []string{"shared/broken.go"}} assertScopedFailure(t, Run(context.Background(), request), GoFormat, "shared/broken.go") } func assertScopedFailure(t *testing.T, err error, check Check, path string) { t.Helper() failure, ok := err.(*Failure) if !ok || failure.Check != check { t.Fatalf("validation error = %v, want %s failure", err, check) } for _, finding := range failure.Findings { if finding.Path == path { return } } t.Fatalf("%s findings = %#v, want path %q", check, failure.Findings, path) } func TestFileSizeExclusionsAreModuleRelative(t *testing.T) { repository := newTreeRepository(t) writeTreeFile(t, repository, "nested/go.mod", []byte("module example.com/nested\n")) writeTreeFile(t, repository, "nested/dist/output.bin", []byte(strings.Repeat("x", 20))) writeTreeFile(t, repository, "nested/dist-old/output.bin", []byte(strings.Repeat("x", 20))) commit := commitCandidateTree(t, repository) tree, err := ReadCandidateTreeForScope(context.Background(), repository, commit, TreeScope{ModuleDir: filepath.Join(repository, "nested")}) if err != nil { t.Fatal(err) } cleanupCandidateTree(t, tree) settings := fileSizeSettingsForModule(&FileSizeSettings{LimitBytes: int64Pointer(10), Exclusions: []string{"./dist/"}}, tree.modulePath) err = checkFileSize(tree, settings) var failure *Failure if !errors.As(err, &failure) || failure.Check != FileSize || len(failure.Findings) != 1 || failure.Findings[0].Path != "nested/dist-old/output.bin" { t.Fatalf("module-relative file-size exclusion error = %v, want only nested/dist-old/output.bin", err) } } func TestSharedFileSelectionFailureCarriesCauseAndPresentation(t *testing.T) { repository := newTreeRepository(t) writeTreeFile(t, repository, "go.mod", []byte("module example.com/root\n")) commit := commitCandidateTree(t, repository) installGoShim(t, "", false) 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 || failure.Cause == nil || len(failure.Findings) != 1 || failure.Findings[0].Path != "missing.txt" { t.Fatalf("shared-file selection error = %#v, want path, cause, and shared-files identity", err) } if !strings.Contains(err.Error(), "shared-files") || !strings.Contains(err.Error(), "missing.txt") { t.Fatalf("shared-file error text = %q, want existing check/path presentation", err) } }