package app import ( "context" "encoding/json" "fmt" "net/http" "net/http/httptest" "testing" "git.5iot.com/iotlcm-public/iotlcm-app/internal/model/entity" "github.com/gogf/gf/v2/frame/g" "github.com/gogf/gf/v2/os/gcfg" ) func TestAppNormalizeUpstreamSessionSupportsTelecomFields(t *testing.T) { session := &appOneLinkMockSimSessionItem{} raw := json.RawMessage(`{"onlineStatus":"0","accessType":"5G","ipv4Address":"192.0.2.10","ipv6Address":"2001:db8::1","apn":"CMIOT","startTime":"2026-10-08 10:00:00"}`) appNormalizeUpstreamSession(session, raw) if session.Status != "0" || session.Rat != "5G" || session.Ipv4 != "192.0.2.10" || session.Ipv6 != "2001:db8::1" || session.ApnId != "CMIOT" { t.Fatalf("normalized session = %+v", session) } } func TestAppDiagnosisUpstreamUsesProxy(t *testing.T) { proxy := httptest.NewServer(http.HandlerFunc(func(writer http.ResponseWriter, request *http.Request) { if request.Method != http.MethodPost || request.URL.Path != "/sim/query/sim-basic-info" { t.Errorf("request = %s %s", request.Method, request.URL.Path) } var input appUpstreamSimQueryInput if err := json.NewDecoder(request.Body).Decode(&input); err != nil { t.Errorf("decode proxy request: %v", err) } else if len(input.SimKeys) != 1 || input.SimKeys[0].ChanKey != "test-channel" || input.SimKeys[0].ICCID != "test-iccid" || input.SimKeys[0].IMSI != "test-imsi" || input.SimKeys[0].MSISDN != "test-msisdn" { t.Errorf("proxy sim key = %+v", input.SimKeys) } writer.Header().Set("Content-Type", "application/json") _, _ = writer.Write([]byte(`{"code":0,"data":[{"code":0,"resData":{"simStatus":"0"}}]}`)) })) defer proxy.Close() config, err := gcfg.NewAdapterContent(fmt.Sprintf(`{"upstream":{"default":{"clientName":%q,"timeout":"2s"},"gateway":{"clientName":"http://127.0.0.1:1"}}}`, proxy.URL)) if err != nil { t.Fatalf("configure upstream: %v", err) } original := g.Cfg().GetAdapter() g.Cfg().SetAdapter(config) t.Cleanup(func() { g.Cfg().SetAdapter(original) }) items, err := queryAppUpstreamSimWithKey(context.Background(), &appUpstreamSimKey{ ChanKey: "test-channel", ICCID: "test-iccid", IMSI: "test-imsi", MSISDN: "test-msisdn", }, "/sim/query/sim-basic-info", nil) if err != nil { t.Fatalf("query upstream through proxy: %v", err) } if len(items) != 1 || items[0].Code != 0 { t.Fatalf("proxy response = %+v", items) } } func TestAppDiagnosisUsesDedicatedProxyRoute(t *testing.T) { proxy := httptest.NewServer(http.HandlerFunc(func(writer http.ResponseWriter, request *http.Request) { if request.URL.Path != "/internal/app-diagnosis/sim/query/sim-basic-info" { t.Errorf("unexpected path: %s", request.URL.Path) } if request.Header.Get("X-App-Diagnosis-Token") != "test-internal-token" { t.Errorf("missing internal token") } writer.Header().Set("Content-Type", "application/json") _, _ = writer.Write([]byte(`{"code":0,"data":[{"code":0,"resData":{"simStatus":"0"}}]}`)) })) defer proxy.Close() adapter, err := gcfg.NewAdapterContent(fmt.Sprintf(`{"upstream":{"default":{"clientName":%q,"timeout":"2s"},"appDiagnosisToken":"test-internal-token"}}`, proxy.URL)) if err != nil { t.Fatal(err) } previous := g.Cfg().GetAdapter() g.Cfg().SetAdapter(adapter) t.Cleanup(func() { g.Cfg().SetAdapter(previous) }) ctx := context.WithValue(context.Background(), appDiagnosisProxyContextKey{}, true) items, err := queryAppUpstreamSimWithKey(ctx, &appUpstreamSimKey{ChanKey: "test-channel", ICCID: "test-iccid"}, "/sim/query/sim-basic-info", nil) if err != nil || len(items) != 1 || items[0].Code != 0 { t.Fatalf("dedicated proxy request: items=%+v err=%v", items, err) } } func TestAppDiagnosisDoesNotFallbackToMockSession(t *testing.T) { proxy := httptest.NewServer(http.HandlerFunc(func(writer http.ResponseWriter, request *http.Request) { writer.Header().Set("Content-Type", "application/json") _, _ = writer.Write([]byte(`{"code":500,"message":"upstream unavailable"}`)) })) 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) }) ctx := context.WithValue(context.Background(), appDiagnosisProxyContextKey{}, true) session, source, err := queryAppCardRealtimeSessionWithKey(ctx, &appCardQueryRow{}, &appUpstreamSimKey{ChanKey: "test-channel", ICCID: "test-iccid"}) if err == nil || session != nil || source != "upstream" { t.Fatalf("session = %+v, source = %q, err = %v; want upstream failure without mock fallback", session, source, err) } } func TestAppDiagnosisTreatsMissingProxyChannelAsNotIncluded(t *testing.T) { proxy := httptest.NewServer(http.HandlerFunc(func(writer http.ResponseWriter, request *http.Request) { writer.WriteHeader(http.StatusNotFound) _, _ = writer.Write([]byte(`Not Found{"code":404,"message":"诊断通道不可用"}`)) })) defer proxy.Close() adapter, err := gcfg.NewAdapterContent(fmt.Sprintf(`{"upstream":{"default":{"clientName":%q,"timeout":"2s"},"appDiagnosisToken":"test-internal-token"}}`, proxy.URL)) if err != nil { t.Fatal(err) } previous := g.Cfg().GetAdapter() g.Cfg().SetAdapter(adapter) t.Cleanup(func() { g.Cfg().SetAdapter(previous) }) ctx := context.WithValue(context.Background(), appDiagnosisProxyContextKey{}, true) _, err = queryAppUpstreamSimWithKey(ctx, &appUpstreamSimKey{ChanKey: "missing-channel", ICCID: "test-iccid"}, "/sim/query/sim-basic-info", nil) if err == nil || err.Error() != appDiagnosisNotIncludedMessage { t.Fatalf("missing proxy channel error = %v", err) } row := &appCardQueryRow{TSimCardInfo: entity.TSimCardInfo{ Iccid: "test-iccid", Imsi: "test-imsi", Msisdn: "test-msisdn", ChanKey: "missing-channel", }} _, err = (&sAppDiagnosis{}).prepareDiagnosisSimKey(ctx, row) if err == nil || err.Error() != appDiagnosisNotIncludedMessage { t.Fatalf("legacy diagnosis preflight error = %v", err) } }