Skip to content

Commit 10e12a3

Browse files
AchoArnoldCopilot
andcommitted
test: add bulk SMS integration tests for CSV and Excel upload
- TestBulkSMS_CSV: uploads CSV with 1 message, fires SENT event, verifies history - TestBulkSMS_Excel: uploads Excel with 2 messages, fires DELIVERED on one, verifies mixed status counts - Add shared helpers: uploadBulkFile, fetchBulkMessages, searchMessages, findBulkEntry - Add excelize/v2 dependency for Excel file creation in tests Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent 493bef7 commit 10e12a3

4 files changed

Lines changed: 288 additions & 7 deletions

File tree

tests/go.mod

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,26 @@
11
module github.com/NdoleStudio/httpsms/tests
22

3-
go 1.23
3+
go 1.24.0
44

55
require (
66
github.com/NdoleStudio/httpsms-go v0.0.8
77
github.com/golang-jwt/jwt/v5 v5.2.2
88
github.com/google/uuid v1.6.0
99
github.com/stretchr/testify v1.11.1
1010
github.com/wiremock/go-wiremock v1.14.0
11+
github.com/xuri/excelize/v2 v2.10.1
1112
)
1213

1314
require (
1415
github.com/davecgh/go-spew v1.1.1 // indirect
1516
github.com/pmezard/go-difflib v1.0.0 // indirect
17+
github.com/richardlehane/mscfb v1.0.6 // indirect
18+
github.com/richardlehane/msoleps v1.0.6 // indirect
19+
github.com/tiendc/go-deepcopy v1.7.2 // indirect
20+
github.com/xuri/efp v0.0.1 // indirect
21+
github.com/xuri/nfp v0.0.2-0.20250530014748-2ddeb826f9a9 // indirect
22+
golang.org/x/crypto v0.48.0 // indirect
23+
golang.org/x/net v0.50.0 // indirect
24+
golang.org/x/text v0.34.0 // indirect
1625
gopkg.in/yaml.v3 v3.0.1 // indirect
1726
)

tests/go.sum

