10 "github.com/stretchr/testify/require"
13 func TestWhatsAppBatchesFollowTheTimesOfTheMessages(t *testing.T) {
14 testCases := []struct {
20 name: "messages close together form one batch",
21 times: []time.Duration{0, 10 * time.Second, 30 * time.Second},
25 name: "a quiet chat starts a new batch",
26 times: []time.Duration{0, 10 * time.Second, 31 * time.Second},
30 name: "a batch ends after two minutes",
31 times: []time.Duration{0, 20 * time.Second, 40 * time.Second, 60 * time.Second, 80 * time.Second, 100 * time.Second, 120 * time.Second, 140 * time.Second},
36 for _, tc := range testCases {
37 t.Run(tc.name, func(t *testing.T) {
38 batches := whatsappTestState(t, filepath.Join(t.TempDir(), whatsappStateFileName))
39 start := time.Date(2026, 1, 1, 12, 0, 0, 0, time.UTC)
40 for index, offset := range tc.times {
41 _, err := batches.Add(whatsappTestChat, WhatsAppMessage{
42 ID: string(rune('a' + index)),
43 Time: start.Add(offset),
46 require.NoError(t, err)
49 require.NoError(t, batches.CloseAll())
53 for _, batch := range batches.Closed() {
54 sizes = append(sizes, len(batch.Messages))
57 require.Equal(t, tc.sizes, sizes)
62 func TestWhatsAppBatchClosesAtFiftyMessages(t *testing.T) {
63 batches := whatsappTestState(t, filepath.Join(t.TempDir(), whatsappStateFileName))
64 start := time.Date(2026, 1, 1, 12, 0, 0, 0, time.UTC)
65 for index := range whatsappBatchMessages {
66 closed, err := batches.Add(whatsappTestChat, WhatsAppMessage{
67 ID: time.Duration(index).String(),
71 require.NoError(t, err)
72 require.Equal(t, index == whatsappBatchMessages-1, closed)
75 require.Len(t, batches.Closed(), 1)
78 func TestWhatsAppBatchClosesInRealTime(t *testing.T) {
79 batches := whatsappTestState(t, filepath.Join(t.TempDir(), whatsappStateFileName))
80 arrival := time.Date(2026, 1, 1, 12, 0, 0, 0, time.UTC)
81 for index, offset := range []time.Duration{0, 15 * time.Second, 30 * time.Second} {
82 _, err := batches.Add(whatsappTestChat, WhatsAppMessage{
83 ID: string(rune('a' + index)),
86 }, arrival.Add(offset))
87 require.NoError(t, err)
90 wait, open := batches.Wait(arrival.Add(40 * time.Second))
92 require.Equal(t, 10*time.Second, wait)
94 closed, err := batches.CloseDue(arrival.Add(49 * time.Second))
95 require.NoError(t, err)
96 require.False(t, closed)
98 closed, err = batches.CloseDue(arrival.Add(50 * time.Second))
99 require.NoError(t, err)
100 require.True(t, closed)
102 _, open = batches.Wait(arrival.Add(50 * time.Second))
103 require.False(t, open)
106 func TestWhatsAppBatchesSurviveARestart(t *testing.T) {
107 path := filepath.Join(t.TempDir(), whatsappDirName, whatsappStateFileName)
108 batches := whatsappTestState(t, path)
109 for _, id := range []string{"a", "a", "b"} {
110 _, err := batches.Add(whatsappTestChat, WhatsAppMessage{
112 Time: time.Date(2026, 1, 1, 12, 0, 0, 0, time.UTC),
115 require.NoError(t, err)
118 info, err := os.Stat(path)
119 require.NoError(t, err)
120 require.Equal(t, os.FileMode(0o600), info.Mode().Perm())
122 again := whatsappTestState(t, path)
123 require.NoError(t, again.CloseAll())
125 closed := again.Closed()
126 require.Len(t, closed, 1)
127 require.Equal(t, []string{"hi a", "hi b"}, []string{closed[0].Messages[0].Text, closed[0].Messages[1].Text})
129 require.NoError(t, again.Remove(closed[0].Number))
130 require.Empty(t, whatsappTestState(t, path).Closed())
133 func TestWhatsAppStateRefusesABrokenFile(t *testing.T) {
134 path := filepath.Join(t.TempDir(), whatsappStateFileName)
135 require.NoError(t, os.WriteFile(path, []byte("{"), 0o600))
137 _, err := openWhatsAppState(path)
138 require.ErrorContains(t, err, path)
141 func TestWhatsAppStateKeepsTheLastSendRecords(t *testing.T) {
142 state := whatsappTestState(t, filepath.Join(t.TempDir(), whatsappStateFileName))
143 for index := range whatsappSendRecords + 1 {
144 require.NoError(t, state.PutSend(WhatsAppSend{
145 ID: fmt.Sprint(index),
150 _, found := state.Send("0")
151 require.False(t, found)
153 _, found = state.Send(fmt.Sprint(whatsappSendRecords))
154 require.True(t, found)
157 // whatsappTestState opens the batches of the state file at path with the
158 // times of the plan.
159 func whatsappTestState(t *testing.T, path string) *WhatsAppState {
162 batches, err := openWhatsAppState(path)
163 require.NoError(t, err)