@@ -22,111 +22,113 @@ var twoHealthyShards = []map[uint32]bool{
2222 {0 : true , 1 : true },
2323}
2424
25- func TestPlugin_OutcomeWithMultiNodeObservations (t * testing.T ) {
26- lggr := logger .Test (t )
27- store := NewStore ()
28- store .SetAllShardHealth (map [uint32 ]bool {0 : true , 1 : true , 2 : true })
25+ func TestPlugin_Outcome (t * testing.T ) {
26+ t .Run ("WithMultiNodeObservations" , func (t * testing.T ) {
27+ lggr := logger .Test (t )
28+ store := NewStore ()
29+ store .SetAllShardHealth (map [uint32 ]bool {0 : true , 1 : true , 2 : true })
30+
31+ config := ocr3types.ReportingPluginConfig {
32+ N : 4 , F : 1 ,
33+ OffchainConfig : []byte {},
34+ MaxDurationObservation : 0 ,
35+ MaxDurationShouldAcceptAttestedReport : 0 ,
36+ MaxDurationShouldTransmitAcceptedReport : 0 ,
37+ }
2938
30- config := ocr3types.ReportingPluginConfig {
31- N : 4 , F : 1 ,
32- OffchainConfig : []byte {},
33- MaxDurationObservation : 0 ,
34- MaxDurationShouldAcceptAttestedReport : 0 ,
35- MaxDurationShouldTransmitAcceptedReport : 0 ,
36- }
39+ plugin , err := NewPlugin (store , config , lggr , nil )
40+ require .NoError (t , err )
3741
38- plugin , err := NewPlugin (store , config , lggr , nil )
39- require .NoError (t , err )
42+ ctx := t .Context ()
43+ intialSeqNr := uint64 (42 )
44+ outcomeCtx := ocr3types.OutcomeContext {SeqNr : intialSeqNr }
45+
46+ // Observations from 4 NOPs reporting health and workflows
47+ observations := []struct {
48+ name string
49+ shardHealth map [uint32 ]bool
50+ workflows []string
51+ }{
52+ {
53+ name : "NOP 0" ,
54+ shardHealth : map [uint32 ]bool {0 : true , 1 : true , 2 : true },
55+ workflows : []string {"wf-A" , "wf-B" , "wf-C" },
56+ },
57+ {
58+ name : "NOP 1" ,
59+ shardHealth : map [uint32 ]bool {0 : true , 1 : true , 2 : true },
60+ workflows : []string {"wf-B" , "wf-C" , "wf-D" },
61+ },
62+ {
63+ name : "NOP 2" ,
64+ shardHealth : map [uint32 ]bool {0 : true , 1 : true , 2 : false }, // shard 2 unhealthy
65+ workflows : []string {"wf-A" , "wf-C" },
66+ },
67+ {
68+ name : "NOP 3" ,
69+ shardHealth : map [uint32 ]bool {0 : true , 1 : true , 2 : true },
70+ workflows : []string {"wf-A" , "wf-B" , "wf-D" },
71+ },
72+ }
4073
41- ctx := t .Context ()
42- intialSeqNr := uint64 (42 )
43- outcomeCtx := ocr3types.OutcomeContext {SeqNr : intialSeqNr }
44-
45- // Observations from 4 NOPs reporting health and workflows
46- observations := []struct {
47- name string
48- shardHealth map [uint32 ]bool
49- workflows []string
50- }{
51- {
52- name : "NOP 0" ,
53- shardHealth : map [uint32 ]bool {0 : true , 1 : true , 2 : true },
54- workflows : []string {"wf-A" , "wf-B" , "wf-C" },
55- },
56- {
57- name : "NOP 1" ,
58- shardHealth : map [uint32 ]bool {0 : true , 1 : true , 2 : true },
59- workflows : []string {"wf-B" , "wf-C" , "wf-D" },
60- },
61- {
62- name : "NOP 2" ,
63- shardHealth : map [uint32 ]bool {0 : true , 1 : true , 2 : false }, // shard 2 unhealthy
64- workflows : []string {"wf-A" , "wf-C" },
65- },
66- {
67- name : "NOP 3" ,
68- shardHealth : map [uint32 ]bool {0 : true , 1 : true , 2 : true },
69- workflows : []string {"wf-A" , "wf-B" , "wf-D" },
70- },
71- }
74+ // Build attributed observations
75+ aos := make ([]types.AttributedObservation , 0 )
76+ for _ , obs := range observations {
77+ pbObs := & pb.Observation {
78+ ShardHealthStatus : obs .shardHealth ,
79+ WorkflowIds : obs .workflows ,
80+ Now : timestamppb .Now (),
81+ }
82+ rawObs , err := proto .Marshal (pbObs )
83+ require .NoError (t , err )
7284
73- // Build attributed observations
74- aos := make ([]types.AttributedObservation , 0 )
75- for _ , obs := range observations {
76- pbObs := & pb.Observation {
77- ShardHealthStatus : obs .shardHealth ,
78- WorkflowIds : obs .workflows ,
79- Now : timestamppb .Now (),
85+ aos = append (aos , types.AttributedObservation {
86+ Observation : rawObs ,
87+ Observer : commontypes .OracleID (len (aos )),
88+ })
8089 }
81- rawObs , err := proto .Marshal (pbObs )
82- require .NoError (t , err )
8390
84- aos = append (aos , types.AttributedObservation {
85- Observation : rawObs ,
86- Observer : commontypes .OracleID (len (aos )),
87- })
88- }
89-
90- // Execute Outcome phase
91- outcome , err := plugin .Outcome (ctx , outcomeCtx , nil , aos )
92- require .NoError (t , err )
93- require .NotNil (t , outcome )
91+ // Execute Outcome phase
92+ outcome , err := plugin .Outcome (ctx , outcomeCtx , nil , aos )
93+ require .NoError (t , err )
94+ require .NotNil (t , outcome )
9495
95- // Verify outcome
96- outcomeProto := & pb.Outcome {}
97- err = proto .Unmarshal (outcome , outcomeProto )
98- require .NoError (t , err )
96+ // Verify outcome
97+ outcomeProto := & pb.Outcome {}
98+ err = proto .Unmarshal (outcome , outcomeProto )
99+ require .NoError (t , err )
99100
100- // Check consensus results
101- require .NotNil (t , outcomeProto .State )
102- require .Equal (t , intialSeqNr + 1 , outcomeProto .State .Id , "ID should match SeqNr" )
103- t .Logf ("Outcome - ID: %d, HealthyShards: %v" , outcomeProto .State .Id , outcomeProto .State .GetRoutableShards ())
104- t .Logf ("Workflows assigned: %d" , len (outcomeProto .Routes ))
105-
106- // Verify all workflows are assigned
107- expectedWorkflows := map [string ]bool {"wf-A" : true , "wf-B" : true , "wf-C" : true , "wf-D" : true }
108- require .Equal (t , len (expectedWorkflows ), len (outcomeProto .Routes ))
109- for wf := range expectedWorkflows {
110- route , exists := outcomeProto .Routes [wf ]
111- require .True (t , exists , "workflow %s should be assigned" , wf )
112- require .True (t , route .Shard <= 2 , "shard should be healthy (0-2)" )
113- t .Logf (" %s → shard %d" , wf , route .Shard )
114- }
101+ // Check consensus results
102+ require .NotNil (t , outcomeProto .State )
103+ require .Equal (t , intialSeqNr + 1 , outcomeProto .State .Id , "ID should match SeqNr" )
104+ t .Logf ("Outcome - ID: %d, HealthyShards: %v" , outcomeProto .State .Id , outcomeProto .State .GetRoutableShards ())
105+ t .Logf ("Workflows assigned: %d" , len (outcomeProto .Routes ))
106+
107+ // Verify all workflows are assigned
108+ expectedWorkflows := map [string ]bool {"wf-A" : true , "wf-B" : true , "wf-C" : true , "wf-D" : true }
109+ require .Equal (t , len (expectedWorkflows ), len (outcomeProto .Routes ))
110+ for wf := range expectedWorkflows {
111+ route , exists := outcomeProto .Routes [wf ]
112+ require .True (t , exists , "workflow %s should be assigned" , wf )
113+ require .True (t , route .Shard <= 2 , "shard should be healthy (0-2)" )
114+ t .Logf (" %s → shard %d" , wf , route .Shard )
115+ }
115116
116- // Verify determinism: run again, should get same assignments
117- outcome2 , err := plugin .Outcome (ctx , outcomeCtx , nil , aos )
118- require .NoError (t , err )
117+ // Verify determinism: run again, should get same assignments
118+ outcome2 , err := plugin .Outcome (ctx , outcomeCtx , nil , aos )
119+ require .NoError (t , err )
119120
120- outcomeProto2 := & pb.Outcome {}
121- err = proto .Unmarshal (outcome2 , outcomeProto2 )
122- require .NoError (t , err )
121+ outcomeProto2 := & pb.Outcome {}
122+ err = proto .Unmarshal (outcome2 , outcomeProto2 )
123+ require .NoError (t , err )
123124
124- // Same workflows → same shards
125- for wf , route1 := range outcomeProto .Routes {
126- route2 , exists := outcomeProto2 .Routes [wf ]
127- require .True (t , exists )
128- require .Equal (t , route1 .Shard , route2 .Shard , "workflow %s should assign to same shard" , wf )
129- }
125+ // Same workflows → same shards
126+ for wf , route1 := range outcomeProto .Routes {
127+ route2 , exists := outcomeProto2 .Routes [wf ]
128+ require .True (t , exists )
129+ require .Equal (t , route1 .Shard , route2 .Shard , "workflow %s should assign to same shard" , wf )
130+ }
131+ })
130132}
131133
132134func TestPlugin_StateTransitions (t * testing.T ) {
0 commit comments