142 lines
3.5 KiB
Go
142 lines
3.5 KiB
Go
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)
|
|
}
|
|
}
|
|
}
|