Files
miti99bot/internal/modules/misc/wheelofnames_api_client_test.go

185 lines
6.0 KiB
Go

package misc
import (
"bytes"
"context"
"encoding/json"
"errors"
"net/http"
"net/http/httptest"
"slices"
"testing"
)
func TestWheelAPIClient_RenderValidRequest(t *testing.T) {
var got wheelAPIRequest
var gotAccept string
var gotAuthorization string
var gotContentType string
var gotMethod string
var gotPath string
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
gotMethod = r.Method
gotPath = r.URL.Path
gotAccept = r.Header.Get("Accept")
gotAuthorization = r.Header.Get("Authorization")
gotContentType = r.Header.Get("Content-Type")
if err := json.NewDecoder(r.Body).Decode(&got); err != nil {
t.Errorf("Decode request body: %v", err)
}
w.Header().Set("Content-Type", "image/gif")
_, _ = w.Write([]byte("GIF89a-remote"))
}))
defer server.Close()
client := wheelAPIClient{
HTTP: server.Client(),
URL: server.URL + "/api/gif",
Token: "secret-token",
}
data, err := client.Render(context.Background(), []string{"alice", "bob", "carol"}, 1)
if err != nil {
t.Fatalf("Render: %v", err)
}
if !bytes.Equal(data, []byte("GIF89a-remote")) {
t.Fatalf("data = %q, want remote GIF bytes", data)
}
if gotMethod != http.MethodPost {
t.Fatalf("method = %q, want POST", gotMethod)
}
if gotPath != "/api/gif" {
t.Fatalf("path = %q, want /api/gif", gotPath)
}
if gotAccept != "image/gif" {
t.Fatalf("Accept = %q, want image/gif", gotAccept)
}
if gotContentType != "application/json" {
t.Fatalf("Content-Type = %q, want application/json", gotContentType)
}
if gotAuthorization != "Bearer secret-token" {
t.Fatalf("Authorization = %q, want bearer token", gotAuthorization)
}
if !slices.Equal(got.Options, []string{"alice", "bob", "carol"}) {
t.Fatalf("options = %#v, want original options", got.Options)
}
if got.WinnerIndex != 1 {
t.Fatalf("winnerIndex = %d, want 1", got.WinnerIndex)
}
assertWheelRemoteDefaults(t, got)
}
func TestWheelAPIClient_RenderWithoutTokenOmitsAuthorization(t *testing.T) {
var gotAuthorization string
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
gotAuthorization = r.Header.Get("Authorization")
w.Header().Set("Content-Type", "image/gif")
_, _ = w.Write([]byte("GIF89a"))
}))
defer server.Close()
client := wheelAPIClient{HTTP: server.Client(), URL: server.URL + "/api/gif"}
if _, err := client.Render(context.Background(), []string{"alice"}, 0); err != nil {
t.Fatalf("Render: %v", err)
}
if gotAuthorization != "" {
t.Fatalf("Authorization = %q, want empty", gotAuthorization)
}
}
func TestWheelAPIClient_RenderNotConfigured(t *testing.T) {
client := wheelAPIClient{}
_, err := client.Render(context.Background(), []string{"alice"}, 0)
if !errors.Is(err, errWheelAPINotConfigured) {
t.Fatalf("Render error = %v, want errWheelAPINotConfigured", err)
}
}
func TestWheelAPIClient_RenderRejectsInvalidInput(t *testing.T) {
client := wheelAPIClient{URL: "https://example.com/api/gif"}
for _, tc := range []struct {
name string
url string
options []string
winner int
}{
{name: "bad scheme", url: "ftp://example.com/api/gif", options: []string{"alice"}, winner: 0},
{name: "empty options", url: "https://example.com/api/gif", options: nil, winner: 0},
{name: "winner out of range", url: "https://example.com/api/gif", options: []string{"alice"}, winner: 1},
} {
t.Run(tc.name, func(t *testing.T) {
client.URL = tc.url
if _, err := client.Render(context.Background(), tc.options, tc.winner); err == nil {
t.Fatalf("Render returned nil error")
}
})
}
}
func TestWheelAPIClient_RenderReturnsErrorsForBadResponses(t *testing.T) {
for _, tc := range []struct {
name string
status int
contentType string
body []byte
}{
{name: "unauthorized", status: http.StatusUnauthorized, contentType: "text/plain", body: []byte("no")},
{name: "server error", status: http.StatusInternalServerError, contentType: "text/plain", body: []byte("bad")},
{name: "non gif", status: http.StatusOK, contentType: "text/plain", body: []byte("not gif")},
{name: "empty gif", status: http.StatusOK, contentType: "image/gif", body: nil},
{name: "mislabeled gif", status: http.StatusOK, contentType: "image/gif", body: []byte("not gif")},
} {
t.Run(tc.name, func(t *testing.T) {
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", tc.contentType)
w.WriteHeader(tc.status)
_, _ = w.Write(tc.body)
}))
defer server.Close()
client := wheelAPIClient{HTTP: server.Client(), URL: server.URL + "/api/gif"}
if _, err := client.Render(context.Background(), []string{"alice"}, 0); err == nil {
t.Fatalf("Render returned nil error")
}
})
}
}
func TestWheelAPIClient_RenderRejectsOversizedResponse(t *testing.T) {
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "image/gif")
_, _ = w.Write(bytes.Repeat([]byte("a"), int(wheelRemoteMaxBytes)+1))
}))
defer server.Close()
client := wheelAPIClient{HTTP: server.Client(), URL: server.URL + "/api/gif"}
if _, err := client.Render(context.Background(), []string{"alice"}, 0); err == nil {
t.Fatalf("Render returned nil error")
}
}
func TestWheelAPIClient_DefaultHTTPClientHasTimeout(t *testing.T) {
client := wheelAPIClient{}
if got := client.httpClient().Timeout; got != wheelRemoteTimeout {
t.Fatalf("timeout = %s, want %s", got, wheelRemoteTimeout)
}
}
func assertWheelRemoteDefaults(t *testing.T, got wheelAPIRequest) {
t.Helper()
if got.DurationMs != wheelRemoteDurationMs {
t.Fatalf("durationMs = %d, want %d", got.DurationMs, wheelRemoteDurationMs)
}
if got.HoldMs != wheelRemoteHoldMs {
t.Fatalf("holdMs = %d, want %d", got.HoldMs, wheelRemoteHoldMs)
}
if got.FPS != wheelRemoteFPS {
t.Fatalf("fps = %d, want %d", got.FPS, wheelRemoteFPS)
}
if got.Size != wheelRemoteSize {
t.Fatalf("size = %d, want %d", got.Size, wheelRemoteSize)
}
if got.Theme != wheelRemoteTheme {
t.Fatalf("theme = %q, want %q", got.Theme, wheelRemoteTheme)
}
}