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

@ -35,28 +35,45 @@ func TestConnectionLifecycle(t *testing.T) {
servertest.AssertSymmetricVisibility(t, h.Clients())
})
t.Run("node_departs_peers_update", func(t *testing.T) {
t.Run("node_departs_peer_goes_offline", func(t *testing.T) {
t.Parallel()
h := servertest.NewHarness(t, 3)
departingName := h.Client(2).Name
h.Client(2).Disconnect(t)
// The remaining clients should eventually see the departed
// node go offline or disappear. The grace period in poll.go
// is 10 seconds, so we need a generous timeout.
h.Client(0).WaitForCondition(t, "peer offline or gone", 60*time.Second,
// First verify the departing node is online (may need a moment
// for Online status to propagate after mesh formation).
h.Client(0).WaitForCondition(t, "peer initially online", 15*time.Second,
func(nm *netmap.NetworkMap) bool {
for _, p := range nm.Peers {
hi := p.Hostinfo()
if hi.Valid() && hi.Hostname() == departingName {
isOnline, known := p.Online().GetOk()
// Peer is still present but offline is acceptable.
return known && isOnline
}
}
return false
})
h.Client(2).Disconnect(t)
// After the 10-second grace period, the remaining clients
// should see the departed node as offline. The peer stays
// in the peer list (non-ephemeral nodes are not removed).
h.Client(0).WaitForCondition(t, "peer goes offline", 30*time.Second,
func(nm *netmap.NetworkMap) bool {
for _, p := range nm.Peers {
hi := p.Hostinfo()
if hi.Valid() && hi.Hostname() == departingName {
isOnline, known := p.Online().GetOk()
return known && !isOnline
}
}
// Peer gone entirely is also acceptable.
return true
return false
})
})