func TestAgent(t *testing.T) { mockCtrl := gomock.NewController(t) defer mockCtrl.Finish() type testStruct struct { msg string agent *Agent appliedFunc func(*Agent) error expectedPeers []peerExpectation expectedErr error } tests := []testStruct{ func() (s testStruct) { s.msg = "one retain" s.expectedPeers = createPeerExpectations( mockCtrl, []string{"localhost:1234"}, 1, ) s.appliedFunc = func(a *Agent) error { _, err := a.RetainPeer(s.expectedPeers[0].identifier, s.expectedPeers[0].subscribers[0]) return err } return }(), func() (s testStruct) { s.msg = "one retain one release" s.expectedPeers = []peerExpectation{} s.appliedFunc = func(a *Agent) error { pid := hostport.PeerIdentifier("localhost:1234") sub := transporttest.NewMockPeerSubscriber(mockCtrl) if _, err := a.RetainPeer(pid, sub); err != nil { return err } return a.ReleasePeer(pid, sub) } return }(), func() (s testStruct) { s.msg = "one retain one release using peer" s.expectedPeers = []peerExpectation{} s.appliedFunc = func(a *Agent) error { pid := hostport.PeerIdentifier("localhost:1234") sub := transporttest.NewMockPeerSubscriber(mockCtrl) peer, err := a.RetainPeer(pid, sub) if err != nil { return err } err = a.ReleasePeer(peer, sub) return err } return }(), func() (s testStruct) { s.msg = "three retains" s.expectedPeers = createPeerExpectations( mockCtrl, []string{"localhost:1234"}, 3, ) s.appliedFunc = func(a *Agent) error { a.RetainPeer(s.expectedPeers[0].identifier, s.expectedPeers[0].subscribers[0]) a.RetainPeer(s.expectedPeers[0].identifier, s.expectedPeers[0].subscribers[1]) _, err := a.RetainPeer(s.expectedPeers[0].identifier, s.expectedPeers[0].subscribers[2]) return err } return }(), func() (s testStruct) { s.msg = "three retains, one release" s.expectedPeers = createPeerExpectations( mockCtrl, []string{"localhost:1234"}, 2, ) s.appliedFunc = func(a *Agent) error { unSub := transporttest.NewMockPeerSubscriber(mockCtrl) a.RetainPeer(s.expectedPeers[0].identifier, unSub) a.RetainPeer(s.expectedPeers[0].identifier, s.expectedPeers[0].subscribers[0]) a.ReleasePeer(s.expectedPeers[0].identifier, unSub) a.RetainPeer(s.expectedPeers[0].identifier, s.expectedPeers[0].subscribers[1]) return nil } return }(), func() (s testStruct) { s.msg = "three retains, three release" s.expectedPeers = []peerExpectation{} s.appliedFunc = func(a *Agent) error { pid := hostport.PeerIdentifier("localhost:123") sub := transporttest.NewMockPeerSubscriber(mockCtrl) sub2 := transporttest.NewMockPeerSubscriber(mockCtrl) sub3 := transporttest.NewMockPeerSubscriber(mockCtrl) a.RetainPeer(pid, sub) a.RetainPeer(pid, sub2) a.ReleasePeer(pid, sub) a.RetainPeer(pid, sub3) a.ReleasePeer(pid, sub2) a.ReleasePeer(pid, sub3) return nil } return }(), func() (s testStruct) { s.msg = "no retains, one release" s.agent = NewAgent() s.expectedPeers = []peerExpectation{} pid := hostport.PeerIdentifier("localhost:1234") s.expectedErr = errors.ErrAgentHasNoReferenceToPeer{ Agent: s.agent, PeerIdentifier: pid, } s.appliedFunc = func(a *Agent) error { return a.ReleasePeer(pid, transporttest.NewMockPeerSubscriber(mockCtrl)) } return }(), func() (s testStruct) { s.msg = "retain with invalid identifier" s.expectedPeers = []peerExpectation{} pid := transporttest.NewMockPeerIdentifier(mockCtrl) s.expectedErr = errors.ErrInvalidPeerType{ ExpectedType: "hostport.PeerIdentifier", PeerIdentifier: pid, } s.appliedFunc = func(a *Agent) error { _, err := a.RetainPeer(pid, transporttest.NewMockPeerSubscriber(mockCtrl)) return err } return }(), func() (s testStruct) { s.msg = "one retains, one release (from different subscriber)" s.expectedPeers = createPeerExpectations( mockCtrl, []string{"localhost:1234"}, 1, ) invalidSub := transporttest.NewMockPeerSubscriber(mockCtrl) s.expectedErr = errors.ErrPeerHasNoReferenceToSubscriber{ PeerIdentifier: s.expectedPeers[0].identifier, PeerSubscriber: invalidSub, } s.appliedFunc = func(a *Agent) error { a.RetainPeer(s.expectedPeers[0].identifier, s.expectedPeers[0].subscribers[0]) return a.ReleasePeer(s.expectedPeers[0].identifier, invalidSub) } return }(), func() (s testStruct) { s.msg = "multi peer retain/release" s.expectedPeers = createPeerExpectations( mockCtrl, []string{"localhost:1234", "localhost:1111", "localhost:2222"}, 2, ) s.appliedFunc = func(a *Agent) error { expP1 := s.expectedPeers[0] expP2 := s.expectedPeers[1] expP3 := s.expectedPeers[2] rndP1 := hostport.PeerIdentifier("localhost:9888") rndP2 := hostport.PeerIdentifier("localhost:9883") rndSub1 := transporttest.NewMockPeerSubscriber(mockCtrl) rndSub2 := transporttest.NewMockPeerSubscriber(mockCtrl) rndSub3 := transporttest.NewMockPeerSubscriber(mockCtrl) // exp1: Defer a bunch of Releases a.RetainPeer(expP1.identifier, rndSub1) defer a.ReleasePeer(expP1.identifier, rndSub1) a.RetainPeer(expP1.identifier, expP1.subscribers[0]) a.RetainPeer(expP1.identifier, rndSub2) defer a.ReleasePeer(expP1.identifier, rndSub2) a.RetainPeer(expP1.identifier, expP1.subscribers[1]) // exp2: Retain a subscriber, release it, then retain it again a.RetainPeer(expP2.identifier, expP2.subscribers[0]) a.RetainPeer(expP2.identifier, expP2.subscribers[1]) a.ReleasePeer(expP2.identifier, expP2.subscribers[0]) a.ReleasePeer(expP2.identifier, expP2.subscribers[1]) a.RetainPeer(expP2.identifier, expP2.subscribers[0]) a.RetainPeer(expP2.identifier, expP2.subscribers[1]) // exp3: Retain release a Peer a.RetainPeer(expP3.identifier, rndSub3) a.ReleasePeer(expP3.identifier, rndSub3) a.RetainPeer(expP3.identifier, expP3.subscribers[0]) a.RetainPeer(expP3.identifier, expP3.subscribers[1]) // rnd1: retain/release on random sub a.RetainPeer(rndP1, rndSub1) a.ReleasePeer(rndP1, rndSub1) // rnd2: retain/release on already used subscriber a.RetainPeer(rndP2, expP1.subscribers[0]) a.ReleasePeer(rndP2, expP1.subscribers[0]) return nil } return }(), func() (s testStruct) { s.msg = "notification verification" s.expectedPeers = createPeerExpectations( mockCtrl, []string{"localhost:1234"}, 2, ) s.expectedPeers[0].subscribers[0].EXPECT().NotifyStatusChanged( peerIdentifierMatcher(s.expectedPeers[0].identifier), ).Times(3) s.expectedPeers[0].subscribers[1].EXPECT().NotifyStatusChanged( peerIdentifierMatcher(s.expectedPeers[0].identifier), ).Times(3) s.appliedFunc = func(a *Agent) error { peer, _ := a.RetainPeer(s.expectedPeers[0].identifier, s.expectedPeers[0].subscribers[0]) a.RetainPeer(s.expectedPeers[0].identifier, s.expectedPeers[0].subscribers[1]) a.NotifyStatusChanged(peer) a.NotifyStatusChanged(peer) a.NotifyStatusChanged(peer) return nil } return }(), func() (s testStruct) { s.msg = "no notification after release" s.expectedPeers = []peerExpectation{} sub := transporttest.NewMockPeerSubscriber(mockCtrl) sub.EXPECT().NotifyStatusChanged(gomock.Any()).Times(0) pid := hostport.PeerIdentifier("localhost:1234") s.appliedFunc = func(a *Agent) error { peer, _ := a.RetainPeer(pid, sub) a.ReleasePeer(pid, sub) a.NotifyStatusChanged(peer) a.NotifyStatusChanged(peer) a.NotifyStatusChanged(peer) return nil } return }(), func() (s testStruct) { s.msg = "notification before versus after release" s.expectedPeers = []peerExpectation{} pid := hostport.PeerIdentifier("localhost:1234") sub := transporttest.NewMockPeerSubscriber(mockCtrl) sub.EXPECT().NotifyStatusChanged(peerIdentifierMatcher(pid)).Times(1) s.appliedFunc = func(a *Agent) error { peer, _ := a.RetainPeer(pid, sub) a.NotifyStatusChanged(peer) a.ReleasePeer(pid, sub) a.NotifyStatusChanged(peer) a.NotifyStatusChanged(peer) a.NotifyStatusChanged(peer) return nil } return }(), } for _, tt := range tests { agent := tt.agent if agent == nil { agent = NewAgent() } err := tt.appliedFunc(agent) assert.Equal(t, tt.expectedErr, err, tt.msg) assert.Len(t, agent.peerNodes, len(tt.expectedPeers), tt.msg) for _, expectedPeerNode := range tt.expectedPeers { peerNode, ok := agent.peerNodes[expectedPeerNode.identifier.Identifier()] assert.True(t, ok, tt.msg) assert.Equal(t, expectedPeerNode.identifier.Identifier(), peerNode.peer.Identifier(), tt.msg) assert.Len(t, peerNode.subscribers, len(expectedPeerNode.subscribers), tt.msg) for _, expectedSubscriber := range expectedPeerNode.subscribers { _, ok := peerNode.subscribers[expectedSubscriber] assert.True(t, ok, "subscriber (%v) not in list (%v). %s", expectedSubscriber, peerNode.subscribers, tt.msg) } } } }
func TestSingle(t *testing.T) { mockCtrl := gomock.NewController(t) defer mockCtrl.Finish() type expectedChooseResult struct { peer transport.Peer err error } type testStruct struct { msg string pid transport.PeerIdentifier agent *transporttest.MockAgent appliedFunc func(*single) error expectedPeerID transport.PeerIdentifier expectedPeer transport.Peer expectedAgent transport.Agent expectedStarted bool expectedErr error expectedChooseResults []expectedChooseResult } tests := []testStruct{ func() (s testStruct) { s.msg = "setup" s.pid = transporttest.NewMockPeerIdentifier(mockCtrl) s.agent = transporttest.NewMockAgent(mockCtrl) s.appliedFunc = func(pl *single) error { return nil } s.expectedPeerID = s.pid s.expectedAgent = s.agent s.expectedStarted = false return }(), func() (s testStruct) { s.msg = "stop before start" s.pid = transporttest.NewMockPeerIdentifier(mockCtrl) s.agent = transporttest.NewMockAgent(mockCtrl) s.appliedFunc = func(pl *single) error { return pl.Stop() } s.expectedErr = errors.ErrPeerListNotStarted("single") s.expectedPeerID = s.pid s.expectedAgent = s.agent s.expectedStarted = false return }(), func() (s testStruct) { s.msg = "choose before start" s.pid = transporttest.NewMockPeerIdentifier(mockCtrl) s.agent = transporttest.NewMockAgent(mockCtrl) s.appliedFunc = func(pl *single) error { return nil } s.expectedPeerID = s.pid s.expectedAgent = s.agent s.expectedStarted = false s.expectedChooseResults = []expectedChooseResult{{ peer: nil, err: errors.ErrPeerListNotStarted("single"), }} return }(), func() (s testStruct) { s.msg = "start and choose" s.pid = transporttest.NewMockPeerIdentifier(mockCtrl) s.agent = transporttest.NewMockAgent(mockCtrl) s.expectedPeer = transporttest.NewMockPeer(mockCtrl) s.agent.EXPECT().RetainPeer(s.pid, gomock.Any()).Return(s.expectedPeer, nil) s.appliedFunc = func(pl *single) error { return pl.Start() } s.expectedPeerID = s.pid s.expectedAgent = s.agent s.expectedStarted = true s.expectedChooseResults = []expectedChooseResult{{ peer: s.expectedPeer, err: nil, }} return }(), func() (s testStruct) { s.msg = "start with agent error" s.pid = transporttest.NewMockPeerIdentifier(mockCtrl) s.agent = transporttest.NewMockAgent(mockCtrl) s.expectedErr = fmt.Errorf("test error") s.agent.EXPECT().RetainPeer(s.pid, gomock.Any()).Return(nil, s.expectedErr) s.appliedFunc = func(pl *single) error { return pl.Start() } s.expectedPeerID = s.pid s.expectedAgent = s.agent s.expectedStarted = false return }(), func() (s testStruct) { s.msg = "start twice" s.pid = transporttest.NewMockPeerIdentifier(mockCtrl) s.agent = transporttest.NewMockAgent(mockCtrl) s.expectedPeer = transporttest.NewMockPeer(mockCtrl) s.agent.EXPECT().RetainPeer(s.pid, gomock.Any()).Return(s.expectedPeer, nil) s.appliedFunc = func(pl *single) error { pl.Start() return pl.Start() } s.expectedErr = errors.ErrPeerListAlreadyStarted("single") s.expectedPeerID = s.pid s.expectedAgent = s.agent s.expectedStarted = true return }(), func() (s testStruct) { s.msg = "start stop" s.pid = transporttest.NewMockPeerIdentifier(mockCtrl) s.agent = transporttest.NewMockAgent(mockCtrl) peer := transporttest.NewMockPeer(mockCtrl) s.agent.EXPECT().RetainPeer(s.pid, gomock.Any()).Return(peer, nil) s.agent.EXPECT().ReleasePeer(s.pid, gomock.Any()).Return(nil) s.appliedFunc = func(pl *single) error { err := pl.Start() if err != nil { return err } return pl.Stop() } s.expectedErr = nil s.expectedPeerID = s.pid s.expectedPeer = nil s.expectedAgent = s.agent s.expectedStarted = false return }(), func() (s testStruct) { s.msg = "start stop release failure" s.pid = transporttest.NewMockPeerIdentifier(mockCtrl) s.agent = transporttest.NewMockAgent(mockCtrl) s.expectedPeer = transporttest.NewMockPeer(mockCtrl) s.agent.EXPECT().RetainPeer(s.pid, gomock.Any()).Return(s.expectedPeer, nil) s.expectedErr = errors.ErrAgentHasNoReferenceToPeer{} s.agent.EXPECT().ReleasePeer(s.pid, gomock.Any()).Return(s.expectedErr) s.appliedFunc = func(pl *single) error { err := pl.Start() if err != nil { return err } return pl.Stop() } s.expectedPeerID = s.pid s.expectedAgent = s.agent s.expectedStarted = false return }(), } for _, tt := range tests { pl := NewSingle(tt.pid, tt.agent).(*single) err := tt.appliedFunc(pl) assert.Equal(t, tt.expectedErr, err, tt.msg) assert.Equal(t, tt.expectedAgent, pl.agent, tt.msg) assert.Equal(t, tt.expectedPeerID, pl.initialPeerID, tt.msg) assert.Equal(t, tt.expectedPeer, pl.peer, tt.msg) assert.Equal(t, tt.expectedStarted, pl.started, tt.msg) for _, expectedResult := range tt.expectedChooseResults { peer, err := pl.ChoosePeer(context.Background(), &transport.Request{}) assert.Equal(t, expectedResult.peer, peer, tt.msg) assert.True(t, expectedResult.peer == peer, tt.msg) assert.Equal(t, expectedResult.err, err, tt.msg) } } }