master
go 203 lines 5.97 KB
Raw
1 // SPDX-License-Identifier: GPL-3.0-or-later
2
3 package metrix
4
5 import (
6 "testing"
7
8 "github.com/stretchr/testify/require"
9 )
10
11 func TestStateSetStoreScenarios(t *testing.T) {
12 tests := map[string]struct {
13 run func(t *testing.T)
14 }{
15 "snapshot stateset read and flatten": {
16 run: func(t *testing.T) {
17 s := NewCollectorStore()
18 cc := cycleController(t, s)
19 ss := s.Write().SnapshotMeter("svc").StateSet(
20 "mode",
21 WithStateSetStates("maintenance", "operational", "recovery"),
22 WithStateSetMode(ModeEnum),
23 )
24
25 cc.BeginCycle()
26 ss.Enable("operational")
27 cc.CommitCycleSuccess()
28
29 mustStateSet(t, s.Read(), "svc.mode", nil, map[string]bool{
30 "maintenance": false,
31 "operational": true,
32 "recovery": false,
33 })
34 _, ok := s.Read().Value("svc.mode", nil)
35 require.False(t, ok, "expected non-scalar stateset to be unavailable via Value")
36
37 fr := s.Read(ReadFlatten())
38 mustValue(t, fr, "svc.mode", Labels{"svc.mode": "maintenance"}, 0)
39 mustValue(t, fr, "svc.mode", Labels{"svc.mode": "operational"}, 1)
40 mustValue(t, fr, "svc.mode", Labels{"svc.mode": "recovery"}, 0)
41 },
42 },
43 "snapshot stateset freshness hides stale series from Read but not ReadRaw": {
44 run: func(t *testing.T) {
45 s := NewCollectorStore()
46 cc := cycleController(t, s)
47 ss := s.Write().SnapshotMeter("svc").StateSet(
48 "mode",
49 WithStateSetStates("maintenance", "operational"),
50 WithStateSetMode(ModeEnum),
51 )
52
53 cc.BeginCycle()
54 ss.Enable("operational")
55 cc.CommitCycleSuccess()
56
57 cc.BeginCycle()
58 cc.CommitCycleSuccess()
59
60 _, ok := s.Read().StateSet("svc.mode", nil)
61 require.False(t, ok, "expected stale snapshot stateset hidden from Read")
62 mustStateSet(t, s.Read(ReadRaw()), "svc.mode", nil, map[string]bool{
63 "maintenance": false,
64 "operational": true,
65 })
66
67 _, ok = s.Read(ReadFlatten()).Value("svc.mode", Labels{"svc.mode": "operational"})
68 require.False(t, ok, "expected stale snapshot stateset flattened series hidden from Read(ReadFlatten())")
69 mustValue(t, s.Read(ReadRaw(), ReadFlatten()), "svc.mode", Labels{"svc.mode": "operational"}, 1)
70 },
71 },
72 "stateful stateset remains visible across cycles without writes": {
73 run: func(t *testing.T) {
74 s := NewCollectorStore()
75 cc := cycleController(t, s)
76 ss := s.Write().StatefulMeter("svc").StateSet(
77 "feature_flags",
78 WithStateSetStates("a", "b", "c"),
79 )
80
81 cc.BeginCycle()
82 ss.ObserveStateSet(StateSetPoint{States: map[string]bool{"a": true, "b": true}})
83 cc.CommitCycleSuccess()
84
85 cc.BeginCycle()
86 cc.CommitCycleSuccess()
87
88 mustStateSet(t, s.Read(), "svc.feature_flags", nil, map[string]bool{
89 "a": true,
90 "b": true,
91 "c": false,
92 })
93 mustValue(t, s.Read(ReadFlatten()), "svc.feature_flags", Labels{"svc.feature_flags": "a"}, 1)
94 mustValue(t, s.Read(ReadFlatten()), "svc.feature_flags", Labels{"svc.feature_flags": "b"}, 1)
95 mustValue(t, s.Read(ReadFlatten()), "svc.feature_flags", Labels{"svc.feature_flags": "c"}, 0)
96 },
97 },
98 "stateset enum mode validation panics on invalid active count": {
99 run: func(t *testing.T) {
100 s := NewCollectorStore()
101 cc := cycleController(t, s)
102 ss := s.Write().SnapshotMeter("svc").StateSet(
103 "mode",
104 WithStateSetStates("maintenance", "operational"),
105 WithStateSetMode(ModeEnum),
106 )
107
108 cc.BeginCycle()
109 expectPanic(t, func() {
110 ss.Enable("maintenance", "operational")
111 })
112 cc.AbortCycle()
113 },
114 },
115 "stateset observe panics on undeclared state": {
116 run: func(t *testing.T) {
117 s := NewCollectorStore()
118 cc := cycleController(t, s)
119 ss := s.Write().SnapshotMeter("svc").StateSet(
120 "mode",
121 WithStateSetStates("maintenance", "operational"),
122 )
123
124 cc.BeginCycle()
125 expectPanic(t, func() {
126 ss.ObserveStateSet(StateSetPoint{States: map[string]bool{"unknown": true}})
127 })
128 cc.AbortCycle()
129 },
130 },
131 "stateset declaration requires WithStateSetStates": {
132 run: func(t *testing.T) {
133 s := NewCollectorStore()
134 expectPanic(t, func() {
135 _ = s.Write().SnapshotMeter("svc").StateSet("mode")
136 })
137 },
138 },
139 "stateset schema mismatch panics on redeclaration": {
140 run: func(t *testing.T) {
141 s := NewCollectorStore()
142 _ = s.Write().SnapshotMeter("svc").StateSet(
143 "mode",
144 WithStateSetStates("maintenance", "operational"),
145 )
146 expectPanic(t, func() {
147 _ = s.Write().SnapshotMeter("svc").StateSet(
148 "mode",
149 WithStateSetStates("maintenance", "recovery"),
150 )
151 })
152 },
153 },
154 "flatten label key collision panics": {
155 run: func(t *testing.T) {
156 s := NewCollectorStore()
157 cc := cycleController(t, s)
158 ss := s.Write().SnapshotMeter("svc").
159 WithLabels(Label{Key: "svc.mode", Value: "already-present"}).
160 StateSet("mode", WithStateSetStates("maintenance", "operational"))
161
162 cc.BeginCycle()
163 expectPanic(t, func() {
164 ss.Enable("maintenance")
165 })
166 cc.AbortCycle()
167 },
168 },
169 "stateset read returns a copy": {
170 run: func(t *testing.T) {
171 s := NewCollectorStore()
172 cc := cycleController(t, s)
173 ss := s.Write().SnapshotMeter("svc").StateSet("mode", WithStateSetStates("a", "b"))
174
175 cc.BeginCycle()
176 ss.ObserveStateSet(StateSetPoint{States: map[string]bool{"a": true}})
177 cc.CommitCycleSuccess()
178
179 p, ok := s.Read().StateSet("svc.mode", nil)
180 require.True(t, ok, "expected stateset point")
181 p.States["a"] = false
182
183 mustStateSet(t, s.Read(), "svc.mode", nil, map[string]bool{"a": true, "b": false})
184 },
185 },
186 }
187
188 for name, tc := range tests {
189 t.Run(name, tc.run)
190 }
191 }
192
193 func mustStateSet(t *testing.T, r Reader, name string, labels Labels, want map[string]bool) {
194 t.Helper()
195 got, ok := r.StateSet(name, labels)
196 require.True(t, ok, "expected stateset for %s", name)
197 require.Len(t, got.States, len(want), "unexpected stateset size for %s", name)
198 for k, w := range want {
199 g, present := got.States[k]
200 require.True(t, present, "missing stateset state %s for %s", k, name)
201 require.Equal(t, w, g, "unexpected stateset state %s for %s", k, name)
202 }
203 }