package main import ( "context" "errors" "io" "net/http" "net/http/httptest" "strings" "testing" "tera-clone/services/tera-api/internal/directstream" "tera-clone/services/tera-api/internal/serial" ) type ticketStatusStub struct { status directstream.TicketServiceSnapshot err error } func (s ticketStatusStub) Status(context.Context) (directstream.TicketServiceSnapshot, error) { return s.status, s.err } type serialStatusStub struct { status serial.StreamSnapshot err error } func (s serialStatusStub) Status(context.Context) (serial.StreamSnapshot, error) { return s.status, s.err } func TestSessiondHealthAndReadinessExposeDependencyFailures(t *testing.T) { broker, err := directstream.NewBroker(unavailableTransportFactory{}, directstream.BrokerOptions{}) if err != nil { t.Fatal(err) } defer broker.Close() metrics := &directstream.WebSocketMetrics{} handler := newSessiondStatusHandler( broker, metrics, ticketStatusStub{status: directstream.TicketServiceSnapshot{ActiveBindings: 1}}, serialStatusStub{err: errors.New("seriald unavailable")}, ) health := httptest.NewRecorder() handler(false).ServeHTTP(health, httptest.NewRequest(http.MethodGet, "/healthz", nil)) if health.Code != http.StatusOK { t.Fatalf("health status = %d", health.Code) } if body := health.Body.String(); !containsAll(body, `"status":"degraded"`, `"serial_error":"seriald unavailable"`, `"active_bindings":1`) { t.Fatalf("health body = %s", body) } ready := httptest.NewRecorder() handler(true).ServeHTTP(ready, httptest.NewRequest(http.MethodGet, "/readyz", nil)) if ready.Code != http.StatusServiceUnavailable { t.Fatalf("readiness status = %d, body = %s", ready.Code, ready.Body.String()) } } type unavailableTransportFactory struct{} func (unavailableTransportFactory) Open(context.Context, string) (io.ReadWriteCloser, error) { return nil, errors.New("not used") } func containsAll(value string, expected ...string) bool { for _, item := range expected { if !strings.Contains(value, item) { return false } } return true }