Files
ITWorx-Pulse-Public/internal/promqlbinding/binding_test.go
T
ITWorx Pulse release export bd774932d5
Public source validation / validate (push) Failing after 3m8s
Publish ITWorx Pulse source
2026-09-03 02:09:19 +02:00

144 lines
5.5 KiB
Go

package promqlbinding
import (
"context"
"errors"
"strings"
"testing"
"time"
"github.com/itworx/pulse/internal/auth"
"github.com/itworx/pulse/internal/datasource"
"github.com/itworx/pulse/internal/metriccatalog"
"github.com/itworx/pulse/internal/queryplan"
)
func TestCompileQuotesLabelValuesAndUsesBoundedWindow(t *testing.T) {
registry, err := metriccatalog.DefaultRegistry()
if err != nil {
t.Fatal(err)
}
definition, ok := registry.Find("host.cpu.utilization")
if !ok {
t.Fatal("seed metric missing")
}
compiled, err := Compile(definition, map[string]string{"instance": "server-1", "window": "5m"})
if err != nil {
t.Fatal(err)
}
if !strings.Contains(compiled, `instance="server-1"`) || !strings.Contains(compiled, "[5m]") || strings.Contains(compiled, "{{") {
t.Fatalf("compiled=%s", compiled)
}
}
func TestCompileRejectsInjectionMissingAndUnknownValues(t *testing.T) {
registry, err := metriccatalog.DefaultRegistry()
if err != nil {
t.Fatal(err)
}
definition, _ := registry.Find("host.cpu.utilization")
cases := []map[string]string{
{"instance": `server-1" or up`, "window": "5m"},
{"instance": "server-1"},
{"instance": "server-1", "window": "5m", "groupBy": "container"},
{"instance": "server-1", "window": "5m;drop"},
}
for index, values := range cases {
if _, err := Compile(definition, values); err == nil {
t.Fatalf("case %d accepted", index)
}
}
}
func TestCompilePlanDoesNotAppendArbitraryGrouping(t *testing.T) {
registry, err := metriccatalog.DefaultRegistry()
if err != nil {
t.Fatal(err)
}
planner := queryplan.NewPlanner(registry, queryplan.Limits{})
request := queryplan.Request{Metric: "container.cpu.utilization", Scope: map[string]string{"containerId": "media_server"}, Range: queryplan.Range{From: now().Add(-3600), To: now(), StepSeconds: 60}, GroupBy: []string{"container"}}
plan, err := planner.Plan(auth.WithPrincipal(context.Background(), auth.Principal{Subject: "viewer", Role: auth.RoleViewer}), request)
if err != nil {
t.Fatal(err)
}
compiled, err := CompilePlan(plan)
if err != nil {
t.Fatal(err)
}
if !strings.Contains(compiled, `name="media_server"`) || strings.Contains(compiled, `container="media_server"`) || strings.Contains(compiled, "groupBy") {
t.Fatalf("compiled=%s", compiled)
}
if WindowForStep(15) != "1m" || WindowForStep(60) != "1m" || WindowForStep(90) != "90s" || WindowForStep(3600) != "1h" {
t.Fatal("unexpected window formatting")
}
}
func TestCompilePlanOmitsWindowForInstantGaugeTemplate(t *testing.T) {
registry, err := metriccatalog.DefaultRegistry()
if err != nil {
t.Fatal(err)
}
planner := queryplan.NewPlanner(registry, queryplan.Limits{})
request := queryplan.Request{Metric: "host.memory.utilization", Scope: map[string]string{"serverId": "smoke-host"}, Range: queryplan.Range{From: now().Add(-5 * time.Minute), To: now(), StepSeconds: 15}}
plan, err := planner.Plan(auth.WithPrincipal(context.Background(), auth.Principal{Subject: "viewer", Role: auth.RoleViewer}), request)
if err != nil {
t.Fatal(err)
}
compiled, err := CompilePlan(plan)
if err != nil {
t.Fatal(err)
}
if !strings.Contains(compiled, `instance="smoke-host"`) || strings.Contains(compiled, "{{") || strings.Contains(compiled, "[15s]") {
t.Fatalf("compiled=%s", compiled)
}
}
func TestBindingFallbackIsDeterministic(t *testing.T) {
registry, err := metriccatalog.DefaultRegistry()
if err != nil {
t.Fatal(err)
}
definition, _ := registry.Find("host.cpu.utilization")
first := Binding{ID: "z", Definition: definition, RequiredCapabilities: []string{"cap-z"}, Priority: 1}
second := Binding{ID: "a", Definition: definition, RequiredCapabilities: []string{"cap-a"}, Priority: 1}
second.Definition.Version = 2
capabilities := datasource.CapabilitySet{{ID: "cap-a", Version: "v1", State: datasource.CapabilityEnabled}}
selected, err := SelectBinding([]Binding{first, second}, capabilities)
if err != nil {
t.Fatal(err)
}
if selected.ID != "a" {
t.Fatalf("selected=%s", selected.ID)
}
fallback, err := SelectBinding([]Binding{second, first}, nil)
if err == nil || fallback.ID != "a" {
t.Fatalf("fallback=%+v err=%v", fallback, err)
}
}
func TestRawQueryRequiresAdminAndHonorsCancellation(t *testing.T) {
viewer := auth.WithPrincipal(context.Background(), auth.Principal{Subject: "viewer", Role: auth.RoleViewer})
if _, err := ValidateRawQuery(viewer, "up"); err == nil {
t.Fatal("viewer raw query accepted")
}
admin := auth.WithPrincipal(context.Background(), auth.Principal{Subject: "admin", Role: auth.RoleAdministrator})
query, err := ValidateRawQuery(admin, " up ")
if err != nil || query != "up" {
t.Fatalf("query=%q err=%v", query, err)
}
canceled, cancel := context.WithCancel(admin)
cancel()
if _, err := ValidateRawQuery(canceled, "up"); !errors.Is(err, context.Canceled) {
t.Fatalf("cancel=%v", err)
}
}
func TestCompileRejectsOversizedCompiledQuery(t *testing.T) {
definition := metriccatalog.Definition{SemanticName: "host.cpu.utilization", Version: 1, Description: "cpu", Unit: "percent", ValueKind: "gauge", SourceKind: "prometheus", QueryTemplate: strings.Repeat("up ", 2100) + "{{instance}}", AllowedLabels: []string{"instance"}, CardinalityBudget: 1, Limits: metriccatalog.Limits{MaxRangeSeconds: 60, MaxSeries: 1, MaxPoints: 10, TimeoutSeconds: 1}, AllowedVisualizations: []string{"stat"}, FreshnessSeconds: 1}
if _, err := Compile(definition, map[string]string{"instance": "server-1"}); err == nil {
t.Fatal("oversized query accepted")
}
}
func now() time.Time { return time.Date(2026, 8, 1, 12, 0, 0, 0, time.UTC) }