package app import ( "context" "encoding/json" "fmt" "net/http" "net/http/httptest" "strings" "sync" "testing" "github.com/gogf/gf/v2/frame/g" "github.com/gogf/gf/v2/os/gcfg" "git.5iot.com/iotlcm-public/iotlcm-app/internal/model" "git.5iot.com/iotlcm-public/iotlcm-app/internal/model/entity" ) func TestAppDiagnosisUpstreamContract(t *testing.T) { scenarios := []string{"success", "session failure with IMEI", "IMEI failure with session"} for _, scenario := range scenarios { t.Run(scenario, func(t *testing.T) { var mu sync.Mutex seen := map[string]int{} responses := map[string]string{ "sim-basic-info": `{"simStatus":"1","iccid":"card","imsi":"imsi","msisdn":"msisdn"}`, "sim-status": `{"cardStatus":4}`, "sim-session": `{"status":"0","apnId":"CMIOT","ipv4":"192.0.2.10"}`, "sim-imei": `{"imei":"latest"}`, "sim-device-status": `{"deviceStatus":"1","operatingStatus":"1"}`, "sim-card-bind-status": `{"bindStatus":"1","orderStatus":"0"}`, "sim-region-limit-status": `{"regionLimitStatus":"0","orderStatus":"1"}`, "sim-data-diagnosis": `{"status":"0","message":"正确","result":[{"APNList":{"APNInfo":[{"APNName":"CMIOT","modifyTime":"20250526140211","APNStatus":"2","APNServiceUsageState":"1"}]}}]}`, "sim-stop-reason": `{"stopReason":"000000000000"}`, "sim-frozen-state": `{"frozenStatus":"0"}`, "sim-datausage": `{"dataUsage":0}`, "sim-apn-info": `{"apnList":[{"apnName":"CMIOT","status":"0"}]}`, } proxy := httptest.NewServer(http.HandlerFunc(func(writer http.ResponseWriter, request *http.Request) { const prefix = "/internal/app-diagnosis/sim/query/" if !strings.HasPrefix(request.URL.Path, prefix) || request.Header.Get("X-App-Diagnosis-Token") != "test-token" { t.Errorf("proxy contract mismatch: %s", request.URL.Path) } var input appUpstreamSimQueryInput if err := json.NewDecoder(request.Body).Decode(&input); err != nil || len(input.SimKeys) != 1 || input.SimKeys[0].ICCID != "card" || input.SimKeys[0].ChanKey != "test-channel" { t.Errorf("query key mismatch: %+v, %v", input, err) } endpoint := strings.TrimPrefix(request.URL.Path, prefix) mu.Lock() seen[endpoint]++ mu.Unlock() writer.Header().Set("Content-Type", "application/json") fail := scenario == "session failure with IMEI" && endpoint == "sim-session" || scenario == "IMEI failure with session" && endpoint == "sim-imei" if fail { _, _ = writer.Write([]byte(`{"code":0,"data":[{"code":500,"message":"query failed"}]}`)) return } raw, ok := responses[endpoint] if !ok { t.Errorf("unknown endpoint: %s", endpoint) return } _, _ = fmt.Fprintf(writer, `{"code":0,"data":[{"code":0,"resData":%s}]}`, raw) })) defer proxy.Close() adapter, err := gcfg.NewAdapterContent(fmt.Sprintf(`{"upstream":{"default":{"clientName":%q,"timeout":"2s"},"appDiagnosisToken":"test-token","onelinkMock":{"enabled":true}}}`, proxy.URL)) if err != nil { t.Fatal(err) } previous := g.Cfg().GetAdapter() g.Cfg().SetAdapter(adapter) t.Cleanup(func() { g.Cfg().SetAdapter(previous) }) row := &appCardQueryRow{TSimCardInfo: entity.TSimCardInfo{Id: 1, Iccid: "card", Imsi: "imsi", Msisdn: "msisdn", ChanKey: "test-channel", BossCardStatus: 2, DataServiceStatus: 1, Imei: "local-old"}} result, err := NewAppDiagnosis().buildDiagnosisInfo(context.Background(), row, &model.AppCardModel{}) if err != nil { t.Fatal(err) } if result.Info.Status == nil || *result.Info.Status != "4" { t.Fatal("card status did not come from sim-status") } if result.Info.DataStatus == nil || *result.Info.DataStatus != "0" { t.Fatal("APN status did not come from sim-apn-info") } if result.Info.UsedFlowMb == nil || *result.Info.UsedFlowMb != 0 { t.Fatal("zero usage was lost") } if result.Info.RateLimitStatus == nil || *result.Info.RateLimitStatus != "1" || len(result.Info.RateLimitApnDetails) != 1 { t.Fatal("mobile APN policy response was not normalized") } if scenario == "session failure with IMEI" { if result.Modules["session"].Status != "error" || result.Info.OnlineStatus != nil || result.Session.Imei == nil || *result.Session.Imei != "latest" { t.Fatal("session failure was masked or IMEI was not independent") } } else if scenario == "IMEI failure with session" { if result.Modules["imei"].Status != "error" || result.Modules["session"].Status != "success" || result.Session.Imei != nil { t.Fatal("IMEI failure fell back to another source") } } else if result.Info.OnlineStatus == nil || *result.Info.OnlineStatus != "1" { t.Fatal("online status did not come from session") } }) } }