96b1edff45
iotlcm-upstream-chan:运营商上游通道适配服务(统一 /sim/* 协议)。 - 内联原私有依赖:yunze-public → internal/pub/(10 包 18 文件), yunze-pb/pb → internal/pb/(9 文件);Consul 注册改用官方 gogf/gf/contrib/registry/consul/v2(API 兼容) - Go module:yz-svr → git.5iot.com/iotlcm-public/iotlcm-upstream-chan - 修复 mingqian 包测试:实现重构后 `adapter.operations` 字段已移除, 测试断言按当前实现语义重写(不改产品行为) - 凭据与内部标识脱敏:MySQL/Redis/Consul 凭据、私网与生产 IP、 内网域名、真实手机号、注释里的真实 token/transid - 移除内部资产:CI 触发器、生产通道种子 SQL、真实凭据测试资源文档 - Dockerfile 改用公共基础镜像,移除写死的 registry 凭据 - 补 Apache-2.0 LICENSE,重写 README,LF 行尾 + .gitattributes
295 lines
11 KiB
Go
295 lines
11 KiB
Go
package upstreamapi
|
|
|
|
import (
|
|
"context"
|
|
"testing"
|
|
"time"
|
|
|
|
"git.5iot.com/iotlcm-public/iotlcm-upstream-chan/internal/consts"
|
|
"git.5iot.com/iotlcm-public/iotlcm-upstream-chan/internal/model"
|
|
)
|
|
|
|
type historyUsageTestChannel struct {
|
|
model.SimCardChannelI
|
|
monthlyCalls int
|
|
dailyCalls int
|
|
monthlyBatchCalls int
|
|
dailyBatchCalls int
|
|
currentCalls int
|
|
}
|
|
|
|
func (c *historyUsageTestChannel) QuerySimDataUsageMonthly(_ context.Context, input *model.SimDataUsageMonthlyInput) (model.SimDataUsageMonthlyOutput, error) {
|
|
c.monthlyCalls++
|
|
output := make(model.SimDataUsageMonthlyOutput, 0, len(input.SimKeys))
|
|
for _, simKey := range input.SimKeys {
|
|
output = append(output, &model.SimBaseRes[model.SimDataUsageMonthlyOutputItem]{
|
|
SimKey: simKey,
|
|
Code: consts.ApiStatusSuccess,
|
|
})
|
|
}
|
|
return output, nil
|
|
}
|
|
|
|
func (c *historyUsageTestChannel) QuerySimDataUsageDaily(_ context.Context, input *model.SimDataUsageDailyInput) (model.SimDataUsageDailyOutput, error) {
|
|
c.dailyCalls++
|
|
output := make(model.SimDataUsageDailyOutput, 0, len(input.SimKeys))
|
|
for _, simKey := range input.SimKeys {
|
|
output = append(output, &model.SimBaseRes[model.SimDataUsageDailyOutputItem]{
|
|
SimKey: simKey,
|
|
Code: consts.ApiStatusSuccess,
|
|
})
|
|
}
|
|
return output, nil
|
|
}
|
|
|
|
func (c *historyUsageTestChannel) QuerySimDataUsageMonthlyBatch(_ context.Context, input *model.SimDataUsageMonthlyInput) (model.SimDataUsageMonthlyOutput, error) {
|
|
c.monthlyBatchCalls++
|
|
return c.QuerySimDataUsageMonthly(context.Background(), input)
|
|
}
|
|
|
|
func (c *historyUsageTestChannel) QuerySimDataUsageDailyBatch(_ context.Context, input *model.SimDataUsageDailyInput) (model.SimDataUsageDailyOutput, error) {
|
|
c.dailyBatchCalls++
|
|
return c.QuerySimDataUsageDaily(context.Background(), input)
|
|
}
|
|
|
|
func (c *historyUsageTestChannel) QuerySimCardStatusBatch(_ context.Context, _ *model.SimCardStatusInput) (model.SimCardStatusOutput, error) {
|
|
return nil, nil
|
|
}
|
|
|
|
func (c *historyUsageTestChannel) QuerySimDataUsage(_ context.Context, input *model.SimDataUsageInput) (model.SimDataUsageOutput, error) {
|
|
c.currentCalls++
|
|
output := make(model.SimDataUsageOutput, 0, len(input.SimKeys))
|
|
for _, simKey := range input.SimKeys {
|
|
output = append(output, &model.SimBaseRes[model.SimDataUsageItem]{
|
|
SimKey: simKey,
|
|
Code: consts.ApiStatusSuccess,
|
|
})
|
|
}
|
|
return output, nil
|
|
}
|
|
|
|
func TestValidateHistoricalPeriodAtUTC8Boundary(t *testing.T) {
|
|
now := time.Date(2026, time.September, 2, 16, 0, 0, 0, time.UTC)
|
|
|
|
monthCases := []struct {
|
|
name string
|
|
queryDate string
|
|
want string
|
|
}{
|
|
{name: "previous month", queryDate: "202608", want: ""},
|
|
{name: "current month", queryDate: "202609", want: historicalMonthRangeError},
|
|
{name: "future month", queryDate: "202610", want: historicalMonthRangeError},
|
|
{name: "empty month", queryDate: "", want: historicalMonthFormatError},
|
|
{name: "hyphenated month", queryDate: "2026-08", want: historicalMonthFormatError},
|
|
{name: "invalid month", queryDate: "202613", want: historicalMonthFormatError},
|
|
}
|
|
for _, tc := range monthCases {
|
|
t.Run("month "+tc.name, func(t *testing.T) {
|
|
if got := validateHistoricalMonth(tc.queryDate, now); got != tc.want {
|
|
t.Fatalf("validateHistoricalMonth(%q) = %q, want %q", tc.queryDate, got, tc.want)
|
|
}
|
|
})
|
|
}
|
|
|
|
batchMonthCases := []struct {
|
|
name string
|
|
queryDate string
|
|
want string
|
|
}{
|
|
{name: "previous month", queryDate: "202608", want: currentBatchMonthRangeError},
|
|
{name: "current month", queryDate: "202609", want: ""},
|
|
{name: "future month", queryDate: "202610", want: currentBatchMonthRangeError},
|
|
{name: "empty month", queryDate: "", want: historicalMonthFormatError},
|
|
{name: "invalid month", queryDate: "202613", want: historicalMonthFormatError},
|
|
}
|
|
for _, tc := range batchMonthCases {
|
|
t.Run("batch month "+tc.name, func(t *testing.T) {
|
|
if got := validateCurrentBatchMonth(tc.queryDate, now); got != tc.want {
|
|
t.Fatalf("validateCurrentBatchMonth(%q) = %q, want %q", tc.queryDate, got, tc.want)
|
|
}
|
|
})
|
|
}
|
|
|
|
dayCases := []struct {
|
|
name string
|
|
queryDate string
|
|
want string
|
|
}{
|
|
{name: "previous day", queryDate: "20260902", want: ""},
|
|
{name: "current day", queryDate: "20260903", want: historicalDailyRangeError},
|
|
{name: "future day", queryDate: "20260904", want: historicalDailyRangeError},
|
|
{name: "empty day", queryDate: "", want: historicalDailyFormatError},
|
|
{name: "hyphenated day", queryDate: "2026-09-02", want: historicalDailyFormatError},
|
|
{name: "invalid day", queryDate: "20260230", want: historicalDailyFormatError},
|
|
}
|
|
for _, tc := range dayCases {
|
|
t.Run("day "+tc.name, func(t *testing.T) {
|
|
if got := validateHistoricalDay(tc.queryDate, now); got != tc.want {
|
|
t.Fatalf("validateHistoricalDay(%q) = %q, want %q", tc.queryDate, got, tc.want)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestValidateHistoricalPeriodAcrossMonthEnd(t *testing.T) {
|
|
beforeMidnight := time.Date(2026, time.August, 31, 15, 59, 59, 0, time.UTC)
|
|
afterMidnight := time.Date(2026, time.August, 31, 16, 0, 0, 0, time.UTC)
|
|
|
|
if got := validateHistoricalMonth("202608", beforeMidnight); got != historicalMonthRangeError {
|
|
t.Fatalf("current month before UTC+8 month end = %q", got)
|
|
}
|
|
if got := validateHistoricalDay("20260831", beforeMidnight); got != historicalDailyRangeError {
|
|
t.Fatalf("current day before UTC+8 month end = %q", got)
|
|
}
|
|
if got := validateHistoricalMonth("202608", afterMidnight); got != "" {
|
|
t.Fatalf("completed month after UTC+8 month end = %q", got)
|
|
}
|
|
if got := validateHistoricalDay("20260831", afterMidnight); got != "" {
|
|
t.Fatalf("completed day after UTC+8 month end = %q", got)
|
|
}
|
|
if got := validateCurrentBatchMonth("202608", beforeMidnight); got != "" {
|
|
t.Fatalf("current batch month before UTC+8 month end = %q", got)
|
|
}
|
|
if got := validateCurrentBatchMonth("202608", afterMidnight); got != currentBatchMonthRangeError {
|
|
t.Fatalf("previous batch month after UTC+8 month end = %q", got)
|
|
}
|
|
if got := validateCurrentBatchMonth("202609", afterMidnight); got != "" {
|
|
t.Fatalf("current batch month after UTC+8 month end = %q", got)
|
|
}
|
|
}
|
|
|
|
func TestHistoricalPeriodValidationSkipsChannels(t *testing.T) {
|
|
ctx := context.Background()
|
|
api := NewUpstreamAPI()
|
|
channel := &historyUsageTestChannel{}
|
|
api.RegisterChan("test_channel", "v1", &model.SimCardChannelCache{Instance: channel})
|
|
simKeys := []*model.SimKey{
|
|
{ChanKey: "test_channel", ICCID: "iccid-1"},
|
|
{ChanKey: "test_channel", ICCID: "iccid-2"},
|
|
}
|
|
now := time.Now().In(historicalPeriodLocation)
|
|
|
|
monthly, err := api.QuerySimDataUsageMonthly(ctx, &model.SimDataUsageMonthlyInput{
|
|
SimKeys: simKeys,
|
|
ReqData: model.SimDataUsageMonthlyInputItem{QueryDate: now.Format("200601")},
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("QuerySimDataUsageMonthly returned error: %v", err)
|
|
}
|
|
assertMonthlyFailures(t, monthly, simKeys, historicalMonthRangeError)
|
|
|
|
daily, err := api.QuerySimDataUsageDaily(ctx, &model.SimDataUsageDailyInput{
|
|
SimKeys: simKeys,
|
|
ReqData: model.SimDataUsageDailyInputItem{QueryDate: now.Format("20060102")},
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("QuerySimDataUsageDaily returned error: %v", err)
|
|
}
|
|
assertDailyFailures(t, daily, simKeys, historicalDailyRangeError)
|
|
|
|
monthlyBatch, err := api.QuerySimDataUsageMonthlyBatch(ctx, &model.SimDataUsageMonthlyInput{
|
|
SimKeys: simKeys,
|
|
ReqData: model.SimDataUsageMonthlyInputItem{QueryDate: "999912"},
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("QuerySimDataUsageMonthlyBatch returned error: %v", err)
|
|
}
|
|
assertMonthlyFailures(t, monthlyBatch, simKeys, currentBatchMonthRangeError)
|
|
|
|
dailyBatch, err := api.QuerySimDataUsageDailyBatch(ctx, &model.SimDataUsageDailyInput{
|
|
SimKeys: simKeys,
|
|
ReqData: model.SimDataUsageDailyInputItem{QueryDate: "99991231"},
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("QuerySimDataUsageDailyBatch returned error: %v", err)
|
|
}
|
|
assertDailyFailures(t, dailyBatch, simKeys, historicalDailyRangeError)
|
|
|
|
invalidMonthly, err := api.QuerySimDataUsageMonthly(ctx, &model.SimDataUsageMonthlyInput{
|
|
SimKeys: simKeys,
|
|
ReqData: model.SimDataUsageMonthlyInputItem{QueryDate: "2026-08"},
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("invalid monthly query returned error: %v", err)
|
|
}
|
|
assertMonthlyFailures(t, invalidMonthly, simKeys, historicalMonthFormatError)
|
|
|
|
invalidDaily, err := api.QuerySimDataUsageDailyBatch(ctx, &model.SimDataUsageDailyInput{
|
|
SimKeys: simKeys,
|
|
ReqData: model.SimDataUsageDailyInputItem{QueryDate: ""},
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("invalid daily batch query returned error: %v", err)
|
|
}
|
|
assertDailyFailures(t, invalidDaily, simKeys, historicalDailyFormatError)
|
|
|
|
if channel.monthlyCalls != 0 || channel.dailyCalls != 0 || channel.monthlyBatchCalls != 0 || channel.dailyBatchCalls != 0 {
|
|
t.Fatalf("rejected periods called channel: %+v", channel)
|
|
}
|
|
}
|
|
|
|
func TestHistoricalPeriodsRouteAndCurrentUsageRemainsAvailable(t *testing.T) {
|
|
ctx := context.Background()
|
|
api := NewUpstreamAPI()
|
|
channel := &historyUsageTestChannel{}
|
|
api.RegisterChan("test_channel", "v1", &model.SimCardChannelCache{Instance: channel})
|
|
simKeys := []*model.SimKey{{ChanKey: "test_channel", ICCID: "iccid-1"}}
|
|
currentMonth := time.Now().In(historicalPeriodLocation).Format(historicalMonthLayout)
|
|
|
|
if _, err := api.QuerySimDataUsageMonthly(ctx, &model.SimDataUsageMonthlyInput{
|
|
SimKeys: simKeys,
|
|
ReqData: model.SimDataUsageMonthlyInputItem{QueryDate: "200001"},
|
|
}); err != nil {
|
|
t.Fatalf("historical monthly query returned error: %v", err)
|
|
}
|
|
if _, err := api.QuerySimDataUsageDaily(ctx, &model.SimDataUsageDailyInput{
|
|
SimKeys: simKeys,
|
|
ReqData: model.SimDataUsageDailyInputItem{QueryDate: "20000101"},
|
|
}); err != nil {
|
|
t.Fatalf("historical daily query returned error: %v", err)
|
|
}
|
|
if _, err := api.QuerySimDataUsageMonthlyBatch(ctx, &model.SimDataUsageMonthlyInput{
|
|
SimKeys: simKeys,
|
|
ReqData: model.SimDataUsageMonthlyInputItem{QueryDate: currentMonth},
|
|
}); err != nil {
|
|
t.Fatalf("current monthly batch query returned error: %v", err)
|
|
}
|
|
if _, err := api.QuerySimDataUsageDailyBatch(ctx, &model.SimDataUsageDailyInput{
|
|
SimKeys: simKeys,
|
|
ReqData: model.SimDataUsageDailyInputItem{QueryDate: "20000101"},
|
|
}); err != nil {
|
|
t.Fatalf("historical daily batch query returned error: %v", err)
|
|
}
|
|
if _, err := api.QuerySimDataUsage(ctx, &model.SimDataUsageInput{SimKeys: simKeys}); err != nil {
|
|
t.Fatalf("current usage query returned error: %v", err)
|
|
}
|
|
|
|
if channel.monthlyCalls != 2 || channel.dailyCalls != 2 || channel.monthlyBatchCalls != 1 || channel.dailyBatchCalls != 1 || channel.currentCalls != 1 {
|
|
t.Fatalf("unexpected channel calls: %+v", channel)
|
|
}
|
|
}
|
|
|
|
func assertMonthlyFailures(t *testing.T, output model.SimDataUsageMonthlyOutput, simKeys []*model.SimKey, message string) {
|
|
t.Helper()
|
|
if len(output) != len(simKeys) {
|
|
t.Fatalf("monthly output length = %d, want %d", len(output), len(simKeys))
|
|
}
|
|
for i, item := range output {
|
|
if item.Code != consts.ApiStatusError || item.Message != message || item.SimKey != simKeys[i] {
|
|
t.Fatalf("monthly output[%d] = %+v", i, item)
|
|
}
|
|
}
|
|
}
|
|
|
|
func assertDailyFailures(t *testing.T, output model.SimDataUsageDailyOutput, simKeys []*model.SimKey, message string) {
|
|
t.Helper()
|
|
if len(output) != len(simKeys) {
|
|
t.Fatalf("daily output length = %d, want %d", len(output), len(simKeys))
|
|
}
|
|
for i, item := range output {
|
|
if item.Code != consts.ApiStatusError || item.Message != message || item.SimKey != simKeys[i] {
|
|
t.Fatalf("daily output[%d] = %+v", i, item)
|
|
}
|
|
}
|
|
}
|