This is an automated email from the ASF dual-hosted git repository. tuhaihe pushed a commit to branch REL_2_STABLE in repository https://gitbox.apache.org/repos/asf/cloudberry-backup.git
commit 98848ab1236d5a9fbac8120e45c22441c3bca2ed Author: woblerr <[email protected]> AuthorDate: Sun Mar 22 15:12:36 2026 +0300 Add end-to-end tests for gpbackman functionality. Also add vscode to gitignore. --- .gitignore | 6 + end_to_end/end_to_end_suite_test.go | 36 ++ end_to_end/gpbackman_test.go | 719 ++++++++++++++++++++++++++++++++++++ 3 files changed, 761 insertions(+) diff --git a/.gitignore b/.gitignore index 3bef3410..a6a328c1 100644 --- a/.gitignore +++ b/.gitignore @@ -27,6 +27,12 @@ _testmain.go gpbackup gprestore gpbackup_helper +gpbackman + +!gpbackman/ # Logs *.log + +# vscode +.vscode/ diff --git a/end_to_end/end_to_end_suite_test.go b/end_to_end/end_to_end_suite_test.go index 5738e924..2eed0150 100644 --- a/end_to_end/end_to_end_suite_test.go +++ b/end_to_end/end_to_end_suite_test.go @@ -1,6 +1,7 @@ package end_to_end_test import ( + "database/sql" "encoding/csv" "flag" "fmt" @@ -30,6 +31,7 @@ import ( "github.com/apache/cloudberry-go-libs/structmatcher" "github.com/apache/cloudberry-go-libs/testhelper" "github.com/blang/semver" + _ "github.com/mattn/go-sqlite3" "github.com/pkg/errors" "github.com/spf13/pflag" @@ -69,6 +71,7 @@ var ( schema2TupleCounts map[string]int backupDir string segmentCount int + gpbackmanPath string ) const ( @@ -463,6 +466,38 @@ func moveSegmentBackupFiles(tarBaseName string, extractDirectory string, isMulti } } +// gpbackman helpers + +func gpbackman(args ...string) []byte { + command := exec.Command(gpbackmanPath, args...) + return mustRunCommand(command) +} + +func gpbackmanWithError(args ...string) ([]byte, error) { + command := exec.Command(gpbackmanPath, args...) + return command.CombinedOutput() +} + +func getHistoryDBPathForCluster() string { + mdd := backupCluster.GetDirForContent(-1) + return path.Join(mdd, "gpbackup_history.db") +} + +// queryHistoryDB runs an SQL query against gpbackup_history.db using database/sql and returns the trimmed output. +func queryHistoryDB(historyDB string, query string) string { + db, err := sql.Open("sqlite3", historyDB) + Expect(err).ToNot(HaveOccurred()) + defer db.Close() + + var result string + err = db.QueryRow(query).Scan(&result) + if err == sql.ErrNoRows { + return "" + } + Expect(err).ToNot(HaveOccurred()) + return strings.TrimSpace(result) +} + func TestEndToEnd(t *testing.T) { format.MaxLength = 0 RegisterFailHandler(Fail) @@ -544,6 +579,7 @@ options: gprestorePath = fmt.Sprintf("%s/gprestore", binDir) backupHelperPath = fmt.Sprintf("%s/gpbackup_helper", binDir) restoreHelperPath = backupHelperPath + gpbackmanPath = fmt.Sprintf("%s/gpbackman", binDir) } segConfig := cluster.MustGetSegmentConfiguration(backupConn) backupCluster = cluster.NewCluster(segConfig) diff --git a/end_to_end/gpbackman_test.go b/end_to_end/gpbackman_test.go new file mode 100644 index 00000000..6db59af4 --- /dev/null +++ b/end_to_end/gpbackman_test.go @@ -0,0 +1,719 @@ +package end_to_end_test + +import ( + "fmt" + "strings" + + "github.com/apache/cloudberry-backup/filepath" + + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" +) + +// countBackupInfoLines counts the number of backup entry rows in backup-info +// output. Data rows contain '|' separators but do not contain the header +// label "TIMESTAMP". +func countBackupInfoLines(output []byte) int { + count := 0 + for _, line := range strings.Split(string(output), "\n") { + trimmed := strings.TrimSpace(line) + if trimmed == "" { + continue + } + if strings.Contains(trimmed, "|") && + !strings.Contains(trimmed, "TIMESTAMP") && + !isSeparatorLine(trimmed) { + count++ + } + } + return count +} + +// isSeparatorLine returns true for lines like "---+---+---". +func isSeparatorLine(line string) bool { + for _, c := range line { + if c != '-' && c != '+' && c != ' ' { + return false + } + } + return true +} + +var _ = Describe("gpbackman end to end tests", func() { + + // ------------------------------------------------------------------ // + // backup-info + // ------------------------------------------------------------------ // + Describe("backup-info", func() { + var ( + historyDB string + timestampMap map[string]string + ) + + BeforeEach(func() { + end_to_end_setup() + historyDB = getHistoryDBPathForCluster() + timestampMap = make(map[string]string) + + // 1. Full local backup (with --leaf-partition-data for incremental compatibility) + output := gpbackup(gpbackupPath, backupHelperPath, + "--backup-dir", backupDir, + "--leaf-partition-data") + timestampMap["full_local"] = getBackupTimestamp(string(output)) + + // 2. Full local backup with --include-table + output = gpbackup(gpbackupPath, backupHelperPath, + "--backup-dir", backupDir, + "--include-table", "public.foo") + timestampMap["full_include_table"] = getBackupTimestamp(string(output)) + + // 3. Full local backup with --exclude-schema + output = gpbackup(gpbackupPath, backupHelperPath, + "--backup-dir", backupDir, + "--exclude-schema", "schema2") + timestampMap["full_exclude_schema"] = getBackupTimestamp(string(output)) + + // 4. Incremental local backup (depends on full_local) + output = gpbackup(gpbackupPath, backupHelperPath, + "--backup-dir", backupDir, + "--incremental", + "--leaf-partition-data") + timestampMap["incremental"] = getBackupTimestamp(string(output)) + + // 5. Metadata-only backup + output = gpbackup(gpbackupPath, backupHelperPath, + "--backup-dir", backupDir, + "--metadata-only") + timestampMap["metadata_only"] = getBackupTimestamp(string(output)) + }) + + AfterEach(func() { + end_to_end_teardown() + }) + + It("lists all backups", func() { + output := gpbackman( + "backup-info", + "--history-db", historyDB, + ) + lines := countBackupInfoLines(output) + Expect(lines).To(BeNumerically(">=", 5), + fmt.Sprintf("Expected at least 5 backup entries, got %d.\nOutput:\n%s", + lines, string(output))) + }) + + It("filters by type full", func() { + output := gpbackman( + "backup-info", + "--history-db", historyDB, + "--type", "full", + ) + Expect(string(output)).To(ContainSubstring("full")) + Expect(string(output)).ToNot(ContainSubstring("incremental")) + }) + + It("filters by type incremental", func() { + output := gpbackman( + "backup-info", + "--history-db", historyDB, + "--type", "incremental", + ) + Expect(string(output)).To(ContainSubstring("incremental")) + lines := countBackupInfoLines(output) + Expect(lines).To(BeNumerically(">=", 1), + "Expected at least 1 incremental backup") + }) + + It("filters by type metadata-only", func() { + output := gpbackman( + "backup-info", + "--history-db", historyDB, + "--type", "metadata-only", + ) + Expect(string(output)).To(ContainSubstring("metadata-only")) + lines := countBackupInfoLines(output) + Expect(lines).To(BeNumerically(">=", 1), + "Expected at least 1 metadata-only backup") + }) + + It("filters by include-table", func() { + output := gpbackman( + "backup-info", + "--history-db", historyDB, + "--table", "public.foo", + ) + Expect(string(output)).To(ContainSubstring(timestampMap["full_include_table"])) + }) + + It("filters by exclude-schema", func() { + output := gpbackman( + "backup-info", + "--history-db", historyDB, + "--schema", "schema2", + "--exclude", + ) + Expect(string(output)).To(ContainSubstring(timestampMap["full_exclude_schema"])) + }) + + It("shows backup chain with --timestamp", func() { + output := gpbackman( + "backup-info", + "--history-db", historyDB, + "--timestamp", timestampMap["full_local"], + ) + outputStr := string(output) + Expect(outputStr).To(ContainSubstring(timestampMap["full_local"]), + "Expected the specified backup timestamp in the output") + }) + + It("shows detail with --timestamp --detail", func() { + output := gpbackman( + "backup-info", + "--history-db", historyDB, + "--timestamp", timestampMap["incremental"], + "--detail", + ) + Expect(string(output)).To(Or( + ContainSubstring("OBJECT FILTERING"), + ContainSubstring("object filtering"), + )) + }) + + It("shows detail for all backups with --detail", func() { + output := gpbackman( + "backup-info", + "--history-db", historyDB, + "--detail", + ) + Expect(string(output)).To(Or( + ContainSubstring("OBJECT FILTERING"), + ContainSubstring("object filtering"), + )) + Expect(string(output)).To(ContainSubstring("public.foo")) + }) + + It("rejects incompatible flags --timestamp with --type", func() { + _, err := gpbackmanWithError( + "backup-info", + "--history-db", historyDB, + "--timestamp", timestampMap["full_local"], + "--type", "full", + ) + Expect(err).To(HaveOccurred()) + }) + + It("rejects invalid timestamp format", func() { + _, err := gpbackmanWithError( + "backup-info", + "--history-db", historyDB, + "--timestamp", "invalid", + ) + Expect(err).To(HaveOccurred()) + }) + }) + + // ------------------------------------------------------------------ // + // report-info + // ------------------------------------------------------------------ // + Describe("report-info", func() { + var ( + historyDB string + timestampMap map[string]string + ) + + BeforeEach(func() { + end_to_end_setup() + historyDB = getHistoryDBPathForCluster() + timestampMap = make(map[string]string) + + // 1. Full local backup + output := gpbackup(gpbackupPath, backupHelperPath, + "--backup-dir", backupDir) + timestampMap["full_local"] = getBackupTimestamp(string(output)) + + // 2. Plugin backup using example_plugin + copyPluginToAllHosts(backupConn, examplePluginExec) + output = gpbackup(gpbackupPath, backupHelperPath, + "--plugin-config", examplePluginTestConfig) + timestampMap["plugin"] = getBackupTimestamp(string(output)) + }) + + AfterEach(func() { + end_to_end_teardown() + }) + + It("displays local backup report with --backup-dir", func() { + output := gpbackman( + "report-info", + "--history-db", historyDB, + "--timestamp", timestampMap["full_local"], + "--backup-dir", backupDir, + ) + Expect(string(output)).To(ContainSubstring("Backup Report")) + Expect(string(output)).To(ContainSubstring(timestampMap["full_local"])) + }) + + It("displays local backup report without --backup-dir", func() { + output := gpbackman( + "report-info", + "--history-db", historyDB, + "--timestamp", timestampMap["full_local"], + ) + Expect(string(output)).To(ContainSubstring("Backup Report")) + }) + + It("displays plugin backup report", func() { + ts := timestampMap["plugin"] + reportDir := fmt.Sprintf("/tmp/plugin_dest/%s/%s", ts[:8], ts) + output := gpbackman( + "report-info", + "--history-db", historyDB, + "--timestamp", ts, + "--plugin-config", examplePluginTestConfig, + "--plugin-report-file-path", reportDir, + ) + Expect(string(output)).To(ContainSubstring("Backup Report")) + Expect(string(output)).To(ContainSubstring(ts)) + }) + + It("rejects --plugin-report-file-path without --plugin-config", func() { + _, err := gpbackmanWithError( + "report-info", + "--history-db", historyDB, + "--timestamp", timestampMap["full_local"], + "--plugin-report-file-path", "/tmp/fake_report", + ) + Expect(err).To(HaveOccurred()) + }) + }) + + // ------------------------------------------------------------------ // + // backup-delete + // ------------------------------------------------------------------ // + Describe("backup-delete", func() { + var historyDB string + + BeforeEach(func() { + end_to_end_setup() + historyDB = getHistoryDBPathForCluster() + }) + + AfterEach(func() { + end_to_end_teardown() + }) + + It("deletes a local backup by timestamp", func() { + output := gpbackup(gpbackupPath, backupHelperPath, + "--backup-dir", backupDir) + timestamp := getBackupTimestamp(string(output)) + + gpbackman( + "backup-delete", + "--history-db", historyDB, + "--timestamp", timestamp, + "--backup-dir", backupDir, + ) + + dateDeleted := queryHistoryDB(historyDB, + fmt.Sprintf("SELECT date_deleted FROM backups WHERE timestamp = '%s'", timestamp)) + Expect(dateDeleted).ToNot(BeEmpty()) + Expect(dateDeleted).ToNot(Equal("In progress")) + + fpInfo := filepath.NewFilePathInfo(backupCluster, backupDir, timestamp, "", false) + backupDirCoordinator := fpInfo.GetDirForContent(-1) + Expect(backupDirCoordinator).ToNot(BeADirectory()) + }) + + It("deletes a local backup without --backup-dir", func() { + output := gpbackup(gpbackupPath, backupHelperPath, + "--backup-dir", backupDir) + timestamp := getBackupTimestamp(string(output)) + + gpbackman( + "backup-delete", + "--history-db", historyDB, + "--timestamp", timestamp, + ) + + dateDeleted := queryHistoryDB(historyDB, + fmt.Sprintf("SELECT date_deleted FROM backups WHERE timestamp = '%s'", timestamp)) + Expect(dateDeleted).ToNot(BeEmpty()) + }) + + It("deletes with --cascade for incremental chain", func() { + fullOutput := gpbackup(gpbackupPath, backupHelperPath, + "--backup-dir", backupDir, + "--leaf-partition-data") + fullTimestamp := getBackupTimestamp(string(fullOutput)) + + incrOutput := gpbackup(gpbackupPath, backupHelperPath, + "--backup-dir", backupDir, + "--incremental", + "--leaf-partition-data") + incrTimestamp := getBackupTimestamp(string(incrOutput)) + + gpbackman( + "backup-delete", + "--history-db", historyDB, + "--timestamp", fullTimestamp, + "--backup-dir", backupDir, + "--cascade", + ) + + fullDeleted := queryHistoryDB(historyDB, + fmt.Sprintf("SELECT date_deleted FROM backups WHERE timestamp = '%s'", fullTimestamp)) + Expect(fullDeleted).ToNot(BeEmpty()) + + incrDeleted := queryHistoryDB(historyDB, + fmt.Sprintf("SELECT date_deleted FROM backups WHERE timestamp = '%s'", incrTimestamp)) + Expect(incrDeleted).ToNot(BeEmpty()) + }) + + It("deletes a plugin backup", func() { + copyPluginToAllHosts(backupConn, examplePluginExec) + + output := gpbackup(gpbackupPath, backupHelperPath, + "--plugin-config", examplePluginTestConfig) + timestamp := getBackupTimestamp(string(output)) + + gpbackman( + "backup-delete", + "--history-db", historyDB, + "--timestamp", timestamp, + "--plugin-config", examplePluginTestConfig, + ) + + dateDeleted := queryHistoryDB(historyDB, + fmt.Sprintf("SELECT date_deleted FROM backups WHERE timestamp = '%s'", timestamp)) + Expect(dateDeleted).ToNot(BeEmpty()) + Expect(dateDeleted).ToNot(Equal("In progress")) + }) + + It("fails for non-existent timestamp", func() { + _, err := gpbackmanWithError( + "backup-delete", + "--history-db", historyDB, + "--timestamp", "29991231235959", + "--backup-dir", backupDir, + ) + Expect(err).To(HaveOccurred()) + }) + + It("skips already-deleted backup without --force", func() { + output := gpbackup(gpbackupPath, backupHelperPath, + "--backup-dir", backupDir) + timestamp := getBackupTimestamp(string(output)) + + gpbackman( + "backup-delete", + "--history-db", historyDB, + "--timestamp", timestamp, + "--backup-dir", backupDir, + ) + + // Second delete without --force should succeed without error. + // gpbackman silently skips already-deleted backups (logged at debug level). + gpbackman( + "backup-delete", + "--history-db", historyDB, + "--timestamp", timestamp, + "--backup-dir", backupDir, + ) + + dateDeleted := queryHistoryDB(historyDB, + fmt.Sprintf("SELECT date_deleted FROM backups WHERE timestamp = '%s'", timestamp)) + Expect(dateDeleted).ToNot(BeEmpty(), + "Backup should still be marked as deleted after second delete attempt") + }) + + It("re-deletes with --force", func() { + output := gpbackup(gpbackupPath, backupHelperPath, + "--backup-dir", backupDir) + timestamp := getBackupTimestamp(string(output)) + + gpbackman( + "backup-delete", + "--history-db", historyDB, + "--timestamp", timestamp, + "--backup-dir", backupDir, + ) + + gpbackman( + "backup-delete", + "--history-db", historyDB, + "--timestamp", timestamp, + "--backup-dir", backupDir, + "--force", + "--ignore-errors", + ) + + dateDeleted := queryHistoryDB(historyDB, + fmt.Sprintf("SELECT date_deleted FROM backups WHERE timestamp = '%s'", timestamp)) + Expect(dateDeleted).ToNot(BeEmpty()) + }) + }) + + // ------------------------------------------------------------------ // + // backup-delete: local file cleanup on segments + // ------------------------------------------------------------------ // + Describe("backup-delete local file cleanup", func() { + var historyDB string + + BeforeEach(func() { + end_to_end_setup() + historyDB = getHistoryDBPathForCluster() + }) + + AfterEach(func() { + end_to_end_teardown() + }) + + It("removes backup files after deletion", func() { + output := gpbackup(gpbackupPath, backupHelperPath, + "--backup-dir", backupDir, + "--single-backup-dir") + timestamp := getBackupTimestamp(string(output)) + + fpInfo := filepath.NewFilePathInfo(backupCluster, backupDir, timestamp, "", true) + backupTimestampDir := fpInfo.GetDirForContent(-1) + Expect(backupTimestampDir).To(BeADirectory(), + "Backup directory should exist before deletion") + + gpbackman( + "backup-delete", + "--history-db", historyDB, + "--timestamp", timestamp, + "--backup-dir", backupDir, + ) + + Expect(backupTimestampDir).ToNot(BeADirectory(), + "Backup directory should be removed after deletion") + }) + }) + + // ------------------------------------------------------------------ // + // backup-clean + // ------------------------------------------------------------------ // + Describe("backup-clean", func() { + var historyDB string + + BeforeEach(func() { + end_to_end_setup() + historyDB = getHistoryDBPathForCluster() + }) + + AfterEach(func() { + end_to_end_teardown() + }) + + It("cleans local backups with --before-timestamp", func() { + output1 := gpbackup(gpbackupPath, backupHelperPath, + "--backup-dir", backupDir) + timestamp1 := getBackupTimestamp(string(output1)) + + output2 := gpbackup(gpbackupPath, backupHelperPath, + "--backup-dir", backupDir) + timestamp2 := getBackupTimestamp(string(output2)) + + gpbackman( + "backup-clean", + "--history-db", historyDB, + "--before-timestamp", timestamp2, + "--backup-dir", backupDir, + ) + + dateDeleted1 := queryHistoryDB(historyDB, + fmt.Sprintf("SELECT date_deleted FROM backups WHERE timestamp = '%s'", timestamp1)) + Expect(dateDeleted1).ToNot(BeEmpty(), + fmt.Sprintf("Expected backup %s to be deleted", timestamp1)) + }) + + It("cleans local backups with --after-timestamp", func() { + output1 := gpbackup(gpbackupPath, backupHelperPath, + "--backup-dir", backupDir) + timestamp1 := getBackupTimestamp(string(output1)) + + output2 := gpbackup(gpbackupPath, backupHelperPath, + "--backup-dir", backupDir) + timestamp2 := getBackupTimestamp(string(output2)) + + gpbackman( + "backup-clean", + "--history-db", historyDB, + "--after-timestamp", timestamp1, + "--backup-dir", backupDir, + ) + + dateDeleted2 := queryHistoryDB(historyDB, + fmt.Sprintf("SELECT date_deleted FROM backups WHERE timestamp = '%s'", timestamp2)) + Expect(dateDeleted2).ToNot(BeEmpty(), + fmt.Sprintf("Expected backup %s to be deleted", timestamp2)) + }) + + It("cleans plugin backups with --before-timestamp", func() { + copyPluginToAllHosts(backupConn, examplePluginExec) + + output := gpbackup(gpbackupPath, backupHelperPath, + "--plugin-config", examplePluginTestConfig) + timestamp := getBackupTimestamp(string(output)) + + gpbackman( + "backup-clean", + "--history-db", historyDB, + "--before-timestamp", timestamp, + "--plugin-config", examplePluginTestConfig, + ) + + countStr := queryHistoryDB(historyDB, + fmt.Sprintf("SELECT count(*) FROM backups WHERE timestamp = '%s' AND date_deleted = ''", + timestamp)) + Expect(countStr).ToNot(BeEmpty()) + }) + + It("cleans local backups with --cascade for incremental chain", func() { + fullOutput := gpbackup(gpbackupPath, backupHelperPath, + "--backup-dir", backupDir, + "--leaf-partition-data") + fullTimestamp := getBackupTimestamp(string(fullOutput)) + + _ = gpbackup(gpbackupPath, backupHelperPath, + "--backup-dir", backupDir, + "--incremental", + "--leaf-partition-data") + + gpbackman( + "backup-clean", + "--history-db", historyDB, + "--before-timestamp", fullTimestamp, + "--backup-dir", backupDir, + "--cascade", + ) + // Success if no error was thrown + }) + }) + + // ------------------------------------------------------------------ // + // history-clean + // ------------------------------------------------------------------ // + Describe("history-clean", func() { + var historyDB string + + BeforeEach(func() { + end_to_end_setup() + historyDB = getHistoryDBPathForCluster() + }) + + AfterEach(func() { + end_to_end_teardown() + }) + + It("cleans deleted backup entries from history DB", func() { + output := gpbackup(gpbackupPath, backupHelperPath, + "--backup-dir", backupDir) + timestamp := getBackupTimestamp(string(output)) + + gpbackman( + "backup-delete", + "--history-db", historyDB, + "--timestamp", timestamp, + "--backup-dir", backupDir, + ) + + dateDeleted := queryHistoryDB(historyDB, + fmt.Sprintf("SELECT date_deleted FROM backups WHERE timestamp = '%s'", timestamp)) + Expect(dateDeleted).ToNot(BeEmpty()) + + // --before-timestamp uses strictly less than (<), so use a + // far-future cutoff to include the target timestamp. + gpbackman( + "history-clean", + "--history-db", historyDB, + "--before-timestamp", "99991231235959", + ) + + countStr := queryHistoryDB(historyDB, + fmt.Sprintf("SELECT count(*) FROM backups WHERE timestamp = '%s'", timestamp)) + Expect(countStr).To(Equal("0"), + fmt.Sprintf("Expected backup %s to be removed from history DB", timestamp)) + }) + + It("cleans with --older-than-days 0", func() { + output := gpbackup(gpbackupPath, backupHelperPath, + "--backup-dir", backupDir) + timestamp := getBackupTimestamp(string(output)) + + gpbackman( + "backup-delete", + "--history-db", historyDB, + "--timestamp", timestamp, + "--backup-dir", backupDir, + ) + + gpbackman( + "history-clean", + "--history-db", historyDB, + "--older-than-days", "0", + ) + + countStr := queryHistoryDB(historyDB, + fmt.Sprintf("SELECT count(*) FROM backups WHERE timestamp = '%s'", timestamp)) + Expect(countStr).To(Equal("0")) + }) + + It("leaves non-deleted entries intact", func() { + output1 := gpbackup(gpbackupPath, backupHelperPath, + "--backup-dir", backupDir) + timestamp1 := getBackupTimestamp(string(output1)) + + output2 := gpbackup(gpbackupPath, backupHelperPath, + "--backup-dir", backupDir) + timestamp2 := getBackupTimestamp(string(output2)) + + gpbackman( + "backup-delete", + "--history-db", historyDB, + "--timestamp", timestamp1, + "--backup-dir", backupDir, + ) + + gpbackman( + "history-clean", + "--history-db", historyDB, + "--older-than-days", "0", + ) + + count1 := queryHistoryDB(historyDB, + fmt.Sprintf("SELECT count(*) FROM backups WHERE timestamp = '%s'", timestamp1)) + Expect(count1).To(Equal("0"), + "Deleted backup should be removed from history") + + count2 := queryHistoryDB(historyDB, + fmt.Sprintf("SELECT count(*) FROM backups WHERE timestamp = '%s'", timestamp2)) + Expect(count2).To(Equal("1"), + "Non-deleted backup should remain in history") + }) + }) + + // ------------------------------------------------------------------ // + // version & help + // ------------------------------------------------------------------ // + Describe("gpbackman --version", func() { + It("prints version information", func() { + output := gpbackman("--version") + Expect(string(output)).To(ContainSubstring("gpbackman")) + }) + }) + + Describe("gpbackman --help", func() { + It("prints help for all subcommands", func() { + for _, subcmd := range []string{ + "backup-info", "backup-delete", "backup-clean", + "history-clean", "report-info", + } { + output := gpbackman(subcmd, "--help") + Expect(string(output)).To(ContainSubstring(subcmd), + fmt.Sprintf("Help for %s should mention the command name", subcmd)) + } + }) + }) +}) --------------------------------------------------------------------- To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]
