Files
affiliate_dash/backend/internal/service/rock_support_test.go
T

188 lines
7.0 KiB
Go

package service
import (
"context"
"fmt"
"strings"
"testing"
"time"
"affiliate_dash/internal/model"
)
func createSupportTestOrder(t *testing.T, svc *RockService, merchantID uint, orderNo, clientOrderNo string) {
t.Helper()
now := time.Now()
order := model.FulfillmentOrder{
MerchantID: merchantID,
OrderNo: orderNo,
ClientOrderNo: clientOrderNo,
ProductSKU: model.RockSKUBlackTshirt,
ProductName: "黑色ROCK长袖T恤",
OrderStatus: model.OrderStatusPaid,
OrderSource: "manual",
Currency: "POINT",
CreatedAt: now,
UpdatedAt: now,
}
if err := svc.db.Create(&order).Error; err != nil {
t.Fatalf("创建订单失败: %v", err)
}
}
func createSupportTestToken(t *testing.T, svc *RockService, upstreamID, redeemCode, orderNo, status, roleName, roleID string) model.RockToken {
t.Helper()
now := time.Now()
token := model.RockToken{
UpstreamTokenID: upstreamID,
Token: "jwt-" + upstreamID,
RedeemCode: redeemCode,
Color: model.RockColorBlack,
Status: status,
AssignedOrderNo: orderNo,
ExpectedUID: roleID,
ActualRoleName: roleName,
ActualRoleID: roleID,
ActualChannel: "安卓-微信",
BoundAt: &now,
}
if err := svc.db.Create(&token).Error; err != nil {
t.Fatalf("创建 Token 失败: %v", err)
}
return token
}
func TestSupportBoundTokens_RequiresSearchAndFiltersBound(t *testing.T) {
db := newRockTestDB(t)
svc := NewRockService(db, NewRockClient("https://example.com"))
ctx := context.Background()
var selfMerchant model.Merchant
if err := db.Where("code = ?", model.MerchantCodeSelfOperated).First(&selfMerchant).Error; err != nil {
t.Fatalf("读取自营商户失败: %v", err)
}
otherMerchant := model.Merchant{Code: "support-test-other", Name: "测试商户", Status: model.MerchantStatusActive}
if err := db.Create(&otherMerchant).Error; err != nil {
t.Fatalf("创建测试商户失败: %v", err)
}
createSupportTestOrder(t, svc, selfMerchant.ID, "SUP-ORDER-1", "SUP-CLIENT-1")
createSupportTestOrder(t, svc, otherMerchant.ID, "SUP-ORDER-2", "SUP-CLIENT-2")
selfToken1 := createSupportTestToken(t, svc, "SUP-TOKEN-1", "SUP-CODE-1", "SUP-ORDER-1", model.RockStatusBound, "客服测试角色", "10001")
selfToken2 := createSupportTestToken(t, svc, "SUP-TOKEN-2", "SUP-CODE-2", "SUP-ORDER-1", model.RockStatusBound, "客服第二个角色", "10002")
otherToken := createSupportTestToken(t, svc, "SUP-TOKEN-3", "SUP-CODE-3", "SUP-ORDER-2", model.RockStatusBound, "其他商户角色", "20001")
usingToken := createSupportTestToken(t, svc, "SUP-TOKEN-4", "SUP-CODE-4", "SUP-ORDER-1", model.RockStatusUsing, "使用中角色", "10003")
// 不传搜索条件时必须拒绝,避免一次拉取全量数据。
if _, _, err := svc.ListSupportBoundTokens(ctx, 1, 20, " "); err == nil || !strings.Contains(err.Error(), "请输入") {
t.Fatalf("空搜索条件必须拒绝,实际: %v", err)
}
// 输入搜索条件后返回全部命中,且不混入 using。
allTokens, allTotal, err := svc.ListSupportBoundTokens(ctx, 1, 20, "SUP-")
if err != nil {
t.Fatalf("查询失败: %v", err)
}
if allTotal != 3 || len(allTokens) != 3 {
t.Fatalf("应返回 3 条绑定中 Token,实际 total=%d len=%d", allTotal, len(allTokens))
}
for _, token := range allTokens {
if token.Status != model.RockStatusBound {
t.Fatalf("查询混入非 bound 状态: %+v", token)
}
if token.TokenCode == "" {
t.Fatalf("TokenCode 未填充: %+v", token)
}
if token.ID == usingToken.ID {
t.Fatalf("using Token 不应出现在查询结果中: %+v", token)
}
}
// 按商户单号查询:同一订单下的两条 Token 都应命中。
byClientOrder, clientTotal, err := svc.ListSupportBoundTokens(ctx, 1, 20, "SUP-CLIENT-1")
if err != nil {
t.Fatalf("按商户单号查询失败: %v", err)
}
if clientTotal != 2 || len(byClientOrder) != 2 {
t.Fatalf("按商户单号应查到 2 条,实际 total=%d len=%d", clientTotal, len(byClientOrder))
}
for _, token := range byClientOrder {
if token.ID != selfToken1.ID && token.ID != selfToken2.ID {
t.Fatalf("按商户单号查询结果不符: %+v", token)
}
}
// 按上游编号查询:只命中其他商户那条。
byUpstream, upstreamTotal, err := svc.ListSupportBoundTokens(ctx, 1, 20, "SUP-TOKEN-3")
if err != nil {
t.Fatalf("按上游编号查询失败: %v", err)
}
if upstreamTotal != 1 || len(byUpstream) != 1 || byUpstream[0].ID != otherToken.ID {
t.Fatalf("按上游编号查询结果不符: total=%d tokens=%+v", upstreamTotal, byUpstream)
}
// 按 Token ID 查询。
byID, idTotal, err := svc.ListSupportBoundTokens(ctx, 1, 20, fmt.Sprint(otherToken.ID))
if err != nil {
t.Fatalf("按 Token ID 查询失败: %v", err)
}
if idTotal != 1 || len(byID) != 1 || byID[0].ID != otherToken.ID {
t.Fatalf("按 Token ID 查询结果不符: total=%d tokens=%+v", idTotal, byID)
}
// 使用中的 Token 不能被客服查询到。
usingResult, usingTotal, err := svc.ListSupportBoundTokens(ctx, 1, 20, "SUP-TOKEN-4")
if err != nil {
t.Fatalf("查询 using Token 失败: %v", err)
}
if usingTotal != 0 || len(usingResult) != 0 {
t.Fatalf("using Token 不应被查询到,实际 total=%d", usingTotal)
}
}
func TestSupportTokenStatusLogs_RequiresBound(t *testing.T) {
db := newRockTestDB(t)
svc := NewRockService(db, NewRockClient("https://example.com"))
ctx := context.Background()
var selfMerchant model.Merchant
if err := db.Where("code = ?", model.MerchantCodeSelfOperated).First(&selfMerchant).Error; err != nil {
t.Fatalf("读取自营商户失败: %v", err)
}
createSupportTestOrder(t, svc, selfMerchant.ID, "SUP-LOG-ORDER-1", "SUP-LOG-CLIENT-1")
boundToken := createSupportTestToken(t, svc, "SUP-LOG-TOKEN-1", "SUP-LOG-CODE-1", "SUP-LOG-ORDER-1", model.RockStatusBound, "自营客服角色", "30001")
usingToken := createSupportTestToken(t, svc, "SUP-LOG-TOKEN-2", "SUP-LOG-CODE-2", "SUP-LOG-ORDER-1", model.RockStatusUsing, "使用中角色", "30002")
for _, token := range []model.RockToken{boundToken, usingToken} {
entry := model.RockTokenStatusLog{
TokenID: token.ID,
UpstreamTokenID: token.UpstreamTokenID,
EventType: rockTokenEventStatusChanged,
FromStatus: model.RockStatusUsing,
ToStatus: token.Status,
Source: rockTokenSourceBindCheck,
Reason: "测试状态变更",
}
if err := db.Create(&entry).Error; err != nil {
t.Fatalf("创建状态日志失败: %v", err)
}
}
logs, err := svc.ListSupportTokenStatusLogs(ctx, boundToken.ID)
if err != nil {
t.Fatalf("读取绑定中 Token 历史失败: %v", err)
}
if len(logs) != 1 {
t.Fatalf("绑定中 Token 应读到 1 条历史,实际 %d", len(logs))
}
if _, err := svc.ListSupportTokenStatusLogs(ctx, usingToken.ID); err == nil || !strings.Contains(err.Error(), "不存在") {
t.Fatalf("非绑定中 Token 必须拒绝,实际: %v", err)
}
if _, err := svc.ListSupportTokenStatusLogs(ctx, 999999); err == nil || !strings.Contains(err.Error(), "不存在") {
t.Fatalf("不存在的 Token 必须拒绝,实际: %v", err)
}
}