// SPDX-FileCopyrightText: 2025 Romain Maneschi // // SPDX-License-Identifier: EUPL-1.2 package exec import ( "context" _ "embed" "os" "path/filepath" "strconv" "strings" "testing" "github.com/go-git/go-git/v5/plumbing" "gitroot.dev/libs/golang/plugin/model" "gitroot.dev/server/configuration" "gitroot.dev/server/repository" "gitroot.dev/server/user" ) const pluginCacheDir = "pluginCache" ///go:embed resources/GitRoot.priv // var gitrootPriv []byte ///go:embed resources/GitRoot.pub // var gitrootPub []byte type fakeConf struct { kind string dataDir string } func (f *fakeConf) GetExecConf() configuration.ExecConf { dc := configuration.ExecDefaultConf switch f.kind { case "bareMetal": dc.BareMetal.Enabled = true case "bwrap": dc.Bwrap.Enabled = true case "container": dc.Container.Enabled = true dc.Container.Bin = "podman" case "ssh": dc.Ssh.Enabled = true u := os.Getenv("SSH_USER") a := os.Getenv("SSH_ADDR") p := os.Getenv("SSH_PORT") port, _ := strconv.Atoi(p) dc.Ssh.Hosts = []configuration.Host{ {User: u, Address: a, Port: port, PublicKey: "ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBCBMx22obajd8AZ75MtevWOR62MHAI3Lf2pMt5rDjgdPJUy2v3ncdSdod8qbsJ7Ie0I3OMlq+21Lr51PLNJj5Ok="}, } } return dc } func (f *fakeConf) PathCacheProject(repoName string, pluginName string) string { return filepath.Join(f.dataDir, pluginCacheDir) } type testDate struct { executor string build string status *model.ExecStatus env []string } var testData = []*testDate{ {executor: "bareMetal", env: []string{"MISE_CACHE_DIR=/cache/mise"}}, {executor: "bwrap", env: []string{"MISE_CACHE_DIR=/cache/mise"}}, //TODO no container on gitroot.dev instance //{executor: "container", build: "docker.io/bash:5.3.3", env: []string{"MISE_CACHE_DIR=/cache/mise"}}, //{executor: "container", build: "./Containerfile", env: []string{"MISE_CACHE_DIR=/cache/mise"}}, // WARN if you uncomment that be sure to set env look line 47 of this file //{executor: "ssh", env: []string{"MISE_CACHE_DIR=/cache/mise"}}, } func TestExec(t *testing.T) { for _, tt := range testData { dataDir := t.TempDir() conf := configuration.NewConfiguration(dataDir) userManager, _ := user.NewManager(conf) // os.WriteFile(filepath.Join(conf.GetDirPathData(), fmt.Sprintf("%s.pub", conf.RootCommiterPseudo())), gitrootPub, os.ModeExclusive) // os.WriteFile(filepath.Join(conf.GetDirPathData(), fmt.Sprintf("%s.priv", conf.RootCommiterPseudo())), gitrootPriv, os.ModeExclusive) userManager, _ = user.NewManager(conf) m := NewManager(t.Context(), &fakeConf{kind: tt.executor, dataDir: dataDir}, userManager) repoManager := repository.NewManager(conf, userManager) err := repoManager.CreateUserRepo(context.TODO(), repository.RepoConf{Name: "exec", DefaultBranch: "main"}, []user.SimpleUser{}, []byte("")) if err != nil { t.Fatal(err) } repo, err := repoManager.Open(t.Context(), "exec") if err != nil { t.Fatal(err) } repoWrite, _ := repo.WillWrite(plumbing.NewBranchReferenceName("main")) repoWrite.Write("Containerfile", []byte("from docker.io/bash:5.3.3")) repoWrite.CommitAll("add containerfile", userManager.RootCommiter()) repoWrite.Accept() repoWrite.Storer().Commit() cmds := []model.Cmd{ {Cmd: "echo", Args: []string{"$MISE_CACHE_DIR"}}, {Cmd: "echo", Args: []string{"$MISE_CACHE_DIR > arti"}}, {Cmd: "ls", Args: []string{"-a", "."}}, {Cmd: "dd", Args: []string{"if=/dev/zero", "of=test_write.tmp", "bs=1M", "count=500", "conv=fdatasync", "oflag=dsync"}}, {Cmd: "rm", Args: []string{"test_write.tmp"}}, } if tt.executor == "bareMetal" { cmds = append(cmds, model.Cmd{Cmd: "echo", Args: []string{"Hello world > $PWD$MISE_CACHE_DIR/myFileCache"}}) } else { cmds = append(cmds, model.Cmd{Cmd: "echo", Args: []string{"Hello world > $MISE_CACHE_DIR/myFileCache"}}) } status, err := m.Exec(repo, "main", "pluginName", model.Exec{ Build: tt.build, ReportStats: true, Cmds: cmds, Env: tt.env, Artifacts: []string{"arti"}, Cache: []model.Cache{{Key: "keyCacheDir", Path: "/cache/mise", ReadOnly: false}}, }) if err != nil { t.Fatal(err) } tt.status = status if len(tt.status.CmdsLogs) < 6 { t.Fatal("All step should have logs") } hasContainer := tt.executor != "bareMetal" && tt.executor != "ssh" firstIndex := 0 if hasContainer { firstIndex++ } hasBuild := strings.HasPrefix(tt.build, "./") if hasBuild { firstIndex++ } contentLog, err := os.ReadFile(filepath.Join(dataDir, pluginCacheDir, tt.status.CmdsLogs[firstIndex])) if err != nil { t.Fatal(err) } if !strings.Contains(string(contentLog), "/cache/mise") { t.Fatal(string(contentLog)) } contentLog, err = os.ReadFile(filepath.Join(dataDir, pluginCacheDir, tt.status.CmdsLogs[firstIndex+2])) if err != nil { t.Fatal(err) } if !strings.Contains(string(contentLog), ".gitroot") { t.Fatal(string(contentLog)) } if len(tt.status.Artifacts) < 1 { t.Fatal("should have artifacts") } contentArtifact, err := os.ReadFile(filepath.Join(dataDir, pluginCacheDir, tt.status.Artifacts[0])) if err != nil { t.Fatal(err) } if !strings.Contains(string(contentArtifact), "/cache/mise") { t.Fatal(contentArtifact) } contentCache, err := os.ReadFile(filepath.Join(dataDir, pluginCacheDir, "cache", "keyCacheDir", "myFileCache")) if err != nil { t.Fatal(err) } if !strings.Contains(string(contentCache), "Hello world") { t.Fatal(contentCache) } } t.Log("|executor| cmd |status|memory|cpu|nb thread|read|write|") t.Log("|--------|------|------|------|---|---------|----|-----|") for _, tt := range testData { for i, stat := range tt.status.CmdsStats { t.Logf("|%s|%s|%d|%d|%d|%d|%d|%d|", tt.executor, tt.status.CmdsExec[i], tt.status.CmdsStatus[i], stat.MaxMemoryBytes, stat.TotalCPUTimeMs, stat.MaxThreads, stat.ReadBytesTotal, stat.WriteBytesTotal) } } //uncomment to see stats //t.FailNow() }