Lines changed: 28 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,10 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb
6868
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
6969
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c h1:ncq/mPwQF4JjgDlrVEn3C11VoGHZN7m8qihwgMEtzYw=
7070
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE=
71+
github.com/richardlehane/mscfb v1.0.6 h1:eN3bvvZCp00bs7Zf52bxNwAx5lJDBK1tCuH19qq5aC8=
72+
github.com/richardlehane/mscfb v1.0.6/go.mod h1:pe0+IUIc0AHh0+teNzBlJCtSyZdFOGgV4ZK9bsoV+Jo=
73+
github.com/richardlehane/msoleps v1.0.6 h1:9BvkpjvD+iUBalUY4esMwv6uBkfOip/Lzvd93jvR9gg=
74+
github.com/richardlehane/msoleps v1.0.6/go.mod h1:BWev5JBpU9Ko2WAgmZEuiz4/u3ZYTKbjLycmwiWUfWg=
7175
github.com/shirou/gopsutil/v3 v3.23.12 h1:z90NtUkp3bMtmICZKpC4+WaknU1eXtp5vtbQ11DgpE4=
7276
github.com/shirou/gopsutil/v3 v3.23.12/go.mod h1:1FrWgea594Jp7qmjHUUPlJDTPgcsb9mGnXDxavtikzM=
7377
github.com/shoenig/go-m1cpu v0.1.6 h1:nxdKQNcEB6vzgA2E2bvzKIYRuNj7XNJ4S/aRSwKzFtM=
@@ -78,12 +82,20 @@ github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu
7882
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
7983
github.com/testcontainers/testcontainers-go v0.28.0 h1:1HLm9qm+J5VikzFDYhOd+Zw12NtOl+8drH2E8nTY1r8=
8084
github.com/testcontainers/testcontainers-go v0.28.0/go.mod h1:COlDpUXbwW3owtpMkEB1zo9gwb1CoKVKlyrVPejF4AU=
85+
github.com/tiendc/go-deepcopy v1.7.2 h1:Ut2yYR7W9tWjTQitganoIue4UGxZwCcJy3orjrrIj44=
86+
github.com/tiendc/go-deepcopy v1.7.2/go.mod h1:4bKjNC2r7boYOkD2IOuZpYjmlDdzjbpTRyCx+goBCJQ=
8187
github.com/tklauser/go-sysconf v0.3.12 h1:0QaGUFOdQaIVdPgfITYzaTegZvdCjmYO52cSFAEVmqU=
8288
github.com/tklauser/go-sysconf v0.3.12/go.mod h1:Ho14jnntGE1fpdOqQEEaiKRpvIavV0hSfmBq8nJbHYI=
8389
github.com/tklauser/numcpus v0.6.1 h1:ng9scYS7az0Bk4OZLvrNXNSAO2Pxr1XXRAPyjhIx+Fk=
8490
github.com/tklauser/numcpus v0.6.1/go.mod h1:1XfjsgE2zo8GVw7POkMbHENHzVg3GzmoZ9fESEdAacY=
8591
github.com/wiremock/go-wiremock v1.14.0 h1:cVAV98Odg+hySEYKDRUasVo30q7JE/ysrdx5qOmF4f4=
8692
github.com/wiremock/go-wiremock v1.14.0/go.mod h1:T5XkKnsKS2asycbUrk2cpxXTEXwa6klHfCWVN8BkhkU=
93+
github.com/xuri/efp v0.0.1 h1:fws5Rv3myXyYni8uwj2qKjVaRP30PdjeYe2Y6FDsCL8=
94+
github.com/xuri/efp v0.0.1/go.mod h1:ybY/Jr0T0GTCnYjKqmdwxyxn2BQf2RcQIIvex5QldPI=
95+
github.com/xuri/excelize/v2 v2.10.1 h1:V62UlqopMqha3kOpnlHy2CcRVw1V8E63jFoWUmMzxN0=
96+
github.com/xuri/excelize/v2 v2.10.1/go.mod h1:iG5tARpgaEeIhTqt3/fgXCGoBRt4hNXgCp3tfXKoOIc=
97+
github.com/xuri/nfp v0.0.2-0.20250530014748-2ddeb826f9a9 h1:+C0TIdyyYmzadGaL/HBLbf3WdLgC29pgyhTjAT/0nuE=
98+
github.com/xuri/nfp v0.0.2-0.20250530014748-2ddeb826f9a9/go.mod h1:WwHg+CVyzlv/TX9xqBFXEZAuxOPxn2k1GNHwG41IIUQ=
8799
github.com/yusufpapurcu/wmi v1.2.3 h1:E1ctvB7uKFMOJw3fdOW32DwGE9I7t++CRUEMKvFoFiw=
88100
github.com/yusufpapurcu/wmi v1.2.3/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0=
89101
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.45.0 h1:x8Z78aZx8cOF0+Kkazoc7lwUNMGy0LrzEMxTm4BbTxg=
@@ -94,14 +106,24 @@ go.opentelemetry.io/otel/metric v1.19.0 h1:aTzpGtV0ar9wlV4Sna9sdJyII5jTVJEvKETPi
94106
go.opentelemetry.io/otel/metric v1.19.0/go.mod h1:L5rUsV9kM1IxCj1MmSdS+JQAcVm319EUrDVLrt7jqt8=
95107
go.opentelemetry.io/otel/trace v1.19.0 h1:DFVQmlVbfVeOuBRrwdtaehRrWiL1JoVs9CPIQ1Dzxpg=
96108
go.opentelemetry.io/otel/trace v1.19.0/go.mod h1:mfaSyvGyEJEI0nyV2I4qhNQnbBOUUmYZpYojqMnX2vo=
109+
golang.org/x/crypto v0.48.0 h1:/VRzVqiRSggnhY7gNRxPauEQ5Drw9haKdM0jqfcCFts=
110+
golang.org/x/crypto v0.48.0/go.mod h1:r0kV5h3qnFPlQnBSrULhlsRfryS2pmewsg+XfMgkVos=
97111
golang.org/x/exp v0.0.0-20230510235704-dd950f8aeaea h1:vLCWI/yYrdEHyN2JzIzPO3aaQJHQdp89IZBA/+azVC4=
98112
golang.org/x/exp v0.0.0-20230510235704-dd950f8aeaea/go.mod h1:V1LtkGg67GoY2N1AnLN78QLrzxkLyJw7RJb1gzOOz9w=
99-
golang.org/x/mod v0.11.0 h1:bUO06HqtnRcc/7l71XBe4WcqTZ+3AH1J59zWDDwLKgU=
100-
golang.org/x/mod v0.11.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
101-
golang.org/x/sys v0.16.0 h1:xWw16ngr6ZMtmxDyKyIgsE93KNKz5HKmMa3b8ALHidU=
102-
golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
103-
golang.org/x/tools v0.10.0 h1:tvDr/iQoUqNdohiYm0LmmKcBk+q86lb9EprIUFhHHGg=
104-
golang.org/x/tools v0.10.0/go.mod h1:UJwyiVBsOA2uwvK/e5OY3GTpDUJriEd+/YlqAwLPmyM=
113+
golang.org/x/image v0.25.0 h1:Y6uW6rH1y5y/LK1J8BPWZtr6yZ7hrsy6hFrXjgsc2fQ=
114+
golang.org/x/image v0.25.0/go.mod h1:tCAmOEGthTtkalusGp1g3xa2gke8J6c2N565dTyl9Rs=
115+
golang.org/x/mod v0.32.0 h1:9F4d3PHLljb6x//jOyokMv3eX+YDeepZSEo3mFJy93c=
116+
golang.org/x/mod v0.32.0/go.mod h1:SgipZ/3h2Ci89DlEtEXWUk/HteuRin+HHhN+WbNhguU=
117+
golang.org/x/net v0.50.0 h1:ucWh9eiCGyDR3vtzso0WMQinm2Dnt8cFMuQa9K33J60=
118+
golang.org/x/net v0.50.0/go.mod h1:UgoSli3F/pBgdJBHCTc+tp3gmrU4XswgGRgtnwWTfyM=
119+
golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4=
120+
golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
121+
golang.org/x/sys v0.41.0 h1:Ivj+2Cp/ylzLiEU89QhWblYnOE9zerudt9Ftecq2C6k=
122+
golang.org/x/sys v0.41.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
123+
golang.org/x/text v0.34.0 h1:oL/Qq0Kdaqxa1KbNeMKwQq0reLCCaFtqu2eNuSeNHbk=
124+
golang.org/x/text v0.34.0/go.mod h1:homfLqTYRFyVYemLBFl5GgL/DWEiH5wcsQ5gSh1yziA=
125+
golang.org/x/tools v0.41.0 h1:a9b8iMweWG+S0OBnlU36rzLp20z1Rp10w+IY2czHTQc=
126+
golang.org/x/tools v0.41.0/go.mod h1:XSY6eDqxVNiYgezAVqqCeihT4j1U2CCsqvH3WhQpnlg=
105127
google.golang.org/genproto/googleapis/rpc v0.0.0-20230711160842-782d3b101e98 h1:bVf09lpb+OJbByTj913DRJioFFAjf/ZGxEz7MajTp2U=
106128
google.golang.org/genproto/googleapis/rpc v0.0.0-20230711160842-782d3b101e98/go.mod h1:TUfxEVdsvPg18p6AslUXFoLdpED4oBnGwyqk3dV1XzM=
107129
google.golang.org/grpc v1.58.3 h1:BjnpXut1btbtgN/6sp+brB2Kbm2LjNXnidYujAVbSoQ=

