package blob_test import ( "context" "sync" "testing" "time" "github.com/pkg/errors" "github.com/stretchr/testify/require" "github.com/kopia/kopia/internal/blobtesting" "github.com/kopia/kopia/internal/gather" "github.com/kopia/kopia/repo/blob" ) func TestListAllBlobs(t *testing.T) { data := blobtesting.DataMap{} st := blobtesting.NewMapStorage(data, nil, nil) ctx := context.Background() st.PutBlob(ctx, "foo", gather.FromSlice([]byte{1, 2, 3}), blob.PutOptions{}) st.PutBlob(ctx, "boo", gather.FromSlice([]byte{2, 3, 4}), blob.PutOptions{}) st.PutBlob(ctx, "bar", gather.FromSlice([]byte{3, 4, 5}), blob.PutOptions{}) result1, err := blob.ListAllBlobs(ctx, st, "") require.NoError(t, err) require.ElementsMatch(t, []blob.ID{"foo", "boo", "bar"}, blob.IDsFromMetadata(result1)) result2, err := blob.ListAllBlobs(ctx, st, "b") require.NoError(t, err) require.ElementsMatch(t, []blob.ID{"boo", "bar"}, blob.IDsFromMetadata(result2)) result3, err := blob.ListAllBlobs(ctx, st, "c") require.NoError(t, err) require.ElementsMatch(t, []blob.ID{}, blob.IDsFromMetadata(result3)) } func TestIterateAllPrefixesInParallel(t *testing.T) { data := blobtesting.DataMap{} st := blobtesting.NewMapStorage(data, nil, nil) ctx := context.Background() st.PutBlob(ctx, "foo", gather.FromSlice([]byte{1, 2, 3}), blob.PutOptions{}) st.PutBlob(ctx, "boo", gather.FromSlice([]byte{2, 3, 4}), blob.PutOptions{}) st.PutBlob(ctx, "bar", gather.FromSlice([]byte{3, 4, 5}), blob.PutOptions{}) var ( mu sync.Mutex got []blob.ID ) require.NoError(t, blob.IterateAllPrefixesInParallel(ctx, 10, st, []blob.ID{ "b", "c", }, func(m blob.Metadata) error { mu.Lock() defer mu.Unlock() got = append(got, m.BlobID) return nil })) require.ElementsMatch(t, []blob.ID{"boo", "bar"}, got) got = nil require.NoError(t, blob.IterateAllPrefixesInParallel(ctx, 0, st, []blob.ID{ "f", }, func(m blob.Metadata) error { mu.Lock() defer mu.Unlock() got = append(got, m.BlobID) return nil })) require.ElementsMatch(t, []blob.ID{"foo"}, got) got = nil require.NoError(t, blob.IterateAllPrefixesInParallel(ctx, 0, st, []blob.ID{ "f", "b", }, func(m blob.Metadata) error { mu.Lock() defer mu.Unlock() got = append(got, m.BlobID) return nil })) require.ElementsMatch(t, []blob.ID{"foo", "bar", "boo"}, got) errDummy := errors.New("dummy") require.ErrorIs(t, errDummy, blob.IterateAllPrefixesInParallel(ctx, 10, st, []blob.ID{ "b", "c", }, func(m blob.Metadata) error { return errDummy })) } func TestEnsureLengthExactly(t *testing.T) { require.NoError(t, blob.EnsureLengthExactly(3, 3)) require.NoError(t, blob.EnsureLengthExactly(3, -1)) require.Error(t, blob.EnsureLengthExactly(3, 4)) require.Error(t, blob.EnsureLengthExactly(3, 2)) } func TestIDsFromMetadata(t *testing.T) { require.Equal(t, []blob.ID{"foo", "bar", "baz"}, blob.IDsFromMetadata([]blob.Metadata{ {BlobID: "foo", Length: 11}, {BlobID: "bar", Length: 22}, {BlobID: "baz", Length: 55}, })) } func TestMaxTimestamp(t *testing.T) { t0 := time.Date(2020, 1, 2, 3, 4, 5, 6, time.UTC) t1 := t0.Add(1 * time.Hour) t2 := t0.Add(-1 * time.Hour) require.Equal(t, t1, blob.MaxTimestamp([]blob.Metadata{ {BlobID: "foo", Timestamp: t0}, {BlobID: "bar", Timestamp: t1}, {BlobID: "baz", Timestamp: t2}, })) require.Equal(t, time.Time{}, blob.MaxTimestamp([]blob.Metadata{})) } func TestMinTimestamp(t *testing.T) { t0 := time.Date(2020, 1, 2, 3, 4, 5, 6, time.UTC) t1 := t0.Add(1 * time.Hour) t2 := t0.Add(-1 * time.Hour) require.Equal(t, t2, blob.MinTimestamp([]blob.Metadata{ {BlobID: "foo", Timestamp: t0}, {BlobID: "bar", Timestamp: t1}, {BlobID: "baz", Timestamp: t2}, })) require.Equal(t, time.Time{}, blob.MinTimestamp([]blob.Metadata{})) } func TestTotalLength(t *testing.T) { require.Equal(t, int64(357), blob.TotalLength([]blob.Metadata{ {BlobID: "foo", Length: 123}, {BlobID: "bar", Length: 234}, })) } func TestDeleteMultiple(t *testing.T) { data := blobtesting.DataMap{ "foo": []byte{1, 2, 3}, "bar": []byte{1, 2, 4}, "baz": []byte{1, 2, 5}, "qux": []byte{1, 2, 6}, } st := blobtesting.NewMapStorage(data, nil, nil) require.NoError(t, blob.DeleteMultiple(context.Background(), st, []blob.ID{"bar", "qux"}, 4)) require.Equal(t, blobtesting.DataMap{ "foo": []byte{1, 2, 3}, "baz": []byte{1, 2, 5}, }, data) } func TestMetadataJSONString(t *testing.T) { bm := blob.Metadata{ BlobID: "foo", Length: 12345, Timestamp: time.Date(2000, 1, 2, 3, 4, 5, 6, time.UTC), } require.JSONEq(t, `{"id":"foo","length":12345,"timestamp":"2000-01-02T03:04:05.000000006Z"}`, bm.String()) } func TestPutBlobAndGetMetadata(t *testing.T) { data := blobtesting.DataMap{} fixedTime := time.Date(2000, 1, 2, 3, 4, 5, 6, time.UTC) st := blobtesting.NewMapStorage(data, nil, func() time.Time { return fixedTime }) bm, err := blob.PutBlobAndGetMetadata(context.Background(), st, "foo", gather.FromSlice([]byte{1, 2, 3}), blob.PutOptions{}) require.NoError(t, err) require.Equal(t, fixedTime, bm.Timestamp) }