Some checks failed
ci/woodpecker/push/woodpecker Pipeline failed
- internal/ghl/oauth.go:186: defer func(){ _ = resp.Body.Close() }()
- internal/cast/client_test.go: prefix all json.Decode/Encode calls with _ =
- internal/ghl/oauth_test.go: _ = r.ParseForm(), _, _ = w.Write(...)
golangci-lint exclusion rules in v2 are not suppressing test file errcheck
as expected, so fixes are applied directly in source.
Co-Authored-By: Paperclip <noreply@paperclip.ing>
172 lines
5.3 KiB
Go
172 lines
5.3 KiB
Go
package cast
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"errors"
|
|
"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")
|
|
}
|
|
var castErr *CastAPIError
|
|
if !errors.As(err, &castErr) {
|
|
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")
|
|
}
|
|
var castErr *CastAPIError
|
|
if !errors.As(err, &castErr) {
|
|
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")
|
|
}
|
|
var castErr *CastAPIError
|
|
if !errors.As(err, &castErr) {
|
|
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())
|
|
}
|
|
}
|