github.com/xmidt-org/webpa-common@v1.11.9/device/rehasher/rehasher_test.go (about)

     1  package rehasher
     2  
     3  import (
     4  	"errors"
     5  	"testing"
     6  	"time"
     7  
     8  	"github.com/stretchr/testify/assert"
     9  	"github.com/stretchr/testify/mock"
    10  	"github.com/stretchr/testify/require"
    11  	"github.com/xmidt-org/webpa-common/device"
    12  	"github.com/xmidt-org/webpa-common/logging"
    13  	"github.com/xmidt-org/webpa-common/service"
    14  	"github.com/xmidt-org/webpa-common/service/monitor"
    15  	"github.com/xmidt-org/webpa-common/xmetrics/xmetricstest"
    16  )
    17  
    18  func testNewNilConnector(t *testing.T) {
    19  	assert := assert.New(t)
    20  	assert.Panics(func() {
    21  		New(nil, nil, WithAccessorFactory(nil), WithIsRegistered(func(string) bool { return true }))
    22  	})
    23  }
    24  
    25  func testNewEmptyServices(t *testing.T) {
    26  	assert := assert.New(t)
    27  
    28  	assert.Panics(func() {
    29  		New(new(device.MockConnector), nil, WithAccessorFactory(nil), WithIsRegistered(func(string) bool { return true }))
    30  	})
    31  }
    32  
    33  func testNewMissingIsRegistered(t *testing.T) {
    34  	var (
    35  		assert = assert.New(t)
    36  		c      = new(device.MockConnector)
    37  	)
    38  
    39  	assert.Panics(func() {
    40  		New(c, nil, WithAccessorFactory(nil))
    41  	})
    42  
    43  	c.AssertExpectations(t)
    44  }
    45  
    46  func testNewNilLogger(t *testing.T) {
    47  	var (
    48  		assert = assert.New(t)
    49  
    50  		isRegistered = func(string) bool {
    51  			assert.Fail("isRegistered should not have been called")
    52  			return false
    53  		}
    54  
    55  		connector = new(device.MockConnector)
    56  		r         = New(
    57  			connector,
    58  			[]string{"talaria"},
    59  			WithLogger(nil),
    60  			WithIsRegistered(isRegistered),
    61  		)
    62  	)
    63  
    64  	assert.NotNil(r)
    65  	connector.AssertExpectations(t)
    66  }
    67  
    68  func testNewNilMetricsProvider(t *testing.T) {
    69  	var (
    70  		assert = assert.New(t)
    71  
    72  		isRegistered = func(string) bool {
    73  			assert.Fail("isRegistered should not have been called")
    74  			return false
    75  		}
    76  
    77  		connector = new(device.MockConnector)
    78  		r         = New(
    79  			connector,
    80  			[]string{"talaria"},
    81  			WithLogger(logging.NewTestLogger(nil, t)),
    82  			WithIsRegistered(isRegistered),
    83  			WithMetricsProvider(nil),
    84  		)
    85  	)
    86  
    87  	assert.NotNil(r)
    88  	connector.AssertExpectations(t)
    89  }
    90  
    91  func TestNew(t *testing.T) {
    92  	t.Run("NilConnector", testNewNilConnector)
    93  	t.Run("EmptyServices", testNewEmptyServices)
    94  	t.Run("MissingIsRegistered", testNewMissingIsRegistered)
    95  	t.Run("NilLogger", testNewNilLogger)
    96  	t.Run("NilMetricsProvider", testNewNilMetricsProvider)
    97  }
    98  
    99  func testRehasherServiceDiscoveryError(t *testing.T) {
   100  	var (
   101  		assert   = assert.New(t)
   102  		require  = require.New(t)
   103  		provider = xmetricstest.NewProvider(nil, Metrics)
   104  
   105  		isRegistered = func(string) bool {
   106  			assert.Fail("isRegistered should not have been called")
   107  			return false
   108  		}
   109  
   110  		serviceDiscoveryError = errors.New("service discovery error")
   111  		connector             = new(device.MockConnector)
   112  		r                     = New(
   113  			connector,
   114  			[]string{"talaria", "caduceus"},
   115  			WithLogger(logging.NewTestLogger(nil, t)),
   116  			WithIsRegistered(isRegistered),
   117  			WithMetricsProvider(provider),
   118  		)
   119  	)
   120  
   121  	require.NotNil(r)
   122  	connector.On("DisconnectAll", device.CloseReason{Err: serviceDiscoveryError, Text: ServiceDiscoveryError}).Return(12)
   123  	provider.Expect(RehashKeepDevice, service.ServiceLabel, "talaria")(xmetricstest.Gauge, xmetricstest.Value(0.0))
   124  	provider.Expect(RehashDisconnectDevice, service.ServiceLabel, "talaria")(xmetricstest.Gauge, xmetricstest.Value(0.0))
   125  	provider.Expect(RehashDisconnectAllCounter, service.ServiceLabel, "talaria", ReasonLabel, DisconnectAllServiceDiscoveryError)(
   126  		xmetricstest.Counter,
   127  		xmetricstest.Value(1.0),
   128  	)
   129  	provider.Expect(RehashTimestamp, service.ServiceLabel, "talaria")(xmetricstest.Gauge, xmetricstest.Value(0.0))
   130  	provider.Expect(RehashDurationMilliseconds, service.ServiceLabel, "talaria")(xmetricstest.Gauge, xmetricstest.Value(0.0))
   131  
   132  	r.MonitorEvent(monitor.Event{EventCount: 10, Key: "test", Service: "talaria", Err: serviceDiscoveryError})
   133  
   134  	connector.AssertExpectations(t)
   135  	provider.AssertExpectations(t)
   136  }
   137  
   138  func testRehasherServiceDiscoveryStopped(t *testing.T) {
   139  	var (
   140  		assert   = assert.New(t)
   141  		require  = require.New(t)
   142  		provider = xmetricstest.NewProvider(nil, Metrics)
   143  
   144  		isRegistered = func(string) bool {
   145  			assert.Fail("isRegistered should not have been called")
   146  			return false
   147  		}
   148  
   149  		connector = new(device.MockConnector)
   150  		r         = New(
   151  			connector,
   152  			[]string{"caduceus"},
   153  			WithLogger(logging.NewTestLogger(nil, t)),
   154  			WithIsRegistered(isRegistered),
   155  			WithMetricsProvider(provider),
   156  		)
   157  	)
   158  
   159  	require.NotNil(r)
   160  	connector.On("DisconnectAll", device.CloseReason{Text: ServiceDiscoveryStopped}).Return(0)
   161  	provider.Expect(RehashKeepDevice, service.ServiceLabel, "caduceus")(xmetricstest.Gauge, xmetricstest.Value(0.0))
   162  	provider.Expect(RehashDisconnectDevice, service.ServiceLabel, "caduceus")(xmetricstest.Gauge, xmetricstest.Value(0.0))
   163  	provider.Expect(RehashDisconnectAllCounter, service.ServiceLabel, "caduceus", ReasonLabel, DisconnectAllServiceDiscoveryStopped)(
   164  		xmetricstest.Counter,
   165  		xmetricstest.Value(1.0),
   166  	)
   167  	provider.Expect(RehashTimestamp, service.ServiceLabel, "caduceus")(xmetricstest.Gauge, xmetricstest.Value(0.0))
   168  	provider.Expect(RehashDurationMilliseconds, service.ServiceLabel, "caduceus")(xmetricstest.Gauge, xmetricstest.Value(0.0))
   169  
   170  	r.MonitorEvent(monitor.Event{Key: "test", Service: "caduceus", EventCount: 10, Stopped: true})
   171  
   172  	connector.AssertExpectations(t)
   173  	provider.AssertExpectations(t)
   174  }
   175  
   176  func testRehasherInitialEvent(t *testing.T) {
   177  	var (
   178  		assert   = assert.New(t)
   179  		require  = require.New(t)
   180  		provider = xmetricstest.NewProvider(nil, Metrics)
   181  
   182  		isRegistered = func(string) bool {
   183  			assert.Fail("isRegistered should not have been called")
   184  			return false
   185  		}
   186  
   187  		connector = new(device.MockConnector)
   188  		r         = New(
   189  			connector,
   190  			[]string{"talaria"},
   191  			WithLogger(logging.NewTestLogger(nil, t)),
   192  			WithIsRegistered(isRegistered),
   193  			WithMetricsProvider(provider),
   194  		)
   195  	)
   196  
   197  	require.NotNil(r)
   198  	provider.Expect(RehashKeepDevice, service.ServiceLabel, "talaria")(xmetricstest.Gauge, xmetricstest.Value(0.0))
   199  	provider.Expect(RehashDisconnectDevice, service.ServiceLabel, "talaria")(xmetricstest.Gauge, xmetricstest.Value(0.0))
   200  	provider.Expect(RehashDisconnectAllCounter, service.ServiceLabel, "talaria")(xmetricstest.Counter, xmetricstest.Value(0.0))
   201  	provider.Expect(RehashTimestamp, service.ServiceLabel, "talaria")(xmetricstest.Gauge, xmetricstest.Value(0.0))
   202  	provider.Expect(RehashDurationMilliseconds, service.ServiceLabel, "talaria")(xmetricstest.Gauge, xmetricstest.Value(0.0))
   203  
   204  	r.MonitorEvent(monitor.Event{Key: "test", Service: "talaria", EventCount: 1})
   205  
   206  	connector.AssertExpectations(t)
   207  	provider.AssertExpectations(t)
   208  }
   209  
   210  func testRehasherSkippedService(t *testing.T) {
   211  	var (
   212  		assert   = assert.New(t)
   213  		require  = require.New(t)
   214  		provider = xmetricstest.NewProvider(nil, Metrics)
   215  
   216  		isRegistered = func(string) bool {
   217  			assert.Fail("isRegistered should not have been called")
   218  			return false
   219  		}
   220  
   221  		connector = new(device.MockConnector)
   222  		r         = New(
   223  			connector,
   224  			[]string{"caduceus"},
   225  			WithLogger(logging.NewTestLogger(nil, t)),
   226  			WithIsRegistered(isRegistered),
   227  			WithMetricsProvider(provider),
   228  		)
   229  	)
   230  
   231  	require.NotNil(r)
   232  	connector.AssertNotCalled(t, "DisconnectAll", mock.Anything)
   233  	provider.Expect(RehashKeepDevice, service.ServiceLabel, "caduceus")(xmetricstest.Gauge, xmetricstest.Value(0.0))
   234  	provider.Expect(RehashDisconnectDevice, service.ServiceLabel, "caduceus")(xmetricstest.Gauge, xmetricstest.Value(0.0))
   235  	provider.Expect(RehashDisconnectAllCounter, service.ServiceLabel, "caduceus", ReasonLabel, DisconnectAllServiceDiscoveryNoInstances)(
   236  		xmetricstest.Counter,
   237  		xmetricstest.Value(0.0),
   238  	)
   239  	provider.Expect(RehashTimestamp, service.ServiceLabel, "caduceus")(xmetricstest.Gauge, xmetricstest.Value(0.0))
   240  	provider.Expect(RehashDurationMilliseconds, service.ServiceLabel, "caduceus")(xmetricstest.Gauge, xmetricstest.Value(0.0))
   241  
   242  	r.MonitorEvent(monitor.Event{Key: "test", Service: "tr1d1um", EventCount: 10})
   243  
   244  	connector.AssertExpectations(t)
   245  	provider.AssertExpectations(t)
   246  }
   247  
   248  func testRehasherNoInstances(t *testing.T) {
   249  	var (
   250  		assert   = assert.New(t)
   251  		require  = require.New(t)
   252  		provider = xmetricstest.NewProvider(nil, Metrics)
   253  
   254  		isRegistered = func(string) bool {
   255  			assert.Fail("isRegistered should not have been called")
   256  			return false
   257  		}
   258  
   259  		connector = new(device.MockConnector)
   260  		r         = New(
   261  			connector,
   262  			[]string{"caduceus"},
   263  			WithLogger(logging.NewTestLogger(nil, t)),
   264  			WithIsRegistered(isRegistered),
   265  			WithMetricsProvider(provider),
   266  		)
   267  	)
   268  
   269  	require.NotNil(r)
   270  	connector.On("DisconnectAll", device.CloseReason{Text: ServiceDiscoveryNoInstances}).Return(0)
   271  	provider.Expect(RehashKeepDevice, service.ServiceLabel, "caduceus")(xmetricstest.Gauge, xmetricstest.Value(0.0))
   272  	provider.Expect(RehashDisconnectDevice, service.ServiceLabel, "caduceus")(xmetricstest.Gauge, xmetricstest.Value(0.0))
   273  	provider.Expect(RehashDisconnectAllCounter, service.ServiceLabel, "caduceus", ReasonLabel, DisconnectAllServiceDiscoveryNoInstances)(
   274  		xmetricstest.Counter,
   275  		xmetricstest.Value(1.0),
   276  	)
   277  	provider.Expect(RehashTimestamp, service.ServiceLabel, "caduceus")(xmetricstest.Gauge, xmetricstest.Value(0.0))
   278  	provider.Expect(RehashDurationMilliseconds, service.ServiceLabel, "caduceus")(xmetricstest.Gauge, xmetricstest.Value(0.0))
   279  
   280  	r.MonitorEvent(monitor.Event{Key: "test", Service: "caduceus", EventCount: 10})
   281  
   282  	connector.AssertExpectations(t)
   283  	provider.AssertExpectations(t)
   284  }
   285  
   286  func testRehasherRehash(t *testing.T) {
   287  	var (
   288  		assert   = assert.New(t)
   289  		require  = require.New(t)
   290  		provider = xmetricstest.NewProvider(nil, Metrics)
   291  
   292  		keepID   = device.ID("keep")
   293  		keepNode = "keep.xfinity.net"
   294  
   295  		rehashedID = device.ID("rehashed")
   296  		rehashNode = "rehash.xfinity.net"
   297  
   298  		accessorErrorID = device.ID("accessorError")
   299  		accessorError   = errors.New("expected accessor error")
   300  
   301  		expectedNodes   = []string{keepNode, rehashNode}
   302  		accessorFactory = service.AccessorFactory(func(actualNodes []string) service.Accessor {
   303  			assert.Equal(expectedNodes, actualNodes)
   304  			return service.AccessorFunc(func(key []byte) (string, error) {
   305  				switch string(key) {
   306  				case string(keepID):
   307  					return keepNode, nil
   308  
   309  				case string(rehashedID):
   310  					return rehashNode, nil
   311  
   312  				case string(accessorErrorID):
   313  					return "", accessorError
   314  
   315  				default:
   316  					assert.Fail("Invalid accessor key")
   317  					return "", errors.New("test failure: invalid accessor key")
   318  				}
   319  			})
   320  		})
   321  
   322  		isRegistered = func(v string) bool {
   323  			return keepNode == v
   324  		}
   325  
   326  		expectedDuration = 10 * time.Minute
   327  		start            = time.Now()
   328  		end              = start.Add(expectedDuration)
   329  		nowFirst         = true
   330  		now              = func() time.Time {
   331  			if nowFirst {
   332  				nowFirst = false
   333  				return start
   334  			}
   335  
   336  			return end
   337  		}
   338  
   339  		connector = new(device.MockConnector)
   340  		r         = New(
   341  			connector,
   342  			[]string{"talaria", "caduceus"},
   343  			WithLogger(logging.NewTestLogger(nil, t)),
   344  			WithIsRegistered(isRegistered),
   345  			WithAccessorFactory(accessorFactory),
   346  			WithMetricsProvider(provider),
   347  		)
   348  	)
   349  
   350  	require.NotNil(r)
   351  	r.(*rehasher).now = now
   352  	connector.On("DisconnectIf", mock.MatchedBy(
   353  		func(func(device.ID) (device.CloseReason, bool)) bool { return true },
   354  	)).
   355  		Run(func(arguments mock.Arguments) {
   356  			f := arguments.Get(0).(func(device.ID) (device.CloseReason, bool))
   357  
   358  			reason, closed := f(keepID)
   359  			assert.Equal(device.CloseReason{}, reason)
   360  			assert.False(closed)
   361  
   362  			reason, closed = f(rehashedID)
   363  			assert.Equal(device.CloseReason{Text: RehashOtherInstance}, reason)
   364  			assert.True(closed)
   365  
   366  			reason, closed = f(accessorErrorID)
   367  			assert.Equal(device.CloseReason{Err: accessorError, Text: RehashError}, reason)
   368  			assert.True(closed)
   369  		}).
   370  		Return(2)
   371  
   372  	provider.Expect(RehashKeepDevice, service.ServiceLabel, "caduceus")(xmetricstest.Gauge, xmetricstest.Value(1.0))
   373  	provider.Expect(RehashDisconnectDevice, service.ServiceLabel, "caduceus")(xmetricstest.Gauge, xmetricstest.Value(2.0))
   374  	provider.Expect(RehashDisconnectAllCounter, service.ServiceLabel, "caduceus")(xmetricstest.Counter, xmetricstest.Value(0.0))
   375  	provider.Expect(RehashTimestamp, service.ServiceLabel, "caduceus")(
   376  		xmetricstest.Gauge,
   377  		xmetricstest.Value(float64(start.UTC().Unix())),
   378  	)
   379  	provider.Expect(RehashDurationMilliseconds, service.ServiceLabel, "caduceus")(
   380  		xmetricstest.Gauge,
   381  		xmetricstest.Value(float64(expectedDuration/time.Millisecond)),
   382  	)
   383  
   384  	r.MonitorEvent(monitor.Event{Key: "test", Service: "caduceus", EventCount: 10, Instances: expectedNodes})
   385  
   386  	connector.AssertExpectations(t)
   387  	provider.AssertExpectations(t)
   388  }
   389  
   390  func TestRehasher(t *testing.T) {
   391  	t.Run("ServiceDiscoveryError", testRehasherServiceDiscoveryError)
   392  	t.Run("ServiceDiscoveryStopped", testRehasherServiceDiscoveryStopped)
   393  	t.Run("InitialEvent", testRehasherInitialEvent)
   394  	t.Run("NoInstances", testRehasherNoInstances)
   395  	t.Run("Rehash", testRehasherRehash)
   396  	t.Run("SkippedServicee", testRehasherSkippedService)
   397  }