]> ruderich.org/simon Gitweb - safcm/safcm.git/blob - cmd/safcm/sync_test.go
safcm: forbid syncing groups which depend on "detected" groups
[safcm/safcm.git] / cmd / safcm / sync_test.go
1 // Copyright (C) 2021  Simon Ruderich
2 //
3 // This program is free software: you can redistribute it and/or modify
4 // it under the terms of the GNU General Public License as published by
5 // the Free Software Foundation, either version 3 of the License, or
6 // (at your option) any later version.
7 //
8 // This program is distributed in the hope that it will be useful,
9 // but WITHOUT ANY WARRANTY; without even the implied warranty of
10 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11 // GNU General Public License for more details.
12 //
13 // You should have received a copy of the GNU General Public License
14 // along with this program.  If not, see <http://www.gnu.org/licenses/>.
15
16 package main
17
18 import (
19         "bytes"
20         "fmt"
21         "log"
22         "os"
23         "testing"
24
25         "ruderich.org/simon/safcm"
26         "ruderich.org/simon/safcm/cmd/safcm/config"
27         "ruderich.org/simon/safcm/rpc"
28         "ruderich.org/simon/safcm/testutil"
29 )
30
31 func TestHostsToSync(t *testing.T) {
32         cwd, err := os.Getwd()
33         if err != nil {
34                 t.Fatal(err)
35         }
36         defer os.Chdir(cwd)
37
38         err = os.Chdir("testdata/project")
39         if err != nil {
40                 t.Fatal(err)
41         }
42         _, allHosts, allGroups, err := LoadBaseFiles()
43         if err != nil {
44                 t.Fatal(err)
45         }
46
47         const errMsg = `
48
49 Groups depending on "detected" groups cannot be used to select hosts as these
50 are only available after the hosts were contacted.
51 `
52
53         tests := []struct {
54                 name   string
55                 names  []string
56                 exp    []*config.Host
57                 expErr error
58         }{
59                 {
60                         "empty names",
61                         nil,
62                         nil,
63                         nil,
64                 },
65
66                 {
67                         "no match",
68                         []string{"unknown-host/group"},
69                         nil,
70                         fmt.Errorf("hosts/groups not found: \"unknown-host/group\""),
71                 },
72
73                 {
74                         "host: single name",
75                         []string{"host2"},
76                         []*config.Host{
77                                 allHosts.Map["host2"],
78                         },
79                         nil,
80                 },
81                 {
82                         "host: multiple names",
83                         []string{"host2", "host1.example.org"},
84                         []*config.Host{
85                                 allHosts.Map["host1.example.org"],
86                                 allHosts.Map["host2"],
87                         },
88                         nil,
89                 },
90                 {
91                         "host: multiple identical names",
92                         []string{"host2", "host2"},
93                         []*config.Host{
94                                 allHosts.Map["host2"],
95                         },
96                         nil,
97                 },
98                 {
99                         "host: multiple names, including unknown",
100                         []string{"host2", "unknown-host"},
101                         nil,
102                         fmt.Errorf("hosts/groups not found: \"unknown-host\""),
103                 },
104                 {
105                         "host: multiple names, including unknowns",
106                         []string{"host2", "unknown-host", "unknown-host-2"},
107                         nil,
108                         fmt.Errorf("hosts/groups not found: \"unknown-host\" \"unknown-host-2\""),
109                 },
110
111                 {
112                         "group: single name",
113                         []string{"group3"},
114                         []*config.Host{
115                                 allHosts.Map["host1.example.org"],
116                         },
117                         nil,
118                 },
119                 {
120                         "group: multiple names",
121                         []string{"group3", "group2"},
122                         []*config.Host{
123                                 allHosts.Map["host1.example.org"],
124                                 allHosts.Map["host2"],
125                         },
126                         nil,
127                 },
128                 {
129                         "group: multiple identical names",
130                         []string{"group3", "group2", "group3"},
131                         []*config.Host{
132                                 allHosts.Map["host1.example.org"],
133                                 allHosts.Map["host2"],
134                         },
135                         nil,
136                 },
137                 {
138                         "group: multiple names, including unknown",
139                         []string{"group3", "group2", "unknown-group"},
140                         nil,
141                         fmt.Errorf("hosts/groups not found: \"unknown-group\""),
142                 },
143                 {
144                         "group: \"all\"",
145                         []string{"all"},
146                         []*config.Host{
147                                 allHosts.Map["host1.example.org"],
148                                 allHosts.Map["host2"],
149                                 allHosts.Map["host3.example.net"],
150                         },
151                         nil,
152                 },
153
154                 {
155                         "group: single name (detected)",
156                         []string{"group"},
157                         nil,
158                         fmt.Errorf(`group "group" depends on "detected" groups` + errMsg),
159                 },
160                 {
161                         "group: multiple names (detected)",
162                         []string{"group", "group2"},
163                         nil,
164                         fmt.Errorf(`group "group" depends on "detected" groups` + errMsg),
165                 },
166
167                 {
168                         "\"all\" and name",
169                         []string{"all", "group2"},
170                         []*config.Host{
171                                 allHosts.Map["host1.example.org"],
172                                 allHosts.Map["host2"],
173                                 allHosts.Map["host3.example.net"],
174                         },
175                         nil,
176                 },
177                 {
178                         "\"all\" and names",
179                         []string{"all", "group2", "host2"},
180                         []*config.Host{
181                                 allHosts.Map["host1.example.org"],
182                                 allHosts.Map["host2"],
183                                 allHosts.Map["host3.example.net"],
184                         },
185                         nil,
186                 },
187         }
188
189         for _, tc := range tests {
190                 t.Run(tc.name, func(t *testing.T) {
191                         res, err := hostsToSync(tc.names, allHosts, allGroups)
192                         testutil.AssertEqual(t, "res", res, tc.exp)
193                         testutil.AssertErrorEqual(t, "err", err, tc.expErr)
194                 })
195         }
196 }
197
198 func TestLogEvent(t *testing.T) {
199         // Restore default logger
200         defer log.SetFlags(log.Flags())
201         defer log.SetOutput(os.Stderr)
202
203         tests := []struct {
204                 name      string
205                 event     Event
206                 level     safcm.LogLevel
207                 isTTY     bool
208                 exp       string
209                 expFailed bool
210         }{
211
212                 {
213                         "Error",
214                         Event{
215                                 Error: fmt.Errorf("fake error"),
216                         },
217                         safcm.LogDebug3,
218                         false,
219                         "[error]   [fake-host] fake error\n",
220                         true,
221                 },
222                 {
223                         "Error (tty)",
224                         Event{
225                                 Error: fmt.Errorf("fake error"),
226                         },
227                         safcm.LogDebug3,
228                         true,
229                         "[error]   [\x1b[31mfake-host\x1b[0m] fake error\n",
230                         true,
231                 },
232                 {
233                         "Error: escape",
234                         Event{
235                                 Error: fmt.Errorf("\x00"),
236                         },
237                         safcm.LogDebug3,
238                         false,
239                         "[error]   [fake-host] \\x00\n",
240                         true,
241                 },
242                 {
243                         "Error: escape (tty)",
244                         Event{
245                                 Error: fmt.Errorf("\x00"),
246                         },
247                         safcm.LogDebug3,
248                         true,
249                         "[error]   [\x1b[31mfake-host\x1b[0m] \x1b[35m\\x00\x1b[0m\n",
250                         true,
251                 },
252
253                 {
254                         "Log: info",
255                         Event{
256                                 Log: Log{
257                                         Level: safcm.LogInfo,
258                                         Text:  "info log",
259                                 },
260                         },
261                         safcm.LogDebug3,
262                         false,
263                         "[info]    [fake-host] info log\n",
264                         false,
265                 },
266                 {
267                         "Log: info (tty)",
268                         Event{
269                                 Log: Log{
270                                         Level: safcm.LogInfo,
271                                         Text:  "info log",
272                                 },
273                         },
274                         safcm.LogDebug3,
275                         true,
276                         "[info]    [fake-host] info log\n",
277                         false,
278                 },
279                 {
280                         "Log: verbose",
281                         Event{
282                                 Log: Log{
283                                         Level: safcm.LogVerbose,
284                                         Text:  "verbose log",
285                                 },
286                         },
287                         safcm.LogDebug3,
288                         false,
289                         "[verbose] [fake-host] verbose log\n",
290                         false,
291                 },
292                 {
293                         "Log: debug",
294                         Event{
295                                 Log: Log{
296                                         Level: safcm.LogDebug,
297                                         Text:  "debug log",
298                                 },
299                         },
300                         safcm.LogDebug3,
301                         false,
302                         "[debug]   [fake-host] debug log\n",
303                         false,
304                 },
305                 {
306                         "Log: debug2",
307                         Event{
308                                 Log: Log{
309                                         Level: safcm.LogDebug2,
310                                         Text:  "debug2 log",
311                                 },
312                         },
313                         safcm.LogDebug3,
314                         false,
315                         "[debug2]  [fake-host] debug2 log\n",
316                         false,
317                 },
318                 {
319                         "Log: debug3",
320                         Event{
321                                 Log: Log{
322                                         Level: safcm.LogDebug3,
323                                         Text:  "debug3 log",
324                                 },
325                         },
326                         safcm.LogDebug3,
327                         false,
328                         fmt.Sprintf("[INVALID=%d] [fake-host] debug3 log\n",
329                                 safcm.LogDebug3),
330                         false,
331                 },
332                 {
333                         "Log: debug3 (tty)",
334                         Event{
335                                 Log: Log{
336                                         Level: safcm.LogDebug3,
337                                         Text:  "debug3 log",
338                                 },
339                         },
340                         safcm.LogDebug3,
341                         true,
342                         fmt.Sprintf("[INVALID=%d] [\x1b[31mfake-host\x1b[0m] debug3 log\n",
343                                 safcm.LogDebug3),
344                         false,
345                 },
346                 {
347                         "Log: escape",
348                         Event{
349                                 Log: Log{
350                                         Level: safcm.LogInfo,
351                                         Text:  "\x00",
352                                 },
353                         },
354                         safcm.LogDebug3,
355                         false,
356                         "[info]    [fake-host] \\x00\n",
357                         false,
358                 },
359                 {
360                         "Log: escape (tty)",
361                         Event{
362                                 Log: Log{
363                                         Level: safcm.LogInfo,
364                                         Text:  "\x00",
365                                 },
366                         },
367                         safcm.LogDebug3,
368                         true,
369                         "[info]    [fake-host] \x1b[35m\\x00\x1b[0m\n",
370                         false,
371                 },
372
373                 {
374                         "ConnEvent: stderr",
375                         Event{
376                                 ConnEvent: rpc.ConnEvent{
377                                         Type: rpc.ConnEventStderr,
378                                         Data: "fake stderr",
379                                 },
380                         },
381                         safcm.LogDebug3,
382                         false,
383                         "[stderr]  [fake-host] fake stderr\n",
384                         false,
385                 },
386                 {
387                         "ConnEvent: stderr (tty)",
388                         Event{
389                                 ConnEvent: rpc.ConnEvent{
390                                         Type: rpc.ConnEventStderr,
391                                         Data: "fake stderr",
392                                 },
393                         },
394                         safcm.LogDebug3,
395                         true,
396                         "[stderr]  [fake-host] fake stderr\n",
397                         false,
398                 },
399                 {
400                         "ConnEvent: debug",
401                         Event{
402                                 ConnEvent: rpc.ConnEvent{
403                                         Type: rpc.ConnEventDebug,
404                                         Data: "conn debug",
405                                 },
406                         },
407                         safcm.LogDebug3,
408                         false,
409                         "[debug3]  [fake-host] conn debug\n",
410                         false,
411                 },
412                 {
413                         "ConnEvent: upload",
414                         Event{
415                                 ConnEvent: rpc.ConnEvent{
416                                         Type: rpc.ConnEventUpload,
417                                 },
418                         },
419                         safcm.LogDebug3,
420                         false,
421                         "[info]    [fake-host] remote helper upload in progress\n",
422                         false,
423                 },
424                 {
425                         "ConnEvent: upload (ignored)",
426                         Event{
427                                 ConnEvent: rpc.ConnEvent{
428                                         Type: rpc.ConnEventUpload,
429                                 },
430                         },
431                         safcm.LogError,
432                         false,
433                         "",
434                         false,
435                 },
436                 {
437                         "ConnEvent: invalid",
438                         Event{
439                                 ConnEvent: rpc.ConnEvent{
440                                         Type: 42,
441                                         Data: "invalid",
442                                 },
443                         },
444                         safcm.LogError,
445                         false,
446                         "[INVALID=42] [fake-host] invalid\n",
447                         false,
448                 },
449                 {
450                         "ConnEvent: invalid (tty)",
451                         Event{
452                                 ConnEvent: rpc.ConnEvent{
453                                         Type: 42,
454                                         Data: "invalid",
455                                 },
456                         },
457                         safcm.LogError,
458                         true,
459                         "[INVALID=42] [\x1b[31mfake-host\x1b[0m] invalid\n",
460                         false,
461                 },
462                 {
463                         "ConnEvent: escape",
464                         Event{
465                                 ConnEvent: rpc.ConnEvent{
466                                         Type: rpc.ConnEventStderr,
467                                         Data: "\x00",
468                                 },
469                         },
470                         safcm.LogDebug3,
471                         false,
472                         "[stderr]  [fake-host] \\x00\n",
473                         false,
474                 },
475                 {
476                         "ConnEvent: escape (tty)",
477                         Event{
478                                 ConnEvent: rpc.ConnEvent{
479                                         Type: rpc.ConnEventDebug,
480                                         Data: "\x01",
481                                 },
482                         },
483                         safcm.LogDebug3,
484                         true,
485                         "[debug3]  [fake-host] \x1b[35m\\x01\x1b[0m\n",
486                         false,
487                 },
488
489                 {
490                         "Escaped",
491                         Event{
492                                 Log: Log{
493                                         Level: safcm.LogInfo,
494                                         Text:  "\x00",
495                                 },
496                                 Escaped: true,
497                         },
498                         safcm.LogDebug3,
499                         false,
500                         "[info]    [fake-host] \x00\n",
501                         false,
502                 },
503                 {
504                         "Escaped (tty)",
505                         Event{
506                                 Log: Log{
507                                         Level: safcm.LogInfo,
508                                         Text:  "\x00",
509                                 },
510                                 Escaped: true,
511                         },
512                         safcm.LogDebug3,
513                         true,
514                         "[info]    [fake-host] \x00\n",
515                         false,
516                 },
517
518                 {
519                         "empty (invalid)",
520                         Event{},
521                         safcm.LogDebug3,
522                         false,
523                         "[INVALID=0] [fake-host] \n",
524                         false,
525                 },
526         }
527
528         for _, tc := range tests {
529                 t.Run(tc.name, func(t *testing.T) {
530                         tc.event.Host = &config.Host{
531                                 Name: "fake-host",
532                         }
533
534                         var buf bytes.Buffer
535                         log.SetFlags(0)
536                         log.SetOutput(&buf)
537
538                         var failed bool
539                         logEvent(tc.event, tc.level, tc.isTTY, &failed)
540
541                         testutil.AssertEqual(t, "log",
542                                 buf.String(), tc.exp)
543                         testutil.AssertEqual(t, "failed",
544                                 failed, tc.expFailed)
545                 })
546         }
547 }