From c38b30f0adc4d0cce6d2b26401fcff99114375b3 Mon Sep 17 00:00:00 2001 From: Rowan Seymour Date: Fri, 29 Sep 2023 15:34:04 -0500 Subject: [PATCH] Allow outgoing tests to check multiple requests --- handlers/africastalking/handler_test.go | 29 ++++-- handlers/arabiacell/handler_test.go | 21 ++-- handlers/burstsms/handler_test.go | 13 ++- handlers/i2sms/handler_test.go | 15 ++- handlers/test.go | 125 ++++++++++++++++-------- handlers/twiml/handlers_test.go | 32 +++--- 6 files changed, 155 insertions(+), 80 deletions(-) diff --git a/handlers/africastalking/handler_test.go b/handlers/africastalking/handler_test.go index 0bfdf2580..42c257766 100644 --- a/handlers/africastalking/handler_test.go +++ b/handlers/africastalking/handler_test.go @@ -2,6 +2,7 @@ package africastalking import ( "net/http/httptest" + "net/url" "testing" "time" @@ -123,7 +124,9 @@ var outgoingTestCases = []OutgoingTestCase{ MockResponseBody: `{ "SMSMessageData": {"Recipients": [{"status": "Success", "messageId": "1002"}] } }`, MockResponseStatus: 200, ExpectedHeaders: map[string]string{"apikey": "KEY"}, - ExpectedPostParams: map[string]string{"message": "Simple Message ☺", "username": "Username", "to": "+250788383383", "from": "2020"}, + ExpectedRequests: []ExpectedRequest{ + {Form: url.Values{"message": {"Simple Message ☺"}, "username": {"Username"}, "to": {"+250788383383"}, "from": {"2020"}}}, + }, ExpectedMsgStatus: "W", ExpectedExternalID: "1002", SendPrep: setSendURL, @@ -135,7 +138,9 @@ var outgoingTestCases = []OutgoingTestCase{ MsgAttachments: []string{"image/jpeg:https://foo.bar/image.jpg"}, MockResponseBody: `{ "SMSMessageData": {"Recipients": [{"status": "Success", "messageId": "1002"}] } }`, MockResponseStatus: 200, - ExpectedPostParams: map[string]string{"message": "My pic!\nhttps://foo.bar/image.jpg"}, + ExpectedRequests: []ExpectedRequest{ + {Form: url.Values{"message": {"My pic!\nhttps://foo.bar/image.jpg"}, "username": {"Username"}, "to": {"+250788383383"}, "from": {"2020"}}}, + }, ExpectedMsgStatus: "W", ExpectedExternalID: "1002", SendPrep: setSendURL, @@ -146,9 +151,11 @@ var outgoingTestCases = []OutgoingTestCase{ MsgURN: "tel:+250788383383", MockResponseBody: `{ "SMSMessageData": {"Recipients": [{"status": "Failed" }] } }`, MockResponseStatus: 200, - ExpectedPostParams: map[string]string{"message": `No External ID`}, - ExpectedMsgStatus: "E", - SendPrep: setSendURL, + ExpectedRequests: []ExpectedRequest{ + {Form: url.Values{"message": {`No External ID`}, "username": {"Username"}, "to": {"+250788383383"}, "from": {"2020"}}}, + }, + ExpectedMsgStatus: "E", + SendPrep: setSendURL, }, { Label: "Error Sending", @@ -156,9 +163,11 @@ var outgoingTestCases = []OutgoingTestCase{ MsgURN: "tel:+250788383383", MockResponseBody: `{ "error": "failed" }`, MockResponseStatus: 401, - ExpectedPostParams: map[string]string{"message": `Error Message`}, - ExpectedMsgStatus: "E", - SendPrep: setSendURL, + ExpectedRequests: []ExpectedRequest{ + {Form: url.Values{"message": {`Error Message`}, "username": {"Username"}, "to": {"+250788383383"}, "from": {"2020"}}}, + }, + ExpectedMsgStatus: "E", + SendPrep: setSendURL, }, } @@ -170,7 +179,9 @@ var sharedSendTestCases = []OutgoingTestCase{ MockResponseBody: `{ "SMSMessageData": {"Recipients": [{"status": "Success", "messageId": "1002"}] } }`, MockResponseStatus: 200, ExpectedHeaders: map[string]string{"apikey": "KEY"}, - ExpectedPostParams: map[string]string{"message": "Simple Message ☺", "username": "Username", "to": "+250788383383", "from": ""}, + ExpectedRequests: []ExpectedRequest{ + {Form: url.Values{"message": {"Simple Message ☺"}, "username": {"Username"}, "to": {"+250788383383"}}}, + }, ExpectedMsgStatus: "W", ExpectedExternalID: "1002", SendPrep: setSendURL, diff --git a/handlers/arabiacell/handler_test.go b/handlers/arabiacell/handler_test.go index 96934ba98..22e8b3e64 100644 --- a/handlers/arabiacell/handler_test.go +++ b/handlers/arabiacell/handler_test.go @@ -2,6 +2,7 @@ package arabiacell import ( "net/http/httptest" + "net/url" "testing" "github.com/nyaruka/courier" @@ -60,14 +61,18 @@ var defaultSendTestCases = []OutgoingTestCase{ external1 `, MockResponseStatus: 200, - ExpectedPostParams: map[string]string{ - "userName": "user1", - "password": "pass1", - "handlerType": "send_msg", - "serviceId": "service1", - "msisdn": "+250788383383", - "messageBody": "Simple Message ☺\nhttps://foo.bar/image.jpg", - "chargingLevel": "0", + ExpectedRequests: []ExpectedRequest{ + { + Form: url.Values{ + "userName": {"user1"}, + "password": {"pass1"}, + "handlerType": {"send_msg"}, + "serviceId": {"service1"}, + "msisdn": {"+250788383383"}, + "messageBody": {"Simple Message ☺\nhttps://foo.bar/image.jpg"}, + "chargingLevel": {"0"}, + }, + }, }, ExpectedMsgStatus: "W", ExpectedExternalID: "external1", diff --git a/handlers/burstsms/handler_test.go b/handlers/burstsms/handler_test.go index 750873bcc..eae25d0d9 100644 --- a/handlers/burstsms/handler_test.go +++ b/handlers/burstsms/handler_test.go @@ -2,6 +2,7 @@ package burstsms import ( "net/http/httptest" + "net/url" "testing" "github.com/nyaruka/courier" @@ -69,10 +70,14 @@ var defaultSendTestCases = []OutgoingTestCase{ MsgAttachments: []string{"image/jpeg:https://foo.bar/image.jpg"}, MockResponseBody: `{ "message_id": 19835, "recipients": 3, "cost": 1.000 }`, MockResponseStatus: 200, - ExpectedPostParams: map[string]string{ - "to": "250788383383", - "message": "Simple Message ☺\nhttps://foo.bar/image.jpg", - "from": "2020", + ExpectedRequests: []ExpectedRequest{ + { + Form: url.Values{ + "to": {"250788383383"}, + "message": {"Simple Message ☺\nhttps://foo.bar/image.jpg"}, + "from": {"2020"}, + }, + }, }, ExpectedMsgStatus: "W", ExpectedExternalID: "19835", diff --git a/handlers/i2sms/handler_test.go b/handlers/i2sms/handler_test.go index f8853b153..799e0fd6d 100644 --- a/handlers/i2sms/handler_test.go +++ b/handlers/i2sms/handler_test.go @@ -2,6 +2,7 @@ package i2sms import ( "net/http/httptest" + "net/url" "testing" "github.com/nyaruka/courier" @@ -57,11 +58,15 @@ var defaultSendTestCases = []OutgoingTestCase{ MsgAttachments: []string{"image/jpeg:https://foo.bar/image.jpg"}, MockResponseBody: `{"result":{"session_id":"5b8fc97d58795484819426"}, "error_code": "00", "error_desc": "Success"}`, MockResponseStatus: 200, - ExpectedPostParams: map[string]string{ - "action": "send_single", - "mobile": "250788383383", - "message": "Simple Message ☺\nhttps://foo.bar/image.jpg", - "channel": "hash123", + ExpectedRequests: []ExpectedRequest{ + { + Form: url.Values{ + "action": {"send_single"}, + "mobile": {"250788383383"}, + "message": {"Simple Message ☺\nhttps://foo.bar/image.jpg"}, + "channel": {"hash123"}, + }, + }, }, ExpectedMsgStatus: "W", ExpectedExternalID: "5b8fc97d58795484819426", diff --git a/handlers/test.go b/handlers/test.go index bdc4482f4..6101ced2e 100644 --- a/handlers/test.go +++ b/handlers/test.go @@ -268,6 +268,38 @@ func RunIncomingTestCases(t *testing.T, channels []courier.Channel, handler cour // SendPrepFunc allows test cases to modify the channel, msg or server before a message is sent type SendPrepFunc func(*httptest.Server, courier.ChannelHandler, courier.Channel, courier.MsgOut) +type ExpectedRequest struct { + Headers map[string]string + Path string + Params map[string]string + Form url.Values + Body string +} + +func (e *ExpectedRequest) AssertMatches(t *testing.T, actual *http.Request, requestNum int) { + if e.Headers != nil { + for k, v := range e.Headers { + assert.Equal(t, v, actual.Header.Get(k), "header %s mismatch for request %d", k, requestNum) + } + } + if e.Path != "" { + assert.Equal(t, e.Path, actual.URL.Path, "patch mismatch for request %d", requestNum) + } + if e.Params != nil { + for k, v := range e.Params { + assert.Equal(t, v, actual.URL.Query().Get(k), "URL param %s mismatch for request %d", requestNum) + } + } + if e.Form != nil { + actual.ParseMultipartForm(32 << 20) + assert.Equal(t, e.Form, actual.PostForm, "form mismatch for request %d", requestNum) + } + if e.Body != "" { + value, _ := io.ReadAll(actual.Body) + assert.Equal(t, e.Body, strings.Trim(string(value), "\n"), "body mismatch for request %d", requestNum) + } +} + // OutgoingTestCase defines the test values for a particular test case type OutgoingTestCase struct { Label string @@ -292,18 +324,20 @@ type OutgoingTestCase struct { MockResponseBody string MockResponses map[MockedRequest]*httpx.MockResponse - ExpectedRequestPath string - ExpectedURLParams map[string]string - ExpectedPostParams map[string]string // deprecated, use ExpectedPostForm - ExpectedPostForm url.Values - ExpectedRequestBody string - ExpectedHeaders map[string]string + ExpectedRequests []ExpectedRequest ExpectedMsgStatus courier.MsgStatus ExpectedExternalID string ExpectedErrors []*courier.ChannelError ExpectedStopEvent bool ExpectedContactURNs map[string]bool ExpectedNewURN string + + // deprecated, use ExpectedRequests + ExpectedRequestPath string + ExpectedURLParams map[string]string + ExpectedPostParams map[string]string + ExpectedRequestBody string + ExpectedHeaders map[string]string } // RunOutgoingTestCases runs all the passed in test cases against the channel @@ -347,11 +381,13 @@ func RunOutgoingTestCases(t *testing.T, channel courier.Channel, handler courier msg.WithOptIn(tc.MsgOptIn) } - var testRequest *http.Request + actualRequests := make([]*http.Request, 0, 1) server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + // copy request and add to list body, _ := io.ReadAll(r.Body) - testRequest = httptest.NewRequest(r.Method, r.URL.String(), bytes.NewBuffer(body)) - testRequest.Header = r.Header + copy := httptest.NewRequest(r.Method, r.URL.String(), bytes.NewBuffer(body)) + copy.Header = r.Header + actualRequests = append(actualRequests, copy) if (len(tc.MockResponses)) == 0 { w.WriteHeader(tc.MockResponseStatus) @@ -387,49 +423,54 @@ func RunOutgoingTestCases(t *testing.T, channel courier.Channel, handler courier assert.Equal(t, tc.ExpectedErrors, clog.Errors(), "unexpected errors logged") - if tc.ExpectedRequestPath != "" { - require.NotNil(testRequest, "path should not be nil") - require.Equal(tc.ExpectedRequestPath, testRequest.URL.Path) - } + if tc.ExpectedRequestPath != "" || tc.ExpectedURLParams != nil || tc.ExpectedPostParams != nil || tc.ExpectedRequestBody != "" || tc.ExpectedHeaders != nil { + testRequest := actualRequests[len(actualRequests)-1] - if tc.ExpectedURLParams != nil { - require.NotNil(testRequest) - for k, v := range tc.ExpectedURLParams { - value := testRequest.URL.Query().Get(k) - require.Equal(v, value, fmt.Sprintf("%s not equal", k)) + if tc.ExpectedRequestPath != "" { + require.NotNil(testRequest, "path should not be nil") + require.Equal(tc.ExpectedRequestPath, testRequest.URL.Path) } - } - - if tc.ExpectedPostParams != nil { - require.NotNil(testRequest, "post body should not be nil") - for k, v := range tc.ExpectedPostParams { - value := testRequest.PostFormValue(k) - require.Equal(v, value) + if tc.ExpectedURLParams != nil { + require.NotNil(testRequest) + for k, v := range tc.ExpectedURLParams { + value := testRequest.URL.Query().Get(k) + require.Equal(v, value, fmt.Sprintf("%s not equal", k)) + } } - } else if tc.ExpectedPostForm != nil { - require.NotNil(testRequest, "post body should not be nil") - testRequest.ParseMultipartForm(32 << 20) - assert.Equal(t, tc.ExpectedPostForm, testRequest.PostForm) - } + if tc.ExpectedPostParams != nil { + require.NotNil(testRequest, "post body should not be nil") + for k, v := range tc.ExpectedPostParams { + value := testRequest.PostFormValue(k) + require.Equal(v, value) + } + } + if tc.ExpectedRequestBody != "" { + require.NotNil(testRequest, "request body should not be nil") + value, _ := io.ReadAll(testRequest.Body) + require.Equal(tc.ExpectedRequestBody, strings.Trim(string(value), "\n")) + } + if tc.ExpectedHeaders != nil { + require.NotNil(testRequest, "headers should not be nil") + for k, v := range tc.ExpectedHeaders { + value := testRequest.Header.Get(k) + require.Equal(v, value) + } + } + } else if len(tc.ExpectedRequests) > 0 { + assert.Len(t, actualRequests, len(tc.ExpectedRequests), "unexpected number of requests made") - if tc.ExpectedRequestBody != "" { - require.NotNil(testRequest, "request body should not be nil") - value, _ := io.ReadAll(testRequest.Body) - require.Equal(tc.ExpectedRequestBody, strings.Trim(string(value), "\n")) + for i, expectedRequest := range tc.ExpectedRequests { + if (len(actualRequests) - 1) < i { + break + } + expectedRequest.AssertMatches(t, actualRequests[i], i) + } } if (len(tc.MockResponses)) != 0 { assert.Equal(t, len(tc.MockResponses), mockRRCount, "mocked request count mismatch") } - if tc.ExpectedHeaders != nil { - require.NotNil(testRequest, "headers should not be nil") - for k, v := range tc.ExpectedHeaders { - value := testRequest.Header.Get(k) - require.Equal(v, value) - } - } - if tc.ExpectedExternalID != "" { require.Equal(tc.ExpectedExternalID, status.ExternalID()) } diff --git a/handlers/twiml/handlers_test.go b/handlers/twiml/handlers_test.go index b5b6b678f..033b5e9c5 100644 --- a/handlers/twiml/handlers_test.go +++ b/handlers/twiml/handlers_test.go @@ -631,12 +631,16 @@ var defaultSendTestCases = []OutgoingTestCase{ MsgAttachments: []string{"image/jpeg:https://foo.bar/image.jpg"}, MockResponseBody: `{ "sid": "1002" }`, MockResponseStatus: 200, - ExpectedPostForm: url.Values{ - "Body": []string{"My pic!"}, - "To": []string{"+250788383383"}, - "MediaUrl": []string{"https://foo.bar/image.jpg"}, - "From": []string{"2020"}, - "StatusCallback": []string{"https://localhost/c/t/8eb23e93-5ecb-45ba-b726-3b064e0c56ab/status?id=10&action=callback"}, + ExpectedRequests: []ExpectedRequest{ + { + Form: url.Values{ + "Body": []string{"My pic!"}, + "To": []string{"+250788383383"}, + "MediaUrl": []string{"https://foo.bar/image.jpg"}, + "From": []string{"2020"}, + "StatusCallback": []string{"https://localhost/c/t/8eb23e93-5ecb-45ba-b726-3b064e0c56ab/status?id=10&action=callback"}, + }, + }, }, ExpectedMsgStatus: "W", SendPrep: setSendURL, @@ -647,12 +651,16 @@ var defaultSendTestCases = []OutgoingTestCase{ MsgAttachments: []string{"image/jpeg:https://foo.bar/image.jpg", "audio/mp4:https://foo.bar/audio.m4a"}, MockResponseBody: `{ "sid": "1002" }`, MockResponseStatus: 200, - ExpectedPostForm: url.Values{ - "Body": []string{""}, - "To": []string{"+250788383383"}, - "MediaUrl": []string{"https://foo.bar/image.jpg", "https://foo.bar/audio.m4a"}, - "From": []string{"2020"}, - "StatusCallback": []string{"https://localhost/c/t/8eb23e93-5ecb-45ba-b726-3b064e0c56ab/status?id=10&action=callback"}, + ExpectedRequests: []ExpectedRequest{ + { + Form: url.Values{ + "Body": []string{""}, + "To": []string{"+250788383383"}, + "MediaUrl": []string{"https://foo.bar/image.jpg", "https://foo.bar/audio.m4a"}, + "From": []string{"2020"}, + "StatusCallback": []string{"https://localhost/c/t/8eb23e93-5ecb-45ba-b726-3b064e0c56ab/status?id=10&action=callback"}, + }, + }, }, ExpectedMsgStatus: "W", SendPrep: setSendURL,