go.temporal.io/server/chasm/ref_test.go

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

1 package chasm
2
3 import (
4 "math/rand"
5 "reflect"
6 "testing"
7
8 "github.com/stretchr/testify/require"
9 "github.com/stretchr/testify/suite"
10 persistencespb "go.temporal.io/server/api/persistence/v1"
11 "go.temporal.io/server/common/log"
12 "go.temporal.io/server/common/primitives"
13 "go.temporal.io/server/common/testing/protorequire"
14 "go.uber.org/mock/gomock"
15 )
16
17 type componentRefSuite struct {
18 suite.Suite
19 *require.Assertions
20 protorequire.ProtoAssertions
21
22 controller *gomock.Controller
23
24 registry *Registry
25 }
26
27 func TestComponentRefSuite(t *testing.T) {
28 suite.Run(t, new(componentRefSuite))
29 }
30
31 func (s *componentRefSuite) SetupTest() {
32 // Do this in SetupSubTest() as well, if we have sub tests in this suite.
33 s.Assertions = require.New(s.T())
34 s.ProtoAssertions = protorequire.New(s.T())
35
36 s.controller = gomock.NewController(s.T())
37
38 s.registry = NewRegistry(log.NewTestLogger())
39 err := s.registry.Register(newTestLibrary(s.controller))
40 s.NoError(err)
41 }
42
43 func (s *componentRefSuite) TestArchetypeID() {
44 executionKey := ExecutionKey{
45 NamespaceID: primitives.NewUUID().String(),
46 BusinessID: primitives.NewUUID().String(),
47 RunID: primitives.NewUUID().String(),
48 }
49 ref := NewComponentRef[*TestComponent](executionKey)
50
51 archetypeID, err := ref.ArchetypeID(s.registry)
52 s.NoError(err)
53
54 rc, ok := s.registry.componentOf(reflect.TypeFor[*TestComponent]())
55 s.True(ok)
56
57 s.Equal(rc.componentID, archetypeID)
58 }
59
60 func (s *componentRefSuite) TestNewComponentRefByArchetypeID() {
61 executionKey := ExecutionKey{
62 NamespaceID: primitives.NewUUID().String(),
63 BusinessID: primitives.NewUUID().String(),
64 RunID: primitives.NewUUID().String(),
65 }
66 expectArchetypeID := WorkflowArchetypeID
67 ref := NewComponentRefByArchetypeID(executionKey, expectArchetypeID)
68
69 s.Equal(executionKey, ref.ExecutionKey)
70
71 archetypeID, err := ref.ArchetypeID(s.registry)
72 s.NoError(err)
73 s.Equal(expectArchetypeID, archetypeID)
74 }
75
76 func (s *componentRefSuite) TestSerializeDeserialize() {
77 _, err := DeserializeComponentRef(nil)
78 s.ErrorIs(err, ErrInvalidComponentRef)
79 _, err = DeserializeComponentRef([]byte{})
80 s.ErrorIs(err, ErrInvalidComponentRef)
81
82 executionKey := ExecutionKey{
83 NamespaceID: primitives.NewUUID().String(),
84 BusinessID: primitives.NewUUID().String(),
85 RunID: primitives.NewUUID().String(),
86 }
87 ref := ComponentRef{
88 ExecutionKey: executionKey,
89 executionGoType: reflect.TypeFor[*TestComponent](),
90 executionLastUpdateVT: &persistencespb.VersionedTransition{
91 NamespaceFailoverVersion: rand.Int63(),
92 TransitionCount: rand.Int63(),
93 },
94 componentPath: []string{primitives.NewUUID().String(), primitives.NewUUID().String()},
95 componentInitialVT: &persistencespb.VersionedTransition{
96 NamespaceFailoverVersion: rand.Int63(),
97 TransitionCount: rand.Int63(),
98 },
99 }
100
101 serializedRef, err := ref.Serialize(s.registry)
102 s.NoError(err)
103
104 deserializedRef, err := DeserializeComponentRef(serializedRef)
105 s.NoError(err)
106
107 s.ProtoEqual(ref.executionLastUpdateVT, deserializedRef.executionLastUpdateVT)
108 s.ProtoEqual(ref.componentInitialVT, deserializedRef.componentInitialVT)
109
110 rootRc, ok := s.registry.ComponentFor(&TestComponent{})
111 s.True(ok)
112 s.Equal(rootRc.componentID, deserializedRef.archetypeID)
113
114 s.Equal(ref.ExecutionKey, deserializedRef.ExecutionKey)
115 s.Equal(ref.componentPath, deserializedRef.componentPath)
116 }
117
118 func (s *componentRefSuite) TestForConsistencyLevel() {
119 newRef := func() ComponentRef {
120 return ComponentRef{
121 ExecutionKey: ExecutionKey{
122 NamespaceID: primitives.NewUUID().String(),
123 BusinessID: primitives.NewUUID().String(),
124 RunID: primitives.NewUUID().String(),
125 },
126 archetypeID: WorkflowArchetypeID,
127 executionGoType: reflect.TypeFor[*TestComponent](),
128 executionLastUpdateVT: &persistencespb.VersionedTransition{
129 NamespaceFailoverVersion: rand.Int63(),
130 TransitionCount: rand.Int63(),
131 },
132 componentPath: []string{primitives.NewUUID().String()},
133 componentInitialVT: &persistencespb.VersionedTransition{
134 NamespaceFailoverVersion: rand.Int63(),
135 TransitionCount: rand.Int63(),
136 },
137 }
138 }
139
140 testCases := []struct {
141 name string
142 level RefConsistencyLevel
143 // verify asserts the level-specific expectations on the original (orig) and adjusted refs.
144 verify func(orig, adjusted ComponentRef)
145 }{
146 {
147 name: "ExecutionLastUpdate keeps everything",
148 level: RefConsistencyLevelExecutionLastUpdate,
149 verify: func(orig, adjusted ComponentRef) {
150 // Staleness keyed off the execution's last-update transition; nothing relaxed.
151 s.ProtoEqual(orig.executionLastUpdateVT, adjusted.executionLastUpdateVT)
152 s.ProtoEqual(orig.componentInitialVT, adjusted.componentInitialVT)
153 s.Equal(orig.RunID, adjusted.RunID)
154 },
155 },
156 {
157 name: "ComponentCreation keys staleness off the component creation VT",
158 level: RefConsistencyLevelComponentCreation,
159 verify: func(orig, adjusted ComponentRef) {
160 // The staleness check (Node.IsStale keys off executionLastUpdateVT) now uses the
161 // component's creation transition, so a behind mutable state is still reloaded.
162 s.ProtoEqual(orig.componentInitialVT, adjusted.executionLastUpdateVT)
163 // The creation transition is preserved for the component-identity check, run ID kept.
164 s.ProtoEqual(orig.componentInitialVT, adjusted.componentInitialVT)
165 s.Equal(orig.RunID, adjusted.RunID)
166 },
167 },
168 {
169 name: "CurrentRun drops both VTs and the run ID",
170 level: RefConsistencyLevelCurrentRun,
171 verify: func(orig, adjusted ComponentRef) {
172 s.Nil(adjusted.executionLastUpdateVT)
173 s.Nil(adjusted.componentInitialVT)
174 s.Empty(adjusted.RunID)
175 },
176 },
177 }
178
179 for _, tc := range testCases {
180 s.Run(tc.name, func() {
181 ref := newRef()
182 adjusted, err := ref.forConsistencyLevel(tc.level)
183 s.NoError(err)
184 tc.verify(ref, adjusted)
185
186 // Invariants for every level: path/archetype identity is preserved, and the receiver
187 // is never mutated (value semantics).
188 s.Equal(ref.componentPath, adjusted.componentPath)
189 s.Equal(ref.archetypeID, adjusted.archetypeID)
190 s.NotNil(ref.executionLastUpdateVT)
191 s.NotNil(ref.componentInitialVT)
192 s.NotEmpty(ref.RunID)
193 })
194 }
195 }