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  }