chore: 并入 iotlcm-upstream-chan 历史到 upstream-chan/(原提交 1e74497)
This commit is contained in:
@@ -0,0 +1,205 @@
|
||||
package upstreamimpl
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"sync/atomic"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"git.5iot.com/iotlcm-public/iotlcm-upstream-chan/internal/logic/upstreamimpl/dx"
|
||||
"git.5iot.com/iotlcm-public/iotlcm-upstream-chan/internal/model"
|
||||
)
|
||||
|
||||
func TestDXBatchDayAvailable(t *testing.T) {
|
||||
now := time.Date(2026, 9, 20, 12, 0, 0, 0, dxBatchLocation)
|
||||
tests := map[string]bool{
|
||||
"20260913": true,
|
||||
"20260919": true,
|
||||
"20260912": false,
|
||||
"20260920": false,
|
||||
"20260921": false,
|
||||
}
|
||||
for queryDate, want := range tests {
|
||||
if got := dxBatchDayAvailable(queryDate, now); got != want {
|
||||
t.Fatalf("dxBatchDayAvailable(%q) = %v, want %v", queryDate, got, want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestDXBatchMonthAvailable(t *testing.T) {
|
||||
now := time.Date(2026, 9, 20, 12, 0, 0, 0, dxBatchLocation)
|
||||
if !dxBatchMonthAvailable("202609", now) {
|
||||
t.Fatal("current month should use telecom batch API")
|
||||
}
|
||||
if dxBatchMonthAvailable("202608", now) || dxBatchMonthAvailable("invalid", now) {
|
||||
t.Fatal("historical or invalid month must not use telecom current-month batch API")
|
||||
}
|
||||
}
|
||||
|
||||
func TestQuerySimDataUsageMonthlyUsesBatchForCurrentMonth(t *testing.T) {
|
||||
var calls atomic.Int32
|
||||
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
calls.Add(1)
|
||||
if r.URL.Path != "/api/v1/batchQry/batchQryFlowByMonth" {
|
||||
t.Fatalf("path = %q", r.URL.Path)
|
||||
}
|
||||
var request dxBatchRequest
|
||||
if err := json.NewDecoder(r.Body).Decode(&request); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if len(request.AccessNumbers) != 2 || request.CustNumber != "cust-1" {
|
||||
t.Fatalf("request = %+v", request)
|
||||
}
|
||||
_, _ = w.Write([]byte(`{"code":0,"msg":"查询成功","data":[{"accessNumber":"13300000001","result":"1","unit":"MB","flowAmount":"12.5"},{"accessNumber":"13300000002","result":"1","unit":"MB","flowAmount":"7.5"}]}`))
|
||||
}))
|
||||
defer server.Close()
|
||||
|
||||
sim := &sSimDX{client: dx.NewClient(dx.Config{BaseURL: server.URL, AppKey: "test", AppSecret: "secret"}), custNumber: "cust-1"}
|
||||
queryMonth := time.Now().In(dxBatchLocation).Format("200601")
|
||||
output, err := sim.QuerySimDataUsageMonthly(context.Background(), &model.SimDataUsageMonthlyInput{
|
||||
SimKeys: []*model.SimKey{{MSISDN: "13300000001"}, {MSISDN: "13300000002"}},
|
||||
ReqData: model.SimDataUsageMonthlyInputItem{QueryDate: queryMonth},
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if calls.Load() != 1 || len(output) != 2 || output[0].ResData.DataAmount != 12.5 || output[1].ResData.DataAmount != 7.5 {
|
||||
t.Fatalf("calls=%d output=%+v", calls.Load(), output)
|
||||
}
|
||||
}
|
||||
|
||||
func TestQuerySimDataUsageDailyLegacyFallbackIsBounded(t *testing.T) {
|
||||
var active atomic.Int32
|
||||
var maximum atomic.Int32
|
||||
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
current := active.Add(1)
|
||||
defer active.Add(-1)
|
||||
for {
|
||||
previous := maximum.Load()
|
||||
if current <= previous || maximum.CompareAndSwap(previous, current) {
|
||||
break
|
||||
}
|
||||
}
|
||||
time.Sleep(30 * time.Millisecond)
|
||||
w.Header().Set("Content-Type", "application/xml")
|
||||
_, _ = w.Write([]byte(`<root><NEW_DATA_TICKET_QRsp><IRESULT>0</IRESULT><SMSG>OK</SMSG><TOTAL_BYTES_CNT>1MB</TOTAL_BYTES_CNT></NEW_DATA_TICKET_QRsp></root>`))
|
||||
}))
|
||||
defer server.Close()
|
||||
|
||||
sim := &sSimDX{client: dx.NewClient(dx.Config{BaseURL: server.URL, AppKey: "test", AppSecret: "secret", Timeout: time.Second})}
|
||||
keys := make([]*model.SimKey, 12)
|
||||
for index := range keys {
|
||||
keys[index] = &model.SimKey{MSISDN: "13300000000"}
|
||||
}
|
||||
output, err := sim.QuerySimDataUsageDaily(context.Background(), &model.SimDataUsageDailyInput{
|
||||
SimKeys: keys,
|
||||
ReqData: model.SimDataUsageDailyInputItem{QueryDate: "20260101"},
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if len(output) != len(keys) {
|
||||
t.Fatalf("results = %d, want %d", len(output), len(keys))
|
||||
}
|
||||
if maximum.Load() > dxLegacyUsageConcurrency || maximum.Load() < 2 {
|
||||
t.Fatalf("maximum concurrency = %d, want 2..%d", maximum.Load(), dxLegacyUsageConcurrency)
|
||||
}
|
||||
}
|
||||
|
||||
func TestQuerySimDataUsageDailyRejectsMalformedXML(t *testing.T) {
|
||||
cases := []struct {
|
||||
name string
|
||||
body string
|
||||
}{
|
||||
{name: "missing business node", body: `<root><ERROR>denied</ERROR></root>`},
|
||||
{name: "missing result", body: `<root><NEW_DATA_TICKET_QRsp><TOTAL_BYTES_CNT>12MB</TOTAL_BYTES_CNT></NEW_DATA_TICKET_QRsp></root>`},
|
||||
{name: "missing amount", body: `<root><NEW_DATA_TICKET_QRsp><IRESULT>0</IRESULT></NEW_DATA_TICKET_QRsp></root>`},
|
||||
{name: "invalid amount", body: `<root><NEW_DATA_TICKET_QRsp><IRESULT>0</IRESULT><TOTAL_BYTES_CNT>invalidMB</TOTAL_BYTES_CNT></NEW_DATA_TICKET_QRsp></root>`},
|
||||
{name: "non numeric result", body: `<root><NEW_DATA_TICKET_QRsp><IRESULT>ERROR</IRESULT></NEW_DATA_TICKET_QRsp></root>`},
|
||||
}
|
||||
for _, item := range cases {
|
||||
t.Run(item.name, func(t *testing.T) {
|
||||
server := httptest.NewServer(http.HandlerFunc(func(writer http.ResponseWriter, request *http.Request) {
|
||||
writer.Header().Set("Content-Type", "application/xml")
|
||||
_, _ = writer.Write([]byte(item.body))
|
||||
}))
|
||||
defer server.Close()
|
||||
|
||||
sim := &sSimDX{client: dx.NewClient(dx.Config{BaseURL: server.URL, AppKey: "test", AppSecret: "test"})}
|
||||
output, err := sim.QuerySimDataUsageDaily(context.Background(), &model.SimDataUsageDailyInput{
|
||||
SimKeys: []*model.SimKey{{ICCID: "audit-card", MSISDN: "13300000001"}},
|
||||
ReqData: model.SimDataUsageDailyInputItem{QueryDate: "20260101"},
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if len(output) != 1 || output[0].Code == 0 {
|
||||
t.Fatalf("malformed response accepted: %+v", output)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestQuerySimDataUsageDailyBatchFailureFallsBackToLegacy(t *testing.T) {
|
||||
var batchCalls atomic.Int32
|
||||
var singleCalls atomic.Int32
|
||||
server := httptest.NewServer(http.HandlerFunc(func(writer http.ResponseWriter, request *http.Request) {
|
||||
if request.URL.Path == "/api/v1/batchQry/batchQryFlowByDate" {
|
||||
batchCalls.Add(1)
|
||||
writer.Header().Set("Content-Type", "application/json")
|
||||
_, _ = writer.Write([]byte(`{"code":403,"msg":"denied"}`))
|
||||
return
|
||||
}
|
||||
singleCalls.Add(1)
|
||||
writer.Header().Set("Content-Type", "application/xml")
|
||||
_, _ = writer.Write([]byte(`<root><NEW_DATA_TICKET_QRsp><IRESULT>0</IRESULT><TOTAL_BYTES_CNT>12MB</TOTAL_BYTES_CNT></NEW_DATA_TICKET_QRsp></root>`))
|
||||
}))
|
||||
defer server.Close()
|
||||
|
||||
sim := &sSimDX{client: dx.NewClient(dx.Config{BaseURL: server.URL, AppKey: "test", AppSecret: "test"}), custNumber: "audit-customer"}
|
||||
queryDate := time.Now().In(dxBatchLocation).AddDate(0, 0, -1).Format("20060102")
|
||||
output, err := sim.QuerySimDataUsageDaily(context.Background(), &model.SimDataUsageDailyInput{
|
||||
SimKeys: []*model.SimKey{{MSISDN: "13300000001"}},
|
||||
ReqData: model.SimDataUsageDailyInputItem{QueryDate: queryDate},
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if batchCalls.Load() != 1 || singleCalls.Load() != 1 {
|
||||
t.Fatalf("batch calls=%d, single calls=%d, want 1 each", batchCalls.Load(), singleCalls.Load())
|
||||
}
|
||||
if len(output) != 1 || output[0].Code != 0 || output[0].ResData.DataAmount != 12 {
|
||||
t.Fatalf("unexpected fallback output: %+v", output)
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseDXDataUsage(t *testing.T) {
|
||||
cases := []struct {
|
||||
value string
|
||||
want float64
|
||||
invalid bool
|
||||
}{
|
||||
{value: "0MB", want: 0},
|
||||
{value: "12.5MB", want: 12.5},
|
||||
{value: " 1 gb ", want: 1024},
|
||||
{value: "1024KB", want: 1},
|
||||
{value: "12", want: 12},
|
||||
{value: "", invalid: true},
|
||||
{value: "invalidMB", invalid: true},
|
||||
{value: "-1MB", invalid: true},
|
||||
{value: "NaNMB", invalid: true},
|
||||
{value: "InfMB", invalid: true},
|
||||
{value: "1e308GB", invalid: true},
|
||||
}
|
||||
for _, item := range cases {
|
||||
t.Run(item.value, func(t *testing.T) {
|
||||
amount, err := parseDXDataUsage(item.value)
|
||||
if (err != nil) != item.invalid || (!item.invalid && amount != item.want) {
|
||||
t.Fatalf("parseDXDataUsage(%q) = %v, %v, want %v, invalid=%v", item.value, amount, err, item.want, item.invalid)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user