package cli_test import ( "context" "os" "slices" "strings" "testing" "github.com/stretchr/testify/require" "github.com/kopia/kopia/internal/editor" "github.com/kopia/kopia/notification/notifytemplate" "github.com/kopia/kopia/tests/testenv" ) func TestNotificationTemplates(t *testing.T) { t.Parallel() runner := testenv.NewInProcRunner(t) e := testenv.NewCLITest(t, testenv.RepoFormatNotImportant, runner) defer e.RunAndExpectSuccess(t, "repo", "disconnect") e.RunAndExpectSuccess(t, "repo", "create", "filesystem", "--path", e.RepoDir) defaultTemplates := e.RunAndExpectSuccess(t, "notification", "template", "list") require.Len(t, defaultTemplates[1:], len(notifytemplate.SupportedTemplates())) // initially all templates are built-in for _, line := range defaultTemplates[1:] { require.Contains(t, line, "") } // override 'test-notification.txt' template from STDIN and verify. runner.SetNextStdin(strings.NewReader("Subject: test-template-subject\n\ntest-template-body1\ntest-template-body2\n")) e.RunAndExpectSuccess(t, "notification", "template", "set", "test-notification.txt", "--from-stdin") verifyTemplateContents(t, e, "test-notification.txt", []string{ "Subject: test-template-subject", "", "test-template-body1", "test-template-body2", }) // make sure it shows up as verifyHasLine(t, e.RunAndExpectSuccess(t, "notification", "template", "list"), func(s string) bool { return strings.Contains(s, "test-notification.txt") && strings.Contains(s, "") }) // reset 'test-notification.txt' template and verify. e.RunAndExpectSuccess(t, "notification", "template", "remove", "test-notification.txt") // make sure it shows up as verifyHasLine(t, e.RunAndExpectSuccess(t, "notification", "template", "list"), func(s string) bool { return strings.Contains(s, "test-notification.txt") && strings.Contains(s, "") }) // now the same using external file td := t.TempDir() fname := td + "/template.md" // no such file e.RunAndExpectFailure(t, "notification", "template", "set", "test-notification.txt", "--from-file="+fname) os.WriteFile(fname, []byte("Subject: test-template-subject\n\ntest-template-body3\ntest-template-body4\n"), 0o600) e.RunAndExpectSuccess(t, "notification", "template", "set", "test-notification.txt", "--from-file="+fname) verifyTemplateContents(t, e, "test-notification.txt", []string{ "Subject: test-template-subject", "", "test-template-body3", "test-template-body4", }) // override editor for the next part oldEditor := editor.EditFile defer func() { editor.EditFile = oldEditor }() invokedSecond := false // when editor is first invoked, it will show the old template // try setting the template to an invalid value and verify that the editor is invoked again. editor.EditFile = func(ctx context.Context, filename string) error { b, err := os.ReadFile(filename) require.NoError(t, err) // verify we got the old version of the template require.Equal(t, "Subject: test-template-subject\n\ntest-template-body3\ntest-template-body4\n", string(b)) // write an invalid template that fails to parse os.WriteFile(filename, []byte("Subject: test-template-subject\n\ntest-template-body5 {{\ntest-template-body6\n"), 0o600) // editor will be invoked again editor.EditFile = func(ctx context.Context, filename string) error { invokedSecond = true // this time we write the corrected template os.WriteFile(filename, []byte("Subject: test-template-subject\n\ntest-template-body5\ntest-template-body6\n"), 0o600) return nil } return nil } e.RunAndExpectSuccess(t, "notification", "template", "set", "test-notification.txt", "--editor") verifyTemplateContents(t, e, "test-notification.txt", []string{ "Subject: test-template-subject", "", "test-template-body5", "test-template-body6", }) require.True(t, invokedSecond) } func verifyTemplateContents(t *testing.T, e *testenv.CLITest, templateName string, expectedLines []string) { t.Helper() lines := e.RunAndExpectSuccess(t, "notification", "template", "show", ""+templateName) require.Equal(t, expectedLines, lines) } func verifyHasLine(t *testing.T, lines []string, ok func(s string) bool) { t.Helper() if slices.ContainsFunc(lines, ok) { return } t.Errorf("output line meeting given condition was not found: %v", lines) }