372 lines
12 KiB
Plaintext
372 lines
12 KiB
Plaintext
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))
|
|
} |