package upstreamimpl import ( "bytes" "encoding/json" "net/http" "net/http/httptest" "strings" "testing" "github.com/gogf/gf/v2/frame/g" "github.com/gogf/gf/v2/net/ghttp" "github.com/gogf/gf/v2/os/gcfg" "git.5iot.com/iotlcm-public/iotlcm-upstream-proxy/internal/logic/upstreamapi" "git.5iot.com/iotlcm-public/iotlcm-upstream-proxy/internal/model" ) func TestAPNProxyHTTPFailureIsUncertainOnlyOnAPNRoute(t *testing.T) { oldAdapter := g.Cfg().GetAdapter() defer g.Cfg().SetAdapter(oldAdapter) for _, tc := range []struct { name, path, body string disconnect, uncertain bool }{ {"HTTP200顶层9999", "/sim/change/sim-apn", `{"code":9999,"message":"模拟网关未确认"}`, false, true}, {"通道连接断开", "/sim/change/sim-apn", "", true, true}, {"非APN同名字段", "/sim/change/sim-status", `{"code":9999,"message":"模拟非APN网关错误"}`, false, false}, } { t.Run(tc.name, func(t *testing.T) { chanServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { if tc.disconnect { conn, _, err := w.(http.Hijacker).Hijack() if err != nil { t.Error(err) return } _ = conn.Close() return } _, _ = w.Write([]byte(tc.body)) })) defer chanServer.Close() adapter, err := gcfg.NewAdapterContent(`{"gateway":{"address":"` + strings.TrimPrefix(chanServer.URL, "http://") + `","timeout":"1s"}}`) if err != nil { t.Fatal(err) } g.Cfg().SetAdapter(adapter) api := upstreamapi.NewUpstreamAPI() api.RegisterChan("fake-channel", "test", &model.SimCardChannelCache{Instance: &sSimYD{}}) proxy := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { var input model.SimBaseReqJson if err := json.NewDecoder(r.Body).Decode(&input); err != nil { t.Error(err) return } ctx := (&ghttp.Request{Request: r}).Context() items, err := api.SimBase(ctx, &input) if err != nil { t.Error(err) return } _ = json.NewEncoder(w).Encode(map[string]any{"code": 0, "data": items}) })) defer proxy.Close() body := []byte(`{"simKeys":[{"chanKey":"fake-channel","iccid":"fake-card"}],"reqData":{"operationId":"fake-operation","submitOnly":true}}`) response, err := http.Post(proxy.URL+tc.path, "application/json", bytes.NewReader(body)) if err != nil { t.Fatal(err) } defer response.Body.Close() var output struct { Code int `json:"code"` Data model.SimBaseResJson `json:"data"` } if err := json.NewDecoder(response.Body).Decode(&output); err != nil { t.Fatal(err) } if response.StatusCode != 200 || output.Code != 0 || len(output.Data) != 1 || output.Data[0].Code != 9999 || output.Data[0].Message == "" { t.Fatalf("代理异常响应无效:HTTP=%d result=%+v", response.StatusCode, output) } isUncertain := strings.Contains(string(output.Data[0].ResData), `"uncertain":true`) if isUncertain != tc.uncertain { t.Fatalf("APN路由隔离失效:%s", output.Data[0].ResData) } }) } } func TestAPNProxyHTTPKeepsDurableJobAndFrozenRequest(t *testing.T) { oldAdapter := g.Cfg().GetAdapter() defer g.Cfg().SetAdapter(oldAdapter) var captured model.SimBaseReqJson chanServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { if err := json.NewDecoder(r.Body).Decode(&captured); err != nil { t.Error(err) return } _, _ = w.Write([]byte(`{"code":0,"data":[{"code":0,"simKey":{"chanKey":"fake-channel","iccid":"fake-card","msisdn":"frozen-number"},"resData":{"orderNum":"saved-job","pending":true}}]}`)) })) defer chanServer.Close() adapter, err := gcfg.NewAdapterContent(`{"gateway":{"address":"` + strings.TrimPrefix(chanServer.URL, "http://") + `","timeout":"1s"}}`) if err != nil { t.Fatal(err) } g.Cfg().SetAdapter(adapter) api := upstreamapi.NewUpstreamAPI() api.RegisterChan("fake-channel", "test", &model.SimCardChannelCache{Instance: &sSimYD{}}) input := &model.SimBaseReqJson{SimKeys: []*model.SimKey{{ChanKey: "fake-channel", ICCID: "fake-card", MSISDN: "frozen-number"}}, ReqData: json.RawMessage(`{"operationId":"stable-operation","recoverOnly":true}`)} ctx := (&ghttp.Request{Request: httptest.NewRequest("POST", "/sim/change/sim-apn", nil)}).Context() items, err := api.SimBase(ctx, input) if err != nil || len(items) != 1 || !strings.Contains(string(items[0].ResData), `"orderNum":"saved-job"`) || !strings.Contains(string(items[0].ResData), `"pending":true`) { t.Fatalf("HTTP转发丢失jobId:%+v %v", items, err) } if string(captured.ReqData) != string(input.ReqData) || captured.SimKeys[0].MSISDN != "frozen-number" { t.Fatalf("恢复请求被改写:%+v", captured) } }