package cmd import ( "encoding/json" "errors" "flag" "io" "os" "path/filepath" "testing" "github.com/stretchr/testify/require" "github.com/urfave/cli/v2" ) func TestRunBackupRejectsMutuallyExclusiveModes(t *testing.T) { set := flag.NewFlagSet("backup", flag.ContinueOnError) set.Bool("db-only", true, "") set.Bool("images-only", true, "") err := runBackup(cli.NewContext(nil, set, nil)) require.EqualError(t, err, "--db-only and --images-only cannot be used together") } func TestWriteFileAtomically(t *testing.T) { t.Run("success", func(t *testing.T) { dir := t.TempDir() target := filepath.Join(dir, "backup.sql") require.NoError(t, writeFileAtomically(target, func(w io.Writer) error { _, err := io.WriteString(w, "complete backup") return err })) content, err := os.ReadFile(target) require.NoError(t, err) require.Equal(t, "complete backup", string(content)) requireNoPartFiles(t, dir) }) t.Run("failure preserves previous backup", func(t *testing.T) { dir := t.TempDir() target := filepath.Join(dir, "backup.sql") require.NoError(t, os.WriteFile(target, []byte("known good"), 0o600)) writeErr := errors.New("dump failed") err := writeFileAtomically(target, func(w io.Writer) error { _, _ = io.WriteString(w, "partial") return writeErr }) require.ErrorIs(t, err, writeErr) content, err := os.ReadFile(target) require.NoError(t, err) require.Equal(t, "known good", string(content)) requireNoPartFiles(t, dir) }) t.Run("failure leaves no new backup", func(t *testing.T) { dir := t.TempDir() target := filepath.Join(dir, "backup.sql") writeErr := errors.New("dump failed") err := writeFileAtomically(target, func(w io.Writer) error { _, _ = io.WriteString(w, "partial") return writeErr }) require.ErrorIs(t, err, writeErr) require.NoFileExists(t, target) requireNoPartFiles(t, dir) }) } func requireNoPartFiles(t *testing.T, dir string) { t.Helper() matches, err := filepath.Glob(filepath.Join(dir, "*.part")) require.NoError(t, err) require.Empty(t, matches) } func TestImageManifestEntryIntegrityFields(t *testing.T) { entry := imageManifestEntry{ Key: "tamabox/image.webp", Size: 3, SHA256: "actual-content-sha256", ETag: "provider-etag", } encoded, err := json.Marshal(entry) require.NoError(t, err) require.JSONEq(t, `{ "key":"tamabox/image.webp", "size":3, "sha256":"actual-content-sha256", "etag":"provider-etag" }`, string(encoded)) require.NotContains(t, string(encoded), "md5") }