Files
Felis/internal/operator/gauge_test.go
T
flyemoji 79eae7f669 feat(metrics): publish felis_servers_total from a fleet snapshot
A per-object reconcile cannot maintain felis_servers_total (spec §23): it
sees one server per call, so it could never Set a correct fleet-wide gauge
and inc/dec on transitions would drift on any missed event. Add a snapshot
producer instead.

metrics.SyncServerGauge Resets the GaugeVec then Sets one child per state,
so a state that drains to zero reports 0 rather than a stale last value.
operator.GaugeSyncer is a manager.Runnable that periodically Lists the
fleet and republishes from it, defaulting an unset desiredState to Stopped.

SyncOnce is exercised end-to-end against a fake client (List, default,
republish); the ticker loop in Start is the only untested I/O edge.
2026-06-30 12:52:53 +09:00

51 lines
1.8 KiB
Go

package operator_test
import (
"context"
"testing"
"felis.lolicon.best/internal/apis/felis/v1alpha1"
"felis.lolicon.best/internal/metrics"
"felis.lolicon.best/internal/operator"
"github.com/prometheus/client_golang/prometheus/testutil"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"sigs.k8s.io/controller-runtime/pkg/client/fake"
)
func gaugeServer(name string, state v1alpha1.DesiredState) *v1alpha1.MinecraftServer {
return &v1alpha1.MinecraftServer{
ObjectMeta: metav1.ObjectMeta{Name: name, Namespace: "minecraft"},
Spec: v1alpha1.MinecraftServerSpec{Subdomain: name, DesiredState: state},
}
}
// TestGaugeSyncerPublishesFleetStateFromCluster drives SyncOnce against a fake
// client to verify the whole production path bar the ticker: List the fleet,
// translate each server to its desiredState (an unset state defaulting to
// Stopped), and republish felis_servers_total partitioned by that state.
func TestGaugeSyncerPublishesFleetStateFromCluster(t *testing.T) {
scheme := newScheme(t)
c := fake.NewClientBuilder().
WithScheme(scheme).
WithObjects(
gaugeServer("a", v1alpha1.DesiredRunning),
gaugeServer("b", v1alpha1.DesiredRunning),
gaugeServer("c", v1alpha1.DesiredStopped),
gaugeServer("d", ""), // unset desiredState must be counted as Stopped
).
Build()
g := &operator.GaugeSyncer{Client: c}
if err := g.SyncOnce(context.Background()); err != nil {
t.Fatalf("SyncOnce: %v", err)
}
if got := testutil.ToFloat64(metrics.ServersTotal.WithLabelValues("Running")); got != 2 {
t.Errorf("servers_total{state=Running} = %v, want 2", got)
}
// One explicit Stopped plus one unset (defaulted to Stopped) = 2.
if got := testutil.ToFloat64(metrics.ServersTotal.WithLabelValues("Stopped")); got != 2 {
t.Errorf("servers_total{state=Stopped} = %v, want 2", got)
}
}