// Package failurekit refuses a build module whose tests do not run mesh-sdk's conformance kit (issue 1901, its thorough // fix): a module that claims a node-build-* seat and serves a test step must hold a test that calls conformance.Run, so // its parse of the test runner's output is held to the failure contract (a failing test named with its cause, never a // passing line in its place). A build module with no test step (mesh-build-image builds images and runs no tests) is // exempt, and said so. package failurekit import ( "encoding/json" "fmt" "io/fs" "os" "path/filepath" "sort" "strings" ) // Judged is what the check says of one build module: Refused says why it fails the check, "" when it passes; Exempt // says why the kit does not apply. type Judged struct { Module, Refused, Exempt string } type manifest struct { Claims []struct { Name string `json:"name"` } `json:"claims"` Tools []json.RawMessage `json:"tools"` } // Judge reads every modules//module.json of the catalogue at root and judges each module that claims a node-build-* // seat. func Judge(root string) ([]Judged, error) { manifests, err := filepath.Glob(filepath.Join(root, "modules", "*", "module.json")) if err != nil { return nil, err } sort.Strings(manifests) var out []Judged for _, p := range manifests { raw, err := os.ReadFile(p) if err != nil { return nil, err } var m manifest if err := json.Unmarshal(raw, &m); err != nil { return nil, fmt.Errorf("%s: %w", p, err) } seat := "" for _, c := range m.Claims { if strings.HasPrefix(c.Name, "node-build-") { seat = c.Name } } if seat == "" { continue } dir := filepath.Dir(p) j := Judged{Module: filepath.Base(dir)} test := testTool(m.Tools) switch { case test == "": j.Exempt = fmt.Sprintf("it claims %s and serves no test step", seat) default: runs, err := runsTheKit(dir) if err != nil { return nil, err } if !runs { j.Refused = fmt.Sprintf("it claims %s and serves the test step %s, and no test of it calls conformance.Run "+ "(mesh-sdk go/buildstep/conformance): run the kit with the module's own parse path (issue 1901)", seat, test) } } out = append(out, j) } return out, nil } // testTool is the name of the module's test step tool (_test), or "". func testTool(tools []json.RawMessage) string { for _, raw := range tools { var name string if json.Unmarshal(raw, &name) != nil { var t struct { Name string `json:"name"` } if json.Unmarshal(raw, &t) != nil { continue } name = t.Name } if strings.HasSuffix(name, "_test") { return name } } return "" } // runsTheKit says a _test.go file under dir calls conformance.Run. func runsTheKit(dir string) (bool, error) { found := false err := filepath.WalkDir(dir, func(p string, d fs.DirEntry, err error) error { if err != nil || found || d.IsDir() || !strings.HasSuffix(p, "_test.go") { return err } raw, err := os.ReadFile(p) if err != nil { return err } found = strings.Contains(string(raw), "conformance.Run(") && strings.Contains(string(raw), `"git.novox.be/novox/mesh-sdk/go/buildstep/conformance"`) return nil }) return found, err }