package cast import ( "context" "encoding/json" "net/http" "net/http/httptest" "sync/atomic" "testing" ) func TestSendSMS_Success(t *testing.T) { srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { if r.Method != http.MethodPost { t.Errorf("expected POST, got %s", r.Method) } if r.URL.Path != "/api/sms/send" { t.Errorf("expected /api/sms/send, got %s", r.URL.Path) } if r.Header.Get("X-API-Key") != "cast_testkey" { t.Errorf("expected X-API-Key cast_testkey, got %s", r.Header.Get("X-API-Key")) } var body SendRequest json.NewDecoder(r.Body).Decode(&body) if body.To != "09171234567" { t.Errorf("expected to=09171234567, got %s", body.To) } w.WriteHeader(http.StatusOK) json.NewEncoder(w).Encode(SendResponse{Success: true, MessageID: "abc123", Parts: 1}) })) defer srv.Close() client := NewClient(srv.URL, "cast_testkey", "") resp, err := client.SendSMS(context.Background(), "09171234567", "test message") if err != nil { t.Fatalf("unexpected error: %v", err) } if !resp.Success || resp.MessageID != "abc123" { t.Errorf("unexpected response: %+v", resp) } } func TestSendSMS_APIError(t *testing.T) { srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusPaymentRequired) json.NewEncoder(w).Encode(SendResponse{Success: false, Error: "insufficient credits"}) })) defer srv.Close() client := NewClient(srv.URL, "cast_testkey", "") _, err := client.SendSMS(context.Background(), "09171234567", "test") if err == nil { t.Fatal("expected error, got nil") } castErr, ok := err.(*CastAPIError) if !ok { t.Fatalf("expected CastAPIError, got %T", err) } if castErr.StatusCode != http.StatusPaymentRequired { t.Errorf("expected 402, got %d", castErr.StatusCode) } } func TestSendSMS_SuccessFalseInBody(t *testing.T) { srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusOK) json.NewEncoder(w).Encode(SendResponse{Success: false, Error: "invalid number"}) })) defer srv.Close() client := NewClient(srv.URL, "cast_testkey", "") _, err := client.SendSMS(context.Background(), "09171234567", "test") if err == nil { t.Fatal("expected error, got nil") } castErr, ok := err.(*CastAPIError) if !ok { t.Fatalf("expected CastAPIError, got %T", err) } if castErr.APIError != "invalid number" { t.Errorf("expected 'invalid number', got %s", castErr.APIError) } } func TestSendSMS_WithSenderID(t *testing.T) { srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { var body SendRequest json.NewDecoder(r.Body).Decode(&body) if body.SenderID != "CAST" { t.Errorf("expected sender_id=CAST, got %q", body.SenderID) } w.WriteHeader(http.StatusOK) json.NewEncoder(w).Encode(SendResponse{Success: true, MessageID: "x1", Parts: 1}) })) defer srv.Close() client := NewClient(srv.URL, "cast_testkey", "CAST") _, err := client.SendSMS(context.Background(), "09171234567", "test") if err != nil { t.Fatalf("unexpected error: %v", err) } } func TestSendSMS_WithoutSenderID(t *testing.T) { srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { var rawBody map[string]interface{} json.NewDecoder(r.Body).Decode(&rawBody) if _, ok := rawBody["sender_id"]; ok { t.Error("sender_id should be omitted when empty") } w.WriteHeader(http.StatusOK) json.NewEncoder(w).Encode(SendResponse{Success: true, MessageID: "x2", Parts: 1}) })) defer srv.Close() client := NewClient(srv.URL, "cast_testkey", "") _, err := client.SendSMS(context.Background(), "09171234567", "test") if err != nil { t.Fatalf("unexpected error: %v", err) } } // TC5: Failed Cast API key — 401 response is returned as a CastAPIError. func TestSendSMS_Unauthorized(t *testing.T) { srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusUnauthorized) json.NewEncoder(w).Encode(SendResponse{Success: false, Error: "invalid api key"}) })) defer srv.Close() client := NewClient(srv.URL, "cast_badkey", "") _, err := client.SendSMS(context.Background(), "09171234567", "test") if err == nil { t.Fatal("expected error for 401, got nil") } castErr, ok := err.(*CastAPIError) if !ok { t.Fatalf("expected CastAPIError, got %T: %v", err, err) } if castErr.StatusCode != http.StatusUnauthorized { t.Errorf("expected status 401, got %d", castErr.StatusCode) } } func TestSendSMS_RetryOn429(t *testing.T) { var callCount atomic.Int32 srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { n := callCount.Add(1) if n <= 2 { w.Header().Set("Retry-After", "0") w.WriteHeader(http.StatusTooManyRequests) return } w.WriteHeader(http.StatusOK) json.NewEncoder(w).Encode(SendResponse{Success: true, MessageID: "retry-ok", Parts: 1}) })) defer srv.Close() client := NewClient(srv.URL, "cast_testkey", "") resp, err := client.SendSMS(context.Background(), "09171234567", "test") if err != nil { t.Fatalf("unexpected error: %v", err) } if resp.MessageID != "retry-ok" { t.Errorf("expected retry-ok, got %s", resp.MessageID) } if callCount.Load() != 3 { t.Errorf("expected 3 calls, got %d", callCount.Load()) } }