Skip to content

Commit

Permalink
Merge pull request #51 from xmidt-org/chore/unit-tests
Browse files Browse the repository at this point in the history
Chore/unit tests
  • Loading branch information
johnabass authored Dec 26, 2024
2 parents 72469ab + f842432 commit edc1faa
Show file tree
Hide file tree
Showing 2 changed files with 209 additions and 0 deletions.
84 changes: 84 additions & 0 deletions consistent/builder_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
package consistent

import (
"hash/fnv"
"sort"
"testing"

"github.com/stretchr/testify/suite"
"github.com/xmidt-org/medley"
)

type BuilderSuite struct {
suite.Suite

object []byte
}

func (suite *BuilderSuite) SetupTest() {
suite.object = []byte("test value")
}

func (suite *BuilderSuite) testStringsDefault() {
services := []string{"service1", "service2", "service3"}
ring := Strings(services...).Build()
suite.Require().NotNil(ring)
suite.Require().True(sort.IsSorted(ring.nodes))

result, err := ring.Find(suite.object)
suite.NoError(err)
suite.Contains(services, result)
}

func (suite *BuilderSuite) testStringsCustom() {
services := []string{"service1", "service2", "service3", "additional1", "additional2"}
ring := Strings(services[:3]...).
VNodes(100).
Services(services[3:]...).
Algorithm(medley.Algorithm{New64: fnv.New64}).
ServiceHasher(nil). // force the default
Build()

suite.Require().NotNil(ring)
suite.Require().True(sort.IsSorted(ring.nodes))

result, err := ring.Find(suite.object)
suite.NoError(err)
suite.Contains(services, result)
}

func (suite *BuilderSuite) TestStrings() {
suite.Run("Default", suite.testStringsDefault)
suite.Run("Custom", suite.testStringsCustom)
}

func (suite *BuilderSuite) TestServices() {
services := []string{"service1", "service2", "service3"}
ring := Services(services...).Build()
suite.Require().NotNil(ring)
suite.Require().True(sort.IsSorted(ring.nodes))

result, err := ring.Find(suite.object)
suite.NoError(err)
suite.Contains(services, result)
}

func (suite *BuilderSuite) TestBasicServices() {
services := []medley.BasicService{
{Host: "service1.net"},
{Host: "service2.net", Port: 8080},
{Host: "service3.net", Path: "/foo/bar"},
}

ring := BasicServices(services...).Build()
suite.Require().NotNil(ring)
suite.Require().True(sort.IsSorted(ring.nodes))

result, err := ring.Find(suite.object)
suite.NoError(err)
suite.Contains(services, result)
}

func TestBuilder(t *testing.T) {
suite.Run(t, new(BuilderSuite))
}
125 changes: 125 additions & 0 deletions consistent/ring_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
package consistent

import (
"math/rand"
"sort"
"testing"

"github.com/stretchr/testify/suite"
"github.com/xmidt-org/medley"
)

const (
// objectSeed is the random number seed we use to create objects to hash
objectSeed int64 = 7245298734452934458

// objectCount is the number of random objects we generate for hash inputs
objectCount int = 1000
)

type RingSuite struct {
suite.Suite

rand *rand.Rand
objects [objectCount][16]byte

originalServices []string
original *Ring[string]
}

func (suite *RingSuite) SetupSuite() {
suite.rand = rand.New(

Check failure on line 31 in consistent/ring_test.go

View workflow job for this annotation

GitHub Actions / ci / Go Lint

G404: Use of weak random number generator (math/rand or math/rand/v2 instead of crypto/rand) (gosec)
rand.NewSource(objectSeed),
)

for i := 0; i < len(suite.objects); i++ {
suite.rand.Read(suite.objects[i][:])
}

suite.originalServices = []string{
"original1.service.net", "original2.service.net", "original3.service.net", "original4.service.net",
}

suite.original = Strings(suite.originalServices...).Build()
suite.Require().NotNil(suite.original)
suite.Require().True(sort.IsSorted(suite.original.nodes))

distribution := make(map[string]int)
for _, object := range suite.objects {
result, err := suite.original.Find(object[:])
suite.Require().NoError(err)
suite.Require().Contains(suite.originalServices, result)
distribution[result] += 1
}

// the distribution should be close to even
expectedCount := objectCount / len(suite.originalServices)
for _, actualCount := range distribution {
// each count should be within 25% of its expected value.
// 25% is just a guess, but it should prevent drift as
// the codebase changes.
suite.InEpsilon(expectedCount, actualCount, 0.25)
}
}

func (suite *RingSuite) update(services ...string) (*Ring[string], bool) {
updated, didUpdate := Update(suite.original, services...)
suite.Require().NotNil(updated)
suite.Require().True(sort.IsSorted(updated.nodes))

return updated, didUpdate
}

func (suite *RingSuite) testUpdateEmpty() {
// the list of updated services is empty
updated, didUpdate := suite.update()
suite.True(didUpdate)
suite.Empty(updated.nodes)

for _, object := range suite.objects {
result, err := updated.Find(object[:])
suite.Empty(result)
suite.ErrorIs(err, medley.ErrNoServices)
}
}

func (suite *RingSuite) testUpdatePartial() {
partial := []string{"new1", suite.originalServices[0], "new2"}
updated, didUpdate := suite.update(partial...)
suite.True(didUpdate)

for _, object := range suite.objects {
result, err := updated.Find(object[:])
suite.Contains(partial, result)
suite.NoError(err)
}
}

func (suite *RingSuite) testUpdateAllNew() {
allNew := []string{"new1.service.com", "new2.service.com", "new3.service.com", "new4.service.com"}
updated, didUpdate := suite.update(allNew...)
suite.True(didUpdate)

for _, object := range suite.objects {
result, err := updated.Find(object[:])
suite.Contains(allNew, result)
suite.NoError(err)
}
}

func (suite *RingSuite) testUpdateNotNeeded() {
updated, didUpdate := suite.update(suite.originalServices...)
suite.Same(suite.original, updated)
suite.False(didUpdate)
}

func (suite *RingSuite) TestUpdate() {
suite.Run("Empty", suite.testUpdateEmpty)
suite.Run("Partial", suite.testUpdatePartial)
suite.Run("AllNew", suite.testUpdateAllNew)
suite.Run("NotNeeded", suite.testUpdateNotNeeded)
}

func TestRing(t *testing.T) {
suite.Run(t, new(RingSuite))
}

0 comments on commit edc1faa

Please sign in to comment.