Files
Sebastian Spaink 3bf93d9796 config: migrate server.metrics and metrics_export validation to Rego (#8900)
Following #8891, which moved top-level config validation to an embedded
Rego policy, this migrates the `server/metrics` and `metrics_export`
configs onto Rego as well. Plugins register their recognized options via
`config.RegisterConfigSpec` (derived from their struct fields) so
unknown-option warnings live with each struct that brings the config.

The goal is migrate more `validateAndInjectDefaults` in follow up PRs,
this setups the foundation for other migrations to follow.

---------

Signed-off-by: Sebastian Spaink <sebastianspaink@gmail.com>
2026-07-17 08:43:48 -05:00

77 lines
1.9 KiB
Go

// Copyright 2026 The OPA Authors. All rights reserved.
// Use of this source code is governed by an Apache2
// license that can be found in the LICENSE file.
package config
import (
"slices"
"strings"
"testing"
)
func TestSpecsFromStruct(t *testing.T) {
type leaf struct {
Buckets []float64 `json:"buckets,omitempty"`
}
type mid struct {
Leaf *leaf `json:"leaf,omitempty"`
Name string `json:"name"`
hidden string //nolint:unused // unexported: must be ignored
Skip string `json:"-"`
}
type namedEntry struct {
Value string `json:"value"`
}
type embedded struct {
Shared string `json:"shared"`
}
type root struct {
embedded
Mid mid `json:"mid"`
Entries map[string]*namedEntry `json:"entries,omitempty"`
List []namedEntry `json:"list,omitempty"`
Raw string `json:"raw,omitempty"`
}
specs := SpecsFromStruct[root]("top")
// Collect into a lookup keyed by the dotted pattern for easy assertion.
got := map[string][]string{}
for _, s := range specs {
got[strings.Join(s.Pattern, ".")] = s.Keys
}
want := map[string][]string{
"top": {"shared", "mid", "entries", "list", "raw"}, // embedded flattened, "-" skipped, unexported skipped
"top.mid": {"leaf", "name"},
"top.mid.leaf": {"buckets"},
"top.entries.*": {"value"},
"top.list.*": {"value"},
}
if len(got) != len(want) {
t.Fatalf("expected %d specs, got %d: %v", len(want), len(got), got)
}
for pat, wantKeys := range want {
gotKeys, ok := got[pat]
if !ok {
t.Errorf("missing spec for pattern %q", pat)
continue
}
if !equalUnordered(gotKeys, wantKeys) {
t.Errorf("pattern %q: want keys %v, got %v", pat, wantKeys, gotKeys)
}
}
}
func equalUnordered(a, b []string) bool {
if len(a) != len(b) {
return false
}
ac, bc := slices.Clone(a), slices.Clone(b)
slices.Sort(ac)
slices.Sort(bc)
return slices.Equal(ac, bc)
}