Revision c74df714d4f603884788205f46ac73d5beca813b authored by William Banfield on 21 March 2022, 20:37:37 UTC, committed by William Banfield on 21 March 2022, 20:37:37 UTC
1 parent 1d2858f
Raw File
node_info_test.go
package types

import (
	"fmt"
	"testing"

	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"

	"github.com/tendermint/tendermint/crypto/ed25519"
	tmnet "github.com/tendermint/tendermint/libs/net"
	"github.com/tendermint/tendermint/version"
)

const testCh = 0x01

func TestNodeInfoValidate(t *testing.T) {

	// empty fails
	ni := NodeInfo{}
	assert.Error(t, ni.Validate())

	channels := make([]byte, maxNumChannels)
	for i := 0; i < maxNumChannels; i++ {
		channels[i] = byte(i)
	}
	dupChannels := make([]byte, 5)
	copy(dupChannels, channels[:5])
	dupChannels = append(dupChannels, testCh)

	nonASCII := "¢§µ"
	emptyTab := "\t"
	emptySpace := "  "

	testCases := []struct {
		testName         string
		malleateNodeInfo func(*NodeInfo)
		expectErr        bool
	}{
		{
			"Too Many Channels",
			func(ni *NodeInfo) { ni.Channels = append(channels, byte(maxNumChannels)) }, // nolint: gocritic
			true,
		},
		{"Duplicate Channel", func(ni *NodeInfo) { ni.Channels = dupChannels }, true},
		{"Good Channels", func(ni *NodeInfo) { ni.Channels = ni.Channels[:5] }, false},

		{"Invalid NetAddress", func(ni *NodeInfo) { ni.ListenAddr = "not-an-address" }, true},
		{"Good NetAddress", func(ni *NodeInfo) { ni.ListenAddr = "0.0.0.0:26656" }, false},

		{"Non-ASCII Version", func(ni *NodeInfo) { ni.Version = nonASCII }, true},
		{"Empty tab Version", func(ni *NodeInfo) { ni.Version = emptyTab }, true},
		{"Empty space Version", func(ni *NodeInfo) { ni.Version = emptySpace }, true},
		{"Empty Version", func(ni *NodeInfo) { ni.Version = "" }, false},

		{"Non-ASCII Moniker", func(ni *NodeInfo) { ni.Moniker = nonASCII }, true},
		{"Empty tab Moniker", func(ni *NodeInfo) { ni.Moniker = emptyTab }, true},
		{"Empty space Moniker", func(ni *NodeInfo) { ni.Moniker = emptySpace }, true},
		{"Empty Moniker", func(ni *NodeInfo) { ni.Moniker = "" }, true},
		{"Good Moniker", func(ni *NodeInfo) { ni.Moniker = "hey its me" }, false},

		{"Non-ASCII TxIndex", func(ni *NodeInfo) { ni.Other.TxIndex = nonASCII }, true},
		{"Empty tab TxIndex", func(ni *NodeInfo) { ni.Other.TxIndex = emptyTab }, true},
		{"Empty space TxIndex", func(ni *NodeInfo) { ni.Other.TxIndex = emptySpace }, true},
		{"Empty TxIndex", func(ni *NodeInfo) { ni.Other.TxIndex = "" }, false},
		{"Off TxIndex", func(ni *NodeInfo) { ni.Other.TxIndex = "off" }, false},

		{"Non-ASCII RPCAddress", func(ni *NodeInfo) { ni.Other.RPCAddress = nonASCII }, true},
		{"Empty tab RPCAddress", func(ni *NodeInfo) { ni.Other.RPCAddress = emptyTab }, true},
		{"Empty space RPCAddress", func(ni *NodeInfo) { ni.Other.RPCAddress = emptySpace }, true},
		{"Empty RPCAddress", func(ni *NodeInfo) { ni.Other.RPCAddress = "" }, false},
		{"Good RPCAddress", func(ni *NodeInfo) { ni.Other.RPCAddress = "0.0.0.0:26657" }, false},
	}

	nodeKeyID := testNodeID()
	name := "testing"

	// test case passes
	ni = testNodeInfo(t, nodeKeyID, name)
	ni.Channels = channels
	assert.NoError(t, ni.Validate())

	for _, tc := range testCases {
		t.Run(tc.testName, func(t *testing.T) {
			ni := testNodeInfo(t, nodeKeyID, name)
			ni.Channels = channels
			tc.malleateNodeInfo(&ni)
			err := ni.Validate()
			if tc.expectErr {
				assert.Error(t, err, tc.testName)
			} else {
				assert.NoError(t, err, tc.testName)
			}
		})

	}

}

func testNodeID() NodeID {
	return NodeIDFromPubKey(ed25519.GenPrivKey().PubKey())
}

func testNodeInfo(t *testing.T, id NodeID, name string) NodeInfo {
	return testNodeInfoWithNetwork(t, id, name, "testing")
}

func testNodeInfoWithNetwork(t *testing.T, id NodeID, name, network string) NodeInfo {
	t.Helper()
	return NodeInfo{
		ProtocolVersion: ProtocolVersion{
			P2P:   version.P2PProtocol,
			Block: version.BlockProtocol,
			App:   0,
		},
		NodeID:     id,
		ListenAddr: fmt.Sprintf("127.0.0.1:%d", getFreePort(t)),
		Network:    network,
		Version:    "1.2.3-rc0-deadbeef",
		Channels:   []byte{testCh},
		Moniker:    name,
		Other: NodeInfoOther{
			TxIndex:    "on",
			RPCAddress: fmt.Sprintf("127.0.0.1:%d", getFreePort(t)),
		},
	}
}

