package wkapi import ( "encoding/json" "net/http" "net/http/httptest" "sync/atomic" "testing" ) func TestSendMessageRetriesRetryRequiredWithSameClientMessageNumber(t *testing.T) { var attempts atomic.Int32 var clientMsgNo string server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { if r.URL.Path != "/message/send" { t.Fatalf("path = %s, want /message/send", r.URL.Path) } var got SendMessageRequest if err := json.NewDecoder(r.Body).Decode(&got); err != nil { t.Fatalf("decode request: %v", err) } if clientMsgNo == "" { clientMsgNo = got.ClientMsgNo } else if got.ClientMsgNo != clientMsgNo { t.Fatalf("client_msg_no = %q, want stable %q", got.ClientMsgNo, clientMsgNo) } if attempts.Add(1) <= 2 { w.WriteHeader(http.StatusServiceUnavailable) _, _ = w.Write([]byte(`{"error":"retry required"}`)) return } w.Header().Set("Content-Type", "application/json") _, _ = w.Write([]byte(`{"message_id":101,"message_seq":7,"reason":1}`)) })) defer server.Close() client := New(server.URL) response, err := client.SendMessage(SendMessageRequest{ FromUID: "user_1", ChannelID: "agent_channel", ChannelType: 2, ClientMsgNo: "lineup-idempotent-message", Payload: "aGVsbG8=", }) if err != nil { t.Fatalf("SendMessage() error = %v", err) } if response.MessageID != 101 || response.MessageSeq != 7 { t.Fatalf("SendMessage() response = %#v", response) } if got := attempts.Load(); got != 3 { t.Fatalf("attempts = %d, want 3", got) } } func TestSendMessageDoesNotRetryPermanentAPIError(t *testing.T) { var attempts atomic.Int32 server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { attempts.Add(1) w.WriteHeader(http.StatusBadRequest) _, _ = w.Write([]byte(`{"error":"invalid request"}`)) })) defer server.Close() _, err := New(server.URL).SendMessage(SendMessageRequest{ClientMsgNo: "invalid"}) if err == nil { t.Fatal("SendMessage() error = nil, want permanent error") } if got := attempts.Load(); got != 1 { t.Fatalf("attempts = %d, want 1", got) } }