Files
iotlcm/internal/cmd/http_test.go
T
2026-10-29 15:03:39 +08:00

161 lines
5.5 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package cmd
import (
"context"
"crypto/sha256"
"encoding/json"
"fmt"
"net/url"
"os"
"strings"
"testing"
"time"
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/net/ghttp"
"github.com/gogf/gf/v2/os/gcfg"
v1 "git.5iot.com/iotlcm-public/iotlcm-upstream-proxy/api/cardquery/v1"
_ "git.5iot.com/iotlcm-public/iotlcm-upstream-proxy/internal/logic/simKeyFill"
"git.5iot.com/iotlcm-public/iotlcm-upstream-proxy/internal/logic/upstreamapi"
"git.5iot.com/iotlcm-public/iotlcm-upstream-proxy/internal/logic/upstreamimpl"
"git.5iot.com/iotlcm-public/iotlcm-upstream-proxy/internal/model"
"git.5iot.com/iotlcm-public/iotlcm-upstream-proxy/internal/service"
)
func TestAPNHTTPHarness(t *testing.T) {
if os.Getenv("APN_HTTP_CHAIN_HELPER") != "1" {
t.Skip("未启用本机HTTP模拟链")
}
gateway, err := url.Parse(os.Getenv("APN_HTTP_CHAIN_CHAN"))
if err != nil || gateway.Scheme != "http" || gateway.Hostname() != "127.0.0.1" || gateway.Port() == "" {
t.Fatal("只允许本机模拟chan")
}
adapter, err := gcfg.NewAdapterContent(fmt.Sprintf(`{"gateway":{"address":%q,"timeout":"2s"}}`, gateway.Host))
if err != nil {
t.Fatal(err)
}
g.Cfg().SetAdapter(adapter)
api := upstreamapi.NewUpstreamAPI()
api.RegisterChan("fake-channel", "1", &model.SimCardChannelCache{Instance: upstreamimpl.NewSimYD()})
service.RegisterUpstreamAPI(api)
server := ghttp.GetServer(t.Name())
server.SetAddr("127.0.0.1:0")
bindHTTPRoutes(server)
stop := make(chan struct{})
server.BindHandler("POST:/__test/stop", func(r *ghttp.Request) { close(stop) })
if err := server.Start(); err != nil {
t.Fatal(err)
}
defer server.Shutdown()
fmt.Printf("APN_CHAIN_READY=http://127.0.0.1:%d\n", server.GetListenedPort())
select {
case <-stop:
case <-time.After(3 * time.Minute):
t.Error("HTTP模拟链未正常结束")
}
}
type apnBindingTestFill struct {
service.ISimKeyFill
captured chan model.SimBaseReqJson
}
func (s *apnBindingTestFill) SimKeyFill(_ context.Context, input *model.SimBaseReqJson) (model.SimBaseResJson, error) {
s.captured <- *input
return model.SimBaseResJson{}, nil
}
func TestAPNProductionHTTPBindingPreservesRawPayload(t *testing.T) {
old := service.SimKeyFill()
capture := &apnBindingTestFill{ISimKeyFill: old, captured: make(chan model.SimBaseReqJson, 1)}
service.RegisterSimKeyFill(capture)
t.Cleanup(func() { service.RegisterSimKeyFill(old) })
server := ghttp.GetServer(t.Name())
server.SetAddr("127.0.0.1:0")
bindHTTPRoutes(server)
server.BindHandler("POST:/__test/legacy-binding", func(r *ghttp.Request) {
var req v1.SimBaseReq
if err := r.Parse(&req); err != nil {
t.Error(err)
r.Response.WriteStatus(400)
return
}
capture.captured <- req.SimBaseReqJson
})
if err := server.Start(); err != nil {
t.Fatal(err)
}
t.Cleanup(func() { _ = server.Shutdown() })
for _, data := range []string{
`{"operationId":"frozen-operation","submitOnly":true,"operType":"1","apnName":"CMIOT"}`,
`{"operationId":"frozen-operation","queryJobId":"saved-job","operType":"1","apnName":"CMIOT"}`,
`{"operationId":"frozen-operation","recoverOnly":true,"operType":"1","apnName":"CMIOT"}`,
} {
for _, path := range []string{"/sim/change/sim-apn", "/sim/query/sim-apn-capability", "/sim/query/sim-session"} {
input := model.SimBaseReqJson{SimKeys: []*model.SimKey{{ChanKey: "fake", ICCID: "fake-card", MSISDN: "frozen-number"}}, ReqData: json.RawMessage(data)}
response, err := g.Client().ContentJson().Post(context.Background(), fmt.Sprintf("http://127.0.0.1:%d%s", server.GetListenedPort(), path), input)
if err != nil {
t.Fatal(err)
}
body := response.ReadAll()
_ = response.Close()
select {
case got := <-capture.captured:
var wantData, gotData map[string]any
_ = json.Unmarshal(input.ReqData, &wantData)
_ = json.Unmarshal(got.ReqData, &gotData)
wantJSON, _ := json.Marshal(wantData)
gotJSON, _ := json.Marshal(gotData)
if path == "/sim/query/sim-session" {
baselineResponse, err := g.Client().ContentJson().Post(context.Background(), fmt.Sprintf("http://127.0.0.1:%d/__test/legacy-binding", server.GetListenedPort()), input)
if err != nil {
t.Fatal(err)
}
_ = baselineResponse.Close()
baseline := <-capture.captured
if string(got.ReqData) != string(baseline.ReqData) {
t.Fatalf("非APN旧绑定行为改变:got=%q baseline=%q", got.ReqData, baseline.ReqData)
}
} else if sha256.Sum256(wantJSON) != sha256.Sum256(gotJSON) {
t.Fatalf("APN冻结请求摘要改变:path=%s want=%s got=%s", path, wantJSON, gotJSON)
}
if len(got.SimKeys) != 1 || *got.SimKeys[0] != *input.SimKeys[0] {
t.Fatalf("代理生产绑定丢字段:%+v", got)
}
default:
t.Fatalf("未经过生产代理controller:%s", body)
}
}
}
}
func TestBindHTTPRoutesRegistersChannelControlRoutes(t *testing.T) {
server := ghttp.GetServer("proxy-channel-refresh-route-test")
server.SetAddr("127.0.0.1:0")
bindHTTPRoutes(server)
if err := server.Start(); err != nil {
t.Fatalf("start HTTP server: %v", err)
}
t.Cleanup(func() {
if err := server.Shutdown(); err != nil {
t.Errorf("shutdown HTTP server: %v", err)
}
})
want := map[string]bool{
"/internal/channel/refresh": false,
"/internal/channel/remove": false,
"/internal/app-diagnosis/sim/*any": false,
}
for _, route := range server.GetRoutes() {
if _, ok := want[route.Route]; ok && strings.EqualFold(route.Method, "POST") {
want[route.Route] = true
}
}
for route, found := range want {
if !found {
t.Fatalf("POST %s route was not registered: %+v", route, server.GetRoutes())
}
}
}