/* Copyright 2026 Arctel.net Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ package template import ( "bytes" "encoding/json" "net/http" "net/http/httptest" "testing" "github.com/Rain-kl/Wavelet/internal/apps/oauth" "github.com/Rain-kl/Wavelet/internal/model" "github.com/Rain-kl/Wavelet/internal/testhelper" "github.com/Rain-kl/Wavelet/internal/util" "github.com/gin-gonic/gin" ) func setupTestRouter(authUser *model.User) *gin.Engine { gin.SetMode(gin.TestMode) r := gin.New() adminGroup := r.Group("/api/v1/admin") // Mock authentication middleware adminGroup.Use(func(c *gin.Context) { if authUser != nil { util.SetToContext(c, oauth.UserObjKey, authUser) } c.Next() }) adminGroup.GET("/templates", ListTemplates) adminGroup.POST("/templates", CreateTemplate) templateRouter := adminGroup.Group("/templates/:key") { templateRouter.GET("", GetTemplate) templateRouter.PUT("", UpdateTemplate) templateRouter.DELETE("", DeleteTemplate) } return r } func TestCreateTemplate(t *testing.T) { dbConn, _, cleanup := testhelper.SetupTestEnvironment(t) defer cleanup() adminUser := &model.User{ID: 1001, Username: "admin", IsAdmin: true} router := setupTestRouter(adminUser) t.Run("create successfully", func(t *testing.T) { payload := CreateTemplateRequest{ Key: "test_template", Name: "Test Template", Type: "email", Subject: "Test Subject", Content: "Hello {{.Name}}", Description: "Test Desc", } body, _ := json.Marshal(payload) req, _ := http.NewRequest("POST", "/api/v1/admin/templates", bytes.NewBuffer(body)) req.Header.Set("Content-Type", "application/json") w := httptest.NewRecorder() router.ServeHTTP(w, req) if w.Code != http.StatusOK { t.Fatalf("expected 200 OK, got %d. Body: %s", w.Code, w.Body.String()) } var tmpl model.Template err := dbConn.Where("key = ?", "test_template").First(&tmpl).Error if err != nil { t.Fatalf("failed to find template in DB: %v", err) } if tmpl.Name != "Test Template" { t.Errorf("expected Name 'Test Template', got '%s'", tmpl.Name) } }) t.Run("create duplicate key error", func(t *testing.T) { payload := CreateTemplateRequest{ Key: "test_template", Name: "Another Name", Type: "email", Subject: "Another Subject", Content: "Hello", Description: "desc", } body, _ := json.Marshal(payload) req, _ := http.NewRequest("POST", "/api/v1/admin/templates", bytes.NewBuffer(body)) req.Header.Set("Content-Type", "application/json") w := httptest.NewRecorder() router.ServeHTTP(w, req) if w.Code != http.StatusBadRequest { t.Errorf("expected 400 Bad Request on duplicate key, got %d", w.Code) } }) } func TestListTemplates(t *testing.T) { dbConn, _, cleanup := testhelper.SetupTestEnvironment(t) defer cleanup() // Seed system templates manually for testing t1 := model.Template{Key: "login_email", Name: "Login Code", Type: "email", Content: "code {{.Code}}", IsSystem: true} t2 := model.Template{Key: "register_email", Name: "Register Code", Type: "email", Content: "code {{.Code}}", IsSystem: true} dbConn.Create(&t1) dbConn.Create(&t2) adminUser := &model.User{ID: 1001, Username: "admin", IsAdmin: true} router := setupTestRouter(adminUser) t.Run("list templates", func(t *testing.T) { req, _ := http.NewRequest("GET", "/api/v1/admin/templates", nil) w := httptest.NewRecorder() router.ServeHTTP(w, req) if w.Code != http.StatusOK { t.Fatalf("expected 200 OK, got %d", w.Code) } var resp util.ResponseAny json.Unmarshal(w.Body.Bytes(), &resp) dataBytes, _ := json.Marshal(resp.Data) var templates []model.Template json.Unmarshal(dataBytes, &templates) if len(templates) != 2 { t.Errorf("expected 2 templates, got %d", len(templates)) } }) } func TestGetTemplate(t *testing.T) { dbConn, _, cleanup := testhelper.SetupTestEnvironment(t) defer cleanup() t1 := model.Template{Key: "login_email", Name: "Login Code", Type: "email", Content: "code {{.Code}}", IsSystem: true} dbConn.Create(&t1) adminUser := &model.User{ID: 1001, Username: "admin", IsAdmin: true} router := setupTestRouter(adminUser) t.Run("get existing", func(t *testing.T) { req, _ := http.NewRequest("GET", "/api/v1/admin/templates/login_email", nil) w := httptest.NewRecorder() router.ServeHTTP(w, req) if w.Code != http.StatusOK { t.Errorf("expected 200 OK, got %d", w.Code) } }) t.Run("get non-existent", func(t *testing.T) { req, _ := http.NewRequest("GET", "/api/v1/admin/templates/non_existent", nil) w := httptest.NewRecorder() router.ServeHTTP(w, req) if w.Code != http.StatusNotFound { t.Errorf("expected 404 Not Found, got %d", w.Code) } }) } func TestUpdateTemplate(t *testing.T) { dbConn, _, cleanup := testhelper.SetupTestEnvironment(t) defer cleanup() t1 := model.Template{Key: "login_email", Name: "Login Code", Type: "email", Content: "code {{.Code}}", IsSystem: true} dbConn.Create(&t1) adminUser := &model.User{ID: 1001, Username: "admin", IsAdmin: true} router := setupTestRouter(adminUser) t.Run("update successfully", func(t *testing.T) { payload := UpdateTemplateRequest{ Name: "Updated Login Code", Type: "email", Subject: "New Subject", Content: "new code {{.Code}}", Description: "new desc", } body, _ := json.Marshal(payload) req, _ := http.NewRequest("PUT", "/api/v1/admin/templates/login_email", bytes.NewBuffer(body)) req.Header.Set("Content-Type", "application/json") w := httptest.NewRecorder() router.ServeHTTP(w, req) if w.Code != http.StatusOK { t.Fatalf("expected 200 OK, got %d. Body: %s", w.Code, w.Body.String()) } var tmpl model.Template dbConn.Where("key = ?", "login_email").First(&tmpl) if tmpl.Name != "Updated Login Code" || tmpl.Subject != "New Subject" { t.Errorf("database values not updated: %+v", tmpl) } }) } func TestDeleteTemplate(t *testing.T) { dbConn, _, cleanup := testhelper.SetupTestEnvironment(t) defer cleanup() t1 := model.Template{Key: "login_email", Name: "Login Code", Type: "email", Content: "code {{.Code}}", IsSystem: true} t2 := model.Template{Key: "custom_tmpl", Name: "Custom", Type: "email", Content: "hi", IsSystem: false} dbConn.Create(&t1) dbConn.Create(&t2) adminUser := &model.User{ID: 1001, Username: "admin", IsAdmin: true} router := setupTestRouter(adminUser) t.Run("delete system template should fail", func(t *testing.T) { req, _ := http.NewRequest("DELETE", "/api/v1/admin/templates/login_email", nil) w := httptest.NewRecorder() router.ServeHTTP(w, req) if w.Code != http.StatusBadRequest { t.Errorf("expected 400 Bad Request when deleting system template, got %d", w.Code) } }) t.Run("delete custom template should succeed", func(t *testing.T) { req, _ := http.NewRequest("DELETE", "/api/v1/admin/templates/custom_tmpl", nil) w := httptest.NewRecorder() router.ServeHTTP(w, req) if w.Code != http.StatusOK { t.Fatalf("expected 200 OK, got %d", w.Code) } var count int64 dbConn.Model(&model.Template{}).Where("key = ?", "custom_tmpl").Count(&count) if count != 0 { t.Error("custom template was not deleted from DB") } }) }