api/types/network: use blackbox testing for ports

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
This commit is contained in:
Sebastiaan van Stijn
2026-03-12 14:39:42 +01:00
parent 91f3ec65e6
commit a722b68f5c

View File

@@ -1,4 +1,4 @@
package network
package network_test
import (
"encoding/json"
@@ -9,67 +9,69 @@ import (
"testing"
"gotest.tools/v3/assert"
"github.com/moby/moby/api/types/network"
)
type TestRanger interface {
Range() PortRange
Range() network.PortRange
}
var (
_ TestRanger = Port{}
_ TestRanger = PortRange{}
_ TestRanger = network.Port{}
_ TestRanger = network.PortRange{}
)
func TestPort(t *testing.T) {
t.Run("Zero Value", func(t *testing.T) {
var p Port
var p network.Port
assert.Check(t, p.IsZero())
assert.Check(t, !p.IsValid())
assert.Equal(t, p.String(), "invalid port")
assert.Equal(t, p.Proto(), IPProtocol(""))
assert.Equal(t, p.Proto(), network.IPProtocol(""))
assert.Equal(t, p.Num(), uint16(0))
assert.Equal(t, p.String(), "")
assert.Equal(t, p.Range(), PortRange{})
assert.Equal(t, p.Port(), "")
assert.Equal(t, p.Range(), network.PortRange{})
t.Run("Marshal Unmarshal", func(t *testing.T) {
var p Port
var p network.Port
bytes, err := p.MarshalText()
assert.NilError(t, err)
assert.Check(t, len(bytes) == 0)
err = p.UnmarshalText([]byte(""))
assert.NilError(t, err)
assert.Equal(t, p, Port{})
assert.Equal(t, p, network.Port{})
})
t.Run("JSON Marshal Unmarshal", func(t *testing.T) {
var p Port
var p network.Port
bytes, err := json.Marshal(p)
assert.NilError(t, err)
assert.Equal(t, string(bytes), `""`)
err = json.Unmarshal([]byte(`""`), &p)
assert.NilError(t, err)
assert.Equal(t, p, Port{})
assert.Equal(t, p, network.Port{})
})
})
t.Run("PortFrom", func(t *testing.T) {
tests := []struct {
num uint16
proto IPProtocol
proto network.IPProtocol
}{
{0, TCP},
{80, TCP},
{8080, TCP},
{65535, TCP},
{80, UDP},
{8080, SCTP},
{proto: network.TCP},
{num: 80, proto: network.TCP},
{num: 8080, proto: network.TCP},
{num: 65535, proto: network.TCP},
{num: 80, proto: network.UDP},
{num: 8080, proto: network.SCTP},
}
for _, tc := range tests {
t.Run(fmt.Sprintf("%d_%s", tc.num, tc.proto), func(t *testing.T) {
p, ok := PortFrom(tc.num, tc.proto)
p, ok := network.PortFrom(tc.num, tc.proto)
assert.Check(t, ok)
assert.Equal(t, p.Num(), tc.num)
assert.Equal(t, p.Port(), strconv.Itoa(int(tc.num)))
@@ -87,14 +89,14 @@ func TestPort(t *testing.T) {
negativeTests := []struct {
num uint16
proto IPProtocol
proto network.IPProtocol
}{
{0, ""},
{80, ""},
{num: 0, proto: ""},
{num: 80, proto: ""},
}
for _, tc := range negativeTests {
t.Run(fmt.Sprintf("%d_%s", tc.num, tc.proto), func(t *testing.T) {
p, ok := PortFrom(tc.num, tc.proto)
p, ok := network.PortFrom(tc.num, tc.proto)
assert.Check(t, !ok)
assert.Check(t, p.IsZero())
assert.Check(t, !p.IsValid())
@@ -106,56 +108,56 @@ func TestPort(t *testing.T) {
t.Run("ParsePort", func(t *testing.T) {
tests := []struct {
in string
port Port // output of ParsePort()
str string // output of String().
portRange PortRange // output of Range()
port network.Port // output of ParsePort()
str string // output of String().
portRange network.PortRange // output of Range()
}{
// Zero port
// Zero network.Port
{
in: "0/tcp",
port: portFrom(0, TCP),
port: portFrom(0, network.TCP),
str: "0/tcp",
portRange: portRangeFrom(0, 0, TCP),
portRange: portRangeFrom(0, 0, network.TCP),
},
// Max valid port
// Max valid network.Port
{
in: "65535/tcp",
port: portFrom(65535, TCP),
port: portFrom(65535, network.TCP),
str: "65535/tcp",
portRange: portRangeFrom(65535, 65535, TCP),
portRange: portRangeFrom(65535, 65535, network.TCP),
},
// Simple valid ports
// Simple valid network.Ports
{
in: "1234/tcp",
port: portFrom(1234, TCP),
port: portFrom(1234, network.TCP),
str: "1234/tcp",
portRange: portRangeFrom(1234, 1234, TCP),
portRange: portRangeFrom(1234, 1234, network.TCP),
},
{
in: "1234/udp",
port: portFrom(1234, UDP),
port: portFrom(1234, network.UDP),
str: "1234/udp",
portRange: portRangeFrom(1234, 1234, UDP),
portRange: portRangeFrom(1234, 1234, network.UDP),
},
{
in: "1234/sctp",
port: portFrom(1234, SCTP),
port: portFrom(1234, network.SCTP),
str: "1234/sctp",
portRange: portRangeFrom(1234, 1234, SCTP),
portRange: portRangeFrom(1234, 1234, network.SCTP),
},
// Default protocol is tcp
{
in: "1234",
port: portFrom(1234, TCP),
port: portFrom(1234, network.TCP),
str: "1234/tcp",
portRange: portRangeFrom(1234, 1234, TCP),
portRange: portRangeFrom(1234, 1234, network.TCP),
},
// Default protocol is tcp
{
in: "1234/",
port: portFrom(1234, TCP),
port: portFrom(1234, network.TCP),
str: "1234/tcp",
portRange: portRangeFrom(1234, 1234, TCP),
portRange: portRangeFrom(1234, 1234, network.TCP),
},
{
in: "1234/tcp:ipv6only",
@@ -167,22 +169,22 @@ func TestPort(t *testing.T) {
for _, tc := range tests {
t.Run(strings.ReplaceAll(tc.in, "/", "_"), func(t *testing.T) {
got, err := ParsePort(tc.in)
got, err := network.ParsePort(tc.in)
assert.NilError(t, err)
assert.Equal(t, got, tc.port)
MustParsePort(tc.in) // should not panic
network.MustParsePort(tc.in) // should not panic
assert.Check(t, !got.IsZero())
assert.Check(t, got.IsValid())
// Check that ParsePort is a pure function.
got2, err := ParsePort(tc.in)
got2, err := network.ParsePort(tc.in)
assert.NilError(t, err)
assert.Equal(t, got2, got)
// Check that ParsePort(port.String()) is the identity function.
got3, err := ParsePort(got.String())
got3, err := network.ParsePort(got.String())
assert.NilError(t, err)
assert.Equal(t, got3, got)
@@ -195,7 +197,7 @@ func TestPort(t *testing.T) {
assert.Equal(t, s, wants)
js := `"` + tc.in + `"`
var jsgot Port
var jsgot network.Port
err = json.Unmarshal([]byte(js), &jsgot)
assert.NilError(t, err)
assert.Equal(t, jsgot, got)
@@ -213,9 +215,9 @@ func TestPort(t *testing.T) {
}
t.Run("Normalize Protocol", func(t *testing.T) {
p1 := MustParsePort("1234/tcp")
p2 := MustParsePort("1234/TCP")
p3 := MustParsePort("1234/tCp")
p1 := network.MustParsePort("1234/tcp")
p2 := network.MustParsePort("1234/TCP")
p3 := network.MustParsePort("1234/tCp")
assert.Equal(t, p1, p2)
assert.Equal(t, p2, p3)
})
@@ -245,7 +247,7 @@ func TestPort(t *testing.T) {
for _, s := range negativeTests {
t.Run(strings.ReplaceAll(s, "/", "_"), func(t *testing.T) {
got, err := ParsePort(s)
got, err := network.ParsePort(s)
assert.ErrorContains(t, err, "invalid port")
assert.Check(t, got.IsZero())
assert.Check(t, !got.IsValid())
@@ -256,11 +258,11 @@ func TestPort(t *testing.T) {
return
}
var jsgot Port
var jsGot network.Port
js := []byte(`"` + s + `"`)
err = json.Unmarshal(js, &jsgot)
err = json.Unmarshal(js, &jsGot)
assert.ErrorContains(t, err, "invalid port")
assert.Equal(t, jsgot, Port{})
assert.Equal(t, jsGot, network.Port{})
})
}
})
@@ -268,36 +270,36 @@ func TestPort(t *testing.T) {
func TestPortRange(t *testing.T) {
t.Run("Zero Value", func(t *testing.T) {
var pr PortRange
var pr network.PortRange
assert.Check(t, pr.IsZero())
assert.Check(t, !pr.IsValid())
assert.Equal(t, pr.String(), "invalid port range")
assert.Equal(t, pr.Start(), uint16(0))
assert.Equal(t, pr.End(), uint16(0))
assert.Equal(t, pr.Proto(), IPProtocol(""))
assert.Equal(t, pr.Proto(), network.IPProtocol(""))
assert.Equal(t, pr.Range(), pr)
assert.Check(t, slices.Equal(slices.Collect(pr.All()), []Port{}))
assert.Check(t, slices.Equal(slices.Collect(pr.All()), []network.Port{}))
t.Run("Marshal Unmarshal", func(t *testing.T) {
var pr PortRange
var pr network.PortRange
bytes, err := pr.MarshalText()
assert.NilError(t, err)
assert.Check(t, len(bytes) == 0)
err = pr.UnmarshalText([]byte(""))
assert.NilError(t, err)
assert.Equal(t, pr, PortRange{})
assert.Equal(t, pr, network.PortRange{})
})
t.Run("JSON Marshal Unmarshal", func(t *testing.T) {
var pr PortRange
var pr network.PortRange
bytes, err := json.Marshal(pr)
assert.NilError(t, err)
assert.Equal(t, string(bytes), `""`)
err = json.Unmarshal([]byte(`""`), &pr)
assert.NilError(t, err)
assert.Equal(t, pr, PortRange{})
assert.Equal(t, pr, network.PortRange{})
})
})
@@ -305,20 +307,20 @@ func TestPortRange(t *testing.T) {
tests := []struct {
start uint16
end uint16
proto IPProtocol
proto network.IPProtocol
}{
{0, 0, TCP},
{0, 1234, TCP},
{80, 80, TCP},
{80, 8080, TCP},
{1234, 65535, TCP},
{80, 80, UDP},
{80, 8080, SCTP},
{start: 0, end: 0, proto: network.TCP},
{start: 0, end: 1234, proto: network.TCP},
{start: 80, end: 80, proto: network.TCP},
{start: 80, end: 8080, proto: network.TCP},
{start: 1234, end: 65535, proto: network.TCP},
{start: 80, end: 80, proto: network.UDP},
{start: 80, end: 8080, proto: network.SCTP},
}
for _, tc := range tests {
t.Run(fmt.Sprintf("%d_%d_%s", tc.start, tc.end, tc.proto), func(t *testing.T) {
pr, ok := PortRangeFrom(tc.start, tc.end, tc.proto)
pr, ok := network.PortRangeFrom(tc.start, tc.end, tc.proto)
assert.Check(t, ok)
assert.Equal(t, pr.Start(), tc.start)
assert.Equal(t, pr.End(), tc.end)
@@ -327,9 +329,9 @@ func TestPortRange(t *testing.T) {
}
t.Run("Normalize Protocol", func(t *testing.T) {
pr1, _ := PortRangeFrom(1234, 5678, "tcp")
pr2, _ := PortRangeFrom(1234, 5678, "TCP")
pr3, _ := PortRangeFrom(1234, 5678, "tCp")
pr1, _ := network.PortRangeFrom(1234, 5678, "tcp")
pr2, _ := network.PortRangeFrom(1234, 5678, "TCP")
pr3, _ := network.PortRangeFrom(1234, 5678, "tCp")
assert.Equal(t, pr1, pr2)
assert.Equal(t, pr2, pr3)
})
@@ -337,14 +339,14 @@ func TestPortRange(t *testing.T) {
negativeTests := []struct {
start uint16
end uint16
proto IPProtocol
proto network.IPProtocol
}{
{1234, 80, TCP}, // end < start
{0, 0, ""}, // empty protocol
{start: 1234, end: 80, proto: network.TCP}, // end < start
{}, // empty protocol
}
for _, tc := range negativeTests {
t.Run(fmt.Sprintf("%d_%d_%s", tc.start, tc.end, tc.proto), func(t *testing.T) {
pr, ok := PortRangeFrom(tc.start, tc.end, tc.proto)
pr, ok := network.PortRangeFrom(tc.start, tc.end, tc.proto)
assert.Check(t, !ok)
assert.Check(t, pr.IsZero())
assert.Check(t, !pr.IsValid())
@@ -355,52 +357,52 @@ func TestPortRange(t *testing.T) {
t.Run("ParsePortRange", func(t *testing.T) {
tests := []struct {
in string
portRange PortRange // output of ParsePortRange() and Range()
str string // output of String(). If "", use in.
portRange network.PortRange // output of network.ParsePortRange() and Range()
str string // output of String(). If "", use in.
}{
// Zero port
{
in: "0-1234/tcp",
portRange: portRangeFrom(0, 1234, TCP),
portRange: portRangeFrom(0, 1234, network.TCP),
str: "0-1234/tcp",
},
// Max valid port
{
in: "1234-65535/tcp",
portRange: portRangeFrom(1234, 65535, TCP),
portRange: portRangeFrom(1234, 65535, network.TCP),
str: "1234-65535/tcp",
},
// Simple valid ports
{
in: "1234-4567/tcp",
portRange: portRangeFrom(1234, 4567, TCP),
portRange: portRangeFrom(1234, 4567, network.TCP),
str: "1234-4567/tcp",
},
{
in: "1234-4567/udp",
portRange: portRangeFrom(1234, 4567, UDP),
portRange: portRangeFrom(1234, 4567, network.UDP),
str: "1234-4567/udp",
},
// Default protocol is tcp
{
in: "1234-4567",
portRange: portRangeFrom(1234, 4567, TCP),
portRange: portRangeFrom(1234, 4567, network.TCP),
str: "1234-4567/tcp",
},
// Default protocol is tcp
{
in: "1234-4567/",
portRange: portRangeFrom(1234, 4567, TCP),
portRange: portRangeFrom(1234, 4567, network.TCP),
str: "1234-4567/tcp",
},
{
in: "1234/tcp",
portRange: portRangeFrom(1234, 1234, TCP),
portRange: portRangeFrom(1234, 1234, network.TCP),
str: "1234/tcp",
},
{
in: "1234",
portRange: portRangeFrom(1234, 1234, TCP),
portRange: portRangeFrom(1234, 1234, network.TCP),
str: "1234/tcp",
},
{
@@ -412,21 +414,21 @@ func TestPortRange(t *testing.T) {
for _, tc := range tests {
t.Run(strings.ReplaceAll(tc.in, "/", "_"), func(t *testing.T) {
got, err := ParsePortRange(tc.in)
got, err := network.ParsePortRange(tc.in)
assert.NilError(t, err)
assert.Equal(t, got, tc.portRange)
assert.Check(t, !got.IsZero())
assert.Check(t, got.IsValid())
MustParsePortRange(tc.in) // should not panic
network.MustParsePortRange(tc.in) // should not panic
// Check that ParsePortRange is a pure function.
got2, err := ParsePortRange(tc.in)
got2, err := network.ParsePortRange(tc.in)
assert.NilError(t, err)
assert.Equal(t, got2, got)
// Check that ParsePortRange(port.String()) is the identity function.
got3, err := ParsePortRange(got.String())
got3, err := network.ParsePortRange(got.String())
assert.NilError(t, err)
assert.Equal(t, got3, got)
@@ -439,7 +441,7 @@ func TestPortRange(t *testing.T) {
assert.Equal(t, s, wants)
js := `"` + tc.in + `"`
var jsgot PortRange
var jsgot network.PortRange
err = json.Unmarshal([]byte(js), &jsgot)
assert.NilError(t, err)
assert.Equal(t, jsgot, got)
@@ -455,9 +457,9 @@ func TestPortRange(t *testing.T) {
})
t.Run("Normalize Protocol", func(t *testing.T) {
pr1 := MustParsePortRange("1234-5678/tcp")
pr2 := MustParsePortRange("1234-5678/TCP")
pr3 := MustParsePortRange("1234-5678/tCp")
pr1 := network.MustParsePortRange("1234-5678/tcp")
pr2 := network.MustParsePortRange("1234-5678/TCP")
pr3 := network.MustParsePortRange("1234-5678/tCp")
assert.Equal(t, pr1, pr2)
assert.Equal(t, pr2, pr3)
})
@@ -491,7 +493,7 @@ func TestPortRange(t *testing.T) {
for _, s := range negativeTests {
t.Run(strings.ReplaceAll(s, "/", "_"), func(t *testing.T) {
got, err := ParsePortRange(s)
got, err := network.ParsePortRange(s)
assert.Check(t, err != nil)
assert.Check(t, got.IsZero())
assert.Check(t, !got.IsValid())
@@ -502,11 +504,11 @@ func TestPortRange(t *testing.T) {
return
}
var jsgot PortRange
var jsgot network.PortRange
js := []byte(`"` + s + `"`)
err = json.Unmarshal(js, &jsgot)
assert.Check(t, err != nil)
assert.Equal(t, jsgot, PortRange{})
assert.Equal(t, jsgot, network.PortRange{})
})
}
}
@@ -515,32 +517,32 @@ func TestPortRange(t *testing.T) {
t.Run("PortRange All()", func(t *testing.T) {
tests := []struct {
in string
want []Port
want []network.Port
}{
{
in: "1000-1000/tcp",
want: []Port{portFrom(1000, TCP)},
want: []network.Port{portFrom(1000, network.TCP)},
},
{
in: "1000-1002/tcp",
want: []Port{portFrom(1000, TCP), portFrom(1001, TCP), portFrom(1002, TCP)},
want: []network.Port{portFrom(1000, network.TCP), portFrom(1001, network.TCP), portFrom(1002, network.TCP)},
},
{
in: "0-0/tcp", // TODO(thaJeztah): should this result in "zero-value range" and an empty list?
want: []Port{portFrom(0, TCP)},
want: []network.Port{portFrom(0, network.TCP)},
},
{
in: "65535-65535/tcp",
want: []Port{portFrom(65535, TCP)},
want: []network.Port{portFrom(65535, network.TCP)},
},
{
in: "65530-65535/tcp",
want: []Port{portFrom(65530, TCP), portFrom(65531, TCP), portFrom(65532, TCP), portFrom(65533, TCP), portFrom(65534, TCP), portFrom(65535, TCP)},
want: []network.Port{portFrom(65530, network.TCP), portFrom(65531, network.TCP), portFrom(65532, network.TCP), portFrom(65533, network.TCP), portFrom(65534, network.TCP), portFrom(65535, network.TCP)},
},
}
for _, tc := range tests {
pr := MustParsePortRange(tc.in)
pr := network.MustParsePortRange(tc.in)
ports := slices.Collect(pr.All())
if !slices.Equal(ports, tc.want) {
t.Errorf("PortRange.All() = %#v, want %#v", ports, tc.want)
@@ -548,9 +550,9 @@ func TestPortRange(t *testing.T) {
}
t.Run("All() stop early", func(t *testing.T) {
want := []Port{portFrom(1000, TCP), portFrom(1001, TCP)}
pr := MustParsePortRange("1000-2000/tcp")
var ports []Port
want := []network.Port{portFrom(1000, network.TCP), portFrom(1001, network.TCP)}
pr := network.MustParsePortRange("1000-2000/tcp")
var ports []network.Port
for p := range pr.All() {
ports = append(ports, p)
if len(ports) == 2 {
@@ -566,7 +568,7 @@ func TestPortRange(t *testing.T) {
func BenchmarkPortRangeAll(b *testing.B) {
b.Run("Single Port", func(b *testing.B) {
pr := MustParsePortRange("1234/tcp")
pr := network.MustParsePortRange("1234/tcp")
b.ResetTimer()
for i := 0; i < b.N; i++ {
var sink int64
@@ -580,7 +582,7 @@ func BenchmarkPortRangeAll(b *testing.B) {
})
b.Run("Range", func(b *testing.B) {
pr := MustParsePortRange("0-65535/tcp")
pr := network.MustParsePortRange("0-65535/tcp")
b.ResetTimer()
for i := 0; i < b.N; i++ {
var sink int64
@@ -594,16 +596,16 @@ func BenchmarkPortRangeAll(b *testing.B) {
})
}
func portFrom(num uint16, proto IPProtocol) Port {
p, ok := PortFrom(num, proto)
func portFrom(num uint16, proto network.IPProtocol) network.Port {
p, ok := network.PortFrom(num, proto)
if !ok {
panic("invalid port")
}
return p
}
func portRangeFrom(start, end uint16, proto IPProtocol) PortRange {
pr, ok := PortRangeFrom(start, end, proto)
func portRangeFrom(start, end uint16, proto network.IPProtocol) network.PortRange {
pr, ok := network.PortRangeFrom(start, end, proto)
if !ok {
panic("invalid port range")
}