package api import ( "testing" "golang.org/x/crypto/bcrypt" "vpn-platform/backend/internal/model" "vpn-platform/backend/internal/store" ) func TestClientSessionCannotExpandToSameNameOtherLine(t *testing.T) { if err := store.Init(t.TempDir()); err != nil { t.Fatal(err) } hashA, _ := bcrypt.GenerateFromPassword([]byte("password-a"), bcrypt.MinCost) hashB, _ := bcrypt.GenerateFromPassword([]byte("password-b"), bcrypt.MinCost) lineA := model.Tunnel{Name: "A", Protocol: "wireguard"} lineB := model.Tunnel{Name: "B", Protocol: "wireguard"} store.DB.Create(&lineA) store.DB.Create(&lineB) uA := model.VPNUser{TunnelID: lineA.ID, Username: "same", Password: string(hashA), Enabled: true, Source: "local"} uB := model.VPNUser{TunnelID: lineB.ID, Username: "same", Password: string(hashB), Enabled: true, Source: "local"} store.DB.Create(&uA) store.DB.Create(&uB) passed, _, err := authorizedLines("same", "password-a", "") if err != nil || len(passed) != 1 || passed[0].ID != uA.ID { t.Fatalf("错误的授权记录: %+v, %v", passed, err) } token, _, err := issueClientToken("same", "laptop", "linux", passed) if err != nil { t.Fatal(err) } var session model.ClientSession if err := store.DB.Where("token_hash = ?", clientTokenHash(token)).First(&session).Error; err != nil { t.Fatal(err) } granted := currentGrantedUsers(&session) if _, _, err := clientAuthorizedTunnel(granted, lineB.ID); err == nil { t.Fatal("错误密码的同名线路不得获得授权") } if _, _, err := clientAuthorizedTunnel(granted, lineA.ID); err != nil { t.Fatal(err) } store.DB.Model(&uA).Update("password", string(hashB)) if got := currentGrantedUsers(&session); len(got) != 0 { t.Fatalf("修改密码后旧授权必须失效: %+v", got) } } func TestAllocateClientAddressSkipsUsedAndBoundaries(t *testing.T) { used := map[string]bool{ "10.8.0.1": true, // 服务端自身 "10.8.0.2": true, // 已占用 Peer } addr, err := allocateClientAddress("10.8.0.1/24", used) if err != nil { t.Fatalf("allocate: %v", err) } if addr != "10.8.0.3/32" { t.Fatalf("应分配第一个空闲地址 10.8.0.3/32,得到 %s", addr) } } func TestAllocateClientAddressSmallSubnet(t *testing.T) { // /30 只有 2 个可用主机位,均被占用时必须报错而不是返回网络/广播地址 used := map[string]bool{"10.8.0.1": true, "10.8.0.2": true} if _, err := allocateClientAddress("10.8.0.1/30", used); err == nil { t.Fatal("地址耗尽时必须报错") } if _, err := allocateClientAddress("10.8.0.1/31", map[string]bool{}); err == nil { t.Fatal("/31 无可用主机位,必须报错") } if _, err := allocateClientAddress("fd00::1/64", map[string]bool{}); err == nil { t.Fatal("IPv6 网段暂不支持自动分配,必须明确报错") } } func TestAllocateClientAddressExhausted(t *testing.T) { used := map[string]bool{"192.168.1.1": true, "192.168.1.2": true, "192.168.1.3": true, "192.168.1.4": true, "192.168.1.5": true, "192.168.1.6": true} if _, err := allocateClientAddress("192.168.1.1/29", used); err == nil { t.Fatal("/29 全部占用时必须报错") } }