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
}