mirror of
https://github.com/tiennm99/miti99bot.git
synced 2026-10-04 08:13:26 +00:00
The bot now takes the renderer's base URL and appends each /api/<name> route itself, instead of taking the full /api/gif endpoint and swapping its last path segment for /gacha and /genshin. The client, its files, and its errors are named after the renderer rather than wheelofnames; the /wheelofnames command keeps its name. BREAKING CHANGE: WHEELOFNAMES_API_URL is replaced by RENDERER_URL, which holds the base URL (e.g. http://renderer:3000) rather than the /api/gif endpoint. compose.yml sets it, so Coolify needs no value.
180 lines
5.9 KiB
Go
180 lines
5.9 KiB
Go
package random
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"encoding/json"
|
|
"errors"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"slices"
|
|
"testing"
|
|
)
|
|
|
|
func TestRendererClient_RenderValidRequest(t *testing.T) {
|
|
var got wheelRenderRequest
|
|
var gotAccept 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")
|
|
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 := rendererClient{
|
|
HTTP: server.Client(),
|
|
BaseURL: server.URL,
|
|
}
|
|
data, err := client.RenderWheel(context.Background(), []string{"alice", "bob", "carol"}, 1)
|
|
if err != nil {
|
|
t.Fatalf("RenderWheel: %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 !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)
|
|
}
|
|
|
|
// The renderer is internal to the compose network; the bot sends no credentials.
|
|
func TestRendererClient_RenderSendsNoAuthorization(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 := rendererClient{HTTP: server.Client(), BaseURL: server.URL}
|
|
if _, err := client.RenderWheel(context.Background(), []string{"alice"}, 0); err != nil {
|
|
t.Fatalf("RenderWheel: %v", err)
|
|
}
|
|
if gotAuthorization != "" {
|
|
t.Fatalf("Authorization = %q, want empty", gotAuthorization)
|
|
}
|
|
}
|
|
|
|
func TestRendererClient_RenderNotConfigured(t *testing.T) {
|
|
client := rendererClient{}
|
|
_, err := client.RenderWheel(context.Background(), []string{"alice"}, 0)
|
|
if !errors.Is(err, errRendererNotConfigured) {
|
|
t.Fatalf("RenderWheel error = %v, want errRendererNotConfigured", err)
|
|
}
|
|
}
|
|
|
|
func TestRendererClient_RenderRejectsInvalidInput(t *testing.T) {
|
|
client := rendererClient{BaseURL: "https://example.com"}
|
|
for _, tc := range []struct {
|
|
name string
|
|
url string
|
|
options []string
|
|
winner int
|
|
}{
|
|
{name: "bad scheme", url: "ftp://example.com", options: []string{"alice"}, winner: 0},
|
|
{name: "empty options", url: "https://example.com", options: nil, winner: 0},
|
|
{name: "winner out of range", url: "https://example.com", options: []string{"alice"}, winner: 1},
|
|
} {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
client.BaseURL = tc.url
|
|
if _, err := client.RenderWheel(context.Background(), tc.options, tc.winner); err == nil {
|
|
t.Fatalf("RenderWheel returned nil error")
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestRendererClient_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 := rendererClient{HTTP: server.Client(), BaseURL: server.URL}
|
|
if _, err := client.RenderWheel(context.Background(), []string{"alice"}, 0); err == nil {
|
|
t.Fatalf("RenderWheel returned nil error")
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestRendererClient_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(rendererMaxBytes)+1))
|
|
}))
|
|
defer server.Close()
|
|
|
|
client := rendererClient{HTTP: server.Client(), BaseURL: server.URL}
|
|
if _, err := client.RenderWheel(context.Background(), []string{"alice"}, 0); err == nil {
|
|
t.Fatalf("RenderWheel returned nil error")
|
|
}
|
|
}
|
|
|
|
func TestRendererClient_DefaultHTTPClientHasTimeout(t *testing.T) {
|
|
client := rendererClient{}
|
|
if got := client.httpClient().Timeout; got != rendererTimeout {
|
|
t.Fatalf("timeout = %s, want %s", got, rendererTimeout)
|
|
}
|
|
}
|
|
|
|
func assertWheelRemoteDefaults(t *testing.T, got wheelRenderRequest) {
|
|
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)
|
|
}
|
|
}
|