From 806997932ec63f2a961ee2e96ee1b7561ecae362 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 12 Feb 2026 05:40:10 +0000 Subject: [PATCH 1/2] Initial plan From f8ce8840b504ecd27ae2433a43641796fdb30af9 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 12 Feb 2026 05:43:24 +0000 Subject: [PATCH 2/2] Fix default machine ID to respect BitsMachineID setting Co-authored-by: YoshiyukiMineo <7577673+YoshiyukiMineo@users.noreply.github.com> --- v2/machine_bits_test.go | 86 +++++++++++++++++++++++++++++++++++++++++ v2/mock/mock.go | 10 +++++ v2/sonyflake.go | 7 +++- 3 files changed, 102 insertions(+), 1 deletion(-) create mode 100644 v2/machine_bits_test.go diff --git a/v2/machine_bits_test.go b/v2/machine_bits_test.go new file mode 100644 index 0000000..1569930 --- /dev/null +++ b/v2/machine_bits_test.go @@ -0,0 +1,86 @@ +package sonyflake + +import ( + "net" + "testing" + + "github.com/sony/sonyflake/v2/mock" +) + +// TestDefaultMachineIDWithCustomBits tests that when using default machine ID +// with custom BitsMachineID, the machine ID is properly masked to fit within +// the specified bit length. +func TestDefaultMachineIDWithCustomBits(t *testing.T) { + testCases := []struct { + name string + bitsMachineID int + mockIP net.IP + expectError bool + }{ + { + name: "10 bits machine ID with IP that fits", + bitsMachineID: 10, + mockIP: net.IP{192, 168, 0, 1}, // lower 16 bits = 1, fits in 10 bits + expectError: false, + }, + { + name: "10 bits machine ID with IP that exceeds without masking", + bitsMachineID: 10, + mockIP: net.IP{192, 168, 255, 255}, // lower 16 bits = 65535, needs masking to fit in 10 bits + expectError: false, + }, + { + name: "8 bits machine ID", + bitsMachineID: 8, + mockIP: net.IP{192, 168, 100, 200}, // lower 16 bits = 25800 + expectError: false, + }, + { + name: "default 16 bits", + bitsMachineID: 0, // will use default 16 + mockIP: net.IP{192, 168, 255, 255}, + expectError: false, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + // Create a mock that returns our test IP + mockInterfaceAddrs := mock.NewInterfaceAddrsWithIP(tc.mockIP) + + settings := Settings{ + BitsMachineID: tc.bitsMachineID, + } + + // Temporarily replace the default interface addrs function + oldDefaultInterfaceAddrs := defaultInterfaceAddrs + defaultInterfaceAddrs = mockInterfaceAddrs + defer func() { defaultInterfaceAddrs = oldDefaultInterfaceAddrs }() + + sf, err := New(settings) + + if tc.expectError { + if err == nil { + t.Errorf("expected error but got none") + } + } else { + if err != nil { + t.Errorf("unexpected error: %v", err) + } + if sf == nil { + t.Error("sonyflake instance should not be nil") + } + + // Verify the machine ID fits within the specified bits + expectedBits := tc.bitsMachineID + if expectedBits == 0 { + expectedBits = defaultBitsMachine + } + maxMachineID := 1 << expectedBits + if sf.machine >= maxMachineID { + t.Errorf("machine ID %d exceeds max for %d bits (%d)", sf.machine, expectedBits, maxMachineID) + } + } + }) + } +} diff --git a/v2/mock/mock.go b/v2/mock/mock.go index 1bff6ab..639404b 100644 --- a/v2/mock/mock.go +++ b/v2/mock/mock.go @@ -34,3 +34,13 @@ func NewNilInterfaceAddrs() types.InterfaceAddrs { return []net.Addr{}, nil } } + +// NewInterfaceAddrsWithIP returns a private IP address with the given IP. +func NewInterfaceAddrsWithIP(ip net.IP) types.InterfaceAddrs { + ifat := make([]net.Addr, 0, 1) + ifat = append(ifat, &net.IPNet{IP: ip, Mask: []byte{255, 0, 0, 0}}) + + return func() ([]net.Addr, error) { + return ifat, nil + } +} diff --git a/v2/sonyflake.go b/v2/sonyflake.go index 0ca2be4..9ec2f6a 100644 --- a/v2/sonyflake.go +++ b/v2/sonyflake.go @@ -36,7 +36,8 @@ import ( // // MachineID returns the unique ID of a Sonyflake instance. // If MachineID returns an error, the instance will not be created. -// If MachineID is nil, the default MachineID is used, which returns the lower 16 bits of the private IP address. +// If MachineID is nil, the default MachineID is used, which returns the lower bits +// of the private IP address, masked to fit within BitsMachineID bits. // // CheckMachineID validates the uniqueness of a machine ID. // If CheckMachineID returns false, the instance will not be created. @@ -154,6 +155,10 @@ func New(st Settings) (*Sonyflake, error) { var err error if st.MachineID == nil { sf.machine, err = lower16BitPrivateIP(defaultInterfaceAddrs) + if err == nil { + // Mask to use only the required number of bits + sf.machine = sf.machine & (1<