11package ring
22
33import (
4+ "context"
45 "testing"
6+ "time"
57
8+ "github.com/smartcontractkit/chainlink-common/pkg/workflows/ring/pb"
69 "github.com/stretchr/testify/require"
710)
811
@@ -17,10 +20,15 @@ func TestStore_DeterministicHashing(t *testing.T) {
1720 2 : true ,
1821 })
1922
23+ ctx := context .Background ()
24+
2025 // Test determinism: same workflow always gets same shard
21- shard1 := store .GetShardForWorkflow ("workflow-123" )
22- shard2 := store .GetShardForWorkflow ("workflow-123" )
23- shard3 := store .GetShardForWorkflow ("workflow-123" )
26+ shard1 , err := store .GetShardForWorkflow (ctx , "workflow-123" )
27+ require .NoError (t , err )
28+ shard2 , err := store .GetShardForWorkflow (ctx , "workflow-123" )
29+ require .NoError (t , err )
30+ shard3 , err := store .GetShardForWorkflow (ctx , "workflow-123" )
31+ require .NoError (t , err )
2432
2533 require .Equal (t , shard1 , shard2 , "Same workflow should get same shard (call 2)" )
2634 require .Equal (t , shard2 , shard3 , "Same workflow should get same shard (call 3)" )
@@ -39,12 +47,17 @@ func TestStore_ConsistentRingConsistency(t *testing.T) {
3947 store2 .SetAllShardHealth (healthyShards )
4048 store3 .SetAllShardHealth (healthyShards )
4149
50+ ctx := context .Background ()
51+
4252 // All compute same assignments
4353 workflows := []string {"workflow-A" , "workflow-B" , "workflow-C" , "workflow-D" }
4454 for _ , wf := range workflows {
45- s1 := store1 .GetShardForWorkflow (wf )
46- s2 := store2 .GetShardForWorkflow (wf )
47- s3 := store3 .GetShardForWorkflow (wf )
55+ s1 , err := store1 .GetShardForWorkflow (ctx , wf )
56+ require .NoError (t , err )
57+ s2 , err := store2 .GetShardForWorkflow (ctx , wf )
58+ require .NoError (t , err )
59+ s3 , err := store3 .GetShardForWorkflow (ctx , wf )
60+ require .NoError (t , err )
4861
4962 require .Equal (t , s1 , s2 , "All nodes should agree on %s assignment" , wf )
5063 require .Equal (t , s2 , s3 , "All nodes should agree on %s assignment" , wf )
@@ -54,21 +67,26 @@ func TestStore_ConsistentRingConsistency(t *testing.T) {
5467// TestStore_Rebalancing verifies rebalancing when shard health changes
5568func TestStore_Rebalancing (t * testing.T ) {
5669 store := NewStore ()
70+ ctx := context .Background ()
5771
5872 // Start with 3 healthy shards
5973 store .SetAllShardHealth (map [uint32 ]bool {0 : true , 1 : true , 2 : true })
6074 assignments1 := make (map [string ]uint32 )
6175 for i := 1 ; i <= 10 ; i ++ {
6276 wfID := "workflow-" + string (rune (i ))
63- assignments1 [wfID ] = store .GetShardForWorkflow (wfID )
77+ shard , err := store .GetShardForWorkflow (ctx , wfID )
78+ require .NoError (t , err )
79+ assignments1 [wfID ] = shard
6480 }
6581
6682 // Shard 1 fails
6783 store .SetShardHealth (1 , false )
6884 assignments2 := make (map [string ]uint32 )
6985 for i := 1 ; i <= 10 ; i ++ {
7086 wfID := "workflow-" + string (rune (i ))
71- assignments2 [wfID ] = store .GetShardForWorkflow (wfID )
87+ shard , err := store .GetShardForWorkflow (ctx , wfID )
88+ require .NoError (t , err )
89+ assignments2 [wfID ] = shard
7290 }
7391
7492 // Check that rebalancing occurred (some workflows moved)
@@ -95,20 +113,19 @@ func TestStore_GetHealthyShards(t *testing.T) {
95113
96114// TestStore_NilHashRingFallback verifies fallback when hash ring is uninitialized
97115func TestStore_NilHashRingFallback (t * testing.T ) {
98- store := & Store {
99- routingState : make (map [string ]uint32 ),
100- shardHealth : make (map [uint32 ]bool ),
101- healthyShards : make ([]uint32 , 0 ),
102- }
116+ store := NewStore ()
117+ ctx := context .Background ()
103118
104- // Should not panic, should return 0 as fallback
105- shard := store .GetShardForWorkflow ("workflow-123" )
119+ // Should not panic, should return 0 as fallback (no healthy shards set)
120+ shard , err := store .GetShardForWorkflow (ctx , "workflow-123" )
121+ require .NoError (t , err )
106122 require .Equal (t , uint32 (0 ), shard )
107123}
108124
109125// TestStore_DistributionAcrossShards verifies that workflows are distributed across shards
110126func TestStore_DistributionAcrossShards (t * testing.T ) {
111127 store := NewStore ()
128+ ctx := context .Background ()
112129
113130 store .SetAllShardHealth (map [uint32 ]bool {
114131 0 : true ,
@@ -120,7 +137,8 @@ func TestStore_DistributionAcrossShards(t *testing.T) {
120137 distribution := make (map [uint32 ]int )
121138 for i := 0 ; i < 100 ; i ++ {
122139 wfID := "workflow-" + string (rune (i ))
123- shard := store .GetShardForWorkflow (wfID )
140+ shard , err := store .GetShardForWorkflow (ctx , wfID )
141+ require .NoError (t , err )
124142 distribution [shard ]++
125143 }
126144
@@ -137,3 +155,45 @@ func sum(distribution map[uint32]int) int {
137155 }
138156 return total
139157}
158+
159+ // TestStore_PendingAllocsDuringTransition verifies that allocation requests block during transition
160+ // and are fulfilled when SetShardForWorkflow is called
161+ func TestStore_PendingAllocsDuringTransition (t * testing.T ) {
162+ store := NewStore ()
163+ store .SetAllShardHealth (map [uint32 ]bool {0 : true , 1 : true })
164+
165+ // Put store in transition state
166+ store .SetRoutingState (& pb.RoutingState {
167+ State : & pb.RoutingState_Transition {
168+ Transition : & pb.Transition {WantShards : 3 },
169+ },
170+ })
171+
172+ ctx , cancel := context .WithTimeout (context .Background (), 100 * time .Millisecond )
173+ defer cancel ()
174+
175+ // Start a goroutine that requests allocation (will block)
176+ resultCh := make (chan uint32 )
177+ go func () {
178+ shard , _ := store .GetShardForWorkflow (ctx , "workflow-X" )
179+ resultCh <- shard
180+ }()
181+
182+ // Give goroutine time to enqueue request
183+ time .Sleep (10 * time .Millisecond )
184+
185+ // Verify request is pending
186+ pending := store .GetPendingAllocations ()
187+ require .Contains (t , pending , "workflow-X" )
188+
189+ // Fulfill the allocation (simulates transmitter receiving OCR outcome)
190+ store .SetShardForWorkflow ("workflow-X" , 2 )
191+
192+ // Blocked goroutine should now receive result
193+ select {
194+ case shard := <- resultCh :
195+ require .Equal (t , uint32 (2 ), shard )
196+ case <- time .After (50 * time .Millisecond ):
197+ t .Fatal ("allocation was not fulfilled" )
198+ }
199+ }
0 commit comments