tests/helpers_test.go

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import (
88
"fmt"
99
"io"
1010
"math/big"
11+
"mime/multipart"
1112
"net/http"
1213
"strings"
1314
"testing"
@@ -315,3 +316,99 @@ func waitForFCMPush(t *testing.T, messageID string, timeout time.Duration) []wmJ
315316
t.Fatalf("FCM push for message %s not found within %v", messageID, timeout)
316317
return nil
317318
}
319+
320+
type BulkMessageEntry struct {
321+
RequestID string `json:"request_id"`
322+
Total int `json:"total"`
323+
ScheduledCount int `json:"scheduled_count"`
324+
PendingCount int `json:"pending_count"`
325+
FailedCount int `json:"failed_count"`
326+
ExpiredCount int `json:"expired_count"`
327+
SentCount int `json:"sent_count"`
328+
DeliveredCount int `json:"delivered_count"`
329+
CreatedAt string `json:"created_at"`
330+
}
331+
332+
func uploadBulkFile(ctx context.Context, t *testing.T, filename string, fileBytes []byte) (int, []byte) {
333+
t.Helper()
334+
335+
var buf bytes.Buffer
336+
writer := multipart.NewWriter(&buf)
337+
338+
part, err := writer.CreateFormFile("document", filename)
339+
require.NoError(t, err)
340+
341+
_, err = part.Write(fileBytes)
342+
require.NoError(t, err)
343+
require.NoError(t, writer.Close())
344+
345+
url := apiBaseURL + "/v1/bulk-messages"
346+
req, err := http.NewRequestWithContext(ctx, http.MethodPost, url, &buf)
347+
require.NoError(t, err)
348+
req.Header.Set("Content-Type", writer.FormDataContentType())
349+
req.Header.Set("x-api-key", userAPIKey)
350+
351+
resp, err := http.DefaultClient.Do(req)
352+
require.NoError(t, err)
353+
defer resp.Body.Close()
354+
355+
body, err := io.ReadAll(resp.Body)
356+
require.NoError(t, err)
357+
358+
return resp.StatusCode, body
359+
}
360+
361+
func fetchBulkMessages(ctx context.Context, t *testing.T) []BulkMessageEntry {
362+
t.Helper()
363+
364+
url := apiBaseURL + "/v1/bulk-messages"
365+
req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
366+
require.NoError(t, err)
367+
req.Header.Set("x-api-key", userAPIKey)
368+
369+
resp, err := http.DefaultClient.Do(req)
370+
require.NoError(t, err)
371+
defer resp.Body.Close()
372+
373+
body, err := io.ReadAll(resp.Body)
374+
require.NoError(t, err)
375+
require.Equal(t, http.StatusOK, resp.StatusCode, "fetch bulk messages failed: %s", string(body))
376+
377+
var result struct {
378+
Data []BulkMessageEntry `json:"data"`
379+
}
380+
require.NoError(t, json.Unmarshal(body, &result))
381+
return result.Data
382+
}
383+
384+
func searchMessages(ctx context.Context, t *testing.T, query string, owner string) []httpsms.Message {
385+
t.Helper()
386+
387+
url := fmt.Sprintf("%s/v1/messages?query=%s&owners=%s&limit=10&skip=0", apiBaseURL, query, owner)
388+
req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
389+
require.NoError(t, err)
390+
req.Header.Set("x-api-key", userAPIKey)
391+
392+
resp, err := http.DefaultClient.Do(req)
393+
require.NoError(t, err)
394+
defer resp.Body.Close()
395+
396+
body, err := io.ReadAll(resp.Body)
397+
require.NoError(t, err)
398+
require.Equal(t, http.StatusOK, resp.StatusCode, "search messages failed: %s", string(body))
399+
400+
var result struct {
401+
Data []httpsms.Message `json:"data"`
402+
}
403+
require.NoError(t, json.Unmarshal(body, &result))
404+
return result.Data
405+
}
406+
407+
func findBulkEntry(entries []BulkMessageEntry, requestID string) *BulkMessageEntry {
408+
for i := range entries {
409+
if entries[i].RequestID == requestID {
410+
return &entries[i]
411+
}
412+
}
413+
return nil
414+
}

