storage_test.go 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204
  1. package blob_test
  2. import (
  3. "context"
  4. "sync"
  5. "testing"
  6. "time"
  7. "github.com/pkg/errors"
  8. "github.com/stretchr/testify/require"
  9. "github.com/kopia/kopia/internal/blobtesting"
  10. "github.com/kopia/kopia/internal/gather"
  11. "github.com/kopia/kopia/repo/blob"
  12. )
  13. func TestListAllBlobs(t *testing.T) {
  14. data := blobtesting.DataMap{}
  15. st := blobtesting.NewMapStorage(data, nil, nil)
  16. ctx := context.Background()
  17. st.PutBlob(ctx, "foo", gather.FromSlice([]byte{1, 2, 3}), blob.PutOptions{})
  18. st.PutBlob(ctx, "boo", gather.FromSlice([]byte{2, 3, 4}), blob.PutOptions{})
  19. st.PutBlob(ctx, "bar", gather.FromSlice([]byte{3, 4, 5}), blob.PutOptions{})
  20. result1, err := blob.ListAllBlobs(ctx, st, "")
  21. require.NoError(t, err)
  22. require.ElementsMatch(t, []blob.ID{"foo", "boo", "bar"}, blob.IDsFromMetadata(result1))
  23. result2, err := blob.ListAllBlobs(ctx, st, "b")
  24. require.NoError(t, err)
  25. require.ElementsMatch(t, []blob.ID{"boo", "bar"}, blob.IDsFromMetadata(result2))
  26. result3, err := blob.ListAllBlobs(ctx, st, "c")
  27. require.NoError(t, err)
  28. require.ElementsMatch(t, []blob.ID{}, blob.IDsFromMetadata(result3))
  29. }
  30. func TestIterateAllPrefixesInParallel(t *testing.T) {
  31. data := blobtesting.DataMap{}
  32. st := blobtesting.NewMapStorage(data, nil, nil)
  33. ctx := context.Background()
  34. st.PutBlob(ctx, "foo", gather.FromSlice([]byte{1, 2, 3}), blob.PutOptions{})
  35. st.PutBlob(ctx, "boo", gather.FromSlice([]byte{2, 3, 4}), blob.PutOptions{})
  36. st.PutBlob(ctx, "bar", gather.FromSlice([]byte{3, 4, 5}), blob.PutOptions{})
  37. var (
  38. mu sync.Mutex
  39. got []blob.ID
  40. )
  41. require.NoError(t, blob.IterateAllPrefixesInParallel(ctx, 10, st, []blob.ID{
  42. "b",
  43. "c",
  44. }, func(m blob.Metadata) error {
  45. mu.Lock()
  46. defer mu.Unlock()
  47. got = append(got, m.BlobID)
  48. return nil
  49. }))
  50. require.ElementsMatch(t, []blob.ID{"boo", "bar"}, got)
  51. got = nil
  52. require.NoError(t, blob.IterateAllPrefixesInParallel(ctx, 0, st, []blob.ID{
  53. "f",
  54. }, func(m blob.Metadata) error {
  55. mu.Lock()
  56. defer mu.Unlock()
  57. got = append(got, m.BlobID)
  58. return nil
  59. }))
  60. require.ElementsMatch(t, []blob.ID{"foo"}, got)
  61. got = nil
  62. require.NoError(t, blob.IterateAllPrefixesInParallel(ctx, 0, st, []blob.ID{
  63. "f",
  64. "b",
  65. }, func(m blob.Metadata) error {
  66. mu.Lock()
  67. defer mu.Unlock()
  68. got = append(got, m.BlobID)
  69. return nil
  70. }))
  71. require.ElementsMatch(t, []blob.ID{"foo", "bar", "boo"}, got)
  72. errDummy := errors.New("dummy")
  73. require.ErrorIs(t, errDummy, blob.IterateAllPrefixesInParallel(ctx, 10, st, []blob.ID{
  74. "b",
  75. "c",
  76. }, func(m blob.Metadata) error {
  77. return errDummy
  78. }))
  79. }
  80. func TestEnsureLengthExactly(t *testing.T) {
  81. require.NoError(t, blob.EnsureLengthExactly(3, 3))
  82. require.NoError(t, blob.EnsureLengthExactly(3, -1))
  83. require.Error(t, blob.EnsureLengthExactly(3, 4))
  84. require.Error(t, blob.EnsureLengthExactly(3, 2))
  85. }
  86. func TestIDsFromMetadata(t *testing.T) {
  87. require.Equal(t,
  88. []blob.ID{"foo", "bar", "baz"},
  89. blob.IDsFromMetadata([]blob.Metadata{
  90. {BlobID: "foo", Length: 11},
  91. {BlobID: "bar", Length: 22},
  92. {BlobID: "baz", Length: 55},
  93. }))
  94. }
  95. func TestMaxTimestamp(t *testing.T) {
  96. t0 := time.Date(2020, 1, 2, 3, 4, 5, 6, time.UTC)
  97. t1 := t0.Add(1 * time.Hour)
  98. t2 := t0.Add(-1 * time.Hour)
  99. require.Equal(t,
  100. t1,
  101. blob.MaxTimestamp([]blob.Metadata{
  102. {BlobID: "foo", Timestamp: t0},
  103. {BlobID: "bar", Timestamp: t1},
  104. {BlobID: "baz", Timestamp: t2},
  105. }))
  106. require.Equal(t, time.Time{}, blob.MaxTimestamp([]blob.Metadata{}))
  107. }
  108. func TestMinTimestamp(t *testing.T) {
  109. t0 := time.Date(2020, 1, 2, 3, 4, 5, 6, time.UTC)
  110. t1 := t0.Add(1 * time.Hour)
  111. t2 := t0.Add(-1 * time.Hour)
  112. require.Equal(t,
  113. t2,
  114. blob.MinTimestamp([]blob.Metadata{
  115. {BlobID: "foo", Timestamp: t0},
  116. {BlobID: "bar", Timestamp: t1},
  117. {BlobID: "baz", Timestamp: t2},
  118. }))
  119. require.Equal(t, time.Time{}, blob.MinTimestamp([]blob.Metadata{}))
  120. }
  121. func TestTotalLength(t *testing.T) {
  122. require.Equal(t,
  123. int64(357),
  124. blob.TotalLength([]blob.Metadata{
  125. {BlobID: "foo", Length: 123},
  126. {BlobID: "bar", Length: 234},
  127. }))
  128. }
  129. func TestDeleteMultiple(t *testing.T) {
  130. data := blobtesting.DataMap{
  131. "foo": []byte{1, 2, 3},
  132. "bar": []byte{1, 2, 4},
  133. "baz": []byte{1, 2, 5},
  134. "qux": []byte{1, 2, 6},
  135. }
  136. st := blobtesting.NewMapStorage(data, nil, nil)
  137. require.NoError(t, blob.DeleteMultiple(context.Background(), st, []blob.ID{"bar", "qux"}, 4))
  138. require.Equal(t, blobtesting.DataMap{
  139. "foo": []byte{1, 2, 3},
  140. "baz": []byte{1, 2, 5},
  141. }, data)
  142. }
  143. func TestMetadataJSONString(t *testing.T) {
  144. bm := blob.Metadata{
  145. BlobID: "foo",
  146. Length: 12345,
  147. Timestamp: time.Date(2000, 1, 2, 3, 4, 5, 6, time.UTC),
  148. }
  149. require.JSONEq(t, `{"id":"foo","length":12345,"timestamp":"2000-01-02T03:04:05.000000006Z"}`, bm.String())
  150. }
  151. func TestPutBlobAndGetMetadata(t *testing.T) {
  152. data := blobtesting.DataMap{}
  153. fixedTime := time.Date(2000, 1, 2, 3, 4, 5, 6, time.UTC)
  154. st := blobtesting.NewMapStorage(data, nil, func() time.Time {
  155. return fixedTime
  156. })
  157. bm, err := blob.PutBlobAndGetMetadata(context.Background(), st, "foo", gather.FromSlice([]byte{1, 2, 3}), blob.PutOptions{})
  158. require.NoError(t, err)
  159. require.Equal(t, fixedTime, bm.Timestamp)
  160. }