package controller import ( "errors" "html/template" "net/http" "net/http/httptest" "strings" "testing" "git.aetherial.dev/aeth/keiji/pkg/auth" "git.aetherial.dev/aeth/keiji/pkg/storage" "github.com/gin-gonic/gin" ) type adminLegacyListRepo struct{ *htmlLegacyRepo } func (r *adminLegacyListRepo) GetAdminTables() storage.AdminPage { return storage.AdminPage{} } func (r *adminLegacyListRepo) GetAllImages() []storage.Image { return nil } type adminErrorListRepo struct { *adminLegacyListRepo storage.DocumentListReader fail string } func (r *adminErrorListRepo) failure(name string) error { if r.fail == name { return errors.New("list failure: " + name) } return nil } func (r *adminErrorListRepo) GetAdminTablesWithError() (storage.AdminPage, error) { return storage.AdminPage{}, r.failure("tables") } func (r *adminErrorListRepo) GetAllImagesWithError() ([]storage.Image, error) { return nil, r.failure("all images") } func (r *adminErrorListRepo) GetImagesByCategoryWithError(category string) ([]storage.Image, error) { r.category = category return nil, r.failure("images") } func (r *adminErrorListRepo) GetByCategoryWithError(category string) ([]storage.Document, error) { r.category = category return nil, r.failure("documents") } func (r *adminErrorListRepo) GetNavBarLinksWithError() ([]storage.NavBarItem, error) { return nil, r.failure("navbar") } func (r *adminErrorListRepo) GetDropdownElementsWithError() ([]storage.LinkPair, error) { return nil, r.failure("menu") } type adminListAuth struct{} func (adminListAuth) AdminUsername() string { return "user" } func (adminListAuth) AdminPassword() string { return "pass" } func adminListRouter(repo storage.DocumentIO) *gin.Engine { r := gin.New() r.SetHTMLTemplate(template.Must(template.New("test").Parse(`{{define "admin"}}admin {{range index .Tables "modify"}}{{.DisplayName}}{{end}}{{end}}{{define "blogpost_editor"}}editor{{end}}{{define "navbar_editor"}}navbar editor{{end}}{{define "upload"}}upload{{end}}{{define "digital_art_admin_list"}}digital list{{end}}`))) c := &Controller{database: repo, Cache: auth.NewCache(), AuthSource: adminListAuth{}} r.POST("/login", c.Auth) r.GET("/panel", c.AdminPanel) r.GET("/navbar", c.ServeNavbarModify) r.GET("/blog", c.ServeBlogDirectory) r.GET("/editor/:id", c.GetBlogPostEditor) r.GET("/new-blog", c.ServeNewBlogPage) r.GET("/new-navbar", c.ServeNewNavbarItem) r.GET("/upload", c.ServeFileUpload) r.GET("/digital", c.ListDigitalArt) return r } func adminListRequest(r *gin.Engine, path string) *httptest.ResponseRecorder { method, body := http.MethodGet, "" if path == "/login" { method, body = http.MethodPost, "username=user&password=pass" } req := httptest.NewRequest(method, path, strings.NewReader(body)) if method == http.MethodPost { req.Header.Set("Content-Type", "application/x-www-form-urlencoded") } response := httptest.NewRecorder() r.ServeHTTP(response, req) return response } func TestAdminListFailures(t *testing.T) { gin.SetMode(gin.TestMode) for _, tc := range []struct{ path, failure string }{ {"/login", "tables"}, {"/login", "navbar"}, {"/login", "menu"}, {"/panel", "tables"}, {"/panel", "navbar"}, {"/panel", "menu"}, {"/navbar", "navbar"}, {"/navbar", "menu"}, {"/blog", "documents"}, {"/blog", "navbar"}, {"/blog", "menu"}, {"/editor/id", "all images"}, {"/editor/id", "navbar"}, {"/editor/id", "menu"}, {"/new-blog", "all images"}, {"/new-blog", "navbar"}, {"/new-blog", "menu"}, {"/new-navbar", "navbar"}, {"/new-navbar", "menu"}, {"/upload", "navbar"}, {"/upload", "menu"}, {"/digital", "images"}, } { t.Run(tc.path+"/"+tc.failure, func(t *testing.T) { repo := &adminErrorListRepo{adminLegacyListRepo: &adminLegacyListRepo{&htmlLegacyRepo{}}, fail: tc.failure} response := adminListRequest(adminListRouter(repo), tc.path) if response.Code != http.StatusInternalServerError || !strings.Contains(response.Body.String(), "Could not load content") { t.Fatalf("got status %d body %q", response.Code, response.Body.String()) } if tc.path == "/login" && len(response.Result().Cookies()) != 0 { t.Fatal("failed login response set a cookie") } }) } } func TestAdminListEmptyAndLegacyCompatibility(t *testing.T) { gin.SetMode(gin.TestMode) for _, legacy := range []bool{false, true} { for _, tc := range []struct{ path, body string }{ {"/login", "Digital Art"}, {"/panel", "Digital Art"}, {"/navbar", "admin"}, {"/blog", "admin"}, {"/editor/id", "editor"}, {"/new-blog", "editor"}, {"/new-navbar", "navbar editor"}, {"/upload", "upload"}, {"/digital", "digital list"}, } { t.Run(tc.path+map[bool]string{true: "/legacy", false: "/error-reader"}[legacy], func(t *testing.T) { base := &adminLegacyListRepo{&htmlLegacyRepo{}} var repo storage.DocumentIO = base if !legacy { repo = &adminErrorListRepo{adminLegacyListRepo: base} } response := adminListRequest(adminListRouter(repo), tc.path) if response.Code != http.StatusOK || !strings.Contains(response.Body.String(), tc.body) { t.Fatalf("got status %d body %q", response.Code, response.Body.String()) } if tc.path == "/digital" && base.category != storage.DIGITAL_ART { t.Fatalf("category = %q", base.category) } if tc.path == "/login" && len(response.Result().Cookies()) == 0 { t.Fatal("successful login did not set a cookie") } }) } } }