twitch-byte-bot / tui /cmd /api /client_test.go
JuanCS-Dev's picture
fix(bot): cure concurrency crash and harden server for HF Spaces
bb6606d verified
Raw History Blame Contribute Delete
5.13 kB
package api
import (
"bytes"
"encoding/json"
"io"
"net/http"
"reflect"
"testing"
"github.com/JuanCS-Dev/byte-tui/cmd/config"
)
// RoundTripFunc .
type RoundTripFunc func(req *http.Request) *http.Response
// RoundTrip .
func (f RoundTripFunc) RoundTrip(req *http.Request) (*http.Response, error) {
return f(req), nil
}
// NewTestClient returns *http.Client with Transport replaced to avoid making real calls
func NewTestClient(fn RoundTripFunc) *http.Client {
return &http.Client{
Transport: RoundTripFunc(fn),
}
}
func TestClientAuthenticationHeaders(t *testing.T) {
cfg := config.Config{
ApiUrl: "http://test.com",
AdminToken: "test-admin",
HFToken: "test-hf",
}
client := NewClient(cfg)
// Inject mocked HTTP client
client.HTTPClient = NewTestClient(func(req *http.Request) *http.Response {
// Assert Headers Correctness
if req.Header.Get("X-Byte-Admin-Token") != "test-admin" {
t.Errorf("Expected X-Byte-Admin-Token to be 'test-admin', got '%s'", req.Header.Get("X-Byte-Admin-Token"))
}
if req.Header.Get("Authorization") != "Bearer test-hf" {
t.Errorf("Expected Authorization to be 'Bearer test-hf', got '%s'", req.Header.Get("Authorization"))
}
// Fake response
return &http.Response{
StatusCode: 200,
Body: io.NopCloser(bytes.NewBufferString(`{"success": true}`)),
Header: make(http.Header),
}
})
_, err := client.Get("/test")
if err != nil {
t.Fatal(err)
}
}
func TestClientGet(t *testing.T) {
cfg := config.Config{ApiUrl: "http://test.com"}
client := NewClient(cfg)
client.HTTPClient = NewTestClient(func(req *http.Request) *http.Response {
if req.Method != "GET" {
t.Errorf("Expected method GET, got %s", req.Method)
}
return &http.Response{
StatusCode: 200,
Body: io.NopCloser(bytes.NewBufferString(`{"data": "value"}`)),
Header: make(http.Header),
}
})
res, err := client.Get("/some-path")
if err != nil {
t.Fatal(err)
}
expected := map[string]interface{}{"data": "value"}
if !reflect.DeepEqual(res, expected) {
t.Errorf("Expected %v, got %v", expected, res)
}
}
func TestClientPost(t *testing.T) {
cfg := config.Config{ApiUrl: "http://test.com"}
client := NewClient(cfg)
client.HTTPClient = NewTestClient(func(req *http.Request) *http.Response {
if req.Method != "POST" {
t.Errorf("Expected method POST, got %s", req.Method)
}
var payload map[string]interface{}
json.NewDecoder(req.Body).Decode(&payload)
if payload["input"] != "hello" {
t.Errorf("Expected body {input: hello}, got %v", payload)
}
return &http.Response{
StatusCode: 200,
Body: io.NopCloser(bytes.NewBufferString(`{"result": "ok"}`)),
Header: make(http.Header),
}
})
res, err := client.Post("/send", map[string]interface{}{"input": "hello"})
if err != nil {
t.Fatal(err)
}
if res["result"] != "ok" {
t.Errorf("Expected 'result': 'ok', got %v", res)
}
}
func TestClientErrorHandling500(t *testing.T) {
cfg := config.Config{ApiUrl: "http://test.com"}
client := NewClient(cfg)
client.HTTPClient = NewTestClient(func(req *http.Request) *http.Response {
return &http.Response{
StatusCode: 500,
Status: "500 Internal Server Error",
Body: io.NopCloser(bytes.NewBufferString(`{"detail": "Agent crashed"}`)),
Header: make(http.Header),
}
})
_, err := client.Get("/crash")
if err == nil {
t.Fatal("Expected an error for 500 status code, got none")
}
apiErr, ok := err.(*APIError)
if !ok {
t.Fatalf("Expected error of type *APIError, got %T", err)
}
if apiErr.StatusCode != 500 {
t.Errorf("Expected status code 500, got %d", apiErr.StatusCode)
}
if apiErr.Message != "Agent crashed" {
t.Errorf("Expected message 'Agent crashed', got '%s'", apiErr.Message)
}
}
func TestClientHealthPlaintext(t *testing.T) {
cfg := config.Config{ApiUrl: "http://test.com"}
client := NewClient(cfg)
client.HTTPClient = NewTestClient(func(req *http.Request) *http.Response {
return &http.Response{
StatusCode: 200,
Body: io.NopCloser(bytes.NewBufferString("AGENT_ONLINE")),
Header: make(http.Header),
}
})
res, err := client.Get("/health")
if err != nil {
t.Fatal(err)
}
if res["status"] != "AGENT_ONLINE" || res["ok"] != true {
t.Errorf("Expected {ok: true, status: AGENT_ONLINE}, got %v", res)
}
}
func TestClientPutAndDelete(t *testing.T) {
cfg := config.Config{ApiUrl: "http://test.com"}
client := NewClient(cfg)
client.HTTPClient = NewTestClient(func(req *http.Request) *http.Response {
if req.Method == "PUT" || req.Method == "DELETE" {
return &http.Response{
StatusCode: 200,
Body: io.NopCloser(bytes.NewBufferString(`{"status": "mod_ok"}`)),
Header: make(http.Header),
}
}
return &http.Response{StatusCode: 400, Body: io.NopCloser(bytes.NewBufferString(""))}
})
resPut, err := client.Put("/modify", map[string]interface{}{"val": 1})
if err != nil || resPut["status"] != "mod_ok" {
t.Errorf("Failed PUT test")
}
resDel, err := client.Delete("/remove")
if err != nil || resDel["status"] != "mod_ok" {
t.Errorf("Failed DELETE test")
}
}