package vaultik_test import ( "context" "io" "path/filepath" "testing" "filippo.io/age" "github.com/spf13/afero" "github.com/stretchr/testify/require" "sneak.berlin/go/vaultik/internal/log" "sneak.berlin/go/vaultik/internal/ui" "sneak.berlin/go/vaultik/internal/vaultik" ) // TestVerifyPassesWhenBackupReusesStoredBlob backs up a file, then backs it // up again from an empty local index, as after `database delete`, with an // extra age recipient. The second backup builds the same blob, finds it // already stored and skips the upload, but its own copy is larger, because // age writes a header entry for each recipient. The second snapshot must // record the size of the stored object, so shallow and deep verify pass. func TestVerifyPassesWhenBackupReusesStoredBlob(t *testing.T) { log.Initialize(log.Config{}) t.Parallel() fs := afero.NewOsFs() tempDir := t.TempDir() dataDir := filepath.Join(tempDir, "source") storeDir := filepath.Join(tempDir, "remote") secondDBPath := filepath.Join(tempDir, "second.sqlite") chunkSize := int64(64 * 1024) maxBlobSize := int64(512 * 1024) require.NoError(t, fs.MkdirAll(dataDir, 0o755)) require.NoError(t, afero.WriteFile(fs, filepath.Join(dataDir, "data.bin"), bytesPattern("reuse-", int(chunkSize*3)), 0o644)) ctx := context.Background() runFileStorageBackup(ctx, t, fs, dataDir, storeDir, filepath.Join(tempDir, "first.sqlite"), chunkSize, maxBlobSize) env := setupDedupBackupEnv( ctx, t, fs, storeDir, secondDBPath, chunkSize, maxBlobSize) defer func() { _ = env.db.Close() }() extra, err := age.GenerateX25519Identity() require.NoError(t, err) env.cfg.AgeRecipients = append(env.cfg.AgeRecipients, extra.Recipient().String()) snapshotID, result := runDedupSnapshot(ctx, t, env.sm, env.makeScanner(), env.cfg.Hostname, "second", dataDir, secondDBPath) require.Positive(t, result.BlobsCreated) require.Zero(t, result.BlobsUploaded, "the second backup should find every blob already stored") require.NoError(t, env.db.Close()) verifier := &vaultik.Vaultik{ Config: env.cfg, Storage: env.storer, Fs: fs, Stdout: io.Discard, Stderr: io.Discard, UI: ui.NewWithColor(io.Discard, false), } verifier.SetContext(ctx) require.NoError(t, verifier.VerifySnapshotWithOptions(snapshotID, &vaultik.VerifyOptions{}), "shallow verify should pass") require.NoError(t, verifier.VerifySnapshotWithOptions(snapshotID, &vaultik.VerifyOptions{Deep: true}), "deep verify should pass") }