CAT-BG5: mesh-build-go regenerates a contracts module's Go files and names each out of step
The unit level fails on a file that differs, is missing or was not generated; a build ask answers the same files in its out-of-step, a failed build's too, so a trunk build's end raises generated-types-out-of-step (lead's ruling on A2k finding 1). The compare is buildstep's, each build module its own root, with the seat generator as a requirement. Claude-Session: https://claude.ai/code/session_01JG73KJNrH5EgFXMKKYSrig
This commit is contained in:
@@ -142,6 +142,12 @@ trunk build's, in chunks, only what the holder lacks; the holder asks the builde
|
||||
seat's events. The step answers the seat's form `oci-artifact-registry/<module>/<artifact>@<digest>` (ADR 0605 §9); the
|
||||
builder writes the older form beside it while a counted node-engine reads only that (ADR 0624); a publish the holder refuses, or a
|
||||
seat nothing holds, fails the step `not stored: <artifact>: <the holder's words>`. The packed bytes never stay on the node.
|
||||
A contracts module (a package artifact naming `generated-from`) has its generated Go files regenerated with the seat
|
||||
generator this module is built with and compared (`buildstep/generated.go`): the unit level of `build_go_test` fails
|
||||
naming each file that differs, is missing or was not written by the generator (its `out-of-step`), and a build ask
|
||||
answers the same files in its answer's `out-of-step`, a failed build's too, so a trunk build's end can raise
|
||||
`generated-types-out-of-step` where no test step runs. Its TypeScript files are mesh-build-node's to compare.
|
||||
|
||||
The module writes into no store itself and requires no `artifact-store`. A Go package (kind `package`, language `go`; `gopackage.go`) is
|
||||
the directory its one path names packed as Go's module zip, every file under `<module path>@<version>/`, its digest the
|
||||
zip's (no `h1:` sum is answered: nothing carries it on); a trunk build the builder gave a version publishes it through
|
||||
|
||||
@@ -29,6 +29,10 @@ type AskSteps struct {
|
||||
// {"imports": [...]}, sorted, from the workspace the build ran in. A build that built an artifact asks it once, at
|
||||
// its end; nil reports none.
|
||||
Imports StepFunc
|
||||
// Compare regenerates a contracts module's generated files (a package artifact naming generated-from) and answers
|
||||
// {"out-of-step": [...]} (CompareStep): a build ask runs it once on the fetched workspace and answers the paths in
|
||||
// its out-of-step, a failed build's too, so a trunk build's end names them where no test step runs. Nil compares none.
|
||||
Compare StepFunc
|
||||
// Toolchain is the toolchain the module builds with now; a build ask naming another is declined (Declining). Nil checks nothing
|
||||
// (an image or an archive names none).
|
||||
Toolchain func(ctx context.Context) (delivery.ToolchainUsed, error)
|
||||
@@ -156,6 +160,24 @@ func (s AskSteps) build(ctx context.Context, job delivery.BuildJob) (any, error)
|
||||
}
|
||||
}
|
||||
f = got
|
||||
if s.Compare != nil {
|
||||
Phase(ctx, "compare the generated files")
|
||||
out, err := s.Compare(ctx, map[string]any{"workspace": f.Workspace, "module": job.Path})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("the generated files of %s: %w", job.Module, err)
|
||||
}
|
||||
r, err := raw(out)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var read struct {
|
||||
OutOfStep []string `json:"out-of-step"`
|
||||
}
|
||||
if err := json.Unmarshal(r, &read); err != nil {
|
||||
return nil, fmt.Errorf("the compare of %s answered what is not a list: %s", job.Module, r)
|
||||
}
|
||||
answer.OutOfStep = read.OutOfStep
|
||||
}
|
||||
}
|
||||
var (
|
||||
out any
|
||||
@@ -186,6 +208,10 @@ func (s AskSteps) build(ctx context.Context, job delivery.BuildJob) (any, error)
|
||||
out, err = s.Build(ctx, args)
|
||||
}
|
||||
if err != nil {
|
||||
// The compare's out-of-step is answered with the failure, so the build's end still names it.
|
||||
if len(answer.OutOfStep) > 0 {
|
||||
return answer, fmt.Errorf("%s: %w", a.Artifact, err)
|
||||
}
|
||||
return nil, fmt.Errorf("%s: %w", a.Artifact, err)
|
||||
}
|
||||
r, err := raw(out)
|
||||
|
||||
@@ -0,0 +1,132 @@
|
||||
package buildstep
|
||||
|
||||
// **Regenerate and compare** (CAT-BG5, CAT-BN2; novox/hq ADR 0608 §5, design 26 *Contracts modules*). A module whose
|
||||
// package artifact names `generated-from` holds contracts only: every file under its generated roots is written by the
|
||||
// seat generator (seatgen) from the definitions in that directory. A build module regenerates them with the generator
|
||||
// it was built with (its version is this package's requirement, so a new generator is a new build module and a new
|
||||
// fingerprint of what it builds) and names each file out of step: one that differs, one missing, one under a generated
|
||||
// root the generator did not write. The test step fails on any; a trunk build's ask answers them in its out-of-step
|
||||
// (delivery.BuildAnswer.OutOfStep), so the builder's build end raises generated-types-out-of-step even where no test
|
||||
// step runs. Each build module compares its own roots: mesh-build-go `go`, mesh-build-node `ts`.
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"git.novox.be/novox/mesh-sdk/go/catalogue"
|
||||
"git.novox.be/novox/seatgen/seatgen"
|
||||
)
|
||||
|
||||
// GeneratedRootGo and GeneratedRootTS are the generated roots of a contracts module, one per build module.
|
||||
const (
|
||||
GeneratedRootGo = "go"
|
||||
GeneratedRootTS = "ts"
|
||||
)
|
||||
|
||||
var goModuleLine = regexp.MustCompile(`(?m)^module\s+(\S+)`)
|
||||
|
||||
// OutOfStep is every file under root, a path from the module's directory, that the generator writes otherwise than the
|
||||
// module holds it, for each `generated-from` its package artifacts name, sorted and each once; none for a module naming
|
||||
// none. The generated Go tree's module path is read from <from>/go/go.mod and the TypeScript package's name from
|
||||
// <from>/ts/package.json, as the contracts module states them.
|
||||
func OutOfStep(moduleDir, root string) ([]string, error) {
|
||||
raw, err := os.ReadFile(filepath.Join(moduleDir, "module.json"))
|
||||
if errors.Is(err, os.ErrNotExist) {
|
||||
return nil, nil // no manifest here: nothing names generated-from
|
||||
}
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("the module's manifest cannot be read for its generated files: %w", err)
|
||||
}
|
||||
var m catalogue.Manifest
|
||||
if err := json.Unmarshal(raw, &m); err != nil {
|
||||
return nil, fmt.Errorf("the module's manifest does not parse: %w", err)
|
||||
}
|
||||
froms := map[string]bool{}
|
||||
if m.Build != nil {
|
||||
for _, a := range m.Build.Artifacts {
|
||||
if a.GeneratedFrom != "" {
|
||||
froms[filepath.Clean(a.GeneratedFrom)] = true
|
||||
}
|
||||
}
|
||||
}
|
||||
seen := map[string]bool{}
|
||||
for from := range froms {
|
||||
if filepath.IsAbs(from) || from == ".." || strings.HasPrefix(from, "../") {
|
||||
return nil, fmt.Errorf("generated-from %q is not inside the module", from)
|
||||
}
|
||||
dir := filepath.Join(moduleDir, from)
|
||||
goModule, tsPackage, err := generatedNames(dir)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
g, err := seatgen.Load(dir, goModule, tsPackage)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("the seat definitions in %s do not load: %w", from, err)
|
||||
}
|
||||
files, err := seatgen.Generate(g)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("the seat definitions in %s do not generate: %w", from, err)
|
||||
}
|
||||
out, err := seatgen.Compare(dir, files)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, line := range out {
|
||||
p, _, _ := strings.Cut(line, ": ")
|
||||
if p == root || strings.HasPrefix(p, root+"/") {
|
||||
seen[filepath.ToSlash(filepath.Join(from, p))] = true
|
||||
}
|
||||
}
|
||||
}
|
||||
paths := make([]string, 0, len(seen))
|
||||
for p := range seen {
|
||||
paths = append(paths, p)
|
||||
}
|
||||
sort.Strings(paths)
|
||||
return paths, nil
|
||||
}
|
||||
|
||||
func generatedNames(dir string) (goModule, tsPackage string, err error) {
|
||||
if raw, err := os.ReadFile(filepath.Join(dir, GeneratedRootGo, "go.mod")); err == nil {
|
||||
if m := goModuleLine.FindSubmatch(raw); m != nil {
|
||||
goModule = string(m[1])
|
||||
}
|
||||
}
|
||||
if raw, err := os.ReadFile(filepath.Join(dir, GeneratedRootTS, "package.json")); err == nil {
|
||||
var p struct {
|
||||
Name string `json:"name"`
|
||||
}
|
||||
if json.Unmarshal(raw, &p) == nil {
|
||||
tsPackage = p.Name
|
||||
}
|
||||
}
|
||||
if goModule == "" || tsPackage == "" {
|
||||
return "", "", errors.New("a contracts module names its generated Go module in go/go.mod and its TypeScript package in ts/package.json; " +
|
||||
filepath.Base(dir) + " names " + map[bool]string{true: "not the TypeScript package", false: "not the Go module"}[goModule != ""])
|
||||
}
|
||||
return goModule, tsPackage, nil
|
||||
}
|
||||
|
||||
// CompareStep is AskSteps.Compare for a build module comparing root: it reads {"workspace", "module"}, the workspace's
|
||||
// tree where tree says it is (the build module's own layout), and answers {"out-of-step": [...]}.
|
||||
func CompareStep(root string, tree func(workspace string) string) StepFunc {
|
||||
return func(_ context.Context, args map[string]any) (any, error) {
|
||||
ws, _ := args["workspace"].(string)
|
||||
mod, _ := args["module"].(string)
|
||||
if ws == "" {
|
||||
return nil, Refuse("the compare names no workspace")
|
||||
}
|
||||
paths, err := OutOfStep(filepath.Join(tree(ws), filepath.FromSlash(mod)), root)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return map[string]any{"out-of-step": paths}, nil
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,134 @@
|
||||
package buildstep
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
"sort"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"git.novox.be/novox/mesh-sdk/go/delivery"
|
||||
"git.novox.be/novox/seatgen/seatgen"
|
||||
)
|
||||
|
||||
// contracts writes a contracts module of one seat, its generated files as the generator writes them, and answers its
|
||||
// directory and the generated paths.
|
||||
func contracts(t *testing.T, manifest string) (string, []string) {
|
||||
t.Helper()
|
||||
dir := t.TempDir()
|
||||
for p, text := range map[string]string{
|
||||
"module.json": manifest,
|
||||
"seats/index.json": `{"format":1,"seats":["one-store"]}`,
|
||||
"seats/one-store/seat.json": `{"format":1,"seat":"one-store","scope":"mesh","grain":"seat-list","decision":"ADR 1","verbs":[{"name":"query","input":{"type":"object"}}]}`,
|
||||
} {
|
||||
writeContract(t, filepath.Join(dir, p), text)
|
||||
}
|
||||
m, err := seatgen.Load(dir, "example.org/c/go", "@example/c")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
files, err := seatgen.Generate(m)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
var paths []string
|
||||
for p, b := range files {
|
||||
writeContract(t, filepath.Join(dir, p), string(b))
|
||||
paths = append(paths, p)
|
||||
}
|
||||
// The names the compare reads, where the contracts module states them; a generated go.mod or package.json is kept.
|
||||
if _, ok := files["go/go.mod"]; !ok {
|
||||
writeContract(t, filepath.Join(dir, "go/go.mod"), "module example.org/c/go\n")
|
||||
}
|
||||
if _, ok := files["ts/package.json"]; !ok {
|
||||
writeContract(t, filepath.Join(dir, "ts/package.json"), `{"name":"@example/c"}`)
|
||||
}
|
||||
return dir, paths
|
||||
}
|
||||
|
||||
func writeContract(t *testing.T, path, text string) {
|
||||
t.Helper()
|
||||
if err := os.MkdirAll(filepath.Dir(path), 0o755); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := os.WriteFile(path, []byte(text), 0o644); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
// **CAT-BG5, CAT-BN2**: a contracts module's generated files are regenerated and compared, each build module its own
|
||||
// root; a file that differs, one missing and one the generator did not write are each named; a clean tree and a module
|
||||
// naming no generated-from name none; a build ask answers them in its out-of-step.
|
||||
func TestAContractsModulesGeneratedFilesAreComparedWithTheGenerator(t *testing.T) {
|
||||
marked := `{"module":"c","build":{"artifacts":[{"name":"go","kind":"package","language":"go","paths":["go"],"generated-from":"."}]}}`
|
||||
dir, paths := contracts(t, marked)
|
||||
var goFile, tsFile string
|
||||
for _, p := range paths {
|
||||
if strings.HasPrefix(p, "go/") && strings.HasSuffix(p, ".go") && goFile == "" {
|
||||
goFile = p
|
||||
}
|
||||
if strings.HasPrefix(p, "ts/") && strings.HasSuffix(p, ".ts") && tsFile == "" {
|
||||
tsFile = p
|
||||
}
|
||||
}
|
||||
if goFile == "" || tsFile == "" {
|
||||
t.Fatalf("the generator wrote no Go or no TypeScript file: %v", paths)
|
||||
}
|
||||
for _, root := range []string{GeneratedRootGo, GeneratedRootTS} {
|
||||
if got, err := OutOfStep(dir, root); err != nil || len(got) != 0 {
|
||||
t.Fatalf("a clean tree, root %s: %v %v", root, got, err)
|
||||
}
|
||||
}
|
||||
writeContract(t, filepath.Join(dir, goFile), "package changed\n")
|
||||
writeContract(t, filepath.Join(dir, "go/extra.go"), "package extra\n")
|
||||
writeContract(t, filepath.Join(dir, tsFile), "// changed\n")
|
||||
got, err := OutOfStep(dir, GeneratedRootGo)
|
||||
if want := sorted(goFile, "go/extra.go"); err != nil || !reflect.DeepEqual(got, want) {
|
||||
t.Errorf("Go root: %v %v, want %v", got, err, want)
|
||||
}
|
||||
if got, _ := OutOfStep(dir, GeneratedRootTS); !reflect.DeepEqual(got, []string{tsFile}) {
|
||||
t.Errorf("TypeScript root: %v, want %s alone", got, tsFile)
|
||||
}
|
||||
_ = os.Remove(filepath.Join(dir, goFile))
|
||||
if got, _ := OutOfStep(dir, GeneratedRootGo); !reflect.DeepEqual(got, sorted(goFile, "go/extra.go")) {
|
||||
t.Errorf("a missing file is not named: %v", got)
|
||||
}
|
||||
plain, _ := contracts(t, `{"module":"p","build":{"artifacts":[{"name":"go","kind":"package","language":"go","paths":["go"]}]}}`)
|
||||
writeContract(t, filepath.Join(plain, "go/extra.go"), "package extra\n")
|
||||
if got, err := OutOfStep(plain, GeneratedRootGo); err != nil || len(got) != 0 {
|
||||
t.Errorf("a module naming no generated-from compared: %v %v", got, err)
|
||||
}
|
||||
|
||||
// The build ask answers them beside what it built, and with a failed build too.
|
||||
built := 0
|
||||
steps := AskSteps{Compare: CompareStep(GeneratedRootGo, func(ws string) string { return ws }),
|
||||
Fetch: func(context.Context, map[string]any) (any, error) {
|
||||
return map[string]any{"workspace": filepath.Dir(dir)}, nil
|
||||
},
|
||||
Build: func(context.Context, map[string]any) (any, error) {
|
||||
if built++; built > 1 {
|
||||
return nil, errors.New("the second artifact does not build")
|
||||
}
|
||||
return map[string]any{"digest": "sha256:aa"}, nil
|
||||
}}
|
||||
job := delivery.BuildJob{Module: "c", Path: filepath.Base(dir), Fetch: map[string]any{"repository": "novox/c"},
|
||||
Artifacts: []delivery.AskArtifact{{Artifact: "go"}}}
|
||||
out, err := RunAsk(context.Background(), steps, ask(job))
|
||||
if a, ok := out.(delivery.BuildAnswer); err != nil || !ok || !reflect.DeepEqual(a.OutOfStep, sorted(goFile, "go/extra.go")) || len(a.Artifacts) != 1 {
|
||||
t.Errorf("the build ask's out-of-step: %+v %v", out, err)
|
||||
}
|
||||
job.Artifacts = append(job.Artifacts, delivery.AskArtifact{Artifact: "go-other"})
|
||||
built = 0
|
||||
out, err = RunAsk(context.Background(), steps, ask(job))
|
||||
if a, ok := out.(delivery.BuildAnswer); err == nil || !ok || len(a.OutOfStep) != 2 {
|
||||
t.Errorf("a failed build answered no out-of-step: %+v %v", out, err)
|
||||
}
|
||||
}
|
||||
|
||||
func sorted(paths ...string) []string {
|
||||
sort.Strings(paths)
|
||||
return paths
|
||||
}
|
||||
@@ -4,6 +4,7 @@ go 1.26.0
|
||||
|
||||
require (
|
||||
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261102191058-f02f142af128
|
||||
git.novox.be/novox/seatgen v0.0.0
|
||||
github.com/nats-io/nats.go v1.54.0
|
||||
)
|
||||
|
||||
|
||||
@@ -83,6 +83,12 @@ func finish(put Put, key string, step delivery.Step, result any, err error, logf
|
||||
step.State, step.Reason = delivery.StepRefused, err.Error()
|
||||
default:
|
||||
step.State, step.Reason = delivery.StepFailed, err.Error()
|
||||
// A failed step may answer what it found on the way (a build's out-of-step files): kept on its end.
|
||||
if result != nil {
|
||||
if raw, merr := json.Marshal(result); merr == nil {
|
||||
step.Result = raw
|
||||
}
|
||||
}
|
||||
}
|
||||
werr := writeEnd(put, key, step)
|
||||
if werr == nil {
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
"time"
|
||||
|
||||
"git.novox.be/novox/mesh-catalog/modules/mesh-build-go/buildstep"
|
||||
@@ -38,6 +39,7 @@ func (m *Module) askSteps(steps []StepTool) buildstep.AskSteps {
|
||||
return buildstep.AskSteps{
|
||||
Fetch: byName["build_go_fetch"], Build: byName["build_go_build"], Digest: byName["build_go_digest"], Test: byName["build_go_test"],
|
||||
Imports: byName[ImportsTool],
|
||||
Compare: buildstep.CompareStep(buildstep.GeneratedRootGo, func(ws string) string { return filepath.Join(m.Work, ws, "tree") }),
|
||||
Toolchain: func(ctx context.Context) (delivery.ToolchainUsed, error) {
|
||||
installed, err := m.Probe(ctx)
|
||||
if err != nil {
|
||||
|
||||
@@ -72,6 +72,9 @@ type TestResult struct {
|
||||
// JudgedBy is the program that ran the level (ADR 0535 §5): "installed", or "candidate <merge commit>" under the
|
||||
// runner workspace.
|
||||
JudgedBy string `json:"judged_by,omitempty"`
|
||||
// OutOfStep are the generated files of a contracts module in the workspace out of step with what the generator writes
|
||||
// (CAT-BG5, buildstep.OutOfStep): the unit level fails on any, Why naming each. Absent when none is.
|
||||
OutOfStep []string `json:"out-of-step,omitempty"`
|
||||
// By is the build module and the SDK it was built with (sdkstep.By): the checker names it when a failure hides
|
||||
// its cause.
|
||||
By string `json:"by,omitempty"`
|
||||
|
||||
@@ -15,6 +15,7 @@ import (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"git.novox.be/novox/mesh-catalog/modules/mesh-build-go/buildstep"
|
||||
sdkstep "git.novox.be/novox/mesh-sdk/go/buildstep"
|
||||
"git.novox.be/novox/mesh-sdk/go/delivery"
|
||||
"git.novox.be/novox/mesh-sdk/go/forgekey"
|
||||
@@ -100,9 +101,42 @@ func (m *Module) test(ctx context.Context, args map[string]any) (any, error) {
|
||||
return nil, err
|
||||
}
|
||||
res.JudgedBy = RunnerInstalled
|
||||
if level == LevelUnit {
|
||||
if err := compareGenerated(tree, res); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return res, nil
|
||||
}
|
||||
|
||||
// compareGenerated regenerates the generated Go files of each contracts module in the tree (the tree's own manifest and
|
||||
// each modules/<m>/module.json; CAT-BG5) and fails the level on any out of step, Why naming each path.
|
||||
func compareGenerated(tree string, res *TestResult) error {
|
||||
manifests, _ := filepath.Glob(filepath.Join(tree, "modules", "*", "module.json"))
|
||||
if _, err := os.Stat(filepath.Join(tree, "module.json")); err == nil {
|
||||
manifests = append([]string{filepath.Join(tree, "module.json")}, manifests...)
|
||||
}
|
||||
for _, mf := range manifests {
|
||||
dir := filepath.Dir(mf)
|
||||
paths, err := buildstep.OutOfStep(dir, buildstep.GeneratedRootGo)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
rel, _ := filepath.Rel(tree, dir)
|
||||
for _, p := range paths {
|
||||
res.OutOfStep = append(res.OutOfStep, filepath.ToSlash(filepath.Join(rel, p)))
|
||||
}
|
||||
}
|
||||
if len(res.OutOfStep) > 0 {
|
||||
why := "generated files out of step with the seat generator (regenerate them): " + strings.Join(res.OutOfStep, ", ")
|
||||
if res.Why != "" {
|
||||
why = res.Why + "; " + why
|
||||
}
|
||||
res.Passed, res.Why = false, why
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// testAsCandidate runs a level as the candidate's program (ADR 0535 §1): the integration level is refused first as the
|
||||
// installed program would refuse it (no postgres), then the whole level runs in the candidate's one shut-off process.
|
||||
func (m *Module) testAsCandidate(ctx context.Context, workspace, level string, packages []string, pattern string, names []string) (*TestResult, error) {
|
||||
|
||||
@@ -178,6 +178,40 @@ func TestUnitLevelOfAFixtureWithNoTestsSaysSo(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// CAT-BG5: a contracts module (a package artifact naming generated-from) whose generated Go files are out of step with
|
||||
// the seat generator fails the unit level, each path named in its out-of-step and its why; its TypeScript is not this
|
||||
// module's to compare.
|
||||
func TestUnitLevelOfAContractsModuleOutOfStepFailsNamingEachPath(t *testing.T) {
|
||||
m, states, ws, _ := workspaceFor(t, "empty")
|
||||
tree := filepath.Join(m.Work, ws, "tree")
|
||||
for p, text := range map[string]string{
|
||||
"module.json": `{"module":"c","build":{"artifacts":[{"name":"go","kind":"package","language":"go","paths":["go"],"generated-from":"."}]}}`,
|
||||
"seats/index.json": `{"format":1,"seats":["one-store"]}`,
|
||||
"seats/one-store/seat.json": `{"format":1,"seat":"one-store","scope":"mesh","grain":"seat-list","decision":"ADR 1","verbs":[{"name":"query","input":{"type":"object"}}]}`,
|
||||
"go/go.mod": "module example.org/c/go\n",
|
||||
"go/stray.go": "package stray\n",
|
||||
"ts/package.json": `{"name":"@example/c"}`,
|
||||
"ts/stray.ts": "// not this module's\n",
|
||||
} {
|
||||
full := filepath.Join(tree, filepath.FromSlash(p))
|
||||
_ = os.MkdirAll(filepath.Dir(full), 0o755)
|
||||
if err := os.WriteFile(full, []byte(text), 0o644); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
res, st := runUnit(t, m, states, ws)
|
||||
stray := false
|
||||
for _, p := range res.OutOfStep {
|
||||
stray = stray || p == "go/stray.go"
|
||||
if !strings.HasPrefix(p, "go/") {
|
||||
t.Errorf("%s is not under the Go root", p)
|
||||
}
|
||||
}
|
||||
if st.State != delivery.StepPassed || res.Passed || !stray || !strings.Contains(res.Why, "go/stray.go") {
|
||||
t.Errorf("result = %+v (%s %s)", res, st.State, st.Reason)
|
||||
}
|
||||
}
|
||||
|
||||
// A package that does not build fails the level with the compiler's words.
|
||||
func TestUnitLevelOfAPackageThatDoesNotBuildFailsWithTheCompilersLine(t *testing.T) {
|
||||
m, states, ws, _ := workspaceFor(t, "broken")
|
||||
|
||||
Reference in New Issue
Block a user