github.com/braveheart12/just@v0.8.7/ledger/artifactmanager/jettreeupdater_test.go (about) 1 /* 2 * Copyright 2019 Insolar Technologies 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package artifactmanager 18 19 import ( 20 "context" 21 "sync" 22 "testing" 23 24 "github.com/gojuno/minimock" 25 "github.com/insolar/insolar" 26 "github.com/insolar/insolar/gen" 27 "github.com/insolar/insolar/ledger/storage/nodes" 28 "github.com/pkg/errors" 29 "github.com/stretchr/testify/require" 30 31 "github.com/insolar/insolar/core" 32 "github.com/insolar/insolar/core/message" 33 "github.com/insolar/insolar/core/reply" 34 "github.com/insolar/insolar/instrumentation/inslogger" 35 "github.com/insolar/insolar/ledger/storage" 36 "github.com/insolar/insolar/ledger/storage/jet" 37 "github.com/insolar/insolar/testutils" 38 ) 39 40 func TestJetTreeUpdater_otherNodesForPulse(t *testing.T) { 41 ctx := inslogger.TestContext(t) 42 mc := minimock.NewController(t) 43 defer mc.Finish() 44 45 jc := testutils.NewJetCoordinatorMock(mc) 46 ans := nodes.NewAccessorMock(mc) 47 js := storage.NewJetStorageMock(mc) 48 jtu := &jetTreeUpdater{ 49 Nodes: ans, 50 JetStorage: js, 51 JetCoordinator: jc, 52 } 53 54 t.Run("active nodes storage returns error", func(t *testing.T) { 55 ans.InRoleMock.Expect( 56 100, core.StaticRoleLightMaterial, 57 ).Return( 58 nil, errors.New("some"), 59 ) 60 61 nodes, err := jtu.otherNodesForPulse(ctx, core.PulseNumber(100)) 62 require.Error(t, err) 63 require.Empty(t, nodes) 64 }) 65 66 meRef := testutils.RandomRef() 67 jc.MeMock.Return(meRef) 68 69 t.Run("no active nodes at all", func(t *testing.T) { 70 71 ans.InRoleMock.Expect( 72 100, core.StaticRoleLightMaterial, 73 ).Return( 74 []insolar.Node{}, nil, 75 ) 76 77 nodes, err := jtu.otherNodesForPulse(ctx, core.PulseNumber(100)) 78 require.Error(t, err) 79 require.Empty(t, nodes) 80 }) 81 82 t.Run("one active node, it's me", func(t *testing.T) { 83 ans.InRoleMock.Expect( 84 100, core.StaticRoleLightMaterial, 85 ).Return( 86 []insolar.Node{{ID: meRef}}, nil, 87 ) 88 89 nodes, err := jtu.otherNodesForPulse(ctx, core.PulseNumber(100)) 90 require.Error(t, err) 91 require.Empty(t, nodes) 92 }) 93 94 t.Run("active node", func(t *testing.T) { 95 someNode := insolar.Node{ID: gen.Reference()} 96 ans.InRoleMock.Expect( 97 100, core.StaticRoleLightMaterial, 98 ).Return( 99 []insolar.Node{someNode}, nil, 100 ) 101 102 nodes, err := jtu.otherNodesForPulse(ctx, core.PulseNumber(100)) 103 require.NoError(t, err) 104 require.Contains(t, nodes, someNode) 105 }) 106 107 t.Run("active node and me", func(t *testing.T) { 108 meNode := insolar.Node{ID: meRef} 109 someNode := insolar.Node{ID: gen.Reference()} 110 111 ans.InRoleMock.Expect( 112 100, core.StaticRoleLightMaterial, 113 ).Return( 114 []insolar.Node{someNode, meNode}, nil, 115 ) 116 117 nodes, err := jtu.otherNodesForPulse(ctx, core.PulseNumber(100)) 118 require.NoError(t, err) 119 require.Contains(t, nodes, someNode) 120 require.NotContains(t, nodes, meNode) 121 }) 122 } 123 124 func TestJetTreeUpdater_fetchActualJetFromOtherNodes(t *testing.T) { 125 ctx := inslogger.TestContext(t) 126 mc := minimock.NewController(t) 127 defer mc.Finish() 128 129 jc := testutils.NewJetCoordinatorMock(mc) 130 ans := nodes.NewAccessorMock(mc) 131 js := storage.NewJetStorageMock(mc) 132 mb := testutils.NewMessageBusMock(mc) 133 jtu := &jetTreeUpdater{ 134 Nodes: ans, 135 JetStorage: js, 136 JetCoordinator: jc, 137 MessageBus: mb, 138 } 139 140 meRef := testutils.RandomRef() 141 jc.MeMock.Return(meRef) 142 143 ans.InRoleMock.Expect( 144 100, core.StaticRoleLightMaterial, 145 ).Return( 146 []insolar.Node{{ID: gen.Reference()}}, nil, 147 ) 148 149 t.Run("MB error on fetching actual jet", func(t *testing.T) { 150 target := testutils.RandomID() 151 152 mb.SendMock.Return(nil, errors.New("some")) 153 154 jetID, err := jtu.fetchActualJetFromOtherNodes(ctx, target, core.PulseNumber(100)) 155 require.Error(t, err) 156 require.Nil(t, jetID) 157 }) 158 159 t.Run("MB got one not actual jet", func(t *testing.T) { 160 target := testutils.RandomID() 161 162 mb.SendMock.Return( 163 &reply.Jet{ID: *jet.NewID(0, nil), Actual: false}, 164 nil, 165 ) 166 167 jetID, err := jtu.fetchActualJetFromOtherNodes(ctx, target, core.PulseNumber(100)) 168 require.Error(t, err) 169 require.Nil(t, jetID) 170 }) 171 t.Run("MB got one actual jet", func(t *testing.T) { 172 target := testutils.RandomID() 173 174 mb.SendMock.Return( 175 &reply.Jet{ID: *jet.NewID(0, nil), Actual: true}, 176 nil, 177 ) 178 179 jetID, err := jtu.fetchActualJetFromOtherNodes(ctx, target, core.PulseNumber(100)) 180 require.NoError(t, err) 181 require.Equal(t, jet.NewID(0, nil), jetID) 182 }) 183 184 // TODO: multiple nodes returned different results 185 // TODO: multiple nodes returned the same result 186 } 187 188 func TestJetTreeUpdater_fetchJet(t *testing.T) { 189 ctx := inslogger.TestContext(t) 190 mc := minimock.NewController(t) 191 defer mc.Finish() 192 193 jc := testutils.NewJetCoordinatorMock(mc) 194 ans := nodes.NewAccessorMock(mc) 195 js := storage.NewJetStorageMock(mc) 196 mb := testutils.NewMessageBusMock(mc) 197 jtu := &jetTreeUpdater{ 198 Nodes: ans, 199 JetStorage: js, 200 JetCoordinator: jc, 201 MessageBus: mb, 202 sequencer: map[seqKey]*seqEntry{}, 203 } 204 205 target := testutils.RandomID() 206 207 t.Run("quick reply, data is up to date", func(t *testing.T) { 208 js.FindJetMock.Return(jet.NewID(0, nil), true) 209 jetID, err := jtu.fetchJet(ctx, target, core.PulseNumber(100)) 210 require.NoError(t, err) 211 require.Equal(t, jet.NewID(0, nil), jetID) 212 }) 213 214 t.Run("fetch jet from friends", func(t *testing.T) { 215 meRef := testutils.RandomRef() 216 jc.MeMock.Return(meRef) 217 218 ans.InRoleMock.Expect( 219 100, core.StaticRoleLightMaterial, 220 ).Return( 221 []insolar.Node{{ID: gen.Reference()}}, nil, 222 ) 223 mb.SendMock.Return( 224 &reply.Jet{ID: *jet.NewID(0, nil), Actual: true}, 225 nil, 226 ) 227 228 js.FindJetMock.Return(jet.NewID(0, nil), false) 229 js.UpdateJetTreeFunc = func(ctx context.Context, pn core.PulseNumber, actual bool, jets ...core.RecordID) { 230 require.Equal(t, core.PulseNumber(100), pn) 231 require.True(t, actual) 232 require.Equal(t, []core.RecordID{*jet.NewID(0, nil)}, jets) 233 } 234 235 jetID, err := jtu.fetchJet(ctx, target, core.PulseNumber(100)) 236 require.NoError(t, err) 237 require.Equal(t, jet.NewID(0, nil), jetID) 238 }) 239 } 240 241 func TestJetTreeUpdater_Concurrency(t *testing.T) { 242 ctx := inslogger.TestContext(t) 243 mc := minimock.NewController(t) 244 defer mc.Finish() 245 246 jc := testutils.NewJetCoordinatorMock(mc) 247 ans := nodes.NewAccessorMock(mc) 248 js := storage.NewJetStorageMock(mc) 249 mb := testutils.NewMessageBusMock(mc) 250 jtu := &jetTreeUpdater{ 251 Nodes: ans, 252 JetStorage: js, 253 JetCoordinator: jc, 254 MessageBus: mb, 255 sequencer: map[seqKey]*seqEntry{}, 256 } 257 258 meRef := testutils.RandomRef() 259 jc.MeMock.Return(meRef) 260 261 nodes := []insolar.Node{{ID: gen.Reference()}, {ID: gen.Reference()}, {ID: gen.Reference()}} 262 263 ans.InRoleMock.Return(nodes, nil) 264 265 dataMu := sync.Mutex{} 266 data := map[byte]*core.RecordID{ 267 0: jet.NewID(2, []byte{0}), // 00 268 128: jet.NewID(2, []byte{0}), // 10 269 64: jet.NewID(2, []byte{0}), // 01 270 192: jet.NewID(2, []byte{0}), // 11 271 } 272 273 mb.SendFunc = func(ctx context.Context, msg core.Message, opt *core.MessageSendOptions) (core.Reply, error) { 274 dataMu.Lock() 275 defer dataMu.Unlock() 276 277 b := msg.(*message.GetJet).Object.Bytes()[0] 278 return &reply.Jet{ID: *data[b], Actual: true}, nil 279 } 280 281 i := 100 282 for i > 0 { 283 i-- 284 285 treeMu := sync.Mutex{} 286 tree := jet.NewTree(false) 287 288 js.UpdateJetTreeFunc = func(ctx context.Context, pn core.PulseNumber, actual bool, jets ...core.RecordID) { 289 treeMu.Lock() 290 defer treeMu.Unlock() 291 292 for _, id := range jets { 293 tree.Update(id, actual) 294 } 295 } 296 js.FindJetFunc = func(ctx context.Context, pulse core.PulseNumber, id core.RecordID) (*core.RecordID, bool) { 297 treeMu.Lock() 298 defer treeMu.Unlock() 299 300 return tree.Find(id) 301 } 302 303 wg := sync.WaitGroup{} 304 wg.Add(4) 305 306 for _, b := range []byte{0, 128, 192} { 307 go func(b byte) { 308 target := core.NewRecordID(0, []byte{b}) 309 310 jetID, err := jtu.fetchJet(ctx, *target, core.PulseNumber(100)) 311 require.NoError(t, err) 312 313 dataMu.Lock() 314 require.Equal(t, data[b], jetID) 315 dataMu.Unlock() 316 317 wg.Done() 318 }(b) 319 } 320 go func() { 321 dataMu.Lock() 322 jtu.releaseJet(ctx, *data[128], core.PulseNumber(100)) 323 dataMu.Unlock() 324 325 wg.Done() 326 }() 327 wg.Wait() 328 } 329 }