Codebase list golang-github-gocql-gocql / HEAD token_test.go
HEAD

Tree @HEAD (Download .tar.gz)

token_test.go @HEAD

be90d7c
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
68df836
 
be90d7c
 
 
 
68df836
be90d7c
 
 
 
 
 
 
68df836
 
 
 
 
be90d7c
 
 
 
 
68df836
be90d7c
 
68df836
be90d7c
 
68df836
be90d7c
 
68df836
be90d7c
 
68df836
be90d7c
 
68df836
be90d7c
 
68df836
be90d7c
 
68df836
be90d7c
 
68df836
be90d7c
 
68df836
be90d7c
 
68df836
be90d7c
 
68df836
be90d7c
 
68df836
be90d7c
 
 
 
 
68df836
be90d7c
 
68df836
be90d7c
 
 
 
 
68df836
be90d7c
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
68df836
be90d7c
 
 
 
 
 
 
 
 
 
68df836
be90d7c
 
 
 
 
 
68df836
be90d7c
 
 
 
68df836
be90d7c
 
 
 
 
 
68df836
be90d7c
 
 
 
 
 
 
 
 
 
 
 
68df836
be90d7c
 
 
 
 
 
68df836
be90d7c
 
 
 
68df836
be90d7c
 
 
 
 
 
68df836
be90d7c
 
 
 
 
 
 
 
 
 
 
68df836
be90d7c
 
 
 
 
 
68df836
be90d7c
 
 
 
68df836
be90d7c
 
 
 
// Copyright (c) 2015 The gocql Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package gocql

import (
	"bytes"
	"fmt"
	"math/big"
	"net"
	"sort"
	"strconv"
	"testing"
)

// Tests of the murmur3Patitioner
func TestMurmur3Partitioner(t *testing.T) {
	token := murmur3Partitioner{}.ParseString("-1053604476080545076")

	if "-1053604476080545076" != token.String() {
		t.Errorf("Expected '-1053604476080545076' but was '%s'", token)
	}

	// at least verify that the partitioner
	// doesn't return nil
	pk, _ := marshalInt(nil, 1)
	token = murmur3Partitioner{}.Hash(pk)
	if token == nil {
		t.Fatal("token was nil")
	}
}

// Tests of the murmur3Token
func TestMurmur3Token(t *testing.T) {
	if murmur3Token(42).Less(murmur3Token(42)) {
		t.Errorf("Expected Less to return false, but was true")
	}
	if !murmur3Token(-42).Less(murmur3Token(42)) {
		t.Errorf("Expected Less to return true, but was false")
	}
	if murmur3Token(42).Less(murmur3Token(-42)) {
		t.Errorf("Expected Less to return false, but was true")
	}
}

// Tests of the orderedPartitioner
func TestOrderedPartitioner(t *testing.T) {
	// at least verify that the partitioner
	// doesn't return nil
	p := orderedPartitioner{}
	pk, _ := marshalInt(nil, 1)
	token := p.Hash(pk)
	if token == nil {
		t.Fatal("token was nil")
	}

	str := token.String()
	parsedToken := p.ParseString(str)

	if !bytes.Equal([]byte(token.(orderedToken)), []byte(parsedToken.(orderedToken))) {
		t.Errorf("Failed to convert to and from a string %s expected %x but was %x",
			str,
			[]byte(token.(orderedToken)),
			[]byte(parsedToken.(orderedToken)),
		)
	}
}

// Tests of the orderedToken
func TestOrderedToken(t *testing.T) {
	if orderedToken([]byte{0, 0, 4, 2}).Less(orderedToken([]byte{0, 0, 4, 2})) {
		t.Errorf("Expected Less to return false, but was true")
	}
	if !orderedToken([]byte{0, 0, 3}).Less(orderedToken([]byte{0, 0, 4, 2})) {
		t.Errorf("Expected Less to return true, but was false")
	}
	if orderedToken([]byte{0, 0, 4, 2}).Less(orderedToken([]byte{0, 0, 3})) {
		t.Errorf("Expected Less to return false, but was true")
	}
}

// Tests of the randomPartitioner
func TestRandomPartitioner(t *testing.T) {
	// at least verify that the partitioner
	// doesn't return nil
	p := randomPartitioner{}
	pk, _ := marshalInt(nil, 1)
	token := p.Hash(pk)
	if token == nil {
		t.Fatal("token was nil")
	}

	str := token.String()
	parsedToken := p.ParseString(str)

	if (*big.Int)(token.(*randomToken)).Cmp((*big.Int)(parsedToken.(*randomToken))) != 0 {
		t.Errorf("Failed to convert to and from a string %s expected %v but was %v",
			str,
			token,
			parsedToken,
		)
	}
}

