fix: 修复 CMPP 协议字段容量与版本兼容性
CSS quality / css-quality (push) Has been cancelled

This commit is contained in:
hectorzhao
2026-09-20 15:49:37 +08:00
parent b24cd7c08d
commit 001d5f2cbd
37 changed files with 1933 additions and 295 deletions
@@ -0,0 +1,146 @@
package inbound
import (
"encoding/json"
cmpp "github.com/bigwhite/gocmpp"
"net"
"sync"
"testing"
"time"
)
func TestZeroSequenceReceiptRecoversAndAcknowledgesOverTCP(t *testing.T) {
for _, version := range []cmpp.Type{cmpp.V20, cmpp.V30} {
t.Run(version.String(), func(t *testing.T) {
resetDownstreamRegistry()
defer resetDownstreamRegistry()
listener, err := net.Listen("tcp", "127.0.0.1:0")
if err != nil {
t.Fatal(err)
}
defer listener.Close()
tcp, err := net.Dial("tcp", listener.Addr().String())
if err != nil {
t.Fatal(err)
}
remote, err := listener.Accept()
if err != nil {
t.Fatal(err)
}
server, client := cmpp.NewConn(remote, version), cmpp.NewConn(tcp, version)
server.SetState(cmpp.CONN_AUTHOK)
client.SetState(cmpp.CONN_AUTHOK)
defer server.Close()
defer client.Close()
acknowledged := make(chan downstreamDeliveryLifecycleEvent, 2)
session := &downstreamSession{account: "qa", conn: server, protocol: version.String(), connectionID: "reconnected", mu: &sync.Mutex{}, deliveryReport: func(e downstreamDeliveryLifecycleEvent) {
if e.Kind == "acknowledged" {
acknowledged <- e
}
}}
// Simulate a fresh connection with no in-memory original Submit mapping.
downstreamRegistry.Lock()
downstreamRegistry.byAccount["qa"] = session
downstreamRegistry.Unlock()
var event DownstreamReceipt
if err = json.Unmarshal([]byte(`{"deliveryId":"qa-zero","account":"qa","messageId":"original","submitSequenceId":0,"receiptStatus":"delivered","phoneNumber":"13800138000"}`), &event); err != nil {
t.Fatal(err)
}
result, err := PushReceiptWithResult(event)
if err != nil || !result.Sent {
t.Fatalf("zero cannot recover: %+v %v", result, err)
}
p, err := client.RecvAndUnpackPkt(time.Second)
if err != nil {
t.Fatal(err)
}
var content string
var seq uint32
var msgID uint64
switch pkt := p.(type) {
case *cmpp.Cmpp2DeliverReqPkt:
content, seq, msgID = pkt.MsgContent, pkt.SeqId, pkt.MsgId
case *cmpp.Cmpp3DeliverReqPkt:
content, seq, msgID = pkt.MsgContent, pkt.SeqId, pkt.MsgId
default:
t.Fatalf("bad packet %T", p)
}
var receipt cmpp.CmppReceiptPkt
if err = receipt.UnpackVersion([]byte(content), version); err != nil {
t.Fatal(err)
}
if receipt.MsgId != messageIDFrom("original", 0) || msgID != receipt.MsgId {
t.Fatalf("wrong original Msg_Id: %d", receipt.MsgId)
}
var response cmpp.Packer = &cmpp.Cmpp3DeliverRspPkt{MsgId: msgID, Result: 0}
if version == cmpp.V20 {
response = &cmpp.Cmpp2DeliverRspPkt{MsgId: msgID, Result: 0}
}
if err = client.SendPkt(response, seq); err != nil {
t.Fatal(err)
}
p, err = server.RecvAndUnpackPkt(time.Second)
if err != nil {
t.Fatal(err)
}
switch pkt := p.(type) {
case *cmpp.Cmpp2DeliverRspPkt:
handleDownstreamAcknowledgement(server, pkt.SeqId, pkt.MsgId, uint32(pkt.Result), nil)
case *cmpp.Cmpp3DeliverRspPkt:
handleDownstreamAcknowledgement(server, pkt.SeqId, pkt.MsgId, pkt.Result, nil)
}
select {
case ack := <-acknowledged:
if ack.Result != 0 || ack.MessageID != receipt.MsgId {
t.Fatal("wrong ACK")
}
case <-time.After(time.Second):
t.Fatal("missing ACK")
}
raw, err := json.Marshal(submitRequest{SequenceID: 0})
if err != nil {
t.Fatal(err)
}
var fields map[string]any
json.Unmarshal(raw, &fields)
if value, ok := fields["sequenceId"]; !ok || value != float64(0) {
t.Fatal("zero omitted from Submit callback")
}
})
}
}
func TestDownstreamAckCollisionDoesNotReplacePendingDelivery(t *testing.T) {
conn := &cmpp.Conn{}
session := &downstreamSession{conn: conn}
for _, sequence := range []uint32{^uint32(0), 0} {
first := registerDownstreamAck(session, "first", "a", sequence, 1, time.Now().Add(time.Minute))
if first == nil {
t.Fatal("first registration failed")
}
if registerDownstreamAck(session, "second", "b", sequence, 2, time.Now().Add(time.Minute)) != nil {
t.Fatal("overwrote pending delivery")
}
if takeDownstreamAck(conn, sequence) != first {
t.Fatal("lost original delivery")
}
}
}
func TestAbsentAndZeroSubmitSequenceRemainDistinct(t *testing.T) {
resetDownstreamRegistry()
defer resetDownstreamRegistry()
for _, payload := range []string{`{}`, `{"submitSequenceId":null}`, `{"submitSequenceId":0}`} {
event := DownstreamReceipt{Account: "qa", MessageID: "original"}
if err := json.Unmarshal([]byte(payload), &event); err != nil {
t.Fatal(err)
}
result, err := PushReceiptWithResult(event)
if err != nil {
t.Fatal(err)
}
if result.Retryable != (event.SubmitSequenceID != nil) {
t.Fatalf("missing confused with zero: %s %+v", payload, result)
}
}
}