1
1
package integration
2
2
3
3
import (
4
+ "cmp"
4
5
"encoding/json"
5
6
"fmt"
6
- "sort"
7
7
"strings"
8
8
"testing"
9
9
"time"
10
10
11
+ tcmp "github.com/google/go-cmp/cmp"
12
+ "github.com/google/go-cmp/cmp/cmpopts"
11
13
v1 "github.com/juanfont/headscale/gen/go/headscale/v1"
12
14
"github.com/juanfont/headscale/hscontrol/policy"
13
15
"github.com/juanfont/headscale/integration/hsic"
14
16
"github.com/juanfont/headscale/integration/tsic"
15
17
"github.com/stretchr/testify/assert"
18
+ "github.com/stretchr/testify/require"
16
19
"golang.org/x/exp/slices"
17
20
)
18
21
@@ -30,6 +33,16 @@ func executeAndUnmarshal[T any](headscale ControlServer, command []string, resul
30
33
return nil
31
34
}
32
35
36
+ // Interface ensuring that we can sort structs from gRPC that
37
+ // have an ID field.
38
+ type GRPCSortable interface {
39
+ GetId () uint64
40
+ }
41
+
42
+ func sortWithID [T GRPCSortable ](a , b T ) int {
43
+ return cmp .Compare (a .GetId (), b .GetId ())
44
+ }
45
+
33
46
func TestUserCommand (t * testing.T ) {
34
47
IntegrationSkip (t )
35
48
t .Parallel ()
@@ -49,7 +62,7 @@ func TestUserCommand(t *testing.T) {
49
62
headscale , err := scenario .Headscale ()
50
63
assertNoErr (t , err )
51
64
52
- var listUsers []v1.User
65
+ var listUsers []* v1.User
53
66
err = executeAndUnmarshal (headscale ,
54
67
[]string {
55
68
"headscale" ,
@@ -62,8 +75,8 @@ func TestUserCommand(t *testing.T) {
62
75
)
63
76
assertNoErr (t , err )
64
77
78
+ slices .SortFunc (listUsers , sortWithID )
65
79
result := []string {listUsers [0 ].GetName (), listUsers [1 ].GetName ()}
66
- sort .Strings (result )
67
80
68
81
assert .Equal (
69
82
t ,
@@ -76,15 +89,14 @@ func TestUserCommand(t *testing.T) {
76
89
"headscale" ,
77
90
"users" ,
78
91
"rename" ,
79
- "--output" ,
80
- "json" ,
81
- "user2" ,
82
- "newname" ,
92
+ "--output=json" ,
93
+ fmt .Sprintf ("--identifier=%d" , listUsers [1 ].GetId ()),
94
+ "--new-name=newname" ,
83
95
},
84
96
)
85
97
assertNoErr (t , err )
86
98
87
- var listAfterRenameUsers []v1.User
99
+ var listAfterRenameUsers []* v1.User
88
100
err = executeAndUnmarshal (headscale ,
89
101
[]string {
90
102
"headscale" ,
@@ -97,14 +109,131 @@ func TestUserCommand(t *testing.T) {
97
109
)
98
110
assertNoErr (t , err )
99
111
112
+ slices .SortFunc (listUsers , sortWithID )
100
113
result = []string {listAfterRenameUsers [0 ].GetName (), listAfterRenameUsers [1 ].GetName ()}
101
- sort .Strings (result )
102
114
103
115
assert .Equal (
104
116
t ,
105
- []string {"newname " , "user1 " },
117
+ []string {"user1 " , "newname " },
106
118
result ,
107
119
)
120
+
121
+ var listByUsername []* v1.User
122
+ err = executeAndUnmarshal (headscale ,
123
+ []string {
124
+ "headscale" ,
125
+ "users" ,
126
+ "list" ,
127
+ "--output" ,
128
+ "json" ,
129
+ "--name=user1" ,
130
+ },
131
+ & listByUsername ,
132
+ )
133
+ assertNoErr (t , err )
134
+
135
+ slices .SortFunc (listByUsername , sortWithID )
136
+ want := []* v1.User {
137
+ {
138
+ Id : 1 ,
139
+ Name : "user1" ,
140
+ },
141
+ }
142
+
143
+ if diff := tcmp .Diff (want , listByUsername , cmpopts .IgnoreUnexported (v1.User {}), cmpopts .IgnoreFields (v1.User {}, "CreatedAt" )); diff != "" {
144
+ t .Errorf ("unexpected users (-want +got):\n %s" , diff )
145
+ }
146
+
147
+ var listByID []* v1.User
148
+ err = executeAndUnmarshal (headscale ,
149
+ []string {
150
+ "headscale" ,
151
+ "users" ,
152
+ "list" ,
153
+ "--output" ,
154
+ "json" ,
155
+ "--identifier=1" ,
156
+ },
157
+ & listByID ,
158
+ )
159
+ assertNoErr (t , err )
160
+
161
+ slices .SortFunc (listByID , sortWithID )
162
+ want = []* v1.User {
163
+ {
164
+ Id : 1 ,
165
+ Name : "user1" ,
166
+ },
167
+ }
168
+
169
+ if diff := tcmp .Diff (want , listByID , cmpopts .IgnoreUnexported (v1.User {}), cmpopts .IgnoreFields (v1.User {}, "CreatedAt" )); diff != "" {
170
+ t .Errorf ("unexpected users (-want +got):\n %s" , diff )
171
+ }
172
+
173
+ deleteResult , err := headscale .Execute (
174
+ []string {
175
+ "headscale" ,
176
+ "users" ,
177
+ "destroy" ,
178
+ "--force" ,
179
+ // Delete "user1"
180
+ "--identifier=1" ,
181
+ },
182
+ )
183
+ assert .Nil (t , err )
184
+ assert .Contains (t , deleteResult , "User destroyed" )
185
+
186
+ var listAfterIDDelete []* v1.User
187
+ err = executeAndUnmarshal (headscale ,
188
+ []string {
189
+ "headscale" ,
190
+ "users" ,
191
+ "list" ,
192
+ "--output" ,
193
+ "json" ,
194
+ },
195
+ & listAfterIDDelete ,
196
+ )
197
+ assertNoErr (t , err )
198
+
199
+ slices .SortFunc (listAfterIDDelete , sortWithID )
200
+ want = []* v1.User {
201
+ {
202
+ Id : 2 ,
203
+ Name : "newname" ,
204
+ },
205
+ }
206
+
207
+ if diff := tcmp .Diff (want , listAfterIDDelete , cmpopts .IgnoreUnexported (v1.User {}), cmpopts .IgnoreFields (v1.User {}, "CreatedAt" )); diff != "" {
208
+ t .Errorf ("unexpected users (-want +got):\n %s" , diff )
209
+ }
210
+
211
+ deleteResult , err = headscale .Execute (
212
+ []string {
213
+ "headscale" ,
214
+ "users" ,
215
+ "destroy" ,
216
+ "--force" ,
217
+ "--name=newname" ,
218
+ },
219
+ )
220
+ assert .Nil (t , err )
221
+ assert .Contains (t , deleteResult , "User destroyed" )
222
+
223
+ var listAfterNameDelete []v1.User
224
+ err = executeAndUnmarshal (headscale ,
225
+ []string {
226
+ "headscale" ,
227
+ "users" ,
228
+ "list" ,
229
+ "--output" ,
230
+ "json" ,
231
+ },
232
+ & listAfterNameDelete ,
233
+ )
234
+ assertNoErr (t , err )
235
+
236
+ require .Len (t , listAfterNameDelete , 0 )
108
237
}
109
238
110
239
func TestPreAuthKeyCommand (t * testing.T ) {
@@ -1716,4 +1845,3 @@ func TestPolicyBrokenConfigCommand(t *testing.T) {
1716
1845
)
1717
1846
assert .ErrorContains (t , err , "acl policy not found" )
1718
1847
}
1719
-
0 commit comments