github.com/hairyhenderson/gomplate/v4@v4.0.0-pre-2.0.20240520121557-362f058f0c93/internal/cidr/cidr_test.go (about)

     1  package cidr
     2  
     3  import (
     4  	"fmt"
     5  	"math/big"
     6  	"net/netip"
     7  	"testing"
     8  
     9  	"github.com/stretchr/testify/assert"
    10  	"github.com/stretchr/testify/require"
    11  )
    12  
    13  func TestSubnetBig(t *testing.T) {
    14  	cases := []struct {
    15  		base string
    16  		num  *big.Int
    17  		out  string
    18  		bits int
    19  		err  bool
    20  	}{
    21  		{
    22  			base: "192.168.2.0/20",
    23  			bits: 4,
    24  			num:  big.NewInt(int64(6)),
    25  			out:  "192.168.6.0/24",
    26  		},
    27  		{
    28  			base: "192.168.2.0/20",
    29  			bits: 4,
    30  			num:  big.NewInt(int64(0)),
    31  			out:  "192.168.0.0/24",
    32  		},
    33  		{
    34  			base: "192.168.0.0/31",
    35  			bits: 1,
    36  			num:  big.NewInt(int64(1)),
    37  			out:  "192.168.0.1/32",
    38  		},
    39  		{
    40  			base: "192.168.0.0/21",
    41  			bits: 4,
    42  			num:  big.NewInt(int64(7)),
    43  			out:  "192.168.3.128/25",
    44  		},
    45  		{
    46  			base: "fe80::/48",
    47  			bits: 16,
    48  			num:  big.NewInt(int64(6)),
    49  			out:  "fe80:0:0:6::/64",
    50  		},
    51  		{
    52  			base: "fe80::/48",
    53  			bits: 33,
    54  			num:  big.NewInt(int64(6)),
    55  			out:  "fe80::3:0:0:0/81",
    56  		},
    57  		{
    58  			base: "fe80::/49",
    59  			bits: 16,
    60  			num:  big.NewInt(int64(7)),
    61  			out:  "fe80:0:0:3:8000::/65",
    62  		},
    63  		{
    64  			base: "192.168.2.0/31",
    65  			bits: 2,
    66  			num:  big.NewInt(int64(0)),
    67  			err:  true, // not enough bits to expand into
    68  		},
    69  		{
    70  			base: "fe80::/126",
    71  			bits: 4,
    72  			num:  big.NewInt(int64(0)),
    73  			err:  true, // not enough bits to expand into
    74  		},
    75  		{
    76  			base: "192.168.2.0/24",
    77  			bits: 4,
    78  			num:  big.NewInt(int64(16)),
    79  			err:  true, // can't fit 16 into 4 bits
    80  		},
    81  	}
    82  
    83  	for _, testCase := range cases {
    84  		t.Run(fmt.Sprintf("SubnetBig(%#v,%#v,%#v)", testCase.base, testCase.bits, testCase.num), func(t *testing.T) {
    85  			base := netip.MustParsePrefix(testCase.base)
    86  
    87  			subnet, err := SubnetBig(base, testCase.bits, testCase.num)
    88  			if testCase.err {
    89  				require.Error(t, err)
    90  			} else {
    91  				require.NoError(t, err)
    92  				assert.Equal(t, testCase.out, subnet.String())
    93  			}
    94  		})
    95  	}
    96  }
    97  
    98  func TestHostBig(t *testing.T) {
    99  	cases := []struct {
   100  		prefix string
   101  		num    *big.Int
   102  		out    string
   103  		err    bool
   104  	}{
   105  		{
   106  			prefix: "192.168.2.0/20",
   107  			num:    big.NewInt(int64(6)),
   108  			out:    "192.168.0.6",
   109  		},
   110  		{
   111  			prefix: "192.168.0.0/20",
   112  			num:    big.NewInt(int64(257)),
   113  			out:    "192.168.1.1",
   114  		},
   115  		{
   116  			prefix: "2001:db8::/32",
   117  			num:    big.NewInt(int64(1)),
   118  			out:    "2001:db8::1",
   119  		},
   120  		{
   121  			prefix: "192.168.1.0/24",
   122  			num:    big.NewInt(int64(256)),
   123  			err:    true, // only 0-255 will fit in 8 bits
   124  		},
   125  		{
   126  			prefix: "192.168.0.0/30",
   127  			num:    big.NewInt(int64(-3)),
   128  			out:    "192.168.0.1", // 4 address (0-3) in 2 bits; 3rd from end = 1
   129  		},
   130  		{
   131  			prefix: "192.168.0.0/30",
   132  			num:    big.NewInt(int64(-4)),
   133  			out:    "192.168.0.0", // 4 address (0-3) in 2 bits; 4th from end = 0
   134  		},
   135  		{
   136  			prefix: "192.168.0.0/30",
   137  			num:    big.NewInt(int64(-5)),
   138  			err:    true, // 4 address (0-3) in 2 bits; cannot accommodate 5
   139  		},
   140  		{
   141  			prefix: "fd9d:bc11:4020::/64",
   142  			num:    big.NewInt(int64(2)),
   143  			out:    "fd9d:bc11:4020::2",
   144  		},
   145  		{
   146  			prefix: "fd9d:bc11:4020::/64",
   147  			num:    big.NewInt(int64(-2)),
   148  			out:    "fd9d:bc11:4020:0:ffff:ffff:ffff:fffe",
   149  		},
   150  	}
   151  
   152  	for _, testCase := range cases {
   153  		t.Run(fmt.Sprintf("HostBig(%v,%v)", testCase.prefix, testCase.num), func(t *testing.T) {
   154  			network := netip.MustParsePrefix(testCase.prefix)
   155  
   156  			gotIP, err := HostBig(network, testCase.num)
   157  			if testCase.err {
   158  				require.Error(t, err)
   159  			} else {
   160  				require.NoError(t, err)
   161  				assert.Equal(t, testCase.out, gotIP.String())
   162  			}
   163  		})
   164  	}
   165  }
   166  
   167  func TestPreviousNextSubnet(t *testing.T) {
   168  	testCases := []struct {
   169  		next, prev string
   170  		overflow   bool
   171  	}{
   172  		{"10.0.0.0/24", "9.255.255.0/24", false},
   173  		{"100.0.0.0/26", "99.255.255.192/26", false},
   174  		{"0.0.0.0/26", "255.255.255.192/26", true},
   175  		{"2001:db8:e000::/36", "2001:db8:d000::/36", false},
   176  		{"::/64", "ffff:ffff:ffff:ffff::/64", true},
   177  	}
   178  	for _, tc := range testCases {
   179  		c1 := netip.MustParsePrefix(tc.next)
   180  		c2 := netip.MustParsePrefix(tc.prev)
   181  		mask := c1.Bits()
   182  
   183  		p1, rollback := PreviousSubnet(c1, mask)
   184  		if tc.overflow {
   185  			assert.True(t, rollback)
   186  			continue
   187  		}
   188  
   189  		assert.Equal(t, c2.String(), p1.String())
   190  	}
   191  
   192  	for _, tc := range testCases {
   193  		c1 := netip.MustParsePrefix(tc.next)
   194  		c2 := netip.MustParsePrefix(tc.prev)
   195  		mask := c1.Bits()
   196  
   197  		n1, rollover := NextSubnet(c2, mask)
   198  		if tc.overflow {
   199  			assert.True(t, rollover)
   200  			continue
   201  		}
   202  		assert.Equal(t, c1, n1)
   203  	}
   204  }