go.temporal.io/server/chasm/visibility_test.go

352 LOC · 0 covered · 352 uncovered · 0 ranges · 0 concepts · 0 introducers · 0 tests

1 package chasm
2
3 import (
4 "testing"
5
6 "github.com/stretchr/testify/require"
7 "github.com/stretchr/testify/suite"
8 commonpb "go.temporal.io/api/common/v1"
9 persistencespb "go.temporal.io/server/api/persistence/v1"
10 "go.temporal.io/server/common/log"
11 "go.temporal.io/server/common/payload"
12 "go.temporal.io/server/common/testing/protorequire"
13 )
14
15 type (
16 visibilitySuite struct {
17 suite.Suite
18 *require.Assertions
19
20 mockContext *MockContext
21 mockMutableContext *MockMutableContext
22
23 registry *Registry
24
25 visibility *Visibility
26 }
27 )
28
29 func TestVisibilitySuite(t *testing.T) {
30 suite.Run(t, new(visibilitySuite))
31 }
32
33 func (s *visibilitySuite) SetupTest() {
34 s.initAssertions()
35 s.mockContext = &MockContext{}
36 s.mockMutableContext = &MockMutableContext{}
37
38 s.registry = NewRegistry(log.NewTestLogger())
39 err := s.registry.Register(&CoreLibrary{})
40 s.NoError(err)
41
42 s.visibility = NewVisibility(s.mockMutableContext)
43 s.Len(s.mockMutableContext.Tasks, 1)
44 s.mockMutableContext.Tasks = nil // Clear tasks added during creation
45 }
46
47 func (s *visibilitySuite) SetupSubTest() {
48 s.initAssertions()
49 }
50
51 func (s *visibilitySuite) initAssertions() {
52 s.Assertions = require.New(s.T())
53 }
54
55 func (s *visibilitySuite) TestComponentFqType() {
56 rc, ok := s.registry.ComponentFor(&Visibility{})
57 s.True(ok)
58 s.Equal(visibilityComponentType, rc.FqType())
59 }
60
61 func (s *visibilitySuite) TestTaskFqType() {
62 rc, ok := s.registry.TaskFor(&persistencespb.ChasmVisibilityTaskData{})
63 s.True(ok)
64 s.Equal(visibilityTaskType, rc.FqType())
65 }
66
67 func (s *visibilitySuite) TestLifeCycleState() {
68 s.Equal(LifecycleStateRunning, s.visibility.LifecycleState(s.mockMutableContext))
69 }
70
71 func (s *visibilitySuite) TestMergeCustomSearchAttributes() {
72 sa := s.visibility.CustomSearchAttributes(s.mockMutableContext)
73 s.Empty(sa)
74
75 stringKey, stringVal := "stringKey", "stringValue"
76 intKey, intVal := "intKey", 42
77 floatKey, floatVal := "floatKey", 3.14
78
79 // Add SA via Visibility struct method.
80 s.visibility.MergeCustomSearchAttributes(
81 s.mockMutableContext,
82 map[string]*commonpb.Payload{
83 stringKey: s.mustEncode(stringVal),
84 intKey: s.mustEncode(intVal),
85 floatKey: s.mustEncode(floatVal),
86 },
87 )
88 s.Len(s.mockMutableContext.Tasks, 1)
89 s.assertTaskPayload(2, s.mockMutableContext.Tasks[0].Payload)
90
91 sa = s.visibility.CustomSearchAttributes(s.mockMutableContext)
92 s.Len(sa, 3)
93
94 var actualStringVal string
95 err := payload.Decode(sa[stringKey], &actualStringVal)
96 s.NoError(err)
97 s.Equal(stringVal, actualStringVal)
98
99 var actualIntVal int
100 err = payload.Decode(sa[intKey], &actualIntVal)
101 s.NoError(err)
102 s.Equal(intVal, actualIntVal)
103
104 var actualFloatVal float64
105 err = payload.Decode(sa[floatKey], &actualFloatVal)
106 s.NoError(err)
107 s.Equal(floatVal, actualFloatVal)
108
109 // Test remove search attributes by setting payload to nil.
110 s.visibility.MergeCustomSearchAttributes(s.mockMutableContext, map[string]*commonpb.Payload{
111 intKey: s.mustEncode(intVal),
112 floatKey: nil,
113 })
114 s.NoError(err)
115 s.Len(s.mockMutableContext.Tasks, 2)
116 s.assertTaskPayload(3, s.mockMutableContext.Tasks[1].Payload)
117
118 sa = s.visibility.CustomSearchAttributes(s.mockMutableContext)
119 s.NoError(err)
120 s.Len(sa, 2, "intKey and stringKey should remain")
121
122 // Test removing all search attributes also removes the node.
123 s.visibility.MergeCustomSearchAttributes(s.mockMutableContext, map[string]*commonpb.Payload{
124 stringKey: nil,
125 intKey: nil,
126 })
127 s.Len(s.mockMutableContext.Tasks, 3)
128 s.assertTaskPayload(4, s.mockMutableContext.Tasks[2].Payload)
129 _, ok := s.visibility.SA.TryGet(s.mockContext)
130 s.False(ok)
131 s.Nil(s.visibility.CustomSearchAttributes(s.mockContext))
132 }
133
134 func (s *visibilitySuite) TestNewVisibilityWithData_FilterNilSearchAttributes() {
135 stringKey, stringVal := "stringKey", "stringValue"
136 // SA with 1 valid and 2 nil values - nil values should be filtered out
137 customSearchAttributes := map[string]*commonpb.Payload{
138 stringKey: s.mustEncode(stringVal),
139 "nilKey1": nil,
140 "nilKey2": nil,
141 }
142 // Memo with 1 valid and 2 nil values - nil values should be filtered out
143 customMemo := map[string]*commonpb.Payload{
144 stringKey: s.mustEncode(stringVal),
145 "nilKey1": nil,
146 "nilKey2": nil,
147 }
148 visibility := NewVisibilityWithData(s.mockMutableContext, customSearchAttributes, customMemo)
149 // SA should have only 1 field (nil values filtered out)
150 s.Len(visibility.SA.Get(s.mockContext).IndexedFields, 1)
151 s.NotNil(visibility.SA.Get(s.mockContext).IndexedFields[stringKey])
152 // Memo should have only 1 field (nil values filtered out)
153 s.Len(visibility.Memo.Get(s.mockContext).Fields, 1)
154 s.NotNil(visibility.Memo.Get(s.mockContext).Fields[stringKey])
155 }
156
157 func (s *visibilitySuite) TestReplaceCustomSearchAttributes() {
158 stringKey, stringVal := "stringKey", "stringValue"
159 intKey, intVal := "intKey", 42
160 floatKey, floatVal := "floatKey", 3.14
161 byteKey, byteVal := "byteKey", []byte{0x01, 0x02, 0x03}
162
163 // Set up some initial SA.
164 s.visibility.ReplaceCustomSearchAttributes(
165 s.mockMutableContext,
166 map[string]*commonpb.Payload{
167 stringKey: s.mustEncode(stringVal),
168 intKey: s.mustEncode(intVal),
169 floatKey: s.mustEncode(floatVal),
170 },
171 )
172 s.Len(s.mockMutableContext.Tasks, 1)
173 s.assertTaskPayload(2, s.mockMutableContext.Tasks[0].Payload)
174
175 sa := s.visibility.CustomSearchAttributes(s.mockMutableContext)
176 s.Len(sa, 3)
177
178 // Set to a new set of SA, non-existing keys should be removed.
179 s.visibility.ReplaceCustomSearchAttributes(
180 s.mockMutableContext,
181 map[string]*commonpb.Payload{
182 floatKey: s.mustEncode(floatVal),
183 byteKey: s.mustEncode(byteVal),
184 },
185 )
186 s.Len(s.mockMutableContext.Tasks, 2)
187 s.assertTaskPayload(3, s.mockMutableContext.Tasks[1].Payload)
188
189 sa = s.visibility.CustomSearchAttributes(s.mockMutableContext)
190 s.Len(sa, 2)
191
192 // Setting to an empty map should remove the node.
193 s.visibility.ReplaceCustomSearchAttributes(
194 s.mockMutableContext,
195 map[string]*commonpb.Payload{},
196 )
197 s.Len(s.mockMutableContext.Tasks, 3)
198 s.assertTaskPayload(4, s.mockMutableContext.Tasks[2].Payload)
199 _, ok := s.visibility.SA.TryGet(s.mockContext)
200 s.False(ok)
201 s.Nil(s.visibility.CustomSearchAttributes(s.mockContext))
202
203 // Test that nil values are filtered out during replace.
204 s.visibility.ReplaceCustomSearchAttributes(
205 s.mockMutableContext,
206 map[string]*commonpb.Payload{
207 stringKey: s.mustEncode(stringVal),
208 intKey: nil, // Should be filtered out
209 },
210 )
211 s.Len(s.mockMutableContext.Tasks, 4)
212 s.assertTaskPayload(5, s.mockMutableContext.Tasks[3].Payload)
213
214 sa = s.visibility.CustomSearchAttributes(s.mockMutableContext)
215 s.Len(sa, 1, "nil values should be filtered out")
216 s.NotNil(sa[stringKey])
217 s.Nil(sa[intKey])
218
219 // Test that replacing with all nil values removes the node.
220 s.visibility.ReplaceCustomSearchAttributes(
221 s.mockMutableContext,
222 map[string]*commonpb.Payload{
223 stringKey: nil,
224 intKey: nil,
225 },
226 )
227 s.Len(s.mockMutableContext.Tasks, 5)
228 s.assertTaskPayload(6, s.mockMutableContext.Tasks[4].Payload)
229 _, ok = s.visibility.SA.TryGet(s.mockContext)
230 s.False(ok)
231 s.Nil(s.visibility.CustomSearchAttributes(s.mockContext))
232 }
233
234 func (s *visibilitySuite) TestMergeCustomMemo() {
235 memo := s.visibility.CustomMemo(s.mockMutableContext)
236 s.Empty(memo)
237
238 stringKey, stringVal := "stringKey", "stringValue"
239 intKey, intVal := "intKey", 42
240 floatKey, floatVal := "floatKey", 3.14
241
242 // Add memo via Visibility struct method.
243 s.visibility.MergeCustomMemo(s.mockMutableContext, map[string]*commonpb.Payload{
244 stringKey: s.mustEncode(stringVal),
245 intKey: s.mustEncode(intVal),
246 floatKey: s.mustEncode(floatVal),
247 })
248 s.Len(s.mockMutableContext.Tasks, 1)
249 s.assertTaskPayload(2, s.mockMutableContext.Tasks[0].Payload)
250
251 memo = s.visibility.CustomMemo(s.mockMutableContext)
252 s.Len(memo, 3)
253
254 var actualStringVal string
255 err := payload.Decode(memo[stringKey], &actualStringVal)
256 s.NoError(err)
257 s.Equal(stringVal, actualStringVal)
258
259 var actualIntVal int
260 err = payload.Decode(memo[intKey], &actualIntVal)
261 s.NoError(err)
262 s.Equal(intVal, actualIntVal)
263
264 var actualFloatVal float64
265 err = payload.Decode(memo[floatKey], &actualFloatVal)
266 s.NoError(err)
267 s.Equal(floatVal, actualFloatVal)
268
269 // Test remove memo by setting payload to nil.
270 s.visibility.MergeCustomMemo(s.mockMutableContext, map[string]*commonpb.Payload{
271 intKey: s.mustEncode(intVal),
272 floatKey: nil,
273 })
274 s.Len(s.mockMutableContext.Tasks, 2)
275 s.assertTaskPayload(3, s.mockMutableContext.Tasks[1].Payload)
276
277 memo = s.visibility.CustomMemo(s.mockMutableContext)
278 s.Len(memo, 2, "intKey and stringKey should remain")
279
280 // Test removing all memo fields also removes the node.
281 s.visibility.MergeCustomMemo(s.mockMutableContext, map[string]*commonpb.Payload{
282 stringKey: nil,
283 intKey: nil,
284 })
285 s.Len(s.mockMutableContext.Tasks, 3)
286 s.assertTaskPayload(4, s.mockMutableContext.Tasks[2].Payload)
287 _, ok := s.visibility.Memo.TryGet(s.mockContext)
288 s.False(ok)
289 s.Nil(s.visibility.CustomMemo(s.mockContext))
290 }
291
292 func (s *visibilitySuite) TestReplaceCustomMemo() {
293 stringKey, stringVal := "stringKey", "stringValue"
294 intKey, intVal := "intKey", 42
295 floatKey, floatVal := "floatKey", 3.14
296 byteKey, byteVal := "byteKey", []byte{0x01, 0x02, 0x03}
297
298 // Set up some initial memo fields.
299 s.visibility.ReplaceCustomMemo(
300 s.mockMutableContext,
301 map[string]*commonpb.Payload{
302 stringKey: s.mustEncode(stringVal),
303 intKey: s.mustEncode(intVal),
304 floatKey: s.mustEncode(floatVal),
305 },
306 )
307 s.Len(s.mockMutableContext.Tasks, 1)
308 s.assertTaskPayload(2, s.mockMutableContext.Tasks[0].Payload)
309
310 memo := s.visibility.CustomMemo(s.mockMutableContext)
311 s.Len(memo, 3)
312
313 // Set to a new set of memo fields, non-existing keys should be removed.
314 s.visibility.ReplaceCustomMemo(
315 s.mockMutableContext,
316 map[string]*commonpb.Payload{
317 floatKey: s.mustEncode(floatVal),
318 byteKey: s.mustEncode(byteVal),
319 stringKey: nil, // nil value must be filtered out
320 },
321 )
322 s.Len(s.mockMutableContext.Tasks, 2)
323 s.assertTaskPayload(3, s.mockMutableContext.Tasks[1].Payload)
324
325 memo = s.visibility.CustomMemo(s.mockMutableContext)
326 s.Len(memo, 2)
327
328 // Setting to an empty map should remove the node.
329 s.visibility.ReplaceCustomMemo(
330 s.mockMutableContext,
331 map[string]*commonpb.Payload{},
332 )
333 s.Len(s.mockMutableContext.Tasks, 3)
334 s.assertTaskPayload(4, s.mockMutableContext.Tasks[2].Payload)
335 _, ok := s.visibility.Memo.TryGet(s.mockContext)
336 s.False(ok)
337 s.Nil(s.visibility.CustomMemo(s.mockContext))
338 }
339
340 func (s *visibilitySuite) assertTaskPayload(expectedCount int64, taskPayload any) {
341 protorequire.ProtoEqual(
342 s.T(),
343 &persistencespb.ChasmVisibilityTaskData{TransitionCount: expectedCount},
344 taskPayload.(*persistencespb.ChasmVisibilityTaskData),
345 )
346 }
347
348 func (s *visibilitySuite) mustEncode(v any) *commonpb.Payload {
349 p, err := payload.Encode(v)
350 s.NoError(err)
351 return p
352 }