1 // Copyright (C) 2021 Simon Ruderich
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.
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.
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/>.
22 "ruderich.org/simon/safcm"
23 "ruderich.org/simon/safcm/cmd/safcm/config"
24 "ruderich.org/simon/safcm/testutil"
27 func TestFormatFileChanges(t *testing.T) {
32 changes []safcm.FileChange
42 Path: "created: file",
44 New: safcm.FileChangeInfo{
53 Path: "created: link",
55 New: safcm.FileChangeInfo{
56 Mode: fs.ModeSymlink | 0777,
64 Path: "type change: file -> dir",
65 Old: safcm.FileChangeInfo{
72 New: safcm.FileChangeInfo{
73 Mode: fs.ModeDir | 0751,
79 DataDiff: `@@ -1,2 +1 @@
86 Old: safcm.FileChangeInfo{
93 New: safcm.FileChangeInfo{
102 Path: "group change",
103 Old: safcm.FileChangeInfo{
110 New: safcm.FileChangeInfo{
120 Old: safcm.FileChangeInfo{
127 New: safcm.FileChangeInfo{
136 Path: "mode change (setuid)",
137 Old: safcm.FileChangeInfo{
144 New: safcm.FileChangeInfo{
145 Mode: 0755 | fs.ModeSetuid,
153 Path: "content change",
154 Old: safcm.FileChangeInfo{
161 New: safcm.FileChangeInfo{
168 DataDiff: `@@ -1,2 +1,2 @@
175 Path: "multiple changes",
176 Old: safcm.FileChangeInfo{
183 New: safcm.FileChangeInfo{
184 Mode: fs.ModeDir | 0755,
190 DataDiff: `@@ -1,2 +1 @@
197 "created: file": created, file, user(1000) group(2000), 0644
198 "created: link": created, symlink, user(1000) group(2000), 0777
199 "type change: file -> dir": file -> dir
203 "user change": user(1000) group(2000) -> user2(1001) group(2000)
204 "group change": user(1000) group(2000) -> user(1000) group2(2001)
205 "mode change": 0755 -> 0750
206 "mode change (setuid)": 0755 -> 04755
212 "multiple changes": file -> dir, user(1000) group(2000) -> user2(1001) group2(2001), 0644 -> 0755
225 Path: "created: file",
227 New: safcm.FileChangeInfo{
236 Path: "created: link",
238 New: safcm.FileChangeInfo{
239 Mode: fs.ModeSymlink | 0777,
247 Path: "type change: file -> dir",
248 Old: safcm.FileChangeInfo{
255 New: safcm.FileChangeInfo{
256 Mode: fs.ModeDir | 0751,
262 DataDiff: `@@ -1,2 +1 @@
269 Old: safcm.FileChangeInfo{
276 New: safcm.FileChangeInfo{
285 Path: "group change",
286 Old: safcm.FileChangeInfo{
293 New: safcm.FileChangeInfo{
303 Old: safcm.FileChangeInfo{
310 New: safcm.FileChangeInfo{
319 Path: "mode change (setuid)",
320 Old: safcm.FileChangeInfo{
327 New: safcm.FileChangeInfo{
328 Mode: 0755 | fs.ModeSetuid,
336 Path: "content change",
337 Old: safcm.FileChangeInfo{
344 New: safcm.FileChangeInfo{
351 DataDiff: `@@ -1,2 +1,2 @@
358 Path: "multiple changes",
359 Old: safcm.FileChangeInfo{
366 New: safcm.FileChangeInfo{
367 Mode: fs.ModeDir | 0755,
373 DataDiff: `@@ -1,2 +1 @@
379 "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",
390 New: safcm.FileChangeInfo{
399 `changed 1 file(s): (dry-run)
400 "file": created, file, user(1000) group(2000), 0644
412 New: safcm.FileChangeInfo{
421 "changed 1 file(s): (dry-run)\n\x1B[36m\"file\"\x1B[0m: \x1B[32mcreated\x1B[0m, file, user(1000) group(2000), 0644\n",
432 New: safcm.FileChangeInfo{
443 Old: safcm.FileChangeInfo{
450 New: safcm.FileChangeInfo{
461 "\x00": created, invalid type dLDpSc?---------, \x01(-1) \x02(-2), 07777
463 \ No newline at end of file
464 "\x00": file -> invalid type dLDpSc?---------, \x01(-1) \x02(-2) -> \x03(-3) \x04(-4), 0 -> 07777
466 \ No newline at end of file
478 New: safcm.FileChangeInfo{
489 Old: safcm.FileChangeInfo{
496 New: safcm.FileChangeInfo{
506 "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",
510 for _, tc := range tests {
511 t.Run(tc.name, func(t *testing.T) {
513 config: &config.Config{
519 res := s.formatFileChanges(tc.changes)
520 testutil.AssertEqual(t, "res", res, tc.exp)
525 func TestFormatPackageChanges(t *testing.T) {
530 changes []safcm.PackageChange
538 []safcm.PackageChange{
546 `installed 2 package(s):
556 []safcm.PackageChange{
564 "installed 2 package(s):\n\x1b[36m\"package-one\"\x1b[0m\n\x1b[36m\"package-two\"\x1b[0m\n",
571 []safcm.PackageChange{
579 `installed 2 package(s): (dry-run)
589 []safcm.PackageChange{
597 "installed 2 package(s): (dry-run)\n\x1b[36m\"package-one\"\x1b[0m\n\x1b[36m\"package-two\"\x1b[0m\n",
604 []safcm.PackageChange{
609 `installed 1 package(s):
618 []safcm.PackageChange{
623 "installed 1 package(s):\n\x1b[36m\"\\x00\"\x1b[0m\n",
627 for _, tc := range tests {
628 t.Run(tc.name, func(t *testing.T) {
630 config: &config.Config{
636 res := s.formatPackageChanges(tc.changes)
637 testutil.AssertEqual(t, "res", res, tc.exp)
642 func TestFormatServiceChanges(t *testing.T) {
647 changes []safcm.ServiceChange
655 []safcm.ServiceChange{
665 Name: "service-three",
670 `modified 3 service(s):
671 "service-one": started
672 "service-two": enabled
673 "service-three": started, enabled
681 []safcm.ServiceChange{
691 Name: "service-three",
696 "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",
703 []safcm.ServiceChange{
713 Name: "service-three",
718 `modified 3 service(s): (dry-run)
719 "service-one": started
720 "service-two": enabled
721 "service-three": started, enabled
729 []safcm.ServiceChange{
739 Name: "service-three",
744 "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",
751 []safcm.ServiceChange{
761 `modified 2 service(s):
763 "\x01": started, enabled
771 []safcm.ServiceChange{
781 "modified 2 service(s):\n\x1b[36m\"\\x00\"\x1b[0m: \n\x1b[36m\"\\x01\"\x1b[0m: started, enabled\n",
785 for _, tc := range tests {
786 t.Run(tc.name, func(t *testing.T) {
788 config: &config.Config{
794 res := s.formatServiceChanges(tc.changes)
795 testutil.AssertEqual(t, "res", res, tc.exp)
800 func TestFormatCommandChanges(t *testing.T) {
806 changes []safcm.CommandChange
815 []safcm.CommandChange{
817 Command: "fake command",
818 Output: "fake output",
821 Command: "fake command with no output",
824 Command: "fake command with newline",
825 Output: "fake output\n",
828 Command: "fake command with more output",
829 Output: "fake out\nfake put\nfake\n",
832 Command: "fake failed command",
833 Output: "fake output",
837 `executed 5 command(s):
840 > \ No newline at end of file
841 "fake command with no output"
842 "fake command with newline":
844 "fake command with more output":
848 "fake failed command", failed: "fake error":
850 > \ No newline at end of file
859 []safcm.CommandChange{
861 Command: "fake command",
862 Output: "fake output",
865 Command: "fake command with no output",
868 Command: "fake command with newline",
869 Output: "fake output\n",
872 Command: "fake command with more output",
873 Output: "fake out\nfake put\nfake\n",
876 Command: "fake failed command",
877 Output: "fake output",
881 "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",
889 []safcm.CommandChange{
891 Command: "fake command",
892 Output: "fake output",
895 `executed 1 command(s): (dry-run)
898 > \ No newline at end of file
907 []safcm.CommandChange{
909 Command: "fake command",
910 Output: "fake output",
913 "executed 1 command(s): (dry-run)\n\x1b[36m\"fake command\"\x1b[0m:\n > fake output\n > \\ No newline at end of file\n",
921 []safcm.CommandChange{
923 Command: "fake command",
924 Output: "fake output",
927 Command: "fake command with no output",
930 Command: "fake command with newline",
931 Output: "fake output\n",
934 Command: "fake command with more output",
935 Output: "fake out\nfake put\nfake\n",
938 Command: "fake failed command",
939 Output: "fake output",
943 `executed 5 command(s), 1 with no output:
946 > \ No newline at end of file
947 "fake command with newline":
949 "fake command with more output":
953 "fake failed command", failed: "fake error":
955 > \ No newline at end of file
964 []safcm.CommandChange{
966 Command: "fake command",
967 Output: "fake output",
970 Command: "fake command with no output",
973 Command: "fake command with newline",
974 Output: "fake output\n",
977 Command: "fake command with more output",
978 Output: "fake out\nfake put\nfake\n",
981 Command: "fake failed command",
982 Output: "fake output",
986 "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",
990 "quiet (only quiet commands)",
994 []safcm.CommandChange{
996 Command: "fake command with no output",
999 Command: "fake command with no output",
1002 `executed 2 command(s), 2 with no output
1007 "quiet (quiet with errors)",
1011 []safcm.CommandChange{
1013 Command: "fake command with no output but error",
1014 Error: "fake error",
1017 Command: "fake command with no output",
1020 `executed 2 command(s), 1 with no output:
1021 "fake command with no output but error", failed: "fake error"
1030 []safcm.CommandChange{
1032 Command: "fake command",
1035 Command: "fake command with no output",
1038 Command: "fake command with newline",
1041 Command: "fake command with more output",
1044 Command: "fake failed command",
1047 `executed 5 command(s): (dry-run)
1049 "fake command with no output"
1050 "fake command with newline"
1051 "fake command with more output"
1052 "fake failed command"
1061 []safcm.CommandChange{
1069 `executed 1 command(s):
1070 "\x00", trigger for "\x01", failed: "\x03":
1072 > \ No newline at end of file
1081 []safcm.CommandChange{
1089 "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",
1093 for _, tc := range tests {
1094 t.Run(tc.name, func(t *testing.T) {
1096 config: &config.Config{
1103 res := s.formatCommandChanges(tc.changes)
1104 testutil.AssertEqual(t, "res", res, tc.exp)