mesh-build-go: build_go_test takes packages as a typed list too (issue 1925)
mesh/delivery delivered
mesh/check failed: shanks: the check of novox/mesh-catalog#465 failed: the unit level: packages is a list of package patterns | 70ef99595aa9 on e52024…

This commit is contained in:
2026-10-23 22:21:41 +02:00
parent e830045545
commit 70ef99595a
2 changed files with 93 additions and 6 deletions
@@ -0,0 +1,79 @@
package main
import (
"context"
"reflect"
"strings"
"testing"
"time"
"git.novox.be/novox/mesh-sdk/go/buildstep"
"git.novox.be/novox/mesh-sdk/go/delivery"
)
// 1925/AC1: packagesArg takes the list as the bus's JSON shape ([]any) and as the typed list an ask hands in process
// ([]string), and refuses a bad element in either.
func TestPackagesArgTakesBothShapes(t *testing.T) {
cases := []struct {
name string
in any
want []string
refused bool
}{
{"absent", nil, nil, false},
{"typed", []string{"x/a", "x/b/..."}, []string{"x/a", "x/b/..."}, false},
{"json", []any{"x/a", "x/b/..."}, []string{"x/a", "x/b/..."}, false},
{"typed empty", []string{}, []string{}, false},
{"typed dotdot", []string{"x/../y"}, nil, true},
{"json dotdot", []any{"x/../y"}, nil, true},
{"typed bad", []string{"x a"}, nil, true},
{"json bad", []any{"x a"}, nil, true},
{"json not a string", []any{3}, nil, true},
{"a string", "x/a", nil, true},
}
for _, c := range cases {
got, err := packagesArg(c.in)
if c.refused != (err != nil) {
t.Errorf("%s: err = %v, refused want %v", c.name, err, c.refused)
continue
}
if !c.refused && !reflect.DeepEqual(got, c.want) {
t.Errorf("%s: got %#v, want %#v", c.name, got, c.want)
}
}
}
// 1925/AC2: a check ask through the module's real build_go_test body gets past the packages argument: the digest names
// a package, buildstep.RunAsk hands it in as a typed list, and the step fails only later, on the workspace nobody fetched.
func TestACheckAskReachesTheTestStepPastItsPackages(t *testing.T) {
m := &Module{Node: "ace", Work: t.TempDir()}
var calls []string
steps := fakeSteps(&calls)
for i := range steps {
switch steps[i].Name {
case "build_go_digest":
steps[i].Run = func(ctx context.Context, args map[string]any) (any, error) {
return map[string]any{"level": "unit", "packages": map[string]string{"x/a": "d1"}}, nil
}
case "build_go_test":
for _, real := range m.StepTools() {
if real.Name == "build_go_test" {
steps[i].Run = real.Run
}
}
}
}
a := delivery.StepAsk{ID: buildstep.NewID(), Kind: delivery.AskCheck, Language: delivery.AskGo, Asker: "mesh-delivery-checker",
At: time.Now(), Check: &delivery.CheckJob{Fetch: map[string]any{"repository": "novox/x", "commit": askCommit}, Levels: []string{delivery.LevelUnit}}}
out, err := buildstep.RunAsk(context.Background(), m.askSteps(steps), a)
got := ""
if err != nil {
got = err.Error()
}
if strings.Contains(got, "packages is a list") {
t.Fatalf("the test step refused the ask's packages: %s", got)
}
if !strings.Contains(got, "no workspace") {
t.Fatalf("want the step to fail on the unfetched workspace, past its packages; err=%v out=%+v", err, out)
}
}
@@ -127,17 +127,25 @@ func (m *Module) besideEnv(workspace string) []string {
return []string{"MESH_CHECK_BESIDE=" + dir}
}
// packagesArg reads the packages argument: a list of package patterns, as []any when it came over the bus as JSON and
// as []string when an ask handed it in process (issue 1925).
func packagesArg(v any) ([]string, error) {
if v == nil {
var list []string
switch t := v.(type) {
case nil:
return nil, nil
}
list, ok := v.([]any)
if !ok {
case []string:
list = t
case []any:
for _, e := range t {
s, _ := e.(string)
list = append(list, s)
}
default:
return nil, fmt.Errorf("packages is a list of package patterns")
}
out := []string{}
for _, e := range list {
s, _ := e.(string)
for _, s := range list {
if !packageOK.MatchString(s) || strings.Contains(strings.TrimSuffix(s, "/..."), "..") {
return nil, fmt.Errorf("%q is not a package pattern", s)
}