Ctrl K
ComunidadGuías…

internal/promql/client_test.go

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

Archivo original ↗Preguntar a KiBot
package promql

import (
	"context"
	"math"
	"net/http"
	"net/http/httptest"
	"testing"
	"time"
)

func TestScalarParsesPrometheusStringValues(t *testing.T) {
	// Prometheus sends sample values as JSON *strings*, not numbers, and the
	// set includes NaN and the infinities. Anything unparseable must be
	// dropped rather than silently becoming zero.
	tests := []struct {
		name string
		in   []any
		want float64
		ok   bool
	}{
		{"integer", []any{1.0, "42"}, 42, true},
		{"float", []any{1.0, "3.5"}, 3.5, true},
		{"negative", []any{1.0, "-0.25"}, -0.25, true},
		{"exponent", []any{1.0, "1.5e3"}, 1500, true},
		{"positive infinity", []any{1.0, "+Inf"}, math.Inf(1), true},
		{"negative infinity", []any{1.0, "-Inf"}, math.Inf(-1), true},
		{"wrong length", []any{"42"}, 0, false},
		{"number not string", []any{1.0, 42.0}, 0, false},
		{"garbage", []any{1.0, "not-a-number"}, 0, false},
	}
	for _, tc := range tests {
		t.Run(tc.name, func(t *testing.T) {
			got, ok := scalar(tc.in)
			if ok != tc.ok {
				t.Fatalf("ok = %v, want %v", ok, tc.ok)
			}
			if ok && got != tc.want {
				t.Fatalf("value = %v, want %v", got, tc.want)
			}
		})
	}
}

func TestScalarNaN(t *testing.T) {
	// NaN parses successfully but never equals itself, so it needs its own case.
	got, ok := scalar([]any{1.0, "NaN"})
	if !ok {
		t.Fatal("NaN should parse")
	}
	if !math.IsNaN(got) {
		t.Fatalf("got %v, want NaN", got)
	}
}

func TestQueryOneEmptyResultIsNotAnError(t *testing.T) {
	// An absent metric is normal - no such device, or the exporter has not
	// started. It must not surface as an error.
	srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		_, _ = w.Write([]byte(`{"status":"success","data":{"resultType":"vector","result":[]}}`))
	}))
	defer srv.Close()

	c := New(func() string { return srv.URL })
	_, ok, err := c.QueryOne(context.Background(), "up")
	if err != nil {
		t.Fatalf("unexpected error: %v", err)
	}
	if ok {
		t.Fatal("ok should be false for an empty result")
	}
}

func TestUnconfiguredURL(t *testing.T) {
	c := New(func() string { return "" })
	if _, err := c.Query(context.Background(), "up"); err != ErrNotConfigured {
		t.Fatalf("got %v, want ErrNotConfigured", err)
	}
}

func TestQueryReportsPrometheusErrors(t *testing.T) {
	srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		w.WriteHeader(http.StatusBadRequest)
		_, _ = w.Write([]byte(`{"status":"error","errorType":"bad_data","error":"parse error"}`))
	}))
	defer srv.Close()

	c := New(func() string { return srv.URL })
	_, err := c.Query(context.Background(), "((")
	if err == nil {
		t.Fatal("expected an error")
	}
}

func TestQueryRangeDropsUnparseablePoints(t *testing.T) {
	srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		_, _ = w.Write([]byte(`{"status":"success","data":{"resultType":"matrix","result":[
			{"metric":{"__name__":"x"},"values":[[1,"1"],[2,"bad"],[3,"3"]]}]}}`))
	}))
	defer srv.Close()

	c := New(func() string { return srv.URL })
	series, err := c.QueryRange(context.Background(), "x", time.Unix(1, 0), time.Unix(3, 0), time.Second)
	if err != nil {
		t.Fatal(err)
	}
	if len(series) != 1 {
		t.Fatalf("got %d series, want 1", len(series))
	}
	if got := len(series[0].Points); got != 2 {
		t.Fatalf("got %d points, want 2 (the unparseable one dropped)", got)
	}
}