Ctrl K
ComunidadGuías…

internal/telemetry/metric_test.go

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
package telemetry

import (
	"io"
	"strconv"
	"strings"
	"sync"
	"testing"
)

func countAfter(t *testing.T, page, prefix string) int {
	t.Helper()
	for line := range strings.SplitSeq(page, "\n") {
		if !strings.HasPrefix(line, prefix) {
			continue
		}
		value := strings.TrimPrefix(line, prefix)
		count, err := strconv.Atoi(value)
		if err != nil {
			t.Fatalf("the line %q does not end in a count: %v", line, err)
		}
		return count
	}
	t.Fatalf("no line starting %q in\n%s", prefix, page)
	return 0
}

func TestAHistogramCountsEveryObservationIntoTheBucketsAboveIt(t *testing.T) {
	cases := []struct {
		name    string
		seen    []float64
		buckets []string
		sum     string
		count   string
	}{
		{
			name:    "nothing observed yet",
			seen:    nil,
			buckets: []string{"0", "0", "0", "0"},
			sum:     "0",
			count:   "0",
		},
		{
			name:    "one observation in the first bucket",
			seen:    []float64{0.1},
			buckets: []string{"1", "1", "1", "1"},
			sum:     "0.1",
			count:   "1",
		},
		{
			name:    "one observation past the last bound",
			seen:    []float64{9},
			buckets: []string{"0", "0", "0", "1"},
			sum:     "9",
			count:   "1",
		},
		{
			name:    "an observation exactly on a bound belongs to it",
			seen:    []float64{1},
			buckets: []string{"0", "1", "1", "1"},
			sum:     "1",
			count:   "1",
		},
		{
			name:    "one in each bucket",
			seen:    []float64{0.25, 0.75, 3, 40},
			buckets: []string{"1", "2", "3", "4"},
			sum:     "44",
			count:   "4",
		},
	}
	for _, one := range cases {
		t.Run(one.name, func(t *testing.T) {
			reg := New()
			took := reg.Histogram("spinoza_check_run_duration_seconds", "how long a run of the checks took", []float64{0.5, 1, 5})
			for _, value := range one.seen {
				took.Observe(value)
			}

			page := rendered(t, reg)

			want := []string{
				`spinoza_check_run_duration_seconds_bucket{le="0.5"} ` + one.buckets[0],
				`spinoza_check_run_duration_seconds_bucket{le="1"} ` + one.buckets[1],
				`spinoza_check_run_duration_seconds_bucket{le="5"} ` + one.buckets[2],
				`spinoza_check_run_duration_seconds_bucket{le="+Inf"} ` + one.buckets[3],
				"spinoza_check_run_duration_seconds_sum " + one.sum,
				"spinoza_check_run_duration_seconds_count " + one.count,
			}
			for _, line := range want {
				if !strings.Contains(page, line+"\n") {
					t.Fatalf("the page reads\n%s\nand should carry\n%s", page, line)
				}
			}
		})
	}
}

func TestAHistogramBucketNeverReadsLowerThanTheOneBelowIt(t *testing.T) {
	reg := New()
	took := reg.Histogram("spinoza_check_run_duration_seconds", "how long a run of the checks took", []float64{0.5, 1, 5})
	for _, value := range []float64{0.1, 0.2, 0.7, 2, 3, 40, 41} {
		took.Observe(value)
	}

	page := rendered(t, reg)

	running := 0
	for _, bound := range []string{"0.5", "1", "5", "+Inf"} {
		count := countAfter(t, page, `spinoza_check_run_duration_seconds_bucket{le="`+bound+`"} `)
		if count < running {
			t.Fatalf("bucket %s reads %d after %d, so the buckets are not cumulative", bound, count, running)
		}
		running = count
	}
	if running != 7 {
		t.Fatalf("the +Inf bucket reads %d and seven observations were made", running)
	}
	if countAfter(t, page, "spinoza_check_run_duration_seconds_count ") != 7 {
		t.Fatalf("the count disagrees with the +Inf bucket:\n%s", page)
	}
}

