diff --git a/hscontrol/policy/v2/tailscale_ssh_data_compat_test.go b/hscontrol/policy/v2/tailscale_ssh_data_compat_test.go index b04e799fa..60a12d8df 100644 --- a/hscontrol/policy/v2/tailscale_ssh_data_compat_test.go +++ b/hscontrol/policy/v2/tailscale_ssh_data_compat_test.go @@ -189,15 +189,12 @@ func TestSSHDataCompat(t *testing.T) { nodeName, ) - // Build expected SSHPolicy from the typed rules. - var wantSSH *tailcfg.SSHPolicy - if len(capture.SSHRules) > 0 { - wantSSH = &tailcfg.SSHPolicy{Rules: capture.SSHRules} - } - - // Normalize: treat empty-rules SSHPolicy as nil - if gotSSH != nil && len(gotSSH.Rules) == 0 { - gotSSH = nil + // Nil and empty SSHPolicy differ on the wire: nil + // keeps the client's previous rules, empty clears + // them. Take presence from the captured netmap. + wantSSH := &tailcfg.SSHPolicy{Rules: capture.SSHRules} + if capture.Netmap != nil && capture.Netmap.SSHPolicy == nil { + wantSSH = nil } // Compare headscale output against Tailscale expected. @@ -220,6 +217,17 @@ func TestSSHDataCompat(t *testing.T) { ) } + // EquateEmpty hides "rules":null vs "rules":[]; + // pin the captured shape separately. + if gotSSH != nil && capture.Netmap != nil && + capture.Netmap.SSHPolicy != nil { + assert.Equalf(t, + capture.Netmap.SSHPolicy.Rules == nil, + gotSSH.Rules == nil, + "%s/%s: rules null-vs-[] mismatch", tf.TestID, nodeName, + ) + } + // Separate presence check: the fields ignored by // the diff above must still be populated on matching // rules. This catches regressions where headscale