package vaultik_test import ( "bytes" "context" "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "sneak.berlin/go/vaultik/internal/log" "sneak.berlin/go/vaultik/internal/vaultik" ) // TestPruneBlobs_UnreadableManifestDeletesNothing is the regression guard // for issue #157: prune identifies referenced blobs by reading every // snapshot's manifest, and a manifest it cannot decode used to be logged // and skipped. Blobs referenced only by that snapshot then looked // unreferenced and were deleted, with a zero exit — silent backup loss, // made worse by `snapshot create --prune` running unattended with force. // // The single blob here is referenced only by the snapshot whose manifest // is corrupt, so the old behaviour would delete it and succeed. Prune // must instead delete nothing and return an error. func TestPruneBlobs_UnreadableManifestDeletesNothing(t *testing.T) { log.Initialize(log.Config{}) t.Parallel() env := newListEnv(t) ctx := context.Background() blobKey := "blobs/" + testBlobHashA[:2] + "/" + testBlobHashA[2:4] + "/" + testBlobHashA require.NoError(t, env.store.Put(ctx, blobKey, bytes.NewReader([]byte("blob-bytes")))) // A manifest at the path prune reads, but with contents it cannot // decode. require.NoError(t, env.store.Put(ctx, "metadata/corruptkey/manifest.json.zst", bytes.NewReader([]byte("not a valid manifest")))) err := env.v.PruneBlobs(&vaultik.PruneOptions{Force: true}) require.Error(t, err, "prune must fail when a manifest cannot be read") assert.True(t, env.store.hasKey(blobKey), "no blob may be deleted when a manifest is unreadable") }