github.com/ncw/rclone@v1.48.1-0.20190724201158-a35aa1360e3e/backend/googlephotos/pattern_test.go (about) 1 package googlephotos 2 3 import ( 4 "context" 5 "fmt" 6 "testing" 7 "time" 8 9 "github.com/ncw/rclone/backend/googlephotos/api" 10 "github.com/ncw/rclone/fs" 11 "github.com/ncw/rclone/fs/dirtree" 12 "github.com/ncw/rclone/fstest" 13 "github.com/ncw/rclone/fstest/mockobject" 14 "github.com/stretchr/testify/assert" 15 "github.com/stretchr/testify/require" 16 ) 17 18 // time for directories 19 var startTime = fstest.Time("2019-06-24T15:53:05.999999999Z") 20 21 // mock Fs for testing patterns 22 type testLister struct { 23 t *testing.T 24 albums *albums 25 names []string 26 uploaded dirtree.DirTree 27 } 28 29 // newTestLister makes a mock for testing 30 func newTestLister(t *testing.T) *testLister { 31 return &testLister{ 32 t: t, 33 albums: newAlbums(), 34 uploaded: dirtree.New(), 35 } 36 } 37 38 // mock listDir for testing 39 func (f *testLister) listDir(ctx context.Context, prefix string, filter api.SearchFilter) (entries fs.DirEntries, err error) { 40 for _, name := range f.names { 41 entries = append(entries, mockobject.New(prefix+name)) 42 } 43 return entries, nil 44 } 45 46 // mock listAlbums for testing 47 func (f *testLister) listAlbums(shared bool) (all *albums, err error) { 48 return f.albums, nil 49 } 50 51 // mock listUploads for testing 52 func (f *testLister) listUploads(ctx context.Context, dir string) (entries fs.DirEntries, err error) { 53 entries, _ = f.uploaded[dir] 54 return entries, nil 55 } 56 57 // mock dirTime for testing 58 func (f *testLister) dirTime() time.Time { 59 return startTime 60 } 61 62 func TestPatternMatch(t *testing.T) { 63 for testNumber, test := range []struct { 64 // input 65 root string 66 itemPath string 67 isFile bool 68 // expected output 69 wantMatch []string 70 wantPrefix string 71 wantPattern *dirPattern 72 }{ 73 { 74 root: "", 75 itemPath: "", 76 isFile: false, 77 wantMatch: []string{""}, 78 wantPrefix: "", 79 wantPattern: &patterns[0], 80 }, 81 { 82 root: "", 83 itemPath: "", 84 isFile: true, 85 wantMatch: nil, 86 wantPrefix: "", 87 wantPattern: nil, 88 }, 89 { 90 root: "upload", 91 itemPath: "", 92 isFile: false, 93 wantMatch: []string{"upload", ""}, 94 wantPrefix: "", 95 wantPattern: &patterns[1], 96 }, 97 { 98 root: "upload/dir", 99 itemPath: "", 100 isFile: false, 101 wantMatch: []string{"upload/dir", "dir"}, 102 wantPrefix: "", 103 wantPattern: &patterns[1], 104 }, 105 { 106 root: "upload/file.jpg", 107 itemPath: "", 108 isFile: true, 109 wantMatch: []string{"upload/file.jpg", "file.jpg"}, 110 wantPrefix: "", 111 wantPattern: &patterns[2], 112 }, 113 { 114 root: "media", 115 itemPath: "", 116 isFile: false, 117 wantMatch: []string{"media"}, 118 wantPrefix: "", 119 wantPattern: &patterns[3], 120 }, 121 { 122 root: "", 123 itemPath: "media", 124 isFile: false, 125 wantMatch: []string{"media"}, 126 wantPrefix: "media/", 127 wantPattern: &patterns[3], 128 }, 129 { 130 root: "media/all", 131 itemPath: "", 132 isFile: false, 133 wantMatch: []string{"media/all"}, 134 wantPrefix: "", 135 wantPattern: &patterns[4], 136 }, 137 { 138 root: "media", 139 itemPath: "all", 140 isFile: false, 141 wantMatch: []string{"media/all"}, 142 wantPrefix: "all/", 143 wantPattern: &patterns[4], 144 }, 145 { 146 root: "media/all", 147 itemPath: "file.jpg", 148 isFile: true, 149 wantMatch: []string{"media/all/file.jpg", "file.jpg"}, 150 wantPrefix: "file.jpg/", 151 wantPattern: &patterns[5], 152 }, 153 } { 154 t.Run(fmt.Sprintf("#%d,root=%q,itemPath=%q,isFile=%v", testNumber, test.root, test.itemPath, test.isFile), func(t *testing.T) { 155 gotMatch, gotPrefix, gotPattern := patterns.match(test.root, test.itemPath, test.isFile) 156 assert.Equal(t, test.wantMatch, gotMatch) 157 assert.Equal(t, test.wantPrefix, gotPrefix) 158 assert.Equal(t, test.wantPattern, gotPattern) 159 }) 160 } 161 } 162 163 func TestPatternMatchToEntries(t *testing.T) { 164 ctx := context.Background() 165 f := newTestLister(t) 166 f.names = []string{"file.jpg"} 167 f.albums.add(&api.Album{ 168 ID: "1", 169 Title: "sub/one", 170 }) 171 f.albums.add(&api.Album{ 172 ID: "2", 173 Title: "sub", 174 }) 175 f.uploaded.AddEntry(mockobject.New("upload/file1.jpg")) 176 f.uploaded.AddEntry(mockobject.New("upload/dir/file2.jpg")) 177 178 for testNumber, test := range []struct { 179 // input 180 root string 181 itemPath string 182 // expected output 183 wantMatch []string 184 wantPrefix string 185 remotes []string 186 }{ 187 { 188 root: "", 189 itemPath: "", 190 wantMatch: []string{""}, 191 wantPrefix: "", 192 remotes: []string{"media/", "album/", "shared-album/", "upload/"}, 193 }, 194 { 195 root: "upload", 196 itemPath: "", 197 wantMatch: []string{"upload", ""}, 198 wantPrefix: "", 199 remotes: []string{"upload/file1.jpg", "upload/dir/"}, 200 }, 201 { 202 root: "upload", 203 itemPath: "dir", 204 wantMatch: []string{"upload/dir", "dir"}, 205 wantPrefix: "dir/", 206 remotes: []string{"upload/dir/file2.jpg"}, 207 }, 208 { 209 root: "media", 210 itemPath: "", 211 wantMatch: []string{"media"}, 212 wantPrefix: "", 213 remotes: []string{"all/", "by-year/", "by-month/", "by-day/"}, 214 }, 215 { 216 root: "media/all", 217 itemPath: "", 218 wantMatch: []string{"media/all"}, 219 wantPrefix: "", 220 remotes: []string{"file.jpg"}, 221 }, 222 { 223 root: "media", 224 itemPath: "all", 225 wantMatch: []string{"media/all"}, 226 wantPrefix: "all/", 227 remotes: []string{"all/file.jpg"}, 228 }, 229 { 230 root: "media/by-year", 231 itemPath: "", 232 wantMatch: []string{"media/by-year"}, 233 wantPrefix: "", 234 remotes: []string{"2000/", "2001/", "2002/", "2003/"}, 235 }, 236 { 237 root: "media/by-year/2000", 238 itemPath: "", 239 wantMatch: []string{"media/by-year/2000", "2000"}, 240 wantPrefix: "", 241 remotes: []string{"file.jpg"}, 242 }, 243 { 244 root: "media/by-month", 245 itemPath: "", 246 wantMatch: []string{"media/by-month"}, 247 wantPrefix: "", 248 remotes: []string{"2000/", "2001/", "2002/", "2003/"}, 249 }, 250 { 251 root: "media/by-month/2001", 252 itemPath: "", 253 wantMatch: []string{"media/by-month/2001", "2001"}, 254 wantPrefix: "", 255 remotes: []string{"2001-01/", "2001-02/", "2001-03/", "2001-04/"}, 256 }, 257 { 258 root: "media/by-month/2001/2001-01", 259 itemPath: "", 260 wantMatch: []string{"media/by-month/2001/2001-01", "2001", "01"}, 261 wantPrefix: "", 262 remotes: []string{"file.jpg"}, 263 }, 264 { 265 root: "media/by-day", 266 itemPath: "", 267 wantMatch: []string{"media/by-day"}, 268 wantPrefix: "", 269 remotes: []string{"2000/", "2001/", "2002/", "2003/"}, 270 }, 271 { 272 root: "media/by-day/2001", 273 itemPath: "", 274 wantMatch: []string{"media/by-day/2001", "2001"}, 275 wantPrefix: "", 276 remotes: []string{"2001-01-01/", "2001-01-02/", "2001-01-03/", "2001-01-04/"}, 277 }, 278 { 279 root: "media/by-day/2001/2001-01-02", 280 itemPath: "", 281 wantMatch: []string{"media/by-day/2001/2001-01-02", "2001", "01", "02"}, 282 wantPrefix: "", 283 remotes: []string{"file.jpg"}, 284 }, 285 { 286 root: "album", 287 itemPath: "", 288 wantMatch: []string{"album"}, 289 wantPrefix: "", 290 remotes: []string{"sub/"}, 291 }, 292 { 293 root: "album/sub", 294 itemPath: "", 295 wantMatch: []string{"album/sub", "sub"}, 296 wantPrefix: "", 297 remotes: []string{"one/", "file.jpg"}, 298 }, 299 { 300 root: "album/sub/one", 301 itemPath: "", 302 wantMatch: []string{"album/sub/one", "sub/one"}, 303 wantPrefix: "", 304 remotes: []string{"file.jpg"}, 305 }, 306 { 307 root: "shared-album", 308 itemPath: "", 309 wantMatch: []string{"shared-album"}, 310 wantPrefix: "", 311 remotes: []string{"sub/"}, 312 }, 313 { 314 root: "shared-album/sub", 315 itemPath: "", 316 wantMatch: []string{"shared-album/sub", "sub"}, 317 wantPrefix: "", 318 remotes: []string{"one/", "file.jpg"}, 319 }, 320 { 321 root: "shared-album/sub/one", 322 itemPath: "", 323 wantMatch: []string{"shared-album/sub/one", "sub/one"}, 324 wantPrefix: "", 325 remotes: []string{"file.jpg"}, 326 }, 327 } { 328 t.Run(fmt.Sprintf("#%d,root=%q,itemPath=%q", testNumber, test.root, test.itemPath), func(t *testing.T) { 329 match, prefix, pattern := patterns.match(test.root, test.itemPath, false) 330 assert.Equal(t, test.wantMatch, match) 331 assert.Equal(t, test.wantPrefix, prefix) 332 assert.NotNil(t, pattern) 333 assert.NotNil(t, pattern.toEntries) 334 335 entries, err := pattern.toEntries(ctx, f, prefix, match) 336 assert.NoError(t, err) 337 var remotes = []string{} 338 for _, entry := range entries { 339 remote := entry.Remote() 340 if _, isDir := entry.(fs.Directory); isDir { 341 remote += "/" 342 } 343 remotes = append(remotes, remote) 344 if len(remotes) >= 4 { 345 break // only test first 4 entries 346 } 347 } 348 assert.Equal(t, test.remotes, remotes) 349 }) 350 } 351 } 352 353 func TestPatternYears(t *testing.T) { 354 f := newTestLister(t) 355 entries, err := years(context.Background(), f, "potato/", nil) 356 require.NoError(t, err) 357 358 year := 2000 359 for _, entry := range entries { 360 assert.Equal(t, "potato/"+fmt.Sprint(year), entry.Remote()) 361 year++ 362 } 363 } 364 365 func TestPatternMonths(t *testing.T) { 366 f := newTestLister(t) 367 entries, err := months(context.Background(), f, "potato/", []string{"", "2020"}) 368 require.NoError(t, err) 369 370 assert.Equal(t, 12, len(entries)) 371 for i, entry := range entries { 372 assert.Equal(t, fmt.Sprintf("potato/2020-%02d", i+1), entry.Remote()) 373 } 374 } 375 376 func TestPatternDays(t *testing.T) { 377 f := newTestLister(t) 378 entries, err := days(context.Background(), f, "potato/", []string{"", "2020"}) 379 require.NoError(t, err) 380 381 assert.Equal(t, 366, len(entries)) 382 assert.Equal(t, "potato/2020-01-01", entries[0].Remote()) 383 assert.Equal(t, "potato/2020-12-31", entries[len(entries)-1].Remote()) 384 } 385 386 func TestPatternYearMonthDayFilter(t *testing.T) { 387 ctx := context.Background() 388 f := newTestLister(t) 389 390 // Years 391 sf, err := yearMonthDayFilter(ctx, f, []string{"", "2000"}) 392 require.NoError(t, err) 393 assert.Equal(t, api.SearchFilter{ 394 Filters: &api.Filters{ 395 DateFilter: &api.DateFilter{ 396 Dates: []api.Date{ 397 { 398 Year: 2000, 399 }, 400 }, 401 }, 402 }, 403 }, sf) 404 405 _, err = yearMonthDayFilter(ctx, f, []string{"", "potato"}) 406 require.Error(t, err) 407 _, err = yearMonthDayFilter(ctx, f, []string{"", "999"}) 408 require.Error(t, err) 409 _, err = yearMonthDayFilter(ctx, f, []string{"", "4000"}) 410 require.Error(t, err) 411 412 // Months 413 sf, err = yearMonthDayFilter(ctx, f, []string{"", "2000", "01"}) 414 require.NoError(t, err) 415 assert.Equal(t, api.SearchFilter{ 416 Filters: &api.Filters{ 417 DateFilter: &api.DateFilter{ 418 Dates: []api.Date{ 419 { 420 Month: 1, 421 Year: 2000, 422 }, 423 }, 424 }, 425 }, 426 }, sf) 427 428 _, err = yearMonthDayFilter(ctx, f, []string{"", "2000", "potato"}) 429 require.Error(t, err) 430 _, err = yearMonthDayFilter(ctx, f, []string{"", "2000", "0"}) 431 require.Error(t, err) 432 _, err = yearMonthDayFilter(ctx, f, []string{"", "2000", "13"}) 433 require.Error(t, err) 434 435 // Days 436 sf, err = yearMonthDayFilter(ctx, f, []string{"", "2000", "01", "02"}) 437 require.NoError(t, err) 438 assert.Equal(t, api.SearchFilter{ 439 Filters: &api.Filters{ 440 DateFilter: &api.DateFilter{ 441 Dates: []api.Date{ 442 { 443 Day: 2, 444 Month: 1, 445 Year: 2000, 446 }, 447 }, 448 }, 449 }, 450 }, sf) 451 452 _, err = yearMonthDayFilter(ctx, f, []string{"", "2000", "01", "potato"}) 453 require.Error(t, err) 454 _, err = yearMonthDayFilter(ctx, f, []string{"", "2000", "01", "0"}) 455 require.Error(t, err) 456 _, err = yearMonthDayFilter(ctx, f, []string{"", "2000", "01", "32"}) 457 require.Error(t, err) 458 } 459 460 func TestPatternAlbumsToEntries(t *testing.T) { 461 f := newTestLister(t) 462 ctx := context.Background() 463 464 _, err := albumsToEntries(ctx, f, false, "potato/", "sub") 465 assert.Equal(t, fs.ErrorDirNotFound, err) 466 467 f.albums.add(&api.Album{ 468 ID: "1", 469 Title: "sub/one", 470 }) 471 472 entries, err := albumsToEntries(ctx, f, false, "potato/", "sub") 473 assert.NoError(t, err) 474 assert.Equal(t, 1, len(entries)) 475 assert.Equal(t, "potato/one", entries[0].Remote()) 476 _, ok := entries[0].(fs.Directory) 477 assert.Equal(t, true, ok) 478 479 f.albums.add(&api.Album{ 480 ID: "1", 481 Title: "sub", 482 }) 483 f.names = []string{"file.jpg"} 484 485 entries, err = albumsToEntries(ctx, f, false, "potato/", "sub") 486 assert.NoError(t, err) 487 assert.Equal(t, 2, len(entries)) 488 assert.Equal(t, "potato/one", entries[0].Remote()) 489 _, ok = entries[0].(fs.Directory) 490 assert.Equal(t, true, ok) 491 assert.Equal(t, "potato/file.jpg", entries[1].Remote()) 492 _, ok = entries[1].(fs.Object) 493 assert.Equal(t, true, ok) 494 495 }