sync: 同步 v1.0.4 开源代码并脱敏

This commit is contained in:
MiFaZhan
2026-10-29 14:59:32 +08:00
parent 14f4bb64e1
commit 3e5a7453b8
74 changed files with 3297 additions and 248 deletions
+169 -7
View File
@@ -4,17 +4,18 @@ import (
"context"
"encoding/json"
"fmt"
"io"
"net/http"
"net/http/httptest"
"strings"
"sync/atomic"
"testing"
"time"
"git.5iot.com/iotlcm-public/iotlcm-upstream-chan/internal/consts"
"git.5iot.com/iotlcm-public/iotlcm-upstream-chan/internal/logic/upstreamimpl/dx"
"git.5iot.com/iotlcm-public/iotlcm-upstream-chan/internal/model"
"git.5iot.com/iotlcm-public/iotlcm-upstream-chan/internal/model/entity"
"git.5iot.com/iotlcm-public/iotlcm-upstream-chan/internal/service"
"io"
"net/http"
"net/http/httptest"
"strings"
"testing"
"time"
)
func newDXV3TestClient(t *testing.T, handler http.HandlerFunc) *sSimDXV3 {
@@ -24,6 +25,167 @@ func newDXV3TestClient(t *testing.T, handler http.HandlerFunc) *sSimDXV3 {
return &sSimDXV3{client: dx.NewClient(dx.Config{BaseURL: server.URL, AppKey: "test-key", AppSecret: "test-secret", UppercaseSign: true}), custNbr: "test-customer"}
}
func TestDXV3APNWriteUncertainAndNoReplay(t *testing.T) {
for _, tc := range []struct {
name, body string
drop, uncertain bool
redirect int
}{
{name: "断响应", drop: true, uncertain: true},
{name: "坏JSON", body: "{", uncertain: true},
{name: "缺result", body: `{"code":0}`, uncertain: true},
{name: "卡号不符", body: `{"code":0,"result":{"msisdn":"other","transactionId":"job"}}`, uncertain: true},
{name: "缺transactionId", body: `{"code":0,"result":{}}`, uncertain: true},
{name: "明确拒绝", body: `{"code":9999,"message":"拒绝"}`},
{name: "明确成功", body: `{"code":0,"result":{"transactionId":"job"}}`},
{name: "跳转307", redirect: 307, uncertain: true},
{name: "跳转308", redirect: 308, uncertain: true},
} {
t.Run(tc.name, func(t *testing.T) {
var writes, targetWrites atomic.Int32
client := newDXV3TestClient(t, func(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodPost {
t.Errorf("办理方法被改变:%s", r.Method)
}
writes.Add(1)
if r.URL.Path == "/redirect-target" {
targetWrites.Add(1)
fmt.Fprint(w, `{"code":0,"result":{"transactionId":"unexpected"}}`)
return
}
if r.URL.Path != "/api/v3/singleCutNet" {
t.Errorf("错误办理路径:%s", r.URL.Path)
}
var body map[string]string
if err := json.NewDecoder(r.Body).Decode(&body); err != nil || body["msisdn"] != "fake-msisdn" || body["operType"] != "1" {
t.Errorf("办理请求不符:%+v %v", body, err)
}
if tc.drop {
conn, _, err := w.(http.Hijacker).Hijack()
if err != nil {
t.Error(err)
return
}
_ = conn.Close()
return
}
if tc.redirect != 0 {
w.Header().Set("Location", "/redirect-target")
w.WriteHeader(tc.redirect)
return
}
fmt.Fprint(w, tc.body)
})
client.enableOperations = true
out, err := client.ChangeSimApnFunction(context.Background(), &model.SimCardOperateChangeApnInput{SimKeys: []*model.SimKey{{MSISDN: "fake-msisdn"}}, ReqData: model.SimCardOperateChangeApnInputItem{OperType: "0"}})
if err != nil || len(out) != 1 || writes.Load() != 1 || targetWrites.Load() != 0 || out[0].ResData.Uncertain != tc.uncertain || out[0].ResData.Pending {
t.Fatalf("写后分类/重放错误:out=%+v writes=%d target=%d err=%v", out, writes.Load(), targetWrites.Load(), err)
}
if (tc.name == "明确成功") != (out[0].Code == 0) {
t.Fatalf("成功失败分类错误:%+v", out[0])
}
t.Logf("writes=%d targetWrites=%d uncertain=%v code=%d", writes.Load(), targetWrites.Load(), out[0].ResData.Uncertain, out[0].Code)
})
}
}
func TestDXV3SingleCutNetAcceptanceModes(t *testing.T) {
for _, mode := range []string{"同步兼容", "仅提交", "持久化回调"} {
for _, accepted := range []bool{true, false} {
t.Run(fmt.Sprintf("%s/受理=%t", mode, accepted), func(t *testing.T) {
writes, checkpoints := 0, 0
client := newDXV3TestClient(t, func(w http.ResponseWriter, r *http.Request) {
writes++
if !accepted {
fmt.Fprint(w, `{"code":9999,"message":"明确拒绝"}`)
return
}
fmt.Fprintf(w, `{"code":0,"result":{"transactionId":"order-%d"}}`, writes)
})
client.enableOperations = true
wantUncertain := accepted && mode != "同步兼容"
check := func(items model.SimCardOperateChangeApnOutput) {
t.Helper()
for i, item := range items {
if (item.Code == 0) != accepted || item.ResData.Uncertain != wantUncertain || item.ResData.Pending {
t.Fatalf("受理与完成分类错误:%+v", item)
}
if accepted && item.ResData.OrderNum != fmt.Sprintf("order-%d", i+1) {
t.Fatalf("未保留逐卡受理流水:%+v", item)
}
if wantUncertain && !strings.Contains(item.Message, "最终办理结果未确认") {
t.Fatalf("受理消息未明确最终结果未知:%s", item.Message)
}
if !accepted && (item.ErrCode != "9999" || item.ResData.OrderNum != "") {
t.Fatalf("明确拒绝不应携带受理流水:%+v", item)
}
}
}
ctx := context.Background()
if mode == "持久化回调" {
ctx = context.WithValue(ctx, model.ApnOperationCheckpointKey{}, func(items model.SimCardOperateChangeApnOutput) error {
checkpoints++
check(items)
return nil
})
}
items, err := client.ChangeSimApnFunction(ctx, &model.SimCardOperateChangeApnInput{
SimKeys: []*model.SimKey{{ChanKey: "test", ICCID: "card-1", MSISDN: "test-msisdn-1"}, {ChanKey: "test", ICCID: "card-2", MSISDN: "test-msisdn-2"}},
ReqData: model.SimCardOperateChangeApnInputItem{OperType: "1", SubmitOnly: mode == "仅提交"},
})
if err != nil || len(items) != 2 || writes != 2 || (mode == "持久化回调" && checkpoints != 2) {
t.Fatalf("逐卡处理异常:items=%+v writes=%d checkpoints=%d err=%v", items, writes, checkpoints, err)
}
check(items)
})
}
}
}
func TestDXV3APNPrewriteValidationSendsNothing(t *testing.T) {
for _, tc := range []struct {
name string
enabled bool
key *model.SimKey
oper string
}{
{"禁用办理", false, &model.SimKey{MSISDN: "fake"}, "0"},
{"缺卡号", true, nil, "0"},
{"缺msisdn", true, &model.SimKey{ICCID: "fake"}, "0"},
{"错误操作", true, &model.SimKey{MSISDN: "fake"}, "invalid"},
} {
t.Run(tc.name, func(t *testing.T) {
var writes atomic.Int32
client := newDXV3TestClient(t, func(w http.ResponseWriter, r *http.Request) { writes.Add(1) })
client.enableOperations = tc.enabled
out, err := client.ChangeSimApnFunction(context.Background(), &model.SimCardOperateChangeApnInput{SimKeys: []*model.SimKey{tc.key}, ReqData: model.SimCardOperateChangeApnInputItem{OperType: tc.oper}})
if err != nil || len(out) != 1 || writes.Load() != 0 || out[0].Code == 0 || out[0].ResData.Uncertain {
t.Fatalf("预写拒绝错误:out=%+v writes=%d err=%v", out, writes.Load(), err)
}
})
}
}
func TestDXV3ReadRedirectUnchanged(t *testing.T) {
var calls atomic.Int32
client := newDXV3TestClient(t, func(w http.ResponseWriter, r *http.Request) {
calls.Add(1)
if r.Method != http.MethodGet {
t.Errorf("查询方法改变:%s", r.Method)
}
if r.URL.Path != "/read-target" {
w.Header().Set("Location", "/read-target")
w.WriteHeader(http.StatusTemporaryRedirect)
return
}
fmt.Fprint(w, `{"code":0,"result":{"dataAmount":"2"}}`)
})
out, err := client.QuerySimDataUsage(context.Background(), &model.SimDataUsageInput{SimKeys: []*model.SimKey{{MSISDN: "fake"}}})
if err != nil || len(out) != 1 || out[0].Code != 0 || calls.Load() != 2 {
t.Fatalf("查询跳转被改变:%+v %v calls=%d", out, err, calls.Load())
}
}
func TestDXV3TemplateCoexistsWithV1(t *testing.T) {
for _, key := range []string{"sim_dx_v1", "sim_dx_v3"} {
if _, ok := service.UpstreamAPI().GetTemp(key); !ok {
@@ -57,7 +219,7 @@ func TestDXV3SessionAndRealTimeUsageContracts(t *testing.T) {
}
switch r.URL.Path {
case "/api/v3/getOnlineStatus":
fmt.Fprint(w, `{"code":"0","result":{"onlineStatus":"1","apnName":"iot.apn","ipv4Address":"192.0.2.1"}}`)
fmt.Fprint(w, `{"code":"0","result":{"onlineStatus":"1","apnName":"iot.apn","ipv4Address":"127.0.0.1"}}`)
case "/api/v3/getDataUsage":
fmt.Fprint(w, `{"code":0,"result":{"dataAmount":"12.5"}}`)
default: