可靠性: 完善 R2 备份恢复与对象生命周期
This commit is contained in:
@@ -0,0 +1,96 @@
|
||||
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")
|
||||
}
|
||||
Reference in New Issue
Block a user