Files
gochat/internal/service/dashboard_app_service_test.go_BAK
T
2026-06-04 15:44:48 +08:00

660 lines
20 KiB
Plaintext

package service
import (
"context"
"encoding/json"
"testing"
"github.com/gochat/gochat/internal/model"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
// ========== Create ==========
func TestDashboardAppService_Create_成功(t *testing.T) {
db, _, svc := setupDashboardAppService(t)
account := createTestAccount(t, db)
req := &CreateDashboardAppRequest{
Title: "销售仪表盘",
Description: "销售数据看板",
Kind: "frame",
Content: json.RawMessage(`[{"type":"frame","url":"https://example.com/widget"}]`),
}
app, err := svc.Create(context.Background(), account.ID, nil, req)
require.NoError(t, err)
assert.NotZero(t, app.ID)
assert.Equal(t, account.ID, app.AccountID)
assert.Nil(t, app.UserID)
assert.Equal(t, "销售仪表盘", app.Title)
assert.Equal(t, "销售数据看板", app.Description)
assert.Equal(t, "frame", app.Kind)
assert.True(t, *app.Active)
assert.Equal(t, json.RawMessage(`[{"type":"frame","url":"https://example.com/widget"}]`), app.Content)
}
func TestDashboardAppService_Create_带UserID(t *testing.T) {
db, _, svc := setupDashboardAppService(t)
account := createTestAccount(t, db)
user := createTestUser(t, db, account.ID)
userID := user.ID
req := &CreateDashboardAppRequest{
Title: "个人仪表盘",
Content: json.RawMessage(`[{"type":"frame","url":"https://example.com/personal"}]`),
}
app, err := svc.Create(context.Background(), account.ID, &userID, req)
require.NoError(t, err)
assert.NotZero(t, app.ID)
assert.Equal(t, account.ID, app.AccountID)
assert.NotNil(t, app.UserID)
assert.Equal(t, user.ID, *app.UserID)
}
func TestDashboardAppService_Create_空Content(t *testing.T) {
db, _, svc := setupDashboardAppService(t)
account := createTestAccount(t, db)
req := &CreateDashboardAppRequest{
Title: "空内容仪表盘",
Content: json.RawMessage(""),
}
app, err := svc.Create(context.Background(), account.ID, nil, req)
require.NoError(t, err)
assert.NotZero(t, app.ID)
assert.Equal(t, "空内容仪表盘", app.Title)
// Content should default to []
assert.Equal(t, json.RawMessage("[]"), app.Content)
}
func TestDashboardAppService_Create_设置ActiveFalse(t *testing.T) {
db, _, svc := setupDashboardAppService(t)
account := createTestAccount(t, db)
active := false
req := &CreateDashboardAppRequest{
Title: "禁用仪表盘",
Active: &active,
}
app, err := svc.Create(context.Background(), account.ID, nil, req)
require.NoError(t, err)
assert.False(t, *app.Active)
}
func TestDashboardAppService_Create_无效Content(t *testing.T) {
db, _, svc := setupDashboardAppService(t)
account := createTestAccount(t, db)
req := &CreateDashboardAppRequest{
Title: "无效内容",
Content: json.RawMessage(`{"not_array":true}`),
}
app, err := svc.Create(context.Background(), account.ID, nil, req)
assert.Error(t, err)
assert.Nil(t, app)
// Error should mention "must be a JSON array"
assert.Contains(t, err.Error(), "JSON array")
}
func TestDashboardAppService_Create_无效WidgetURL(t *testing.T) {
db, _, svc := setupDashboardAppService(t)
account := createTestAccount(t, db)
req := &CreateDashboardAppRequest{
Title: "无效URL",
Content: json.RawMessage(`[{"type":"frame","url":"ftp://bad.com"}]`),
}
app, err := svc.Create(context.Background(), account.ID, nil, req)
assert.Error(t, err)
assert.Nil(t, app)
assert.Contains(t, err.Error(), "http/https")
}
func TestDashboardAppService_Create_无效WidgetType(t *testing.T) {
db, _, svc := setupDashboardAppService(t)
account := createTestAccount(t, db)
req := &CreateDashboardAppRequest{
Title: "无效类型",
Content: json.RawMessage(`[{"type":"link","url":"https://example.com"}]`),
}
app, err := svc.Create(context.Background(), account.ID, nil, req)
assert.Error(t, err)
assert.Nil(t, app)
assert.Contains(t, err.Error(), "must be 'frame'")
}
// ========== GetByID ==========
func TestDashboardAppService_GetByID_成功(t *testing.T) {
db, _, svc := setupDashboardAppService(t)
account := createTestAccount(t, db)
created := createTestDashboardApp(t, db, account.ID)
app, err := svc.GetByID(context.Background(), created.ID)
require.NoError(t, err)
assert.Equal(t, created.ID, app.ID)
assert.Equal(t, created.Title, app.Title)
assert.Equal(t, created.AccountID, app.AccountID)
assert.Equal(t, "frame", app.Kind)
assert.True(t, *app.Active)
}
func TestDashboardAppService_GetByID_不存在(t *testing.T) {
_, _, svc := setupDashboardAppService(t)
app, err := svc.GetByID(context.Background(), 99999)
assert.Error(t, err)
assert.Nil(t, app)
}
// ========== Update ==========
func TestDashboardAppService_Update_修改Title(t *testing.T) {
db, _, svc := setupDashboardAppService(t)
account := createTestAccount(t, db)
created := createTestDashboardApp(t, db, account.ID)
req := &UpdateDashboardAppRequest{
Title: "更新后的标题",
Content: json.RawMessage(""),
}
app, err := svc.Update(context.Background(), created.ID, req)
require.NoError(t, err)
assert.Equal(t, "更新后的标题", app.Title)
// Content unchanged because req.Content is ""
assert.Equal(t, created.Content, app.Content)
}
func TestDashboardAppService_Update_修改Content(t *testing.T) {
db, _, svc := setupDashboardAppService(t)
account := createTestAccount(t, db)
created := createTestDashboardApp(t, db, account.ID)
req := &UpdateDashboardAppRequest{
Title: "",
Content: json.RawMessage(`[{"type":"frame","url":"https://example.com/kpi"}]`),
}
app, err := svc.Update(context.Background(), created.ID, req)
require.NoError(t, err)
// Title unchanged because req.Title is ""
assert.Equal(t, created.Title, app.Title)
assert.Equal(t, json.RawMessage(`[{"type":"frame","url":"https://example.com/kpi"}]`), app.Content)
}
func TestDashboardAppService_Update_修改Description和Active(t *testing.T) {
db, _, svc := setupDashboardAppService(t)
account := createTestAccount(t, db)
created := createTestDashboardApp(t, db, account.ID)
active := false
req := &UpdateDashboardAppRequest{
Description: "新描述",
Active: &active,
}
app, err := svc.Update(context.Background(), created.ID, req)
require.NoError(t, err)
assert.Equal(t, "新描述", app.Description)
assert.False(t, *app.Active)
// Title unchanged
assert.Equal(t, created.Title, app.Title)
}
func TestDashboardAppService_Update_不存在(t *testing.T) {
_, _, svc := setupDashboardAppService(t)
req := &UpdateDashboardAppRequest{
Title: "不存在",
Content: json.RawMessage(""),
}
app, err := svc.Update(context.Background(), 99999, req)
assert.Error(t, err)
assert.Nil(t, app)
}
// ========== Patch (partial update via Update) ==========
func TestDashboardAppService_Patch_只修改Active(t *testing.T) {
db, _, svc := setupDashboardAppService(t)
account := createTestAccount(t, db)
created := createTestDashboardApp(t, db, account.ID)
active := false
req := &UpdateDashboardAppRequest{
Active: &active,
}
app, err := svc.Update(context.Background(), created.ID, req)
require.NoError(t, err)
assert.False(t, *app.Active)
// All other fields unchanged
assert.Equal(t, created.Title, app.Title)
assert.Equal(t, created.Content, app.Content)
assert.Equal(t, created.Description, app.Description)
}
// ========== Delete ==========
func TestDashboardAppService_Delete_成功(t *testing.T) {
db, _, svc := setupDashboardAppService(t)
account := createTestAccount(t, db)
created := createTestDashboardApp(t, db, account.ID)
err := svc.Delete(context.Background(), created.ID)
require.NoError(t, err)
// Verify deletion — GetByID should fail
app, err := svc.GetByID(context.Background(), created.ID)
assert.Error(t, err)
assert.Nil(t, app)
}
func TestDashboardAppService_Delete_不存在(t *testing.T) {
_, _, svc := setupDashboardAppService(t)
// Deleting non-existent ID should not error (gorm soft-delete with non-existent ID is OK)
err := svc.Delete(context.Background(), 99999)
assert.NoError(t, err)
}
// ========== ListByAccount ==========
func TestDashboardAppService_ListByAccount_成功(t *testing.T) {
db, _, svc := setupDashboardAppService(t)
account := createTestAccount(t, db)
// Create 3 dashboard apps for the same account
for i := 0; i < 3; i++ {
createTestDashboardApp(t, db, account.ID, func(app *model.DashboardApp) {
app.Title = "仪表盘-" + string(rune('A'+i))
})
}
apps, count, err := svc.ListByAccount(context.Background(), account.ID, 1, 10)
require.NoError(t, err)
assert.Equal(t, int64(3), count)
assert.Len(t, apps, 3)
}
func TestDashboardAppService_ListByAccount_分页(t *testing.T) {
db, _, svc := setupDashboardAppService(t)
account := createTestAccount(t, db)
// Create 5 dashboard apps
for i := 0; i < 5; i++ {
createTestDashboardApp(t, db, account.ID)
}
// Page 1: perPage=2 → 2 items, total count=5
apps, count, err := svc.ListByAccount(context.Background(), account.ID, 1, 2)
require.NoError(t, err)
assert.Equal(t, int64(5), count)
assert.Len(t, apps, 2)
// Page 2: perPage=2 → 2 items
apps, count, err = svc.ListByAccount(context.Background(), account.ID, 2, 2)
require.NoError(t, err)
assert.Equal(t, int64(5), count)
assert.Len(t, apps, 2)
// Page 3: perPage=2 → 1 item
apps, count, err = svc.ListByAccount(context.Background(), account.ID, 3, 2)
require.NoError(t, err)
assert.Equal(t, int64(5), count)
assert.Len(t, apps, 1)
}
func TestDashboardAppService_ListByAccount_空列表(t *testing.T) {
db, _, svc := setupDashboardAppService(t)
account := createTestAccount(t, db)
apps, count, err := svc.ListByAccount(context.Background(), account.ID, 1, 10)
require.NoError(t, err)
assert.Equal(t, int64(0), count)
assert.Len(t, apps, 0)
}
func TestDashboardAppService_ListByAccount_不同账户隔离(t *testing.T) {
db, _, svc := setupDashboardAppService(t)
account1 := createTestAccount(t, db)
account2 := createTestAccount(t, db)
createTestDashboardApp(t, db, account1.ID)
createTestDashboardApp(t, db, account1.ID)
createTestDashboardApp(t, db, account2.ID)
// account1 should have 2
apps, count, err := svc.ListByAccount(context.Background(), account1.ID, 1, 10)
require.NoError(t, err)
assert.Equal(t, int64(2), count)
assert.Len(t, apps, 2)
// account2 should have 1
apps, count, err = svc.ListByAccount(context.Background(), account2.ID, 1, 10)
require.NoError(t, err)
assert.Equal(t, int64(1), count)
assert.Len(t, apps, 1)
}
// ========== ListActiveByAccount ==========
func TestDashboardAppService_ListActiveByAccount_只返回Active(t *testing.T) {
db, _, svc := setupDashboardAppService(t)
account := createTestAccount(t, db)
// Create 2 active apps
createTestDashboardApp(t, db, account.ID)
createTestDashboardApp(t, db, account.ID)
// Create 1 inactive app
createTestDashboardApp(t, db, account.ID, func(app *model.DashboardApp) {
app.Active = model.BoolPtr(false)
})
apps, count, err := svc.ListActiveByAccount(context.Background(), account.ID, 1, 10)
require.NoError(t, err)
assert.Equal(t, int64(2), count)
assert.Len(t, apps, 2)
for _, a := range apps {
assert.True(t, *a.Active)
}
}
// ========== Search ==========
func TestDashboardAppService_Search_按标题搜索(t *testing.T) {
db, _, svc := setupDashboardAppService(t)
account := createTestAccount(t, db)
// Create apps with different titles
createTestDashboardApp(t, db, account.ID, func(app *model.DashboardApp) {
app.Title = "销售仪表盘"
})
createTestDashboardApp(t, db, account.ID, func(app *model.DashboardApp) {
app.Title = "客服监控面板"
})
createTestDashboardApp(t, db, account.ID, func(app *model.DashboardApp) {
app.Title = "运营数据看板"
})
apps, count, err := svc.Search(context.Background(), account.ID, "仪表盘", 1, 10)
require.NoError(t, err)
assert.Equal(t, int64(1), count)
assert.Len(t, apps, 1)
assert.Equal(t, "销售仪表盘", apps[0].Title)
}
func TestDashboardAppService_Search_空结果(t *testing.T) {
db, _, svc := setupDashboardAppService(t)
account := createTestAccount(t, db)
createTestDashboardApp(t, db, account.ID, func(app *model.DashboardApp) {
app.Title = "客服监控"
})
apps, count, err := svc.Search(context.Background(), account.ID, "不存在", 1, 10)
require.NoError(t, err)
assert.Equal(t, int64(0), count)
assert.Len(t, apps, 0)
}
func TestDashboardAppService_Search_账户隔离(t *testing.T) {
db, _, svc := setupDashboardAppService(t)
account1 := createTestAccount(t, db)
account2 := createTestAccount(t, db)
createTestDashboardApp(t, db, account1.ID, func(app *model.DashboardApp) {
app.Title = "仪表盘A"
})
createTestDashboardApp(t, db, account2.ID, func(app *model.DashboardApp) {
app.Title = "仪表盘B"
})
// Search in account1 should only find account1's apps
apps, count, err := svc.Search(context.Background(), account1.ID, "仪表盘", 1, 10)
require.NoError(t, err)
assert.Equal(t, int64(1), count)
assert.Len(t, apps, 1)
assert.Equal(t, "仪表盘A", apps[0].Title)
}
// ========== Widget Management ==========
func TestDashboardAppService_AddWidget_成功(t *testing.T) {
db, _, svc := setupDashboardAppService(t)
account := createTestAccount(t, db)
created := createTestDashboardApp(t, db, account.ID)
req := &AddWidgetRequest{
Type: "frame",
URL: "https://example.com/new-widget",
Name: "新Widget",
}
app, err := svc.AddWidget(context.Background(), created.ID, req)
require.NoError(t, err)
var widgets []model.DashboardWidget
err = json.Unmarshal(app.Content, &widgets)
require.NoError(t, err)
// Should have original widget + new widget
assert.Len(t, widgets, 2)
lastWidget := widgets[len(widgets)-1]
assert.Equal(t, "frame", lastWidget.Type)
assert.Equal(t, "https://example.com/new-widget", lastWidget.URL)
assert.Equal(t, "新Widget", lastWidget.Name)
}
func TestDashboardAppService_AddWidget_到空Content(t *testing.T) {
db, _, svc := setupDashboardAppService(t)
account := createTestAccount(t, db)
// Create app with empty content
created := createTestDashboardApp(t, db, account.ID, func(app *model.DashboardApp) {
app.Content = json.RawMessage(`[]`)
})
req := &AddWidgetRequest{
Type: "frame",
URL: "https://example.com/widget",
}
app, err := svc.AddWidget(context.Background(), created.ID, req)
require.NoError(t, err)
var widgets []model.DashboardWidget
err = json.Unmarshal(app.Content, &widgets)
require.NoError(t, err)
assert.Len(t, widgets, 1)
assert.Equal(t, "https://example.com/widget", widgets[0].URL)
}
func TestDashboardAppService_AddWidget_无效Type(t *testing.T) {
db, _, svc := setupDashboardAppService(t)
account := createTestAccount(t, db)
created := createTestDashboardApp(t, db, account.ID)
req := &AddWidgetRequest{
Type: "link",
URL: "https://example.com/widget",
}
app, err := svc.AddWidget(context.Background(), created.ID, req)
assert.Error(t, err)
assert.Nil(t, app)
assert.Contains(t, err.Error(), "must be 'frame'")
}
func TestDashboardAppService_AddWidget_无效URL(t *testing.T) {
db, _, svc := setupDashboardAppService(t)
account := createTestAccount(t, db)
created := createTestDashboardApp(t, db, account.ID)
req := &AddWidgetRequest{
Type: "frame",
URL: "ftp://bad.com",
}
app, err := svc.AddWidget(context.Background(), created.ID, req)
assert.Error(t, err)
assert.Nil(t, app)
assert.Contains(t, err.Error(), "http/https")
}
func TestDashboardAppService_GetWidgets_成功(t *testing.T) {
db, _, svc := setupDashboardAppService(t)
account := createTestAccount(t, db)
created := createTestDashboardApp(t, db, account.ID)
widgets, err := svc.GetWidgets(context.Background(), created.ID)
require.NoError(t, err)
assert.Len(t, widgets, 1)
assert.Equal(t, "frame", widgets[0].Type)
}
func TestDashboardAppService_GetWidgets_空Content(t *testing.T) {
db, _, svc := setupDashboardAppService(t)
account := createTestAccount(t, db)
created := createTestDashboardApp(t, db, account.ID, func(app *model.DashboardApp) {
app.Content = json.RawMessage(`[]`)
})
widgets, err := svc.GetWidgets(context.Background(), created.ID)
require.NoError(t, err)
assert.Len(t, widgets, 0)
}
func TestDashboardAppService_GetWidgets_不存在(t *testing.T) {
_, _, svc := setupDashboardAppService(t)
widgets, err := svc.GetWidgets(context.Background(), 99999)
assert.Error(t, err)
assert.Nil(t, widgets)
}
func TestDashboardAppService_UpdateWidget_成功(t *testing.T) {
db, _, svc := setupDashboardAppService(t)
account := createTestAccount(t, db)
// Create app with 2 widgets
created := createTestDashboardApp(t, db, account.ID, func(app *model.DashboardApp) {
app.Content = json.RawMessage(`[{"type":"frame","url":"https://a.com"},{"type":"frame","url":"https://b.com"}]`)
})
req := &UpdateWidgetRequest{
URL: "https://updated.com",
Name: "更新名称",
}
app, err := svc.UpdateWidget(context.Background(), created.ID, 0, req)
require.NoError(t, err)
var widgets []model.DashboardWidget
err = json.Unmarshal(app.Content, &widgets)
require.NoError(t, err)
assert.Len(t, widgets, 2)
assert.Equal(t, "https://updated.com", widgets[0].URL)
assert.Equal(t, "更新名称", widgets[0].Name)
// Second widget unchanged
assert.Equal(t, "https://b.com", widgets[1].URL)
}
func TestDashboardAppService_UpdateWidget_越界(t *testing.T) {
db, _, svc := setupDashboardAppService(t)
account := createTestAccount(t, db)
created := createTestDashboardApp(t, db, account.ID)
req := &UpdateWidgetRequest{
URL: "https://updated.com",
}
app, err := svc.UpdateWidget(context.Background(), created.ID, 5, req)
assert.Error(t, err)
assert.Nil(t, app)
assert.Contains(t, err.Error(), "out of range")
}
func TestDashboardAppService_RemoveWidget_成功(t *testing.T) {
db, _, svc := setupDashboardAppService(t)
account := createTestAccount(t, db)
// Create app with 2 widgets
created := createTestDashboardApp(t, db, account.ID, func(app *model.DashboardApp) {
app.Content = json.RawMessage(`[{"type":"frame","url":"https://a.com"},{"type":"frame","url":"https://b.com"}]`)
})
app, err := svc.RemoveWidget(context.Background(), created.ID, 0)
require.NoError(t, err)
var widgets []model.DashboardWidget
err = json.Unmarshal(app.Content, &widgets)
require.NoError(t, err)
assert.Len(t, widgets, 1)
// Only second widget remains
assert.Equal(t, "https://b.com", widgets[0].URL)
}
func TestDashboardAppService_RemoveWidget_越界(t *testing.T) {
db, _, svc := setupDashboardAppService(t)
account := createTestAccount(t, db)
created := createTestDashboardApp(t, db, account.ID)
app, err := svc.RemoveWidget(context.Background(), created.ID, 5)
assert.Error(t, err)
assert.Nil(t, app)
assert.Contains(t, err.Error(), "out of range")
}
func TestDashboardAppService_RemoveWidget_最后一个(t *testing.T) {
db, _, svc := setupDashboardAppService(t)
account := createTestAccount(t, db)
created := createTestDashboardApp(t, db, account.ID)
app, err := svc.RemoveWidget(context.Background(), created.ID, 0)
require.NoError(t, err)
var widgets []model.DashboardWidget
err = json.Unmarshal(app.Content, &widgets)
require.NoError(t, err)
assert.Len(t, widgets, 0)
}
// ========== ValidateContent (model-level) ==========
func TestDashboardAppService_Create_验证ValidContent(t *testing.T) {
db, _, svc := setupDashboardAppService(t)
account := createTestAccount(t, db)
// Valid: array of frame widgets with http URLs
req := &CreateDashboardAppRequest{
Title: "验证通过",
Content: json.RawMessage(`[{"type":"frame","url":"https://example.com"},{"type":"frame","url":"http://other.com"}]`),
}
app, err := svc.Create(context.Background(), account.ID, nil, req)
require.NoError(t, err)
assert.NotZero(t, app.ID)
}
func TestDashboardAppService_Create_验证空数组(t *testing.T) {
db, _, svc := setupDashboardAppService(t)
account := createTestAccount(t, db)
req := &CreateDashboardAppRequest{
Title: "空数组",
Content: json.RawMessage(`[]`),
}
app, err := svc.Create(context.Background(), account.ID, nil, req)
require.NoError(t, err)
assert.NotZero(t, app.ID)
}