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