func TestTheBucketsOfAHistogramComeOutInAscendingOrderEndingAtInf(t *testing.T) {
	reg := New()
	reg.Histogram("spinoza_informer_sync_seconds", "how long an informer took to sync", []float64{5, 0.5, 1, 5})

	page := rendered(t, reg)

	want := []string{`le="0.5"`, `le="1"`, `le="5"`, `le="+Inf"`}
	at := 0
	for _, bound := range want {
		found := strings.Index(page[at:], bound)
		if found < 0 {
			t.Fatalf("the buckets came out as\n%s\nand %s is missing or out of order", page, bound)
		}
		at += found
	}
	if strings.Count(page, "_bucket{") != len(want) {
		t.Fatalf("a repeated bound was declared twice:\n%s", page)
	}
}

func TestManyGoroutinesIncrementingOneCounterLoseNothing(t *testing.T) {
	reg := New()
	calls := reg.Counter("spinoza_apiserver_calls_total", "calls spinoza has made to an apiserver", "outcome")
	const writers = 64
	const each = 200

	running := sync.WaitGroup{}
	running.Add(writers)
	for range writers {
		go func() {
			defer running.Done()
			for range each {
				calls.Inc("ok")
			}
		}()
	}
	running.Wait()

	want := "spinoza_apiserver_calls_total{outcome=\"ok\"} " + strconv.Itoa(writers*each)
	if !strings.Contains(rendered(t, reg), want+"\n") {
		t.Fatalf("the counter should read %s and reads\n%s", want, rendered(t, reg))
	}
}

func TestTheRegistryCanBeReadWhileItIsBeingWrittenTo(t *testing.T) {
	reg := New()
	calls := reg.Counter("spinoza_apiserver_calls_total", "calls spinoza has made to an apiserver", "outcome")
	took := reg.Histogram("spinoza_check_run_duration_seconds", "how long a run of the checks took", []float64{0.5, 1, 5})
	sessions := reg.Gauge("spinoza_sessions", "signed-in sessions right now", "role")

	running := sync.WaitGroup{}
	running.Add(3)
	go func() {
		defer running.Done()
		for at := range 500 {
			calls.Inc("outcome" + strconv.Itoa(at%7))
		}
	}()
	go func() {
		defer running.Done()
		for at := range 500 {
			took.Observe(float64(at) / 100)
			sessions.Set(float64(at), "admin")
		}
	}()
	go func() {
		defer running.Done()
		for range 200 {
			err := reg.Write(io.Discard)
			if err != nil {
				t.Errorf("write the registry: %v", err)
				return
			}
		}
	}()
	running.Wait()

	if !strings.Contains(rendered(t, reg), "spinoza_check_run_duration_seconds_count 500\n") {
		t.Fatal("the histogram lost an observation made while the page was being read")
	}
}

func TestACounterAskedToGoBackwardsKeepsItsValueAndSaysSo(t *testing.T) {
	cases := []struct {
		name  string
		delta float64
		want  string
	}{
		{name: "a negative step", delta: -1, want: "1"},
		{name: "a positive step", delta: 2, want: "3"},
		{name: "no step at all", delta: 0, want: "1"},
	}
	for _, one := range cases {
		t.Run(one.name, func(t *testing.T) {
			reg := New()
			failures := reg.Counter("spinoza_audit_write_failures_total", "audit rows spinoza could not write")
			failures.Inc()
			failures.Add(one.delta)

			page := rendered(t, reg)

			if !strings.Contains(page, "spinoza_audit_write_failures_total "+one.want+"\n") {
				t.Fatalf("the counter should read %s and the page reads\n%s", one.want, page)
			}
		})
	}
}

func TestAGaugeCarriesTheLastValueItWasGiven(t *testing.T) {
	reg := New()
	open := reg.Gauge("spinoza_websocket_sessions", "websocket sessions open right now")
	open.Inc()
	open.Inc()
	open.Dec()
	open.Add(4)

	if !strings.Contains(rendered(t, reg), "spinoza_websocket_sessions 5\n") {
		t.Fatalf("the gauge reads\n%s", rendered(t, reg))
	}

	open.Set(0)

	if !strings.Contains(rendered(t, reg), "spinoza_websocket_sessions 0\n") {
		t.Fatalf("a gauge set back to zero reads\n%s", rendered(t, reg))
	}
}