func getFreePort(t *testing.T) int {
	t.Helper()
	port, err := tmnet.GetFreePort()
	require.NoError(t, err)
	return port
}

func TestNodeInfoCompatible(t *testing.T) {
	nodeKey1ID := testNodeID()
	nodeKey2ID := testNodeID()
	name := "testing"

	var newTestChannel byte = 0x2

	// test NodeInfo is compatible
	ni1 := testNodeInfo(t, nodeKey1ID, name)
	ni2 := testNodeInfo(t, nodeKey2ID, name)
	assert.NoError(t, ni1.CompatibleWith(ni2))

	// add another channel; still compatible
	ni2.Channels = []byte{newTestChannel, testCh}
	assert.NoError(t, ni1.CompatibleWith(ni2))

	testCases := []struct {
		testName         string
		malleateNodeInfo func(*NodeInfo)
	}{
		{"Wrong block version", func(ni *NodeInfo) { ni.ProtocolVersion.Block++ }},
		{"Wrong network", func(ni *NodeInfo) { ni.Network += "-wrong" }},
		{"No common channels", func(ni *NodeInfo) { ni.Channels = []byte{newTestChannel} }},
	}

	for _, tc := range testCases {
		ni := testNodeInfo(t, nodeKey2ID, name)
		tc.malleateNodeInfo(&ni)
		assert.Error(t, ni1.CompatibleWith(ni))
	}
}

func TestNodeInfoAddChannel(t *testing.T) {
	nodeInfo := testNodeInfo(t, testNodeID(), "testing")
	nodeInfo.Channels = []byte{}
	require.Empty(t, nodeInfo.Channels)

	nodeInfo.AddChannel(2)
	require.Contains(t, nodeInfo.Channels, byte(0x02))

	// adding the same channel again shouldn't be a problem
	nodeInfo.AddChannel(2)
	require.Contains(t, nodeInfo.Channels, byte(0x02))
}

func TestParseAddressString(t *testing.T) {
	testCases := []struct {
		name     string
		addr     string
		expected string
		correct  bool
	}{
		{"no node id and no protocol", "127.0.0.1:8080", "", false},
		{"no node id w/ tcp input", "tcp://127.0.0.1:8080", "", false},
		{"no node id w/ udp input", "udp://127.0.0.1:8080", "", false},

		{
			"no protocol",
			"deadbeefdeadbeefdeadbeefdeadbeefdeadbeef@127.0.0.1:8080",
			"deadbeefdeadbeefdeadbeefdeadbeefdeadbeef@127.0.0.1:8080",
			true,
		},
		{
			"tcp input",
			"tcp://deadbeefdeadbeefdeadbeefdeadbeefdeadbeef@127.0.0.1:8080",
			"deadbeefdeadbeefdeadbeefdeadbeefdeadbeef@127.0.0.1:8080",
			true,
		},
		{
			"udp input",
			"udp://deadbeefdeadbeefdeadbeefdeadbeefdeadbeef@127.0.0.1:8080",
			"deadbeefdeadbeefdeadbeefdeadbeefdeadbeef@127.0.0.1:8080",
			true,
		},
		{"malformed tcp input", "tcp//deadbeefdeadbeefdeadbeefdeadbeefdeadbeef@127.0.0.1:8080", "", false},
		{"malformed udp input", "udp//deadbeefdeadbeefdeadbeefdeadbeefdeadbeef@127.0.0.1:8080", "", false},

		// {"127.0.0:8080", false},
		{"invalid host", "notahost", "", false},
		{"invalid port", "127.0.0.1:notapath", "", false},
		{"invalid host w/ port", "notahost:8080", "", false},
		{"just a port", "8082", "", false},
		{"non-existent port", "127.0.0:8080000", "", false},

		{"too short nodeId", "deadbeef@127.0.0.1:8080", "", false},
		{"too short, not hex nodeId", "this-isnot-hex@127.0.0.1:8080", "", false},
		{"not hex nodeId", "xxxxbeefdeadbeefdeadbeefdeadbeefdeadbeef@127.0.0.1:8080", "", false},

		{"too short nodeId w/tcp", "tcp://deadbeef@127.0.0.1:8080", "", false},
		{"too short notHex nodeId w/tcp", "tcp://this-isnot-hex@127.0.0.1:8080", "", false},
		{"notHex nodeId w/tcp", "tcp://xxxxbeefdeadbeefdeadbeefdeadbeefdeadbeef@127.0.0.1:8080", "", false},
		{
			"correct nodeId w/tcp",
			"tcp://deadbeefdeadbeefdeadbeefdeadbeefdeadbeef@127.0.0.1:8080",
			"deadbeefdeadbeefdeadbeefdeadbeefdeadbeef@127.0.0.1:8080",
			true,
		},

		{"no node id", "tcp://@127.0.0.1:8080", "", false},
		{"no node id or IP", "tcp://@", "", false},
		{"tcp no host, w/ port", "tcp://:26656", "", false},
		{"empty", "", "", false},
		{"node id delimiter 1", "@", "", false},
		{"node id delimiter 2", " @", "", false},
		{"node id delimiter 3", " @ ", "", false},
	}

	for _, tc := range testCases {
		tc := tc
		t.Run(tc.name, func(t *testing.T) {
			addr, port, err := ParseAddressString(tc.addr)
			if tc.correct {
				require.NoError(t, err, tc.addr)
				assert.Contains(t, tc.expected, addr.String())
				assert.Contains(t, tc.expected, fmt.Sprint(port))
			} else {
				assert.Error(t, err, "%v", tc.addr)
			}
		})
	}
}
back to top