go.uber.org/yarpc@v1.72.1/peer/abstractpeer/peeraction_test.go (about)

     1  // Copyright (c) 2022 Uber Technologies, Inc.
     2  //
     3  // Permission is hereby granted, free of charge, to any person obtaining a copy
     4  // of this software and associated documentation files (the "Software"), to deal
     5  // in the Software without restriction, including without limitation the rights
     6  // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
     7  // copies of the Software, and to permit persons to whom the Software is
     8  // furnished to do so, subject to the following conditions:
     9  //
    10  // The above copyright notice and this permission notice shall be included in
    11  // all copies or substantial portions of the Software.
    12  //
    13  // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    14  // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    15  // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    16  // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    17  // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    18  // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
    19  // THE SOFTWARE.
    20  
    21  package abstractpeer
    22  
    23  import (
    24  	"fmt"
    25  	"sync"
    26  	"testing"
    27  	"time"
    28  
    29  	"github.com/stretchr/testify/assert"
    30  	"go.uber.org/yarpc/api/peer"
    31  	"go.uber.org/yarpc/internal/testtime"
    32  )
    33  
    34  // There are no actual tests in this file, it contains a series of helper methods
    35  // for testing abstractpeer.Peers
    36  
    37  // Dependences are passed through all the PeerActions in order to pass certain
    38  // state in between Actions
    39  type Dependencies struct {
    40  	Subscribers map[string]peer.Subscriber
    41  }
    42  
    43  // PeerAction defines actions that can be applied on a abstractpeer.Peer
    44  type PeerAction interface {
    45  	Apply(*testing.T, *Peer, *Dependencies)
    46  }
    47  
    48  // StartStopReqAction will run a StartRequest and (optionally) EndRequest
    49  type StartStopReqAction struct {
    50  	Stop bool
    51  }
    52  
    53  // Apply will run StartRequest and (optionally) EndRequest
    54  func (sa StartStopReqAction) Apply(t *testing.T, p *Peer, d *Dependencies) {
    55  	p.StartRequest()
    56  	p.NotifyStatusChanged()
    57  	if sa.Stop {
    58  		p.EndRequest()
    59  		p.NotifyStatusChanged()
    60  	}
    61  }
    62  
    63  // SetStatusAction will run a SetStatus on a Peer
    64  type SetStatusAction struct {
    65  	InputStatus peer.ConnectionStatus
    66  }
    67  
    68  // Apply will run SetStatus on the Peer
    69  func (sa SetStatusAction) Apply(t *testing.T, p *Peer, d *Dependencies) {
    70  	p.SetStatus(sa.InputStatus)
    71  	p.NotifyStatusChanged()
    72  
    73  	assert.Equal(t, sa.InputStatus, p.Status().ConnectionStatus)
    74  }
    75  
    76  // SubscribeAction will run an Subscribe on a Peer
    77  type SubscribeAction struct {
    78  	// SubscriberID is a unique identifier for a subscriber that is
    79  	// contained in the Dependencies object passed in Apply
    80  	SubscriberID string
    81  
    82  	// ExpectedSubCount is the number of subscribers on the Peer after
    83  	// the subscription
    84  	ExpectedSubCount int
    85  }
    86  
    87  // Apply will run Subscribe on a Peer
    88  func (sa SubscribeAction) Apply(t *testing.T, p *Peer, d *Dependencies) {
    89  	sub, ok := d.Subscribers[sa.SubscriberID]
    90  	assert.True(t, ok, "referenced a subscriberID that does not exist %s", sa.SubscriberID)
    91  
    92  	p.Subscribe(sub)
    93  
    94  	assert.Equal(t, sa.ExpectedSubCount, p.NumSubscribers())
    95  }
    96  
    97  // UnsubscribeAction will run Unsubscribe on a Peer
    98  type UnsubscribeAction struct {
    99  	// SubscriberID is a unique identifier for a subscriber that is
   100  	// contained in the Dependencies object passed in Apply
   101  	SubscriberID string
   102  
   103  	// ExpectedErrType is the type of error that is expected to be returned
   104  	// from Unsubscribe
   105  	ExpectedErrType error
   106  
   107  	// ExpectedSubCount is the number of subscribers on the Peer after
   108  	// the subscription
   109  	ExpectedSubCount int
   110  }
   111  
   112  // Apply will run Unsubscribe from the Peer and assert on the result
   113  func (ua UnsubscribeAction) Apply(t *testing.T, p *Peer, d *Dependencies) {
   114  	sub, ok := d.Subscribers[ua.SubscriberID]
   115  	assert.True(t, ok, "referenced a subscriberID that does not exist %s", ua.SubscriberID)
   116  
   117  	err := p.Unsubscribe(sub)
   118  
   119  	assert.Equal(t, ua.ExpectedSubCount, p.NumSubscribers())
   120  	if err != nil {
   121  		assert.IsType(t, ua.ExpectedErrType, err)
   122  	} else {
   123  		assert.Nil(t, ua.ExpectedErrType)
   124  	}
   125  }
   126  
   127  // PeerConcurrentAction will run a series of actions in parallel
   128  type PeerConcurrentAction struct {
   129  	Actions []PeerAction
   130  	Wait    time.Duration
   131  }
   132  
   133  // Apply runs all the ConcurrentAction's actions in goroutines with a delay of `Wait`
   134  // between each action. Returns when all actions have finished executing
   135  func (a PeerConcurrentAction) Apply(t *testing.T, p *Peer, d *Dependencies) {
   136  	var wg sync.WaitGroup
   137  
   138  	wg.Add(len(a.Actions))
   139  	for _, action := range a.Actions {
   140  		go func(ac PeerAction) {
   141  			defer wg.Done()
   142  			ac.Apply(t, p, d)
   143  		}(action)
   144  
   145  		if a.Wait > 0 {
   146  			testtime.Sleep(a.Wait)
   147  		}
   148  	}
   149  
   150  	wg.Wait()
   151  }
   152  
   153  // ApplyPeerActions runs all the PeerActions on the Peer
   154  func ApplyPeerActions(t *testing.T, p *Peer, actions []PeerAction, d *Dependencies) {
   155  	for i, action := range actions {
   156  		t.Run(fmt.Sprintf("action #%d: %T", i, action), func(t *testing.T) {
   157  			action.Apply(t, p, d)
   158  		})
   159  	}
   160  }