// SPDX-FileCopyrightText: 2025 Romain Maneschi // // SPDX-License-Identifier: MIT package test import ( "bytes" "errors" "io/fs" "os" "path/filepath" "testing" "gitroot.dev/libs/golang/plugin/model" ) type fakeServer struct { t *testing.T dirGit string fsGit model.FsBase grfsGit *model.GrFs dirWeb string fsWeb model.FsBase grfsWeb *model.GrFs dirCache string fsCache model.FsBase grfsCache *model.GrFs override Override NbCall nbCall } type Override struct { PluginOption func(opts ...model.PluginOptionWith) ForgeConf func() (model.ForgeConf, error) Worktree func() *model.GrFs Webcontent func() *model.GrFs Cache func() *model.GrFs ModifyContent func(path string, content string) ModifyWebContent func(filepath, content string) ReplaceWebContent func(filepath, oldContent, content string) ModifyCacheContent func(filepath, content string) CopyFile func(fromFs model.FsBase, fromPath string, toFs model.FsBase, toPath string) error DeleteFile func(fromFs model.FsBase, filename string) error MoveFile func(fromFs model.FsBase, fromPath string, toFs model.FsBase, toPath string) error ReplaceContent func(fromFs model.FsBase, filepath string, oldContent string, content string) error WriteContent func(fromFs model.FsBase, filepath string, content string) error CommitAllIfNeeded func(message string) DiffWithParent func(hash string, oldFilepath string, newFilepath string) (string, error) Merge func(from string, to string) Commits func(from string, to string) ([]model.Commit, error) Exec func(exec model.Exec) (model.ExecStatus, error) Report func(level model.ReportLevel, content []string) error CanCallFunc func(plugin string, name string, args map[string]string) bool ExportFunc func(name string, callback func(args map[string]string) (map[string]string, error)) CallFunc func(plugin string, name string, args map[string]string) (map[string]string, error) } type nbCall struct { PluginOption int ForgeConf int Worktree int Webcontent int Cache int ModifyContent int ModifyWebContent int ReplaceWebContent int ModifyCacheContent int CopyFile int DeleteFile int MoveFile int ReplaceContent int WriteContent int CommitAllIfNeeded int DiffWithParent int Log int LogError int Merge int Commits int Exec int Report int CanCallFunc int ExportFunc int CallFunc int } func NewFakeServer(t *testing.T) *fakeServer { dirGit, _ := os.MkdirTemp("", "gitrootPluginTestGit*") dirWeb, _ := os.MkdirTemp("", "gitrootPluginTestWeb*") dirCache, _ := os.MkdirTemp("", "gitrootPluginTestCache*") s := &fakeServer{ t: t, dirGit: dirGit, fsGit: model.FsBase(dirGit), grfsGit: nil, dirWeb: dirWeb, fsWeb: model.FsBase(dirWeb), grfsWeb: nil, dirCache: dirCache, fsCache: model.FsBase(dirCache), grfsCache: nil, override: Override{}, NbCall: nbCall{}, } s.grfsGit = model.NewGrFs(s.fsGit, s) s.grfsWeb = model.NewGrFs(s.fsWeb, s) s.grfsCache = model.NewGrFs(s.fsCache, s) return s } func NewFakeServerWithOverride(t *testing.T, o Override) *fakeServer { return NewFakeServer(t).SetOverride(o) } func (s *fakeServer) SetOverride(o Override) *fakeServer { s.override = o return s } // To check that interface is always respected func (s *fakeServer) AsServer() model.Server { return s } func (s *fakeServer) PluginOption(opts ...model.PluginOptionWith) { s.NbCall.PluginOption++ if s.override.PluginOption != nil { s.override.PluginOption(opts...) } } func (s *fakeServer) ForgeConf() (model.ForgeConf, error) { s.NbCall.ForgeConf++ if s.override.ForgeConf != nil { return s.override.ForgeConf() } return model.ForgeConf{ Domain: "test", ExternalSshAddr: "ssh://127.0.0.1:4545/", ExternalHttpAddr: "http://127.0.0.1:4546/", RootRepositoryName: "root", }, nil } func (s *fakeServer) Worktree() *model.GrFs { s.NbCall.Worktree++ if s.override.Worktree != nil { return s.override.Worktree() } return s.grfsGit } func (s *fakeServer) Webcontent() *model.GrFs { s.NbCall.Webcontent++ if s.override.Webcontent != nil { return s.override.Webcontent() } return s.grfsWeb } func (s *fakeServer) Cache() *model.GrFs { s.NbCall.Cache++ if s.override.Cache != nil { return s.override.Cache() } return s.grfsCache } // TODO delete after v0.4 is released // // Deprecated: Use Worktree().WriteContent(filepath string, content string) instead func (s *fakeServer) ModifyContent(path, content string) { s.NbCall.ModifyContent++ if s.override.ModifyContent != nil { s.override.ModifyContent(path, content) } else { os.MkdirAll(s.dirGit+"/"+filepath.Dir(path), fs.ModePerm) if err := os.WriteFile(s.dirGit+"/"+path, []byte(content), fs.ModePerm); err != nil { s.t.Fatal(err) } } } // TODO delete after v0.4 is released // // Deprecated: Use Webcontent().WriteContent(filepath string, content string) instead func (s *fakeServer) ModifyWebContent(path, content string) { s.NbCall.ModifyWebContent++ s.t.Logf("ModifyWebContent called with path=%s", path) if s.override.ModifyWebContent != nil { s.override.ModifyWebContent(path, content) } else { os.MkdirAll(s.dirWeb+"/"+filepath.Dir(path), fs.ModePerm) if err := os.WriteFile(s.dirWeb+"/"+path, []byte(content), fs.ModePerm); err != nil { s.t.Fatal(err) } } } // TODO delete after v0.4 is released // // Deprecated: Use Webcontent().ReplaceContent(filepath string, oldContent string, content string) instead func (s *fakeServer) ReplaceWebContent(filepath, oldContent, content string) { s.NbCall.ReplaceWebContent++ if s.override.ReplaceWebContent != nil { s.override.ReplaceWebContent(filepath, oldContent, content) } else { content, err := os.ReadFile(filepath) if err != nil { s.t.Fatal(err) } if err := os.WriteFile(s.dirWeb+"/"+filepath, bytes.Replace(content, []byte(oldContent), []byte(content), 1), 0666); err != nil { s.t.Fatal(err) } } } // TODO delete after v0.4 is released // // Deprecated: Use Cache().WriteContent(filepath string, content string) instead func (s *fakeServer) ModifyCacheContent(path, content string) { s.NbCall.ModifyCacheContent++ if s.override.ModifyCacheContent != nil { s.override.ModifyCacheContent(path, content) } else { os.MkdirAll(s.dirCache+"/"+filepath.Dir(path), fs.ModePerm) if err := os.WriteFile(s.dirCache+"/"+path, []byte(content), fs.ModePerm); err != nil { s.t.Fatal(err) } } } // CopyFile implements [model.ServerNeeded]. func (s *fakeServer) CopyFile(fromFs model.FsBase, fromPath string, toFs model.FsBase, toPath string) error { s.NbCall.CopyFile++ if s.override.CopyFile != nil { return s.override.CopyFile(fromFs, fromPath, toFs, toPath) } else { realPath := "" switch fromFs { case s.fsGit: realPath = filepath.Join(s.dirGit, fromPath) case s.fsWeb: realPath = filepath.Join(s.dirWeb, fromPath) case s.fsCache: realPath = filepath.Join(s.dirCache, fromPath) default: return errors.New("unknown fsbase") } content, err := os.ReadFile(realPath) if err != nil { return err } return s.WriteContent(toFs, toPath, string(content)) } } // DeleteFile implements [model.ServerNeeded]. func (s *fakeServer) DeleteFile(fromFs model.FsBase, filename string) error { s.NbCall.DeleteFile++ if s.override.DeleteFile != nil { return s.override.DeleteFile(fromFs, filename) } else { realPath := "" switch fromFs { case s.fsGit: realPath = filepath.Join(s.dirGit, filename) case s.fsWeb: realPath = filepath.Join(s.dirWeb, filename) case s.fsCache: realPath = filepath.Join(s.dirCache, filename) default: return errors.New("unknown fsbase") } return os.Remove(realPath) } } // MoveFile implements [model.ServerNeeded]. func (s *fakeServer) MoveFile(fromFs model.FsBase, fromPath string, toFs model.FsBase, toPath string) error { s.NbCall.MoveFile++ if s.override.MoveFile != nil { return s.override.MoveFile(fromFs, fromPath, toFs, toPath) } else { if err := s.CopyFile(fromFs, fromPath, toFs, toPath); err != nil { return err } return s.DeleteFile(fromFs, fromPath) } } // ReplaceContent implements [model.ServerNeeded]. func (s *fakeServer) ReplaceContent(fromFs model.FsBase, path string, oldContent string, content string) error { s.NbCall.ReplaceContent++ if s.override.ReplaceContent != nil { return s.override.ReplaceContent(fromFs, path, oldContent, content) } else { realPath := "" switch fromFs { case s.fsGit: realPath = filepath.Join(s.dirGit, path) case s.fsWeb: realPath = filepath.Join(s.dirWeb, path) case s.fsCache: realPath = filepath.Join(s.dirCache, path) default: return errors.New("unknown fsbase") } content, err := os.ReadFile(realPath) if err != nil { return err } return os.WriteFile(realPath, bytes.Replace(content, []byte(oldContent), []byte(content), 1), 0666) } } // WriteContent implements [model.ServerNeeded]. func (s *fakeServer) WriteContent(fromFs model.FsBase, path string, content string) error { s.NbCall.WriteContent++ if s.override.WriteContent != nil { return s.override.WriteContent(fromFs, path, content) } else { switch fromFs { case s.fsGit: os.MkdirAll(s.dirGit+"/"+filepath.Dir(path), fs.ModePerm) return os.WriteFile(s.dirGit+"/"+path, []byte(content), fs.ModePerm) case s.fsWeb: os.MkdirAll(s.dirWeb+"/"+filepath.Dir(path), fs.ModePerm) return os.WriteFile(s.dirWeb+"/"+path, []byte(content), fs.ModePerm) case s.fsCache: os.MkdirAll(s.dirCache+"/"+filepath.Dir(path), fs.ModePerm) return os.WriteFile(s.dirCache+"/"+path, []byte(content), fs.ModePerm) } return errors.New("unknown fsbase") } } func (s *fakeServer) CommitAllIfNeeded(message string) { s.NbCall.CommitAllIfNeeded++ if s.override.CommitAllIfNeeded != nil { s.override.CommitAllIfNeeded(message) } } func (s *fakeServer) DiffWithParent(hash string, oldFilepath string, newFilepath string) (string, error) { s.NbCall.DiffWithParent++ if s.override.DiffWithParent != nil { return s.override.DiffWithParent(hash, oldFilepath, newFilepath) } return "", nil } func (s *fakeServer) Log(message string) { s.NbCall.Log++ } func (s *fakeServer) LogError(message string, err error) { s.NbCall.LogError++ } func (s *fakeServer) Merge(from string, to string) { s.NbCall.Merge++ if s.override.Merge != nil { s.override.Merge(from, to) } } func (s *fakeServer) Commits(from string, to string) ([]model.Commit, error) { s.NbCall.Commits++ if s.override.Commits != nil { return s.override.Commits(from, to) } return []model.Commit{}, nil } func (s *fakeServer) Exec(exec model.Exec) (model.ExecStatus, error) { s.NbCall.Exec++ if s.override.Exec != nil { return s.override.Exec(exec) } return model.ExecStatus{}, nil } func (s *fakeServer) Report(level model.ReportLevel, content []string) error { s.NbCall.Report++ if s.override.Report != nil { return s.override.Report(level, content) } return nil } func (s *fakeServer) ExportFunc(name string, callback func(args map[string]string) (map[string]string, error)) { s.NbCall.ExportFunc++ if s.override.ExportFunc != nil { s.override.ExportFunc(name, callback) } } func (s *fakeServer) CanCallFunc(plugin string, name string, args map[string]string) bool { s.NbCall.CanCallFunc++ if s.override.CanCallFunc != nil { return s.override.CanCallFunc(plugin, name, args) } return false } func (s *fakeServer) CallFunc(plugin string, name string, args map[string]string) (map[string]string, error) { s.NbCall.CallFunc++ if s.override.CallFunc != nil { return s.override.CallFunc(plugin, name, args) } return map[string]string{}, nil }