package cmppadapter import ( "bytes" "crypto/md5" "fmt" "log" "net" "testing" "time" cmpp "github.com/bigwhite/gocmpp" cmpputils "github.com/bigwhite/gocmpp/utils" ) const ( spikeUser = "900001" spikePassword = "888888" ) func TestGocmppConnectSubmitAndActiveTest(t *testing.T) { addr := reserveTCPAddr(t) handlers := []cmpp.Handler{ cmpp.HandlerFunc(handleSpikeLogin), cmpp.HandlerFunc(handleSpikeSubmit), } go func() { err := cmpp.ListenAndServe(addr, cmpp.V30, 2*time.Second, 3, nil, handlers...) if err != nil { log.Printf("gocmpp spike server stopped: %v", err) } }() time.Sleep(300 * time.Millisecond) client := cmpp.NewClient(cmpp.V30) defer client.Disconnect() if err := client.Connect(addr, spikeUser, spikePassword, 2*time.Second); err != nil { t.Fatalf("connect gocmpp server: %v", err) } content, err := cmpputils.Utf8ToUcs2("测试gocmpp submit") if err != nil { t.Fatalf("encode submit content: %v", err) } _, err = client.SendReqPkt(&cmpp.Cmpp3SubmitReqPkt{ PkTotal: 1, PkNumber: 1, RegisteredDelivery: 1, MsgLevel: 1, ServiceId: "test", FeeUserType: 2, FeeTerminalId: "13500002696", FeeTerminalType: 0, MsgFmt: 8, MsgSrc: spikeUser, FeeType: "02", FeeCode: "10", ValidTime: "151105131555101+", AtTime: "", SrcId: spikeUser, DestUsrTl: 1, DestTerminalId: []string{"13500002696"}, DestTerminalType: 0, MsgLength: uint8(len(content)), MsgContent: content, }) if err != nil { t.Fatalf("send submit: %v", err) } submitRsp := recvUntil[*cmpp.Cmpp3SubmitRspPkt](t, client, 2*time.Second) if submitRsp.Result != 0 { t.Fatalf("unexpected submit result: %d", submitRsp.Result) } if submitRsp.MsgId == 0 { t.Fatal("expected gateway msg id") } _, err = client.SendReqPkt(&cmpp.CmppActiveTestReqPkt{}) if err != nil { t.Fatalf("send active test: %v", err) } _ = recvUntil[*cmpp.CmppActiveTestRspPkt](t, client, 2*time.Second) } func TestGocmppDeliverReceiptPackAndUnpack(t *testing.T) { receipt := &cmpp.CmppReceiptPkt{ MsgId: 12878564852733378560, Stat: "DELIVRD", SubmitTime: "2607010900", DoneTime: "2607010901", DestTerminalId: "13500002696", SmscSequence: 42, } receiptBytes, err := receipt.Pack() if err != nil { t.Fatalf("pack receipt: %v", err) } deliver := &cmpp.Cmpp3DeliverReqPkt{ MsgId: 12878564852733378560, DestId: "106900000000", ServiceId: "test", TpPid: 0, TpUdhi: 0, MsgFmt: 0, SrcTerminalId: "13500002696", SrcTerminalType: 0, RegisterDelivery: 1, MsgLength: uint8(cmpp.CmppReceiptPktLen), MsgContent: string(receiptBytes), } data, err := deliver.Pack(1001) if err != nil { t.Fatalf("pack deliver receipt: %v", err) } var unpacked cmpp.Cmpp3DeliverReqPkt if err := unpacked.Unpack(data[8:]); err != nil { t.Fatalf("unpack deliver receipt: %v", err) } var gotReceipt cmpp.CmppReceiptPkt if err := gotReceipt.Unpack([]byte(unpacked.MsgContent)); err != nil { t.Fatalf("unpack receipt content: %v", err) } if gotReceipt.Stat != "DELIVRD" || gotReceipt.SmscSequence != 42 { t.Fatalf("unexpected receipt payload: %+v", &gotReceipt) } } func reserveTCPAddr(t *testing.T) string { t.Helper() listener, err := net.Listen("tcp", "127.0.0.1:0") if err != nil { t.Fatalf("reserve tcp addr: %v", err) } addr := listener.Addr().String() if err := listener.Close(); err != nil { t.Fatalf("close reserved listener: %v", err) } return addr } func recvUntil[T any](t *testing.T, client *cmpp.Client, timeout time.Duration) T { t.Helper() deadline := time.Now().Add(timeout) for time.Now().Before(deadline) { packet, err := client.RecvAndUnpackPkt(200 * time.Millisecond) if err != nil { continue } if typed, ok := packet.(T); ok { return typed } } var zero T t.Fatalf("timed out waiting for %T", zero) return zero } func handleSpikeLogin(response *cmpp.Response, packet *cmpp.Packet, logger *log.Logger) (bool, error) { req, ok := packet.Packer.(*cmpp.CmppConnReqPkt) if !ok { return true, nil } resp := response.Packer.(*cmpp.Cmpp3ConnRspPkt) resp.Version = 0x30 if req.SrcAddr != cmpputils.OctetString(spikeUser, 6) { resp.Status = uint32(cmpp.ErrnoConnInvalidSrcAddr) return false, cmpp.ConnRspStatusErrMap[cmpp.ErrnoConnInvalidSrcAddr] } authSrc := md5.Sum(bytes.Join([][]byte{ []byte(cmpputils.OctetString(spikeUser, 6)), make([]byte, 9), []byte(spikePassword), []byte(cmpputils.TimeStamp2Str(req.Timestamp)), }, nil)) if req.AuthSrc != string(authSrc[:]) { resp.Status = uint32(cmpp.ErrnoConnAuthFailed) return false, cmpp.ConnRspStatusErrMap[cmpp.ErrnoConnAuthFailed] } authIsmg := md5.Sum(bytes.Join([][]byte{{byte(resp.Status)}, authSrc[:], []byte(spikePassword)}, nil)) resp.AuthIsmg = string(authIsmg[:]) return false, nil } func handleSpikeSubmit(response *cmpp.Response, packet *cmpp.Packet, logger *log.Logger) (bool, error) { req, ok := packet.Packer.(*cmpp.Cmpp3SubmitReqPkt) if !ok { return true, nil } if req.DestUsrTl == 0 || len(req.DestTerminalId) == 0 { return false, fmt.Errorf("missing submit destination") } resp := response.Packer.(*cmpp.Cmpp3SubmitRspPkt) resp.MsgId = 12878564852733378560 resp.Result = 0 return false, nil }