mirror of
https://github.com/open-policy-agent/opa.git
synced 2026-08-12 19:32:48 -06:00
3bf93d9796
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>
77 lines
1.9 KiB
Go
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)
|
|
}
|