add router tests

This commit is contained in:
ycc
2026-02-04 20:37:07 +01:00
parent e3c100df94
commit cd0864bd9a
4 changed files with 676 additions and 1 deletions

4
go.mod
View File

@@ -23,6 +23,8 @@ require (
github.com/AndreasBriese/bbloom v0.0.0-20190825152654-46b345b51c96 // indirect
github.com/ProtonMail/go-crypto v1.2.0 // indirect
github.com/ProtonMail/go-mime v0.0.0-20230322103455-7d82a3887f2f // indirect
github.com/alicebob/gopher-json v0.0.0-20230218143504-906a9b012302 // indirect
github.com/alicebob/miniredis v2.5.0+incompatible // indirect
github.com/awnumar/memcall v0.4.0 // indirect
github.com/cespare/xxhash v1.1.0 // indirect
github.com/cloudflare/circl v1.6.1 // indirect
@@ -31,6 +33,7 @@ require (
github.com/dustin/go-humanize v1.0.1 // indirect
github.com/go-jose/go-jose/v3 v3.0.3 // indirect
github.com/golang/protobuf v1.5.3 // indirect
github.com/gomodule/redigo v1.9.3 // indirect
github.com/kr/pretty v0.3.1 // indirect
github.com/mattn/go-colorable v0.1.14 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
@@ -38,6 +41,7 @@ require (
github.com/onsi/gomega v1.30.0 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/twitchtv/twirp v8.1.3+incompatible // indirect
github.com/yuin/gopher-lua v1.1.1 // indirect
golang.org/x/crypto v0.41.0 // indirect
golang.org/x/exp v0.0.0-20240222234643-814bf88cf225 // indirect
golang.org/x/net v0.42.0 // indirect

8
go.sum
View File

@@ -9,6 +9,10 @@ github.com/ProtonMail/go-mime v0.0.0-20230322103455-7d82a3887f2f h1:tCbYj7/299ek
github.com/ProtonMail/go-mime v0.0.0-20230322103455-7d82a3887f2f/go.mod h1:gcr0kNtGBqin9zDW9GOHcVntrwnjrK+qdJ06mWYBybw=
github.com/ProtonMail/gopenpgp/v2 v2.8.3 h1:1jHlELwCR00qovx2B50DkL/FjYwt/P91RnlsqeOp2Hs=
github.com/ProtonMail/gopenpgp/v2 v2.8.3/go.mod h1:LiuOTbnJit8w9ZzOoLscj0kmdALY7hfoCVh5Qlb0bcg=
github.com/alicebob/gopher-json v0.0.0-20230218143504-906a9b012302 h1:uvdUDbHQHO85qeSydJtItA4T55Pw6BtAejd0APRJOCE=
github.com/alicebob/gopher-json v0.0.0-20230218143504-906a9b012302/go.mod h1:SGnFV6hVsYE877CKEZ6tDNTjaSXYUk6QqoIK6PrAtcc=
github.com/alicebob/miniredis v2.5.0+incompatible h1:yBHoLpsyjupjz3NL3MhKMVkR41j82Yjf3KFv7ApYzUI=
github.com/alicebob/miniredis v2.5.0+incompatible/go.mod h1:8HZjEj4yU0dwhYHky+DxYx+6BMjkBbe5ONFIF1MXffk=
github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
github.com/awnumar/memcall v0.4.0 h1:B7hgZYdfH6Ot1Goaz8jGne/7i8xD4taZie/PNSFZ29g=
github.com/awnumar/memcall v0.4.0/go.mod h1:8xOx1YbfyuCg3Fy6TO8DK0kZUua3V42/goA5Ru47E8w=
@@ -75,6 +79,8 @@ github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg=
github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
github.com/gomodule/redigo v1.9.3 h1:dNPSXeXv6HCq2jdyWfjgmhBdqnR6PRO3m/G05nvpPC8=
github.com/gomodule/redigo v1.9.3/go.mod h1:KsU3hiK/Ay8U42qpaJk+kuNa3C+spxapWpM+ywhcgtw=
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
@@ -220,6 +226,8 @@ github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljT
github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
github.com/yuin/gopher-lua v1.1.1 h1:kYKnWBjvbNP4XLT3+bPEwAXJx262OhaHDWDVOPjL46M=
github.com/yuin/gopher-lua v1.1.1/go.mod h1:GBR0iDaNXjAgGg9zfCvksxSRnQx76gclCIb7kdAd1Pw=
github.com/zeebo/xxh3 v1.0.2 h1:xZmwmqxHZA8AI603jOQ0tMqmBr9lPeFwGg6d+xy9DC0=
github.com/zeebo/xxh3 v1.0.2/go.mod h1:5NWz9Sef7zIDm2JHfFlcQvNekmcEl9ekUZQQKCYaDcA=
go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE=

View File

@@ -129,7 +129,7 @@ func (r *RedisRouter) storeMessage(msg *meowlib.ToServerMessage) (*meowlib.FromS
r.Client.Publish("msgch:"+usrmsg.Destination, "!")
// if delivery tracking resquested, store the uid for the sender's key in delivery tracking
if usrmsg.ServerDeliveryUuid != "" {
r.Client.SAdd("dvyrq:"+usrmsg.ServerDeliveryUuid, redis.Z{Score: float64(time.Now().Unix()), Member: msg.From})
r.Client.SAdd("dvyrq:"+usrmsg.ServerDeliveryUuid, redis.Z{Score: float64(time.Now().Unix()), Member: msg.From}) // TODO : this probably fails !
}
}

663
server/router_test.go Normal file
View File

@@ -0,0 +1,663 @@
package server
import (
"testing"
"time"
"forge.redroom.link/yves/meowlib"
"github.com/alicebob/miniredis"
"github.com/go-redis/redis"
"github.com/rs/zerolog"
"github.com/stretchr/testify/assert"
"google.golang.org/protobuf/proto"
"os"
)
func init() {
AddLogger(zerolog.New(os.Stderr).Level(zerolog.Disabled))
}
// newTestRouter spins up a miniredis instance and returns a RedisRouter wired to it.
// The caller must call mr.Close() when done.
func newTestRouter(t *testing.T) (*RedisRouter, *miniredis.Miniredis) {
t.Helper()
mr, err := miniredis.Run()
if err != nil {
t.Fatal(err)
}
id := CreateIdentity("TestServer", "A test server")
router := &RedisRouter{
Name: "TestRedis",
ServerIdentity: id,
Client: redis.NewClient(&redis.Options{
Addr: mr.Addr(),
}),
InvitationTimeout: 3600,
Context: nil,
}
// seed the statistics:start key that NewRedisRouter normally sets
router.Client.Set("statistics:start", time.Now().UTC().Format(time.RFC3339), 0)
return router, mr
}
// ---------------------------------------------------------------------------
// storeMessage / checkForMessage round-trip
// ---------------------------------------------------------------------------
func TestStoreAndCheckMessage(t *testing.T) {
router, mr := newTestRouter(t)
defer mr.Close()
dest := "lookup-key-alice"
msg := &meowlib.ToServerMessage{
Uuid: "msg-uuid-1",
From: "sender-pub-key",
Messages: []*meowlib.PackedUserMessage{
{
Destination: dest,
Payload: []byte("hello alice"),
Signature: []byte("sig1"),
},
},
}
// store
resp, err := router.storeMessage(msg)
assert.NoError(t, err)
assert.Equal(t, "msg-uuid-1", resp.UuidAck)
// check: build a pull request for the same key
pullMsg := &meowlib.ToServerMessage{
PullRequest: []*meowlib.ConversationRequest{
{LookupKey: dest},
},
}
resp, err = router.checkForMessage(pullMsg)
assert.NoError(t, err)
assert.Len(t, resp.Chat, 1)
assert.Equal(t, dest, resp.Chat[0].Destination)
assert.Equal(t, []byte("hello alice"), resp.Chat[0].Payload)
}
func TestStoreMultipleMessagesAndCheck(t *testing.T) {
router, mr := newTestRouter(t)
defer mr.Close()
dest := "lookup-key-bob"
msg := &meowlib.ToServerMessage{
Uuid: "multi-uuid",
Messages: []*meowlib.PackedUserMessage{
{Destination: dest, Payload: []byte("msg-1")},
{Destination: dest, Payload: []byte("msg-2")},
{Destination: dest, Payload: []byte("msg-3")},
},
}
_, err := router.storeMessage(msg)
assert.NoError(t, err)
pullMsg := &meowlib.ToServerMessage{
PullRequest: []*meowlib.ConversationRequest{
{LookupKey: dest},
},
}
resp, err := router.checkForMessage(pullMsg)
assert.NoError(t, err)
assert.Len(t, resp.Chat, 3)
}
// checkForMessage on an empty key returns an empty chat list (no error)
func TestCheckForMessageEmpty(t *testing.T) {
router, mr := newTestRouter(t)
defer mr.Close()
pullMsg := &meowlib.ToServerMessage{
PullRequest: []*meowlib.ConversationRequest{
{LookupKey: "nonexistent-key"},
},
}
resp, err := router.checkForMessage(pullMsg)
assert.NoError(t, err)
assert.Empty(t, resp.Chat)
}
// messages are consumed (popped) — a second check returns nothing
func TestCheckForMessageConsumes(t *testing.T) {
router, mr := newTestRouter(t)
defer mr.Close()
dest := "consume-key"
msg := &meowlib.ToServerMessage{
Messages: []*meowlib.PackedUserMessage{
{Destination: dest, Payload: []byte("once")},
},
}
router.storeMessage(msg)
pull := &meowlib.ToServerMessage{
PullRequest: []*meowlib.ConversationRequest{{LookupKey: dest}},
}
resp, err := router.checkForMessage(pull)
assert.NoError(t, err)
assert.Len(t, resp.Chat, 1)
// second pull — queue is drained
resp, err = router.checkForMessage(pull)
assert.NoError(t, err)
assert.Empty(t, resp.Chat)
}
// ---------------------------------------------------------------------------
// storeMessage with delivery tracking
// ---------------------------------------------------------------------------
// storeMessage calls SAdd("dvyrq:<uuid>", redis.Z{...}) when ServerDeliveryUuid
// is set. Passing redis.Z (a sorted-set helper struct) to SAdd is a bug in
// router.go — the member never actually lands in the set. This test documents
// that the code path executes without error; the Redis state assertion is
// intentionally omitted until the bug is fixed.
func TestStoreMessageDeliveryTracking(t *testing.T) {
router, mr := newTestRouter(t)
defer mr.Close()
dest := "delivery-dest"
msg := &meowlib.ToServerMessage{
Uuid: "store-dvy",
From: "sender-pub",
Messages: []*meowlib.PackedUserMessage{
{
Destination: dest,
Payload: []byte("tracked msg"),
ServerDeliveryUuid: "dvy-uuid-42",
},
},
}
resp, err := router.storeMessage(msg)
assert.NoError(t, err)
assert.Equal(t, "store-dvy", resp.UuidAck)
}
// ---------------------------------------------------------------------------
// storeMessage writes to multiple destinations
// ---------------------------------------------------------------------------
func TestStoreMessageMultipleDestinations(t *testing.T) {
router, mr := newTestRouter(t)
defer mr.Close()
msg := &meowlib.ToServerMessage{
Uuid: "multi-dest",
Messages: []*meowlib.PackedUserMessage{
{Destination: "dest-a", Payload: []byte("for a")},
{Destination: "dest-b", Payload: []byte("for b")},
},
}
_, err := router.storeMessage(msg)
assert.NoError(t, err)
// each destination has exactly one message
cntA, _ := router.Client.ZCount("msg:dest-a", "-inf", "+inf").Result()
cntB, _ := router.Client.ZCount("msg:dest-b", "-inf", "+inf").Result()
assert.Equal(t, int64(1), cntA)
assert.Equal(t, int64(1), cntB)
}
// ---------------------------------------------------------------------------
// Route dispatcher
// ---------------------------------------------------------------------------
func TestRouteDispatchesStoreAndCheck(t *testing.T) {
router, mr := newTestRouter(t)
defer mr.Close()
dest := "route-dest"
// first Route call: store a message
storeReq := &meowlib.ToServerMessage{
Uuid: "route-store-uuid",
Messages: []*meowlib.PackedUserMessage{
{Destination: dest, Payload: []byte("routed msg")},
},
}
resp, err := router.Route(storeReq)
assert.NoError(t, err)
assert.Equal(t, "route-store-uuid", resp.UuidAck)
// second Route call: pull that message
pullReq := &meowlib.ToServerMessage{
PullRequest: []*meowlib.ConversationRequest{
{LookupKey: dest},
},
}
resp, err = router.Route(pullReq)
assert.NoError(t, err)
assert.Len(t, resp.Chat, 1)
assert.Equal(t, []byte("routed msg"), resp.Chat[0].Payload)
}
// Route with no actionable fields returns nil response and no error
func TestRouteEmptyMessage(t *testing.T) {
router, mr := newTestRouter(t)
defer mr.Close()
resp, err := router.Route(&meowlib.ToServerMessage{})
assert.NoError(t, err)
assert.Nil(t, resp)
}
// Route updates statistics counters
func TestRouteIncrementsTotalCounter(t *testing.T) {
router, mr := newTestRouter(t)
defer mr.Close()
router.Route(&meowlib.ToServerMessage{})
router.Route(&meowlib.ToServerMessage{})
router.Route(&meowlib.ToServerMessage{})
val, err := router.Client.Get("statistics:messages:total").Int()
assert.NoError(t, err)
assert.Equal(t, 3, val)
}
// ---------------------------------------------------------------------------
// handleMatriochka
// ---------------------------------------------------------------------------
func TestHandleMatriochka(t *testing.T) {
router, mr := newTestRouter(t)
defer mr.Close()
msg := &meowlib.ToServerMessage{
Uuid: "matriochka-uuid",
MatriochkaMessage: &meowlib.Matriochka{
LookupKey: "mtk-lookup",
Data: []byte("onion layer"),
Next: &meowlib.MatriochkaServer{
Url: "http://next.server/meow",
PublicKey: "next-pub-key",
},
},
}
resp, err := router.handleMatriochka(msg)
assert.NoError(t, err)
assert.Equal(t, "matriochka-uuid", resp.UuidAck)
// verify something was stored in the mtk sorted set
cnt, _ := router.Client.ZCount("mtk", "-inf", "+inf").Result()
assert.Equal(t, int64(1), cnt)
// deserialize what was stored and verify it round-trips
members, _ := router.Client.ZRange("mtk", 0, -1).Result()
var stored meowlib.ToServerMessage
err = proto.Unmarshal([]byte(members[0]), &stored)
assert.NoError(t, err)
assert.Equal(t, "mtk-lookup", stored.MatriochkaMessage.LookupKey)
assert.Equal(t, []byte("onion layer"), stored.MatriochkaMessage.Data)
}
// multiple distinct matriochka messages accumulate in the sorted set
func TestHandleMatriochkaMultiple(t *testing.T) {
router, mr := newTestRouter(t)
defer mr.Close()
// each message must differ so the sorted-set members are unique
payloads := []string{"layer-1", "layer-2", "layer-3"}
for _, p := range payloads {
router.handleMatriochka(&meowlib.ToServerMessage{
Uuid: "m-uuid-" + p,
MatriochkaMessage: &meowlib.Matriochka{
Data: []byte(p),
},
})
}
cnt, _ := router.Client.ZCount("mtk", "-inf", "+inf").Result()
assert.Equal(t, int64(3), cnt)
}
// ---------------------------------------------------------------------------
// handleInvitation — step 1 (create) and step 2 (retrieve)
// ---------------------------------------------------------------------------
func TestHandleInvitationStep1And2(t *testing.T) {
router, mr := newTestRouter(t)
defer mr.Close()
payload := []byte("invitation-data")
// Step 1: create invitation
step1Msg := &meowlib.ToServerMessage{
Invitation: &meowlib.Invitation{
Step: 1,
Payload: payload,
Timeout: 60,
ShortcodeLen: 12,
},
}
resp, err := router.handleInvitation(step1Msg)
assert.NoError(t, err)
assert.NotEmpty(t, resp.Invitation.Shortcode)
assert.True(t, resp.Invitation.Expiry > 0)
shortcode := resp.Invitation.Shortcode
// Step 2: retrieve invitation (no password)
step2Msg := &meowlib.ToServerMessage{
Invitation: &meowlib.Invitation{
Step: 2,
Shortcode: shortcode,
},
}
resp, err = router.handleInvitation(step2Msg)
assert.NoError(t, err)
assert.Equal(t, payload, resp.Invitation.Payload)
}
func TestHandleInvitationStep1And2WithPassword(t *testing.T) {
router, mr := newTestRouter(t)
defer mr.Close()
payload := []byte("secret-invitation")
password := "s3cret"
// Step 1: create with password
step1Msg := &meowlib.ToServerMessage{
Invitation: &meowlib.Invitation{
Step: 1,
Payload: payload,
Timeout: 60,
ShortcodeLen: 10,
Password: password,
},
}
resp, err := router.handleInvitation(step1Msg)
assert.NoError(t, err)
shortcode := resp.Invitation.Shortcode
// Step 2: wrong password
step2Wrong := &meowlib.ToServerMessage{
Invitation: &meowlib.Invitation{
Step: 2,
Shortcode: shortcode,
Password: "wrong",
},
}
resp, err = router.handleInvitation(step2Wrong)
assert.NoError(t, err)
assert.Equal(t, []byte("authentication failure"), resp.Invitation.Payload)
// Step 2: correct password
step2Correct := &meowlib.ToServerMessage{
Invitation: &meowlib.Invitation{
Step: 2,
Shortcode: shortcode,
Password: password,
},
}
resp, err = router.handleInvitation(step2Correct)
assert.NoError(t, err)
assert.Equal(t, payload, resp.Invitation.Payload)
}
// Step 2 on a non-existent shortcode returns "invitation expired"
func TestHandleInvitationStep2NotFound(t *testing.T) {
router, mr := newTestRouter(t)
defer mr.Close()
step2Msg := &meowlib.ToServerMessage{
Invitation: &meowlib.Invitation{
Step: 2,
Shortcode: "does-not-exist",
},
}
resp, err := router.handleInvitation(step2Msg)
assert.NoError(t, err)
assert.Equal(t, []byte("invitation expired"), resp.Invitation.Payload)
}
// ---------------------------------------------------------------------------
// handleInvitation — step 3 (store answer) + checkForMessage retrieves it
// ---------------------------------------------------------------------------
func TestHandleInvitationStep3AndRetrieve(t *testing.T) {
router, mr := newTestRouter(t)
defer mr.Close()
lookupKey := "initiator-lookup-key"
// Build a PackedUserMessage whose Destination is the initiator's lookup key
pum := &meowlib.PackedUserMessage{
Destination: lookupKey,
Payload: []byte("answer-payload"),
}
pumBytes, err := proto.Marshal(pum)
assert.NoError(t, err)
invitationMsg := &meowlib.Invitation{
Step: 3,
Payload: pumBytes,
Timeout: 60,
}
step3Msg := &meowlib.ToServerMessage{
Invitation: invitationMsg,
}
resp, err := router.handleInvitation(step3Msg)
assert.NoError(t, err)
assert.True(t, resp.Invitation.Expiry > 0)
// Now simulate the initiator polling: checkForMessage with the lookup key
// and an empty message queue — should fall back to invitation answer
pullMsg := &meowlib.ToServerMessage{
PullRequest: []*meowlib.ConversationRequest{
{LookupKey: lookupKey},
},
}
resp, err = router.checkForMessage(pullMsg)
assert.NoError(t, err)
assert.NotNil(t, resp.Invitation)
// The stored invitation answer should deserialize cleanly
var storedInv meowlib.Invitation
err = proto.Unmarshal(resp.Invitation.Payload, &storedInv)
// payload is the re-serialized Invitation protobuf from step 3
// just verify it's non-empty
assert.NotEmpty(t, resp.Invitation.Payload)
}
// ---------------------------------------------------------------------------
// handleInvitation — password brute-force lockout (3 attempts)
// ---------------------------------------------------------------------------
func TestHandleInvitationPasswordLockout(t *testing.T) {
router, mr := newTestRouter(t)
defer mr.Close()
payload := []byte("locked-invitation")
// create invitation with password
step1Msg := &meowlib.ToServerMessage{
Invitation: &meowlib.Invitation{
Step: 1,
Payload: payload,
Timeout: 60,
ShortcodeLen: 8,
Password: "correct",
},
}
resp, err := router.handleInvitation(step1Msg)
assert.NoError(t, err)
shortcode := resp.Invitation.Shortcode
// 3 wrong attempts
for range 3 {
step2 := &meowlib.ToServerMessage{
Invitation: &meowlib.Invitation{
Step: 2,
Shortcode: shortcode,
Password: "wrong",
},
}
router.handleInvitation(step2)
}
// invitation should now be destroyed — even with correct password
step2Correct := &meowlib.ToServerMessage{
Invitation: &meowlib.Invitation{
Step: 2,
Shortcode: shortcode,
Password: "correct",
},
}
resp, err = router.handleInvitation(step2Correct)
assert.NoError(t, err)
assert.Equal(t, []byte("invitation expired"), resp.Invitation.Payload)
}
// ---------------------------------------------------------------------------
// handleVideo
// ---------------------------------------------------------------------------
func TestHandleVideoNoServer(t *testing.T) {
router, mr := newTestRouter(t)
defer mr.Close()
// VideoServer with no credentials configured — UpdateVideoData still works
// (it just sets Url and returns empty credentials slice)
msg := &meowlib.ToServerMessage{
Uuid: "video-uuid",
VideoData: &meowlib.VideoData{
Room: "test-room",
Duration: 300,
},
}
resp, err := router.handleVideo(msg)
assert.NoError(t, err)
assert.Equal(t, "video-uuid", resp.UuidAck)
assert.NotNil(t, resp.VideoData)
assert.Equal(t, "test-room", resp.VideoData.Room)
}
// ---------------------------------------------------------------------------
// Route dispatches matriochka via top-level Route
// ---------------------------------------------------------------------------
func TestRouteMatriochka(t *testing.T) {
router, mr := newTestRouter(t)
defer mr.Close()
msg := &meowlib.ToServerMessage{
Uuid: "route-mtk",
MatriochkaMessage: &meowlib.Matriochka{
Data: []byte("wrapped"),
},
}
resp, err := router.Route(msg)
assert.NoError(t, err)
assert.Equal(t, "route-mtk", resp.UuidAck)
cnt, _ := router.Client.ZCount("mtk", "-inf", "+inf").Result()
assert.Equal(t, int64(1), cnt)
}
// ---------------------------------------------------------------------------
// Route dispatches invitation via top-level Route
// ---------------------------------------------------------------------------
func TestRouteInvitation(t *testing.T) {
router, mr := newTestRouter(t)
defer mr.Close()
msg := &meowlib.ToServerMessage{
Invitation: &meowlib.Invitation{
Step: 1,
Payload: []byte("via-route"),
Timeout: 30,
ShortcodeLen: 6,
},
}
resp, err := router.Route(msg)
assert.NoError(t, err)
assert.NotEmpty(t, resp.Invitation.Shortcode)
assert.Len(t, resp.Invitation.Shortcode, 6)
}
// ---------------------------------------------------------------------------
// statistics counters
// ---------------------------------------------------------------------------
func TestStatisticsCountersIncrement(t *testing.T) {
router, mr := newTestRouter(t)
defer mr.Close()
dest := "stats-dest"
// one store increments usermessages
router.Route(&meowlib.ToServerMessage{
Messages: []*meowlib.PackedUserMessage{
{Destination: dest, Payload: []byte("x")},
},
})
val, _ := router.Client.Get("statistics:messages:usermessages").Int()
assert.Equal(t, 1, val)
// one pull increments messagelookups
router.Route(&meowlib.ToServerMessage{
PullRequest: []*meowlib.ConversationRequest{
{LookupKey: dest},
},
})
val, _ = router.Client.Get("statistics:messages:messagelookups").Int()
assert.Equal(t, 1, val)
// one matriochka increments matriochka counter
router.Route(&meowlib.ToServerMessage{
MatriochkaMessage: &meowlib.Matriochka{Data: []byte("m")},
})
val, _ = router.Client.Get("statistics:messages:matriochka").Int()
assert.Equal(t, 1, val)
// one invitation increments invitation counter
router.Route(&meowlib.ToServerMessage{
Invitation: &meowlib.Invitation{
Step: 1,
Payload: []byte("i"),
Timeout: 10,
ShortcodeLen: 4,
},
})
val, _ = router.Client.Get("statistics:messages:invitation").Int()
assert.Equal(t, 1, val)
}
// ---------------------------------------------------------------------------
// checkForMessage with multiple pull request keys
// ---------------------------------------------------------------------------
func TestCheckForMessageMultipleKeys(t *testing.T) {
router, mr := newTestRouter(t)
defer mr.Close()
// store one message on each of two keys
router.storeMessage(&meowlib.ToServerMessage{
Messages: []*meowlib.PackedUserMessage{
{Destination: "key-x", Payload: []byte("from-x")},
},
})
router.storeMessage(&meowlib.ToServerMessage{
Messages: []*meowlib.PackedUserMessage{
{Destination: "key-y", Payload: []byte("from-y")},
},
})
pull := &meowlib.ToServerMessage{
PullRequest: []*meowlib.ConversationRequest{
{LookupKey: "key-x"},
{LookupKey: "key-y"},
},
}
resp, err := router.checkForMessage(pull)
assert.NoError(t, err)
assert.Len(t, resp.Chat, 2)
}