github.com/treeverse/lakefs@v1.24.1-0.20240520134607-95648127bfb0/pkg/catalog/testutils/testutils.go (about) 1 package testutils 2 3 import ( 4 "bytes" 5 "context" 6 "errors" 7 "fmt" 8 "math/rand" 9 "net/url" 10 "sort" 11 "time" 12 13 "github.com/treeverse/lakefs/pkg/block" 14 "github.com/treeverse/lakefs/pkg/catalog" 15 "github.com/treeverse/lakefs/pkg/graveler" 16 "github.com/treeverse/lakefs/pkg/ingest/store" 17 "github.com/treeverse/lakefs/pkg/testutil" 18 ) 19 20 type FakeValueIterator struct { 21 Records []*graveler.ValueRecord 22 Error error 23 index int 24 } 25 26 func (f *FakeValueIterator) Next() bool { 27 if f.Error != nil { 28 return false 29 } 30 f.index++ 31 return f.index < len(f.Records) 32 } 33 34 func (f *FakeValueIterator) SeekGE(id graveler.Key) { 35 if f.Error != nil { 36 return 37 } 38 for i, ent := range f.Records { 39 if bytes.Compare(ent.Key, id) >= 0 { 40 f.index = i - 1 41 return 42 } 43 } 44 } 45 46 func (f *FakeValueIterator) Value() *graveler.ValueRecord { 47 if f.Error != nil || f.index < 0 || f.index >= len(f.Records) { 48 return nil 49 } 50 return f.Records[f.index] 51 } 52 53 func (f *FakeValueIterator) Err() error { 54 return f.Error 55 } 56 57 func (f *FakeValueIterator) Close() {} 58 59 func NewFakeValueIterator(records []*graveler.ValueRecord) *FakeValueIterator { 60 return &FakeValueIterator{ 61 Records: records, 62 Error: nil, 63 index: -1, 64 } 65 } 66 67 type FakeEntryIterator struct { 68 Entries []*catalog.EntryRecord 69 Error error 70 index int 71 } 72 73 func NewFakeEntryIterator(entries []*catalog.EntryRecord) *FakeEntryIterator { 74 return &FakeEntryIterator{ 75 Entries: entries, 76 index: -1, 77 } 78 } 79 80 func (f *FakeEntryIterator) Next() bool { 81 if f.Error != nil { 82 return false 83 } 84 f.index++ 85 return f.index < len(f.Entries) 86 } 87 88 func (f *FakeEntryIterator) SeekGE(id catalog.Path) { 89 if f.Error != nil { 90 return 91 } 92 for i, ent := range f.Entries { 93 if ent.Path >= id { 94 f.index = i - 1 95 return 96 } 97 } 98 } 99 100 func (f *FakeEntryIterator) Value() *catalog.EntryRecord { 101 if f.Error != nil || f.index < 0 || f.index >= len(f.Entries) { 102 return nil 103 } 104 return f.Entries[f.index] 105 } 106 107 func (f *FakeEntryIterator) Err() error { 108 return f.Error 109 } 110 111 func (f *FakeEntryIterator) Close() {} 112 113 type FakeFactory struct { 114 Walker *FakeWalker 115 } 116 117 func (f FakeFactory) GetWalker(_ context.Context, op store.WalkerOptions) (*store.WalkerWrapper, error) { 118 u, _ := url.Parse(op.StorageURI) 119 return store.NewWrapper(f.Walker, u), nil 120 } 121 122 func NewFakeWalker(count, max int, uriPrefix, expectedAfter, expectedContinuationToken, expectedFromSourceURIWithPrefix string, err error) *FakeWalker { 123 w := &FakeWalker{ 124 Max: max, 125 uriPrefix: uriPrefix, 126 expectedAfter: expectedAfter, 127 expectedContinuationToken: expectedContinuationToken, 128 expectedFromSourceURIWithPrefix: expectedFromSourceURIWithPrefix, 129 err: err, 130 } 131 w.createEntries(count) 132 return w 133 } 134 135 type FakeWalker struct { 136 Entries []block.ObjectStoreEntry 137 curr int 138 Max int 139 uriPrefix string 140 expectedAfter string 141 expectedContinuationToken string 142 expectedFromSourceURIWithPrefix string 143 err error 144 } 145 146 func (w *FakeWalker) GetSkippedEntries() []block.ObjectStoreEntry { 147 return nil 148 } 149 150 const ( 151 randomKeyLength = 64 152 entrySize = 132 153 ) 154 155 func (w *FakeWalker) createEntries(count int) { 156 ents := make([]block.ObjectStoreEntry, count) 157 158 // Use same sequence to overcome Graveler ability to create small ranges. 159 // Calling test functions rely on Graveler to not break on the first 1000 entries. 160 // For example, setting "5" here will cause the test to constantly fail. 161 // Fix Bug #3384 162 const seed = 6 163 //nolint:gosec 164 randGen := rand.New(rand.NewSource(seed)) 165 for i := 0; i < count; i++ { 166 relativeKey := testutil.RandomString(randGen, randomKeyLength) 167 fullkey := w.uriPrefix + "/" + relativeKey 168 ents[i] = block.ObjectStoreEntry{ 169 RelativeKey: relativeKey, 170 FullKey: fullkey, 171 Address: w.expectedFromSourceURIWithPrefix + "/" + relativeKey, 172 ETag: "some_etag", 173 Mtime: time.Time{}, 174 Size: entrySize, 175 } 176 } 177 sort.Slice(ents, func(i, j int) bool { 178 return ents[i].RelativeKey < ents[j].RelativeKey 179 }) 180 w.Entries = ents 181 } 182 183 var errUnexpectedValue = errors.New("unexpected value") 184 185 func (w *FakeWalker) Walk(_ context.Context, storageURI *url.URL, op block.WalkOptions, walkFn func(e block.ObjectStoreEntry) error) error { 186 if w.expectedAfter != op.After { 187 return fmt.Errorf("(after) expected %s, got %s: %w", w.expectedAfter, op.After, errUnexpectedValue) 188 } 189 if w.expectedContinuationToken != op.ContinuationToken { 190 return fmt.Errorf("(continuationToken) expected %s, got %s: %w", w.expectedContinuationToken, op.ContinuationToken, errUnexpectedValue) 191 } 192 if w.expectedFromSourceURIWithPrefix != storageURI.String() { 193 return fmt.Errorf("(fromSourceURIWithPrefix) expected %s, got %s: %w", w.expectedFromSourceURIWithPrefix, storageURI.String(), errUnexpectedValue) 194 } 195 if w.err != nil { 196 return w.err 197 } 198 199 for i, e := range w.Entries { 200 w.curr = i 201 if err := walkFn(e); err != nil { 202 if i < w.Max { 203 return fmt.Errorf("didn't expect walk err: %w", err) 204 } 205 if i >= w.Max && !errors.Is(err, catalog.ErrItClosed) { 206 return fmt.Errorf("expected error; expected (%s), got: %w", catalog.ErrItClosed, err) 207 } 208 } 209 } 210 return nil 211 } 212 213 const ContinuationTokenOpaque = "FakeContToken" 214 215 func (w *FakeWalker) Marker() block.Mark { 216 token := "" 217 if w.curr < len(w.Entries)-1 { 218 token = ContinuationTokenOpaque 219 } 220 return block.Mark{ 221 LastKey: w.Entries[w.curr].FullKey, 222 HasMore: w.curr < len(w.Entries)-1, 223 ContinuationToken: token, 224 } 225 }