GitRoot
Craft your forge, Build your project, Grow your community freely
1// SPDX-FileCopyrightText: 2025 Romain Maneschi <romain@gitroot.dev>
2//
3// SPDX-License-Identifier: EUPL-1.2
4
5package exec
6
7import (
8 _ "embed"
9 "os"
10 "path/filepath"
11 "strconv"
12 "strings"
13 "testing"
14
15 "github.com/go-git/go-git/v6/plumbing"
16 "gitroot.dev/libs/golang/plugin/model"
17 "gitroot.dev/server/configuration"
18 "gitroot.dev/server/repository"
19 "gitroot.dev/server/user"
20)
21
22const pluginCacheDir = "pluginCache"
23
24///go:embed resources/GitRoot.priv
25// var gitrootPriv []byte
26
27///go:embed resources/GitRoot.pub
28// var gitrootPub []byte
29
30type fakeConf struct {
31 kind string
32 dataDir string
33}
34
35func (f *fakeConf) GetExecConf() configuration.ExecConf {
36 dc := configuration.ExecDefaultConf
37 switch f.kind {
38 case "bareMetal":
39 dc.BareMetal.Enabled = true
40 case "bwrap":
41 dc.Bwrap.Enabled = true
42 case "container":
43 dc.Container.Enabled = true
44 dc.Container.Bin = "podman"
45 case "ssh":
46 dc.Ssh.Enabled = true
47 u := os.Getenv("SSH_USER")
48 a := os.Getenv("SSH_ADDR")
49 p := os.Getenv("SSH_PORT")
50 port, _ := strconv.Atoi(p)
51 dc.Ssh.Hosts = []configuration.Host{
52 {User: u, Address: a, Port: port, PublicKey: "ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBCBMx22obajd8AZ75MtevWOR62MHAI3Lf2pMt5rDjgdPJUy2v3ncdSdod8qbsJ7Ie0I3OMlq+21Lr51PLNJj5Ok="},
53 }
54 }
55 return dc
56}
57
58func (f *fakeConf) PathCacheProject(repoName string, pluginName string) string {
59 return filepath.Join(f.dataDir, pluginCacheDir)
60}
61
62type testDate struct {
63 executor string
64 build string
65 status *model.ExecStatus
66 env []string
67}
68
69var testData = []*testDate{
70 {executor: "bareMetal", env: []string{"MISE_CACHE_DIR=/cache/mise"}},
71 {executor: "bwrap", env: []string{"MISE_CACHE_DIR=/cache/mise"}},
72 //TODO no container on gitroot.dev instance
73 //{executor: "container", build: "docker.io/bash:5.3.3", env: []string{"MISE_CACHE_DIR=/cache/mise"}},
74 //{executor: "container", build: "./Containerfile", env: []string{"MISE_CACHE_DIR=/cache/mise"}},
75 // WARN if you uncomment that be sure to set env look line 47 of this file
76 //{executor: "ssh", env: []string{"MISE_CACHE_DIR=/cache/mise"}},
77}
78
79func TestExec(t *testing.T) {
80 for _, tt := range testData {
81 dataDir := t.TempDir()
82 conf := configuration.NewConfiguration(dataDir)
83 userManager, _ := user.NewManager(conf)
84 // os.WriteFile(filepath.Join(conf.GetDirPathData(), fmt.Sprintf("%s.pub", conf.RootCommiterPseudo())), gitrootPub, os.ModeExclusive)
85 // os.WriteFile(filepath.Join(conf.GetDirPathData(), fmt.Sprintf("%s.priv", conf.RootCommiterPseudo())), gitrootPriv, os.ModeExclusive)
86 userManager, _ = user.NewManager(conf)
87 m := NewManager(t.Context(), &fakeConf{kind: tt.executor, dataDir: dataDir}, userManager)
88 repoManager := repository.NewManager(conf, userManager)
89 err := repoManager.CreateUserRepo(repository.RepoConf{Name: "exec", DefaultBranch: "main"}, []user.SimpleUser{}, []byte(""))
90 if err != nil {
91 t.Fatal(err)
92 }
93 repo, err := repoManager.Open(t.Context(), "exec")
94 if err != nil {
95 t.Fatal(err)
96 }
97 repoWrite, _ := repo.WillWrite(plumbing.NewBranchReferenceName("main"))
98 repoWrite.Write("Containerfile", []byte("from docker.io/bash:5.3.3"))
99 repoWrite.CommitAll("add containerfile", userManager.RootCommiter())
100 repoWrite.Accept()
101 repoWrite.Storer().Commit()
102
103 cmds := []model.Cmd{
104 {Cmd: "echo", Args: []string{"$MISE_CACHE_DIR"}},
105 {Cmd: "echo", Args: []string{"$MISE_CACHE_DIR > arti"}},
106 {Cmd: "ls", Args: []string{"-a", "."}},
107 {Cmd: "dd", Args: []string{"if=/dev/zero", "of=test_write.tmp", "bs=1M", "count=500", "conv=fdatasync", "oflag=dsync"}},
108 {Cmd: "rm", Args: []string{"test_write.tmp"}},
109 }
110
111 if tt.executor == "bareMetal" {
112 cmds = append(cmds, model.Cmd{Cmd: "echo", Args: []string{"Hello world > $PWD$MISE_CACHE_DIR/myFileCache"}})
113 } else {
114 cmds = append(cmds, model.Cmd{Cmd: "echo", Args: []string{"Hello world > $MISE_CACHE_DIR/myFileCache"}})
115 }
116
117 status, err := m.Exec(repo, "main", "pluginName", model.Exec{
118 Build: tt.build,
119 ReportStats: true,
120 Cmds: cmds,
121 Env: tt.env,
122 Artifacts: []string{"arti"},
123 Cache: []model.Cache{{Key: "keyCacheDir", Path: "/cache/mise", ReadOnly: false}},
124 })
125 if err != nil {
126 t.Fatal(err)
127 }
128
129 tt.status = status
130
131 if len(tt.status.CmdsLogs) < 6 {
132 t.Fatal("All step should have logs")
133 }
134 hasContainer := tt.executor != "bareMetal" && tt.executor != "ssh"
135 firstIndex := 0
136 if hasContainer {
137 firstIndex++
138 }
139 hasBuild := strings.HasPrefix(tt.build, "./")
140 if hasBuild {
141 firstIndex++
142 }
143 contentLog, err := os.ReadFile(filepath.Join(dataDir, pluginCacheDir, tt.status.CmdsLogs[firstIndex]))
144 if err != nil {
145 t.Fatal(err)
146 }
147 if !strings.Contains(string(contentLog), "/cache/mise") {
148 t.Fatal(string(contentLog))
149 }
150
151 contentLog, err = os.ReadFile(filepath.Join(dataDir, pluginCacheDir, tt.status.CmdsLogs[firstIndex+2]))
152 if err != nil {
153 t.Fatal(err)
154 }
155 if !strings.Contains(string(contentLog), ".gitroot") {
156 t.Fatal(string(contentLog))
157 }
158
159 if len(tt.status.Artifacts) < 1 {
160 t.Fatal("should have artifacts")
161 }
162 contentArtifact, err := os.ReadFile(filepath.Join(dataDir, pluginCacheDir, tt.status.Artifacts[0]))
163 if err != nil {
164 t.Fatal(err)
165 }
166 if !strings.Contains(string(contentArtifact), "/cache/mise") {
167 t.Fatal(contentArtifact)
168 }
169
170 contentCache, err := os.ReadFile(filepath.Join(dataDir, pluginCacheDir, "cache", "keyCacheDir", "myFileCache"))
171 if err != nil {
172 t.Fatal(err)
173 }
174 if !strings.Contains(string(contentCache), "Hello world") {
175 t.Fatal(contentCache)
176 }
177 }
178
179 t.Log("|executor| cmd |status|memory|cpu|nb thread|read|write|")
180 t.Log("|--------|------|------|------|---|---------|----|-----|")
181 for _, tt := range testData {
182 for i, stat := range tt.status.CmdsStats {
183 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)
184 }
185 }
186
187 //uncomment to see stats
188 //t.FailNow()
189}