diff --git a/internal/channels/zalo/zalo.go b/internal/channels/zalo/zalo.go index 7a4c84e5..5c9fd03d 100644 --- a/internal/channels/zalo/zalo.go +++ b/internal/channels/zalo/zalo.go @@ -24,11 +24,12 @@ import ( ) const ( - defaultPollTimeout = 30 - maxTextLength = 2000 - defaultMediaMaxMB = 5 - pollErrorBackoff = 5 * time.Second - pairingDebounce = 60 * time.Second + defaultPollTimeout = 30 + maxTextLength = 2000 + defaultMediaMaxMB = 5 + pollErrorBackoff = 5 * time.Second + pairingDebounce = 60 * time.Second + pollTimeoutHeadroom = 7 * time.Second ) // apiBase is the Zalo Bot API root. Declared as a variable so tests can @@ -494,7 +495,7 @@ func (c *Channel) getUpdates(timeout int) ([]zaloUpdate, error) { "timeout": timeout, } - ctx, cancel := context.WithTimeout(context.Background(), time.Duration(timeout+7)*time.Second) + ctx, cancel := context.WithTimeout(context.Background(), time.Duration(timeout)*time.Second+pollTimeoutHeadroom) defer cancel() result, err := c.callAPIWith(ctx, c.pollClient, "getUpdates", params) diff --git a/internal/channels/zalo/zalo_test.go b/internal/channels/zalo/zalo_test.go index 1a501104..0b0b8b49 100644 --- a/internal/channels/zalo/zalo_test.go +++ b/internal/channels/zalo/zalo_test.go @@ -165,6 +165,26 @@ func TestGetUpdates_ParsesSingleUpdate(t *testing.T) { } } +// TestGetUpdates_EmptyResultReturnsNilSlice verifies that when the Zalo API +// returns an empty update object (no pending events), getUpdates returns nil +// instead of a slice with a zero-valued element — the poll loop treats it as +// "nothing to dispatch" without invoking processUpdate. +func TestGetUpdates_EmptyResultReturnsNilSlice(t *testing.T) { + srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + _, _ = w.Write([]byte(`{"ok":true,"result":{}}`)) + })) + defer srv.Close() + + ch := newTestChannel(t, srv.URL) + updates, err := ch.getUpdates(1) + if err != nil { + t.Fatalf("getUpdates: %v", err) + } + if updates != nil { + t.Errorf("updates = %+v, want nil (empty result object)", updates) + } +} + // TestGetUpdates_UnmarshalError verifies a malformed result surfaces an error. func TestGetUpdates_UnmarshalError(t *testing.T) { srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {