Skip to content

Commit

Permalink
Merge branch 'master' into error-vars
Browse files Browse the repository at this point in the history
  • Loading branch information
YoshiyukiMineo authored Aug 13, 2023
2 parents ce38b46 + 18c4908 commit 4ad3bda
Show file tree
Hide file tree
Showing 5 changed files with 149 additions and 11 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ jobs:
test:
strategy:
matrix:
go-version: [1.18.x, 1.19.x]
go-version: [1.20.x, 1.21.x]
os: [ubuntu-latest]
runs-on: ${{matrix.os}}
steps:
Expand Down
35 changes: 35 additions & 0 deletions mock/sonyflake_mock.go
Original file line number Diff line number Diff line change
@@ -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
}
}
21 changes: 12 additions & 9 deletions sonyflake.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ import (
"net"
"sync"
"time"

"github.com/sony/sonyflake/types"
)

// These constants are the bit lengths of Sonyflake ID parts.
Expand Down Expand Up @@ -57,6 +59,8 @@ var (
ErrInvalidMachineID = errors.New("invalid machine ID")
)

var defaultInterfaceAddrs = net.InterfaceAddrs

// New returns a new Sonyflake configured with the given Settings. If an error occurs,
// the *sonyflake.Sonyflake pointer will be nil and an error instance will be returned.
func New(st Settings) (*Sonyflake, error) {
Expand All @@ -74,13 +78,12 @@ func New(st Settings) (*Sonyflake, error) {
sf.startTime = toSonyflakeTime(st.StartTime)
}

// Fallback to default ID resolver
var err error
if st.MachineID == nil {
st.MachineID = lower16BitPrivateIP
sf.machineID, err = lower16BitPrivateIP(defaultInterfaceAddrs)
} else {
sf.machineID, err = st.MachineID()
}

var err error
sf.machineID, err = st.MachineID()
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -151,8 +154,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
}
Expand All @@ -176,8 +179,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
}
Expand Down
94 changes: 93 additions & 1 deletion sonyflake_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,15 @@ package sonyflake

import (
"errors"
"bytes"
"fmt"
"net"
"runtime"
"testing"
"time"

"github.com/sony/sonyflake/mock"
"github.com/sony/sonyflake/types"
)

var sf *Sonyflake
Expand All @@ -24,7 +29,7 @@ func init() {

startTime = toSonyflakeTime(st.StartTime)

ip, _ := lower16BitPrivateIP()
ip, _ := lower16BitPrivateIP(defaultInterfaceAddrs)
machineID = uint64(ip)
}

Expand Down Expand Up @@ -216,6 +221,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")
Expand Down
8 changes: 8 additions & 0 deletions types/types.go
Original file line number Diff line number Diff line change
@@ -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)

0 comments on commit 4ad3bda

Please sign in to comment.