tests/integration_test.go

Lines changed: 153 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import (
1414
httpsms "github.com/NdoleStudio/httpsms-go"
1515
"github.com/stretchr/testify/assert"
1616
"github.com/stretchr/testify/require"
17+
"github.com/xuri/excelize/v2"
1718
)
1819

1920
func TestSendSMS_Encrypted(t *testing.T) {
@@ -402,3 +403,155 @@ func TestHeartbeat_StoreAndIndex(t *testing.T) {
402403
assert.True(t, hb.Charging)
403404
assert.False(t, hb.Timestamp.IsZero(), "timestamp should not be zero")
404405
}
406+
407+
func TestBulkSMS_CSV(t *testing.T) {
408+
ctx := context.Background()
409+
phone := setupPhone(ctx, t, 60)
410+
411+
// Build CSV content with 1 message
412+
csvContent := fmt.Sprintf("FromPhoneNumber,ToPhoneNumber,Content,SendTime(optional)\n%s,%s,CSV bulk test message,\n",
413+
phone.PhoneNumber, randomPhoneNumber())
414+
415+
// Upload CSV
416+
statusCode, respBody := uploadBulkFile(ctx, t, "test.csv", []byte(csvContent))
417+
require.Equal(t, http.StatusAccepted, statusCode, "upload failed: %s", string(respBody))
418+
t.Logf("upload response: %s", string(respBody))
419+
420+
// Parse the response to verify message count
421+
var uploadResp struct {
422+
Message string `json:"message"`
423+
}
424+
require.NoError(t, json.Unmarshal(respBody, &uploadResp))
425+
assert.Contains(t, uploadResp.Message, "1 out of 1")
426+
427+
// Wait a moment for messages to be persisted
428+
time.Sleep(2 * time.Second)
429+
430+
// Search for the bulk message by owner to get message IDs
431+
messages := searchMessages(ctx, t, "", phone.PhoneNumber)
432+
require.GreaterOrEqual(t, len(messages), 1, "expected at least 1 message for phone %s", phone.PhoneNumber)
433+
434+
// Find the message with bulk- request_id prefix
435+
var bulkMsg *httpsms.Message
436+
for i := range messages {
437+
if messages[i].RequestID != nil && strings.HasPrefix(*messages[i].RequestID, "bulk-") {
438+
bulkMsg = &messages[i]
439+
break
440+
}
441+
}
442+
require.NotNil(t, bulkMsg, "no message with bulk- request_id found")
443+
messageID := bulkMsg.ID.String()
444+
requestID := *bulkMsg.RequestID
445+
t.Logf("found bulk message: id=%s, request_id=%s", messageID, requestID)
446+
447+
// Wait for FCM push
448+
waitForFCMPush(t, messageID, 30*time.Second)
449+
450+
// Fire SENT event
451+
fireEvent(ctx, t, phone.PhoneAPIKey, messageID, "SENT")
452+
453+
// Poll until message reaches "sent" status
454+
msg := pollMessageStatus(ctx, t, messageID, "sent", 15*time.Second)
455+
assert.Equal(t, "sent", msg.Status)
456+
457+
// Verify bulk-messages history endpoint
458+
entries := fetchBulkMessages(ctx, t)
459+
entry := findBulkEntry(entries, requestID)
460+
require.NotNil(t, entry, "bulk entry with request_id %s not found in history", requestID)
461+
462+
assert.Equal(t, 1, entry.Total)
463+
assert.Equal(t, 1, entry.SentCount)
464+
assert.Equal(t, 0, entry.PendingCount)
465+
assert.Equal(t, 0, entry.FailedCount)
466+
assert.Equal(t, 0, entry.ExpiredCount)
467+
assert.Equal(t, 0, entry.DeliveredCount)
468+
assert.Equal(t, 0, entry.ScheduledCount)
469+
}
470+
471+
func TestBulkSMS_Excel(t *testing.T) {
472+
ctx := context.Background()
473+
phone := setupPhone(ctx, t, 60)
474+
475+
contact1 := randomPhoneNumber()
476+
contact2 := randomPhoneNumber()
477+
478+
// Build Excel file with 2 messages
479+
f := excelize.NewFile()
480+
sheet := f.GetSheetName(0)
481+
f.SetCellValue(sheet, "A1", "FromPhoneNumber")
482+
f.SetCellValue(sheet, "B1", "ToPhoneNumber")
483+
f.SetCellValue(sheet, "C1", "Content")
484+
f.SetCellValue(sheet, "D1", "SendTime(optional)")
485+
486+
f.SetCellValue(sheet, "A2", phone.PhoneNumber)
487+
f.SetCellValue(sheet, "B2", contact1)
488+
f.SetCellValue(sheet, "C2", "Excel bulk test message 1")
489+
f.SetCellValue(sheet, "D2", "")
490+
491+
f.SetCellValue(sheet, "A3", phone.PhoneNumber)
492+
f.SetCellValue(sheet, "B3", contact2)
493+
f.SetCellValue(sheet, "C3", "Excel bulk test message 2")
494+
f.SetCellValue(sheet, "D3", "")
495+
496+
var excelBuf bytes.Buffer
497+
require.NoError(t, f.Write(&excelBuf))
498+
499+
// Upload Excel
500+
statusCode, respBody := uploadBulkFile(ctx, t, "test.xlsx", excelBuf.Bytes())
501+
require.Equal(t, http.StatusAccepted, statusCode, "upload failed: %s", string(respBody))
502+
t.Logf("upload response: %s", string(respBody))
503+
504+
var uploadResp struct {
505+
Message string `json:"message"`
506+
}
507+
require.NoError(t, json.Unmarshal(respBody, &uploadResp))
508+
assert.Contains(t, uploadResp.Message, "2 out of 2")
509+
510+
// Wait for messages to be persisted
511+
time.Sleep(2 * time.Second)
512+
513+
// Search for bulk messages by owner
514+
messages := searchMessages(ctx, t, "", phone.PhoneNumber)
515+
require.GreaterOrEqual(t, len(messages), 2, "expected at least 2 messages for phone %s", phone.PhoneNumber)
516+
517+
// Find messages with bulk- request_id prefix
518+
var bulkMessages []httpsms.Message
519+
var requestID string
520+
for i := range messages {
521+
if messages[i].RequestID != nil && strings.HasPrefix(*messages[i].RequestID, "bulk-") {
522+
bulkMessages = append(bulkMessages, messages[i])
523+
requestID = *messages[i].RequestID
524+
}
525+
}
526+
require.Len(t, bulkMessages, 2, "expected 2 messages with bulk- request_id")
527+
require.NotEmpty(t, requestID)
528+
t.Logf("found %d bulk messages with request_id=%s", len(bulkMessages), requestID)
529+
530+
// Wait for FCM pushes for both messages
531+
msgID1 := bulkMessages[0].ID.String()
532+
msgID2 := bulkMessages[1].ID.String()
533+
waitForFCMPush(t, msgID1, 30*time.Second)
534+
waitForFCMPush(t, msgID2, 30*time.Second)
535+
536+
// Fire SENT then DELIVERED on message 1, leave message 2 pending
537+
fireEvent(ctx, t, phone.PhoneAPIKey, msgID1, "SENT")
538+
time.Sleep(200 * time.Millisecond)
539+
fireEvent(ctx, t, phone.PhoneAPIKey, msgID1, "DELIVERED")
540+
541+
// Poll until message 1 reaches "delivered"
542+
msg1 := pollMessageStatus(ctx, t, msgID1, "delivered", 15*time.Second)
543+
assert.Equal(t, "delivered", msg1.Status)
544+
545+
// Verify bulk-messages history endpoint
546+
entries := fetchBulkMessages(ctx, t)
547+
entry := findBulkEntry(entries, requestID)
548+
require.NotNil(t, entry, "bulk entry with request_id %s not found in history", requestID)
549+
550+
assert.Equal(t, 2, entry.Total)
551+
assert.Equal(t, 1, entry.DeliveredCount)
552+
assert.Equal(t, 1, entry.PendingCount)
553+
assert.Equal(t, 0, entry.SentCount)
554+
assert.Equal(t, 0, entry.FailedCount)
555+
assert.Equal(t, 0, entry.ExpiredCount)
556+
assert.Equal(t, 0, entry.ScheduledCount)
557+
}

0 commit comments

Comments
 (0)