Files
netbird/client/status/table_test.go
T

177 lines
5.3 KiB
Go

package status
import (
"strings"
"testing"
"time"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
protobuf "google.golang.org/protobuf/proto"
"github.com/netbirdio/netbird/client/anonymize"
"github.com/netbirdio/netbird/client/proto"
)
func TestPeersTable(t *testing.T) {
overview := OutputOverview{Peers: PeersStateOutput{
Total: 2,
Connected: 1,
Details: []PeerStateDetailOutput{
{
FQDN: "web-01.netbird.cloud",
IP: "100.64.0.12",
PubKey: "k3Vx9pQ2abcdefghijklmnopqrstuvwxyz0123456789=",
Status: "Connected",
ConnType: "P2P",
Latency: 12 * time.Millisecond,
TransferReceived: 1024,
TransferSent: 2048,
},
{
FQDN: "old-nas.netbird.cloud",
IP: "100.64.0.77",
Status: "Idle",
},
},
}}
lines := strings.Split(strings.TrimRight(overview.PeersTable(), "\n"), "\n")
require.Len(t, lines, 4, "title, header and one line per peer")
assert.Equal(t, "Peers (1/2 connected):", lines[0])
assert.Equal(t,
[]string{"NAME", "IP", "STATUS", "CONN", "LATENCY", "RX", "/", "TX"},
strings.Fields(lines[1]))
connected := strings.Fields(lines[2])
assert.Equal(t, "web-01", connected[0], "domain suffix must be dropped")
assert.Equal(t, "Connected", connected[2])
assert.Equal(t, "P2P", connected[3])
assert.Equal(t, "12ms", connected[4])
assert.NotContains(t, lines[2], "k3Vx9pQ2", "public key must not be shown")
assert.Contains(t, lines[2], "1.0 KiB / 2.0 KiB")
idle := strings.Fields(lines[3])
assert.Equal(t, []string{"old-nas", "100.64.0.77", "Idle", "-", "-", "0", "B", "/", "0", "B"}, idle,
"missing values render as dashes")
}
func TestPeersTable_NoPeers(t *testing.T) {
out := (&OutputOverview{}).PeersTable()
assert.True(t, strings.HasPrefix(out, "Peers (0/0 connected):\n"))
assert.Contains(t, out, "NAME")
}
func tableRows(t *testing.T, opts ConvertOptions) []string {
t.Helper()
opts.DaemonVersion = resp.GetDaemonVersion()
opts.DaemonStatus = ParseDaemonStatus(resp.GetStatus())
// The anonymizer rewrites slices in place, so work on a copy to keep the
// shared fixture intact for the other tests.
fullStatus := protobuf.Clone(resp.GetFullStatus()).(*proto.FullStatus)
overview := ConvertToStatusOutputOverview(fullStatus, opts)
out := overview.PeersTable()
lines := strings.Split(strings.TrimRight(out, "\n"), "\n")
require.GreaterOrEqual(t, len(lines), 2, "title and header are always present")
return lines
}
// The fixture holds peer-1 (connected, P2P) and peer-2 (connected, relayed).
func TestPeersTable_Filters(t *testing.T) {
tests := []struct {
name string
opts ConvertOptions
wantTitle string
wantPeers []string
}{
{
name: "no filter lists every peer sorted by IP",
wantTitle: "Peers (2/2 connected):",
wantPeers: []string{"peer-1", "peer-2"},
},
{
name: "connection type P2P",
opts: ConvertOptions{ConnectionTypeFilter: "P2P"},
wantTitle: "Peers (1/1 connected):",
wantPeers: []string{"peer-1"},
},
{
name: "connection type Relayed",
opts: ConvertOptions{ConnectionTypeFilter: "relayed"},
wantTitle: "Peers (1/1 connected):",
wantPeers: []string{"peer-2"},
},
{
name: "status with no match",
opts: ConvertOptions{StatusFilter: "idle"},
wantTitle: "Peers (0/0 connected):",
},
{
name: "name prefix",
opts: ConvertOptions{
PrefixNamesFilter: []string{"peer-2"},
PrefixNamesFilterMap: map[string]struct{}{"peer-2": {}},
},
wantTitle: "Peers (1/1 connected):",
wantPeers: []string{"peer-2"},
},
{
name: "IPv4",
opts: ConvertOptions{IPsFilter: map[string]struct{}{"192.168.178.101": {}}},
wantTitle: "Peers (1/1 connected):",
wantPeers: []string{"peer-1"},
},
{
name: "IPv6",
opts: ConvertOptions{IPsFilter: map[string]struct{}{"fd00::1": {}}},
wantTitle: "Peers (1/1 connected):",
wantPeers: []string{"peer-1"},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
lines := tableRows(t, tt.opts)
assert.Equal(t, tt.wantTitle, lines[0])
rows := lines[2:]
require.Len(t, rows, len(tt.wantPeers), "one row per matching peer")
for i, name := range tt.wantPeers {
assert.True(t, strings.HasPrefix(rows[i], name+" "), "row %d should be %s, got %q", i, name, rows[i])
}
})
}
}
// The table shows whatever the anonymizer left in the overview, so it must
// carry the redacted names rather than the originals.
func TestPeersTable_Anonymized(t *testing.T) {
lines := tableRows(t, ConvertOptions{Anonymize: true, AnonymizeLevel: anonymize.LevelStrict})
for _, row := range lines[2:] {
assert.NotContains(t, row, "awesome-domain.com", "FQDN must be anonymized")
assert.NotContains(t, row, "peer-", "peer name must be anonymized")
}
}
func TestShortName(t *testing.T) {
assert.Equal(t, "web-01", shortName("web-01.netbird.cloud"))
assert.Equal(t, "web-01", shortName("web-01"))
assert.Equal(t, "-", shortName(""))
}
func TestTableLatency(t *testing.T) {
tests := map[time.Duration]string{
0: "-",
145939625 * time.Nanosecond: "146ms",
12 * time.Millisecond: "12ms",
420*time.Microsecond + 700: "421µs",
}
for in, want := range tests {
assert.Equal(t, want, tableLatency(PeerStateDetailOutput{Latency: in}), "latency %s", in)
}
}