X-Git-Url: https://ruderich.org/simon/gitweb/?a=blobdiff_plain;f=frontend%2Fchanges_test.go;fp=frontend%2Fchanges_test.go;h=02a95c40c24101d1250b9edb031fc98ea6399428;hb=ecbcb0132728cc18016819a214378b642d92278e;hp=0000000000000000000000000000000000000000;hpb=b0f49e5d47786984e24731b200d3d3d7d6add263;p=safcm%2Fsafcm.git diff --git a/frontend/changes_test.go b/frontend/changes_test.go new file mode 100644 index 0000000..02a95c4 --- /dev/null +++ b/frontend/changes_test.go @@ -0,0 +1,1234 @@ +// Copyright (C) 2021 Simon Ruderich +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +package frontend + +import ( + "io/fs" + "testing" + + "ruderich.org/simon/safcm" + "ruderich.org/simon/safcm/testutil" +) + +func TestFormatChanges(t *testing.T) { + tests := []struct { + name string + dryRun bool + quiet bool + isTTY bool + resp safcm.MsgSyncResp + exp string + }{ + + // Just a few basic tests and border cases; see the other + // tests for more detailed tests of each format function + + { + "no changes", + false, + false, + false, + safcm.MsgSyncResp{}, + "no changes", + }, + + { + "changes", + false, + false, + false, + safcm.MsgSyncResp{ + FileChanges: []safcm.FileChange{ + { + Path: "created", + Created: true, + New: safcm.FileChangeInfo{ + Mode: 0644, + User: "user", + Uid: 1000, + Group: "group", + Gid: 2000, + }, + }, + }, + PackageChanges: []safcm.PackageChange{ + { + Name: "package-one", + }, + { + Name: "package-two", + }, + }, + ServiceChanges: []safcm.ServiceChange{ + { + Name: "service-one", + Started: true, + }, + { + Name: "service-two", + Enabled: true, + }, + { + Name: "service-three", + Started: true, + Enabled: true, + }, + }, + CommandChanges: []safcm.CommandChange{ + { + Command: "fake command", + Output: "fake output", + }, + { + Command: "fake command with no output", + }, + }, + }, + "\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", + }, + + { + "command changes only, dry-run", + true, + false, + false, + safcm.MsgSyncResp{ + CommandChanges: []safcm.CommandChange{ + { + Command: "fake command", + }, + { + Command: "fake command with no output", + }, + { + Command: "fake command with newline", + }, + { + Command: "fake command with more output", + }, + { + Command: "fake failed command", + }, + }, + }, + "\nwill execute 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", + }, + { + "command changes only, quiet & dry-run", + true, + true, + false, + safcm.MsgSyncResp{ + CommandChanges: []safcm.CommandChange{ + { + Command: "fake command", + }, + { + Command: "fake command with no output", + }, + { + Command: "fake command with newline", + }, + { + Command: "fake command with more output", + }, + { + Command: "fake failed command", + }, + }, + }, + "will execute 5 command(s) (dry-run)\n", + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + c := Changes{ + DryRun: tc.dryRun, + Quiet: tc.quiet, + IsTTY: tc.isTTY, + } + + res := c.FormatChanges(tc.resp) + testutil.AssertEqual(t, "res", res, tc.exp) + }) + } +} + +func TestFormatFileChanges(t *testing.T) { + tests := []struct { + name string + dryRun bool + isTTY bool + changes []safcm.FileChange + exp string + }{ + + { + "regular", + false, + false, + []safcm.FileChange{ + { + Path: "created: file", + Created: true, + New: safcm.FileChangeInfo{ + Mode: 0644, + User: "user", + Uid: 1000, + Group: "group", + Gid: 2000, + }, + }, + { + Path: "created: link", + Created: true, + New: safcm.FileChangeInfo{ + Mode: fs.ModeSymlink | 0777, + User: "user", + Uid: 1000, + Group: "group", + Gid: 2000, + }, + }, + { + Path: "type change: file -> dir", + Old: safcm.FileChangeInfo{ + Mode: 0751, + User: "user", + Uid: 1000, + Group: "group", + Gid: 2000, + }, + New: safcm.FileChangeInfo{ + Mode: fs.ModeDir | 0751, + User: "user", + Uid: 1000, + Group: "group", + Gid: 2000, + }, + DataDiff: `@@ -1,2 +1 @@ +-content + +`, + }, + { + Path: "user change", + Old: safcm.FileChangeInfo{ + Mode: 0755, + User: "user", + Uid: 1000, + Group: "group", + Gid: 2000, + }, + New: safcm.FileChangeInfo{ + Mode: 0755, + User: "user2", + Uid: 1001, + Group: "group", + Gid: 2000, + }, + }, + { + Path: "group change", + Old: safcm.FileChangeInfo{ + Mode: 0755, + User: "user", + Uid: 1000, + Group: "group", + Gid: 2000, + }, + New: safcm.FileChangeInfo{ + Mode: 0755, + User: "user", + Uid: 1000, + Group: "group2", + Gid: 2001, + }, + }, + { + Path: "mode change", + Old: safcm.FileChangeInfo{ + Mode: 0755, + User: "user", + Uid: 1000, + Group: "group", + Gid: 2000, + }, + New: safcm.FileChangeInfo{ + Mode: 0750, + User: "user", + Uid: 1000, + Group: "group", + Gid: 2000, + }, + }, + { + Path: "mode change (setuid)", + Old: safcm.FileChangeInfo{ + Mode: 0755, + User: "user", + Uid: 1000, + Group: "group", + Gid: 2000, + }, + New: safcm.FileChangeInfo{ + Mode: 0755 | fs.ModeSetuid, + User: "user", + Uid: 1000, + Group: "group", + Gid: 2000, + }, + }, + { + Path: "content change", + Old: safcm.FileChangeInfo{ + Mode: 0644, + User: "user", + Uid: 1000, + Group: "group", + Gid: 2000, + }, + New: safcm.FileChangeInfo{ + Mode: 0644, + User: "user", + Uid: 1000, + Group: "group", + Gid: 2000, + }, + DataDiff: `@@ -1,2 +1,2 @@ +-old content ++content + +`, + }, + { + Path: "multiple changes", + Old: safcm.FileChangeInfo{ + Mode: 0644, + User: "user", + Uid: 1000, + Group: "group", + Gid: 2000, + }, + New: safcm.FileChangeInfo{ + Mode: fs.ModeDir | 0755, + User: "user2", + Uid: 1001, + Group: "group2", + Gid: 2001, + }, + DataDiff: `@@ -1,2 +1 @@ +-content + +`, + }, + }, + `changed 9 file(s): +"created: file": created, file, user(1000) group(2000), 0644 +"created: link": created, symlink, user(1000) group(2000), 0777 +"type change: file -> dir": file -> dir + @@ -1,2 +1 @@ + -content + +"user change": user(1000) group(2000) -> user2(1001) group(2000) +"group change": user(1000) group(2000) -> user(1000) group2(2001) +"mode change": 0755 -> 0750 +"mode change (setuid)": 0755 -> 04755 +"content change": + @@ -1,2 +1,2 @@ + -old content + +content + +"multiple changes": file -> dir, user(1000) group(2000) -> user2(1001) group2(2001), 0644 -> 0755 + @@ -1,2 +1 @@ + -content + +`, + }, + + { + "regular (tty)", + false, + true, + []safcm.FileChange{ + { + Path: "created: file", + Created: true, + New: safcm.FileChangeInfo{ + Mode: 0644, + User: "user", + Uid: 1000, + Group: "group", + Gid: 2000, + }, + }, + { + Path: "created: link", + Created: true, + New: safcm.FileChangeInfo{ + Mode: fs.ModeSymlink | 0777, + User: "user", + Uid: 1000, + Group: "group", + Gid: 2000, + }, + }, + { + Path: "type change: file -> dir", + Old: safcm.FileChangeInfo{ + Mode: 0751, + User: "user", + Uid: 1000, + Group: "group", + Gid: 2000, + }, + New: safcm.FileChangeInfo{ + Mode: fs.ModeDir | 0751, + User: "user", + Uid: 1000, + Group: "group", + Gid: 2000, + }, + DataDiff: `@@ -1,2 +1 @@ +-content + +`, + }, + { + Path: "user change", + Old: safcm.FileChangeInfo{ + Mode: 0755, + User: "user", + Uid: 1000, + Group: "group", + Gid: 2000, + }, + New: safcm.FileChangeInfo{ + Mode: 0755, + User: "user2", + Uid: 1001, + Group: "group", + Gid: 2000, + }, + }, + { + Path: "group change", + Old: safcm.FileChangeInfo{ + Mode: 0755, + User: "user", + Uid: 1000, + Group: "group", + Gid: 2000, + }, + New: safcm.FileChangeInfo{ + Mode: 0755, + User: "user", + Uid: 1000, + Group: "group2", + Gid: 2001, + }, + }, + { + Path: "mode change", + Old: safcm.FileChangeInfo{ + Mode: 0755, + User: "user", + Uid: 1000, + Group: "group", + Gid: 2000, + }, + New: safcm.FileChangeInfo{ + Mode: 0750, + User: "user", + Uid: 1000, + Group: "group", + Gid: 2000, + }, + }, + { + Path: "mode change (setuid)", + Old: safcm.FileChangeInfo{ + Mode: 0755, + User: "user", + Uid: 1000, + Group: "group", + Gid: 2000, + }, + New: safcm.FileChangeInfo{ + Mode: 0755 | fs.ModeSetuid, + User: "user", + Uid: 1000, + Group: "group", + Gid: 2000, + }, + }, + { + Path: "content change", + Old: safcm.FileChangeInfo{ + Mode: 0644, + User: "user", + Uid: 1000, + Group: "group", + Gid: 2000, + }, + New: safcm.FileChangeInfo{ + Mode: 0644, + User: "user", + Uid: 1000, + Group: "group", + Gid: 2000, + }, + DataDiff: `@@ -1,2 +1,2 @@ +-old content ++content + +`, + }, + { + Path: "multiple changes", + Old: safcm.FileChangeInfo{ + Mode: 0644, + User: "user", + Uid: 1000, + Group: "group", + Gid: 2000, + }, + New: safcm.FileChangeInfo{ + Mode: fs.ModeDir | 0755, + User: "user2", + Uid: 1001, + Group: "group2", + Gid: 2001, + }, + DataDiff: `@@ -1,2 +1 @@ +-content + +`, + }, + }, + "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", + }, + + { + "dry-run", + true, + false, + []safcm.FileChange{ + { + Path: "file", + Created: true, + New: safcm.FileChangeInfo{ + Mode: 0644, + User: "user", + Uid: 1000, + Group: "group", + Gid: 2000, + }, + }, + }, + `will change 1 file(s): (dry-run) +"file": created, file, user(1000) group(2000), 0644 +`, + }, + + { + "dry-run (tty)", + true, + true, + []safcm.FileChange{ + { + Path: "file", + Created: true, + New: safcm.FileChangeInfo{ + Mode: 0644, + User: "user", + Uid: 1000, + Group: "group", + Gid: 2000, + }, + }, + }, + "will change 1 file(s): (dry-run)\n\x1B[36m\"file\"\x1B[0m: \x1B[32mcreated\x1B[0m, file, user(1000) group(2000), 0644\n", + }, + + { + "escaping", + false, + false, + []safcm.FileChange{ + { + Path: "\x00", + Created: true, + New: safcm.FileChangeInfo{ + Mode: 0xFFFFFFFF, + User: "\x01", + Uid: -1, + Group: "\x02", + Gid: -2, + }, + DataDiff: "\x03", + }, + { + Path: "\x00", + Old: safcm.FileChangeInfo{ + Mode: 0x00000000, + User: "\x01", + Uid: -1, + Group: "\x02", + Gid: -2, + }, + New: safcm.FileChangeInfo{ + Mode: 0xFFFFFFFF, + User: "\x03", + Uid: -3, + Group: "\x04", + Gid: -4, + }, + DataDiff: "\x05", + }, + }, + `changed 2 file(s): +"\x00": created, invalid type dLDpSc?---------, \x01(-1) \x02(-2), 07777 + \x03 + \ No newline at end of file +"\x00": file -> invalid type dLDpSc?---------, \x01(-1) \x02(-2) -> \x03(-3) \x04(-4), 0 -> 07777 + \x05 + \ No newline at end of file +`, + }, + + { + "escaping (tty)", + false, + true, + []safcm.FileChange{ + { + Path: "\x00", + Created: true, + New: safcm.FileChangeInfo{ + Mode: 0xFFFFFFFF, + User: "\x01", + Uid: -1, + Group: "\x02", + Gid: -2, + }, + DataDiff: "\x03", + }, + { + Path: "\x00", + Old: safcm.FileChangeInfo{ + Mode: 0x00000000, + User: "\x01", + Uid: -1, + Group: "\x02", + Gid: -2, + }, + New: safcm.FileChangeInfo{ + Mode: 0xFFFFFFFF, + User: "\x03", + Uid: -3, + Group: "\x04", + Gid: -4, + }, + DataDiff: "\x05", + }, + }, + "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", + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + c := Changes{ + DryRun: tc.dryRun, + IsTTY: tc.isTTY, + } + + res := c.FormatFileChanges(tc.changes) + testutil.AssertEqual(t, "res", res, tc.exp) + }) + } +} + +func TestFormatPackageChanges(t *testing.T) { + tests := []struct { + name string + dryRun bool + isTTY bool + changes []safcm.PackageChange + exp string + }{ + + { + "regular", + false, + false, + []safcm.PackageChange{ + { + Name: "package-one", + }, + { + Name: "package-two", + }, + }, + `installed 2 package(s): +"package-one" +"package-two" +`, + }, + + { + "regular (tty)", + false, + true, + []safcm.PackageChange{ + { + Name: "package-one", + }, + { + Name: "package-two", + }, + }, + "installed 2 package(s):\n\x1b[36m\"package-one\"\x1b[0m\n\x1b[36m\"package-two\"\x1b[0m\n", + }, + + { + "dry-run", + true, + false, + []safcm.PackageChange{ + { + Name: "package-one", + }, + { + Name: "package-two", + }, + }, + `will install 2 package(s): (dry-run) +"package-one" +"package-two" +`, + }, + + { + "dry-run (tty)", + true, + true, + []safcm.PackageChange{ + { + Name: "package-one", + }, + { + Name: "package-two", + }, + }, + "will install 2 package(s): (dry-run)\n\x1b[36m\"package-one\"\x1b[0m\n\x1b[36m\"package-two\"\x1b[0m\n", + }, + + { + "escaping", + false, + false, + []safcm.PackageChange{ + { + Name: "\x00", + }, + }, + `installed 1 package(s): +"\x00" +`, + }, + + { + "escaping (tty)", + false, + true, + []safcm.PackageChange{ + { + Name: "\x00", + }, + }, + "installed 1 package(s):\n\x1b[36m\"\\x00\"\x1b[0m\n", + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + c := Changes{ + DryRun: tc.dryRun, + IsTTY: tc.isTTY, + } + + res := c.FormatPackageChanges(tc.changes) + testutil.AssertEqual(t, "res", res, tc.exp) + }) + } +} + +func TestFormatServiceChanges(t *testing.T) { + tests := []struct { + name string + dryRun bool + isTTY bool + changes []safcm.ServiceChange + exp string + }{ + + { + "regular", + false, + false, + []safcm.ServiceChange{ + { + Name: "service-one", + Started: true, + }, + { + Name: "service-two", + Enabled: true, + }, + { + Name: "service-three", + Started: true, + Enabled: true, + }, + }, + `modified 3 service(s): +"service-one": started +"service-two": enabled +"service-three": started, enabled +`, + }, + + { + "regular (tty)", + false, + true, + []safcm.ServiceChange{ + { + Name: "service-one", + Started: true, + }, + { + Name: "service-two", + Enabled: true, + }, + { + Name: "service-three", + Started: true, + Enabled: true, + }, + }, + "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", + }, + + { + "dry-run", + true, + false, + []safcm.ServiceChange{ + { + Name: "service-one", + Started: true, + }, + { + Name: "service-two", + Enabled: true, + }, + { + Name: "service-three", + Started: true, + Enabled: true, + }, + }, + `will modify 3 service(s): (dry-run) +"service-one": started +"service-two": enabled +"service-three": started, enabled +`, + }, + + { + "dry-run (tty)", + true, + true, + []safcm.ServiceChange{ + { + Name: "service-one", + Started: true, + }, + { + Name: "service-two", + Enabled: true, + }, + { + Name: "service-three", + Started: true, + Enabled: true, + }, + }, + "will modify 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", + }, + + { + "escaping", + false, + false, + []safcm.ServiceChange{ + { + Name: "\x00", + }, + { + Name: "\x01", + Started: true, + Enabled: true, + }, + }, + `modified 2 service(s): +"\x00": +"\x01": started, enabled +`, + }, + + { + "escaping (tty)", + false, + true, + []safcm.ServiceChange{ + { + Name: "\x00", + }, + { + Name: "\x01", + Started: true, + Enabled: true, + }, + }, + "modified 2 service(s):\n\x1b[36m\"\\x00\"\x1b[0m: \n\x1b[36m\"\\x01\"\x1b[0m: started, enabled\n", + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + c := Changes{ + DryRun: tc.dryRun, + IsTTY: tc.isTTY, + } + + res := c.FormatServiceChanges(tc.changes) + testutil.AssertEqual(t, "res", res, tc.exp) + }) + } +} + +func TestFormatCommandChanges(t *testing.T) { + tests := []struct { + name string + dryRun bool + quiet bool + isTTY bool + changes []safcm.CommandChange + exp string + }{ + + { + "regular", + false, + false, + false, + []safcm.CommandChange{ + { + Command: "fake command", + Output: "fake output", + }, + { + Command: "fake command with no output", + }, + { + Command: "fake command with newline", + Output: "fake output\n", + }, + { + Command: "fake command with more output", + Output: "fake out\nfake put\nfake\n", + }, + { + Command: "fake failed command", + Output: "fake output", + Error: "fake error", + }, + }, + `executed 5 command(s): +"fake command": + > fake output + > \ No newline at end of file +"fake command with no output" +"fake command with newline": + > fake output +"fake command with more output": + > fake out + > fake put + > fake +"fake failed command", failed: "fake error": + > fake output + > \ No newline at end of file +`, + }, + + { + "regular (tty)", + false, + false, + true, + []safcm.CommandChange{ + { + Command: "fake command", + Output: "fake output", + }, + { + Command: "fake command with no output", + }, + { + Command: "fake command with newline", + Output: "fake output\n", + }, + { + Command: "fake command with more output", + Output: "fake out\nfake put\nfake\n", + }, + { + Command: "fake failed command", + Output: "fake output", + Error: "fake error", + }, + }, + "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", + }, + + { + "dry-run", + true, + false, + false, + []safcm.CommandChange{ + { + Command: "fake command", + }, + }, + `will execute 1 command(s): (dry-run) +"fake command" +`, + }, + + { + "dry-run (tty)", + true, + false, + true, + []safcm.CommandChange{ + { + Command: "fake command", + }, + }, + "will execute 1 command(s): (dry-run)\n\x1b[36m\"fake command\"\x1b[0m\n", + }, + + { + "quiet", + false, + true, + false, + []safcm.CommandChange{ + { + Command: "fake command", + Output: "fake output", + }, + { + Command: "fake command with no output", + }, + { + Command: "fake command with newline", + Output: "fake output\n", + }, + { + Command: "fake command with more output", + Output: "fake out\nfake put\nfake\n", + }, + { + Command: "fake failed command", + Output: "fake output", + Error: "fake error", + }, + }, + `executed 5 command(s), 1 with no output (hidden): +"fake command": + > fake output + > \ No newline at end of file +"fake command with newline": + > fake output +"fake command with more output": + > fake out + > fake put + > fake +"fake failed command", failed: "fake error": + > fake output + > \ No newline at end of file +`, + }, + + { + "quiet (tty)", + false, + true, + true, + []safcm.CommandChange{ + { + Command: "fake command", + Output: "fake output", + }, + { + Command: "fake command with no output", + }, + { + Command: "fake command with newline", + Output: "fake output\n", + }, + { + Command: "fake command with more output", + Output: "fake out\nfake put\nfake\n", + }, + { + Command: "fake failed command", + Output: "fake output", + Error: "fake error", + }, + }, + "executed 5 command(s), 1 with no output (hidden):\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", + }, + + { + "quiet (only quiet commands)", + false, + true, + false, + []safcm.CommandChange{ + { + Command: "fake command with no output", + }, + { + Command: "fake command with no output", + }, + }, + `executed 2 command(s), 2 with no output (hidden) +`, + }, + + { + "quiet (quiet with errors)", + false, + true, + false, + []safcm.CommandChange{ + { + Command: "fake command with no output but error", + Error: "fake error", + }, + { + Command: "fake command with no output", + }, + }, + `executed 2 command(s), 1 with no output (hidden): +"fake command with no output but error", failed: "fake error" +`, + }, + + { + "quiet & dry-run", + true, + true, + false, + []safcm.CommandChange{ + { + Command: "fake command", + }, + { + Command: "fake command with no output", + }, + { + Command: "fake command with newline", + }, + { + Command: "fake command with more output", + }, + { + Command: "fake failed command", + }, + }, + `will execute 5 command(s) (dry-run) +`, + }, + + { + "escaping", + false, + false, + false, + []safcm.CommandChange{ + { + Command: "\x00", + Trigger: "\x01", + Output: "\x02", + Error: "\x03", + }, + }, + `executed 1 command(s): +"\x00", trigger for "\x01", failed: "\x03": + > \x02 + > \ No newline at end of file +`, + }, + + { + "escaping (tty)", + false, + false, + true, + []safcm.CommandChange{ + { + Command: "\x00", + Trigger: "\x01", + Output: "\x02", + Error: "\x03", + }, + }, + "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", + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + c := Changes{ + DryRun: tc.dryRun, + Quiet: tc.quiet, + IsTTY: tc.isTTY, + } + + res := c.FormatCommandChanges(tc.changes) + testutil.AssertEqual(t, "res", res, tc.exp) + }) + } +}