117 lines
3.2 KiB
Go
117 lines
3.2 KiB
Go
|
|
package api
|
||
|
|
|
||
|
|
import (
|
||
|
|
"encoding/json"
|
||
|
|
"io"
|
||
|
|
"net/http"
|
||
|
|
"net/http/httptest"
|
||
|
|
"strings"
|
||
|
|
"testing"
|
||
|
|
|
||
|
|
"forge.secuaas.ovh/olivier/claude-failover/internal/delegation"
|
||
|
|
"forge.secuaas.ovh/olivier/claude-failover/internal/state"
|
||
|
|
)
|
||
|
|
|
||
|
|
// fakeDelegation implements DelegationProvider for tests.
|
||
|
|
type fakeDelegation struct {
|
||
|
|
active []delegation.ActiveJob
|
||
|
|
snapshot delegation.Snapshot
|
||
|
|
}
|
||
|
|
|
||
|
|
func (f *fakeDelegation) Active() []delegation.ActiveJob { return f.active }
|
||
|
|
func (f *fakeDelegation) CountersSnapshot() delegation.Snapshot { return f.snapshot }
|
||
|
|
|
||
|
|
func newTestServer(t *testing.T, deleg DelegationProvider) *httptest.Server {
|
||
|
|
t.Helper()
|
||
|
|
s := New("ignored", state.New(""))
|
||
|
|
if deleg != nil {
|
||
|
|
s.WithDelegation(deleg)
|
||
|
|
}
|
||
|
|
mux := http.NewServeMux()
|
||
|
|
mux.HandleFunc("/health", s.handleHealth)
|
||
|
|
mux.HandleFunc("/status", s.handleStatus)
|
||
|
|
mux.HandleFunc("/watchdog/status", s.handleWatchdogStatus)
|
||
|
|
mux.HandleFunc("/api/delegated/status", s.handleDelegatedStatus)
|
||
|
|
return httptest.NewServer(mux)
|
||
|
|
}
|
||
|
|
|
||
|
|
func TestHandleHealth(t *testing.T) {
|
||
|
|
srv := newTestServer(t, nil)
|
||
|
|
defer srv.Close()
|
||
|
|
resp, err := http.Get(srv.URL + "/health")
|
||
|
|
if err != nil {
|
||
|
|
t.Fatal(err)
|
||
|
|
}
|
||
|
|
defer resp.Body.Close()
|
||
|
|
body, _ := io.ReadAll(resp.Body)
|
||
|
|
if !strings.Contains(string(body), `"status":"ok"`) {
|
||
|
|
t.Errorf("unexpected /health body: %s", body)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
func TestHandleDelegatedStatus_Disabled(t *testing.T) {
|
||
|
|
srv := newTestServer(t, nil)
|
||
|
|
defer srv.Close()
|
||
|
|
resp, err := http.Get(srv.URL + "/api/delegated/status")
|
||
|
|
if err != nil {
|
||
|
|
t.Fatal(err)
|
||
|
|
}
|
||
|
|
defer resp.Body.Close()
|
||
|
|
if resp.StatusCode != http.StatusNotFound {
|
||
|
|
t.Errorf("expected 404 when delegation disabled, got %d", resp.StatusCode)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
func TestHandleDelegatedStatus_Enabled(t *testing.T) {
|
||
|
|
deleg := &fakeDelegation{
|
||
|
|
active: []delegation.ActiveJob{
|
||
|
|
{JobID: "j1", Project: "/p/a", TaskFile: "task-1.md", Provider: "gpu", Duration: "12s"},
|
||
|
|
},
|
||
|
|
snapshot: delegation.Snapshot{Active: 1, CompletedTotal: 5, FailedTotal: 1},
|
||
|
|
}
|
||
|
|
srv := newTestServer(t, deleg)
|
||
|
|
defer srv.Close()
|
||
|
|
|
||
|
|
resp, err := http.Get(srv.URL + "/api/delegated/status")
|
||
|
|
if err != nil {
|
||
|
|
t.Fatal(err)
|
||
|
|
}
|
||
|
|
defer resp.Body.Close()
|
||
|
|
if resp.StatusCode != http.StatusOK {
|
||
|
|
t.Fatalf("expected 200, got %d", resp.StatusCode)
|
||
|
|
}
|
||
|
|
var out struct {
|
||
|
|
Active []delegation.ActiveJob `json:"active"`
|
||
|
|
Counters delegation.Snapshot `json:"counters"`
|
||
|
|
}
|
||
|
|
if err := json.NewDecoder(resp.Body).Decode(&out); err != nil {
|
||
|
|
t.Fatal(err)
|
||
|
|
}
|
||
|
|
if len(out.Active) != 1 || out.Active[0].JobID != "j1" {
|
||
|
|
t.Errorf("active mismatch: %+v", out.Active)
|
||
|
|
}
|
||
|
|
if out.Counters.CompletedTotal != 5 || out.Counters.Active != 1 {
|
||
|
|
t.Errorf("counters mismatch: %+v", out.Counters)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
func TestHandleWatchdogStatus_IncludesDelegationCounters(t *testing.T) {
|
||
|
|
deleg := &fakeDelegation{snapshot: delegation.Snapshot{
|
||
|
|
Active: 2, CompletedTotal: 7, FailedTotal: 3,
|
||
|
|
}}
|
||
|
|
srv := newTestServer(t, deleg)
|
||
|
|
defer srv.Close()
|
||
|
|
|
||
|
|
resp, err := http.Get(srv.URL + "/watchdog/status")
|
||
|
|
if err != nil {
|
||
|
|
t.Fatal(err)
|
||
|
|
}
|
||
|
|
defer resp.Body.Close()
|
||
|
|
body, _ := io.ReadAll(resp.Body)
|
||
|
|
for _, want := range []string{`"delegated_active":2`, `"delegated_completed_total":7`, `"delegated_failed_total":3`} {
|
||
|
|
if !strings.Contains(string(body), want) {
|
||
|
|
t.Errorf("missing %s in body: %s", want, body)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|