func TestRandomPartitionerMatchesReference(t *testing.T) {
	// example taken from datastax python driver
	//    >>> from cassandra.metadata import MD5Token
	//    >>> MD5Token.hash_fn("test")
	//    12707736894140473154801792860916528374L
	var p randomPartitioner
	expect := "12707736894140473154801792860916528374"
	actual := p.Hash([]byte("test")).String()
	if actual != expect {
		t.Errorf("expected random partitioner to generate tokens in the same way as the reference"+
			" python client. Expected %s, but got %s", expect, actual)
	}
}

// Tests of the randomToken
func TestRandomToken(t *testing.T) {
	if ((*randomToken)(big.NewInt(42))).Less((*randomToken)(big.NewInt(42))) {
		t.Errorf("Expected Less to return false, but was true")
	}
	if !((*randomToken)(big.NewInt(41))).Less((*randomToken)(big.NewInt(42))) {
		t.Errorf("Expected Less to return true, but was false")
	}
	if ((*randomToken)(big.NewInt(42))).Less((*randomToken)(big.NewInt(41))) {
		t.Errorf("Expected Less to return false, but was true")
	}
}

type intToken int

func (i intToken) String() string        { return strconv.Itoa(int(i)) }
func (i intToken) Less(token token) bool { return i < token.(intToken) }

// Test of the token ring implementation based on example at the start of this
// page of documentation:
// http://www.datastax.com/docs/0.8/cluster_architecture/partitioning
func TestTokenRing_Int(t *testing.T) {
	host0 := &HostInfo{}
	host25 := &HostInfo{}
	host50 := &HostInfo{}
	host75 := &HostInfo{}
	ring := &tokenRing{
		partitioner: nil,
		// these tokens and hosts are out of order to test sorting
		tokens: []hostToken{
			{intToken(0), host0},
			{intToken(50), host50},
			{intToken(75), host75},
			{intToken(25), host25},
		},
	}

	sort.Sort(ring)

	if host, endToken := ring.GetHostForToken(intToken(0)); host != host0 || endToken != intToken(0) {
		t.Error("Expected host 0 for token 0")
	}
	if host, endToken := ring.GetHostForToken(intToken(1)); host != host25 || endToken != intToken(25) {
		t.Error("Expected host 25 for token 1")
	}
	if host, endToken := ring.GetHostForToken(intToken(24)); host != host25 || endToken != intToken(25) {
		t.Error("Expected host 25 for token 24")
	}
	if host, endToken := ring.GetHostForToken(intToken(25)); host != host25 || endToken != intToken(25) {
		t.Error("Expected host 25 for token 25")
	}
	if host, endToken := ring.GetHostForToken(intToken(26)); host != host50 || endToken != intToken(50) {
		t.Error("Expected host 50 for token 26")
	}
	if host, endToken := ring.GetHostForToken(intToken(49)); host != host50 || endToken != intToken(50) {
		t.Error("Expected host 50 for token 49")
	}
	if host, endToken := ring.GetHostForToken(intToken(50)); host != host50 || endToken != intToken(50) {
		t.Error("Expected host 50 for token 50")
	}
	if host, endToken := ring.GetHostForToken(intToken(51)); host != host75 || endToken != intToken(75) {
		t.Error("Expected host 75 for token 51")
	}
	if host, endToken := ring.GetHostForToken(intToken(74)); host != host75 || endToken != intToken(75) {
		t.Error("Expected host 75 for token 74")
	}
	if host, endToken := ring.GetHostForToken(intToken(75)); host != host75 || endToken != intToken(75) {
		t.Error("Expected host 75 for token 75")
	}
	if host, endToken := ring.GetHostForToken(intToken(76)); host != host0 || endToken != intToken(0) {
		t.Error("Expected host 0 for token 76")
	}
	if host, endToken := ring.GetHostForToken(intToken(99)); host != host0 || endToken != intToken(0) {
		t.Error("Expected host 0 for token 99")
	}
	if host, endToken := ring.GetHostForToken(intToken(100)); host != host0 || endToken != intToken(0) {
		t.Error("Expected host 0 for token 100")
	}
}

// Test for the behavior of a nil pointer to tokenRing
func TestTokenRing_Nil(t *testing.T) {
	var ring *tokenRing = nil

	if host, endToken := ring.GetHostForToken(nil); host != nil || endToken != nil {
		t.Error("Expected nil for nil token ring")
	}
}

