231 lines
6.8 KiB
Go
231 lines
6.8 KiB
Go
package distribution
|
|
|
|
import (
|
|
"context"
|
|
"testing"
|
|
|
|
"hfb_sys/backend/internal/model"
|
|
smsnode "hfb_sys/backend/internal/modules/sms"
|
|
|
|
"go.uber.org/zap"
|
|
)
|
|
|
|
func TestParseSiteSMSConfigNormalizesBusinessTemplates(t *testing.T) {
|
|
cfg, err := ParseSiteSMSConfig(`{
|
|
"enabled": true,
|
|
"sign_name": "分站签名",
|
|
"business_templates": {
|
|
"order_handoff_assist": " SMS_512540945 ",
|
|
"listing_review_rejected": " ",
|
|
"unknown_node": "SMS_BAD",
|
|
"": "SMS_EMPTY"
|
|
}
|
|
}`)
|
|
if err != nil {
|
|
t.Fatalf("ParseSiteSMSConfig() error = %v", err)
|
|
}
|
|
if len(cfg.BusinessTemplates) != 1 {
|
|
t.Fatalf("business_templates = %+v, want 仅保留已登记且非空的模板", cfg.BusinessTemplates)
|
|
}
|
|
if got := cfg.BusinessTemplates[smsnode.NodeOrderHandoffAssist]; got != "SMS_512540945" {
|
|
t.Fatalf("order_handoff_assist = %q, want SMS_512540945", got)
|
|
}
|
|
if _, ok := cfg.BusinessTemplates["unknown_node"]; ok {
|
|
t.Fatal("未登记节点不应保留")
|
|
}
|
|
}
|
|
|
|
func TestUpdateSiteSMSConfigPersistsBusinessTemplates(t *testing.T) {
|
|
db := distributionTestDB(t)
|
|
repo := NewRepository(db, nil)
|
|
ctx := context.Background()
|
|
|
|
site := model.DistributionSite{
|
|
SiteCode: "site_sms_node",
|
|
SiteName: "节点短信站",
|
|
Domain: "smsnode.com",
|
|
Status: "active",
|
|
}
|
|
if err := db.Create(&site).Error; err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
templates := map[string]string{
|
|
smsnode.NodeOrderHandoffAssist: "SMS_512540945",
|
|
smsnode.NodeListingReviewRejected: "SMS_512520993",
|
|
"unknown_node": "SMS_BAD",
|
|
}
|
|
updated, err := repo.UpdateSiteSMSConfig(ctx, 0, site.ID, UpdateSiteSMSConfigRequest{
|
|
BusinessTemplates: &templates,
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("UpdateSiteSMSConfig() error = %v", err)
|
|
}
|
|
if len(updated.BusinessTemplates) != 2 {
|
|
t.Fatalf("business_templates = %+v, want 2 项(过滤未登记节点)", updated.BusinessTemplates)
|
|
}
|
|
|
|
// 未传 BusinessTemplates 时不能被清空。
|
|
newSign := "分站签名"
|
|
if _, err := repo.UpdateSiteSMSConfig(ctx, 0, site.ID, UpdateSiteSMSConfigRequest{SignName: &newSign}); err != nil {
|
|
t.Fatalf("UpdateSiteSMSConfig() error = %v", err)
|
|
}
|
|
reloaded, err := repo.GetSiteSMSConfig(ctx, 0, site.ID)
|
|
if err != nil {
|
|
t.Fatalf("GetSiteSMSConfig() error = %v", err)
|
|
}
|
|
if reloaded.BusinessTemplates[smsnode.NodeOrderHandoffAssist] != "SMS_512540945" {
|
|
t.Fatalf("未传该字段时模板应保留,实际 = %+v", reloaded.BusinessTemplates)
|
|
}
|
|
|
|
// 传空对象表示清空,节点回退主站模板。
|
|
empty := map[string]string{}
|
|
cleared, err := repo.UpdateSiteSMSConfig(ctx, 0, site.ID, UpdateSiteSMSConfigRequest{BusinessTemplates: &empty})
|
|
if err != nil {
|
|
t.Fatalf("UpdateSiteSMSConfig() error = %v", err)
|
|
}
|
|
if len(cleared.BusinessTemplates) != 0 {
|
|
t.Fatalf("business_templates 应被清空, 实际 = %+v", cleared.BusinessTemplates)
|
|
}
|
|
}
|
|
|
|
// 后台改了站点短信凭据后,长期运行的任务必须能自动重建 Provider,
|
|
// 否则更换 AccessKey 要重启进程才会生效。
|
|
func TestResolveBusinessSenderRebuildsProviderAfterCredentialChange(t *testing.T) {
|
|
db := distributionTestDB(t)
|
|
repo := NewRepository(db, nil)
|
|
repo.SetAllowMockSMS(true)
|
|
ctx := context.Background()
|
|
|
|
site := model.DistributionSite{
|
|
SiteCode: "site_sms_rotate",
|
|
SiteName: "凭据轮换站",
|
|
Domain: "smsrotate.com",
|
|
Status: "active",
|
|
}
|
|
if err := db.Create(&site).Error; err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
enabled := true
|
|
provider := "mock"
|
|
sign := "【分站签名】"
|
|
oldKey := "LTAI_OLD_KEY"
|
|
if _, err := repo.UpdateSiteSMSConfig(ctx, 0, site.ID, UpdateSiteSMSConfigRequest{
|
|
Enabled: &enabled,
|
|
Provider: &provider,
|
|
SignName: &sign,
|
|
AccessKeyID: &oldKey,
|
|
}); err != nil {
|
|
t.Fatalf("UpdateSiteSMSConfig() error = %v", err)
|
|
}
|
|
|
|
resolver := NewSMSResolver(repo, zap.NewNop())
|
|
first, _, _, err := resolver.ResolveBusinessSender(ctx, site.ID)
|
|
if err != nil {
|
|
t.Fatalf("ResolveBusinessSender() error = %v", err)
|
|
}
|
|
if first == nil {
|
|
t.Fatal("站点已启用独立短信时应返回通道")
|
|
}
|
|
|
|
// 凭据未变化时应命中缓存,返回同一实例。
|
|
again, _, _, err := resolver.ResolveBusinessSender(ctx, site.ID)
|
|
if err != nil {
|
|
t.Fatalf("ResolveBusinessSender() error = %v", err)
|
|
}
|
|
if again != first {
|
|
t.Fatal("凭据未变化时应复用缓存 Provider")
|
|
}
|
|
|
|
newKey := "LTAI_NEW_KEY"
|
|
if _, err := repo.UpdateSiteSMSConfig(ctx, 0, site.ID, UpdateSiteSMSConfigRequest{
|
|
AccessKeyID: &newKey,
|
|
}); err != nil {
|
|
t.Fatalf("UpdateSiteSMSConfig() error = %v", err)
|
|
}
|
|
|
|
rebuilt, _, _, err := resolver.ResolveBusinessSender(ctx, site.ID)
|
|
if err != nil {
|
|
t.Fatalf("ResolveBusinessSender() error = %v", err)
|
|
}
|
|
if rebuilt == nil {
|
|
t.Fatal("凭据轮换后仍应返回可用通道")
|
|
}
|
|
if rebuilt == first {
|
|
t.Fatal("凭据轮换后应重建 Provider,而不是继续复用旧缓存")
|
|
}
|
|
}
|
|
|
|
func TestResolveBusinessSenderReturnsSiteTemplates(t *testing.T) {
|
|
db := distributionTestDB(t)
|
|
repo := NewRepository(db, nil)
|
|
repo.SetAllowMockSMS(true)
|
|
ctx := context.Background()
|
|
|
|
site := model.DistributionSite{
|
|
SiteCode: "site_sms_sender",
|
|
SiteName: "节点短信通道站",
|
|
Domain: "smssender.com",
|
|
Status: "active",
|
|
}
|
|
if err := db.Create(&site).Error; err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
enabled := true
|
|
provider := "mock"
|
|
sign := "【分站签名】"
|
|
templates := map[string]string{smsnode.NodeOrderHandoffAssist: "SMS_SITE"}
|
|
if _, err := repo.UpdateSiteSMSConfig(ctx, 0, site.ID, UpdateSiteSMSConfigRequest{
|
|
Enabled: &enabled,
|
|
Provider: &provider,
|
|
SignName: &sign,
|
|
BusinessTemplates: &templates,
|
|
}); err != nil {
|
|
t.Fatalf("UpdateSiteSMSConfig() error = %v", err)
|
|
}
|
|
|
|
resolver := NewSMSResolver(repo, zap.NewNop())
|
|
sender, signName, siteTemplates, err := resolver.ResolveBusinessSender(ctx, site.ID)
|
|
if err != nil {
|
|
t.Fatalf("ResolveBusinessSender() error = %v", err)
|
|
}
|
|
if sender == nil {
|
|
t.Fatal("站点已启用独立短信时应返回通道")
|
|
}
|
|
if signName != sign {
|
|
t.Fatalf("signName = %q, want %q", signName, sign)
|
|
}
|
|
if siteTemplates[smsnode.NodeOrderHandoffAssist] != "SMS_SITE" {
|
|
t.Fatalf("站点模板 = %+v", siteTemplates)
|
|
}
|
|
|
|
// 站点未启用独立短信时返回 nil,调用方回退主站通道。
|
|
other := model.DistributionSite{
|
|
SiteCode: "site_sms_off",
|
|
SiteName: "未启用短信站",
|
|
Domain: "smsoff.com",
|
|
Status: "active",
|
|
}
|
|
if err := db.Create(&other).Error; err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
disabledSender, _, _, err := resolver.ResolveBusinessSender(ctx, other.ID)
|
|
if err != nil {
|
|
t.Fatalf("ResolveBusinessSender() error = %v", err)
|
|
}
|
|
if disabledSender != nil {
|
|
t.Fatal("站点未启用时应返回 nil 以便回退主站")
|
|
}
|
|
|
|
// 站点不存在(或非 active)时同样返回 nil,不报错。
|
|
missingSender, _, _, err := resolver.ResolveBusinessSender(ctx, 999999)
|
|
if err != nil {
|
|
t.Fatalf("ResolveBusinessSender() error = %v", err)
|
|
}
|
|
if missingSender != nil {
|
|
t.Fatal("站点不存在时应返回 nil")
|
|
}
|
|
}
|