feat(#141): validate-docmap subcommand — CI hard-fail on missing docmap coverage #142
@@ -66,6 +66,18 @@ func ParseDocMapConfig(localPath string) (*DocMapConfig, error) {
|
|||||||
return &cfg, nil
|
return &cfg, nil
|
||||||
|
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
sonnet-review-bot
commented
[NIT] **[NIT]** `FileCoveredByDocMap` doc comment says 'It is used by static validation tooling (e.g. the validate-docmap subcommand)'. Per the documentation pattern, the doc comment should start with the function name: `// FileCoveredByDocMap reports whether...` — the current phrasing already does start correctly but the second sentence adds implementation context that per the patterns belongs in a separate paragraph or omitted from the primary sentence. Very minor.
|
|||||||
|
// FileCoveredByDocMap reports whether at least one paths: glob in any mapping
|
||||||
|
// of cfg matches the given file path. It is used by static validation tooling
|
||||||
|
// (e.g. the validate-docmap subcommand) to check per-file docmap coverage.
|
||||||
|
func FileCoveredByDocMap(cfg *DocMapConfig, file string) bool {
|
||||||
|
for _, mapping := range cfg.Mappings {
|
||||||
|
if mappingMatches(mapping.Paths, []string{file}) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
// MatchDocs returns deduplicated doc paths for the given changed file paths.
|
// MatchDocs returns deduplicated doc paths for the given changed file paths.
|
||||||
// A mapping matches if any of its path globs matches any of the changed files.
|
// A mapping matches if any of its path globs matches any of the changed files.
|
||||||
func MatchDocs(cfg *DocMapConfig, changedFiles []string) []string {
|
func MatchDocs(cfg *DocMapConfig, changedFiles []string) []string {
|
||||||
|
|||||||
@@ -436,3 +436,52 @@ func writeTempYAML(t *testing.T, content string) string {
|
|||||||
}
|
}
|
||||||
return filepath.Clean(f.Name())
|
return filepath.Clean(f.Name())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ============================================================
|
||||||
|
// FileCoveredByDocMap
|
||||||
|
// ============================================================
|
||||||
|
|
||||||
|
func TestFileCoveredByDocMap(t *testing.T) {
|
||||||
|
cfg := &DocMapConfig{
|
||||||
|
Mappings: []DocMapping{
|
||||||
|
{
|
||||||
|
Paths: []string{"lib/foo/**", "lib/bar/*.go"},
|
||||||
|
Docs: []string{"docs/foo.md"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Paths: []string{"cmd/**"},
|
||||||
|
Docs: []string{"docs/cmd.md"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
cases := []struct {
|
||||||
|
file string
|
||||||
|
covered bool
|
||||||
|
}{
|
||||||
|
{"lib/foo/baz.ex", true},
|
||||||
|
{"lib/foo/sub/deep.ex", true},
|
||||||
|
{"lib/bar/util.go", true},
|
||||||
|
{"lib/bar/sub/util.go", false}, // *.go only matches one level
|
||||||
|
{"cmd/main.go", true},
|
||||||
|
{"cmd/sub/main.go", true},
|
||||||
|
{"internal/secret.go", false},
|
||||||
|
{"", false},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tc := range cases {
|
||||||
|
t.Run(tc.file, func(t *testing.T) {
|
||||||
|
got := FileCoveredByDocMap(cfg, tc.file)
|
||||||
|
if got != tc.covered {
|
||||||
|
t.Errorf("FileCoveredByDocMap(%q) = %v, want %v", tc.file, got, tc.covered)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFileCoveredByDocMap_EmptyConfig(t *testing.T) {
|
||||||
|
cfg := &DocMapConfig{}
|
||||||
|
if FileCoveredByDocMap(cfg, "lib/foo/bar.go") {
|
||||||
|
t.Error("expected false for empty config, got true")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user
[MINOR] FileCoveredByDocMap assumes the caller passes forward-slash paths. Since it's exported, consider normalizing backslashes to forward slashes internally (as done in the CLI) or documenting this requirement in the function comment.