mirror of
https://github.com/sony/sonyflake.git
synced 2026-01-09 04:43:07 +00:00
Increase coverage (#42)
* Introduce mocking framework and increase coverage (#22) This change introduces two common golang patterns: - types: this will allow fine-tuned control over imported types by defining where they will be used and how - mock: this allows the generation of mock constructors, which allows for testing any individual path in a method by "injecting" a mock method which matches the expected type This change also increases test coverage to 100% Co-authored-by: Yoshiyuki Mineo <Yoshiyuki.Mineo@jp.sony.com> * gofmt --------- Co-authored-by: Bradley Boutcher <btboutcher@icloud.com>
This commit is contained in:
35
mock/sonyflake_mock.go
Normal file
35
mock/sonyflake_mock.go
Normal file
@@ -0,0 +1,35 @@
|
||||
// Package mock offers implementations of interfaces defined in types.go
|
||||
// This allows complete control over input / output for any given method that consumes
|
||||
// a given type
|
||||
package mock
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
|
||||
"github.com/sony/sonyflake/types"
|
||||
)
|
||||
|
||||
// NewSuccessfulInterfaceAddrs returns a single private IP address
|
||||
func NewSuccessfulInterfaceAddrs() types.InterfaceAddrs {
|
||||
ifat := make([]net.Addr, 0, 1)
|
||||
ifat = append(ifat, &net.IPNet{IP: []byte{192, 168, 0, 1}, Mask: []byte{255, 0, 0, 0}})
|
||||
|
||||
return func() ([]net.Addr, error) {
|
||||
return ifat, nil
|
||||
}
|
||||
}
|
||||
|
||||
// NewFailingInterfaceAddrs returns an error
|
||||
func NewFailingInterfaceAddrs() types.InterfaceAddrs {
|
||||
return func() ([]net.Addr, error) {
|
||||
return nil, fmt.Errorf("test error")
|
||||
}
|
||||
}
|
||||
|
||||
// NewFailingInterfaceAddrs returns an empty slice of addresses
|
||||
func NewNilInterfaceAddrs() types.InterfaceAddrs {
|
||||
return func() ([]net.Addr, error) {
|
||||
return []net.Addr{}, nil
|
||||
}
|
||||
}
|
||||
14
sonyflake.go
14
sonyflake.go
@@ -12,6 +12,8 @@ import (
|
||||
"net"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/sony/sonyflake/types"
|
||||
)
|
||||
|
||||
// These constants are the bit lengths of Sonyflake ID parts.
|
||||
@@ -50,6 +52,8 @@ type Sonyflake struct {
|
||||
machineID uint16
|
||||
}
|
||||
|
||||
var defaultInterfaceAddrs = net.InterfaceAddrs
|
||||
|
||||
// NewSonyflake returns a new Sonyflake configured with the given Settings.
|
||||
// NewSonyflake returns nil in the following cases:
|
||||
// - Settings.StartTime is ahead of the current time.
|
||||
@@ -71,7 +75,7 @@ func NewSonyflake(st Settings) *Sonyflake {
|
||||
|
||||
var err error
|
||||
if st.MachineID == nil {
|
||||
sf.machineID, err = lower16BitPrivateIP()
|
||||
sf.machineID, err = lower16BitPrivateIP(defaultInterfaceAddrs)
|
||||
} else {
|
||||
sf.machineID, err = st.MachineID()
|
||||
}
|
||||
@@ -131,8 +135,8 @@ func (sf *Sonyflake) toID() (uint64, error) {
|
||||
uint64(sf.machineID), nil
|
||||
}
|
||||
|
||||
func privateIPv4() (net.IP, error) {
|
||||
as, err := net.InterfaceAddrs()
|
||||
func privateIPv4(interfaceAddrs types.InterfaceAddrs) (net.IP, error) {
|
||||
as, err := interfaceAddrs()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -156,8 +160,8 @@ func isPrivateIPv4(ip net.IP) bool {
|
||||
(ip[0] == 10 || ip[0] == 172 && (ip[1] >= 16 && ip[1] < 32) || ip[0] == 192 && ip[1] == 168)
|
||||
}
|
||||
|
||||
func lower16BitPrivateIP() (uint16, error) {
|
||||
ip, err := privateIPv4()
|
||||
func lower16BitPrivateIP(interfaceAddrs types.InterfaceAddrs) (uint16, error) {
|
||||
ip, err := privateIPv4(interfaceAddrs)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
@@ -1,10 +1,15 @@
|
||||
package sonyflake
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"net"
|
||||
"runtime"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/sony/sonyflake/mock"
|
||||
"github.com/sony/sonyflake/types"
|
||||
)
|
||||
|
||||
var sf *Sonyflake
|
||||
@@ -23,7 +28,7 @@ func init() {
|
||||
|
||||
startTime = toSonyflakeTime(st.StartTime)
|
||||
|
||||
ip, _ := lower16BitPrivateIP()
|
||||
ip, _ := lower16BitPrivateIP(defaultInterfaceAddrs)
|
||||
machineID = uint64(ip)
|
||||
}
|
||||
|
||||
@@ -185,6 +190,93 @@ func TestNextIDError(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestPrivateIPv4(t *testing.T) {
|
||||
testCases := []struct {
|
||||
description string
|
||||
expected net.IP
|
||||
interfaceAddrs types.InterfaceAddrs
|
||||
error string
|
||||
}{
|
||||
{
|
||||
description: "InterfaceAddrs returns an error",
|
||||
expected: nil,
|
||||
interfaceAddrs: mock.NewFailingInterfaceAddrs(),
|
||||
error: "test error",
|
||||
},
|
||||
{
|
||||
description: "InterfaceAddrs returns an empty or nil list",
|
||||
expected: nil,
|
||||
interfaceAddrs: mock.NewNilInterfaceAddrs(),
|
||||
error: "no private ip address",
|
||||
},
|
||||
{
|
||||
description: "InterfaceAddrs returns one or more IPs",
|
||||
expected: net.IP{192, 168, 0, 1},
|
||||
interfaceAddrs: mock.NewSuccessfulInterfaceAddrs(),
|
||||
error: "",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.description, func(t *testing.T) {
|
||||
actual, err := privateIPv4(tc.interfaceAddrs)
|
||||
|
||||
if (err != nil) && (tc.error == "") {
|
||||
t.Errorf("expected no error, but got: %s", err)
|
||||
return
|
||||
} else if (err != nil) && (tc.error != "") {
|
||||
return
|
||||
}
|
||||
|
||||
if bytes.Equal(actual, tc.expected) {
|
||||
return
|
||||
} else {
|
||||
t.Errorf("error: expected: %s, but got: %s", tc.expected, actual)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestLower16BitPrivateIP(t *testing.T) {
|
||||
testCases := []struct {
|
||||
description string
|
||||
expected uint16
|
||||
interfaceAddrs types.InterfaceAddrs
|
||||
error string
|
||||
}{
|
||||
{
|
||||
description: "InterfaceAddrs returns an empty or nil list",
|
||||
expected: 0,
|
||||
interfaceAddrs: mock.NewNilInterfaceAddrs(),
|
||||
error: "no private ip address",
|
||||
},
|
||||
{
|
||||
description: "InterfaceAddrs returns one or more IPs",
|
||||
expected: 1,
|
||||
interfaceAddrs: mock.NewSuccessfulInterfaceAddrs(),
|
||||
error: "",
|
||||
},
|
||||
}
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.description, func(t *testing.T) {
|
||||
actual, err := lower16BitPrivateIP(tc.interfaceAddrs)
|
||||
|
||||
if (err != nil) && (tc.error == "") {
|
||||
t.Errorf("expected no error, but got: %s", err)
|
||||
return
|
||||
} else if (err != nil) && (tc.error != "") {
|
||||
return
|
||||
}
|
||||
|
||||
if actual == tc.expected {
|
||||
return
|
||||
} else {
|
||||
t.Errorf("error: expected: %v, but got: %v", tc.expected, actual)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestSonyflakeTimeUnit(t *testing.T) {
|
||||
if time.Duration(sonyflakeTimeUnit) != 10*time.Millisecond {
|
||||
t.Errorf("unexpected time unit")
|
||||
|
||||
8
types/types.go
Normal file
8
types/types.go
Normal file
@@ -0,0 +1,8 @@
|
||||
// Package Types defines type signatures used throughout SonyFlake. This allows for
|
||||
// fine-tuned control over imports, and the ability to mock out imports as well
|
||||
package types
|
||||
|
||||
import "net"
|
||||
|
||||
// InterfaceAddrs defines the interface used for retrieving network addresses
|
||||
type InterfaceAddrs func() ([]net.Addr, error)
|
||||
Reference in New Issue
Block a user