This commit is contained in:
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user