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: MIT
4
5package test
6
7import (
8 "bytes"
9 "errors"
10 "io/fs"
11 "os"
12 "path/filepath"
13 "testing"
14
15 "gitroot.dev/libs/golang/plugin/model"
16)
17
18type fakeServer struct {
19 t *testing.T
20 dirGit string
21 fsGit model.FsBase
22 grfsGit *model.GrFs
23 dirWeb string
24 fsWeb model.FsBase
25 grfsWeb *model.GrFs
26 dirCache string
27 fsCache model.FsBase
28 grfsCache *model.GrFs
29 override Override
30 NbCall nbCall
31}
32
33type Override struct {
34 PluginOption func(opts ...model.PluginOptionWith)
35 ForgeConf func() (model.ForgeConf, error)
36 Worktree func() *model.GrFs
37 Webcontent func() *model.GrFs
38 Cache func() *model.GrFs
39 ModifyContent func(path string, content string)
40 ModifyWebContent func(filepath, content string)
41 ReplaceWebContent func(filepath, oldContent, content string)
42 ModifyCacheContent func(filepath, content string)
43 CopyFile func(fromFs model.FsBase, fromPath string, toFs model.FsBase, toPath string) error
44 DeleteFile func(fromFs model.FsBase, filename string) error
45 MoveFile func(fromFs model.FsBase, fromPath string, toFs model.FsBase, toPath string) error
46 ReplaceContent func(fromFs model.FsBase, filepath string, oldContent string, content string) error
47 WriteContent func(fromFs model.FsBase, filepath string, content string) error
48 CommitAllIfNeeded func(message string)
49 DiffWithParent func(hash string, oldFilepath string, newFilepath string) (string, error)
50 Merge func(from string, to string)
51 Commits func(from string, to string) ([]model.Commit, error)
52 Exec func(exec model.Exec) (model.ExecStatus, error)
53 Report func(level model.ReportLevel, content []string) error
54 CanCallFunc func(plugin string, name string, args map[string]string) bool
55 ExportFunc func(name string, callback func(args map[string]string) (map[string]string, error))
56 CallFunc func(plugin string, name string, args map[string]string) (map[string]string, error)
57}
58
59type nbCall struct {
60 PluginOption int
61 ForgeConf int
62 Worktree int
63 Webcontent int
64 Cache int
65 ModifyContent int
66 ModifyWebContent int
67 ReplaceWebContent int
68 ModifyCacheContent int
69 CopyFile int
70 DeleteFile int
71 MoveFile int
72 ReplaceContent int
73 WriteContent int
74 CommitAllIfNeeded int
75 DiffWithParent int
76 Log int
77 LogError int
78 Merge int
79 Commits int
80 Exec int
81 Report int
82 CanCallFunc int
83 ExportFunc int
84 CallFunc int
85}
86
87func NewFakeServer(t *testing.T) *fakeServer {
88 dirGit, _ := os.MkdirTemp("", "gitrootPluginTestGit*")
89 dirWeb, _ := os.MkdirTemp("", "gitrootPluginTestWeb*")
90 dirCache, _ := os.MkdirTemp("", "gitrootPluginTestCache*")
91 s := &fakeServer{
92 t: t,
93 dirGit: dirGit,
94 fsGit: model.FsBase(dirGit),
95 grfsGit: nil,
96 dirWeb: dirWeb,
97 fsWeb: model.FsBase(dirWeb),
98 grfsWeb: nil,
99 dirCache: dirCache,
100 fsCache: model.FsBase(dirCache),
101 grfsCache: nil,
102 override: Override{},
103 NbCall: nbCall{},
104 }
105 s.grfsGit = model.NewGrFs(s.fsGit, s)
106 s.grfsWeb = model.NewGrFs(s.fsWeb, s)
107 s.grfsCache = model.NewGrFs(s.fsCache, s)
108 return s
109}
110
111func NewFakeServerWithOverride(t *testing.T, o Override) *fakeServer {
112 return NewFakeServer(t).SetOverride(o)
113}
114
115func (s *fakeServer) SetOverride(o Override) *fakeServer {
116 s.override = o
117 return s
118}
119
120// To check that interface is always respected
121func (s *fakeServer) AsServer() model.Server {
122 return s
123}
124
125func (s *fakeServer) PluginOption(opts ...model.PluginOptionWith) {
126 s.NbCall.PluginOption++
127 if s.override.PluginOption != nil {
128 s.override.PluginOption(opts...)
129 }
130}
131
132func (s *fakeServer) ForgeConf() (model.ForgeConf, error) {
133 s.NbCall.ForgeConf++
134 if s.override.ForgeConf != nil {
135 return s.override.ForgeConf()
136 }
137 return model.ForgeConf{
138 Domain: "test",
139 ExternalSshAddr: "ssh://127.0.0.1:4545/",
140 ExternalHttpAddr: "http://127.0.0.1:4546/",
141 RootRepositoryName: "root",
142 }, nil
143}
144
145func (s *fakeServer) Worktree() *model.GrFs {
146 s.NbCall.Worktree++
147 if s.override.Worktree != nil {
148 return s.override.Worktree()
149 }
150 return s.grfsGit
151}
152
153func (s *fakeServer) Webcontent() *model.GrFs {
154 s.NbCall.Webcontent++
155 if s.override.Webcontent != nil {
156 return s.override.Webcontent()
157 }
158 return s.grfsWeb
159}
160
161func (s *fakeServer) Cache() *model.GrFs {
162 s.NbCall.Cache++
163 if s.override.Cache != nil {
164 return s.override.Cache()
165 }
166 return s.grfsCache
167}
168
169// TODO delete after v0.4 is released
170//
171// Deprecated: Use Worktree().WriteContent(filepath string, content string) instead
172func (s *fakeServer) ModifyContent(path, content string) {
173 s.NbCall.ModifyContent++
174 if s.override.ModifyContent != nil {
175 s.override.ModifyContent(path, content)
176 } else {
177 os.MkdirAll(s.dirGit+"/"+filepath.Dir(path), fs.ModePerm)
178 if err := os.WriteFile(s.dirGit+"/"+path, []byte(content), fs.ModePerm); err != nil {
179 s.t.Fatal(err)
180 }
181 }
182}
183
184// TODO delete after v0.4 is released
185//
186// Deprecated: Use Webcontent().WriteContent(filepath string, content string) instead
187func (s *fakeServer) ModifyWebContent(path, content string) {
188 s.NbCall.ModifyWebContent++
189 s.t.Logf("ModifyWebContent called with path=%s", path)
190 if s.override.ModifyWebContent != nil {
191 s.override.ModifyWebContent(path, content)
192 } else {
193 os.MkdirAll(s.dirWeb+"/"+filepath.Dir(path), fs.ModePerm)
194 if err := os.WriteFile(s.dirWeb+"/"+path, []byte(content), fs.ModePerm); err != nil {
195 s.t.Fatal(err)
196 }
197 }
198}
199
200// TODO delete after v0.4 is released
201//
202// Deprecated: Use Webcontent().ReplaceContent(filepath string, oldContent string, content string) instead
203func (s *fakeServer) ReplaceWebContent(filepath, oldContent, content string) {
204 s.NbCall.ReplaceWebContent++
205 if s.override.ReplaceWebContent != nil {
206 s.override.ReplaceWebContent(filepath, oldContent, content)
207 } else {
208 content, err := os.ReadFile(filepath)
209 if err != nil {
210 s.t.Fatal(err)
211 }
212
213 if err := os.WriteFile(s.dirWeb+"/"+filepath, bytes.Replace(content, []byte(oldContent), []byte(content), 1), 0666); err != nil {
214 s.t.Fatal(err)
215 }
216 }
217}
218
219// TODO delete after v0.4 is released
220//
221// Deprecated: Use Cache().WriteContent(filepath string, content string) instead
222func (s *fakeServer) ModifyCacheContent(path, content string) {
223 s.NbCall.ModifyCacheContent++
224 if s.override.ModifyCacheContent != nil {
225 s.override.ModifyCacheContent(path, content)
226 } else {
227 os.MkdirAll(s.dirCache+"/"+filepath.Dir(path), fs.ModePerm)
228 if err := os.WriteFile(s.dirCache+"/"+path, []byte(content), fs.ModePerm); err != nil {
229 s.t.Fatal(err)
230 }
231 }
232}
233
234// CopyFile implements [model.ServerNeeded].
235func (s *fakeServer) CopyFile(fromFs model.FsBase, fromPath string, toFs model.FsBase, toPath string) error {
236 s.NbCall.CopyFile++
237 if s.override.CopyFile != nil {
238 return s.override.CopyFile(fromFs, fromPath, toFs, toPath)
239 } else {
240 realPath := ""
241 switch fromFs {
242 case s.fsGit:
243 realPath = filepath.Join(s.dirGit, fromPath)
244 case s.fsWeb:
245 realPath = filepath.Join(s.dirWeb, fromPath)
246 case s.fsCache:
247 realPath = filepath.Join(s.dirCache, fromPath)
248 default:
249 return errors.New("unknown fsbase")
250 }
251 content, err := os.ReadFile(realPath)
252 if err != nil {
253 return err
254 }
255 return s.WriteContent(toFs, toPath, string(content))
256 }
257}
258
259// DeleteFile implements [model.ServerNeeded].
260func (s *fakeServer) DeleteFile(fromFs model.FsBase, filename string) error {
261 s.NbCall.DeleteFile++
262 if s.override.DeleteFile != nil {
263 return s.override.DeleteFile(fromFs, filename)
264 } else {
265 realPath := ""
266 switch fromFs {
267 case s.fsGit:
268 realPath = filepath.Join(s.dirGit, filename)
269 case s.fsWeb:
270 realPath = filepath.Join(s.dirWeb, filename)
271 case s.fsCache:
272 realPath = filepath.Join(s.dirCache, filename)
273 default:
274 return errors.New("unknown fsbase")
275 }
276 return os.Remove(realPath)
277 }
278}
279
280// MoveFile implements [model.ServerNeeded].
281func (s *fakeServer) MoveFile(fromFs model.FsBase, fromPath string, toFs model.FsBase, toPath string) error {
282 s.NbCall.MoveFile++
283 if s.override.MoveFile != nil {
284 return s.override.MoveFile(fromFs, fromPath, toFs, toPath)
285 } else {
286 if err := s.CopyFile(fromFs, fromPath, toFs, toPath); err != nil {
287 return err
288 }
289 return s.DeleteFile(fromFs, fromPath)
290 }
291}
292
293// ReplaceContent implements [model.ServerNeeded].
294func (s *fakeServer) ReplaceContent(fromFs model.FsBase, path string, oldContent string, content string) error {
295 s.NbCall.ReplaceContent++
296 if s.override.ReplaceContent != nil {
297 return s.override.ReplaceContent(fromFs, path, oldContent, content)
298 } else {
299 realPath := ""
300 switch fromFs {
301 case s.fsGit:
302 realPath = filepath.Join(s.dirGit, path)
303 case s.fsWeb:
304 realPath = filepath.Join(s.dirWeb, path)
305 case s.fsCache:
306 realPath = filepath.Join(s.dirCache, path)
307 default:
308 return errors.New("unknown fsbase")
309 }
310 content, err := os.ReadFile(realPath)
311 if err != nil {
312 return err
313 }
314 return os.WriteFile(realPath, bytes.Replace(content, []byte(oldContent), []byte(content), 1), 0666)
315 }
316}
317
318// WriteContent implements [model.ServerNeeded].
319func (s *fakeServer) WriteContent(fromFs model.FsBase, path string, content string) error {
320 s.NbCall.WriteContent++
321 if s.override.WriteContent != nil {
322 return s.override.WriteContent(fromFs, path, content)
323 } else {
324 switch fromFs {
325 case s.fsGit:
326 os.MkdirAll(s.dirGit+"/"+filepath.Dir(path), fs.ModePerm)
327 return os.WriteFile(s.dirGit+"/"+path, []byte(content), fs.ModePerm)
328 case s.fsWeb:
329 os.MkdirAll(s.dirWeb+"/"+filepath.Dir(path), fs.ModePerm)
330 return os.WriteFile(s.dirWeb+"/"+path, []byte(content), fs.ModePerm)
331 case s.fsCache:
332 os.MkdirAll(s.dirCache+"/"+filepath.Dir(path), fs.ModePerm)
333 return os.WriteFile(s.dirCache+"/"+path, []byte(content), fs.ModePerm)
334 }
335 return errors.New("unknown fsbase")
336 }
337}
338
339func (s *fakeServer) CommitAllIfNeeded(message string) {
340 s.NbCall.CommitAllIfNeeded++
341 if s.override.CommitAllIfNeeded != nil {
342 s.override.CommitAllIfNeeded(message)
343 }
344}
345
346func (s *fakeServer) DiffWithParent(hash string, oldFilepath string, newFilepath string) (string, error) {
347 s.NbCall.DiffWithParent++
348 if s.override.DiffWithParent != nil {
349 return s.override.DiffWithParent(hash, oldFilepath, newFilepath)
350 }
351 return "", nil
352}
353
354func (s *fakeServer) Log(message string) {
355 s.NbCall.Log++
356}
357
358func (s *fakeServer) LogError(message string, err error) {
359 s.NbCall.LogError++
360}
361
362func (s *fakeServer) Merge(from string, to string) {
363 s.NbCall.Merge++
364 if s.override.Merge != nil {
365 s.override.Merge(from, to)
366 }
367}
368
369func (s *fakeServer) Commits(from string, to string) ([]model.Commit, error) {
370 s.NbCall.Commits++
371 if s.override.Commits != nil {
372 return s.override.Commits(from, to)
373 }
374 return []model.Commit{}, nil
375}
376
377func (s *fakeServer) Exec(exec model.Exec) (model.ExecStatus, error) {
378 s.NbCall.Exec++
379 if s.override.Exec != nil {
380 return s.override.Exec(exec)
381 }
382 return model.ExecStatus{}, nil
383}
384
385func (s *fakeServer) Report(level model.ReportLevel, content []string) error {
386 s.NbCall.Report++
387 if s.override.Report != nil {
388 return s.override.Report(level, content)
389 }
390 return nil
391}
392
393func (s *fakeServer) ExportFunc(name string, callback func(args map[string]string) (map[string]string, error)) {
394 s.NbCall.ExportFunc++
395 if s.override.ExportFunc != nil {
396 s.override.ExportFunc(name, callback)
397 }
398}
399
400func (s *fakeServer) CanCallFunc(plugin string, name string, args map[string]string) bool {
401 s.NbCall.CanCallFunc++
402 if s.override.CanCallFunc != nil {
403 return s.override.CanCallFunc(plugin, name, args)
404 }
405 return false
406}
407
408func (s *fakeServer) CallFunc(plugin string, name string, args map[string]string) (map[string]string, error) {
409 s.NbCall.CallFunc++
410 if s.override.CallFunc != nil {
411 return s.override.CallFunc(plugin, name, args)
412 }
413 return map[string]string{}, nil
414}