]> ruderich.org/simon Gitweb - safcm/safcm.git/blob - cmd/safcm/sync_changes_test.go
config: return map from TransitivelyDetectedGroups()
[safcm/safcm.git] / cmd / safcm / sync_changes_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         "io/fs"
20         "testing"
21
22         "ruderich.org/simon/safcm"
23         "ruderich.org/simon/safcm/cmd/safcm/config"
24         "ruderich.org/simon/safcm/testutil"
25 )
26
27 func TestFormatChanges(t *testing.T) {
28         tests := []struct {
29                 name   string
30                 dryRun bool
31                 quiet  bool
32                 isTTY  bool
33                 resp   safcm.MsgSyncResp
34                 exp    string
35         }{
36
37                 // Just a few basic tests and border cases; see the other
38                 // tests for more detailed tests of each format function
39
40                 {
41                         "no changes",
42                         false,
43                         false,
44                         false,
45                         safcm.MsgSyncResp{},
46                         "no changes",
47                 },
48
49                 {
50                         "changes",
51                         false,
52                         false,
53                         false,
54                         safcm.MsgSyncResp{
55                                 FileChanges: []safcm.FileChange{
56                                         {
57                                                 Path:    "created",
58                                                 Created: true,
59                                                 New: safcm.FileChangeInfo{
60                                                         Mode:  0644,
61                                                         User:  "user",
62                                                         Uid:   1000,
63                                                         Group: "group",
64                                                         Gid:   2000,
65                                                 },
66                                         },
67                                 },
68                                 PackageChanges: []safcm.PackageChange{
69                                         {
70                                                 Name: "package-one",
71                                         },
72                                         {
73                                                 Name: "package-two",
74                                         },
75                                 },
76                                 ServiceChanges: []safcm.ServiceChange{
77                                         {
78                                                 Name:    "service-one",
79                                                 Started: true,
80                                         },
81                                         {
82                                                 Name:    "service-two",
83                                                 Enabled: true,
84                                         },
85                                         {
86                                                 Name:    "service-three",
87                                                 Started: true,
88                                                 Enabled: true,
89                                         },
90                                 },
91                                 CommandChanges: []safcm.CommandChange{
92                                         {
93                                                 Command: "fake command",
94                                                 Output:  "fake output",
95                                         },
96                                         {
97                                                 Command: "fake command with no output",
98                                         },
99                                 },
100                         },
101                         "\nchanged 1 file(s):\n\"created\": created, file, user(1000) group(2000), 0644\n\ninstalled 2 package(s):\n\"package-one\"\n\"package-two\"\n\nmodified 3 service(s):\n\"service-one\": started\n\"service-two\": enabled\n\"service-three\": started, enabled\n\nexecuted 2 command(s):\n\"fake command\":\n   > fake output\n   > \\ No newline at end of file\n\"fake command with no output\"\n",
102                 },
103
104                 {
105                         "command changes only, dry-run",
106                         true,
107                         false,
108                         false,
109                         safcm.MsgSyncResp{
110                                 CommandChanges: []safcm.CommandChange{
111                                         {
112                                                 Command: "fake command",
113                                         },
114                                         {
115                                                 Command: "fake command with no output",
116                                         },
117                                         {
118                                                 Command: "fake command with newline",
119                                         },
120                                         {
121                                                 Command: "fake command with more output",
122                                         },
123                                         {
124                                                 Command: "fake failed command",
125                                         },
126                                 },
127                         },
128                         "\nexecuted 5 command(s): (dry-run)\n\"fake command\"\n\"fake command with no output\"\n\"fake command with newline\"\n\"fake command with more output\"\n\"fake failed command\"\n",
129                 },
130                 {
131                         "command changes only, quiet & dry-run",
132                         true,
133                         true,
134                         false,
135                         safcm.MsgSyncResp{
136                                 CommandChanges: []safcm.CommandChange{
137                                         {
138                                                 Command: "fake command",
139                                         },
140                                         {
141                                                 Command: "fake command with no output",
142                                         },
143                                         {
144                                                 Command: "fake command with newline",
145                                         },
146                                         {
147                                                 Command: "fake command with more output",
148                                         },
149                                         {
150                                                 Command: "fake failed command",
151                                         },
152                                 },
153                         },
154                         "executed 5 command(s) (dry-run)\n",
155                 },
156         }
157
158         for _, tc := range tests {
159                 t.Run(tc.name, func(t *testing.T) {
160                         s := &Sync{
161                                 config: &config.Config{
162                                         DryRun: tc.dryRun,
163                                         Quiet:  tc.quiet,
164                                 },
165                                 isTTY: tc.isTTY,
166                         }
167
168                         res := s.formatChanges(tc.resp)
169                         testutil.AssertEqual(t, "res", res, tc.exp)
170                 })
171         }
172 }
173
174 func TestFormatFileChanges(t *testing.T) {
175         tests := []struct {
176                 name    string
177                 dryRun  bool
178                 isTTY   bool
179                 changes []safcm.FileChange
180                 exp     string
181         }{
182
183                 {
184                         "regular",
185                         false,
186                         false,
187                         []safcm.FileChange{
188                                 {
189                                         Path:    "created: file",
190                                         Created: true,
191                                         New: safcm.FileChangeInfo{
192                                                 Mode:  0644,
193                                                 User:  "user",
194                                                 Uid:   1000,
195                                                 Group: "group",
196                                                 Gid:   2000,
197                                         },
198                                 },
199                                 {
200                                         Path:    "created: link",
201                                         Created: true,
202                                         New: safcm.FileChangeInfo{
203                                                 Mode:  fs.ModeSymlink | 0777,
204                                                 User:  "user",
205                                                 Uid:   1000,
206                                                 Group: "group",
207                                                 Gid:   2000,
208                                         },
209                                 },
210                                 {
211                                         Path: "type change: file -> dir",
212                                         Old: safcm.FileChangeInfo{
213                                                 Mode:  0751,
214                                                 User:  "user",
215                                                 Uid:   1000,
216                                                 Group: "group",
217                                                 Gid:   2000,
218                                         },
219                                         New: safcm.FileChangeInfo{
220                                                 Mode:  fs.ModeDir | 0751,
221                                                 User:  "user",
222                                                 Uid:   1000,
223                                                 Group: "group",
224                                                 Gid:   2000,
225                                         },
226                                         DataDiff: `@@ -1,2 +1 @@
227 -content
228  
229 `,
230                                 },
231                                 {
232                                         Path: "user change",
233                                         Old: safcm.FileChangeInfo{
234                                                 Mode:  0755,
235                                                 User:  "user",
236                                                 Uid:   1000,
237                                                 Group: "group",
238                                                 Gid:   2000,
239                                         },
240                                         New: safcm.FileChangeInfo{
241                                                 Mode:  0755,
242                                                 User:  "user2",
243                                                 Uid:   1001,
244                                                 Group: "group",
245                                                 Gid:   2000,
246                                         },
247                                 },
248                                 {
249                                         Path: "group change",
250                                         Old: safcm.FileChangeInfo{
251                                                 Mode:  0755,
252                                                 User:  "user",
253                                                 Uid:   1000,
254                                                 Group: "group",
255                                                 Gid:   2000,
256                                         },
257                                         New: safcm.FileChangeInfo{
258                                                 Mode:  0755,
259                                                 User:  "user",
260                                                 Uid:   1000,
261                                                 Group: "group2",
262                                                 Gid:   2001,
263                                         },
264                                 },
265                                 {
266                                         Path: "mode change",
267                                         Old: safcm.FileChangeInfo{
268                                                 Mode:  0755,
269                                                 User:  "user",
270                                                 Uid:   1000,
271                                                 Group: "group",
272                                                 Gid:   2000,
273                                         },
274                                         New: safcm.FileChangeInfo{
275                                                 Mode:  0750,
276                                                 User:  "user",
277                                                 Uid:   1000,
278                                                 Group: "group",
279                                                 Gid:   2000,
280                                         },
281                                 },
282                                 {
283                                         Path: "mode change (setuid)",
284                                         Old: safcm.FileChangeInfo{
285                                                 Mode:  0755,
286                                                 User:  "user",
287                                                 Uid:   1000,
288                                                 Group: "group",
289                                                 Gid:   2000,
290                                         },
291                                         New: safcm.FileChangeInfo{
292                                                 Mode:  0755 | fs.ModeSetuid,
293                                                 User:  "user",
294                                                 Uid:   1000,
295                                                 Group: "group",
296                                                 Gid:   2000,
297                                         },
298                                 },
299                                 {
300                                         Path: "content change",
301                                         Old: safcm.FileChangeInfo{
302                                                 Mode:  0644,
303                                                 User:  "user",
304                                                 Uid:   1000,
305                                                 Group: "group",
306                                                 Gid:   2000,
307                                         },
308                                         New: safcm.FileChangeInfo{
309                                                 Mode:  0644,
310                                                 User:  "user",
311                                                 Uid:   1000,
312                                                 Group: "group",
313                                                 Gid:   2000,
314                                         },
315                                         DataDiff: `@@ -1,2 +1,2 @@
316 -old content
317 +content
318  
319 `,
320                                 },
321                                 {
322                                         Path: "multiple changes",
323                                         Old: safcm.FileChangeInfo{
324                                                 Mode:  0644,
325                                                 User:  "user",
326                                                 Uid:   1000,
327                                                 Group: "group",
328                                                 Gid:   2000,
329                                         },
330                                         New: safcm.FileChangeInfo{
331                                                 Mode:  fs.ModeDir | 0755,
332                                                 User:  "user2",
333                                                 Uid:   1001,
334                                                 Group: "group2",
335                                                 Gid:   2001,
336                                         },
337                                         DataDiff: `@@ -1,2 +1 @@
338 -content
339  
340 `,
341                                 },
342                         },
343                         `changed 9 file(s):
344 "created: file": created, file, user(1000) group(2000), 0644
345 "created: link": created, symlink, user(1000) group(2000), 0777
346 "type change: file -> dir": file -> dir
347    @@ -1,2 +1 @@
348    -content
349     
350 "user change": user(1000) group(2000) -> user2(1001) group(2000)
351 "group change": user(1000) group(2000) -> user(1000) group2(2001)
352 "mode change": 0755 -> 0750
353 "mode change (setuid)": 0755 -> 04755
354 "content change":
355    @@ -1,2 +1,2 @@
356    -old content
357    +content
358     
359 "multiple changes": file -> dir, user(1000) group(2000) -> user2(1001) group2(2001), 0644 -> 0755
360    @@ -1,2 +1 @@
361    -content
362     
363 `,
364                 },
365
366                 {
367                         "regular (tty)",
368                         false,
369                         true,
370                         []safcm.FileChange{
371                                 {
372                                         Path:    "created: file",
373                                         Created: true,
374                                         New: safcm.FileChangeInfo{
375                                                 Mode:  0644,
376                                                 User:  "user",
377                                                 Uid:   1000,
378                                                 Group: "group",
379                                                 Gid:   2000,
380                                         },
381                                 },
382                                 {
383                                         Path:    "created: link",
384                                         Created: true,
385                                         New: safcm.FileChangeInfo{
386                                                 Mode:  fs.ModeSymlink | 0777,
387                                                 User:  "user",
388                                                 Uid:   1000,
389                                                 Group: "group",
390                                                 Gid:   2000,
391                                         },
392                                 },
393                                 {
394                                         Path: "type change: file -> dir",
395                                         Old: safcm.FileChangeInfo{
396                                                 Mode:  0751,
397                                                 User:  "user",
398                                                 Uid:   1000,
399                                                 Group: "group",
400                                                 Gid:   2000,
401                                         },
402                                         New: safcm.FileChangeInfo{
403                                                 Mode:  fs.ModeDir | 0751,
404                                                 User:  "user",
405                                                 Uid:   1000,
406                                                 Group: "group",
407                                                 Gid:   2000,
408                                         },
409                                         DataDiff: `@@ -1,2 +1 @@
410 -content
411  
412 `,
413                                 },
414                                 {
415                                         Path: "user change",
416                                         Old: safcm.FileChangeInfo{
417                                                 Mode:  0755,
418                                                 User:  "user",
419                                                 Uid:   1000,
420                                                 Group: "group",
421                                                 Gid:   2000,
422                                         },
423                                         New: safcm.FileChangeInfo{
424                                                 Mode:  0755,
425                                                 User:  "user2",
426                                                 Uid:   1001,
427                                                 Group: "group",
428                                                 Gid:   2000,
429                                         },
430                                 },
431                                 {
432                                         Path: "group change",
433                                         Old: safcm.FileChangeInfo{
434                                                 Mode:  0755,
435                                                 User:  "user",
436                                                 Uid:   1000,
437                                                 Group: "group",
438                                                 Gid:   2000,
439                                         },
440                                         New: safcm.FileChangeInfo{
441                                                 Mode:  0755,
442                                                 User:  "user",
443                                                 Uid:   1000,
444                                                 Group: "group2",
445                                                 Gid:   2001,
446                                         },
447                                 },
448                                 {
449                                         Path: "mode change",
450                                         Old: safcm.FileChangeInfo{
451                                                 Mode:  0755,
452                                                 User:  "user",
453                                                 Uid:   1000,
454                                                 Group: "group",
455                                                 Gid:   2000,
456                                         },
457                                         New: safcm.FileChangeInfo{
458                                                 Mode:  0750,
459                                                 User:  "user",
460                                                 Uid:   1000,
461                                                 Group: "group",
462                                                 Gid:   2000,
463                                         },
464                                 },
465                                 {
466                                         Path: "mode change (setuid)",
467                                         Old: safcm.FileChangeInfo{
468                                                 Mode:  0755,
469                                                 User:  "user",
470                                                 Uid:   1000,
471                                                 Group: "group",
472                                                 Gid:   2000,
473                                         },
474                                         New: safcm.FileChangeInfo{
475                                                 Mode:  0755 | fs.ModeSetuid,
476                                                 User:  "user",
477                                                 Uid:   1000,
478                                                 Group: "group",
479                                                 Gid:   2000,
480                                         },
481                                 },
482                                 {
483                                         Path: "content change",
484                                         Old: safcm.FileChangeInfo{
485                                                 Mode:  0644,
486                                                 User:  "user",
487                                                 Uid:   1000,
488                                                 Group: "group",
489                                                 Gid:   2000,
490                                         },
491                                         New: safcm.FileChangeInfo{
492                                                 Mode:  0644,
493                                                 User:  "user",
494                                                 Uid:   1000,
495                                                 Group: "group",
496                                                 Gid:   2000,
497                                         },
498                                         DataDiff: `@@ -1,2 +1,2 @@
499 -old content
500 +content
501  
502 `,
503                                 },
504                                 {
505                                         Path: "multiple changes",
506                                         Old: safcm.FileChangeInfo{
507                                                 Mode:  0644,
508                                                 User:  "user",
509                                                 Uid:   1000,
510                                                 Group: "group",
511                                                 Gid:   2000,
512                                         },
513                                         New: safcm.FileChangeInfo{
514                                                 Mode:  fs.ModeDir | 0755,
515                                                 User:  "user2",
516                                                 Uid:   1001,
517                                                 Group: "group2",
518                                                 Gid:   2001,
519                                         },
520                                         DataDiff: `@@ -1,2 +1 @@
521 -content
522  
523 `,
524                                 },
525                         },
526                         "changed 9 file(s):\n\x1b[36m\"created: file\"\x1b[0m: \x1b[32mcreated\x1b[0m, file, user(1000) group(2000), 0644\n\x1b[36m\"created: link\"\x1b[0m: \x1b[32mcreated\x1b[0m, symlink, user(1000) group(2000), 0777\n\x1b[36m\"type change: file -> dir\"\x1b[0m: file -> dir\n   @@ -1,2 +1 @@\n\x1b[31m   -content\x1b[0m\n    \n\x1b[36m\"user change\"\x1b[0m: user(1000) group(2000) -> user2(1001) group(2000)\n\x1b[36m\"group change\"\x1b[0m: user(1000) group(2000) -> user(1000) group2(2001)\n\x1b[36m\"mode change\"\x1b[0m: 0755 -> 0750\n\x1b[36m\"mode change (setuid)\"\x1b[0m: 0755 -> 04755\n\x1b[36m\"content change\"\x1b[0m:\n   @@ -1,2 +1,2 @@\n\x1b[31m   -old content\x1b[0m\n\x1b[32m   +content\x1b[0m\n    \n\x1b[36m\"multiple changes\"\x1b[0m: file -> dir, user(1000) group(2000) -> user2(1001) group2(2001), 0644 -> 0755\n   @@ -1,2 +1 @@\n\x1b[31m   -content\x1b[0m\n    \n",
527                 },
528
529                 {
530                         "dry-run",
531                         true,
532                         false,
533                         []safcm.FileChange{
534                                 {
535                                         Path:    "file",
536                                         Created: true,
537                                         New: safcm.FileChangeInfo{
538                                                 Mode:  0644,
539                                                 User:  "user",
540                                                 Uid:   1000,
541                                                 Group: "group",
542                                                 Gid:   2000,
543                                         },
544                                 },
545                         },
546                         `changed 1 file(s): (dry-run)
547 "file": created, file, user(1000) group(2000), 0644
548 `,
549                 },
550
551                 {
552                         "dry-run (tty)",
553                         true,
554                         true,
555                         []safcm.FileChange{
556                                 {
557                                         Path:    "file",
558                                         Created: true,
559                                         New: safcm.FileChangeInfo{
560                                                 Mode:  0644,
561                                                 User:  "user",
562                                                 Uid:   1000,
563                                                 Group: "group",
564                                                 Gid:   2000,
565                                         },
566                                 },
567                         },
568                         "changed 1 file(s): (dry-run)\n\x1B[36m\"file\"\x1B[0m: \x1B[32mcreated\x1B[0m, file, user(1000) group(2000), 0644\n",
569                 },
570
571                 {
572                         "escaping",
573                         false,
574                         false,
575                         []safcm.FileChange{
576                                 {
577                                         Path:    "\x00",
578                                         Created: true,
579                                         New: safcm.FileChangeInfo{
580                                                 Mode:  0xFFFFFFFF,
581                                                 User:  "\x01",
582                                                 Uid:   -1,
583                                                 Group: "\x02",
584                                                 Gid:   -2,
585                                         },
586                                         DataDiff: "\x03",
587                                 },
588                                 {
589                                         Path: "\x00",
590                                         Old: safcm.FileChangeInfo{
591                                                 Mode:  0x00000000,
592                                                 User:  "\x01",
593                                                 Uid:   -1,
594                                                 Group: "\x02",
595                                                 Gid:   -2,
596                                         },
597                                         New: safcm.FileChangeInfo{
598                                                 Mode:  0xFFFFFFFF,
599                                                 User:  "\x03",
600                                                 Uid:   -3,
601                                                 Group: "\x04",
602                                                 Gid:   -4,
603                                         },
604                                         DataDiff: "\x05",
605                                 },
606                         },
607                         `changed 2 file(s):
608 "\x00": created, invalid type dLDpSc?---------, \x01(-1) \x02(-2), 07777
609    \x03
610    \ No newline at end of file
611 "\x00": file -> invalid type dLDpSc?---------, \x01(-1) \x02(-2) -> \x03(-3) \x04(-4), 0 -> 07777
612    \x05
613    \ No newline at end of file
614 `,
615                 },
616
617                 {
618                         "escaping (tty)",
619                         false,
620                         true,
621                         []safcm.FileChange{
622                                 {
623                                         Path:    "\x00",
624                                         Created: true,
625                                         New: safcm.FileChangeInfo{
626                                                 Mode:  0xFFFFFFFF,
627                                                 User:  "\x01",
628                                                 Uid:   -1,
629                                                 Group: "\x02",
630                                                 Gid:   -2,
631                                         },
632                                         DataDiff: "\x03",
633                                 },
634                                 {
635                                         Path: "\x00",
636                                         Old: safcm.FileChangeInfo{
637                                                 Mode:  0x00000000,
638                                                 User:  "\x01",
639                                                 Uid:   -1,
640                                                 Group: "\x02",
641                                                 Gid:   -2,
642                                         },
643                                         New: safcm.FileChangeInfo{
644                                                 Mode:  0xFFFFFFFF,
645                                                 User:  "\x03",
646                                                 Uid:   -3,
647                                                 Group: "\x04",
648                                                 Gid:   -4,
649                                         },
650                                         DataDiff: "\x05",
651                                 },
652                         },
653                         "changed 2 file(s):\n\x1b[36m\"\\x00\"\x1b[0m: \x1b[32mcreated\x1b[0m, invalid type dLDpSc?---------, \\x01(-1) \\x02(-2), 07777\n   \\x03\n   \\ No newline at end of file\n\x1b[36m\"\\x00\"\x1b[0m: file -> invalid type dLDpSc?---------, \\x01(-1) \\x02(-2) -> \\x03(-3) \\x04(-4), 0 -> 07777\n   \\x05\n   \\ No newline at end of file\n",
654                 },
655         }
656
657         for _, tc := range tests {
658                 t.Run(tc.name, func(t *testing.T) {
659                         s := &Sync{
660                                 config: &config.Config{
661                                         DryRun: tc.dryRun,
662                                 },
663                                 isTTY: tc.isTTY,
664                         }
665
666                         res := s.formatFileChanges(tc.changes)
667                         testutil.AssertEqual(t, "res", res, tc.exp)
668                 })
669         }
670 }
671
672 func TestFormatPackageChanges(t *testing.T) {
673         tests := []struct {
674                 name    string
675                 dryRun  bool
676                 isTTY   bool
677                 changes []safcm.PackageChange
678                 exp     string
679         }{
680
681                 {
682                         "regular",
683                         false,
684                         false,
685                         []safcm.PackageChange{
686                                 {
687                                         Name: "package-one",
688                                 },
689                                 {
690                                         Name: "package-two",
691                                 },
692                         },
693                         `installed 2 package(s):
694 "package-one"
695 "package-two"
696 `,
697                 },
698
699                 {
700                         "regular (tty)",
701                         false,
702                         true,
703                         []safcm.PackageChange{
704                                 {
705                                         Name: "package-one",
706                                 },
707                                 {
708                                         Name: "package-two",
709                                 },
710                         },
711                         "installed 2 package(s):\n\x1b[36m\"package-one\"\x1b[0m\n\x1b[36m\"package-two\"\x1b[0m\n",
712                 },
713
714                 {
715                         "dry-run",
716                         true,
717                         false,
718                         []safcm.PackageChange{
719                                 {
720                                         Name: "package-one",
721                                 },
722                                 {
723                                         Name: "package-two",
724                                 },
725                         },
726                         `installed 2 package(s): (dry-run)
727 "package-one"
728 "package-two"
729 `,
730                 },
731
732                 {
733                         "dry-run (tty)",
734                         true,
735                         true,
736                         []safcm.PackageChange{
737                                 {
738                                         Name: "package-one",
739                                 },
740                                 {
741                                         Name: "package-two",
742                                 },
743                         },
744                         "installed 2 package(s): (dry-run)\n\x1b[36m\"package-one\"\x1b[0m\n\x1b[36m\"package-two\"\x1b[0m\n",
745                 },
746
747                 {
748                         "escaping",
749                         false,
750                         false,
751                         []safcm.PackageChange{
752                                 {
753                                         Name: "\x00",
754                                 },
755                         },
756                         `installed 1 package(s):
757 "\x00"
758 `,
759                 },
760
761                 {
762                         "escaping (tty)",
763                         false,
764                         true,
765                         []safcm.PackageChange{
766                                 {
767                                         Name: "\x00",
768                                 },
769                         },
770                         "installed 1 package(s):\n\x1b[36m\"\\x00\"\x1b[0m\n",
771                 },
772         }
773
774         for _, tc := range tests {
775                 t.Run(tc.name, func(t *testing.T) {
776                         s := &Sync{
777                                 config: &config.Config{
778                                         DryRun: tc.dryRun,
779                                 },
780                                 isTTY: tc.isTTY,
781                         }
782
783                         res := s.formatPackageChanges(tc.changes)
784                         testutil.AssertEqual(t, "res", res, tc.exp)
785                 })
786         }
787 }
788
789 func TestFormatServiceChanges(t *testing.T) {
790         tests := []struct {
791                 name    string
792                 dryRun  bool
793                 isTTY   bool
794                 changes []safcm.ServiceChange
795                 exp     string
796         }{
797
798                 {
799                         "regular",
800                         false,
801                         false,
802                         []safcm.ServiceChange{
803                                 {
804                                         Name:    "service-one",
805                                         Started: true,
806                                 },
807                                 {
808                                         Name:    "service-two",
809                                         Enabled: true,
810                                 },
811                                 {
812                                         Name:    "service-three",
813                                         Started: true,
814                                         Enabled: true,
815                                 },
816                         },
817                         `modified 3 service(s):
818 "service-one": started
819 "service-two": enabled
820 "service-three": started, enabled
821 `,
822                 },
823
824                 {
825                         "regular (tty)",
826                         false,
827                         true,
828                         []safcm.ServiceChange{
829                                 {
830                                         Name:    "service-one",
831                                         Started: true,
832                                 },
833                                 {
834                                         Name:    "service-two",
835                                         Enabled: true,
836                                 },
837                                 {
838                                         Name:    "service-three",
839                                         Started: true,
840                                         Enabled: true,
841                                 },
842                         },
843                         "modified 3 service(s):\n\x1b[36m\"service-one\"\x1b[0m: started\n\x1b[36m\"service-two\"\x1b[0m: enabled\n\x1b[36m\"service-three\"\x1b[0m: started, enabled\n",
844                 },
845
846                 {
847                         "dry-run",
848                         true,
849                         false,
850                         []safcm.ServiceChange{
851                                 {
852                                         Name:    "service-one",
853                                         Started: true,
854                                 },
855                                 {
856                                         Name:    "service-two",
857                                         Enabled: true,
858                                 },
859                                 {
860                                         Name:    "service-three",
861                                         Started: true,
862                                         Enabled: true,
863                                 },
864                         },
865                         `modified 3 service(s): (dry-run)
866 "service-one": started
867 "service-two": enabled
868 "service-three": started, enabled
869 `,
870                 },
871
872                 {
873                         "dry-run (tty)",
874                         true,
875                         true,
876                         []safcm.ServiceChange{
877                                 {
878                                         Name:    "service-one",
879                                         Started: true,
880                                 },
881                                 {
882                                         Name:    "service-two",
883                                         Enabled: true,
884                                 },
885                                 {
886                                         Name:    "service-three",
887                                         Started: true,
888                                         Enabled: true,
889                                 },
890                         },
891                         "modified 3 service(s): (dry-run)\n\x1b[36m\"service-one\"\x1b[0m: started\n\x1b[36m\"service-two\"\x1b[0m: enabled\n\x1b[36m\"service-three\"\x1b[0m: started, enabled\n",
892                 },
893
894                 {
895                         "escaping",
896                         false,
897                         false,
898                         []safcm.ServiceChange{
899                                 {
900                                         Name: "\x00",
901                                 },
902                                 {
903                                         Name:    "\x01",
904                                         Started: true,
905                                         Enabled: true,
906                                 },
907                         },
908                         `modified 2 service(s):
909 "\x00": 
910 "\x01": started, enabled
911 `,
912                 },
913
914                 {
915                         "escaping (tty)",
916                         false,
917                         true,
918                         []safcm.ServiceChange{
919                                 {
920                                         Name: "\x00",
921                                 },
922                                 {
923                                         Name:    "\x01",
924                                         Started: true,
925                                         Enabled: true,
926                                 },
927                         },
928                         "modified 2 service(s):\n\x1b[36m\"\\x00\"\x1b[0m: \n\x1b[36m\"\\x01\"\x1b[0m: started, enabled\n",
929                 },
930         }
931
932         for _, tc := range tests {
933                 t.Run(tc.name, func(t *testing.T) {
934                         s := &Sync{
935                                 config: &config.Config{
936                                         DryRun: tc.dryRun,
937                                 },
938                                 isTTY: tc.isTTY,
939                         }
940
941                         res := s.formatServiceChanges(tc.changes)
942                         testutil.AssertEqual(t, "res", res, tc.exp)
943                 })
944         }
945 }
946
947 func TestFormatCommandChanges(t *testing.T) {
948         tests := []struct {
949                 name    string
950                 dryRun  bool
951                 quiet   bool
952                 isTTY   bool
953                 changes []safcm.CommandChange
954                 exp     string
955         }{
956
957                 {
958                         "regular",
959                         false,
960                         false,
961                         false,
962                         []safcm.CommandChange{
963                                 {
964                                         Command: "fake command",
965                                         Output:  "fake output",
966                                 },
967                                 {
968                                         Command: "fake command with no output",
969                                 },
970                                 {
971                                         Command: "fake command with newline",
972                                         Output:  "fake output\n",
973                                 },
974                                 {
975                                         Command: "fake command with more output",
976                                         Output:  "fake out\nfake put\nfake\n",
977                                 },
978                                 {
979                                         Command: "fake failed command",
980                                         Output:  "fake output",
981                                         Error:   "fake error",
982                                 },
983                         },
984                         `executed 5 command(s):
985 "fake command":
986    > fake output
987    > \ No newline at end of file
988 "fake command with no output"
989 "fake command with newline":
990    > fake output
991 "fake command with more output":
992    > fake out
993    > fake put
994    > fake
995 "fake failed command", failed: "fake error":
996    > fake output
997    > \ No newline at end of file
998 `,
999                 },
1000
1001                 {
1002                         "regular (tty)",
1003                         false,
1004                         false,
1005                         true,
1006                         []safcm.CommandChange{
1007                                 {
1008                                         Command: "fake command",
1009                                         Output:  "fake output",
1010                                 },
1011                                 {
1012                                         Command: "fake command with no output",
1013                                 },
1014                                 {
1015                                         Command: "fake command with newline",
1016                                         Output:  "fake output\n",
1017                                 },
1018                                 {
1019                                         Command: "fake command with more output",
1020                                         Output:  "fake out\nfake put\nfake\n",
1021                                 },
1022                                 {
1023                                         Command: "fake failed command",
1024                                         Output:  "fake output",
1025                                         Error:   "fake error",
1026                                 },
1027                         },
1028                         "executed 5 command(s):\n\x1b[36m\"fake command\"\x1b[0m:\n   > fake output\n   > \\ No newline at end of file\n\x1b[36m\"fake command with no output\"\x1b[0m\n\x1b[36m\"fake command with newline\"\x1b[0m:\n   > fake output\n\x1b[36m\"fake command with more output\"\x1b[0m:\n   > fake out\n   > fake put\n   > fake\n\x1b[36m\"fake failed command\"\x1b[0m, failed: \"fake error\":\n   > fake output\n   > \\ No newline at end of file\n",
1029                 },
1030
1031                 {
1032                         "dry-run",
1033                         true,
1034                         false,
1035                         false,
1036                         []safcm.CommandChange{
1037                                 {
1038                                         Command: "fake command",
1039                                         Output:  "fake output",
1040                                 },
1041                         },
1042                         `executed 1 command(s): (dry-run)
1043 "fake command":
1044    > fake output
1045    > \ No newline at end of file
1046 `,
1047                 },
1048
1049                 {
1050                         "dry-run (tty)",
1051                         true,
1052                         false,
1053                         true,
1054                         []safcm.CommandChange{
1055                                 {
1056                                         Command: "fake command",
1057                                         Output:  "fake output",
1058                                 },
1059                         },
1060                         "executed 1 command(s): (dry-run)\n\x1b[36m\"fake command\"\x1b[0m:\n   > fake output\n   > \\ No newline at end of file\n",
1061                 },
1062
1063                 {
1064                         "quiet",
1065                         false,
1066                         true,
1067                         false,
1068                         []safcm.CommandChange{
1069                                 {
1070                                         Command: "fake command",
1071                                         Output:  "fake output",
1072                                 },
1073                                 {
1074                                         Command: "fake command with no output",
1075                                 },
1076                                 {
1077                                         Command: "fake command with newline",
1078                                         Output:  "fake output\n",
1079                                 },
1080                                 {
1081                                         Command: "fake command with more output",
1082                                         Output:  "fake out\nfake put\nfake\n",
1083                                 },
1084                                 {
1085                                         Command: "fake failed command",
1086                                         Output:  "fake output",
1087                                         Error:   "fake error",
1088                                 },
1089                         },
1090                         `executed 5 command(s), 1 with no output:
1091 "fake command":
1092    > fake output
1093    > \ No newline at end of file
1094 "fake command with newline":
1095    > fake output
1096 "fake command with more output":
1097    > fake out
1098    > fake put
1099    > fake
1100 "fake failed command", failed: "fake error":
1101    > fake output
1102    > \ No newline at end of file
1103 `,
1104                 },
1105
1106                 {
1107                         "quiet (tty)",
1108                         false,
1109                         true,
1110                         true,
1111                         []safcm.CommandChange{
1112                                 {
1113                                         Command: "fake command",
1114                                         Output:  "fake output",
1115                                 },
1116                                 {
1117                                         Command: "fake command with no output",
1118                                 },
1119                                 {
1120                                         Command: "fake command with newline",
1121                                         Output:  "fake output\n",
1122                                 },
1123                                 {
1124                                         Command: "fake command with more output",
1125                                         Output:  "fake out\nfake put\nfake\n",
1126                                 },
1127                                 {
1128                                         Command: "fake failed command",
1129                                         Output:  "fake output",
1130                                         Error:   "fake error",
1131                                 },
1132                         },
1133                         "executed 5 command(s), 1 with no output:\n\x1b[36m\"fake command\"\x1b[0m:\n   > fake output\n   > \\ No newline at end of file\n\x1b[36m\"fake command with newline\"\x1b[0m:\n   > fake output\n\x1b[36m\"fake command with more output\"\x1b[0m:\n   > fake out\n   > fake put\n   > fake\n\x1b[36m\"fake failed command\"\x1b[0m, failed: \"fake error\":\n   > fake output\n   > \\ No newline at end of file\n",
1134                 },
1135
1136                 {
1137                         "quiet (only quiet commands)",
1138                         false,
1139                         true,
1140                         false,
1141                         []safcm.CommandChange{
1142                                 {
1143                                         Command: "fake command with no output",
1144                                 },
1145                                 {
1146                                         Command: "fake command with no output",
1147                                 },
1148                         },
1149                         `executed 2 command(s), 2 with no output
1150 `,
1151                 },
1152
1153                 {
1154                         "quiet (quiet with errors)",
1155                         false,
1156                         true,
1157                         false,
1158                         []safcm.CommandChange{
1159                                 {
1160                                         Command: "fake command with no output but error",
1161                                         Error:   "fake error",
1162                                 },
1163                                 {
1164                                         Command: "fake command with no output",
1165                                 },
1166                         },
1167                         `executed 2 command(s), 1 with no output:
1168 "fake command with no output but error", failed: "fake error"
1169 `,
1170                 },
1171
1172                 {
1173                         "quiet & dry-run",
1174                         true,
1175                         true,
1176                         false,
1177                         []safcm.CommandChange{
1178                                 {
1179                                         Command: "fake command",
1180                                 },
1181                                 {
1182                                         Command: "fake command with no output",
1183                                 },
1184                                 {
1185                                         Command: "fake command with newline",
1186                                 },
1187                                 {
1188                                         Command: "fake command with more output",
1189                                 },
1190                                 {
1191                                         Command: "fake failed command",
1192                                 },
1193                         },
1194                         `executed 5 command(s) (dry-run)
1195 `,
1196                 },
1197
1198                 {
1199                         "escaping",
1200                         false,
1201                         false,
1202                         false,
1203                         []safcm.CommandChange{
1204                                 {
1205                                         Command: "\x00",
1206                                         Trigger: "\x01",
1207                                         Output:  "\x02",
1208                                         Error:   "\x03",
1209                                 },
1210                         },
1211                         `executed 1 command(s):
1212 "\x00", trigger for "\x01", failed: "\x03":
1213    > \x02
1214    > \ No newline at end of file
1215 `,
1216                 },
1217
1218                 {
1219                         "escaping (tty)",
1220                         false,
1221                         false,
1222                         true,
1223                         []safcm.CommandChange{
1224                                 {
1225                                         Command: "\x00",
1226                                         Trigger: "\x01",
1227                                         Output:  "\x02",
1228                                         Error:   "\x03",
1229                                 },
1230                         },
1231                         "executed 1 command(s):\n\x1b[36m\"\\x00\"\x1b[0m, trigger for \"\\x01\", failed: \"\\x03\":\n   > \x1b[35m\\x02\x1b[0m\n   > \\ No newline at end of file\n",
1232                 },
1233         }
1234
1235         for _, tc := range tests {
1236                 t.Run(tc.name, func(t *testing.T) {
1237                         s := &Sync{
1238                                 config: &config.Config{
1239                                         DryRun: tc.dryRun,
1240                                         Quiet:  tc.quiet,
1241                                 },
1242                                 isTTY: tc.isTTY,
1243                         }
1244
1245                         res := s.formatCommandChanges(tc.changes)
1246                         testutil.AssertEqual(t, "res", res, tc.exp)
1247                 })
1248         }
1249 }