From fa35a8df23d1fd139651deb4f3f078b25aedcf36 Mon Sep 17 00:00:00 2001 From: Norbert Kwizera Date: Thu, 22 Feb 2024 15:11:28 +0200 Subject: [PATCH 01/11] Use MockResponses and ExpectedRequests in DA tests --- handlers/dart/handler_test.go | 95 +++++++++++++++++++---------------- 1 file changed, 51 insertions(+), 44 deletions(-) diff --git a/handlers/dart/handler_test.go b/handlers/dart/handler_test.go index 3425f29b0..b4fd35bdc 100644 --- a/handlers/dart/handler_test.go +++ b/handlers/dart/handler_test.go @@ -1,13 +1,13 @@ package dart import ( - "net/http/httptest" "net/url" "testing" "github.com/nyaruka/courier" . "github.com/nyaruka/courier/handlers" "github.com/nyaruka/courier/test" + "github.com/nyaruka/gocommon/httpx" ) var daTestChannels = []courier.Channel{ @@ -94,88 +94,95 @@ func BenchmarkHandler(b *testing.B) { RunChannelBenchmarks(b, daTestChannels, NewHandler("DA", "DartMedia", sendURL, maxMsgLength), daTestCases) } -// setSendURL takes care of setting the sendURL to call -func setSendURL(s *httptest.Server, h courier.ChannelHandler, c courier.Channel, m courier.MsgOut) { - daHandler := h.(*handler) - daHandler.sendURL = s.URL -} - var defaultSendTestCases = []OutgoingTestCase{ { - Label: "Plain Send", - MsgText: "Simple Message", - MsgURN: "tel:+250788383383", - MockResponseBody: "000", - MockResponseStatus: 200, + Label: "Plain Send", + MsgText: "Simple Message", + MsgURN: "tel:+250788383383", + MockResponses: map[string][]*httpx.MockResponse{ + "http://202.43.169.11/APIhttpU/receive2waysms.php*": { + httpx.NewMockResponse(200, nil, []byte(`000`)), + }, + }, ExpectedRequests: []ExpectedRequest{ {Params: url.Values{"message": {"Simple Message"}, "sendto": {"250788383383"}, "original": {"2020"}, "userid": {"Username"}, "password": {"Password"}, "dcs": {"0"}, "udhl": {"0"}, "messageid": {"10"}}}, }, ExpectedMsgStatus: "W", - SendPrep: setSendURL, }, { - Label: "Long Send", - MsgText: "This is a longer message than 160 characters and will cause us to split it into two separate parts, isn't that right but it is even longer than before I say, I need to keep adding more things to make it work", - MsgURN: "tel:+250788383383", - MockResponseBody: "000", - MockResponseStatus: 200, + Label: "Long Send", + MsgText: "This is a longer message than 160 characters and will cause us to split it into two separate parts, isn't that right but it is even longer than before I say, I need to keep adding more things to make it work", + MsgURN: "tel:+250788383383", + MockResponses: map[string][]*httpx.MockResponse{ + "http://202.43.169.11/APIhttpU/receive2waysms.php*": { + httpx.NewMockResponse(200, nil, []byte(`000`)), + httpx.NewMockResponse(200, nil, []byte(`000`)), + }, + }, ExpectedRequests: []ExpectedRequest{ {Params: url.Values{"message": {"This is a longer message than 160 characters and will cause us to split it into two separate parts, isn't that right but it is even longer than before I say,"}, "sendto": {"250788383383"}, "original": {"2020"}, "userid": {"Username"}, "password": {"Password"}, "dcs": {"0"}, "udhl": {"0"}, "messageid": {"10"}}}, {Params: url.Values{"message": {"I need to keep adding more things to make it work"}, "sendto": {"250788383383"}, "original": {"2020"}, "userid": {"Username"}, "password": {"Password"}, "dcs": {"0"}, "udhl": {"0"}, "messageid": {"10.2"}}}, }, ExpectedMsgStatus: "W", - SendPrep: setSendURL, }, { - Label: "Send Attachment", - MsgText: "My pic!", - MsgURN: "tel:+250788383383", - MsgAttachments: []string{"image/jpeg:https://foo.bar/image.jpg"}, - MockResponseBody: "000", - MockResponseStatus: 200, + Label: "Send Attachment", + MsgText: "My pic!", + MsgURN: "tel:+250788383383", + MsgAttachments: []string{"image/jpeg:https://foo.bar/image.jpg"}, + MockResponses: map[string][]*httpx.MockResponse{ + "http://202.43.169.11/APIhttpU/receive2waysms.php*": { + httpx.NewMockResponse(200, nil, []byte(`000`)), + }, + }, ExpectedRequests: []ExpectedRequest{ {Params: url.Values{"message": {"My pic!\nhttps://foo.bar/image.jpg"}, "sendto": {"250788383383"}, "original": {"2020"}, "userid": {"Username"}, "password": {"Password"}, "dcs": {"0"}, "udhl": {"0"}, "messageid": {"10"}}}, }, ExpectedMsgStatus: "W", - SendPrep: setSendURL, }, { - Label: "Error Sending", - MsgText: "Error Message", - MsgURN: "tel:+250788383383", - MockResponseBody: `Error`, - MockResponseStatus: 400, + Label: "Error Sending", + MsgText: "Error Message", + MsgURN: "tel:+250788383383", + MockResponses: map[string][]*httpx.MockResponse{ + "http://202.43.169.11/APIhttpU/receive2waysms.php*": { + httpx.NewMockResponse(400, nil, []byte(`Error`)), + }, + }, ExpectedRequests: []ExpectedRequest{ {Params: url.Values{"message": {"Error Message"}, "sendto": {"250788383383"}, "original": {"2020"}, "userid": {"Username"}, "password": {"Password"}, "dcs": {"0"}, "udhl": {"0"}, "messageid": {"10"}}}, }, ExpectedMsgStatus: "E", - SendPrep: setSendURL, }, { - Label: "Authentication Error", - MsgText: "Simple Message", - MsgURN: "tel:+250788383383", - MockResponseBody: "001", - MockResponseStatus: 200, + Label: "Authentication Error", + MsgText: "Simple Message", + MsgURN: "tel:+250788383383", + MockResponses: map[string][]*httpx.MockResponse{ + "http://202.43.169.11/APIhttpU/receive2waysms.php*": { + httpx.NewMockResponse(200, nil, []byte(`001`)), + }, + }, ExpectedRequests: []ExpectedRequest{ {Params: url.Values{"message": {"Simple Message"}, "sendto": {"250788383383"}, "original": {"2020"}, "userid": {"Username"}, "password": {"Password"}, "dcs": {"0"}, "udhl": {"0"}, "messageid": {"10"}}}, }, ExpectedMsgStatus: "E", ExpectedLogErrors: []*courier.ChannelError{courier.ErrorExternal("001", "Authentication error.")}, - SendPrep: setSendURL, }, { - Label: "Account Expired", - MsgText: "Simple Message", - MsgURN: "tel:+250788383383", - MockResponseBody: "101", - MockResponseStatus: 200, + Label: "Account Expired", + MsgText: "Simple Message", + MsgURN: "tel:+250788383383", + MockResponses: map[string][]*httpx.MockResponse{ + "http://202.43.169.11/APIhttpU/receive2waysms.php*": { + httpx.NewMockResponse(200, nil, []byte(`101`)), + }, + }, ExpectedRequests: []ExpectedRequest{ {Params: url.Values{"message": {"Simple Message"}, "sendto": {"250788383383"}, "original": {"2020"}, "userid": {"Username"}, "password": {"Password"}, "dcs": {"0"}, "udhl": {"0"}, "messageid": {"10"}}}, }, ExpectedMsgStatus: "E", ExpectedLogErrors: []*courier.ChannelError{courier.ErrorExternal("101", "Account expired or invalid parameters.")}, - SendPrep: setSendURL, }, } From a4bfe48cc17ffc0f954d3dc9c922a611404734a4 Mon Sep 17 00:00:00 2001 From: Norbert Kwizera Date: Thu, 22 Feb 2024 15:16:26 +0200 Subject: [PATCH 02/11] Use MockResponses and ExpectedRequests in D3C tests --- handlers/dialog360/handler_test.go | 218 ++++++++++++++++++----------- 1 file changed, 135 insertions(+), 83 deletions(-) diff --git a/handlers/dialog360/handler_test.go b/handlers/dialog360/handler_test.go index 97291e8f5..0b60ac6f5 100644 --- a/handlers/dialog360/handler_test.go +++ b/handlers/dialog360/handler_test.go @@ -314,16 +314,16 @@ func TestBuildAttachmentRequest(t *testing.T) { } -// setSendURL takes care of setting the base_url to our test server host -func setSendURL(s *httptest.Server, h courier.ChannelHandler, c courier.Channel, m courier.MsgOut) { - c.(*test.MockChannel).SetConfig("base_url", s.URL) -} - var SendTestCasesD3C = []OutgoingTestCase{ { - Label: "Plain Send", - MsgText: "Simple Message", - MsgURN: "whatsapp:250788123123", + Label: "Plain Send", + MsgText: "Simple Message", + MsgURN: "whatsapp:250788123123", + MockResponses: map[string][]*httpx.MockResponse{ + "https://waba-v2.360dialog.io/messages": { + httpx.NewMockResponse(200, nil, []byte(`{ "messages": [{"id": "157b5e14568e8"}] }`)), + }, + }, MockResponseBody: `{ "messages": [{"id": "157b5e14568e8"}] }`, MockResponseStatus: 201, ExpectedRequests: []ExpectedRequest{ @@ -334,12 +334,16 @@ var SendTestCasesD3C = []OutgoingTestCase{ }, ExpectedMsgStatus: "W", ExpectedExtIDs: []string{"157b5e14568e8"}, - SendPrep: setSendURL, }, { - Label: "Unicode Send", - MsgText: "☺", - MsgURN: "whatsapp:250788123123", + Label: "Unicode Send", + MsgText: "☺", + MsgURN: "whatsapp:250788123123", + MockResponses: map[string][]*httpx.MockResponse{ + "https://waba-v2.360dialog.io/messages": { + httpx.NewMockResponse(200, nil, []byte(`{ "messages": [{"id": "157b5e14568e8"}] }`)), + }, + }, MockResponseBody: `{ "messages": [{"id": "157b5e14568e8"}] }`, MockResponseStatus: 201, ExpectedRequests: []ExpectedRequest{ @@ -350,7 +354,6 @@ var SendTestCasesD3C = []OutgoingTestCase{ }, ExpectedMsgStatus: "W", ExpectedExtIDs: []string{"157b5e14568e8"}, - SendPrep: setSendURL, }, { Label: "Audio Send", @@ -369,13 +372,17 @@ var SendTestCasesD3C = []OutgoingTestCase{ }, ExpectedMsgStatus: "W", ExpectedExtIDs: []string{"157b5e14568e8"}, - SendPrep: setSendURL, }, { - Label: "Document Send", - MsgText: "document caption", - MsgURN: "whatsapp:250788123123", - MsgAttachments: []string{"application/pdf:https://foo.bar/document.pdf"}, + Label: "Document Send", + MsgText: "document caption", + MsgURN: "whatsapp:250788123123", + MsgAttachments: []string{"application/pdf:https://foo.bar/document.pdf"}, + MockResponses: map[string][]*httpx.MockResponse{ + "https://waba-v2.360dialog.io/messages": { + httpx.NewMockResponse(200, nil, []byte(`{ "messages": [{"id": "157b5e14568e8"}] }`)), + }, + }, MockResponseBody: `{ "messages": [{"id": "157b5e14568e8"}] }`, MockResponseStatus: 201, ExpectedRequests: []ExpectedRequest{ @@ -386,13 +393,17 @@ var SendTestCasesD3C = []OutgoingTestCase{ }, ExpectedMsgStatus: "W", ExpectedExtIDs: []string{"157b5e14568e8"}, - SendPrep: setSendURL, }, { - Label: "Image Send", - MsgText: "image caption", - MsgURN: "whatsapp:250788123123", - MsgAttachments: []string{"image/jpeg:https://foo.bar/image.jpg"}, + Label: "Image Send", + MsgText: "image caption", + MsgURN: "whatsapp:250788123123", + MsgAttachments: []string{"image/jpeg:https://foo.bar/image.jpg"}, + MockResponses: map[string][]*httpx.MockResponse{ + "https://waba-v2.360dialog.io/messages": { + httpx.NewMockResponse(200, nil, []byte(`{ "messages": [{"id": "157b5e14568e8"}] }`)), + }, + }, MockResponseBody: `{ "messages": [{"id": "157b5e14568e8"}] }`, MockResponseStatus: 201, ExpectedRequests: []ExpectedRequest{ @@ -403,13 +414,17 @@ var SendTestCasesD3C = []OutgoingTestCase{ }, ExpectedMsgStatus: "W", ExpectedExtIDs: []string{"157b5e14568e8"}, - SendPrep: setSendURL, }, { - Label: "Video Send", - MsgText: "video caption", - MsgURN: "whatsapp:250788123123", - MsgAttachments: []string{"video/mp4:https://foo.bar/video.mp4"}, + Label: "Video Send", + MsgText: "video caption", + MsgURN: "whatsapp:250788123123", + MsgAttachments: []string{"video/mp4:https://foo.bar/video.mp4"}, + MockResponses: map[string][]*httpx.MockResponse{ + "https://waba-v2.360dialog.io/messages": { + httpx.NewMockResponse(200, nil, []byte(`{ "messages": [{"id": "157b5e14568e8"}] }`)), + }, + }, MockResponseBody: `{ "messages": [{"id": "157b5e14568e8"}] }`, MockResponseStatus: 201, ExpectedRequests: []ExpectedRequest{ @@ -420,64 +435,84 @@ var SendTestCasesD3C = []OutgoingTestCase{ }, ExpectedMsgStatus: "W", ExpectedExtIDs: []string{"157b5e14568e8"}, - SendPrep: setSendURL, }, { - Label: "Template Send", - MsgText: "templated message", - MsgURN: "whatsapp:250788123123", - MsgLocale: "eng", - MsgMetadata: json.RawMessage(`{ "templating": { "template": { "name": "revive_issue", "uuid": "171f8a4d-f725-46d7-85a6-11aceff0bfe3" }, "params": {"body": [{"type":"text", "value":"Chef"}, {"type": "text" , "value": "tomorrow"}]}, "language": "en_US"}}`), + Label: "Template Send", + MsgText: "templated message", + MsgURN: "whatsapp:250788123123", + MsgLocale: "eng", + MsgMetadata: json.RawMessage(`{ "templating": { "template": { "name": "revive_issue", "uuid": "171f8a4d-f725-46d7-85a6-11aceff0bfe3" }, "params": {"body": [{"type":"text", "value":"Chef"}, {"type": "text" , "value": "tomorrow"}]}, "language": "en_US"}}`), + MockResponses: map[string][]*httpx.MockResponse{ + "https://waba-v2.360dialog.io/messages": { + httpx.NewMockResponse(200, nil, []byte(`{ "messages": [{"id": "157b5e14568e8"}] }`)), + }, + }, ExpectedMsgStatus: "W", ExpectedExtIDs: []string{"157b5e14568e8"}, MockResponseBody: `{ "messages": [{"id": "157b5e14568e8"}] }`, MockResponseStatus: 200, ExpectedRequestBody: `{"messaging_product":"whatsapp","recipient_type":"individual","to":"250788123123","type":"template","template":{"name":"revive_issue","language":{"policy":"deterministic","code":"en_US"},"components":[{"type":"body","sub_type":"","index":"","parameters":[{"type":"text","text":"Chef"},{"type":"text","text":"tomorrow"}]}]}}`, - SendPrep: setSendURL, }, { - Label: "Interactive Button Message Send", - MsgText: "Interactive Button Msg", - MsgURN: "whatsapp:250788123123", - MsgQuickReplies: []string{"BUTTON1"}, + Label: "Interactive Button Message Send", + MsgText: "Interactive Button Msg", + MsgURN: "whatsapp:250788123123", + MsgQuickReplies: []string{"BUTTON1"}, + MockResponses: map[string][]*httpx.MockResponse{ + "https://waba-v2.360dialog.io/messages": { + httpx.NewMockResponse(200, nil, []byte(`{ "messages": [{"id": "157b5e14568e8"}] }`)), + }, + }, MockResponseBody: `{ "messages": [{"id": "157b5e14568e8"}] }`, MockResponseStatus: 201, ExpectedRequestBody: `{"messaging_product":"whatsapp","recipient_type":"individual","to":"250788123123","type":"interactive","interactive":{"type":"button","body":{"text":"Interactive Button Msg"},"action":{"buttons":[{"type":"reply","reply":{"id":"0","title":"BUTTON1"}}]}}}`, ExpectedMsgStatus: "W", ExpectedExtIDs: []string{"157b5e14568e8"}, - SendPrep: setSendURL, }, { - Label: "Interactive List Message Send", - MsgText: "Interactive List Msg", - MsgURN: "whatsapp:250788123123", - MsgQuickReplies: []string{"ROW1", "ROW2", "ROW3", "ROW4"}, + Label: "Interactive List Message Send", + MsgText: "Interactive List Msg", + MsgURN: "whatsapp:250788123123", + MsgQuickReplies: []string{"ROW1", "ROW2", "ROW3", "ROW4"}, + MockResponses: map[string][]*httpx.MockResponse{ + "https://waba-v2.360dialog.io/messages": { + httpx.NewMockResponse(200, nil, []byte(`{ "messages": [{"id": "157b5e14568e8"}] }`)), + }, + }, MockResponseBody: `{ "messages": [{"id": "157b5e14568e8"}] }`, MockResponseStatus: 201, ExpectedRequestBody: `{"messaging_product":"whatsapp","recipient_type":"individual","to":"250788123123","type":"interactive","interactive":{"type":"list","body":{"text":"Interactive List Msg"},"action":{"button":"Menu","sections":[{"rows":[{"id":"0","title":"ROW1"},{"id":"1","title":"ROW2"},{"id":"2","title":"ROW3"},{"id":"3","title":"ROW4"}]}]}}}`, ExpectedMsgStatus: "W", ExpectedExtIDs: []string{"157b5e14568e8"}, - SendPrep: setSendURL, }, { - Label: "Interactive List Message Send In Spanish", - MsgText: "Hola", - MsgURN: "whatsapp:250788123123", - MsgLocale: "spa", - MsgQuickReplies: []string{"ROW1", "ROW2", "ROW3", "ROW4"}, + Label: "Interactive List Message Send In Spanish", + MsgText: "Hola", + MsgURN: "whatsapp:250788123123", + MsgLocale: "spa", + MsgQuickReplies: []string{"ROW1", "ROW2", "ROW3", "ROW4"}, + MockResponses: map[string][]*httpx.MockResponse{ + "https://waba-v2.360dialog.io/messages": { + httpx.NewMockResponse(200, nil, []byte(`{ "messages": [{"id": "157b5e14568e8"}] }`)), + }, + }, MockResponseBody: `{ "messages": [{"id": "157b5e14568e8"}] }`, MockResponseStatus: 201, ExpectedRequestBody: `{"messaging_product":"whatsapp","recipient_type":"individual","to":"250788123123","type":"interactive","interactive":{"type":"list","body":{"text":"Hola"},"action":{"button":"Menú","sections":[{"rows":[{"id":"0","title":"ROW1"},{"id":"1","title":"ROW2"},{"id":"2","title":"ROW3"},{"id":"3","title":"ROW4"}]}]}}}`, ExpectedMsgStatus: "W", ExpectedExtIDs: []string{"157b5e14568e8"}, - SendPrep: setSendURL, }, { - Label: "Interactive Button Message Send with image attachment", - MsgText: "Interactive Button Msg", - MsgURN: "whatsapp:250788123123", - MsgQuickReplies: []string{"BUTTON1"}, - MsgAttachments: []string{"image/jpeg:https://foo.bar/image.jpg"}, + Label: "Interactive Button Message Send with image attachment", + MsgText: "Interactive Button Msg", + MsgURN: "whatsapp:250788123123", + MsgQuickReplies: []string{"BUTTON1"}, + MsgAttachments: []string{"image/jpeg:https://foo.bar/image.jpg"}, + MockResponses: map[string][]*httpx.MockResponse{ + "https://waba-v2.360dialog.io/messages": { + httpx.NewMockResponse(200, nil, []byte(`{ "messages": [{"id": "157b5e14568e8"}] }`)), + }, + }, MockResponseBody: `{ "messages": [{"id": "157b5e14568e8"}] }`, MockResponseStatus: 201, ExpectedRequests: []ExpectedRequest{ @@ -488,14 +523,18 @@ var SendTestCasesD3C = []OutgoingTestCase{ }, ExpectedMsgStatus: "W", ExpectedExtIDs: []string{"157b5e14568e8"}, - SendPrep: setSendURL, }, { - Label: "Interactive Button Message Send with video attachment", - MsgText: "Interactive Button Msg", - MsgURN: "whatsapp:250788123123", - MsgQuickReplies: []string{"BUTTON1"}, - MsgAttachments: []string{"video/mp4:https://foo.bar/video.mp4"}, + Label: "Interactive Button Message Send with video attachment", + MsgText: "Interactive Button Msg", + MsgURN: "whatsapp:250788123123", + MsgQuickReplies: []string{"BUTTON1"}, + MsgAttachments: []string{"video/mp4:https://foo.bar/video.mp4"}, + MockResponses: map[string][]*httpx.MockResponse{ + "https://waba-v2.360dialog.io/messages": { + httpx.NewMockResponse(200, nil, []byte(`{ "messages": [{"id": "157b5e14568e8"}] }`)), + }, + }, MockResponseBody: `{ "messages": [{"id": "157b5e14568e8"}] }`, MockResponseStatus: 201, ExpectedRequests: []ExpectedRequest{ @@ -506,14 +545,18 @@ var SendTestCasesD3C = []OutgoingTestCase{ }, ExpectedMsgStatus: "W", ExpectedExtIDs: []string{"157b5e14568e8"}, - SendPrep: setSendURL, }, { - Label: "Interactive Button Message Send with document attachment", - MsgText: "Interactive Button Msg", - MsgURN: "whatsapp:250788123123", - MsgQuickReplies: []string{"BUTTON1"}, - MsgAttachments: []string{"document/pdf:https://foo.bar/document.pdf"}, + Label: "Interactive Button Message Send with document attachment", + MsgText: "Interactive Button Msg", + MsgURN: "whatsapp:250788123123", + MsgQuickReplies: []string{"BUTTON1"}, + MsgAttachments: []string{"document/pdf:https://foo.bar/document.pdf"}, + MockResponses: map[string][]*httpx.MockResponse{ + "https://waba-v2.360dialog.io/messages": { + httpx.NewMockResponse(200, nil, []byte(`{ "messages": [{"id": "157b5e14568e8"}] }`)), + }, + }, MockResponseBody: `{ "messages": [{"id": "157b5e14568e8"}] }`, MockResponseStatus: 201, ExpectedRequests: []ExpectedRequest{ @@ -524,7 +567,6 @@ var SendTestCasesD3C = []OutgoingTestCase{ }, ExpectedMsgStatus: "W", ExpectedExtIDs: []string{"157b5e14568e8"}, - SendPrep: setSendURL, }, { Label: "Interactive Button Message Send with audio attachment", @@ -544,7 +586,6 @@ var SendTestCasesD3C = []OutgoingTestCase{ }, ExpectedMsgStatus: "W", ExpectedExtIDs: []string{"157b5e14568e8"}, - SendPrep: setSendURL, }, { Label: "Interactive List Message Send with attachment", @@ -564,12 +605,16 @@ var SendTestCasesD3C = []OutgoingTestCase{ }, ExpectedMsgStatus: "W", ExpectedExtIDs: []string{"157b5e14568e8"}, - SendPrep: setSendURL, }, { - Label: "Link Sending", - MsgText: "Link Sending https://link.com", - MsgURN: "whatsapp:250788123123", + Label: "Link Sending", + MsgText: "Link Sending https://link.com", + MsgURN: "whatsapp:250788123123", + MockResponses: map[string][]*httpx.MockResponse{ + "https://waba-v2.360dialog.io/messages": { + httpx.NewMockResponse(200, nil, []byte(`{ "messages": [{"id": "157b5e14568e8"}] }`)), + }, + }, MockResponseBody: `{ "messages": [{"id": "157b5e14568e8"}] }`, MockResponseStatus: 201, ExpectedRequests: []ExpectedRequest{ @@ -580,27 +625,34 @@ var SendTestCasesD3C = []OutgoingTestCase{ }, ExpectedMsgStatus: "W", ExpectedExtIDs: []string{"157b5e14568e8"}, - SendPrep: setSendURL, }, { - Label: "Error Bad JSON", - MsgText: "Error", - MsgURN: "whatsapp:250788123123", + Label: "Error Bad JSON", + MsgText: "Error", + MsgURN: "whatsapp:250788123123", + MockResponses: map[string][]*httpx.MockResponse{ + "https://waba-v2.360dialog.io/messages": { + httpx.NewMockResponse(403, nil, []byte(`bad json`)), + }, + }, MockResponseBody: `bad json`, MockResponseStatus: 403, ExpectedLogErrors: []*courier.ChannelError{courier.ErrorResponseUnparseable("JSON")}, ExpectedMsgStatus: "E", - SendPrep: setSendURL, }, { - Label: "Error", - MsgText: "Error", - MsgURN: "whatsapp:250788123123", + Label: "Error", + MsgText: "Error", + MsgURN: "whatsapp:250788123123", + MockResponses: map[string][]*httpx.MockResponse{ + "https://waba-v2.360dialog.io/messages": { + httpx.NewMockResponse(403, nil, []byte(`{ "error": {"message": "(#130429) Rate limit hit","code": 130429 }}`)), + }, + }, MockResponseBody: `{ "error": {"message": "(#130429) Rate limit hit","code": 130429 }}`, MockResponseStatus: 403, ExpectedLogErrors: []*courier.ChannelError{courier.ErrorExternal("130429", "(#130429) Rate limit hit")}, ExpectedMsgStatus: "E", - SendPrep: setSendURL, }, } From bd2b1e5412a57ca13182b552a2b761202940947b Mon Sep 17 00:00:00 2001 From: Norbert Kwizera Date: Thu, 22 Feb 2024 15:23:39 +0200 Subject: [PATCH 03/11] Use MockResponses and ExpectedRequests in DS tests --- handlers/discord/handler_test.go | 90 ++++++++++++++++++++++---------- 1 file changed, 61 insertions(+), 29 deletions(-) diff --git a/handlers/discord/handler_test.go b/handlers/discord/handler_test.go index 6ba2fa044..225fc1143 100644 --- a/handlers/discord/handler_test.go +++ b/handlers/discord/handler_test.go @@ -1,13 +1,12 @@ package discord import ( - "net/http/httptest" "testing" "github.com/nyaruka/courier" . "github.com/nyaruka/courier/handlers" "github.com/nyaruka/courier/test" - "github.com/nyaruka/courier/utils" + "github.com/nyaruka/gocommon/httpx" ) func TestIncoming(t *testing.T) { @@ -19,7 +18,7 @@ func BenchmarkHandler(b *testing.B) { } var testChannels = []courier.Channel{ - test.NewMockChannel("bac782c2-7aeb-4389-92f5-97887744f573", "DS", "discord", "US", map[string]any{courier.ConfigSendAuthorization: "sesame"}), + test.NewMockChannel("bac782c2-7aeb-4389-92f5-97887744f573", "DS", "discord", "US", map[string]any{courier.ConfigSendAuthorization: "sesame", courier.ConfigSendURL: "http://example.com/discord/rp/send"}), } var testCases = []IncomingTestCase{ @@ -79,55 +78,88 @@ var testCases = []IncomingTestCase{ var sendTestCases = []OutgoingTestCase{ { - Label: "Simple Send", - MsgText: "Hello World", - MsgURN: "discord:694634743521607802", - MockResponseStatus: 200, + Label: "Simple Send", + MsgText: "Hello World", + MsgURN: "discord:694634743521607802", + MockResponses: map[string][]*httpx.MockResponse{ + "http://example.com/discord/rp/send": { + httpx.NewMockResponse(200, nil, []byte(``)), + }, + }, ExpectedRequests: []ExpectedRequest{ { Path: "/discord/rp/send", Body: `{"id":"10","text":"Hello World","to":"694634743521607802","channel":"bac782c2-7aeb-4389-92f5-97887744f573","attachments":[],"quick_replies":null}`, }, }, - SendPrep: setSendURL, }, { - Label: "Attachment", - MsgText: "Hello World", - MsgAttachments: []string{"image/jpeg:https://foo.bar/image.jpg"}, - MsgURN: "discord:694634743521607802", - MockResponseStatus: 200, + Label: "Attachment", + MsgText: "Hello World", + MsgAttachments: []string{"image/jpeg:https://foo.bar/image.jpg"}, + MsgURN: "discord:694634743521607802", + MockResponses: map[string][]*httpx.MockResponse{ + "http://example.com/discord/rp/send": { + httpx.NewMockResponse(200, nil, []byte(``)), + }, + }, ExpectedRequests: []ExpectedRequest{ { Path: "/discord/rp/send", Body: `{"id":"10","text":"Hello World","to":"694634743521607802","channel":"bac782c2-7aeb-4389-92f5-97887744f573","attachments":["https://foo.bar/image.jpg"],"quick_replies":null}`, }, }, - SendPrep: setSendURL, }, { - Label: "Attachement and quick replies", - MsgText: "Hello World", - MsgAttachments: []string{"image/jpeg:https://foo.bar/image.jpg"}, - MsgQuickReplies: []string{"hello", "world"}, - MsgURN: "discord:694634743521607802", - MockResponseStatus: 200, + Label: "Attachement and quick replies", + MsgText: "Hello World", + MsgAttachments: []string{"image/jpeg:https://foo.bar/image.jpg"}, + MsgQuickReplies: []string{"hello", "world"}, + MsgURN: "discord:694634743521607802", + MockResponses: map[string][]*httpx.MockResponse{ + "http://example.com/discord/rp/send": { + httpx.NewMockResponse(200, nil, []byte(``)), + }, + }, ExpectedRequests: []ExpectedRequest{ { Path: "/discord/rp/send", Body: `{"id":"10","text":"Hello World","to":"694634743521607802","channel":"bac782c2-7aeb-4389-92f5-97887744f573","attachments":["https://foo.bar/image.jpg"],"quick_replies":["hello","world"]}`, }, }, - SendPrep: setSendURL, }, -} - -// setSendURL takes care of setting the send_url to our test server host -func setSendURL(s *httptest.Server, h courier.ChannelHandler, c courier.Channel, m courier.MsgOut) { - // this is actually a path, which we'll combine with the test server URL - sendURL := c.StringConfigForKey("send_path", "/discord/rp/send") - sendURL, _ = utils.AddURLPath(s.URL, sendURL) - c.(*test.MockChannel).SetConfig(courier.ConfigSendURL, sendURL) + { + Label: "Error Sending", + MsgText: "Error Sending", + MsgURN: "discord:694634743521607802", + MockResponses: map[string][]*httpx.MockResponse{ + "http://example.com/discord/rp/send": { + httpx.NewMockResponse(400, nil, []byte(``)), + }, + }, + ExpectedRequests: []ExpectedRequest{ + { + Path: "/discord/rp/send", + Body: `{"id":"10","text":"Error Sending","to":"694634743521607802","channel":"bac782c2-7aeb-4389-92f5-97887744f573","attachments":[],"quick_replies":null}`, + }, + }, + }, + { + Label: "Connection Error", + MsgText: "Error", + MsgURN: "discord:694634743521607802", + MockResponses: map[string][]*httpx.MockResponse{ + "http://example.com/discord/rp/send": { + httpx.NewMockResponse(500, nil, []byte(``)), + }, + }, + ExpectedRequests: []ExpectedRequest{ + { + Path: "/discord/rp/send", + Body: `{"id":"10","text":"Error","to":"694634743521607802","channel":"bac782c2-7aeb-4389-92f5-97887744f573","attachments":[],"quick_replies":null}`, + }, + }, + }, } func TestOutgoing(t *testing.T) { From f7bdefd753bb158c0a55e8bd822c6bb264e440a1 Mon Sep 17 00:00:00 2001 From: Norbert Kwizera Date: Thu, 22 Feb 2024 15:31:10 +0200 Subject: [PATCH 04/11] Use MockResponses and ExpectedRequests in DK tests --- handlers/dmark/handler_test.go | 93 +++++++++++++++++++++------------- 1 file changed, 58 insertions(+), 35 deletions(-) diff --git a/handlers/dmark/handler_test.go b/handlers/dmark/handler_test.go index ef51b15a1..40a439c84 100644 --- a/handlers/dmark/handler_test.go +++ b/handlers/dmark/handler_test.go @@ -1,13 +1,14 @@ package dmark import ( - "net/http/httptest" + "net/url" "testing" "time" "github.com/nyaruka/courier" . "github.com/nyaruka/courier/handlers" "github.com/nyaruka/courier/test" + "github.com/nyaruka/gocommon/httpx" ) var testChannels = []courier.Channel{ @@ -90,46 +91,68 @@ func BenchmarkHandler(b *testing.B) { RunChannelBenchmarks(b, testChannels, newHandler(), testCases) } -// setSendURL takes care of setting the sendURL to call -func setSendURL(s *httptest.Server, h courier.ChannelHandler, c courier.Channel, m courier.MsgOut) { - sendURL = s.URL -} - var defaultSendTestCases = []OutgoingTestCase{ { - Label: "Plain Send", - MsgText: "Simple Message ☺", - MsgURN: "tel:+250788383383", - ExpectedExtIDs: []string{"6b1c15d3-cba2-46f7-9a25-78265e58057d"}, - MockResponseBody: `{ "type": "MT", "sms_id": "6b1c15d3-cba2-46f7-9a25-78265e58057d" }`, - MockResponseStatus: 200, - ExpectedHeaders: map[string]string{"Authorization": "Token Authy"}, - ExpectedPostParams: map[string]string{"text": "Simple Message ☺", "receiver": "250788383383", "sender": "2020", "dlr_url": "https://localhost/c/dk/8eb23e93-5ecb-45ba-b726-3b064e0c56ab/status?id=10&status=%s"}, - ExpectedMsgStatus: "W", - SendPrep: setSendURL, + Label: "Plain Send", + MsgText: "Simple Message ☺", + MsgURN: "tel:+250788383383", + MockResponses: map[string][]*httpx.MockResponse{ + "https://smsapi1.dmarkmobile.com/sms/": { + httpx.NewMockResponse(200, nil, []byte(`{ "type": "MT", "sms_id": "6b1c15d3-cba2-46f7-9a25-78265e58057d" }`)), + }, + }, + ExpectedRequests: []ExpectedRequest{{ + Headers: map[string]string{"Authorization": "Token Authy"}, + Form: url.Values{ + "text": {"Simple Message ☺"}, + "receiver": {"250788383383"}, + "sender": {"2020"}, + "dlr_url": {"https://localhost/c/dk/8eb23e93-5ecb-45ba-b726-3b064e0c56ab/status?id=10&status=%s"}, + }, + }}, + ExpectedMsgStatus: "W", + ExpectedExtIDs: []string{"6b1c15d3-cba2-46f7-9a25-78265e58057d"}, }, { - Label: "Invalid Body", - MsgText: "Error Message", - MsgURN: "tel:+250788383383", - MockResponseBody: `{ "error": "failed" }`, - MockResponseStatus: 200, - ExpectedHeaders: map[string]string{"Authorization": "Token Authy"}, - ExpectedPostParams: map[string]string{"text": "Error Message", "receiver": "250788383383", "sender": "2020"}, - ExpectedMsgStatus: "E", - ExpectedLogErrors: []*courier.ChannelError{courier.ErrorResponseValueMissing("sms_id")}, - SendPrep: setSendURL, + Label: "Invalid Body", + MsgText: "Error Message", + MsgURN: "tel:+250788383383", + MockResponses: map[string][]*httpx.MockResponse{ + "https://smsapi1.dmarkmobile.com/sms/": { + httpx.NewMockResponse(200, nil, []byte(`{ "error": "failed" }`)), + }, + }, + ExpectedRequests: []ExpectedRequest{{ + Headers: map[string]string{"Authorization": "Token Authy"}, + Form: url.Values{ + "text": {"Error Message"}, + "receiver": {"250788383383"}, + "sender": {"2020"}, + "dlr_url": {"https://localhost/c/dk/8eb23e93-5ecb-45ba-b726-3b064e0c56ab/status?id=10&status=%s"}, + }, + }}, + ExpectedMsgStatus: "E", + ExpectedLogErrors: []*courier.ChannelError{courier.ErrorResponseValueMissing("sms_id")}, }, { - Label: "Error Sending", - MsgText: "Error Message", - MsgURN: "tel:+250788383383", - MockResponseBody: `{ "error": "failed" }`, - MockResponseStatus: 401, - ExpectedHeaders: map[string]string{"Authorization": "Token Authy"}, - ExpectedPostParams: map[string]string{"text": "Error Message", "receiver": "250788383383", "sender": "2020"}, - ExpectedMsgStatus: "E", - SendPrep: setSendURL, + Label: "Error Sending", + MsgText: "Error Message", + MsgURN: "tel:+250788383383", + MockResponses: map[string][]*httpx.MockResponse{ + "https://smsapi1.dmarkmobile.com/sms/": { + httpx.NewMockResponse(401, nil, []byte(`{ "error": "failed" }`)), + }, + }, + ExpectedRequests: []ExpectedRequest{{ + Headers: map[string]string{"Authorization": "Token Authy"}, + Form: url.Values{ + "text": {"Error Message"}, + "receiver": {"250788383383"}, + "sender": {"2020"}, + "dlr_url": {"https://localhost/c/dk/8eb23e93-5ecb-45ba-b726-3b064e0c56ab/status?id=10&status=%s"}, + }, + }}, + ExpectedMsgStatus: "E", }, } From 9b3f6bbd053bd512e4e4ac0d2da0ef856370419b Mon Sep 17 00:00:00 2001 From: Norbert Kwizera Date: Thu, 22 Feb 2024 15:37:07 +0200 Subject: [PATCH 05/11] Use MockResponses and ExpectedRequests in D3C tests --- handlers/dialog360/handler_test.go | 82 +++++++++++++----------------- 1 file changed, 36 insertions(+), 46 deletions(-) diff --git a/handlers/dialog360/handler_test.go b/handlers/dialog360/handler_test.go index 0b60ac6f5..47ec365a8 100644 --- a/handlers/dialog360/handler_test.go +++ b/handlers/dialog360/handler_test.go @@ -324,8 +324,6 @@ var SendTestCasesD3C = []OutgoingTestCase{ httpx.NewMockResponse(200, nil, []byte(`{ "messages": [{"id": "157b5e14568e8"}] }`)), }, }, - MockResponseBody: `{ "messages": [{"id": "157b5e14568e8"}] }`, - MockResponseStatus: 201, ExpectedRequests: []ExpectedRequest{ { Path: "/messages", @@ -344,8 +342,6 @@ var SendTestCasesD3C = []OutgoingTestCase{ httpx.NewMockResponse(200, nil, []byte(`{ "messages": [{"id": "157b5e14568e8"}] }`)), }, }, - MockResponseBody: `{ "messages": [{"id": "157b5e14568e8"}] }`, - MockResponseStatus: 201, ExpectedRequests: []ExpectedRequest{ { Path: "/messages", @@ -380,11 +376,9 @@ var SendTestCasesD3C = []OutgoingTestCase{ MsgAttachments: []string{"application/pdf:https://foo.bar/document.pdf"}, MockResponses: map[string][]*httpx.MockResponse{ "https://waba-v2.360dialog.io/messages": { - httpx.NewMockResponse(200, nil, []byte(`{ "messages": [{"id": "157b5e14568e8"}] }`)), + httpx.NewMockResponse(201, nil, []byte(`{ "messages": [{"id": "157b5e14568e8"}] }`)), }, }, - MockResponseBody: `{ "messages": [{"id": "157b5e14568e8"}] }`, - MockResponseStatus: 201, ExpectedRequests: []ExpectedRequest{ { Path: "/messages", @@ -401,11 +395,9 @@ var SendTestCasesD3C = []OutgoingTestCase{ MsgAttachments: []string{"image/jpeg:https://foo.bar/image.jpg"}, MockResponses: map[string][]*httpx.MockResponse{ "https://waba-v2.360dialog.io/messages": { - httpx.NewMockResponse(200, nil, []byte(`{ "messages": [{"id": "157b5e14568e8"}] }`)), + httpx.NewMockResponse(201, nil, []byte(`{ "messages": [{"id": "157b5e14568e8"}] }`)), }, }, - MockResponseBody: `{ "messages": [{"id": "157b5e14568e8"}] }`, - MockResponseStatus: 201, ExpectedRequests: []ExpectedRequest{ { Path: "/messages", @@ -422,11 +414,9 @@ var SendTestCasesD3C = []OutgoingTestCase{ MsgAttachments: []string{"video/mp4:https://foo.bar/video.mp4"}, MockResponses: map[string][]*httpx.MockResponse{ "https://waba-v2.360dialog.io/messages": { - httpx.NewMockResponse(200, nil, []byte(`{ "messages": [{"id": "157b5e14568e8"}] }`)), + httpx.NewMockResponse(201, nil, []byte(`{ "messages": [{"id": "157b5e14568e8"}] }`)), }, }, - MockResponseBody: `{ "messages": [{"id": "157b5e14568e8"}] }`, - MockResponseStatus: 201, ExpectedRequests: []ExpectedRequest{ { Path: "/messages", @@ -447,10 +437,9 @@ var SendTestCasesD3C = []OutgoingTestCase{ httpx.NewMockResponse(200, nil, []byte(`{ "messages": [{"id": "157b5e14568e8"}] }`)), }, }, - ExpectedMsgStatus: "W", - ExpectedExtIDs: []string{"157b5e14568e8"}, - MockResponseBody: `{ "messages": [{"id": "157b5e14568e8"}] }`, - MockResponseStatus: 200, + ExpectedMsgStatus: "W", + ExpectedExtIDs: []string{"157b5e14568e8"}, + ExpectedRequestBody: `{"messaging_product":"whatsapp","recipient_type":"individual","to":"250788123123","type":"template","template":{"name":"revive_issue","language":{"policy":"deterministic","code":"en_US"},"components":[{"type":"body","sub_type":"","index":"","parameters":[{"type":"text","text":"Chef"},{"type":"text","text":"tomorrow"}]}]}}`, }, { @@ -460,11 +449,10 @@ var SendTestCasesD3C = []OutgoingTestCase{ MsgQuickReplies: []string{"BUTTON1"}, MockResponses: map[string][]*httpx.MockResponse{ "https://waba-v2.360dialog.io/messages": { - httpx.NewMockResponse(200, nil, []byte(`{ "messages": [{"id": "157b5e14568e8"}] }`)), + httpx.NewMockResponse(201, nil, []byte(`{ "messages": [{"id": "157b5e14568e8"}] }`)), }, }, - MockResponseBody: `{ "messages": [{"id": "157b5e14568e8"}] }`, - MockResponseStatus: 201, + ExpectedRequestBody: `{"messaging_product":"whatsapp","recipient_type":"individual","to":"250788123123","type":"interactive","interactive":{"type":"button","body":{"text":"Interactive Button Msg"},"action":{"buttons":[{"type":"reply","reply":{"id":"0","title":"BUTTON1"}}]}}}`, ExpectedMsgStatus: "W", ExpectedExtIDs: []string{"157b5e14568e8"}, @@ -476,11 +464,10 @@ var SendTestCasesD3C = []OutgoingTestCase{ MsgQuickReplies: []string{"ROW1", "ROW2", "ROW3", "ROW4"}, MockResponses: map[string][]*httpx.MockResponse{ "https://waba-v2.360dialog.io/messages": { - httpx.NewMockResponse(200, nil, []byte(`{ "messages": [{"id": "157b5e14568e8"}] }`)), + httpx.NewMockResponse(201, nil, []byte(`{ "messages": [{"id": "157b5e14568e8"}] }`)), }, }, - MockResponseBody: `{ "messages": [{"id": "157b5e14568e8"}] }`, - MockResponseStatus: 201, + ExpectedRequestBody: `{"messaging_product":"whatsapp","recipient_type":"individual","to":"250788123123","type":"interactive","interactive":{"type":"list","body":{"text":"Interactive List Msg"},"action":{"button":"Menu","sections":[{"rows":[{"id":"0","title":"ROW1"},{"id":"1","title":"ROW2"},{"id":"2","title":"ROW3"},{"id":"3","title":"ROW4"}]}]}}}`, ExpectedMsgStatus: "W", ExpectedExtIDs: []string{"157b5e14568e8"}, @@ -493,11 +480,10 @@ var SendTestCasesD3C = []OutgoingTestCase{ MsgQuickReplies: []string{"ROW1", "ROW2", "ROW3", "ROW4"}, MockResponses: map[string][]*httpx.MockResponse{ "https://waba-v2.360dialog.io/messages": { - httpx.NewMockResponse(200, nil, []byte(`{ "messages": [{"id": "157b5e14568e8"}] }`)), + httpx.NewMockResponse(201, nil, []byte(`{ "messages": [{"id": "157b5e14568e8"}] }`)), }, }, - MockResponseBody: `{ "messages": [{"id": "157b5e14568e8"}] }`, - MockResponseStatus: 201, + ExpectedRequestBody: `{"messaging_product":"whatsapp","recipient_type":"individual","to":"250788123123","type":"interactive","interactive":{"type":"list","body":{"text":"Hola"},"action":{"button":"Menú","sections":[{"rows":[{"id":"0","title":"ROW1"},{"id":"1","title":"ROW2"},{"id":"2","title":"ROW3"},{"id":"3","title":"ROW4"}]}]}}}`, ExpectedMsgStatus: "W", ExpectedExtIDs: []string{"157b5e14568e8"}, @@ -510,11 +496,10 @@ var SendTestCasesD3C = []OutgoingTestCase{ MsgAttachments: []string{"image/jpeg:https://foo.bar/image.jpg"}, MockResponses: map[string][]*httpx.MockResponse{ "https://waba-v2.360dialog.io/messages": { - httpx.NewMockResponse(200, nil, []byte(`{ "messages": [{"id": "157b5e14568e8"}] }`)), + httpx.NewMockResponse(201, nil, []byte(`{ "messages": [{"id": "157b5e14568e8"}] }`)), }, }, - MockResponseBody: `{ "messages": [{"id": "157b5e14568e8"}] }`, - MockResponseStatus: 201, + ExpectedRequests: []ExpectedRequest{ { Path: "/messages", @@ -532,11 +517,10 @@ var SendTestCasesD3C = []OutgoingTestCase{ MsgAttachments: []string{"video/mp4:https://foo.bar/video.mp4"}, MockResponses: map[string][]*httpx.MockResponse{ "https://waba-v2.360dialog.io/messages": { - httpx.NewMockResponse(200, nil, []byte(`{ "messages": [{"id": "157b5e14568e8"}] }`)), + httpx.NewMockResponse(201, nil, []byte(`{ "messages": [{"id": "157b5e14568e8"}] }`)), }, }, - MockResponseBody: `{ "messages": [{"id": "157b5e14568e8"}] }`, - MockResponseStatus: 201, + ExpectedRequests: []ExpectedRequest{ { Path: "/messages", @@ -554,11 +538,10 @@ var SendTestCasesD3C = []OutgoingTestCase{ MsgAttachments: []string{"document/pdf:https://foo.bar/document.pdf"}, MockResponses: map[string][]*httpx.MockResponse{ "https://waba-v2.360dialog.io/messages": { - httpx.NewMockResponse(200, nil, []byte(`{ "messages": [{"id": "157b5e14568e8"}] }`)), + httpx.NewMockResponse(201, nil, []byte(`{ "messages": [{"id": "157b5e14568e8"}] }`)), }, }, - MockResponseBody: `{ "messages": [{"id": "157b5e14568e8"}] }`, - MockResponseStatus: 201, + ExpectedRequests: []ExpectedRequest{ { Path: "/messages", @@ -612,11 +595,10 @@ var SendTestCasesD3C = []OutgoingTestCase{ MsgURN: "whatsapp:250788123123", MockResponses: map[string][]*httpx.MockResponse{ "https://waba-v2.360dialog.io/messages": { - httpx.NewMockResponse(200, nil, []byte(`{ "messages": [{"id": "157b5e14568e8"}] }`)), + httpx.NewMockResponse(201, nil, []byte(`{ "messages": [{"id": "157b5e14568e8"}] }`)), }, }, - MockResponseBody: `{ "messages": [{"id": "157b5e14568e8"}] }`, - MockResponseStatus: 201, + ExpectedRequests: []ExpectedRequest{ { Path: "/messages", @@ -635,10 +617,14 @@ var SendTestCasesD3C = []OutgoingTestCase{ httpx.NewMockResponse(403, nil, []byte(`bad json`)), }, }, - MockResponseBody: `bad json`, - MockResponseStatus: 403, - ExpectedLogErrors: []*courier.ChannelError{courier.ErrorResponseUnparseable("JSON")}, - ExpectedMsgStatus: "E", + ExpectedRequests: []ExpectedRequest{ + { + Path: "/messages", + Body: `{"messaging_product":"whatsapp","recipient_type":"individual","to":"250788123123","type":"text","text":{"body":"Error","preview_url":false}}`, + }, + }, + ExpectedLogErrors: []*courier.ChannelError{courier.ErrorResponseUnparseable("JSON")}, + ExpectedMsgStatus: "E", }, { Label: "Error", @@ -649,10 +635,14 @@ var SendTestCasesD3C = []OutgoingTestCase{ httpx.NewMockResponse(403, nil, []byte(`{ "error": {"message": "(#130429) Rate limit hit","code": 130429 }}`)), }, }, - MockResponseBody: `{ "error": {"message": "(#130429) Rate limit hit","code": 130429 }}`, - MockResponseStatus: 403, - ExpectedLogErrors: []*courier.ChannelError{courier.ErrorExternal("130429", "(#130429) Rate limit hit")}, - ExpectedMsgStatus: "E", + ExpectedRequests: []ExpectedRequest{ + { + Path: "/messages", + Body: `{"messaging_product":"whatsapp","recipient_type":"individual","to":"250788123123","type":"text","text":{"body":"Error","preview_url":false}}`, + }, + }, + ExpectedLogErrors: []*courier.ChannelError{courier.ErrorExternal("130429", "(#130429) Rate limit hit")}, + ExpectedMsgStatus: "E", }, } From d6ef57c2870161e6e6c2c8be731438d90e9a28b5 Mon Sep 17 00:00:00 2001 From: Norbert Kwizera Date: Thu, 22 Feb 2024 15:41:37 +0200 Subject: [PATCH 06/11] Rework DK handler to new send --- handlers/dmark/handler.go | 32 ++++++++++---------------------- handlers/dmark/handler_test.go | 8 +++----- 2 files changed, 13 insertions(+), 27 deletions(-) diff --git a/handlers/dmark/handler.go b/handlers/dmark/handler.go index eebebb1a6..dbe4cd69b 100644 --- a/handlers/dmark/handler.go +++ b/handlers/dmark/handler.go @@ -106,23 +106,16 @@ func (h *handler) receiveStatus(ctx context.Context, channel courier.Channel, w } func (h *handler) Send(ctx context.Context, msg courier.MsgOut, res *courier.SendResult, clog *courier.ChannelLog) error { - // TODO convert functionality from legacy method below - return nil -} - -func (h *handler) SendLegacy(ctx context.Context, msg courier.MsgOut, clog *courier.ChannelLog) (courier.StatusUpdate, error) { // get our authentication token auth := msg.Channel().StringConfigForKey(courier.ConfigAuthToken, "") if auth == "" { - return nil, fmt.Errorf("no authorization token set for DK channel") + return courier.ErrChannelConfig } - callbackDomain := msg.Channel().CallbackDomain(h.Server().Config().Domain) dlrURL := fmt.Sprintf("https://%s%s%s/status?id=%s&status=%%s", callbackDomain, "/c/dk/", msg.Channel().UUID(), msg.ID().String()) - status := h.Backend().NewStatusUpdate(msg.Channel(), msg.ID(), courier.MsgStatusErrored, clog) parts := handlers.SplitMsgByChannel(msg.Channel(), msg.Text(), maxMsgLength) - for i, part := range parts { + for _, part := range parts { form := url.Values{ "sender": []string{strings.TrimLeft(msg.Channel().Address(), "+")}, "receiver": []string{strings.TrimLeft(msg.URN().Path(), "+")}, @@ -132,32 +125,27 @@ func (h *handler) SendLegacy(ctx context.Context, msg courier.MsgOut, clog *cour req, err := http.NewRequest(http.MethodPost, sendURL, strings.NewReader(form.Encode())) if err != nil { - return nil, err + return err } req.Header.Set("Content-Type", "application/x-www-form-urlencoded") req.Header.Set("Accept", "application/json") req.Header.Set("Authorization", fmt.Sprintf("Token %s", auth)) resp, respBody, err := h.RequestHTTP(req, clog) - if err != nil || resp.StatusCode/100 != 2 { - return status, nil + if err != nil || resp.StatusCode/100 == 5 { + return courier.ErrConnectionFailed + } else if resp.StatusCode/100 != 2 { + return courier.ErrResponseStatus } // grab the external id externalID, err := jsonparser.GetString(respBody, "sms_id") if err != nil { - clog.Error(courier.ErrorResponseValueMissing("sms_id")) - return status, nil + return courier.ErrFailedWithReason("", "response missing sms_id") } - // if this is our first message, record the external id - if i == 0 { - status.SetExternalID(externalID) - } - - // this was wired successfully - status.SetStatus(courier.MsgStatusWired) + res.AddExternalID(externalID) } - return status, nil + return nil } diff --git a/handlers/dmark/handler_test.go b/handlers/dmark/handler_test.go index 40a439c84..b57eb10fd 100644 --- a/handlers/dmark/handler_test.go +++ b/handlers/dmark/handler_test.go @@ -110,8 +110,7 @@ var defaultSendTestCases = []OutgoingTestCase{ "dlr_url": {"https://localhost/c/dk/8eb23e93-5ecb-45ba-b726-3b064e0c56ab/status?id=10&status=%s"}, }, }}, - ExpectedMsgStatus: "W", - ExpectedExtIDs: []string{"6b1c15d3-cba2-46f7-9a25-78265e58057d"}, + ExpectedExtIDs: []string{"6b1c15d3-cba2-46f7-9a25-78265e58057d"}, }, { Label: "Invalid Body", @@ -131,8 +130,7 @@ var defaultSendTestCases = []OutgoingTestCase{ "dlr_url": {"https://localhost/c/dk/8eb23e93-5ecb-45ba-b726-3b064e0c56ab/status?id=10&status=%s"}, }, }}, - ExpectedMsgStatus: "E", - ExpectedLogErrors: []*courier.ChannelError{courier.ErrorResponseValueMissing("sms_id")}, + ExpectedError: courier.ErrFailedWithReason("", "response missing sms_id"), }, { Label: "Error Sending", @@ -152,7 +150,7 @@ var defaultSendTestCases = []OutgoingTestCase{ "dlr_url": {"https://localhost/c/dk/8eb23e93-5ecb-45ba-b726-3b064e0c56ab/status?id=10&status=%s"}, }, }}, - ExpectedMsgStatus: "E", + ExpectedError: courier.ErrResponseStatus, }, } From a74c3935ec2064b1726deb40fc48bcfc50486dfc Mon Sep 17 00:00:00 2001 From: Norbert Kwizera Date: Thu, 22 Feb 2024 15:49:54 +0200 Subject: [PATCH 07/11] Rework DS handler to new send --- handlers/discord/handler.go | 25 ++++++++----------------- handlers/discord/handler_test.go | 2 ++ 2 files changed, 10 insertions(+), 17 deletions(-) diff --git a/handlers/discord/handler.go b/handlers/discord/handler.go index d95609e96..19df1c856 100644 --- a/handlers/discord/handler.go +++ b/handlers/discord/handler.go @@ -143,22 +143,14 @@ func (h *handler) receiveStatus(ctx context.Context, statusString string, channe } func (h *handler) Send(ctx context.Context, msg courier.MsgOut, res *courier.SendResult, clog *courier.ChannelLog) error { - // TODO convert functionality from legacy method below - return nil -} -func (h *handler) SendLegacy(ctx context.Context, msg courier.MsgOut, clog *courier.ChannelLog) (courier.StatusUpdate, error) { sendURL := msg.Channel().StringConfigForKey(courier.ConfigSendURL, "") if sendURL == "" { - return nil, fmt.Errorf("no send url set for DS channel") + return courier.ErrChannelConfig } - // figure out what encoding to tell kannel to send as sendMethod := http.MethodPost - // sendBody := msg.Channel().StringConfigForKey(courier.ConfigSendBody, "") contentTypeHeader := jsonMimeTypeType - - status := h.Backend().NewStatusUpdate(msg.Channel(), msg.ID(), courier.MsgStatusErrored, clog) attachmentURLs := []string{} for _, attachment := range msg.Attachments() { _, attachmentURL := handlers.SplitAttachment(attachment) @@ -186,13 +178,13 @@ func (h *handler) SendLegacy(ctx context.Context, msg courier.MsgOut, clog *cour var body io.Reader marshalled, err := json.Marshal(ourMessage) if err != nil { - return nil, err + return err } body = bytes.NewReader(marshalled) req, err := http.NewRequest(sendMethod, sendURL, body) if err != nil { - return nil, err + return err } req.Header.Set("Content-Type", contentTypeHeader) @@ -202,12 +194,11 @@ func (h *handler) SendLegacy(ctx context.Context, msg courier.MsgOut, clog *cour } resp, _, err := h.RequestHTTP(req, clog) - if err != nil || resp.StatusCode/100 != 2 { - return status, nil + if err != nil || resp.StatusCode/100 == 5 { + return courier.ErrConnectionFailed + } else if resp.StatusCode/100 != 2 { + return courier.ErrResponseStatus } - // If we don't have an error, set the message as wired and move on - status.SetStatus(courier.MsgStatusWired) - - return status, nil + return nil } diff --git a/handlers/discord/handler_test.go b/handlers/discord/handler_test.go index 225fc1143..a4b4419b4 100644 --- a/handlers/discord/handler_test.go +++ b/handlers/discord/handler_test.go @@ -143,6 +143,7 @@ var sendTestCases = []OutgoingTestCase{ Body: `{"id":"10","text":"Error Sending","to":"694634743521607802","channel":"bac782c2-7aeb-4389-92f5-97887744f573","attachments":[],"quick_replies":null}`, }, }, + ExpectedError: courier.ErrResponseStatus, }, { Label: "Connection Error", @@ -159,6 +160,7 @@ var sendTestCases = []OutgoingTestCase{ Body: `{"id":"10","text":"Error","to":"694634743521607802","channel":"bac782c2-7aeb-4389-92f5-97887744f573","attachments":[],"quick_replies":null}`, }, }, + ExpectedError: courier.ErrConnectionFailed, }, } From a1727d183495ae873c841a78befea260f795e53f Mon Sep 17 00:00:00 2001 From: Norbert Kwizera Date: Thu, 22 Feb 2024 16:24:50 +0200 Subject: [PATCH 08/11] Rework D3C handler to new send --- handlers/dialog360/handler.go | 66 +++++++++------------------- handlers/dialog360/handler_test.go | 69 ++++++++++++------------------ 2 files changed, 48 insertions(+), 87 deletions(-) diff --git a/handlers/dialog360/handler.go b/handlers/dialog360/handler.go index f25f09c94..3b15e8142 100644 --- a/handlers/dialog360/handler.go +++ b/handlers/dialog360/handler.go @@ -289,29 +289,17 @@ func (h *handler) resolveMediaURL(channel courier.Channel, mediaID string, clog } func (h *handler) Send(ctx context.Context, msg courier.MsgOut, res *courier.SendResult, clog *courier.ChannelLog) error { - // TODO convert functionality from legacy method below - return nil -} - -func (h *handler) SendLegacy(ctx context.Context, msg courier.MsgOut, clog *courier.ChannelLog) (courier.StatusUpdate, error) { - conn := h.Backend().RedisPool().Get() - defer conn.Close() - // get our token - // can't do anything without an access token accessToken := msg.Channel().StringConfigForKey(courier.ConfigAuthToken, "") - if accessToken == "" { - return nil, fmt.Errorf("missing token for D3C channel") - } - urlStr := msg.Channel().StringConfigForKey(courier.ConfigBaseURL, "") url, err := url.Parse(urlStr) - if err != nil { - return nil, fmt.Errorf("invalid base url set for D3C channel: %s", err) + if accessToken == "" || err != nil { + return courier.ErrChannelConfig } sendURL, _ := url.Parse("/messages") - status := h.Backend().NewStatusUpdate(msg.Channel(), msg.ID(), courier.MsgStatusErrored, clog) + conn := h.Backend().RedisPool().Get() + defer conn.Close() hasCaption := false @@ -331,7 +319,7 @@ func (h *handler) SendLegacy(ctx context.Context, msg courier.MsgOut, clog *cour // do we have a template? templating, err := whatsapp.GetTemplating(msg) if err != nil { - return nil, errors.Wrapf(err, "unable to decode template: %s for channel: %s", string(msg.Metadata()), msg.Channel().UUID()) + return errors.Wrapf(err, "unable to decode template: %s for channel: %s", string(msg.Metadata()), msg.Channel().UUID()) } if templating != nil { payload.Type = "template" @@ -394,7 +382,7 @@ func (h *handler) SendLegacy(ctx context.Context, msg courier.MsgOut, clog *cour payload.Interactive = &interactive } else { - return nil, fmt.Errorf("too many quick replies WAC supports only up to 10 quick replies") + return fmt.Errorf("too many quick replies WAC supports only up to 10 quick replies") } } else { // this is still a msg part @@ -480,7 +468,7 @@ func (h *handler) SendLegacy(ctx context.Context, msg courier.MsgOut, clog *cour } else if attType == "document" { filename, err := utils.BasePathForURL(attURL) if err != nil { - return nil, err + return err } document := whatsapp.Media{ Link: attURL, @@ -494,14 +482,10 @@ func (h *handler) SendLegacy(ctx context.Context, msg courier.MsgOut, clog *cour Document *whatsapp.Media "json:\"document,omitempty\"" }{Type: "document", Document: &document} } else if attType == "audio" { - var zeroIndex bool - if i == 0 { - zeroIndex = true - } payloadAudio = whatsapp.SendRequest{MessagingProduct: "whatsapp", RecipientType: "individual", To: msg.URN().Path(), Type: "audio", Audio: &whatsapp.Media{Link: attURL}} - status, err := h.requestD3C(payloadAudio, accessToken, status, sendURL, zeroIndex, clog) + err := h.requestD3C(payloadAudio, accessToken, res, sendURL, clog) if err != nil { - return status, nil + return nil } } else { interactive.Type = "button" @@ -549,7 +533,7 @@ func (h *handler) SendLegacy(ctx context.Context, msg courier.MsgOut, clog *cour payload.Interactive = &interactive } else { - return nil, fmt.Errorf("too many quick replies WAC supports only up to 10 quick replies") + return fmt.Errorf("too many quick replies WAC supports only up to 10 quick replies") } } else { // this is still a msg part @@ -563,29 +547,25 @@ func (h *handler) SendLegacy(ctx context.Context, msg courier.MsgOut, clog *cour } } - var zeroIndex bool - if i == 0 { - zeroIndex = true - } - - status, err := h.requestD3C(payload, accessToken, status, sendURL, zeroIndex, clog) + err := h.requestD3C(payload, accessToken, res, sendURL, clog) if err != nil { - return status, err + return err } if hasCaption { break } } - return status, nil + + return nil } -func (h *handler) requestD3C(payload whatsapp.SendRequest, accessToken string, status courier.StatusUpdate, wacPhoneURL *url.URL, zeroIndex bool, clog *courier.ChannelLog) (courier.StatusUpdate, error) { +func (h *handler) requestD3C(payload whatsapp.SendRequest, accessToken string, res *courier.SendResult, wacPhoneURL *url.URL, clog *courier.ChannelLog) error { jsonBody := jsonx.MustMarshal(payload) req, err := http.NewRequest(http.MethodPost, wacPhoneURL.String(), bytes.NewReader(jsonBody)) if err != nil { - return nil, err + return err } req.Header.Set(d3AuthorizationKey, accessToken) @@ -596,20 +576,16 @@ func (h *handler) requestD3C(payload whatsapp.SendRequest, accessToken string, s respPayload := &whatsapp.SendResponse{} err = json.Unmarshal(respBody, respPayload) if err != nil { - clog.Error(courier.ErrorResponseUnparseable("JSON")) - return status, nil + return courier.ErrResponseUnparseable } if respPayload.Error.Code != 0 { - clog.Error(courier.ErrorExternal(strconv.Itoa(respPayload.Error.Code), respPayload.Error.Message)) - return status, nil + return courier.ErrFailedWithReason(strconv.Itoa(respPayload.Error.Code), respPayload.Error.Message) } externalID := respPayload.Messages[0].ID - if zeroIndex && externalID != "" { - status.SetExternalID(externalID) + if externalID != "" { + res.AddExternalID(externalID) } - // this was wired successfully - status.SetStatus(courier.MsgStatusWired) - return status, nil + return nil } diff --git a/handlers/dialog360/handler_test.go b/handlers/dialog360/handler_test.go index 47ec365a8..983bda5c4 100644 --- a/handlers/dialog360/handler_test.go +++ b/handlers/dialog360/handler_test.go @@ -330,8 +330,7 @@ var SendTestCasesD3C = []OutgoingTestCase{ Body: `{"messaging_product":"whatsapp","recipient_type":"individual","to":"250788123123","type":"text","text":{"body":"Simple Message","preview_url":false}}`, }, }, - ExpectedMsgStatus: "W", - ExpectedExtIDs: []string{"157b5e14568e8"}, + ExpectedExtIDs: []string{"157b5e14568e8"}, }, { Label: "Unicode Send", @@ -348,8 +347,7 @@ var SendTestCasesD3C = []OutgoingTestCase{ Body: `{"messaging_product":"whatsapp","recipient_type":"individual","to":"250788123123","type":"text","text":{"body":"☺","preview_url":false}}`, }, }, - ExpectedMsgStatus: "W", - ExpectedExtIDs: []string{"157b5e14568e8"}, + ExpectedExtIDs: []string{"157b5e14568e8"}, }, { Label: "Audio Send", @@ -366,8 +364,7 @@ var SendTestCasesD3C = []OutgoingTestCase{ {Body: `{"messaging_product":"whatsapp","recipient_type":"individual","to":"250788123123","type":"audio","audio":{"link":"https://foo.bar/audio.mp3"}}`}, {Body: `{"messaging_product":"whatsapp","recipient_type":"individual","to":"250788123123","type":"text","text":{"body":"audio caption","preview_url":false}}`}, }, - ExpectedMsgStatus: "W", - ExpectedExtIDs: []string{"157b5e14568e8"}, + ExpectedExtIDs: []string{"157b5e14568e8", "157b5e14568e8"}, }, { Label: "Document Send", @@ -385,8 +382,7 @@ var SendTestCasesD3C = []OutgoingTestCase{ Body: `{"messaging_product":"whatsapp","recipient_type":"individual","to":"250788123123","type":"document","document":{"link":"https://foo.bar/document.pdf","caption":"document caption","filename":"document.pdf"}}`, }, }, - ExpectedMsgStatus: "W", - ExpectedExtIDs: []string{"157b5e14568e8"}, + ExpectedExtIDs: []string{"157b5e14568e8"}, }, { Label: "Image Send", @@ -404,8 +400,7 @@ var SendTestCasesD3C = []OutgoingTestCase{ Body: `{"messaging_product":"whatsapp","recipient_type":"individual","to":"250788123123","type":"image","image":{"link":"https://foo.bar/image.jpg","caption":"image caption"}}`, }, }, - ExpectedMsgStatus: "W", - ExpectedExtIDs: []string{"157b5e14568e8"}, + ExpectedExtIDs: []string{"157b5e14568e8"}, }, { Label: "Video Send", @@ -423,8 +418,7 @@ var SendTestCasesD3C = []OutgoingTestCase{ Body: `{"messaging_product":"whatsapp","recipient_type":"individual","to":"250788123123","type":"video","video":{"link":"https://foo.bar/video.mp4","caption":"video caption"}}`, }, }, - ExpectedMsgStatus: "W", - ExpectedExtIDs: []string{"157b5e14568e8"}, + ExpectedExtIDs: []string{"157b5e14568e8"}, }, { Label: "Template Send", @@ -437,8 +431,7 @@ var SendTestCasesD3C = []OutgoingTestCase{ httpx.NewMockResponse(200, nil, []byte(`{ "messages": [{"id": "157b5e14568e8"}] }`)), }, }, - ExpectedMsgStatus: "W", - ExpectedExtIDs: []string{"157b5e14568e8"}, + ExpectedExtIDs: []string{"157b5e14568e8"}, ExpectedRequestBody: `{"messaging_product":"whatsapp","recipient_type":"individual","to":"250788123123","type":"template","template":{"name":"revive_issue","language":{"policy":"deterministic","code":"en_US"},"components":[{"type":"body","sub_type":"","index":"","parameters":[{"type":"text","text":"Chef"},{"type":"text","text":"tomorrow"}]}]}}`, }, @@ -452,10 +445,10 @@ var SendTestCasesD3C = []OutgoingTestCase{ httpx.NewMockResponse(201, nil, []byte(`{ "messages": [{"id": "157b5e14568e8"}] }`)), }, }, - - ExpectedRequestBody: `{"messaging_product":"whatsapp","recipient_type":"individual","to":"250788123123","type":"interactive","interactive":{"type":"button","body":{"text":"Interactive Button Msg"},"action":{"buttons":[{"type":"reply","reply":{"id":"0","title":"BUTTON1"}}]}}}`, - ExpectedMsgStatus: "W", - ExpectedExtIDs: []string{"157b5e14568e8"}, + ExpectedRequests: []ExpectedRequest{{ + Body: `{"messaging_product":"whatsapp","recipient_type":"individual","to":"250788123123","type":"interactive","interactive":{"type":"button","body":{"text":"Interactive Button Msg"},"action":{"buttons":[{"type":"reply","reply":{"id":"0","title":"BUTTON1"}}]}}}`, + }}, + ExpectedExtIDs: []string{"157b5e14568e8"}, }, { Label: "Interactive List Message Send", @@ -467,10 +460,10 @@ var SendTestCasesD3C = []OutgoingTestCase{ httpx.NewMockResponse(201, nil, []byte(`{ "messages": [{"id": "157b5e14568e8"}] }`)), }, }, - - ExpectedRequestBody: `{"messaging_product":"whatsapp","recipient_type":"individual","to":"250788123123","type":"interactive","interactive":{"type":"list","body":{"text":"Interactive List Msg"},"action":{"button":"Menu","sections":[{"rows":[{"id":"0","title":"ROW1"},{"id":"1","title":"ROW2"},{"id":"2","title":"ROW3"},{"id":"3","title":"ROW4"}]}]}}}`, - ExpectedMsgStatus: "W", - ExpectedExtIDs: []string{"157b5e14568e8"}, + ExpectedRequests: []ExpectedRequest{{ + Body: `{"messaging_product":"whatsapp","recipient_type":"individual","to":"250788123123","type":"interactive","interactive":{"type":"list","body":{"text":"Interactive List Msg"},"action":{"button":"Menu","sections":[{"rows":[{"id":"0","title":"ROW1"},{"id":"1","title":"ROW2"},{"id":"2","title":"ROW3"},{"id":"3","title":"ROW4"}]}]}}}`, + }}, + ExpectedExtIDs: []string{"157b5e14568e8"}, }, { Label: "Interactive List Message Send In Spanish", @@ -483,10 +476,10 @@ var SendTestCasesD3C = []OutgoingTestCase{ httpx.NewMockResponse(201, nil, []byte(`{ "messages": [{"id": "157b5e14568e8"}] }`)), }, }, - - ExpectedRequestBody: `{"messaging_product":"whatsapp","recipient_type":"individual","to":"250788123123","type":"interactive","interactive":{"type":"list","body":{"text":"Hola"},"action":{"button":"Menú","sections":[{"rows":[{"id":"0","title":"ROW1"},{"id":"1","title":"ROW2"},{"id":"2","title":"ROW3"},{"id":"3","title":"ROW4"}]}]}}}`, - ExpectedMsgStatus: "W", - ExpectedExtIDs: []string{"157b5e14568e8"}, + ExpectedRequests: []ExpectedRequest{{ + Body: `{"messaging_product":"whatsapp","recipient_type":"individual","to":"250788123123","type":"interactive","interactive":{"type":"list","body":{"text":"Hola"},"action":{"button":"Menú","sections":[{"rows":[{"id":"0","title":"ROW1"},{"id":"1","title":"ROW2"},{"id":"2","title":"ROW3"},{"id":"3","title":"ROW4"}]}]}}}`, + }}, + ExpectedExtIDs: []string{"157b5e14568e8"}, }, { Label: "Interactive Button Message Send with image attachment", @@ -506,8 +499,7 @@ var SendTestCasesD3C = []OutgoingTestCase{ Body: `{"messaging_product":"whatsapp","recipient_type":"individual","to":"250788123123","type":"interactive","interactive":{"type":"button","header":{"type":"image","image":{"link":"https://foo.bar/image.jpg"}},"body":{"text":"Interactive Button Msg"},"action":{"buttons":[{"type":"reply","reply":{"id":"0","title":"BUTTON1"}}]}}}`, }, }, - ExpectedMsgStatus: "W", - ExpectedExtIDs: []string{"157b5e14568e8"}, + ExpectedExtIDs: []string{"157b5e14568e8"}, }, { Label: "Interactive Button Message Send with video attachment", @@ -527,8 +519,7 @@ var SendTestCasesD3C = []OutgoingTestCase{ Body: `{"messaging_product":"whatsapp","recipient_type":"individual","to":"250788123123","type":"interactive","interactive":{"type":"button","header":{"type":"video","video":{"link":"https://foo.bar/video.mp4"}},"body":{"text":"Interactive Button Msg"},"action":{"buttons":[{"type":"reply","reply":{"id":"0","title":"BUTTON1"}}]}}}`, }, }, - ExpectedMsgStatus: "W", - ExpectedExtIDs: []string{"157b5e14568e8"}, + ExpectedExtIDs: []string{"157b5e14568e8"}, }, { Label: "Interactive Button Message Send with document attachment", @@ -548,8 +539,7 @@ var SendTestCasesD3C = []OutgoingTestCase{ Body: `{"messaging_product":"whatsapp","recipient_type":"individual","to":"250788123123","type":"interactive","interactive":{"type":"button","header":{"type":"document","document":{"link":"https://foo.bar/document.pdf","filename":"document.pdf"}},"body":{"text":"Interactive Button Msg"},"action":{"buttons":[{"type":"reply","reply":{"id":"0","title":"BUTTON1"}}]}}}`, }, }, - ExpectedMsgStatus: "W", - ExpectedExtIDs: []string{"157b5e14568e8"}, + ExpectedExtIDs: []string{"157b5e14568e8"}, }, { Label: "Interactive Button Message Send with audio attachment", @@ -567,8 +557,7 @@ var SendTestCasesD3C = []OutgoingTestCase{ {Body: `{"messaging_product":"whatsapp","recipient_type":"individual","to":"250788123123","type":"audio","audio":{"link":"https://foo.bar/audio.mp3"}}`}, {Body: `{"messaging_product":"whatsapp","recipient_type":"individual","to":"250788123123","type":"interactive","interactive":{"type":"button","body":{"text":"Interactive Button Msg"},"action":{"buttons":[{"type":"reply","reply":{"id":"0","title":"ROW1"}},{"type":"reply","reply":{"id":"1","title":"ROW2"}},{"type":"reply","reply":{"id":"2","title":"ROW3"}}]}}}`}, }, - ExpectedMsgStatus: "W", - ExpectedExtIDs: []string{"157b5e14568e8"}, + ExpectedExtIDs: []string{"157b5e14568e8", "157b5e14568e8"}, }, { Label: "Interactive List Message Send with attachment", @@ -586,8 +575,7 @@ var SendTestCasesD3C = []OutgoingTestCase{ {Body: `{"messaging_product":"whatsapp","recipient_type":"individual","to":"250788123123","type":"image","image":{"link":"https://foo.bar/image.jpg"}}`}, {Body: `{"messaging_product":"whatsapp","recipient_type":"individual","to":"250788123123","type":"interactive","interactive":{"type":"list","body":{"text":"Interactive List Msg"},"action":{"button":"Menu","sections":[{"rows":[{"id":"0","title":"ROW1"},{"id":"1","title":"ROW2"},{"id":"2","title":"ROW3"},{"id":"3","title":"ROW4"}]}]}}}`}, }, - ExpectedMsgStatus: "W", - ExpectedExtIDs: []string{"157b5e14568e8"}, + ExpectedExtIDs: []string{"157b5e14568e8", "157b5e14568e8"}, }, { Label: "Link Sending", @@ -605,8 +593,7 @@ var SendTestCasesD3C = []OutgoingTestCase{ Body: `{"messaging_product":"whatsapp","recipient_type":"individual","to":"250788123123","type":"text","text":{"body":"Link Sending https://link.com","preview_url":true}}`, }, }, - ExpectedMsgStatus: "W", - ExpectedExtIDs: []string{"157b5e14568e8"}, + ExpectedExtIDs: []string{"157b5e14568e8"}, }, { Label: "Error Bad JSON", @@ -623,8 +610,7 @@ var SendTestCasesD3C = []OutgoingTestCase{ Body: `{"messaging_product":"whatsapp","recipient_type":"individual","to":"250788123123","type":"text","text":{"body":"Error","preview_url":false}}`, }, }, - ExpectedLogErrors: []*courier.ChannelError{courier.ErrorResponseUnparseable("JSON")}, - ExpectedMsgStatus: "E", + ExpectedError: courier.ErrResponseUnparseable, }, { Label: "Error", @@ -641,8 +627,7 @@ var SendTestCasesD3C = []OutgoingTestCase{ Body: `{"messaging_product":"whatsapp","recipient_type":"individual","to":"250788123123","type":"text","text":{"body":"Error","preview_url":false}}`, }, }, - ExpectedLogErrors: []*courier.ChannelError{courier.ErrorExternal("130429", "(#130429) Rate limit hit")}, - ExpectedMsgStatus: "E", + ExpectedError: courier.ErrFailedWithReason("130429", "(#130429) Rate limit hit"), }, } From 49435607c6afcd014ee8308f2e327e881f2c60e9 Mon Sep 17 00:00:00 2001 From: Norbert Kwizera Date: Thu, 22 Feb 2024 16:28:44 +0200 Subject: [PATCH 09/11] Rework DA handler to new send --- handlers/dart/handler.go | 30 ++++++++++-------------------- handlers/dart/handler_test.go | 11 +++-------- 2 files changed, 13 insertions(+), 28 deletions(-) diff --git a/handlers/dart/handler.go b/handlers/dart/handler.go index a5cf0fd42..a46b84d2c 100644 --- a/handlers/dart/handler.go +++ b/handlers/dart/handler.go @@ -146,22 +146,12 @@ func (h *handler) WriteMsgSuccessResponse(ctx context.Context, w http.ResponseWr } func (h *handler) Send(ctx context.Context, msg courier.MsgOut, res *courier.SendResult, clog *courier.ChannelLog) error { - // TODO convert functionality from legacy method below - return nil -} - -func (h *handler) SendLegacy(ctx context.Context, msg courier.MsgOut, clog *courier.ChannelLog) (courier.StatusUpdate, error) { username := msg.Channel().StringConfigForKey(courier.ConfigUsername, "") - if username == "" { - return nil, fmt.Errorf("no username set for %s channel", msg.Channel().ChannelType()) - } - password := msg.Channel().StringConfigForKey(courier.ConfigPassword, "") - if password == "" { - return nil, fmt.Errorf("no password set for %s channel", msg.Channel().ChannelType()) + if username == "" || password == "" { + return courier.ErrChannelConfig } - status := h.Backend().NewStatusUpdate(msg.Channel(), msg.ID(), courier.MsgStatusErrored, clog) parts := handlers.SplitMsgByChannel(msg.Channel(), handlers.GetTextAndAttachments(msg), h.maxLength) for i, part := range parts { form := url.Values{ @@ -185,24 +175,24 @@ func (h *handler) SendLegacy(ctx context.Context, msg courier.MsgOut, clog *cour req, err := http.NewRequest(http.MethodGet, partSendURL.String(), nil) if err != nil { - return nil, err + return err } req.Header.Set("Content-Type", "application/x-www-form-urlencoded") resp, respBody, err := h.RequestHTTP(req, clog) - if err != nil || resp.StatusCode/100 != 2 { - return status, nil + if err != nil || resp.StatusCode/100 == 5 { + return courier.ErrConnectionFailed + } else if resp.StatusCode/100 != 2 { + return courier.ErrResponseStatus } responseCode := stringsx.Truncate(string(respBody), 3) if responseCode != "000" { - clog.Error(courier.ErrorExternal(responseCode, errorCodes[responseCode])) - return status, nil + return courier.ErrFailedWithReason(responseCode, errorCodes[responseCode]) } - status.SetStatus(courier.MsgStatusWired) - } - return status, nil + + return nil } diff --git a/handlers/dart/handler_test.go b/handlers/dart/handler_test.go index b4fd35bdc..e593dac99 100644 --- a/handlers/dart/handler_test.go +++ b/handlers/dart/handler_test.go @@ -107,7 +107,6 @@ var defaultSendTestCases = []OutgoingTestCase{ ExpectedRequests: []ExpectedRequest{ {Params: url.Values{"message": {"Simple Message"}, "sendto": {"250788383383"}, "original": {"2020"}, "userid": {"Username"}, "password": {"Password"}, "dcs": {"0"}, "udhl": {"0"}, "messageid": {"10"}}}, }, - ExpectedMsgStatus: "W", }, { Label: "Long Send", @@ -123,7 +122,6 @@ var defaultSendTestCases = []OutgoingTestCase{ {Params: url.Values{"message": {"This is a longer message than 160 characters and will cause us to split it into two separate parts, isn't that right but it is even longer than before I say,"}, "sendto": {"250788383383"}, "original": {"2020"}, "userid": {"Username"}, "password": {"Password"}, "dcs": {"0"}, "udhl": {"0"}, "messageid": {"10"}}}, {Params: url.Values{"message": {"I need to keep adding more things to make it work"}, "sendto": {"250788383383"}, "original": {"2020"}, "userid": {"Username"}, "password": {"Password"}, "dcs": {"0"}, "udhl": {"0"}, "messageid": {"10.2"}}}, }, - ExpectedMsgStatus: "W", }, { Label: "Send Attachment", @@ -138,7 +136,6 @@ var defaultSendTestCases = []OutgoingTestCase{ ExpectedRequests: []ExpectedRequest{ {Params: url.Values{"message": {"My pic!\nhttps://foo.bar/image.jpg"}, "sendto": {"250788383383"}, "original": {"2020"}, "userid": {"Username"}, "password": {"Password"}, "dcs": {"0"}, "udhl": {"0"}, "messageid": {"10"}}}, }, - ExpectedMsgStatus: "W", }, { Label: "Error Sending", @@ -152,7 +149,7 @@ var defaultSendTestCases = []OutgoingTestCase{ ExpectedRequests: []ExpectedRequest{ {Params: url.Values{"message": {"Error Message"}, "sendto": {"250788383383"}, "original": {"2020"}, "userid": {"Username"}, "password": {"Password"}, "dcs": {"0"}, "udhl": {"0"}, "messageid": {"10"}}}, }, - ExpectedMsgStatus: "E", + ExpectedError: courier.ErrResponseStatus, }, { Label: "Authentication Error", @@ -166,8 +163,7 @@ var defaultSendTestCases = []OutgoingTestCase{ ExpectedRequests: []ExpectedRequest{ {Params: url.Values{"message": {"Simple Message"}, "sendto": {"250788383383"}, "original": {"2020"}, "userid": {"Username"}, "password": {"Password"}, "dcs": {"0"}, "udhl": {"0"}, "messageid": {"10"}}}, }, - ExpectedMsgStatus: "E", - ExpectedLogErrors: []*courier.ChannelError{courier.ErrorExternal("001", "Authentication error.")}, + ExpectedError: courier.ErrFailedWithReason("001", "Authentication error."), }, { Label: "Account Expired", @@ -181,8 +177,7 @@ var defaultSendTestCases = []OutgoingTestCase{ ExpectedRequests: []ExpectedRequest{ {Params: url.Values{"message": {"Simple Message"}, "sendto": {"250788383383"}, "original": {"2020"}, "userid": {"Username"}, "password": {"Password"}, "dcs": {"0"}, "udhl": {"0"}, "messageid": {"10"}}}, }, - ExpectedMsgStatus: "E", - ExpectedLogErrors: []*courier.ChannelError{courier.ErrorExternal("101", "Account expired or invalid parameters.")}, + ExpectedError: courier.ErrFailedWithReason("101", "Account expired or invalid parameters."), }, } From 4fd0863661fa264d445d12ef6af1e554daa7e52c Mon Sep 17 00:00:00 2001 From: Norbert Kwizera Date: Mon, 26 Feb 2024 21:52:56 +0200 Subject: [PATCH 10/11] Make sure we handle connection error properly for D3C --- handlers/dialog360/handler.go | 5 ++++- handlers/dialog360/handler_test.go | 17 +++++++++++++++++ 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/handlers/dialog360/handler.go b/handlers/dialog360/handler.go index 3b15e8142..e2b14d522 100644 --- a/handlers/dialog360/handler.go +++ b/handlers/dialog360/handler.go @@ -572,7 +572,10 @@ func (h *handler) requestD3C(payload whatsapp.SendRequest, accessToken string, r req.Header.Set("Content-Type", "application/json") req.Header.Set("Accept", "application/json") - _, respBody, _ := h.RequestHTTP(req, clog) + resp, respBody, err := h.RequestHTTP(req, clog) + if err != nil || resp.StatusCode/100 == 5 { + return courier.ErrConnectionFailed + } respPayload := &whatsapp.SendResponse{} err = json.Unmarshal(respBody, respPayload) if err != nil { diff --git a/handlers/dialog360/handler_test.go b/handlers/dialog360/handler_test.go index 983bda5c4..09b6af155 100644 --- a/handlers/dialog360/handler_test.go +++ b/handlers/dialog360/handler_test.go @@ -629,6 +629,23 @@ var SendTestCasesD3C = []OutgoingTestCase{ }, ExpectedError: courier.ErrFailedWithReason("130429", "(#130429) Rate limit hit"), }, + { + Label: "Error Connection", + MsgText: "Error", + MsgURN: "whatsapp:250788123123", + MockResponses: map[string][]*httpx.MockResponse{ + "https://waba-v2.360dialog.io/messages": { + httpx.NewMockResponse(500, nil, []byte(`Bad gateway`)), + }, + }, + ExpectedRequests: []ExpectedRequest{ + { + Path: "/messages", + Body: `{"messaging_product":"whatsapp","recipient_type":"individual","to":"250788123123","type":"text","text":{"body":"Error","preview_url":false}}`, + }, + }, + ExpectedError: courier.ErrConnectionFailed, + }, } func TestOutgoing(t *testing.T) { From f3401ba4fe92085fce8fba074f39671f957d15c3 Mon Sep 17 00:00:00 2001 From: Norbert Kwizera Date: Tue, 27 Feb 2024 16:43:39 +0200 Subject: [PATCH 11/11] Make sure we truncate up to 10 QRs on D3C --- handlers/dialog360/handler.go | 21 +++++++++++++++------ handlers/dialog360/handler_test.go | 16 ++++++++++++++++ 2 files changed, 31 insertions(+), 6 deletions(-) diff --git a/handlers/dialog360/handler.go b/handlers/dialog360/handler.go index e2b14d522..29fc0b07f 100644 --- a/handlers/dialog360/handler.go +++ b/handlers/dialog360/handler.go @@ -337,6 +337,13 @@ func (h *handler) Send(ctx context.Context, msg courier.MsgOut, res *courier.Sen } else { if len(qrs) > 0 { payload.Type = "interactive" + + // if we have more than 10 quick replies, truncate and add channel error + if len(qrs) > 10 { + clog.Error(courier.NewChannelError("", "", "too many quick replies D3C supports only up to 10 quick replies")) + qrs = qrs[:10] + } + // We can use buttons if len(qrs) <= 3 { interactive := whatsapp.Interactive{Type: "button", Body: struct { @@ -357,7 +364,7 @@ func (h *handler) Send(ctx context.Context, msg courier.MsgOut, res *courier.Sen Buttons []whatsapp.Button "json:\"buttons,omitempty\"" }{Buttons: btns} payload.Interactive = &interactive - } else if len(qrs) <= 10 { + } else { interactive := whatsapp.Interactive{Type: "list", Body: struct { Text string "json:\"text\"" }{Text: msgParts[i-len(msg.Attachments())]}} @@ -381,8 +388,6 @@ func (h *handler) Send(ctx context.Context, msg courier.MsgOut, res *courier.Sen }} payload.Interactive = &interactive - } else { - return fmt.Errorf("too many quick replies WAC supports only up to 10 quick replies") } } else { // this is still a msg part @@ -430,6 +435,12 @@ func (h *handler) Send(ctx context.Context, msg courier.MsgOut, res *courier.Sen } else { if len(qrs) > 0 { payload.Type = "interactive" + // if we have more than 10 quick replies, truncate and add channel error + if len(qrs) > 10 { + clog.Error(courier.NewChannelError("", "", "too many quick replies D3C supports only up to 10 quick replies")) + qrs = qrs[:10] + } + // We can use buttons if len(qrs) <= 3 { interactive := whatsapp.Interactive{Type: "button", Body: struct { @@ -508,7 +519,7 @@ func (h *handler) Send(ctx context.Context, msg courier.MsgOut, res *courier.Sen }{Buttons: btns} payload.Interactive = &interactive - } else if len(qrs) <= 10 { + } else { interactive := whatsapp.Interactive{Type: "list", Body: struct { Text string "json:\"text\"" }{Text: msgParts[i-len(msg.Attachments())]}} @@ -532,8 +543,6 @@ func (h *handler) Send(ctx context.Context, msg courier.MsgOut, res *courier.Sen }} payload.Interactive = &interactive - } else { - return fmt.Errorf("too many quick replies WAC supports only up to 10 quick replies") } } else { // this is still a msg part diff --git a/handlers/dialog360/handler_test.go b/handlers/dialog360/handler_test.go index 09b6af155..8d2d3af06 100644 --- a/handlers/dialog360/handler_test.go +++ b/handlers/dialog360/handler_test.go @@ -465,6 +465,22 @@ var SendTestCasesD3C = []OutgoingTestCase{ }}, ExpectedExtIDs: []string{"157b5e14568e8"}, }, + { + Label: "Interactive List Message Send more than 10 QRs", + MsgText: "Interactive List Msg", + MsgURN: "whatsapp:250788123123", + MsgQuickReplies: []string{"ROW1", "ROW2", "ROW3", "ROW4", "ROW5", "ROW6", "ROW7", "ROW8", "ROW9", "ROW10", "ROW11", "ROW12"}, + MockResponses: map[string][]*httpx.MockResponse{ + "https://waba-v2.360dialog.io/messages": { + httpx.NewMockResponse(201, nil, []byte(`{ "messages": [{"id": "157b5e14568e8"}] }`)), + }, + }, + ExpectedRequests: []ExpectedRequest{{ + Body: `{"messaging_product":"whatsapp","recipient_type":"individual","to":"250788123123","type":"interactive","interactive":{"type":"list","body":{"text":"Interactive List Msg"},"action":{"button":"Menu","sections":[{"rows":[{"id":"0","title":"ROW1"},{"id":"1","title":"ROW2"},{"id":"2","title":"ROW3"},{"id":"3","title":"ROW4"},{"id":"4","title":"ROW5"},{"id":"5","title":"ROW6"},{"id":"6","title":"ROW7"},{"id":"7","title":"ROW8"},{"id":"8","title":"ROW9"},{"id":"9","title":"ROW10"}]}]}}}`, + }}, + ExpectedExtIDs: []string{"157b5e14568e8"}, + ExpectedLogErrors: []*courier.ChannelError{courier.NewChannelError("", "", "too many quick replies D3C supports only up to 10 quick replies")}, + }, { Label: "Interactive List Message Send In Spanish", MsgText: "Hola",