integration: fix tag tests for tagged nodes with nil user_id

Tagged nodes no longer have user_id set, so ListNodes(user) cannot
find them. Update integration tests to use ListNodes() (all nodes)
when looking up tagged nodes.

Add a findNode helper to locate nodes by predicate from an
unfiltered list, used in ACL tests that have multiple nodes per
scenario.

Updates #3077
This commit is contained in:
Kristoffer Dalby
2026-02-20 13:02:51 +00:00
parent 1e4fc3f179
commit be4fd9ff2d
3 changed files with 93 additions and 89 deletions

View File

@@ -2459,10 +2459,14 @@ func TestACLTagPropagation(t *testing.T) {
err = scenario.WaitForTailscaleSync()
require.NoError(t, err)
nodes, err := headscale.ListNodes("user1")
// Tagged nodes have no user_id, so list all and find by tag.
allNodes, err := headscale.ListNodes()
require.NoError(t, err)
return user2Node, user1Node, nodes[0].GetId()
tagged := findNode(allNodes, func(n *v1.Node) bool { return len(n.GetTags()) > 0 })
require.NotNil(t, tagged, "expected a tagged node")
return user2Node, user1Node, tagged.GetId()
},
initialAccess: true, // user2 can access user1 (has tag:shared)
tagChange: []string{"tag:other"}, // replace with tag:other
@@ -2552,10 +2556,14 @@ func TestACLTagPropagation(t *testing.T) {
err = scenario.WaitForTailscaleSync()
require.NoError(t, err)
nodes, err := headscale.ListNodes("user1")
// Tagged nodes have no user_id, so list all and find by tag.
allNodes, err := headscale.ListNodes()
require.NoError(t, err)
return user2Node, user1Node, nodes[0].GetId()
tagged := findNode(allNodes, func(n *v1.Node) bool { return len(n.GetTags()) > 0 })
require.NotNil(t, tagged, "expected a tagged node")
return user2Node, user1Node, tagged.GetId()
},
initialAccess: false, // user2 cannot access (tag:team-a not in ACL)
tagChange: []string{"tag:team-b"}, // change to tag:team-b
@@ -2645,10 +2653,14 @@ func TestACLTagPropagation(t *testing.T) {
err = scenario.WaitForTailscaleSync()
require.NoError(t, err)
nodes, err := headscale.ListNodes("user1")
// Tagged nodes have no user_id, so list all and find by tag.
allNodes, err := headscale.ListNodes()
require.NoError(t, err)
return user2Node, user1Node, nodes[0].GetId()
tagged := findNode(allNodes, func(n *v1.Node) bool { return len(n.GetTags()) > 0 })
require.NotNil(t, tagged, "expected a tagged node")
return user2Node, user1Node, tagged.GetId()
},
initialAccess: true, // user2 can access (has tag:web)
tagChange: []string{"tag:internal"}, // remove tag:web, keep tag:internal
@@ -2783,34 +2795,10 @@ func TestACLTagPropagation(t *testing.T) {
// Verify tag was applied
assert.EventuallyWithT(t, func(c *assert.CollectT) {
// List nodes by iterating through all users since tagged nodes may "move"
var node *v1.Node
for _, user := range tt.spec.Users {
nodes, err := headscale.ListNodes(user)
if err != nil {
continue
}
for _, n := range nodes {
if n.GetId() == targetNodeID {
node = n
break
}
}
}
// Also check nodes without user filter
if node == nil {
// Try listing all nodes
allNodes, _ := headscale.ListNodes("")
for _, n := range allNodes {
if n.GetId() == targetNodeID {
node = n
break
}
}
}
allNodes, err := headscale.ListNodes()
assert.NoError(c, err)
node := findNode(allNodes, func(n *v1.Node) bool { return n.GetId() == targetNodeID })
assert.NotNil(c, node, "Node should still exist")
if node != nil {
@@ -2991,10 +2979,14 @@ func TestACLTagPropagationPortSpecific(t *testing.T) {
err = scenario.WaitForTailscaleSync()
require.NoError(t, err)
nodes, err := headscale.ListNodes("user1")
// Tagged nodes have no user_id, so list all and find by tag.
allNodes, err := headscale.ListNodes()
require.NoError(t, err)
targetNodeID := nodes[0].GetId()
tagged := findNode(allNodes, func(n *v1.Node) bool { return len(n.GetTags()) > 0 })
require.NotNil(t, tagged, "expected a tagged node")
targetNodeID := tagged.GetId()
targetFQDN, err := user1Node.FQDN()
require.NoError(t, err)

View File

@@ -1004,6 +1004,18 @@ func GetUserByName(headscale ControlServer, username string) (*v1.User, error) {
return nil, fmt.Errorf("user %s not found", username) //nolint:err113
}
// findNode returns the first node in nodes for which match returns true,
// or nil if no node matches.
func findNode(nodes []*v1.Node, match func(*v1.Node) bool) *v1.Node {
for _, n := range nodes {
if match(n) {
return n
}
}
return nil
}
// FindNewClient finds a client that is in the new list but not in the original list.
// This is useful when dynamically adding nodes during tests and needing to identify
// which client was just added.

View File

@@ -176,7 +176,7 @@ func TestTagsAuthKeyWithTagRequestDifferentTag(t *testing.T) {
// Check what tags the node actually has
assert.EventuallyWithT(t, func(c *assert.CollectT) {
nodes, err := headscale.ListNodes(tagTestUser)
nodes, err := headscale.ListNodes()
assert.NoError(c, err)
if len(nodes) == 1 {
@@ -244,7 +244,7 @@ func TestTagsAuthKeyWithTagNoAdvertiseFlag(t *testing.T) {
// Wait for node to be registered and verify it has the key's tags
assert.EventuallyWithT(t, func(c *assert.CollectT) {
nodes, err := headscale.ListNodes(tagTestUser)
nodes, err := headscale.ListNodes()
assert.NoError(c, err)
assert.Len(c, nodes, 1, "Should have exactly 1 node")
@@ -315,7 +315,7 @@ func TestTagsAuthKeyWithTagCannotAddViaCLI(t *testing.T) {
// Wait for initial registration
assert.EventuallyWithT(t, func(c *assert.CollectT) {
nodes, err := headscale.ListNodes(tagTestUser)
nodes, err := headscale.ListNodes()
assert.NoError(c, err)
assert.Len(c, nodes, 1)
@@ -343,7 +343,7 @@ func TestTagsAuthKeyWithTagCannotAddViaCLI(t *testing.T) {
// Check if tags actually changed
assert.EventuallyWithT(t, func(c *assert.CollectT) {
nodes, err := headscale.ListNodes(tagTestUser)
nodes, err := headscale.ListNodes()
assert.NoError(c, err)
if len(nodes) == 1 {
@@ -416,7 +416,7 @@ func TestTagsAuthKeyWithTagCannotChangeViaCLI(t *testing.T) {
// Wait for initial registration
assert.EventuallyWithT(t, func(c *assert.CollectT) {
nodes, err := headscale.ListNodes(tagTestUser)
nodes, err := headscale.ListNodes()
assert.NoError(c, err)
assert.Len(c, nodes, 1)
}, 30*time.Second, 500*time.Millisecond, "waiting for initial registration")
@@ -440,7 +440,7 @@ func TestTagsAuthKeyWithTagCannotChangeViaCLI(t *testing.T) {
// Check if tags remain unchanged
assert.EventuallyWithT(t, func(c *assert.CollectT) {
nodes, err := headscale.ListNodes(tagTestUser)
nodes, err := headscale.ListNodes()
assert.NoError(c, err)
if len(nodes) == 1 {
@@ -515,7 +515,7 @@ func TestTagsAuthKeyWithTagAdminOverrideReauthPreserves(t *testing.T) {
var nodeID uint64
assert.EventuallyWithT(t, func(c *assert.CollectT) {
nodes, err := headscale.ListNodes(tagTestUser)
nodes, err := headscale.ListNodes()
assert.NoError(c, err)
assert.Len(c, nodes, 1)
@@ -533,7 +533,7 @@ func TestTagsAuthKeyWithTagAdminOverrideReauthPreserves(t *testing.T) {
// Verify admin assignment took effect (server-side)
assert.EventuallyWithT(t, func(c *assert.CollectT) {
nodes, err := headscale.ListNodes(tagTestUser)
nodes, err := headscale.ListNodes()
assert.NoError(c, err)
if len(nodes) == 1 {
@@ -561,7 +561,7 @@ func TestTagsAuthKeyWithTagAdminOverrideReauthPreserves(t *testing.T) {
// Verify admin tags are preserved even after reauth - admin decisions are authoritative (server-side)
assert.EventuallyWithT(t, func(c *assert.CollectT) {
nodes, err := headscale.ListNodes(tagTestUser)
nodes, err := headscale.ListNodes()
assert.NoError(c, err)
assert.GreaterOrEqual(c, len(nodes), 1, "Should have at least 1 node")
@@ -643,7 +643,7 @@ func TestTagsAuthKeyWithTagCLICannotModifyAdminTags(t *testing.T) {
var nodeID uint64
assert.EventuallyWithT(t, func(c *assert.CollectT) {
nodes, err := headscale.ListNodes(tagTestUser)
nodes, err := headscale.ListNodes()
assert.NoError(c, err)
assert.Len(c, nodes, 1)
@@ -658,7 +658,7 @@ func TestTagsAuthKeyWithTagCLICannotModifyAdminTags(t *testing.T) {
// Verify admin assignment (server-side)
assert.EventuallyWithT(t, func(c *assert.CollectT) {
nodes, err := headscale.ListNodes(tagTestUser)
nodes, err := headscale.ListNodes()
assert.NoError(c, err)
if len(nodes) == 1 {
@@ -686,7 +686,7 @@ func TestTagsAuthKeyWithTagCLICannotModifyAdminTags(t *testing.T) {
// Verify admin tags are preserved - CLI should not be able to reduce them (server-side)
assert.EventuallyWithT(t, func(c *assert.CollectT) {
nodes, err := headscale.ListNodes(tagTestUser)
nodes, err := headscale.ListNodes()
assert.NoError(c, err)
assert.Len(c, nodes, 1, "Should have exactly 1 node")
@@ -770,7 +770,7 @@ func TestTagsAuthKeyWithoutTagCannotRequestTags(t *testing.T) {
t.Logf("Test 3.1 UNEXPECTED: Registration succeeded when it should have failed")
assert.EventuallyWithT(t, func(c *assert.CollectT) {
nodes, err := headscale.ListNodes(tagTestUser)
nodes, err := headscale.ListNodes()
assert.NoError(c, err)
if len(nodes) == 1 {
@@ -836,7 +836,7 @@ func TestTagsAuthKeyWithoutTagRegisterNoTags(t *testing.T) {
// Verify node has no tags
assert.EventuallyWithT(t, func(c *assert.CollectT) {
nodes, err := headscale.ListNodes(tagTestUser)
nodes, err := headscale.ListNodes()
assert.NoError(c, err)
assert.Len(c, nodes, 1)
@@ -906,7 +906,7 @@ func TestTagsAuthKeyWithoutTagCannotAddViaCLI(t *testing.T) {
// Wait for initial registration
assert.EventuallyWithT(t, func(c *assert.CollectT) {
nodes, err := headscale.ListNodes(tagTestUser)
nodes, err := headscale.ListNodes()
assert.NoError(c, err)
assert.Len(c, nodes, 1)
@@ -933,7 +933,7 @@ func TestTagsAuthKeyWithoutTagCannotAddViaCLI(t *testing.T) {
t.Logf("Test 3.3: CLI command succeeded, checking if tags actually changed")
assert.EventuallyWithT(t, func(c *assert.CollectT) {
nodes, err := headscale.ListNodes(tagTestUser)
nodes, err := headscale.ListNodes()
assert.NoError(c, err)
if len(nodes) == 1 {
@@ -1008,7 +1008,7 @@ func TestTagsAuthKeyWithoutTagCLINoOpAfterAdminWithReset(t *testing.T) {
var nodeID uint64
assert.EventuallyWithT(t, func(c *assert.CollectT) {
nodes, err := headscale.ListNodes(tagTestUser)
nodes, err := headscale.ListNodes()
assert.NoError(c, err)
assert.Len(c, nodes, 1)
@@ -1024,7 +1024,7 @@ func TestTagsAuthKeyWithoutTagCLINoOpAfterAdminWithReset(t *testing.T) {
// Verify admin assignment (server-side)
assert.EventuallyWithT(t, func(c *assert.CollectT) {
nodes, err := headscale.ListNodes(tagTestUser)
nodes, err := headscale.ListNodes()
assert.NoError(c, err)
if len(nodes) == 1 {
@@ -1051,7 +1051,7 @@ func TestTagsAuthKeyWithoutTagCLINoOpAfterAdminWithReset(t *testing.T) {
// Verify admin tags are preserved - --reset should not remove them (server-side)
assert.EventuallyWithT(t, func(c *assert.CollectT) {
nodes, err := headscale.ListNodes(tagTestUser)
nodes, err := headscale.ListNodes()
assert.NoError(c, err)
assert.Len(c, nodes, 1, "Should have exactly 1 node")
@@ -1129,7 +1129,7 @@ func TestTagsAuthKeyWithoutTagCLINoOpAfterAdminWithEmptyAdvertise(t *testing.T)
var nodeID uint64
assert.EventuallyWithT(t, func(c *assert.CollectT) {
nodes, err := headscale.ListNodes(tagTestUser)
nodes, err := headscale.ListNodes()
assert.NoError(c, err)
assert.Len(c, nodes, 1)
@@ -1144,7 +1144,7 @@ func TestTagsAuthKeyWithoutTagCLINoOpAfterAdminWithEmptyAdvertise(t *testing.T)
// Verify admin assignment (server-side)
assert.EventuallyWithT(t, func(c *assert.CollectT) {
nodes, err := headscale.ListNodes(tagTestUser)
nodes, err := headscale.ListNodes()
assert.NoError(c, err)
if len(nodes) == 1 {
@@ -1171,7 +1171,7 @@ func TestTagsAuthKeyWithoutTagCLINoOpAfterAdminWithEmptyAdvertise(t *testing.T)
// Verify admin tags are preserved - empty --advertise-tags should not remove them (server-side)
assert.EventuallyWithT(t, func(c *assert.CollectT) {
nodes, err := headscale.ListNodes(tagTestUser)
nodes, err := headscale.ListNodes()
assert.NoError(c, err)
assert.Len(c, nodes, 1, "Should have exactly 1 node")
@@ -1249,7 +1249,7 @@ func TestTagsAuthKeyWithoutTagCLICannotReduceAdminMultiTag(t *testing.T) {
var nodeID uint64
assert.EventuallyWithT(t, func(c *assert.CollectT) {
nodes, err := headscale.ListNodes(tagTestUser)
nodes, err := headscale.ListNodes()
assert.NoError(c, err)
assert.Len(c, nodes, 1)
@@ -1264,7 +1264,7 @@ func TestTagsAuthKeyWithoutTagCLICannotReduceAdminMultiTag(t *testing.T) {
// Verify admin assignment (server-side)
assert.EventuallyWithT(t, func(c *assert.CollectT) {
nodes, err := headscale.ListNodes(tagTestUser)
nodes, err := headscale.ListNodes()
assert.NoError(c, err)
if len(nodes) == 1 {
@@ -1291,7 +1291,7 @@ func TestTagsAuthKeyWithoutTagCLICannotReduceAdminMultiTag(t *testing.T) {
// Verify admin tags are preserved - CLI should not be able to reduce them (server-side)
assert.EventuallyWithT(t, func(c *assert.CollectT) {
nodes, err := headscale.ListNodes(tagTestUser)
nodes, err := headscale.ListNodes()
assert.NoError(c, err)
assert.Len(c, nodes, 1, "Should have exactly 1 node")
@@ -1373,7 +1373,7 @@ func TestTagsUserLoginOwnedTagAtRegistration(t *testing.T) {
// Verify node has the advertised tag
assert.EventuallyWithT(t, func(c *assert.CollectT) {
nodes, err := headscale.ListNodes(tagTestUser)
nodes, err := headscale.ListNodes()
assert.NoError(c, err)
assert.Len(c, nodes, 1, "Should have exactly 1 node")
@@ -1444,7 +1444,7 @@ func TestTagsUserLoginNonExistentTagAtRegistration(t *testing.T) {
} else {
// Check the result - if registration succeeded, the node should not have the invalid tag
assert.EventuallyWithT(t, func(c *assert.CollectT) {
nodes, err := headscale.ListNodes(tagTestUser)
nodes, err := headscale.ListNodes()
assert.NoError(c, err, "Should be able to list nodes")
if len(nodes) == 0 {
@@ -1512,7 +1512,7 @@ func TestTagsUserLoginUnownedTagAtRegistration(t *testing.T) {
// Check the result - user should NOT be able to claim an unowned tag
assert.EventuallyWithT(t, func(c *assert.CollectT) {
nodes, err := headscale.ListNodes(tagTestUser)
nodes, err := headscale.ListNodes()
assert.NoError(c, err, "Should be able to list nodes")
// Either: no nodes registered (ideal), or node registered without the unowned tag
@@ -1583,7 +1583,7 @@ func TestTagsUserLoginAddTagViaCLIReauth(t *testing.T) {
// Verify initial tag
assert.EventuallyWithT(t, func(c *assert.CollectT) {
nodes, err := headscale.ListNodes(tagTestUser)
nodes, err := headscale.ListNodes()
assert.NoError(c, err)
if len(nodes) == 1 {
@@ -1604,7 +1604,7 @@ func TestTagsUserLoginAddTagViaCLIReauth(t *testing.T) {
// Check final state - EventuallyWithT handles waiting for propagation
assert.EventuallyWithT(t, func(c *assert.CollectT) {
nodes, err := headscale.ListNodes(tagTestUser)
nodes, err := headscale.ListNodes()
assert.NoError(c, err)
if len(nodes) >= 1 {
@@ -1675,7 +1675,7 @@ func TestTagsUserLoginRemoveTagViaCLIReauth(t *testing.T) {
// Verify initial tags
assert.EventuallyWithT(t, func(c *assert.CollectT) {
nodes, err := headscale.ListNodes(tagTestUser)
nodes, err := headscale.ListNodes()
assert.NoError(c, err)
if len(nodes) == 1 {
@@ -1696,7 +1696,7 @@ func TestTagsUserLoginRemoveTagViaCLIReauth(t *testing.T) {
// Check final state - EventuallyWithT handles waiting for propagation
assert.EventuallyWithT(t, func(c *assert.CollectT) {
nodes, err := headscale.ListNodes(tagTestUser)
nodes, err := headscale.ListNodes()
assert.NoError(c, err)
if len(nodes) >= 1 {
@@ -1769,7 +1769,7 @@ func TestTagsUserLoginCLINoOpAfterAdminAssignment(t *testing.T) {
var nodeID uint64
assert.EventuallyWithT(t, func(c *assert.CollectT) {
nodes, err := headscale.ListNodes(tagTestUser)
nodes, err := headscale.ListNodes()
assert.NoError(c, err)
assert.Len(c, nodes, 1)
@@ -1785,7 +1785,7 @@ func TestTagsUserLoginCLINoOpAfterAdminAssignment(t *testing.T) {
// Verify admin assignment (server-side)
assert.EventuallyWithT(t, func(c *assert.CollectT) {
nodes, err := headscale.ListNodes(tagTestUser)
nodes, err := headscale.ListNodes()
assert.NoError(c, err)
if len(nodes) == 1 {
@@ -1810,7 +1810,7 @@ func TestTagsUserLoginCLINoOpAfterAdminAssignment(t *testing.T) {
// Verify admin tags are preserved - CLI advertise-tags should be a no-op after admin assignment (server-side)
assert.EventuallyWithT(t, func(c *assert.CollectT) {
nodes, err := headscale.ListNodes(tagTestUser)
nodes, err := headscale.ListNodes()
assert.NoError(c, err)
assert.Len(c, nodes, 1, "Should have exactly 1 node")
@@ -1887,7 +1887,7 @@ func TestTagsUserLoginCLICannotRemoveAdminTags(t *testing.T) {
var nodeID uint64
assert.EventuallyWithT(t, func(c *assert.CollectT) {
nodes, err := headscale.ListNodes(tagTestUser)
nodes, err := headscale.ListNodes()
assert.NoError(c, err)
assert.Len(c, nodes, 1)
@@ -1902,7 +1902,7 @@ func TestTagsUserLoginCLICannotRemoveAdminTags(t *testing.T) {
// Verify admin assignment (server-side)
assert.EventuallyWithT(t, func(c *assert.CollectT) {
nodes, err := headscale.ListNodes(tagTestUser)
nodes, err := headscale.ListNodes()
assert.NoError(c, err)
if len(nodes) == 1 {
@@ -1927,7 +1927,7 @@ func TestTagsUserLoginCLICannotRemoveAdminTags(t *testing.T) {
// Verify admin tags are preserved - CLI should not be able to remove admin-assigned tags (server-side)
assert.EventuallyWithT(t, func(c *assert.CollectT) {
nodes, err := headscale.ListNodes(tagTestUser)
nodes, err := headscale.ListNodes()
assert.NoError(c, err)
assert.Len(c, nodes, 1, "Should have exactly 1 node")
@@ -2008,7 +2008,7 @@ func TestTagsAuthKeyWithTagRequestNonExistentTag(t *testing.T) {
t.Logf("Test 2.7 UNEXPECTED: Registration succeeded when it should have failed")
assert.EventuallyWithT(t, func(c *assert.CollectT) {
nodes, err := headscale.ListNodes(tagTestUser)
nodes, err := headscale.ListNodes()
assert.NoError(c, err)
if len(nodes) == 1 {
@@ -2079,7 +2079,7 @@ func TestTagsAuthKeyWithTagRequestUnownedTag(t *testing.T) {
t.Logf("Test 2.8 UNEXPECTED: Registration succeeded when it should have failed")
assert.EventuallyWithT(t, func(c *assert.CollectT) {
nodes, err := headscale.ListNodes(tagTestUser)
nodes, err := headscale.ListNodes()
assert.NoError(c, err)
if len(nodes) == 1 {
@@ -2154,7 +2154,7 @@ func TestTagsAuthKeyWithoutTagRequestNonExistentTag(t *testing.T) {
t.Logf("Test 3.7 UNEXPECTED: Registration succeeded when it should have failed")
assert.EventuallyWithT(t, func(c *assert.CollectT) {
nodes, err := headscale.ListNodes(tagTestUser)
nodes, err := headscale.ListNodes()
assert.NoError(c, err)
if len(nodes) == 1 {
@@ -2225,7 +2225,7 @@ func TestTagsAuthKeyWithoutTagRequestUnownedTag(t *testing.T) {
t.Logf("Test 3.8 UNEXPECTED: Registration succeeded when it should have failed")
assert.EventuallyWithT(t, func(c *assert.CollectT) {
nodes, err := headscale.ListNodes(tagTestUser)
nodes, err := headscale.ListNodes()
assert.NoError(c, err)
if len(nodes) == 1 {
@@ -2296,7 +2296,7 @@ func TestTagsAdminAPICannotSetNonExistentTag(t *testing.T) {
var nodeID uint64
assert.EventuallyWithT(t, func(c *assert.CollectT) {
nodes, err := headscale.ListNodes(tagTestUser)
nodes, err := headscale.ListNodes()
assert.NoError(c, err)
assert.Len(c, nodes, 1)
@@ -2369,7 +2369,7 @@ func TestTagsAdminAPICanSetUnownedTag(t *testing.T) {
var nodeID uint64
assert.EventuallyWithT(t, func(c *assert.CollectT) {
nodes, err := headscale.ListNodes(tagTestUser)
nodes, err := headscale.ListNodes()
assert.NoError(c, err)
assert.Len(c, nodes, 1)
@@ -2386,7 +2386,7 @@ func TestTagsAdminAPICanSetUnownedTag(t *testing.T) {
// Verify the tag was applied (server-side)
assert.EventuallyWithT(t, func(c *assert.CollectT) {
nodes, err := headscale.ListNodes(tagTestUser)
nodes, err := headscale.ListNodes()
assert.NoError(c, err)
assert.Len(c, nodes, 1)
@@ -2458,7 +2458,7 @@ func TestTagsAdminAPICannotRemoveAllTags(t *testing.T) {
var nodeID uint64
assert.EventuallyWithT(t, func(c *assert.CollectT) {
nodes, err := headscale.ListNodes(tagTestUser)
nodes, err := headscale.ListNodes()
assert.NoError(c, err)
assert.Len(c, nodes, 1)
@@ -2476,7 +2476,7 @@ func TestTagsAdminAPICannotRemoveAllTags(t *testing.T) {
// Verify original tags are preserved
assert.EventuallyWithT(t, func(c *assert.CollectT) {
nodes, err := headscale.ListNodes(tagTestUser)
nodes, err := headscale.ListNodes()
assert.NoError(c, err)
assert.Len(c, nodes, 1)
@@ -2581,7 +2581,7 @@ func TestTagsIssue2978ReproTagReplacement(t *testing.T) {
var nodeID uint64
assert.EventuallyWithT(t, func(c *assert.CollectT) {
nodes, err := headscale.ListNodes(tagTestUser)
nodes, err := headscale.ListNodes()
assert.NoError(c, err)
assert.Len(c, nodes, 1)
@@ -2608,7 +2608,7 @@ func TestTagsIssue2978ReproTagReplacement(t *testing.T) {
// Verify server-side update happened
assert.EventuallyWithT(t, func(c *assert.CollectT) {
nodes, err := headscale.ListNodes(tagTestUser)
nodes, err := headscale.ListNodes()
assert.NoError(c, err)
if len(nodes) == 1 {
@@ -2680,7 +2680,7 @@ func TestTagsIssue2978ReproTagReplacement(t *testing.T) {
// Verify server-side update
assert.EventuallyWithT(t, func(c *assert.CollectT) {
nodes, err := headscale.ListNodes(tagTestUser)
nodes, err := headscale.ListNodes()
assert.NoError(c, err)
if len(nodes) == 1 {
@@ -2782,7 +2782,7 @@ func TestTagsAdminAPICannotSetInvalidFormat(t *testing.T) {
var nodeID uint64
assert.EventuallyWithT(t, func(c *assert.CollectT) {
nodes, err := headscale.ListNodes(tagTestUser)
nodes, err := headscale.ListNodes()
assert.NoError(c, err)
assert.Len(c, nodes, 1)
@@ -2800,7 +2800,7 @@ func TestTagsAdminAPICannotSetInvalidFormat(t *testing.T) {
// Verify original tags are preserved
assert.EventuallyWithT(t, func(c *assert.CollectT) {
nodes, err := headscale.ListNodes(tagTestUser)
nodes, err := headscale.ListNodes()
assert.NoError(c, err)
assert.Len(c, nodes, 1)
@@ -2886,7 +2886,7 @@ func TestTagsUserLoginReauthWithEmptyTagsRemovesAllTags(t *testing.T) {
var initialNodeID uint64
assert.EventuallyWithT(t, func(c *assert.CollectT) {
nodes, err := headscale.ListNodes(tagTestUser)
nodes, err := headscale.ListNodes()
assert.NoError(c, err)
assert.Len(c, nodes, 1, "Expected exactly one node")
@@ -2949,7 +2949,7 @@ func TestTagsUserLoginReauthWithEmptyTagsRemovesAllTags(t *testing.T) {
// Step 3: Verify tags are removed and ownership is returned to user
// This is the key assertion for bug #2979
assert.EventuallyWithT(t, func(c *assert.CollectT) {
nodes, err := headscale.ListNodes(tagTestUser)
nodes, err := headscale.ListNodes()
assert.NoError(c, err)
if len(nodes) >= 1 {