package integration import ( "net/netip" "testing" "slices" "github.com/juanfont/headscale/integration/hsic" "github.com/samber/lo" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) func TestAuthWebFlowAuthenticationPingAll(t *testing.T) { IntegrationSkip(t) spec := ScenarioSpec{ NodesPerUser: len(MustTestVersions), Users: []string{"user1", "user2"}, } scenario, err := NewScenario(spec) if err != nil { t.Fatalf("failed to create scenario: %s", err) } defer scenario.ShutdownAssertNoPanics(t) err = scenario.CreateHeadscaleEnv( nil, hsic.WithTestName("webauthping"), hsic.WithEmbeddedDERPServerOnly(), hsic.WithTLS(), ) assertNoErrHeadscaleEnv(t, err) allClients, err := scenario.ListTailscaleClients() assertNoErrListClients(t, err) allIps, err := scenario.ListTailscaleClientsIPs() assertNoErrListClientIPs(t, err) err = scenario.WaitForTailscaleSync() assertNoErrSync(t, err) // assertClientsState(t, allClients) allAddrs := lo.Map(allIps, func(x netip.Addr, index int) string { return x.String() }) success := pingAllHelper(t, allClients, allAddrs) t.Logf("%d successful pings out of %d", success, len(allClients)*len(allIps)) } func TestAuthWebFlowLogoutAndRelogin(t *testing.T) { IntegrationSkip(t) t.Parallel() spec := ScenarioSpec{ NodesPerUser: len(MustTestVersions), Users: []string{"user1", "user2"}, } scenario, err := NewScenario(spec) assertNoErr(t, err) defer scenario.ShutdownAssertNoPanics(t) err = scenario.CreateHeadscaleEnv( nil, hsic.WithTestName("weblogout"), hsic.WithTLS(), ) assertNoErrHeadscaleEnv(t, err) allClients, err := scenario.ListTailscaleClients() assertNoErrListClients(t, err) allIps, err := scenario.ListTailscaleClientsIPs() assertNoErrListClientIPs(t, err) err = scenario.WaitForTailscaleSync() assertNoErrSync(t, err) // assertClientsState(t, allClients) allAddrs := lo.Map(allIps, func(x netip.Addr, index int) string { return x.String() }) success := pingAllHelper(t, allClients, allAddrs) t.Logf("%d successful pings out of %d", success, len(allClients)*len(allIps)) headscale, err := scenario.Headscale() assertNoErrGetHeadscale(t, err) listNodes, err := headscale.ListNodes() assert.Equal(t, len(listNodes), len(allClients)) nodeCountBeforeLogout := len(listNodes) t.Logf("node count before logout: %d", nodeCountBeforeLogout) clientIPs := make(map[TailscaleClient][]netip.Addr) for _, client := range allClients { ips, err := client.IPs() if err != nil { t.Fatalf("failed to get IPs for client %s: %s", client.Hostname(), err) } clientIPs[client] = ips } for _, client := range allClients { err := client.Logout() if err != nil { t.Fatalf("failed to logout client %s: %s", client.Hostname(), err) } } err = scenario.WaitForTailscaleLogout() assertNoErrLogout(t, err) t.Logf("all clients logged out") for _, userName := range spec.Users { err = scenario.RunTailscaleUpWithURL(userName, headscale.GetEndpoint()) if err != nil { t.Fatalf("failed to run tailscale up (%q): %s", headscale.GetEndpoint(), err) } } t.Logf("all clients logged in again") allIps, err = scenario.ListTailscaleClientsIPs() assertNoErrListClientIPs(t, err) allAddrs = lo.Map(allIps, func(x netip.Addr, index int) string { return x.String() }) success = pingAllHelper(t, allClients, allAddrs) t.Logf("%d successful pings out of %d", success, len(allClients)*len(allIps)) listNodes, err = headscale.ListNodes() require.Equal(t, nodeCountBeforeLogout, len(listNodes)) t.Logf("node count first login: %d, after relogin: %d", nodeCountBeforeLogout, len(listNodes)) for _, client := range allClients { ips, err := client.IPs() if err != nil { t.Fatalf("failed to get IPs for client %s: %s", client.Hostname(), err) } // lets check if the IPs are the same if len(ips) != len(clientIPs[client]) { t.Fatalf("IPs changed for client %s", client.Hostname()) } for _, ip := range ips { found := slices.Contains(clientIPs[client], ip) if !found { t.Fatalf( "IPs changed for client %s. Used to be %v now %v", client.Hostname(), clientIPs[client], ips, ) } } } t.Logf("all clients IPs are the same") }