sync: 同步 v1.0.4 并脱敏配置与镜像
This commit is contained in:
@@ -0,0 +1,220 @@
|
||||
package simwarning
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/gogf/gf/v2/os/gcfg"
|
||||
)
|
||||
|
||||
func TestMonthlyExpiryRunPagesAndPartialFailures(t *testing.T) {
|
||||
firstPage := make([]monthlyExpiryCompany, monthlyExpiryPageSize)
|
||||
for i := range firstPage {
|
||||
firstPage[i] = monthlyExpiryCompany{TenantID: "000000", DeptID: int64(i + 1), LeaderID: int64(i + 1001)}
|
||||
}
|
||||
calls := make(map[string]int)
|
||||
pages := make([]int64, 0, 2)
|
||||
runner := monthlyExpiryRunner{
|
||||
companies: func(_ context.Context, lastID int64) ([]monthlyExpiryCompany, error) {
|
||||
pages = append(pages, lastID)
|
||||
if lastID == 0 {
|
||||
return firstPage, nil
|
||||
}
|
||||
if lastID == int64(monthlyExpiryPageSize) {
|
||||
return []monthlyExpiryCompany{{TenantID: "000000", DeptID: 101, LeaderID: 1101}}, nil
|
||||
}
|
||||
return nil, fmt.Errorf("unexpected cursor %d", lastID)
|
||||
},
|
||||
summaries: func(_ context.Context, company monthlyExpiryCompany, begin, end time.Time) ([]monthlyExpirySummary, error) {
|
||||
if begin.Format("2006-01-02") != "2026-12-01" || end.Format("2006-01-02") != "2027-02-01" {
|
||||
t.Errorf("wrong query window %s..%s", begin, end)
|
||||
}
|
||||
if company.DeptID == 2 {
|
||||
return nil, errors.New("count failed")
|
||||
}
|
||||
return []monthlyExpirySummary{{Month: "2026-12", Count: 3, FirstDate: "2026-12-01", LastDate: "2026-12-31"},
|
||||
{Month: "2027-01", Count: 6, FirstDate: "2027-01-31", LastDate: "2027-01-31"}}, nil
|
||||
},
|
||||
leader: func(_ context.Context, company monthlyExpiryCompany) (*monthlyExpiryLeader, error) {
|
||||
if company.DeptID == 3 {
|
||||
return nil, nil
|
||||
}
|
||||
return &monthlyExpiryLeader{UserID: company.LeaderID, Phone: "13800000000"}, nil
|
||||
},
|
||||
deliver: func(_ context.Context, notice monthlyExpiryNotice, channel string) (bool, error) {
|
||||
calls[notice.Summary.Month+"/"+channel]++
|
||||
if notice.Company.DeptID == 1 && channel == "sms" {
|
||||
return false, errors.New("sms failed")
|
||||
}
|
||||
if notice.Company.DeptID == 1 && notice.Summary.Month == "2027-01" && channel == "platform" {
|
||||
return false, nil
|
||||
}
|
||||
if notice.Company.DeptID == 4 && notice.Summary.Month == "2026-12" && channel == "platform" {
|
||||
return true, errors.New("dedupe mark failed after delivery")
|
||||
}
|
||||
return true, nil
|
||||
},
|
||||
}
|
||||
stats, err := runner.run(context.Background(), time.Date(2026, 12, 15, 9, 0, 0, 0, monthlyExpiryLocation))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if stats.Companies != 101 || stats.Groups != 198 || stats.Sent != 393 || stats.Duplicate != 1 || stats.Errors != 5 {
|
||||
t.Fatalf("unexpected stats: %+v", stats)
|
||||
}
|
||||
if len(pages) != 2 || pages[0] != 0 || pages[1] != 100 {
|
||||
t.Fatalf("wrong page cursors: %v", pages)
|
||||
}
|
||||
if calls["2026-12/platform"] != 99 || calls["2027-01/platform"] != 99 {
|
||||
t.Fatalf("wrong delivered group counts: %+v", calls)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMonthlyExpiryNoticeContentAndRecipient(t *testing.T) {
|
||||
base := monthlyExpiryNotice{
|
||||
Company: monthlyExpiryCompany{TenantID: "000000", DeptID: 117, LeaderID: 12},
|
||||
Leader: monthlyExpiryLeader{UserID: 12},
|
||||
RunTime: time.Date(2026, 10, 15, 9, 0, 0, 0, monthlyExpiryLocation),
|
||||
Summary: monthlyExpirySummary{Month: "2026-10", Count: 7, FirstDate: "2026-10-21", LastDate: "2026-10-31"},
|
||||
}
|
||||
current := base.content()
|
||||
for _, text := range []string{"【7】张", "2026-10-21至2026-10-31", "本月25日前", "本月底卡片将执行停机"} {
|
||||
if !strings.Contains(current, text) {
|
||||
t.Errorf("current notice missing %q: %s", text, current)
|
||||
}
|
||||
}
|
||||
param := monthlyExpiryMessageParam(base)
|
||||
if *param.MessageType != 1 || *param.SendUserType != 1 || *param.MessageTitle != "到期提醒" || !strings.Contains(param.UserIds.String(), "12") {
|
||||
t.Fatalf("wrong targeted system message: %+v", param)
|
||||
}
|
||||
base.Summary = monthlyExpirySummary{Month: "2026-11", Count: 9, FirstDate: "2026-11-30", LastDate: "2026-11-30"}
|
||||
if got := base.content(); !strings.Contains(got, "次月25日前") || strings.Contains(got, "本月底卡片") {
|
||||
t.Fatalf("next month notice incorrect: %s", got)
|
||||
}
|
||||
if base.expiryDateText() != "2026-11-30" {
|
||||
t.Errorf("single-day range not collapsed: %s", base.expiryDateText())
|
||||
}
|
||||
if key := monthlyExpirySentKey(base, "sms"); !strings.Contains(key, ":000000:117:2026-11:sms") {
|
||||
t.Errorf("missing dedupe dimensions: %s", key)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMonthlyExpirySMSParamsRequireConfirmedTemplate(t *testing.T) {
|
||||
for _, invalid := range []string{"", "invalid", "{}", `{"count":7}`} {
|
||||
if _, err := renderMonthlyExpirySMSParams(invalid, 3, "2026-11-30"); err == nil {
|
||||
t.Errorf("unconfirmed/invalid template accepted: %q", invalid)
|
||||
}
|
||||
}
|
||||
content, err := renderMonthlyExpirySMSParams(`{"actualCount":"{count}","actualExpire":"{expireDate}"}`, 5, "2026-11-01至2026-11-30")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
var params map[string]string
|
||||
if err := json.Unmarshal([]byte(content), ¶ms); err != nil || params["actualCount"] != "5" || params["actualExpire"] != "2026-11-01至2026-11-30" {
|
||||
t.Fatalf("incorrect template params: %s, %v", content, err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMonthlyExpirySMSLocalGateway(t *testing.T) {
|
||||
// 所有请求只发到本机测试服务;清除环境覆盖,绝不读取真实网关配置。
|
||||
for _, name := range []string{"IOTLCM_SMS_GATEWAY_ENABLED", "IOTLCM_SMS_GATEWAY_URL", "IOTLCM_SMS_GATEWAY_GRPC_ADDR", "IOTLCM_SMS_GATEWAY_TIMEOUT", "IOTLCM_SMS_GATEWAY_ROUTER_ID"} {
|
||||
t.Setenv(name, "")
|
||||
}
|
||||
requests := make([]map[string]map[string]string, 0, 3)
|
||||
fail := false
|
||||
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
var body map[string]map[string]string
|
||||
if err := json.NewDecoder(r.Body).Decode(&body); err != nil {
|
||||
t.Errorf("decode request: %v", err)
|
||||
}
|
||||
requests = append(requests, body)
|
||||
if fail {
|
||||
_, _ = w.Write([]byte(`{"code":502,"message":"supplier failure"}`))
|
||||
return
|
||||
}
|
||||
_, _ = w.Write([]byte(`{"code":0,"message":"OK"}`))
|
||||
}))
|
||||
defer server.Close()
|
||||
adapter, err := gcfg.NewAdapterContent()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
original := g.Cfg().GetAdapter()
|
||||
g.Cfg().SetAdapter(adapter)
|
||||
t.Cleanup(func() { g.Cfg().SetAdapter(original) })
|
||||
configure := func(enabled bool, template string) {
|
||||
t.Helper()
|
||||
data, err := json.Marshal(g.Map{
|
||||
"sms": g.Map{"gateway": g.Map{"enabled": enabled, "url": server.URL, "timeout": "1s"}},
|
||||
"warning": g.Map{"expire": g.Map{"monthly": g.Map{
|
||||
"currentMonthParamsTemplate": template, "nextMonthParamsTemplate": template,
|
||||
}}},
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := adapter.SetContent(string(data)); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
configure(true, `{"card_num":"{count}","expire_date":"{expireDate}"}`)
|
||||
notice := monthlyExpiryNotice{
|
||||
RunTime: time.Date(2026, 10, 15, 9, 0, 0, 0, monthlyExpiryLocation),
|
||||
Leader: monthlyExpiryLeader{UserID: 12, Phone: "13800000000"},
|
||||
Summary: monthlyExpirySummary{Month: "2026-10", Count: 7, FirstDate: "2026-10-31", LastDate: "2026-10-31"},
|
||||
}
|
||||
if err := sendMonthlyExpirySMS(context.Background(), notice); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
notice.Summary = monthlyExpirySummary{Month: "2026-11", Count: 9, FirstDate: "2026-11-01", LastDate: "2026-11-30"}
|
||||
if err := sendMonthlyExpirySMS(context.Background(), notice); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if len(requests) != 2 || requests[0]["config"]["templateId"] != "SMS_512596191" || requests[1]["config"]["templateId"] != "SMS_512646156" {
|
||||
t.Fatalf("incorrect gateway templates: %+v", requests)
|
||||
}
|
||||
for _, request := range requests {
|
||||
if request["config"]["signName"] != "示例企业" {
|
||||
t.Fatalf("incorrect gateway sign: %+v", request)
|
||||
}
|
||||
}
|
||||
var params map[string]string
|
||||
if err := json.Unmarshal([]byte(requests[1]["params"]["msgContent"]), ¶ms); err != nil || params["card_num"] != "9" || params["expire_date"] != "2026-11-01至2026-11-30" {
|
||||
t.Fatalf("incorrect gateway params: %+v, %v", params, err)
|
||||
}
|
||||
fail = true
|
||||
if err := sendMonthlyExpirySMS(context.Background(), notice); err == nil {
|
||||
t.Fatal("gateway business failure must propagate")
|
||||
}
|
||||
configure(true, "")
|
||||
if err := sendMonthlyExpirySMS(context.Background(), notice); err == nil || len(requests) != 3 {
|
||||
t.Fatal("unconfirmed template must block before calling gateway")
|
||||
}
|
||||
configure(false, `{"card_num":"{count}","expire_date":"{expireDate}"}`)
|
||||
if err := sendMonthlyExpirySMS(context.Background(), notice); err == nil || len(requests) != 3 {
|
||||
t.Fatal("disabled gateway must not count as successful delivery")
|
||||
}
|
||||
}
|
||||
|
||||
func TestMonthlyExpiryCancellationStopsBeforeDelivery(t *testing.T) {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
cancel()
|
||||
runner := monthlyExpiryRunner{
|
||||
companies: func(context.Context, int64) ([]monthlyExpiryCompany, error) {
|
||||
t.Fatal("canceled run should not query")
|
||||
return nil, nil
|
||||
},
|
||||
}
|
||||
_, err := runner.run(ctx, time.Now())
|
||||
if !errors.Is(err, context.Canceled) {
|
||||
t.Fatalf("expected cancellation, got %v", err)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user