Files
coder/vpn/version_test.go
Dean Sheather 14a60303ac chore: rework RPC version negotiation (#15687)
Changes the RPC header format from `codervpn <version> <role>` to
`codervpn <role> <version1,version2,...>`.

The versions list is a list of the maximum supported minor version for
each major version, sorted by major versions.

E.g. `1.0,2.3,3.1` means `1.0, 2.0, 2.1, 2.2, 2.3, 3.0, 3.1` are
supported.

When we eventually support multiple versions, the peer's version list
will be compared against the current supported versions list to
determine the maximum major and minor version supported by both peers.

Closes #15601
2024-12-04 09:38:24 +00:00

263 lines
5.0 KiB
Go

package vpn_test
import (
"testing"
"github.com/stretchr/testify/require"
"github.com/coder/coder/v2/vpn"
)
func TestRPCVersionLatest(t *testing.T) {
t.Parallel()
require.NoError(t, vpn.CurrentSupportedVersions.Validate())
}
func TestRPCVersionParseString(t *testing.T) {
t.Parallel()
cases := []struct {
name string
input string
want vpn.RPCVersion
}{
{
name: "valid version",
input: "1.0",
want: vpn.RPCVersion{Major: 1, Minor: 0},
},
{
name: "valid version with larger numbers",
input: "12.34",
want: vpn.RPCVersion{Major: 12, Minor: 34},
},
{
name: "empty string",
input: "",
},
{
name: "one part",
input: "1",
},
{
name: "three parts",
input: "1.0.0",
},
{
name: "major version is 0",
input: "0.1",
},
{
name: "invalid major version",
input: "a.1",
},
{
name: "invalid minor version",
input: "1.a",
},
}
// nolint:paralleltest
for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) {
t.Parallel()
got, err := vpn.ParseRPCVersion(tc.input)
if tc.want.Major == 0 {
require.Error(t, err)
} else {
require.NoError(t, err)
require.Equal(t, tc.want, got)
require.Equal(t, tc.input, got.String())
}
})
}
}
func TestRPCVersionIsCompatibleWith(t *testing.T) {
t.Parallel()
cases := []struct {
name string
v1 vpn.RPCVersion
v2 vpn.RPCVersion
want vpn.RPCVersion
wantBool bool
}{
{
name: "same version",
v1: vpn.RPCVersion{Major: 1, Minor: 0},
v2: vpn.RPCVersion{Major: 1, Minor: 0},
want: vpn.RPCVersion{Major: 1, Minor: 0},
},
{
name: "compatible minor versions",
v1: vpn.RPCVersion{Major: 1, Minor: 2},
v2: vpn.RPCVersion{Major: 1, Minor: 3},
want: vpn.RPCVersion{Major: 1, Minor: 2},
},
{
name: "incompatible major versions",
v1: vpn.RPCVersion{Major: 1, Minor: 0},
v2: vpn.RPCVersion{Major: 2, Minor: 0},
want: vpn.RPCVersion{},
},
}
// nolint:paralleltest
for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) {
t.Parallel()
got, ok := tc.v1.IsCompatibleWith(tc.v2)
if tc.want.Major == 0 {
require.False(t, ok)
return
}
require.True(t, ok)
require.Equal(t, got, tc.want)
})
}
}
func TestRPCVersionListParseString(t *testing.T) {
t.Parallel()
cases := []struct {
name string
input string
want vpn.RPCVersionList
errContains string
}{
{
name: "single version",
input: "1.0",
want: vpn.RPCVersionList{
Versions: []vpn.RPCVersion{
{Major: 1, Minor: 0},
},
},
},
{
name: "multiple versions",
input: "1.1,2.3,3.2",
want: vpn.RPCVersionList{
Versions: []vpn.RPCVersion{
{Major: 1, Minor: 1},
{Major: 2, Minor: 3},
{Major: 3, Minor: 2},
},
},
},
{
name: "invalid version",
input: "1.0,invalid",
errContains: "invalid version list",
},
{
name: "empty string",
input: "",
errContains: "invalid version list",
},
{
name: "duplicate versions",
input: "1.0,1.0",
errContains: "duplicate major version",
},
{
name: "duplicate major versions",
input: "1.0,1.2",
errContains: "duplicate major version",
},
{
name: "out of order versions",
input: "2.0,1.0",
errContains: "versions are not sorted",
},
}
for _, tc := range cases {
tc := tc
t.Run(tc.name, func(t *testing.T) {
t.Parallel()
got, err := vpn.ParseRPCVersionList(tc.input)
if tc.errContains != "" {
require.ErrorContains(t, err, tc.errContains)
return
}
require.NoError(t, err)
require.Equal(t, tc.want, got)
require.Equal(t, tc.input, got.String())
})
}
}
func TestRPCVersionListValidate(t *testing.T) {
t.Parallel()
cases := []struct {
name string
list vpn.RPCVersionList
errContains string
}{
{
name: "valid list",
list: vpn.RPCVersionList{
Versions: []vpn.RPCVersion{
{Major: 1, Minor: 1},
{Major: 2, Minor: 3},
{Major: 3, Minor: 2},
},
},
},
{
name: "empty list",
list: vpn.RPCVersionList{
Versions: []vpn.RPCVersion{},
},
errContains: "no versions",
},
{
name: "duplicate versions",
list: vpn.RPCVersionList{
Versions: []vpn.RPCVersion{
{Major: 1, Minor: 0},
{Major: 1, Minor: 0},
},
},
errContains: "duplicate major version",
},
{
name: "duplicate major versions",
list: vpn.RPCVersionList{
Versions: []vpn.RPCVersion{
{Major: 1, Minor: 0},
{Major: 1, Minor: 2},
},
},
errContains: "duplicate major version",
},
{
name: "out of order versions",
list: vpn.RPCVersionList{
Versions: []vpn.RPCVersion{
{Major: 2, Minor: 0},
{Major: 1, Minor: 0},
},
},
errContains: "versions are not sorted",
},
}
for _, tc := range cases {
tc := tc
t.Run(tc.name, func(t *testing.T) {
t.Parallel()
err := tc.list.Validate()
if tc.errContains != "" {
require.ErrorContains(t, err, tc.errContains)
} else {
require.NoError(t, err)
}
})
}
}