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) }