147 lines
4.7 KiB
Go
147 lines
4.7 KiB
Go
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)
|
|
}
|
|
}
|
|
}
|