package buildstep import ( "context" "crypto/sha256" "encoding/hex" "os" "path/filepath" "reflect" "strings" "testing" "git.novox.be/novox/mesh-sdk/go/forgekey" ) func hexOf(s string) string { h := sha256.Sum256([]byte(s)); return hex.EncodeToString(h[:]) } func writeTreeFile(t *testing.T, root, name, content string) { t.Helper() p := filepath.Join(root, filepath.FromSlash(name)) if err := os.MkdirAll(filepath.Dir(p), 0o755); err != nil { t.Fatal(err) } if err := os.WriteFile(p, []byte(content), 0o644); err != nil { t.Fatal(err) } } // ModuleTrees answers, for each directory holding a module.json (the root as "."), the tree id git gives, the sha256 of // module.json and of each lock file present, and reads them from the workspace. func TestModuleTreesAnswersTreeManifestAndLocksPerModuleDirectory(t *testing.T) { root := t.TempDir() writeTreeFile(t, root, "module.json", `{"root":1}`) writeTreeFile(t, root, "go.sum", "rootsum") writeTreeFile(t, root, "modules/a/module.json", `{"a":1}`) writeTreeFile(t, root, "modules/a/go.sum", "asum") writeTreeFile(t, root, "modules/a/package-lock.json", "alock") writeTreeFile(t, root, "modules/b/module.json", `{"b":1}`) merge := strings.Repeat("c", 40) var calls []string var envs [][]string run := func(ctx context.Context, env []string, name string, args ...string) forgekey.Ran { calls = append(calls, name+" "+strings.Join(args[2:], " ")) envs = append(envs, env) switch args[2] { case "ls-files": return forgekey.Ran{Stdout: "module.json\nmodules/a/module.json\nmodules/b/module.json\n"} case "rev-parse": return forgekey.Ran{Stdout: "tree-of-" + args[3] + "\n"} } return forgekey.Ran{Status: 1, Stderr: "unexpected"} } got, err := ModuleTrees(context.Background(), run, []string{"X=1"}, root, merge) if err != nil { t.Fatal(err) } want := map[string]ModuleTree{ ".": {Tree: "tree-of-" + merge + "^{tree}", Manifest: hexOf(`{"root":1}`), Locks: map[string]string{"go.sum": hexOf("rootsum")}}, "modules/a": {Tree: "tree-of-" + merge + ":modules/a", Manifest: hexOf(`{"a":1}`), Locks: map[string]string{"go.sum": hexOf("asum"), "package-lock.json": hexOf("alock")}}, "modules/b": {Tree: "tree-of-" + merge + ":modules/b", Manifest: hexOf(`{"b":1}`)}, } if !reflect.DeepEqual(got, want) { t.Errorf("trees = %+v\nwant %+v", got, want) } if got["modules/b"].Locks != nil { t.Error("a module with no lock file carries a lock map") } for _, e := range envs { if len(e) != 1 || e[0] != "X=1" { t.Errorf("git ran with env %v, want the one given", e) } } } func TestModuleTreesFailsNamingTheGitCommandOrTheUnreadableManifest(t *testing.T) { root := t.TempDir() fail := func(on string) forgekey.Runner { return func(ctx context.Context, env []string, name string, args ...string) forgekey.Ran { if args[2] == on { return forgekey.Ran{Status: 128, Stderr: "fatal: no\n"} } return forgekey.Ran{Stdout: "modules/a/module.json\n"} } } for _, on := range []string{"ls-files", "rev-parse"} { if _, err := ModuleTrees(context.Background(), fail(on), nil, root, sha); err == nil || !strings.Contains(err.Error(), on) || !strings.Contains(err.Error(), "fatal: no") { t.Errorf("%s failing: %v", on, err) } } if _, err := ModuleTrees(context.Background(), fail("none"), nil, root, sha); err == nil { t.Error("a module.json that is not in the workspace was read") } }