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  }