second commit
This commit is contained in:
@@ -0,0 +1,372 @@
|
||||
package v1
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"strconv"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/suite"
|
||||
"gorm.io/driver/sqlite"
|
||||
"gorm.io/gorm"
|
||||
"gorm.io/gorm/logger"
|
||||
|
||||
"github.com/gochat/gochat/internal/model"
|
||||
"github.com/gochat/gochat/internal/repository"
|
||||
"github.com/gochat/gochat/internal/service"
|
||||
ws "github.com/gochat/gochat/internal/ws"
|
||||
)
|
||||
|
||||
// noopTypingIndicator is a stub that satisfies service.TypingIndicator
|
||||
type noopTypingIndicatorTheme struct{}
|
||||
|
||||
func (n *noopTypingIndicatorTheme) SetTypingOn(_ context.Context, _ uint, _ uint, _ *ws.Performer) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (n *noopTypingIndicatorTheme) SetTypingOff(_ context.Context, _ uint, _ uint, _ *ws.Performer) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
type WebWidgetThemeHandlerTestSuite struct {
|
||||
suite.Suite
|
||||
router *gin.Engine
|
||||
themeHandler *WebWidgetThemeHandler
|
||||
preChatHandler *WebWidgetPreChatHandler
|
||||
db *gorm.DB
|
||||
widgetSvc *service.WidgetService
|
||||
inboxSvc *service.InboxService
|
||||
}
|
||||
|
||||
func (s *WebWidgetThemeHandlerTestSuite) SetupSuite() {
|
||||
gin.SetMode(gin.TestMode)
|
||||
|
||||
db, err := gorm.Open(sqlite.Open(":memory:"), &gorm.Config{
|
||||
Logger: logger.Default.LogMode(logger.Silent),
|
||||
})
|
||||
s.Require().NoError(err)
|
||||
s.db = db
|
||||
|
||||
err = db.AutoMigrate(
|
||||
&model.Account{},
|
||||
&model.User{},
|
||||
&model.Inbox{},
|
||||
&model.Contact{},
|
||||
&model.ContactInbox{},
|
||||
&model.Conversation{},
|
||||
&model.Message{},
|
||||
&model.WidgetThemeConfig{},
|
||||
&model.PreChatForm{},
|
||||
)
|
||||
s.Require().NoError(err)
|
||||
|
||||
// Create real repos
|
||||
inboxRepo := repository.NewInboxRepo(db)
|
||||
contactRepo := repository.NewContactRepo(db)
|
||||
contactInboxRepo := repository.NewContactInboxRepo(db)
|
||||
conversationRepo := repository.NewConversationRepo(db)
|
||||
messageRepo := repository.NewMessageRepo(db)
|
||||
themeConfigRepo := repository.NewWidgetThemeConfigRepo(db)
|
||||
preChatFormRepo := repository.NewPreChatFormRepo(db)
|
||||
fileUploadRepo := repository.NewWidgetFileUploadRepo(db)
|
||||
offlineMessageRepo := repository.NewWidgetOfflineMessageRepo(db)
|
||||
|
||||
widgetSvc := service.NewWidgetService(
|
||||
inboxRepo,
|
||||
contactRepo,
|
||||
contactInboxRepo,
|
||||
conversationRepo,
|
||||
messageRepo,
|
||||
&noopTypingIndicatorTheme{},
|
||||
themeConfigRepo,
|
||||
preChatFormRepo,
|
||||
fileUploadRepo,
|
||||
offlineMessageRepo,
|
||||
)
|
||||
inboxSvc := service.NewInboxService(inboxRepo)
|
||||
|
||||
s.widgetSvc = widgetSvc
|
||||
s.inboxSvc = inboxSvc
|
||||
s.themeHandler = NewWebWidgetThemeHandler(widgetSvc, inboxSvc)
|
||||
s.preChatHandler = NewWebWidgetPreChatHandler(widgetSvc, inboxSvc)
|
||||
|
||||
// Setup router for theme handler
|
||||
s.router = gin.New()
|
||||
api := s.router.Group("/api/v1/accounts/:id")
|
||||
api.GET("/inboxes/:inbox_id/web_widget/theme_config", s.themeHandler.GetThemeConfig)
|
||||
api.PUT("/inboxes/:inbox_id/web_widget/theme_config", s.themeHandler.UpdateThemeConfig)
|
||||
api.DELETE("/inboxes/:inbox_id/web_widget/theme_config", s.themeHandler.DeleteThemeConfig)
|
||||
api.GET("/inboxes/:inbox_id/web_widget/pre_chat_form", s.preChatHandler.GetPreChatForm)
|
||||
api.PUT("/inboxes/:inbox_id/web_widget/pre_chat_form", s.preChatHandler.UpdatePreChatForm)
|
||||
api.DELETE("/inboxes/:inbox_id/web_widget/pre_chat_form", s.preChatHandler.DeletePreChatForm)
|
||||
}
|
||||
|
||||
func (s *WebWidgetThemeHandlerTestSuite) TearDownSuite() {
|
||||
sqlDB, _ := s.db.DB()
|
||||
sqlDB.Close()
|
||||
}
|
||||
|
||||
func (s *WebWidgetThemeHandlerTestSuite) seedAccountAndInbox() (uint, uint) {
|
||||
acc := &model.Account{Name: "ThemeTestOrg"}
|
||||
s.Require().NoError(s.db.Create(acc).Error)
|
||||
|
||||
inbox := &model.Inbox{
|
||||
AccountID: acc.ID,
|
||||
Name: "Widget Inbox",
|
||||
ChannelType: "web_widget",
|
||||
Enabled: true,
|
||||
}
|
||||
s.Require().NoError(s.db.Create(inbox).Error)
|
||||
return acc.ID, inbox.ID
|
||||
}
|
||||
|
||||
func (s *WebWidgetThemeHandlerTestSuite) seedNonWidgetInbox(accountID uint) uint {
|
||||
inbox := &model.Inbox{
|
||||
AccountID: accountID,
|
||||
Name: "Telegram Inbox",
|
||||
ChannelType: "telegram",
|
||||
Enabled: true,
|
||||
}
|
||||
s.Require().NoError(s.db.Create(inbox).Error)
|
||||
return inbox.ID
|
||||
}
|
||||
|
||||
func accountURL(accountID, inboxID uint, suffix string) string {
|
||||
return "/api/v1/accounts/" + strconv.FormatUint(uint64(accountID), 10) +
|
||||
"/inboxes/" + strconv.FormatUint(uint64(inboxID), 10) +
|
||||
"/web_widget/" + suffix
|
||||
}
|
||||
|
||||
// --- Theme Config Tests ---
|
||||
|
||||
func (s *WebWidgetThemeHandlerTestSuite) TestGetThemeConfig_InvalidInboxID() {
|
||||
accountID, _ := s.seedAccountAndInbox()
|
||||
w := httptest.NewRecorder()
|
||||
url := "/api/v1/accounts/" + strconv.FormatUint(uint64(accountID), 10) + "/inboxes/abc/web_widget/theme_config"
|
||||
req, _ := http.NewRequest("GET", url, nil)
|
||||
s.router.ServeHTTP(w, req)
|
||||
|
||||
assert.Equal(s.T(), http.StatusBadRequest, w.Code)
|
||||
}
|
||||
|
||||
func (s *WebWidgetThemeHandlerTestSuite) TestGetThemeConfig_InvalidAccountID() {
|
||||
w := httptest.NewRecorder()
|
||||
req, _ := http.NewRequest("GET", "/api/v1/accounts/abc/inboxes/1/web_widget/theme_config", nil)
|
||||
s.router.ServeHTTP(w, req)
|
||||
|
||||
assert.Equal(s.T(), http.StatusBadRequest, w.Code)
|
||||
}
|
||||
|
||||
func (s *WebWidgetThemeHandlerTestSuite) TestGetThemeConfig_InboxNotFound() {
|
||||
accountID, _ := s.seedAccountAndInbox()
|
||||
w := httptest.NewRecorder()
|
||||
url := accountURL(accountID, 9999, "theme_config")
|
||||
req, _ := http.NewRequest("GET", url, nil)
|
||||
s.router.ServeHTTP(w, req)
|
||||
|
||||
assert.Equal(s.T(), http.StatusNotFound, w.Code)
|
||||
}
|
||||
|
||||
func (s *WebWidgetThemeHandlerTestSuite) TestGetThemeConfig_NonWidgetChannel() {
|
||||
accountID, _ := s.seedAccountAndInbox()
|
||||
telegramInboxID := s.seedNonWidgetInbox(accountID)
|
||||
w := httptest.NewRecorder()
|
||||
url := accountURL(accountID, telegramInboxID, "theme_config")
|
||||
req, _ := http.NewRequest("GET", url, nil)
|
||||
s.router.ServeHTTP(w, req)
|
||||
|
||||
assert.Equal(s.T(), http.StatusBadRequest, w.Code)
|
||||
}
|
||||
|
||||
func (s *WebWidgetThemeHandlerTestSuite) TestGetThemeConfig_Success_NoConfig() {
|
||||
accountID, inboxID := s.seedAccountAndInbox()
|
||||
w := httptest.NewRecorder()
|
||||
url := accountURL(accountID, inboxID, "theme_config")
|
||||
req, _ := http.NewRequest("GET", url, nil)
|
||||
s.router.ServeHTTP(w, req)
|
||||
|
||||
assert.Equal(s.T(), http.StatusOK, w.Code)
|
||||
}
|
||||
|
||||
func (s *WebWidgetThemeHandlerTestSuite) TestGetThemeConfig_Success_WithConfig() {
|
||||
accountID, inboxID := s.seedAccountAndInbox()
|
||||
|
||||
// Seed a theme config directly in DB
|
||||
theme := &model.WidgetThemeConfig{
|
||||
InboxID: inboxID,
|
||||
PrimaryColor: "#ff0000",
|
||||
BackgroundColor: "#ffffff",
|
||||
}
|
||||
s.Require().NoError(s.db.Create(theme).Error)
|
||||
|
||||
w := httptest.NewRecorder()
|
||||
url := accountURL(accountID, inboxID, "theme_config")
|
||||
req, _ := http.NewRequest("GET", url, nil)
|
||||
s.router.ServeHTTP(w, req)
|
||||
|
||||
assert.Equal(s.T(), http.StatusOK, w.Code)
|
||||
var resp map[string]interface{}
|
||||
json.Unmarshal(w.Body.Bytes(), &resp)
|
||||
assert.NotNil(s.T(), resp["theme_config"])
|
||||
}
|
||||
|
||||
func (s *WebWidgetThemeHandlerTestSuite) TestUpdateThemeConfig_InvalidInboxID() {
|
||||
accountID, _ := s.seedAccountAndInbox()
|
||||
w := httptest.NewRecorder()
|
||||
url := "/api/v1/accounts/" + strconv.FormatUint(uint64(accountID), 10) + "/inboxes/abc/web_widget/theme_config"
|
||||
body := `{"primary_color": "#00ff00"}`
|
||||
req, _ := http.NewRequest("PUT", url, strings.NewReader(body))
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
s.router.ServeHTTP(w, req)
|
||||
|
||||
assert.Equal(s.T(), http.StatusBadRequest, w.Code)
|
||||
}
|
||||
|
||||
func (s *WebWidgetThemeHandlerTestSuite) TestUpdateThemeConfig_InboxNotFound() {
|
||||
accountID, _ := s.seedAccountAndInbox()
|
||||
w := httptest.NewRecorder()
|
||||
url := accountURL(accountID, 9999, "theme_config")
|
||||
body := `{"primary_color": "#00ff00"}`
|
||||
req, _ := http.NewRequest("PUT", url, strings.NewReader(body))
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
s.router.ServeHTTP(w, req)
|
||||
|
||||
assert.Equal(s.T(), http.StatusNotFound, w.Code)
|
||||
}
|
||||
|
||||
func (s *WebWidgetThemeHandlerTestSuite) TestUpdateThemeConfig_Success() {
|
||||
accountID, inboxID := s.seedAccountAndInbox()
|
||||
w := httptest.NewRecorder()
|
||||
url := accountURL(accountID, inboxID, "theme_config")
|
||||
body := `{"primary_color": "#00ff00", "background_color": "#eeeeee"}`
|
||||
req, _ := http.NewRequest("PUT", url, strings.NewReader(body))
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
s.router.ServeHTTP(w, req)
|
||||
|
||||
assert.Equal(s.T(), http.StatusOK, w.Code)
|
||||
}
|
||||
|
||||
func (s *WebWidgetThemeHandlerTestSuite) TestDeleteThemeConfig_InvalidInboxID() {
|
||||
accountID, _ := s.seedAccountAndInbox()
|
||||
w := httptest.NewRecorder()
|
||||
url := "/api/v1/accounts/" + strconv.FormatUint(uint64(accountID), 10) + "/inboxes/abc/web_widget/theme_config"
|
||||
req, _ := http.NewRequest("DELETE", url, nil)
|
||||
s.router.ServeHTTP(w, req)
|
||||
|
||||
assert.Equal(s.T(), http.StatusBadRequest, w.Code)
|
||||
}
|
||||
|
||||
func (s *WebWidgetThemeHandlerTestSuite) TestDeleteThemeConfig_Success() {
|
||||
accountID, inboxID := s.seedAccountAndInbox()
|
||||
|
||||
// Seed theme config first
|
||||
theme := &model.WidgetThemeConfig{
|
||||
InboxID: inboxID,
|
||||
PrimaryColor: "#ff0000",
|
||||
}
|
||||
s.Require().NoError(s.db.Create(theme).Error)
|
||||
|
||||
w := httptest.NewRecorder()
|
||||
url := accountURL(accountID, inboxID, "theme_config")
|
||||
req, _ := http.NewRequest("DELETE", url, nil)
|
||||
s.router.ServeHTTP(w, req)
|
||||
|
||||
assert.Equal(s.T(), http.StatusOK, w.Code)
|
||||
}
|
||||
|
||||
// --- Pre-Chat Form Tests ---
|
||||
|
||||
func (s *WebWidgetThemeHandlerTestSuite) TestGetPreChatForm_InvalidInboxID() {
|
||||
accountID, _ := s.seedAccountAndInbox()
|
||||
w := httptest.NewRecorder()
|
||||
url := "/api/v1/accounts/" + strconv.FormatUint(uint64(accountID), 10) + "/inboxes/abc/web_widget/pre_chat_form"
|
||||
req, _ := http.NewRequest("GET", url, nil)
|
||||
s.router.ServeHTTP(w, req)
|
||||
|
||||
assert.Equal(s.T(), http.StatusBadRequest, w.Code)
|
||||
}
|
||||
|
||||
func (s *WebWidgetThemeHandlerTestSuite) TestGetPreChatForm_InboxNotFound() {
|
||||
accountID, _ := s.seedAccountAndInbox()
|
||||
w := httptest.NewRecorder()
|
||||
url := accountURL(accountID, 9999, "pre_chat_form")
|
||||
req, _ := http.NewRequest("GET", url, nil)
|
||||
s.router.ServeHTTP(w, req)
|
||||
|
||||
assert.Equal(s.T(), http.StatusNotFound, w.Code)
|
||||
}
|
||||
|
||||
func (s *WebWidgetThemeHandlerTestSuite) TestGetPreChatForm_NonWidgetChannel() {
|
||||
accountID, _ := s.seedAccountAndInbox()
|
||||
telegramInboxID := s.seedNonWidgetInbox(accountID)
|
||||
w := httptest.NewRecorder()
|
||||
url := accountURL(accountID, telegramInboxID, "pre_chat_form")
|
||||
req, _ := http.NewRequest("GET", url, nil)
|
||||
s.router.ServeHTTP(w, req)
|
||||
|
||||
assert.Equal(s.T(), http.StatusBadRequest, w.Code)
|
||||
}
|
||||
|
||||
func (s *WebWidgetThemeHandlerTestSuite) TestGetPreChatForm_Success_NoConfig() {
|
||||
accountID, inboxID := s.seedAccountAndInbox()
|
||||
w := httptest.NewRecorder()
|
||||
url := accountURL(accountID, inboxID, "pre_chat_form")
|
||||
req, _ := http.NewRequest("GET", url, nil)
|
||||
s.router.ServeHTTP(w, req)
|
||||
|
||||
assert.Equal(s.T(), http.StatusOK, w.Code)
|
||||
}
|
||||
|
||||
func (s *WebWidgetThemeHandlerTestSuite) TestUpdatePreChatForm_Success() {
|
||||
accountID, inboxID := s.seedAccountAndInbox()
|
||||
w := httptest.NewRecorder()
|
||||
url := accountURL(accountID, inboxID, "pre_chat_form")
|
||||
body := `{"enabled": true, "message": "Please fill in", "require_name": true, "require_email": true}`
|
||||
req, _ := http.NewRequest("PUT", url, strings.NewReader(body))
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
s.router.ServeHTTP(w, req)
|
||||
|
||||
assert.Equal(s.T(), http.StatusOK, w.Code)
|
||||
}
|
||||
|
||||
func (s *WebWidgetThemeHandlerTestSuite) TestUpdatePreChatForm_InvalidInboxID() {
|
||||
accountID, _ := s.seedAccountAndInbox()
|
||||
w := httptest.NewRecorder()
|
||||
url := "/api/v1/accounts/" + strconv.FormatUint(uint64(accountID), 10) + "/inboxes/abc/web_widget/pre_chat_form"
|
||||
body := `{"enabled": true}`
|
||||
req, _ := http.NewRequest("PUT", url, strings.NewReader(body))
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
s.router.ServeHTTP(w, req)
|
||||
|
||||
assert.Equal(s.T(), http.StatusBadRequest, w.Code)
|
||||
}
|
||||
|
||||
func (s *WebWidgetThemeHandlerTestSuite) TestDeletePreChatForm_Success() {
|
||||
accountID, inboxID := s.seedAccountAndInbox()
|
||||
|
||||
// Seed pre-chat form first
|
||||
form := &model.PreChatForm{
|
||||
InboxID: inboxID,
|
||||
Enabled: true,
|
||||
Message: "Please fill in",
|
||||
RequireName: true,
|
||||
RequireEmail: true,
|
||||
}
|
||||
s.Require().NoError(s.db.Create(form).Error)
|
||||
|
||||
w := httptest.NewRecorder()
|
||||
url := accountURL(accountID, inboxID, "pre_chat_form")
|
||||
req, _ := http.NewRequest("DELETE", url, nil)
|
||||
s.router.ServeHTTP(w, req)
|
||||
|
||||
assert.Equal(s.T(), http.StatusOK, w.Code)
|
||||
}
|
||||
|
||||
func TestWebWidgetThemeHandlerTestSuite(t *testing.T) {
|
||||
suite.Run(t, new(WebWidgetThemeHandlerTestSuite))
|
||||
}
|
||||
Reference in New Issue
Block a user