2019-06-24 09:13:25 +00:00
|
|
|
package xmpp
|
2019-06-13 15:22:39 +00:00
|
|
|
|
|
|
|
import (
|
|
|
|
"bytes"
|
2019-10-29 09:34:33 +00:00
|
|
|
"context"
|
2019-06-13 15:22:39 +00:00
|
|
|
"encoding/xml"
|
|
|
|
"testing"
|
2019-10-29 10:37:49 +00:00
|
|
|
"time"
|
2019-06-26 15:14:52 +00:00
|
|
|
|
|
|
|
"gosrc.io/xmpp/stanza"
|
2019-06-13 15:22:39 +00:00
|
|
|
)
|
|
|
|
|
2019-06-14 07:37:38 +00:00
|
|
|
// ============================================================================
|
|
|
|
// Test route & matchers
|
2019-06-13 15:22:39 +00:00
|
|
|
|
2019-10-29 10:02:41 +00:00
|
|
|
func TestIQResultRoutes(t *testing.T) {
|
2019-10-29 09:34:33 +00:00
|
|
|
t.Parallel()
|
|
|
|
router := NewRouter()
|
2019-10-29 10:37:49 +00:00
|
|
|
conn := NewSenderMock()
|
2019-10-29 09:34:33 +00:00
|
|
|
|
2019-10-29 10:02:41 +00:00
|
|
|
if router.IQResultRoutes == nil {
|
2019-10-29 09:34:33 +00:00
|
|
|
t.Fatal("NewRouter does not initialize isResultRoutes")
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check if the IQ handler was called
|
2019-10-29 10:37:49 +00:00
|
|
|
ctx, cancel := context.WithTimeout(context.Background(), time.Millisecond*100)
|
|
|
|
defer cancel()
|
|
|
|
iq := stanza.NewIQ(stanza.Attrs{Type: stanza.IQTypeResult, Id: "1234"})
|
|
|
|
res := router.NewIQResultRoute(ctx, "1234")
|
|
|
|
go router.route(conn, iq)
|
|
|
|
select {
|
|
|
|
case <-ctx.Done():
|
2019-10-29 09:34:33 +00:00
|
|
|
t.Fatal("IQ result was not matched")
|
2019-10-29 10:37:49 +00:00
|
|
|
case <-res:
|
|
|
|
// Success
|
2019-10-29 09:34:33 +00:00
|
|
|
}
|
|
|
|
|
2019-10-29 10:37:49 +00:00
|
|
|
// The match must only happen once, so the id should no longer be in IQResultRoutes
|
|
|
|
if _, ok := router.IQResultRoutes[iq.Attrs.Id]; ok {
|
|
|
|
t.Fatal("IQ ID was not removed from the route map")
|
2019-10-29 09:34:33 +00:00
|
|
|
}
|
|
|
|
|
2019-10-29 10:37:49 +00:00
|
|
|
// Check other IQ does not matcah
|
|
|
|
ctx, cancel = context.WithTimeout(context.Background(), time.Millisecond*100)
|
|
|
|
defer cancel()
|
|
|
|
iq.Attrs.Id = "4321"
|
|
|
|
res = router.NewIQResultRoute(ctx, "1234")
|
|
|
|
go router.route(conn, iq)
|
|
|
|
select {
|
|
|
|
case <-ctx.Done():
|
|
|
|
// Success
|
|
|
|
case <-res:
|
|
|
|
t.Fatal("IQ result with wrong ID was matched")
|
2019-10-29 09:34:33 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-06-13 15:22:39 +00:00
|
|
|
func TestNameMatcher(t *testing.T) {
|
2019-06-24 09:13:25 +00:00
|
|
|
router := NewRouter()
|
2019-06-26 15:14:52 +00:00
|
|
|
router.HandleFunc("message", func(s Sender, p stanza.Packet) {
|
2019-06-14 07:37:38 +00:00
|
|
|
_ = s.SendRaw(successFlag)
|
2019-06-13 15:22:39 +00:00
|
|
|
})
|
|
|
|
|
|
|
|
// Check that a message packet is properly matched
|
2019-06-14 07:37:38 +00:00
|
|
|
conn := NewSenderMock()
|
2019-06-26 15:14:52 +00:00
|
|
|
msg := stanza.NewMessage(stanza.Attrs{Type: stanza.MessageTypeChat, To: "test@localhost", Id: "1"})
|
2019-06-13 15:22:39 +00:00
|
|
|
msg.Body = "Hello"
|
2019-06-24 09:13:25 +00:00
|
|
|
router.route(conn, msg)
|
2019-06-14 07:37:38 +00:00
|
|
|
if conn.String() != successFlag {
|
2019-06-13 15:22:39 +00:00
|
|
|
t.Error("Message was not matched and routed properly")
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check that an IQ packet is not matched
|
2019-06-14 07:37:38 +00:00
|
|
|
conn = NewSenderMock()
|
2019-06-26 15:14:52 +00:00
|
|
|
iq := stanza.NewIQ(stanza.Attrs{Type: stanza.IQTypeGet, To: "localhost", Id: "1"})
|
|
|
|
iq.Payload = &stanza.DiscoInfo{}
|
2019-06-24 09:13:25 +00:00
|
|
|
router.route(conn, iq)
|
2019-06-14 07:37:38 +00:00
|
|
|
if conn.String() == successFlag {
|
2019-06-13 15:22:39 +00:00
|
|
|
t.Error("IQ should not have been matched and routed")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestIQNSMatcher(t *testing.T) {
|
2019-06-24 09:13:25 +00:00
|
|
|
router := NewRouter()
|
2019-06-13 15:22:39 +00:00
|
|
|
router.NewRoute().
|
2019-06-26 15:14:52 +00:00
|
|
|
IQNamespaces(stanza.NSDiscoInfo, stanza.NSDiscoItems).
|
|
|
|
HandlerFunc(func(s Sender, p stanza.Packet) {
|
2019-06-14 07:37:38 +00:00
|
|
|
_ = s.SendRaw(successFlag)
|
2019-06-13 15:22:39 +00:00
|
|
|
})
|
|
|
|
|
|
|
|
// Check that an IQ with proper namespace does match
|
2019-06-14 07:37:38 +00:00
|
|
|
conn := NewSenderMock()
|
2019-06-26 15:14:52 +00:00
|
|
|
iqDisco := stanza.NewIQ(stanza.Attrs{Type: stanza.IQTypeGet, To: "localhost", Id: "1"})
|
2019-06-22 09:13:33 +00:00
|
|
|
// TODO: Add a function to generate payload with proper namespace initialisation
|
2019-06-26 15:14:52 +00:00
|
|
|
iqDisco.Payload = &stanza.DiscoInfo{
|
2019-06-13 15:22:39 +00:00
|
|
|
XMLName: xml.Name{
|
2019-06-26 15:14:52 +00:00
|
|
|
Space: stanza.NSDiscoInfo,
|
2019-06-13 15:22:39 +00:00
|
|
|
Local: "query",
|
2019-06-19 08:21:57 +00:00
|
|
|
}}
|
2019-06-24 09:13:25 +00:00
|
|
|
router.route(conn, iqDisco)
|
2019-06-14 07:37:38 +00:00
|
|
|
if conn.String() != successFlag {
|
2019-06-13 15:22:39 +00:00
|
|
|
t.Errorf("IQ should have been matched and routed: %v", iqDisco)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check that another namespace is not matched
|
2019-06-14 07:37:38 +00:00
|
|
|
conn = NewSenderMock()
|
2019-06-26 15:14:52 +00:00
|
|
|
iqVersion := stanza.NewIQ(stanza.Attrs{Type: stanza.IQTypeGet, To: "localhost", Id: "1"})
|
2019-06-22 09:13:33 +00:00
|
|
|
// TODO: Add a function to generate payload with proper namespace initialisation
|
2019-06-26 15:14:52 +00:00
|
|
|
iqVersion.Payload = &stanza.DiscoInfo{
|
2019-06-13 15:22:39 +00:00
|
|
|
XMLName: xml.Name{
|
|
|
|
Space: "jabber:iq:version",
|
|
|
|
Local: "query",
|
2019-06-19 08:21:57 +00:00
|
|
|
}}
|
2019-06-24 09:13:25 +00:00
|
|
|
router.route(conn, iqVersion)
|
2019-06-14 07:37:38 +00:00
|
|
|
if conn.String() == successFlag {
|
2019-06-13 15:22:39 +00:00
|
|
|
t.Errorf("IQ should not have been matched and routed: %v", iqVersion)
|
|
|
|
}
|
|
|
|
}
|
2019-06-14 07:37:38 +00:00
|
|
|
|
2019-06-21 14:48:13 +00:00
|
|
|
func TestTypeMatcher(t *testing.T) {
|
2019-06-24 09:13:25 +00:00
|
|
|
router := NewRouter()
|
2019-06-21 14:48:13 +00:00
|
|
|
router.NewRoute().
|
|
|
|
StanzaType("normal").
|
2019-06-26 15:14:52 +00:00
|
|
|
HandlerFunc(func(s Sender, p stanza.Packet) {
|
2019-06-21 14:48:13 +00:00
|
|
|
_ = s.SendRaw(successFlag)
|
|
|
|
})
|
|
|
|
|
|
|
|
// Check that a packet with the proper type matches
|
|
|
|
conn := NewSenderMock()
|
2019-06-26 15:14:52 +00:00
|
|
|
message := stanza.NewMessage(stanza.Attrs{Type: "normal", To: "test@localhost", Id: "1"})
|
2019-06-21 14:48:13 +00:00
|
|
|
message.Body = "hello"
|
2019-06-24 09:13:25 +00:00
|
|
|
router.route(conn, message)
|
2019-06-21 14:48:13 +00:00
|
|
|
|
|
|
|
if conn.String() != successFlag {
|
|
|
|
t.Errorf("'normal' message should have been matched and routed: %v", message)
|
|
|
|
}
|
|
|
|
|
|
|
|
// We should match on default type 'normal' for message without a type
|
|
|
|
conn = NewSenderMock()
|
2019-06-26 15:14:52 +00:00
|
|
|
message = stanza.NewMessage(stanza.Attrs{To: "test@localhost", Id: "1"})
|
2019-06-21 14:48:13 +00:00
|
|
|
message.Body = "hello"
|
2019-06-24 09:13:25 +00:00
|
|
|
router.route(conn, message)
|
2019-06-21 14:48:13 +00:00
|
|
|
|
|
|
|
if conn.String() != successFlag {
|
|
|
|
t.Errorf("message should have been matched and routed: %v", message)
|
|
|
|
}
|
|
|
|
|
|
|
|
// We do not match on other types
|
|
|
|
conn = NewSenderMock()
|
2019-06-26 15:14:52 +00:00
|
|
|
iqVersion := stanza.NewIQ(stanza.Attrs{Type: "get", From: "service.localhost", To: "test@localhost", Id: "1"})
|
|
|
|
iqVersion.Payload = &stanza.DiscoInfo{
|
2019-06-21 14:48:13 +00:00
|
|
|
XMLName: xml.Name{
|
|
|
|
Space: "jabber:iq:version",
|
|
|
|
Local: "query",
|
|
|
|
}}
|
2019-06-24 09:13:25 +00:00
|
|
|
router.route(conn, iqVersion)
|
2019-06-21 14:48:13 +00:00
|
|
|
|
|
|
|
if conn.String() == successFlag {
|
|
|
|
t.Errorf("iq get should not have been matched and routed: %v", iqVersion)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestCompositeMatcher(t *testing.T) {
|
2019-06-24 09:13:25 +00:00
|
|
|
router := NewRouter()
|
2019-06-21 14:48:13 +00:00
|
|
|
router.NewRoute().
|
|
|
|
IQNamespaces("jabber:iq:version").
|
|
|
|
StanzaType("get").
|
2019-06-26 15:14:52 +00:00
|
|
|
HandlerFunc(func(s Sender, p stanza.Packet) {
|
2019-06-21 14:48:13 +00:00
|
|
|
_ = s.SendRaw(successFlag)
|
|
|
|
})
|
|
|
|
|
|
|
|
// Data set
|
2019-06-26 15:14:52 +00:00
|
|
|
getVersionIq := stanza.NewIQ(stanza.Attrs{Type: "get", From: "service.localhost", To: "test@localhost", Id: "1"})
|
|
|
|
getVersionIq.Payload = &stanza.Version{
|
2019-06-21 14:48:13 +00:00
|
|
|
XMLName: xml.Name{
|
|
|
|
Space: "jabber:iq:version",
|
|
|
|
Local: "query",
|
|
|
|
}}
|
|
|
|
|
2019-06-26 15:14:52 +00:00
|
|
|
setVersionIq := stanza.NewIQ(stanza.Attrs{Type: "set", From: "service.localhost", To: "test@localhost", Id: "1"})
|
|
|
|
setVersionIq.Payload = &stanza.Version{
|
2019-06-21 14:48:13 +00:00
|
|
|
XMLName: xml.Name{
|
|
|
|
Space: "jabber:iq:version",
|
|
|
|
Local: "query",
|
|
|
|
}}
|
|
|
|
|
2019-06-26 15:14:52 +00:00
|
|
|
GetDiscoIq := stanza.NewIQ(stanza.Attrs{Type: "get", From: "service.localhost", To: "test@localhost", Id: "1"})
|
|
|
|
GetDiscoIq.Payload = &stanza.DiscoInfo{
|
2019-06-21 14:48:13 +00:00
|
|
|
XMLName: xml.Name{
|
|
|
|
Space: "http://jabber.org/protocol/disco#info",
|
|
|
|
Local: "query",
|
|
|
|
}}
|
|
|
|
|
2019-06-26 15:14:52 +00:00
|
|
|
message := stanza.NewMessage(stanza.Attrs{Type: "normal", To: "test@localhost", Id: "1"})
|
2019-06-21 14:48:13 +00:00
|
|
|
message.Body = "hello"
|
|
|
|
|
|
|
|
tests := []struct {
|
|
|
|
name string
|
2019-06-26 15:14:52 +00:00
|
|
|
input stanza.Packet
|
2019-06-21 14:48:13 +00:00
|
|
|
want bool
|
|
|
|
}{
|
|
|
|
{name: "match get version iq", input: getVersionIq, want: true},
|
|
|
|
{name: "ignore set version iq", input: setVersionIq, want: false},
|
|
|
|
{name: "ignore get discoinfo iq", input: GetDiscoIq, want: false},
|
|
|
|
{name: "ignore message", input: message, want: false},
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
for _, tc := range tests {
|
|
|
|
t.Run(tc.name, func(st *testing.T) {
|
|
|
|
conn := NewSenderMock()
|
2019-06-24 09:13:25 +00:00
|
|
|
router.route(conn, tc.input)
|
2019-06-21 14:48:13 +00:00
|
|
|
|
|
|
|
res := conn.String() == successFlag
|
|
|
|
if tc.want != res {
|
|
|
|
st.Errorf("incorrect result for %#v\nMatch = %#v, expecting %#v", tc.input, res, tc.want)
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// A blank route with empty matcher will always match
|
|
|
|
// It can be use to receive all packets that do not match any of the previous route.
|
|
|
|
func TestCatchallMatcher(t *testing.T) {
|
2019-06-24 09:13:25 +00:00
|
|
|
router := NewRouter()
|
2019-06-21 14:48:13 +00:00
|
|
|
router.NewRoute().
|
2019-06-26 15:14:52 +00:00
|
|
|
HandlerFunc(func(s Sender, p stanza.Packet) {
|
2019-06-21 14:48:13 +00:00
|
|
|
_ = s.SendRaw(successFlag)
|
|
|
|
})
|
|
|
|
|
|
|
|
// Check that we match on several packets
|
|
|
|
conn := NewSenderMock()
|
2019-06-26 15:14:52 +00:00
|
|
|
message := stanza.NewMessage(stanza.Attrs{Type: "chat", To: "test@localhost", Id: "1"})
|
2019-06-21 14:48:13 +00:00
|
|
|
message.Body = "hello"
|
2019-06-24 09:13:25 +00:00
|
|
|
router.route(conn, message)
|
2019-06-21 14:48:13 +00:00
|
|
|
|
|
|
|
if conn.String() != successFlag {
|
|
|
|
t.Errorf("chat message should have been matched and routed: %v", message)
|
|
|
|
}
|
|
|
|
|
|
|
|
conn = NewSenderMock()
|
2019-06-26 15:14:52 +00:00
|
|
|
iqVersion := stanza.NewIQ(stanza.Attrs{Type: "get", From: "service.localhost", To: "test@localhost", Id: "1"})
|
|
|
|
iqVersion.Payload = &stanza.DiscoInfo{
|
2019-06-21 14:48:13 +00:00
|
|
|
XMLName: xml.Name{
|
|
|
|
Space: "jabber:iq:version",
|
|
|
|
Local: "query",
|
|
|
|
}}
|
2019-06-24 09:13:25 +00:00
|
|
|
router.route(conn, iqVersion)
|
2019-06-21 14:48:13 +00:00
|
|
|
|
|
|
|
if conn.String() != successFlag {
|
|
|
|
t.Errorf("iq get should have been matched and routed: %v", iqVersion)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// ============================================================================
|
|
|
|
// SenderMock
|
|
|
|
|
2019-10-29 09:34:33 +00:00
|
|
|
const successFlag = "matched"
|
|
|
|
const cancelledFlag = "cancelled"
|
2019-06-14 07:37:38 +00:00
|
|
|
|
|
|
|
type SenderMock struct {
|
|
|
|
buffer *bytes.Buffer
|
|
|
|
}
|
|
|
|
|
|
|
|
func NewSenderMock() SenderMock {
|
|
|
|
return SenderMock{buffer: new(bytes.Buffer)}
|
|
|
|
}
|
|
|
|
|
2019-06-26 15:14:52 +00:00
|
|
|
func (s SenderMock) Send(packet stanza.Packet) error {
|
2019-06-14 07:37:38 +00:00
|
|
|
out, err := xml.Marshal(packet)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
s.buffer.Write(out)
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2019-11-04 15:32:29 +00:00
|
|
|
func (s SenderMock) SendIQ(ctx context.Context, iq stanza.IQ) (chan stanza.IQ, error) {
|
|
|
|
out, err := xml.Marshal(iq)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
s.buffer.Write(out)
|
|
|
|
return nil, nil
|
|
|
|
}
|
|
|
|
|
2019-06-14 07:37:38 +00:00
|
|
|
func (s SenderMock) SendRaw(str string) error {
|
|
|
|
s.buffer.WriteString(str)
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s SenderMock) String() string {
|
|
|
|
return s.buffer.String()
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestSenderMock(t *testing.T) {
|
|
|
|
conn := NewSenderMock()
|
2019-06-26 15:14:52 +00:00
|
|
|
msg := stanza.NewMessage(stanza.Attrs{To: "test@localhost", Id: "1"})
|
2019-06-14 07:37:38 +00:00
|
|
|
msg.Body = "Hello"
|
|
|
|
if err := conn.Send(msg); err != nil {
|
|
|
|
t.Error("Could not send message")
|
|
|
|
}
|
|
|
|
if conn.String() != "<message id=\"1\" to=\"test@localhost\"><body>Hello</body></message>" {
|
|
|
|
t.Errorf("Incorrect packet sent: %s", conn.String())
|
|
|
|
}
|
|
|
|
}
|