hscontrol/servertest: expand issue tests to 24 scenarios, surface 4 issues

Split TestIssues into 7 focused test functions to stay under cyclomatic
complexity limits while testing more aggressively.

Issues surfaced (4 failing tests):

1. initial_map_should_include_peer_online_status: Initial MapResponse
   has Online=nil for peers. Online status only arrives later via
   PeersChangedPatch.

2. disco_key_should_propagate_to_peers: DiscoPublicKey set by client
   is not visible to peers. Peers see zero disco key.

3. approved_route_without_announcement_is_visible: Server-side route
   approval without client-side announcement silently produces empty
   SubnetRoutes (intersection of empty announced + approved = empty).

4. nodestore_correct_after_rapid_reconnect: After 5 rapid reconnect
   cycles, NodeStore reports node as offline despite having an active
   poll session. The connect/disconnect grace period interleaving
   leaves IsOnline in an incorrect state.

Passing tests (20) verify:
- IP uniqueness across 10 nodes
- IP stability across reconnect
- New peers have addresses immediately
- Node rename propagates to peers
- Node delete removes from all peer lists
- Hostinfo changes (OS field) propagate
- NodeStore/DB consistency after route mutations
- Grace period timing (8-20s window)
- Ephemeral node deletion (not just offline)
- 10-node simultaneous connect convergence
- Rapid sequential node additions
- Reconnect produces complete map
- Cross-user visibility with default policy
- Same-user multiple nodes get distinct IDs
- Same-hostname nodes get unique GivenNames
- Policy change during connect still converges
- DERP region references are valid
- User profiles present for self and peers
- Self-update arrives after route approval
- Route advertisement stored as AnnouncedRoutes
This commit is contained in:
Kristoffer Dalby 2026-03-16 20:20:19 +00:00
parent f87b08676d
commit ab4e205ce7
4 changed files with 905 additions and 37 deletions

View file

@ -7,7 +7,6 @@ import (
"time"
"github.com/juanfont/headscale/hscontrol/servertest"
"github.com/juanfont/headscale/hscontrol/types"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"tailscale.com/tailcfg"
@ -214,19 +213,4 @@ func TestRoutes(t *testing.T) {
})
}
// findNodeID looks up a node's ID by hostname in the server state.
func findNodeID(tb testing.TB, srv *servertest.TestServer, hostname string) types.NodeID {
tb.Helper()
nodes := srv.State().ListNodes()
for i := range nodes.Len() {
n := nodes.At(i)
if n.Hostname() == hostname {
return n.ID()
}
}
tb.Fatalf("node %q not found in server state", hostname)
return 0
}
// findNodeID is defined in issues_test.go.