From 70ef99595aa904f25e75f3662eabef1fdf76198a Mon Sep 17 00:00:00 2001 From: jochen Date: Fri, 23 Oct 2026 22:21:41 +0200 Subject: [PATCH] mesh-build-go: build_go_test takes packages as a typed list too (issue 1925) --- .../cmd/mesh-build-go/packages_shape_test.go | 79 +++++++++++++++++++ .../mesh-build-go/cmd/mesh-build-go/test.go | 20 +++-- 2 files changed, 93 insertions(+), 6 deletions(-) create mode 100644 modules/mesh-build-go/cmd/mesh-build-go/packages_shape_test.go diff --git a/modules/mesh-build-go/cmd/mesh-build-go/packages_shape_test.go b/modules/mesh-build-go/cmd/mesh-build-go/packages_shape_test.go new file mode 100644 index 00000000..fe3a7d8a --- /dev/null +++ b/modules/mesh-build-go/cmd/mesh-build-go/packages_shape_test.go @@ -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) + } +} diff --git a/modules/mesh-build-go/cmd/mesh-build-go/test.go b/modules/mesh-build-go/cmd/mesh-build-go/test.go index d3b42c9e..613fe61c 100644 --- a/modules/mesh-build-go/cmd/mesh-build-go/test.go +++ b/modules/mesh-build-go/cmd/mesh-build-go/test.go @@ -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) }