package services import ( "strings" "sync" "testing" ) // TestRandomAllocationIsUnique verifies that 1000 concurrent IP allocations // produce no duplicates (the random probing strategy must be collision-safe). func TestRandomAllocationIsUnique(t *testing.T) { p := NewIPPressure(nil) const n = 1000 results := make([]string, n) var wg sync.WaitGroup for i := range n { wg.Add(1) go func(i int) { defer wg.Done() results[i] = p.lockIP("client-" + string(rune('A'+i%26)) + string(rune('0'+i/26))) }(i) } wg.Wait() seen := make(map[string]int, n) for i, ip := range results { if ip == "" { t.Fatalf("allocation %d returned empty string", i) } if prev, dup := seen[ip]; dup { t.Fatalf("duplicate IP %s at slots %d and %d", ip, prev, i) } seen[ip] = i } } // TestRandomNumberAllocation checks uniqueness for number allocations. func TestRandomNumberAllocation(t *testing.T) { p := NewIPPressure(nil) const n = 500 nums := make([]int, n) var wg sync.WaitGroup for i := range n { wg.Add(1) go func(i int) { defer wg.Done() nums[i] = p.lockNumber("c" + string(rune('A'+i%26))) }(i) } wg.Wait() seen := make(map[int]bool, n) for _, n := range nums { if seen[n] { t.Fatalf("duplicate number %d", n) } seen[n] = true } } // TestSubNetAllocRelease covers the /24 subnet lifecycle. func TestSubNetAllocRelease(t *testing.T) { hub := newHub() a, _ := connect(hub, "a") b, _ := connect(hub, "b") // Allocate a subnet. resp := asMap(t, hub.Handle(a, msg("alloc/APSubNet"))) if resp["status"] != "success" { t.Fatalf("alloc/APSubNet = %v", resp) } prefix := resp["prefix"].(string) if !strings.HasPrefix(prefix, "10.") { t.Fatalf("prefix %q should start with '10.'", prefix) } // Idempotent: second alloc returns the same prefix. resp2 := asMap(t, hub.Handle(a, msg("alloc/APSubNet"))) if resp2["prefix"] != prefix { t.Fatalf("second alloc/APSubNet should return same prefix, got %v vs %v", resp2["prefix"], prefix) } // b requests a host IP within a's subnet. ipResp := asMap(t, hub.Handle(b, msg("alloc/APSubNetIP", "prefix", prefix))) if ipResp["status"] != "success" { t.Fatalf("alloc/APSubNetIP = %v", ipResp) } ip := ipResp["ip"].(string) if !strings.HasPrefix(ip, prefix+".") { t.Fatalf("allocated IP %q should be within prefix %q", ip, prefix) } // whois query finds b. who := asMap(t, hub.Handle(a, msg("whois/APSubNetIP", "prefix", prefix, "whois", ip))) if who["socket"] != "b" { t.Fatalf("whois/APSubNetIP = %v, want b", who) } // Release b's host IP. if r := asMap(t, hub.Handle(b, msg("release/APSubNetIP"))); r["status"] != "success" { t.Fatalf("release/APSubNetIP = %v", r) } // Release a's subnet. if r := asMap(t, hub.Handle(a, msg("release/APSubNet"))); r["status"] != "success" { t.Fatalf("release/APSubNet = %v", r) } } // TestSubNetIPsAreUnique verifies that multiple clients get distinct IPs // within the same subnet. func TestSubNetIPsAreUnique(t *testing.T) { p := NewIPPressure(nil) sn, ok := p.allocSubNet("owner") if !ok { t.Fatal("allocSubNet failed") } const n = 50 ips := make([]string, n) for i := range n { ip, ok := sn.Alloc("client-" + string(rune('A'+i))) if !ok { t.Fatalf("Alloc failed at %d", i) } ips[i] = ip } seen := make(map[string]bool, n) for _, ip := range ips { if seen[ip] { t.Fatalf("duplicate subnet IP %s", ip) } seen[ip] = true if !strings.HasPrefix(ip, sn.Prefix+".") { t.Fatalf("IP %q not within prefix %q", ip, sn.Prefix) } } }