package main_test import ( "bytes" "net/http" "net/http/httptest" "os" "strings" "testing" "github.com/influxdb/influxdb" "github.com/influxdb/influxdb/cmd/influxd" ) // Ensure the backup can download from the server and save to disk. func TestBackupCommand(t *testing.T) { // Mock the backup endpoint. s := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { if r.URL.Path != "/snapshot" { t.Fatalf("unexpected url path: %s", r.URL.Path) } // Write a simple snapshot to the buffer. sw := influxdb.NewSnapshotWriter() sw.Snapshot = &influxdb.Snapshot{Files: []influxdb.SnapshotFile{ {Name: "meta", Size: 5, Index: 10}, }} sw.FileWriters["meta"] = influxdb.NopWriteToCloser(bytes.NewBufferString("55555")) if _, err := sw.WriteTo(w); err != nil { t.Fatal(err) } })) defer s.Close() // Create a temp path and remove incremental backups at the end. path := tempfile() defer os.Remove(path) defer os.Remove(path + ".0") defer os.Remove(path + ".1") // Execute the backup against the mock server. for i := 0; i < 3; i++ { if err := NewBackupCommand().Run("-host", s.URL, path); err != nil { t.Fatal(err) } } // Verify snapshot and two incremental snapshots were written. if _, err := os.Stat(path); err != nil { t.Fatalf("snapshot not found: %s", err) } else if _, err = os.Stat(path + ".0"); err != nil { t.Fatalf("incremental snapshot(0) not found: %s", err) } else if _, err = os.Stat(path + ".1"); err != nil { t.Fatalf("incremental snapshot(1) not found: %s", err) } } // Ensure the backup command returns an error if flags cannot be parsed. func TestBackupCommand_ErrFlagParse(t *testing.T) { cmd := NewBackupCommand() if err := cmd.Run("-bad-flag"); err == nil || err.Error() != `flag provided but not defined: -bad-flag` { t.Fatal(err) } else if !strings.Contains(cmd.Stderr.String(), "usage") { t.Fatal("usage message not displayed") } } // Ensure the backup command returns an error if the host cannot be parsed. func TestBackupCommand_ErrInvalidHostURL(t *testing.T) { if err := NewBackupCommand().Run("-host", "http://%f"); err == nil || err.Error() != `parse host url: parse http://%f: hexadecimal escape in host` { t.Fatal(err) } } // Ensure the backup command returns an error if the output path is not specified. func TestBackupCommand_ErrPathRequired(t *testing.T) { if err := NewBackupCommand().Run("-host", "//localhost"); err == nil || err.Error() != `snapshot path required` { t.Fatal(err) } } // Ensure the backup returns an error if it cannot connect to the server. func TestBackupCommand_ErrConnectionRefused(t *testing.T) { // Start and immediately stop a server so we have a dead port. s := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {})) s.Close() // Execute the backup command. path := tempfile() defer os.Remove(path) if err := NewBackupCommand().Run("-host", s.URL, path); err == nil || !(strings.Contains(err.Error(), `connection refused`) || strings.Contains(err.Error(), `No connection could be made`)) { t.Fatal(err) } } // Ensure the backup returns any non-200 status codes. func TestBackupCommand_ErrServerError(t *testing.T) { s := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusInternalServerError) })) defer s.Close() // Execute the backup command. path := tempfile() defer os.Remove(path) if err := NewBackupCommand().Run("-host", s.URL, path); err == nil || err.Error() != `download: snapshot error: status=500` { t.Fatal(err) } } // BackupCommand is a test wrapper for main.BackupCommand. type BackupCommand struct { *main.BackupCommand Stderr bytes.Buffer } // NewBackupCommand returns a new instance of BackupCommand. func NewBackupCommand() *BackupCommand { cmd := &BackupCommand{BackupCommand: main.NewBackupCommand()} cmd.BackupCommand.Stderr = &cmd.Stderr return cmd }