diff --git a/internal/actions/actions.go b/internal/actions/actions.go index 5c8b460..7a65ab6 100644 --- a/internal/actions/actions.go +++ b/internal/actions/actions.go @@ -141,17 +141,8 @@ func syncGistPreviews() { func resetHooks() { log.Info().Msg("Resetting Git server hooks for all repositories...") - entries, err := filepath.Glob(filepath.Join(config.GetHomeDir(), "repos", "*", "*")) - if err != nil { - log.Error().Err(err).Msg("Cannot read repos directories") - return - } - - for _, e := range entries { - path := strings.Split(e, string(os.PathSeparator)) - if err := git.CreateDotGitFiles(path[len(path)-2], path[len(path)-1]); err != nil { - log.Error().Err(err).Msgf("Cannot reset hooks for repository %s/%s", path[len(path)-2], path[len(path)-1]) - } + if err := git.ResetHooks(); err != nil { + log.Error().Err(err).Msg("Error resetting hooks for repositories") } } diff --git a/internal/db/user.go b/internal/db/user.go index d7ad00e..be170ba 100644 --- a/internal/db/user.go +++ b/internal/db/user.go @@ -1,6 +1,7 @@ package db import ( + "github.com/thomiceli/opengist/internal/git" "gorm.io/gorm" ) @@ -65,7 +66,17 @@ func (user *User) BeforeDelete(tx *gorm.DB) error { } // Delete all gists created by this user - return tx.Where("user_id = ?", user.ID).Delete(&Gist{}).Error + err = tx.Where("user_id = ?", user.ID).Delete(&Gist{}).Error + if err != nil { + return err + } + + // Delete user directory + if err = git.DeleteUserDirectory(user.Username); err != nil { + return err + } + + return nil } func UserExists(username string) (bool, error) { diff --git a/internal/git/commands.go b/internal/git/commands.go index e1b8173..23a332c 100644 --- a/internal/git/commands.go +++ b/internal/git/commands.go @@ -485,6 +485,22 @@ func GcRepos() error { return err } +func ResetHooks() error { + entries, err := filepath.Glob(filepath.Join(config.GetHomeDir(), ReposDirectory, "*", "*")) + if err != nil { + return err + } + + for _, e := range entries { + repoPath := strings.Split(e, string(os.PathSeparator)) + if err := CreateDotGitFiles(repoPath[len(repoPath)-2], repoPath[len(repoPath)-1]); err != nil { + log.Error().Err(err).Msgf("Cannot reset hooks for repository %s/%s", repoPath[len(repoPath)-2], repoPath[len(repoPath)-1]) + } + } + + return nil +} + func HasNoCommits(user string, gist string) (bool, error) { repositoryPath := RepositoryPath(user, gist) @@ -540,6 +556,10 @@ func CreateDotGitFiles(user string, gist string) error { return nil } +func DeleteUserDirectory(user string) error { + return os.RemoveAll(filepath.Join(config.GetHomeDir(), ReposDirectory, user)) +} + func createDotGitHookFile(repositoryPath string, hook string, content string) error { preReceiveDst, err := os.OpenFile(filepath.Join(repositoryPath, "hooks", hook), os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0744) if err != nil { diff --git a/internal/web/handlers/admin/actions.go b/internal/web/handlers/admin/actions.go new file mode 100644 index 0000000..3683930 --- /dev/null +++ b/internal/web/handlers/admin/actions.go @@ -0,0 +1,42 @@ +package admin + +import ( + "github.com/thomiceli/opengist/internal/actions" + "github.com/thomiceli/opengist/internal/web/context" +) + +func AdminSyncReposFromFS(ctx *context.Context) error { + ctx.AddFlash(ctx.Tr("flash.admin.sync-fs"), "success") + go actions.Run(actions.SyncReposFromFS) + return ctx.RedirectTo("/admin-panel") +} + +func AdminSyncReposFromDB(ctx *context.Context) error { + ctx.AddFlash(ctx.Tr("flash.admin.sync-db"), "success") + go actions.Run(actions.SyncReposFromDB) + return ctx.RedirectTo("/admin-panel") +} + +func AdminGcRepos(ctx *context.Context) error { + ctx.AddFlash(ctx.Tr("flash.admin.git-gc"), "success") + go actions.Run(actions.GitGcRepos) + return ctx.RedirectTo("/admin-panel") +} + +func AdminSyncGistPreviews(ctx *context.Context) error { + ctx.AddFlash(ctx.Tr("flash.admin.sync-previews"), "success") + go actions.Run(actions.SyncGistPreviews) + return ctx.RedirectTo("/admin-panel") +} + +func AdminResetHooks(ctx *context.Context) error { + ctx.AddFlash(ctx.Tr("flash.admin.reset-hooks"), "success") + go actions.Run(actions.ResetHooks) + return ctx.RedirectTo("/admin-panel") +} + +func AdminIndexGists(ctx *context.Context) error { + ctx.AddFlash(ctx.Tr("flash.admin.index-gists"), "success") + go actions.Run(actions.IndexGists) + return ctx.RedirectTo("/admin-panel") +} diff --git a/internal/web/handlers/admin.go b/internal/web/handlers/admin/admin.go similarity index 80% rename from internal/web/handlers/admin.go rename to internal/web/handlers/admin/admin.go index 71a6202..7f5c4b0 100644 --- a/internal/web/handlers/admin.go +++ b/internal/web/handlers/admin/admin.go @@ -1,4 +1,4 @@ -package handlers +package admin import ( "github.com/thomiceli/opengist/internal/actions" @@ -6,6 +6,7 @@ import ( "github.com/thomiceli/opengist/internal/db" "github.com/thomiceli/opengist/internal/git" "github.com/thomiceli/opengist/internal/web/context" + "github.com/thomiceli/opengist/internal/web/handlers" "runtime" "strconv" "time" @@ -55,7 +56,7 @@ func AdminUsers(ctx *context.Context) error { ctx.SetData("adminHeaderPage", "users") ctx.SetData("loadStartTime", time.Now()) - pageInt := getPage(ctx) + pageInt := handlers.GetPage(ctx) var data []*db.User var err error @@ -63,7 +64,7 @@ func AdminUsers(ctx *context.Context) error { return ctx.ErrorRes(500, "Cannot get users", err) } - if err = paginate(ctx, data, pageInt, 10, "data", "admin-panel/users", 1); err != nil { + if err = handlers.Paginate(ctx, data, pageInt, 10, "data", "admin-panel/users", 1); err != nil { return ctx.ErrorRes(404, ctx.Tr("error.page-not-found"), nil) } @@ -73,7 +74,7 @@ func AdminUsers(ctx *context.Context) error { func AdminGists(ctx *context.Context) error { ctx.SetData("htmlTitle", ctx.TrH("admin.gists")+" - "+ctx.TrH("admin.admin_panel")) ctx.SetData("adminHeaderPage", "gists") - pageInt := getPage(ctx) + pageInt := handlers.GetPage(ctx) var data []*db.Gist var err error @@ -81,7 +82,7 @@ func AdminGists(ctx *context.Context) error { return ctx.ErrorRes(500, "Cannot get gists", err) } - if err = paginate(ctx, data, pageInt, 10, "data", "admin-panel/gists", 1); err != nil { + if err = handlers.Paginate(ctx, data, pageInt, 10, "data", "admin-panel/gists", 1); err != nil { return ctx.ErrorRes(404, ctx.Tr("error.page-not-found"), nil) } @@ -123,42 +124,6 @@ func AdminGistDelete(ctx *context.Context) error { return ctx.RedirectTo("/admin-panel/gists") } -func AdminSyncReposFromFS(ctx *context.Context) error { - ctx.AddFlash(ctx.Tr("flash.admin.sync-fs"), "success") - go actions.Run(actions.SyncReposFromFS) - return ctx.RedirectTo("/admin-panel") -} - -func AdminSyncReposFromDB(ctx *context.Context) error { - ctx.AddFlash(ctx.Tr("flash.admin.sync-db"), "success") - go actions.Run(actions.SyncReposFromDB) - return ctx.RedirectTo("/admin-panel") -} - -func AdminGcRepos(ctx *context.Context) error { - ctx.AddFlash(ctx.Tr("flash.admin.git-gc"), "success") - go actions.Run(actions.GitGcRepos) - return ctx.RedirectTo("/admin-panel") -} - -func AdminSyncGistPreviews(ctx *context.Context) error { - ctx.AddFlash(ctx.Tr("flash.admin.sync-previews"), "success") - go actions.Run(actions.SyncGistPreviews) - return ctx.RedirectTo("/admin-panel") -} - -func AdminResetHooks(ctx *context.Context) error { - ctx.AddFlash(ctx.Tr("flash.admin.reset-hooks"), "success") - go actions.Run(actions.ResetHooks) - return ctx.RedirectTo("/admin-panel") -} - -func AdminIndexGists(ctx *context.Context) error { - ctx.AddFlash(ctx.Tr("flash.admin.index-gists"), "success") - go actions.Run(actions.IndexGists) - return ctx.RedirectTo("/admin-panel") -} - func AdminConfig(ctx *context.Context) error { ctx.SetData("htmlTitle", ctx.TrH("admin.configuration")+" - "+ctx.TrH("admin.admin_panel")) ctx.SetData("adminHeaderPage", "config") diff --git a/internal/web/handlers/gist.go b/internal/web/handlers/gist.go index 720bcb5..ee169b4 100644 --- a/internal/web/handlers/gist.go +++ b/internal/web/handlers/gist.go @@ -33,7 +33,7 @@ func AllGists(ctx *context.Context) error { fromUserStr := ctx.Param("user") userLogged := ctx.User - pageInt := getPage(ctx) + pageInt := GetPage(ctx) sort := "created" sortText := ctx.TrH("gist.list.sort-by-created") @@ -150,7 +150,7 @@ func AllGists(ctx *context.Context) error { return ctx.ErrorRes(500, "Error fetching gists", err) } - if err = paginate(ctx, renderedGists, pageInt, 10, "gists", fromUserStr, 2, "&sort="+sort+"&order="+order); err != nil { + if err = Paginate(ctx, renderedGists, pageInt, 10, "gists", fromUserStr, 2, "&sort="+sort+"&order="+order); err != nil { return ctx.ErrorRes(404, ctx.Tr("error.page-not-found"), nil) } @@ -162,7 +162,7 @@ func Search(ctx *context.Context) error { var err error content, meta := ParseSearchQueryStr(ctx.QueryParam("q")) - pageInt := getPage(ctx) + pageInt := GetPage(ctx) var currentUserId uint userLogged := ctx.User @@ -336,14 +336,14 @@ func Revisions(ctx *context.Context) error { userName := gist.User.Username gistName := gist.Identifier() - pageInt := getPage(ctx) + pageInt := GetPage(ctx) commits, err := gist.Log((pageInt - 1) * 10) if err != nil { return ctx.ErrorRes(500, "Error fetching commits log", err) } - if err := paginate(ctx, commits, pageInt, 10, "commits", userName+"/"+gistName+"/revisions", 2); err != nil { + if err := Paginate(ctx, commits, pageInt, 10, "commits", userName+"/"+gistName+"/revisions", 2); err != nil { return ctx.ErrorRes(404, ctx.Tr("error.page-not-found"), nil) } @@ -701,14 +701,14 @@ func DownloadZip(ctx *context.Context) error { func Likes(ctx *context.Context) error { gist := ctx.GetData("gist").(*db.Gist) - pageInt := getPage(ctx) + pageInt := GetPage(ctx) likers, err := gist.GetUsersLikes(pageInt - 1) if err != nil { return ctx.ErrorRes(500, "Error getting users who liked this gist", err) } - if err = paginate(ctx, likers, pageInt, 30, "likers", gist.User.Username+"/"+gist.Identifier()+"/likes", 1); err != nil { + if err = Paginate(ctx, likers, pageInt, 30, "likers", gist.User.Username+"/"+gist.Identifier()+"/likes", 1); err != nil { return ctx.ErrorRes(404, ctx.Tr("error.page-not-found"), nil) } @@ -719,7 +719,7 @@ func Likes(ctx *context.Context) error { func Forks(ctx *context.Context) error { gist := ctx.GetData("gist").(*db.Gist) - pageInt := getPage(ctx) + pageInt := GetPage(ctx) currentUser := ctx.User var fromUserID uint = 0 @@ -732,7 +732,7 @@ func Forks(ctx *context.Context) error { return ctx.ErrorRes(500, "Error getting users who liked this gist", err) } - if err = paginate(ctx, forks, pageInt, 30, "forks", gist.User.Username+"/"+gist.Identifier()+"/forks", 2); err != nil { + if err = Paginate(ctx, forks, pageInt, 30, "forks", gist.User.Username+"/"+gist.Identifier()+"/forks", 2); err != nil { return ctx.ErrorRes(404, ctx.Tr("error.page-not-found"), nil) } diff --git a/internal/web/handlers/util.go b/internal/web/handlers/util.go index 3bde2ef..25b161c 100644 --- a/internal/web/handlers/util.go +++ b/internal/web/handlers/util.go @@ -8,7 +8,7 @@ import ( "strings" ) -func getPage(ctx *context.Context) int { +func GetPage(ctx *context.Context) int { page := ctx.QueryParam("page") if page == "" { page = "1" @@ -22,7 +22,7 @@ func getPage(ctx *context.Context) int { return pageInt } -func paginate[T any](ctx *context.Context, data []*T, pageInt int, perPage int, templateDataName string, urlPage string, labels int, urlParams ...string) error { +func Paginate[T any](ctx *context.Context, data []*T, pageInt int, perPage int, templateDataName string, urlPage string, labels int, urlParams ...string) error { lenData := len(data) if lenData == 0 && pageInt != 1 { return errors.New("page not found") diff --git a/internal/web/server/router.go b/internal/web/server/router.go index 445a353..48759e6 100644 --- a/internal/web/server/router.go +++ b/internal/web/server/router.go @@ -6,6 +6,7 @@ import ( "github.com/thomiceli/opengist/internal/index" "github.com/thomiceli/opengist/internal/web/context" "github.com/thomiceli/opengist/internal/web/handlers" + "github.com/thomiceli/opengist/internal/web/handlers/admin" "github.com/thomiceli/opengist/public" "net/http" "os" @@ -63,22 +64,22 @@ func (s *Server) registerRoutes() { sB := r.SubGroup("/admin-panel") { sB.Use(adminPermission) - sB.GET("", handlers.AdminIndex) - sB.GET("/users", handlers.AdminUsers) - sB.POST("/users/:user/delete", handlers.AdminUserDelete) - sB.GET("/gists", handlers.AdminGists) - sB.POST("/gists/:gist/delete", handlers.AdminGistDelete) - sB.GET("/invitations", handlers.AdminInvitations) - sB.POST("/invitations", handlers.AdminInvitationsCreate) - sB.POST("/invitations/:id/delete", handlers.AdminInvitationsDelete) - sB.POST("/sync-fs", handlers.AdminSyncReposFromFS) - sB.POST("/sync-db", handlers.AdminSyncReposFromDB) - sB.POST("/gc-repos", handlers.AdminGcRepos) - sB.POST("/sync-previews", handlers.AdminSyncGistPreviews) - sB.POST("/reset-hooks", handlers.AdminResetHooks) - sB.POST("/index-gists", handlers.AdminIndexGists) - sB.GET("/configuration", handlers.AdminConfig) - sB.PUT("/set-config", handlers.AdminSetConfig) + sB.GET("", admin.AdminIndex) + sB.GET("/users", admin.AdminUsers) + sB.POST("/users/:user/delete", admin.AdminUserDelete) + sB.GET("/gists", admin.AdminGists) + sB.POST("/gists/:gist/delete", admin.AdminGistDelete) + sB.GET("/invitations", admin.AdminInvitations) + sB.POST("/invitations", admin.AdminInvitationsCreate) + sB.POST("/invitations/:id/delete", admin.AdminInvitationsDelete) + sB.POST("/sync-fs", admin.AdminSyncReposFromFS) + sB.POST("/sync-db", admin.AdminSyncReposFromDB) + sB.POST("/gc-repos", admin.AdminGcRepos) + sB.POST("/sync-previews", admin.AdminSyncGistPreviews) + sB.POST("/reset-hooks", admin.AdminResetHooks) + sB.POST("/index-gists", admin.AdminIndexGists) + sB.GET("/configuration", admin.AdminConfig) + sB.PUT("/set-config", admin.AdminSetConfig) } if config.C.HttpGit { diff --git a/internal/web/test/actions_test.go b/internal/web/test/actions_test.go new file mode 100644 index 0000000..db7f6d0 --- /dev/null +++ b/internal/web/test/actions_test.go @@ -0,0 +1,41 @@ +package test + +import ( + "github.com/stretchr/testify/require" + "github.com/thomiceli/opengist/internal/db" + "testing" +) + +func TestAdminActions(t *testing.T) { + s := Setup(t) + defer Teardown(t, s) + urls := []string{ + "/admin-panel/sync-fs", + "/admin-panel/sync-db", + "/admin-panel/gc-repos", + "/admin-panel/sync-previews", + "/admin-panel/reset-hooks", + "/admin-panel/index-gists", + } + + for _, url := range urls { + err := s.Request("POST", url, nil, 404) + require.NoError(t, err) + } + + user1 := db.UserDTO{Username: "admin", Password: "admin"} + register(t, s, user1) + login(t, s, user1) + for _, url := range urls { + err := s.Request("POST", url, nil, 302) + require.NoError(t, err) + } + + user2 := db.UserDTO{Username: "nonadmin", Password: "nonadmin"} + register(t, s, user2) + login(t, s, user2) + for _, url := range urls { + err := s.Request("POST", url, nil, 404) + require.NoError(t, err) + } +} diff --git a/internal/web/test/admin_test.go b/internal/web/test/admin_test.go new file mode 100644 index 0000000..a18ebb4 --- /dev/null +++ b/internal/web/test/admin_test.go @@ -0,0 +1,261 @@ +package test + +import ( + "github.com/stretchr/testify/require" + "github.com/thomiceli/opengist/internal/config" + "github.com/thomiceli/opengist/internal/db" + "github.com/thomiceli/opengist/internal/git" + "os" + "path/filepath" + "strconv" + "testing" + "time" +) + +func TestAdminPages(t *testing.T) { + s := Setup(t) + defer Teardown(t, s) + urls := []string{ + "/admin-panel", + "/admin-panel/users", + "/admin-panel/gists", + "/admin-panel/invitations", + "/admin-panel/configuration", + } + + for _, url := range urls { + err := s.Request("GET", url, nil, 404) + require.NoError(t, err) + } + + user1 := db.UserDTO{Username: "admin", Password: "admin"} + register(t, s, user1) + login(t, s, user1) + for _, url := range urls { + err := s.Request("GET", url, nil, 200) + require.NoError(t, err) + } + + user2 := db.UserDTO{Username: "nonadmin", Password: "nonadmin"} + register(t, s, user2) + login(t, s, user2) + for _, url := range urls { + err := s.Request("GET", url, nil, 404) + require.NoError(t, err) + } +} + +func TestSetConfig(t *testing.T) { + s := Setup(t) + defer Teardown(t, s) + settings := []string{ + db.SettingDisableSignup, + db.SettingRequireLogin, + db.SettingAllowGistsWithoutLogin, + db.SettingDisableLoginForm, + db.SettingDisableGravatar, + } + + user1 := db.UserDTO{Username: "admin", Password: "admin"} + register(t, s, user1) + login(t, s, user1) + + for _, setting := range settings { + val, err := db.GetSetting(setting) + require.NoError(t, err) + require.Equal(t, "0", val) + + err = s.Request("PUT", "/admin-panel/set-config", settingSet{setting, "1"}, 200) + require.NoError(t, err) + + val, err = db.GetSetting(setting) + require.NoError(t, err) + require.Equal(t, "1", val) + + err = s.Request("PUT", "/admin-panel/set-config", settingSet{setting, "0"}, 200) + require.NoError(t, err) + + val, err = db.GetSetting(setting) + require.NoError(t, err) + require.Equal(t, "0", val) + } +} + +func TestPagination(t *testing.T) { + s := Setup(t) + defer Teardown(t, s) + + user1 := db.UserDTO{Username: "admin", Password: "admin"} + register(t, s, user1) + for i := 0; i < 11; i++ { + user := db.UserDTO{Username: "user" + strconv.Itoa(i), Password: "user" + strconv.Itoa(i)} + register(t, s, user) + } + + login(t, s, user1) + + err := s.Request("GET", "/admin-panel/users", nil, 200) + require.NoError(t, err) + + err = s.Request("GET", "/admin-panel/users?page=2", nil, 200) + require.NoError(t, err) + + err = s.Request("GET", "/admin-panel/users?page=3", nil, 404) + require.NoError(t, err) + + err = s.Request("GET", "/admin-panel/users?page=0", nil, 200) + require.NoError(t, err) + + err = s.Request("GET", "/admin-panel/users?page=-1", nil, 200) + require.NoError(t, err) + + err = s.Request("GET", "/admin-panel/users?page=a", nil, 200) + require.NoError(t, err) +} + +func TestAdminUser(t *testing.T) { + s := Setup(t) + defer Teardown(t, s) + + user1 := db.UserDTO{Username: "admin", Password: "admin"} + user2 := db.UserDTO{Username: "nonadmin", Password: "nonadmin"} + register(t, s, user1) + register(t, s, user2) + + login(t, s, user2) + + gist1 := db.GistDTO{ + Title: "gist", + VisibilityDTO: db.VisibilityDTO{ + Private: 0, + }, + Name: []string{"gist1.txt"}, + Content: []string{"yeah"}, + } + err := s.Request("POST", "/", gist1, 302) + require.NoError(t, err) + + _, err = os.Stat(filepath.Join(config.GetHomeDir(), git.ReposDirectory, user2.Username)) + require.NoError(t, err) + + count, err := db.CountAll(db.User{}) + require.NoError(t, err) + require.Equal(t, int64(2), count) + + login(t, s, user1) + + err = s.Request("POST", "/admin-panel/users/2/delete", nil, 302) + require.NoError(t, err) + + count, err = db.CountAll(db.User{}) + require.NoError(t, err) + require.Equal(t, int64(1), count) + + _, err = os.Stat(filepath.Join(config.GetHomeDir(), git.ReposDirectory, user2.Username)) + require.Error(t, err) +} + +func TestAdminGist(t *testing.T) { + s := Setup(t) + defer Teardown(t, s) + + user1 := db.UserDTO{Username: "admin", Password: "admin"} + register(t, s, user1) + login(t, s, user1) + + gist1 := db.GistDTO{ + Title: "gist", + VisibilityDTO: db.VisibilityDTO{ + Private: 0, + }, + Name: []string{"gist1.txt"}, + Content: []string{"yeah"}, + } + err := s.Request("POST", "/", gist1, 302) + require.NoError(t, err) + + count, err := db.CountAll(db.Gist{}) + require.NoError(t, err) + require.Equal(t, int64(1), count) + + gist1Db, err := db.GetGistByID("1") + require.NoError(t, err) + + _, err = os.Stat(filepath.Join(config.GetHomeDir(), git.ReposDirectory, user1.Username, gist1Db.Identifier())) + require.NoError(t, err) + + err = s.Request("POST", "/admin-panel/gists/1/delete", nil, 302) + require.NoError(t, err) + + count, err = db.CountAll(db.Gist{}) + require.NoError(t, err) + require.Equal(t, int64(0), count) + + _, err = os.Stat(filepath.Join(config.GetHomeDir(), git.ReposDirectory, user1.Username, gist1Db.Identifier())) + require.Error(t, err) +} + +func TestAdminInvitation(t *testing.T) { + s := Setup(t) + defer Teardown(t, s) + + user1 := db.UserDTO{Username: "admin", Password: "admin"} + register(t, s, user1) + login(t, s, user1) + + err := s.Request("POST", "/admin-panel/invitations", invitationAdmin{ + nbMax: "", + expiredAtUnix: "", + }, 302) + require.NoError(t, err) + invitation1, err := db.GetInvitationByID(1) + require.NoError(t, err) + require.Equal(t, invitation1, &db.Invitation{ + ID: 1, + Code: invitation1.Code, + ExpiresAt: time.Now().Unix() + 604800, + NbUsed: 0, + NbMax: 10, + }) + + err = s.Request("POST", "/admin-panel/invitations", invitationAdmin{ + nbMax: "aa", + expiredAtUnix: "1735722000", + }, 302) + require.NoError(t, err) + invitation2, err := db.GetInvitationByID(2) + require.NoError(t, err) + require.Equal(t, invitation2, &db.Invitation{ + ID: 2, + Code: invitation2.Code, + ExpiresAt: time.Unix(1735722000, 0).Unix(), + NbUsed: 0, + NbMax: 10, + }) + + err = s.Request("POST", "/admin-panel/invitations", invitationAdmin{ + nbMax: "20", + expiredAtUnix: "1735722000", + }, 302) + require.NoError(t, err) + invitation3, err := db.GetInvitationByID(3) + require.NoError(t, err) + require.Equal(t, invitation3, &db.Invitation{ + ID: 3, + Code: invitation3.Code, + ExpiresAt: time.Unix(1735722000, 0).Unix(), + NbUsed: 0, + NbMax: 20, + }) + + count, err := db.CountAll(db.Invitation{}) + require.NoError(t, err) + require.Equal(t, int64(3), count) + + err = s.Request("POST", "/admin-panel/invitations/1/delete", nil, 302) + require.NoError(t, err) + + count, err = db.CountAll(db.Invitation{}) + require.NoError(t, err) + require.Equal(t, int64(2), count) +} diff --git a/internal/web/test/auth_test.go b/internal/web/test/auth_test.go index 16bce8e..fc8194f 100644 --- a/internal/web/test/auth_test.go +++ b/internal/web/test/auth_test.go @@ -12,13 +12,13 @@ import ( ) func TestRegister(t *testing.T) { - s := setup(t) - defer teardown(t, s) + s := Setup(t) + defer Teardown(t, s) - err := s.request("GET", "/", nil, 302) + err := s.Request("GET", "/", nil, 302) require.NoError(t, err) - err = s.request("GET", "/register", nil, 200) + err = s.Request("GET", "/register", nil, 200) require.NoError(t, err) user1 := db.UserDTO{Username: "thomas", Password: "thomas"} @@ -29,13 +29,13 @@ func TestRegister(t *testing.T) { require.Equal(t, user1.Username, user1db.Username) require.True(t, user1db.IsAdmin) - err = s.request("GET", "/", nil, 200) + err = s.Request("GET", "/", nil, 200) require.NoError(t, err) s.sessionCookie = "" user2 := db.UserDTO{Username: "thomas", Password: "azeaze"} - err = s.request("POST", "/register", user2, 200) + err = s.Request("POST", "/register", user2, 200) require.Error(t, err) user3 := db.UserDTO{Username: "kaguya", Password: "kaguya"} @@ -53,10 +53,10 @@ func TestRegister(t *testing.T) { } func TestLogin(t *testing.T) { - s := setup(t) - defer teardown(t, s) + s := Setup(t) + defer Teardown(t, s) - err := s.request("GET", "/login", nil, 200) + err := s.Request("GET", "/login", nil, 200) require.NoError(t, err) user1 := db.UserDTO{Username: "thomas", Password: "thomas"} @@ -72,38 +72,33 @@ func TestLogin(t *testing.T) { user2 := db.UserDTO{Username: "thomas", Password: "azeaze"} user3 := db.UserDTO{Username: "azeaze", Password: ""} - err = s.request("POST", "/login", user2, 302) + err = s.Request("POST", "/login", user2, 302) require.Empty(t, s.sessionCookie) require.Error(t, err) - err = s.request("POST", "/login", user3, 302) + err = s.Request("POST", "/login", user3, 302) require.Empty(t, s.sessionCookie) require.Error(t, err) } -func register(t *testing.T, s *testServer, user db.UserDTO) { - err := s.request("POST", "/register", user, 302) +func register(t *testing.T, s *TestServer, user db.UserDTO) { + err := s.Request("POST", "/register", user, 302) require.NoError(t, err) } -func login(t *testing.T, s *testServer, user db.UserDTO) { - err := s.request("POST", "/login", user, 302) +func login(t *testing.T, s *TestServer, user db.UserDTO) { + err := s.Request("POST", "/login", user, 302) require.NoError(t, err) } -type settingSet struct { - key string `form:"key"` - value string `form:"value"` -} - func TestAnonymous(t *testing.T) { - s := setup(t) - defer teardown(t, s) + s := Setup(t) + defer Teardown(t, s) user := db.UserDTO{Username: "thomas", Password: "azeaze"} register(t, s, user) - err := s.request("PUT", "/admin-panel/set-config", settingSet{"require-login", "1"}, 200) + err := s.Request("PUT", "/admin-panel/set-config", settingSet{"require-login", "1"}, 200) require.NoError(t, err) gist1 := db.GistDTO{ @@ -115,41 +110,41 @@ func TestAnonymous(t *testing.T) { Name: []string{"gist1.txt", "gist2.txt", "gist3.txt"}, Content: []string{"yeah", "yeah\ncool", "yeah\ncool gist actually"}, } - err = s.request("POST", "/", gist1, 302) + err = s.Request("POST", "/", gist1, 302) require.NoError(t, err) gist1db, err := db.GetGistByID("1") require.NoError(t, err) - err = s.request("GET", "/all", nil, 200) + err = s.Request("GET", "/all", nil, 200) require.NoError(t, err) cookie := s.sessionCookie s.sessionCookie = "" - err = s.request("GET", "/all", nil, 302) + err = s.Request("GET", "/all", nil, 302) require.NoError(t, err) // Should redirect to login if RequireLogin - err = s.request("GET", "/"+gist1db.User.Username+"/"+gist1db.Uuid, nil, 302) + err = s.Request("GET", "/"+gist1db.User.Username+"/"+gist1db.Uuid, nil, 302) require.NoError(t, err) s.sessionCookie = cookie - err = s.request("PUT", "/admin-panel/set-config", settingSet{"allow-gists-without-login", "1"}, 200) + err = s.Request("PUT", "/admin-panel/set-config", settingSet{"allow-gists-without-login", "1"}, 200) require.NoError(t, err) s.sessionCookie = "" // Should return results - err = s.request("GET", "/"+gist1db.User.Username+"/"+gist1db.Uuid, nil, 200) + err = s.Request("GET", "/"+gist1db.User.Username+"/"+gist1db.Uuid, nil, 200) require.NoError(t, err) } func TestGitOperations(t *testing.T) { - s := setup(t) - defer teardown(t, s) + s := Setup(t) + defer Teardown(t, s) admin := db.UserDTO{Username: "thomas", Password: "thomas"} register(t, s, admin) @@ -170,7 +165,7 @@ func TestGitOperations(t *testing.T) { "yeah", }, } - err := s.request("POST", "/", gist1, 302) + err := s.Request("POST", "/", gist1, 302) require.NoError(t, err) gist2 := db.GistDTO{ @@ -185,7 +180,7 @@ func TestGitOperations(t *testing.T) { "cool", }, } - err = s.request("POST", "/", gist2, 302) + err = s.Request("POST", "/", gist2, 302) require.NoError(t, err) gist3 := db.GistDTO{ @@ -200,7 +195,7 @@ func TestGitOperations(t *testing.T) { "super", }, } - err = s.request("POST", "/", gist3, 302) + err = s.Request("POST", "/", gist3, 302) require.NoError(t, err) gitOperations := func(credentials, owner, url, filename string, expectErrorClone, expectErrorCheck, expectErrorPush bool) { @@ -249,7 +244,7 @@ func TestGitOperations(t *testing.T) { } login(t, s, admin) - err = s.request("PUT", "/admin-panel/set-config", settingSet{"require-login", "1"}, 200) + err = s.Request("PUT", "/admin-panel/set-config", settingSet{"require-login", "1"}, 200) require.NoError(t, err) testsRequireLogin := []struct { @@ -276,7 +271,7 @@ func TestGitOperations(t *testing.T) { } login(t, s, admin) - err = s.request("PUT", "/admin-panel/set-config", settingSet{"allow-gists-without-login", "1"}, 200) + err = s.Request("PUT", "/admin-panel/set-config", settingSet{"allow-gists-without-login", "1"}, 200) require.NoError(t, err) for _, test := range tests { diff --git a/internal/web/test/gist_test.go b/internal/web/test/gist_test.go index 310cc66..b46621c 100644 --- a/internal/web/test/gist_test.go +++ b/internal/web/test/gist_test.go @@ -9,19 +9,19 @@ import ( ) func TestGists(t *testing.T) { - s := setup(t) - defer teardown(t, s) + s := Setup(t) + defer Teardown(t, s) - err := s.request("GET", "/", nil, 302) + err := s.Request("GET", "/", nil, 302) require.NoError(t, err) user1 := db.UserDTO{Username: "thomas", Password: "thomas"} register(t, s, user1) - err = s.request("GET", "/all", nil, 200) + err = s.Request("GET", "/all", nil, 200) require.NoError(t, err) - err = s.request("POST", "/", nil, 200) + err = s.Request("POST", "/", nil, 200) require.NoError(t, err) gist1 := db.GistDTO{ @@ -33,7 +33,7 @@ func TestGists(t *testing.T) { Name: []string{"gist1.txt", "gist2.txt", "gist3.txt"}, Content: []string{"yeah", "yeah\ncool", "yeah\ncool gist actually"}, } - err = s.request("POST", "/", gist1, 302) + err = s.Request("POST", "/", gist1, 302) require.NoError(t, err) gist1db, err := db.GetGistByID("1") @@ -44,7 +44,7 @@ func TestGists(t *testing.T) { require.Regexp(t, "[a-f0-9]{32}", gist1db.Uuid) require.Equal(t, user1.Username, gist1db.User.Username) - err = s.request("GET", "/"+gist1db.User.Username+"/"+gist1db.Uuid, nil, 200) + err = s.Request("GET", "/"+gist1db.User.Username+"/"+gist1db.Uuid, nil, 200) require.NoError(t, err) gist1files, err := git.GetFilesOfRepository(gist1db.User.Username, gist1db.Uuid, "HEAD") @@ -64,7 +64,7 @@ func TestGists(t *testing.T) { Name: []string{"", "gist2.txt", "gist3.txt"}, Content: []string{"", "yeah\ncool", "yeah\ncool gist actually"}, } - err = s.request("POST", "/", gist2, 200) + err = s.Request("POST", "/", gist2, 200) require.NoError(t, err) gist3 := db.GistDTO{ @@ -76,7 +76,7 @@ func TestGists(t *testing.T) { Name: []string{""}, Content: []string{"yeah"}, } - err = s.request("POST", "/", gist3, 302) + err = s.Request("POST", "/", gist3, 302) require.NoError(t, err) gist3db, err := db.GetGistByID("2") @@ -86,26 +86,26 @@ func TestGists(t *testing.T) { require.NoError(t, err) require.Equal(t, "gistfile1.txt", gist3files[0]) - err = s.request("POST", "/"+gist1db.User.Username+"/"+gist1db.Uuid+"/edit", nil, 200) + err = s.Request("POST", "/"+gist1db.User.Username+"/"+gist1db.Uuid+"/edit", nil, 200) require.NoError(t, err) gist1.Name = []string{"gist1.txt"} gist1.Content = []string{"only want one gist"} - err = s.request("POST", "/"+gist1db.User.Username+"/"+gist1db.Uuid+"/edit", gist1, 302) + err = s.Request("POST", "/"+gist1db.User.Username+"/"+gist1db.Uuid+"/edit", gist1, 302) require.NoError(t, err) gist1files, err = git.GetFilesOfRepository(gist1db.User.Username, gist1db.Uuid, "HEAD") require.NoError(t, err) require.Equal(t, 1, len(gist1files)) - err = s.request("POST", "/"+gist1db.User.Username+"/"+gist1db.Uuid+"/delete", nil, 302) + err = s.Request("POST", "/"+gist1db.User.Username+"/"+gist1db.Uuid+"/delete", nil, 302) require.NoError(t, err) } func TestVisibility(t *testing.T) { - s := setup(t) - defer teardown(t, s) + s := Setup(t) + defer Teardown(t, s) user1 := db.UserDTO{Username: "thomas", Password: "thomas"} register(t, s, user1) @@ -119,26 +119,26 @@ func TestVisibility(t *testing.T) { Name: []string{""}, Content: []string{"yeah"}, } - err := s.request("POST", "/", gist1, 302) + err := s.Request("POST", "/", gist1, 302) require.NoError(t, err) gist1db, err := db.GetGistByID("1") require.NoError(t, err) require.Equal(t, db.UnlistedVisibility, gist1db.Private) - err = s.request("POST", "/"+gist1db.User.Username+"/"+gist1db.Uuid+"/visibility", db.VisibilityDTO{Private: db.PrivateVisibility}, 302) + err = s.Request("POST", "/"+gist1db.User.Username+"/"+gist1db.Uuid+"/visibility", db.VisibilityDTO{Private: db.PrivateVisibility}, 302) require.NoError(t, err) gist1db, err = db.GetGistByID("1") require.NoError(t, err) require.Equal(t, db.PrivateVisibility, gist1db.Private) - err = s.request("POST", "/"+gist1db.User.Username+"/"+gist1db.Uuid+"/visibility", db.VisibilityDTO{Private: db.PublicVisibility}, 302) + err = s.Request("POST", "/"+gist1db.User.Username+"/"+gist1db.Uuid+"/visibility", db.VisibilityDTO{Private: db.PublicVisibility}, 302) require.NoError(t, err) gist1db, err = db.GetGistByID("1") require.NoError(t, err) require.Equal(t, db.PublicVisibility, gist1db.Private) - err = s.request("POST", "/"+gist1db.User.Username+"/"+gist1db.Uuid+"/visibility", db.VisibilityDTO{Private: db.UnlistedVisibility}, 302) + err = s.Request("POST", "/"+gist1db.User.Username+"/"+gist1db.Uuid+"/visibility", db.VisibilityDTO{Private: db.UnlistedVisibility}, 302) require.NoError(t, err) gist1db, err = db.GetGistByID("1") require.NoError(t, err) @@ -146,8 +146,8 @@ func TestVisibility(t *testing.T) { } func TestLikeFork(t *testing.T) { - s := setup(t) - defer teardown(t, s) + s := Setup(t) + defer Teardown(t, s) user1 := db.UserDTO{Username: "thomas", Password: "thomas"} register(t, s, user1) @@ -161,7 +161,7 @@ func TestLikeFork(t *testing.T) { Name: []string{""}, Content: []string{"yeah"}, } - err := s.request("POST", "/", gist1, 302) + err := s.Request("POST", "/", gist1, 302) require.NoError(t, err) s.sessionCookie = "" @@ -176,7 +176,7 @@ func TestLikeFork(t *testing.T) { require.NoError(t, err) require.Equal(t, int64(0), likeCount) - err = s.request("POST", "/"+gist1db.User.Username+"/"+gist1db.Uuid+"/like", nil, 302) + err = s.Request("POST", "/"+gist1db.User.Username+"/"+gist1db.Uuid+"/like", nil, 302) require.NoError(t, err) gist1db, err = db.GetGistByID("1") require.NoError(t, err) @@ -185,7 +185,7 @@ func TestLikeFork(t *testing.T) { require.NoError(t, err) require.Equal(t, int64(1), likeCount) - err = s.request("POST", "/"+gist1db.User.Username+"/"+gist1db.Uuid+"/like", nil, 302) + err = s.Request("POST", "/"+gist1db.User.Username+"/"+gist1db.Uuid+"/like", nil, 302) require.NoError(t, err) gist1db, err = db.GetGistByID("1") require.NoError(t, err) @@ -194,7 +194,7 @@ func TestLikeFork(t *testing.T) { require.NoError(t, err) require.Equal(t, int64(0), likeCount) - err = s.request("POST", "/"+gist1db.User.Username+"/"+gist1db.Uuid+"/fork", nil, 302) + err = s.Request("POST", "/"+gist1db.User.Username+"/"+gist1db.Uuid+"/fork", nil, 302) require.NoError(t, err) gist2db, err := db.GetGistByID("2") require.NoError(t, err) @@ -205,8 +205,8 @@ func TestLikeFork(t *testing.T) { } func TestCustomUrl(t *testing.T) { - s := setup(t) - defer teardown(t, s) + s := Setup(t) + defer Teardown(t, s) user1 := db.UserDTO{Username: "thomas", Password: "thomas"} register(t, s, user1) @@ -221,7 +221,7 @@ func TestCustomUrl(t *testing.T) { Name: []string{"gist1.txt", "gist2.txt", "gist3.txt"}, Content: []string{"yeah", "yeah\ncool", "yeah\ncool gist actually"}, } - err := s.request("POST", "/", gist1, 302) + err := s.Request("POST", "/", gist1, 302) require.NoError(t, err) gist1db, err := db.GetGistByID("1") @@ -252,7 +252,7 @@ func TestCustomUrl(t *testing.T) { Name: []string{"gist1.txt", "gist2.txt", "gist3.txt"}, Content: []string{"yeah", "yeah\ncool", "yeah\ncool gist actually"}, } - err = s.request("POST", "/", gist2, 302) + err = s.Request("POST", "/", gist2, 302) require.NoError(t, err) gist2db, err := db.GetGistByID("2") diff --git a/internal/web/test/server.go b/internal/web/test/server.go index f508eed..5d7aba5 100644 --- a/internal/web/test/server.go +++ b/internal/web/test/server.go @@ -26,13 +26,13 @@ import ( var databaseType string -type testServer struct { +type TestServer struct { server *server.Server sessionCookie string } -func newTestServer() (*testServer, error) { - s := &testServer{ +func newTestServer() (*TestServer, error) { + s := &TestServer{ server: server.NewServer(true, path.Join(config.GetHomeDir(), "tmp", "sessions"), true), } @@ -40,15 +40,15 @@ func newTestServer() (*testServer, error) { return s, nil } -func (s *testServer) start() { +func (s *TestServer) start() { s.server.Start() } -func (s *testServer) stop() { +func (s *TestServer) stop() { s.server.Stop() } -func (s *testServer) request(method, uri string, data interface{}, expectedCode int) error { +func (s *TestServer) Request(method, uri string, data interface{}, expectedCode int) error { var bodyReader io.Reader if method == http.MethodPost || method == http.MethodPut { values := structToURLValues(data) @@ -133,11 +133,12 @@ func structToURLValues(s interface{}) url.Values { return v } -func setup(t *testing.T) *testServer { +func Setup(t *testing.T) *TestServer { var databaseDsn string databaseType = os.Getenv("OPENGIST_TEST_DB") switch databaseType { case "sqlite": + default: databaseDsn = "file::memory:" case "postgres": databaseDsn = "postgres://postgres:opengist@localhost:5432/opengist_test" @@ -164,6 +165,9 @@ func setup(t *testing.T) *testServer { homePath := config.GetHomeDir() log.Info().Msg("Data directory: " + homePath) + err = os.MkdirAll(filepath.Join(homePath, "tests"), 0755) + require.NoError(t, err, "Could not create tests directory") + err = os.MkdirAll(filepath.Join(homePath, "tmp", "sessions"), 0755) require.NoError(t, err, "Could not create sessions directory") @@ -189,7 +193,7 @@ func setup(t *testing.T) *testServer { return s } -func teardown(t *testing.T, s *testServer) { +func Teardown(t *testing.T, s *TestServer) { s.stop() //err := db.Close() @@ -213,3 +217,13 @@ func teardown(t *testing.T, s *testServer) { // err = index.Close() // require.NoError(t, err, "Could not close index") } + +type settingSet struct { + key string `form:"key"` + value string `form:"value"` +} + +type invitationAdmin struct { + nbMax string `form:"nbMax"` + expiredAtUnix string `form:"expiredAtUnix"` +}