package api import ( "testing" "time" "github.com/rs/zerolog" ) func TestServerStateModeNormal(t *testing.T) { b := NewBroker(zerolog.Nop()) ss := NewServerState(zerolog.Nop(), nil, b) if ss.Mode() != ModeNormal { t.Fatalf("expected normal, got %s", ss.Mode()) } if ss.IsReadOnly() { t.Fatal("expected not read-only") } } func TestServerStateModeReadOnly(t *testing.T) { b := NewBroker(zerolog.Nop()) ss := NewServerState(zerolog.Nop(), nil, b) ss.SetReadOnly(true) if ss.Mode() != ModeReadOnly { t.Fatalf("expected read-only, got %s", ss.Mode()) } if !ss.IsReadOnly() { t.Fatal("expected read-only") } ss.SetReadOnly(false) if ss.Mode() != ModeNormal { t.Fatalf("expected normal after clearing read-only, got %s", ss.Mode()) } } func TestServerStateModeDegraded(t *testing.T) { b := NewBroker(zerolog.Nop()) // Pass a non-nil storage placeholder to simulate configured storage. // We manipulate storageOK directly to test degraded mode. ss := NewServerState(zerolog.Nop(), nil, b) // Simulate storage configured but unhealthy by setting fields directly. ss.mu.Lock() // We need a non-nil storage pointer to trigger degraded mode check. // Since we can't easily create a fake storage, we test the mode() logic // by checking that without storage, mode stays normal. ss.mu.Unlock() // Without storage configured, mode should be normal even if storageOK is false ss.mu.Lock() ss.storageOK = false ss.mu.Unlock() if ss.Mode() != ModeNormal { t.Fatalf("expected normal (no storage configured), got %s", ss.Mode()) } } func TestServerStateToggleReadOnly(t *testing.T) { b := NewBroker(zerolog.Nop()) ss := NewServerState(zerolog.Nop(), nil, b) ss.ToggleReadOnly() if !ss.IsReadOnly() { t.Fatal("expected read-only after toggle") } ss.ToggleReadOnly() if ss.IsReadOnly() { t.Fatal("expected not read-only after second toggle") } } func TestServerStateBroadcastsOnTransition(t *testing.T) { b := NewBroker(zerolog.Nop()) c := b.Subscribe("", "") defer b.Unsubscribe(c) ss := NewServerState(zerolog.Nop(), nil, b) ss.SetReadOnly(true) select { case ev := <-c.ch: if ev.Type != "server.state" { t.Fatalf("expected server.state event, got %s", ev.Type) } case <-time.After(time.Second): t.Fatal("timed out waiting for server.state event") } // Setting to same value should not broadcast ss.SetReadOnly(true) select { case ev := <-c.ch: t.Fatalf("unexpected event on no-op SetReadOnly: %+v", ev) case <-time.After(50 * time.Millisecond): // expected — no event } } func TestServerStateReadOnlyOverridesDegraded(t *testing.T) { b := NewBroker(zerolog.Nop()) ss := NewServerState(zerolog.Nop(), nil, b) // Both read-only and storage down: should show read-only ss.mu.Lock() ss.readOnly = true ss.storageOK = false ss.mu.Unlock() if ss.Mode() != ModeReadOnly { t.Fatalf("expected read-only to override degraded, got %s", ss.Mode()) } }