-
-
Notifications
You must be signed in to change notification settings - Fork 82
E2E testing
Writing automated E2E tests for calls can be chalenging with SIP. Instrumenting some softphones is also not best deal as we do not have full control or hard to extend.
Using GO language and GO test framework with sipgo library makes this easier.
Show me the code!
Lets go!
sipgox is currently here to help
Lets say you want to test your routing,dialplan in some service like proxy(kamailio), pbx(asterisk). In a call we always have at least 2 parties, UAC and UAS, and It would be great if we can have a test that simulate both sides at the same time.
Lets start simple. UAC creating a Simple Call and checking response
import (
"context"
"testing"
"github.com/emiago/sipgo"
"github.com/emiago/sipgo/sip"
"github.com/emiago/sipgox"
"github.com/stretchr/testify/require"
)
func TestSimpleCall(t *testing.T) {
ctx, _ := context.WithTimeout(ctx, 32*time.Second)
// First create our user agent
uac, _ := sipgo.NewUA(sipgo.WithUserAgent("TestUAC"))
defer uac.Close()
// Using sipgox attach user agent to Phone
uacPhone := sipgox.NewPhone(ua)
// Start dial on sip:[email protected]:5060
// We use context to control duration of dialin
recipient := sip.Uri{User: "123456789", Host: "asterisk.xy", Port: 5060},
dialog, err := uacPhone.Dial(
ctx,
recipient,
sipgox.DialOptions{},
)
require.NoError(t, err)
t.Log("UAC answered")
select {
case <-dialog.Done():
// Other side hanguped
case <-time.After(3 * time.Second): // After 3 second we hangup
dialog.Hangup(ctx)
}
}
What if we want to test that we receive something other than 200 SIP OK This is passed as part of error. Here testing that we have Busy:
func TestBusyCall(t *testing.T) {
// ...
_, err := phone.Dial(...)
e, ok := err.(*sipgox.DialResponseError)
assert.True(t, ok)
assert.Equal(t, sip.StatusBusyHere, e.StatusCode())
}
Now lets add also receiving UAS side. How you automate that your service sends call to UAS is out of scope, but normally you can:
- register UAS
- use some sip headers
func TestCallBothParties(t *testing.T) {
ctx, _ := context.WithTimeout(ctx, 32*time.Second)
// First setup our UAS
uas, _ := sipgo.NewUA(sipgo.WithUserAgent("TestUAS"))
defer uas.Close()
uasPhone := sipgox.NewPhone(usa,
sipgox.WithPhoneListenAddr(sipgox.ListenAddr{
Network: "udp",
Addr: "127.0.0.1:5088",
}),
)
// Run UAS in background
go func() {
// Wait for answer
dialog, err := uasPhone.Answer(ctx, sipgox.AnswerOptions{})
require.NoError(t, err)
// No error so we have answered a call
// Lets check did we recevie some header
hdr := dialog.InviteRequest.GetHeader("X-Test-Call")
assert.NotEmpty(t, hdr)
// Wait for call/dialog to be completed
t.Log("UAS answered")
<-dialog.Done()
return
}()
// First create our user agent
uac, _ := sipgo.NewUA(sipgo.WithUserAgent("TestUAC"))
defer uac.Close()
uacPhone := sipgox.NewPhone(ua)
// Start dial on sip:[email protected]:5060
recipient := sip.Uri{User: "123456789", Host: "asterisk.xy", Port: 5060},
dialog, err := uacPhone.Dial(
ctx,
recipient,
sipgox.DialOptions{
SipHeaders: []sip.Header{
sip.NewHeader("X-Test-Call", "true"), // Pass some custom sip header
},
},
)
require.NoError(t, err)
t.Log("UAC answered")
select {
case <-dialog.Done():
// Other side hanguped
case <-time.After(3 * time.Second): // After 3 second we hangup
dialog.Hangup(ctx)
}
}
if you are interested with dealing media more, then checkout media package for more.
Some example of building echo client/server you have on sipgox