// Test of the recognition of the partitioner class
func TestTokenRing_UnknownPartition(t *testing.T) {
	_, err := newTokenRing("UnknownPartitioner", nil)
	if err == nil {
		t.Error("Expected error for unknown partitioner value, but was nil")
	}
}

func hostsForTests(n int) []*HostInfo {
	hosts := make([]*HostInfo, n)
	for i := 0; i < n; i++ {
		host := &HostInfo{
			connectAddress: net.IPv4(1, 1, 1, byte(n)),
			tokens:         []string{fmt.Sprintf("%d", n)},
		}

		hosts[i] = host
	}
	return hosts
}

// Test of the tokenRing with the Murmur3Partitioner
func TestTokenRing_Murmur3(t *testing.T) {
	// Note, strings are parsed directly to int64, they are not murmur3 hashed
	hosts := hostsForTests(4)
	ring, err := newTokenRing("Murmur3Partitioner", hosts)
	if err != nil {
		t.Fatalf("Failed to create token ring due to error: %v", err)
	}

	p := murmur3Partitioner{}

	for _, host := range hosts {
		actual, _ := ring.GetHostForToken(p.ParseString(host.tokens[0]))
		if !actual.ConnectAddress().Equal(host.ConnectAddress()) {
			t.Errorf("Expected address %v for token %q, but was %v", host.ConnectAddress(),
				host.tokens[0], actual.ConnectAddress())
		}
	}

	actual, _ := ring.GetHostForToken(p.ParseString("12"))
	if !actual.ConnectAddress().Equal(hosts[1].ConnectAddress()) {
		t.Errorf("Expected address 1 for token \"12\", but was %s", actual.ConnectAddress())
	}

	actual, _ = ring.GetHostForToken(p.ParseString("24324545443332"))
	if !actual.ConnectAddress().Equal(hosts[0].ConnectAddress()) {
		t.Errorf("Expected address 0 for token \"24324545443332\", but was %s", actual.ConnectAddress())
	}
}

// Test of the tokenRing with the OrderedPartitioner
func TestTokenRing_Ordered(t *testing.T) {
	// Tokens here more or less are similar layout to the int tokens above due
	// to each numeric character translating to a consistently offset byte.
	hosts := hostsForTests(4)
	ring, err := newTokenRing("OrderedPartitioner", hosts)
	if err != nil {
		t.Fatalf("Failed to create token ring due to error: %v", err)
	}

	p := orderedPartitioner{}

	var actual *HostInfo
	for _, host := range hosts {
		actual, _ := ring.GetHostForToken(p.ParseString(host.tokens[0]))
		if !actual.ConnectAddress().Equal(host.ConnectAddress()) {
			t.Errorf("Expected address %v for token %q, but was %v", host.ConnectAddress(),
				host.tokens[0], actual.ConnectAddress())
		}
	}

	actual, _ = ring.GetHostForToken(p.ParseString("12"))
	if !actual.peer.Equal(hosts[1].peer) {
		t.Errorf("Expected address 1 for token \"12\", but was %s", actual.ConnectAddress())
	}

	actual, _ = ring.GetHostForToken(p.ParseString("24324545443332"))
	if !actual.ConnectAddress().Equal(hosts[1].ConnectAddress()) {
		t.Errorf("Expected address 1 for token \"24324545443332\", but was %s", actual.ConnectAddress())
	}
}

// Test of the tokenRing with the RandomPartitioner
func TestTokenRing_Random(t *testing.T) {
	// String tokens are parsed into big.Int in base 10
	hosts := hostsForTests(4)
	ring, err := newTokenRing("RandomPartitioner", hosts)
	if err != nil {
		t.Fatalf("Failed to create token ring due to error: %v", err)
	}

	p := randomPartitioner{}

	var actual *HostInfo
	for _, host := range hosts {
		actual, _ := ring.GetHostForToken(p.ParseString(host.tokens[0]))
		if !actual.ConnectAddress().Equal(host.ConnectAddress()) {
			t.Errorf("Expected address %v for token %q, but was %v", host.ConnectAddress(),
				host.tokens[0], actual.ConnectAddress())
		}
	}

	actual, _ = ring.GetHostForToken(p.ParseString("12"))
	if !actual.peer.Equal(hosts[1].peer) {
		t.Errorf("Expected address 1 for token \"12\", but was %s", actual.ConnectAddress())
	}

	actual, _ = ring.GetHostForToken(p.ParseString("24324545443332"))
	if !actual.ConnectAddress().Equal(hosts[0].ConnectAddress()) {
		t.Errorf("Expected address 1 for token \"24324545443332\", but was %s", actual.ConnectAddress())
	}
}