github.com/cozy/cozy-stack@v0.0.0-20240603063001-31110fa4cae1/model/sharing/files_test.go (about)

     1  package sharing
     2  
     3  import (
     4  	"strings"
     5  	"testing"
     6  	"time"
     7  
     8  	"github.com/cozy/cozy-stack/model/vfs"
     9  	"github.com/cozy/cozy-stack/pkg/config/config"
    10  	"github.com/cozy/cozy-stack/pkg/consts"
    11  	"github.com/cozy/cozy-stack/pkg/couchdb"
    12  	"github.com/cozy/cozy-stack/tests/testutils"
    13  	"github.com/stretchr/testify/assert"
    14  	"github.com/stretchr/testify/require"
    15  )
    16  
    17  func TestFiles(t *testing.T) {
    18  	if testing.Short() {
    19  		t.Skip("an instance is required for this test: test skipped due to the use of --short flag")
    20  	}
    21  
    22  	config.UseTestFile(t)
    23  	testutils.NeedCouchdb(t)
    24  	setup := testutils.NewSetup(t, t.Name())
    25  	inst := setup.GetTestInstance()
    26  
    27  	t.Run("MakeXorKey", func(t *testing.T) {
    28  		key := MakeXorKey()
    29  		assert.Len(t, key, 16)
    30  		for _, k := range key {
    31  			assert.True(t, k < 16)
    32  		}
    33  	})
    34  
    35  	t.Run("XorID", func(t *testing.T) {
    36  		id := "12345678-abcd-90ef-1337-cafebee54321"
    37  		assert.Equal(t, id, XorID(id, []byte{0}))
    38  		assert.Equal(t, id, XorID(id, []byte{0, 0, 0, 0}))
    39  
    40  		key := MakeXorKey()
    41  		xored := XorID(id, key)
    42  		assert.NotEqual(t, id, xored)
    43  		assert.Equal(t, id, XorID(xored, key))
    44  		for _, c := range xored {
    45  			switch {
    46  			case '0' <= c && c <= '9':
    47  			case 'a' <= c && c <= 'f':
    48  			default:
    49  				assert.Equal(t, '-', c)
    50  			}
    51  		}
    52  
    53  		expected := "03254769-badc-81fe-0226-dbefaff45230"
    54  		assert.Equal(t, expected, XorID(id, []byte{1}))
    55  
    56  		expected = "133b5777-bb3d-9fee-e327-cbf1bfea422e"
    57  		assert.Equal(t, expected, XorID(id, []byte{0, 1, 0, 15}))
    58  	})
    59  
    60  	t.Run("SortFilesToSent", func(t *testing.T) {
    61  		s := &Sharing{}
    62  		foo := map[string]interface{}{"type": "directory", "name": "foo", "path": "/foo"}
    63  		foobar := map[string]interface{}{"type": "directory", "name": "bar", "path": "/foo/bar"}
    64  		foobarbaz := map[string]interface{}{"type": "directory", "name": "baz", "path": "/foo/bar/baz"}
    65  		dela := map[string]interface{}{"_deleted": true, "_id": "dela"} // No type, name, or path on deleted docs
    66  		delb := map[string]interface{}{"_deleted": true, "_id": "delb"}
    67  		filea := map[string]interface{}{"type": "file", "name": "filea"}
    68  		fileb := map[string]interface{}{"type": "file", "name": "fileb"}
    69  		filec := map[string]interface{}{"type": "file", "name": "filec"}
    70  		files := []map[string]interface{}{filea, foobar, foobarbaz, dela, delb, fileb, filec, foo}
    71  		s.SortFilesToSent(files)
    72  		expected := []map[string]interface{}{foo, foobar, foobarbaz, filea, fileb, filec, dela, delb}
    73  		assert.Equal(t, expected, files)
    74  	})
    75  
    76  	t.Run("SharingDir", func(t *testing.T) {
    77  		s := Sharing{
    78  			SID: uuidv7(),
    79  			Rules: []Rule{
    80  				{
    81  					Title:   "Test sharing dir",
    82  					DocType: consts.Files,
    83  					Values:  []string{uuidv7()},
    84  				},
    85  			},
    86  		}
    87  		d1, err := s.CreateDirForSharing(inst, &s.Rules[0], "")
    88  		assert.NoError(t, err)
    89  
    90  		d2, err := s.GetSharingDir(inst)
    91  		assert.NoError(t, err)
    92  		if assert.NotNil(t, d2) {
    93  			assert.Equal(t, d1.DocID, d2.DocID)
    94  			assert.Equal(t, "Test sharing dir", d2.DocName)
    95  			assert.Equal(t, "/Tree Shared with me/Test sharing dir", d2.Fullpath)
    96  			assert.Len(t, d2.ReferencedBy, 1)
    97  			assert.Equal(t, consts.Sharings, d2.ReferencedBy[0].Type)
    98  			assert.Equal(t, s.SID, d2.ReferencedBy[0].ID)
    99  		}
   100  
   101  		err = s.RemoveSharingDir(inst)
   102  		assert.NoError(t, err)
   103  
   104  		key := []string{consts.Sharings, s.SID}
   105  		end := []string{key[0], key[1], couchdb.MaxString}
   106  		req := &couchdb.ViewRequest{
   107  			StartKey:    key,
   108  			EndKey:      end,
   109  			IncludeDocs: true,
   110  		}
   111  		var res couchdb.ViewResponse
   112  		err = couchdb.ExecView(inst, couchdb.FilesReferencedByView, req, &res)
   113  		assert.NoError(t, err)
   114  		assert.Len(t, res.Rows, 0)
   115  	})
   116  
   117  	t.Run("CreateDir", func(t *testing.T) {
   118  		s := Sharing{
   119  			SID: uuidv7(),
   120  			Rules: []Rule{
   121  				{
   122  					Title:   "Test create dir",
   123  					DocType: consts.Files,
   124  					Values:  []string{uuidv7()},
   125  				},
   126  			},
   127  		}
   128  
   129  		idFoo := uuidv7()
   130  		target := map[string]interface{}{
   131  			"_id":  idFoo,
   132  			"_rev": "1-6b501ca58928b02b90c430fd730e8b17",
   133  			"_revisions": map[string]interface{}{
   134  				"start": float64(1),
   135  				"ids": []interface{}{
   136  					"6b501ca58928b02b90c430fd730e8b17",
   137  				},
   138  			},
   139  			"name": "Foo",
   140  		}
   141  		assert.NoError(t, s.CreateDir(inst, target, resolveResolution))
   142  		dir, err := inst.VFS().DirByID(idFoo)
   143  		assert.NoError(t, err)
   144  		if assert.NotNil(t, dir) {
   145  			assert.Equal(t, idFoo, dir.DocID)
   146  			assert.Equal(t, target["_rev"], dir.DocRev)
   147  			assert.Equal(t, "Foo", dir.DocName)
   148  			assert.Equal(t, "/Tree Shared with me/Test create dir/Foo", dir.Fullpath)
   149  		}
   150  
   151  		idBar := uuidv7()
   152  		target = map[string]interface{}{
   153  			"_id":  idBar,
   154  			"_rev": "4-2ee767305024673cfb3f5af037cd2729",
   155  			"_revisions": map[string]interface{}{
   156  				"start": float64(4),
   157  				"ids": []interface{}{
   158  					"2ee767305024673cfb3f5af037cd2729",
   159  					"753875d51501a6b1883a9d62b4d33f91",
   160  				},
   161  			},
   162  			"dir_id":     idFoo,
   163  			"name":       "Bar",
   164  			"created_at": "2018-04-13T15:06:00.012345678+01:00",
   165  			"updated_at": "2018-04-13T15:08:32.581420274+01:00",
   166  			"tags":       []interface{}{"qux", "courge"},
   167  		}
   168  		assert.NoError(t, s.CreateDir(inst, target, resolveResolution))
   169  		dir, err = inst.VFS().DirByID(idBar)
   170  		assert.NoError(t, err)
   171  		if assert.NotNil(t, dir) {
   172  			assert.Equal(t, idBar, dir.DocID)
   173  			assert.Equal(t, target["_rev"], dir.DocRev)
   174  			assert.Equal(t, "Bar", dir.DocName)
   175  			assert.Equal(t, "/Tree Shared with me/Test create dir/Foo/Bar", dir.Fullpath)
   176  			assert.Equal(t, "2018-04-13 15:06:00.012345678 +0100 +0100", dir.CreatedAt.String())
   177  			assert.Equal(t, "2018-04-13 15:08:32.581420274 +0100 +0100", dir.UpdatedAt.String())
   178  			assert.Equal(t, []string{"qux", "courge"}, dir.Tags)
   179  		}
   180  	})
   181  
   182  	t.Run("UpdateDir", func(t *testing.T) {
   183  		s := Sharing{
   184  			SID: uuidv7(),
   185  			Rules: []Rule{
   186  				{
   187  					Title:   "Test update dir",
   188  					DocType: consts.Files,
   189  					Values:  []string{uuidv7()},
   190  				},
   191  			},
   192  		}
   193  
   194  		idFoo := uuidv7()
   195  		target := map[string]interface{}{
   196  			"_id":  idFoo,
   197  			"_rev": "1-4fff5291a41bf1f493460d2070694c5a",
   198  			"_revisions": map[string]interface{}{
   199  				"start": float64(1),
   200  				"ids": []interface{}{
   201  					"4fff5291a41bf1f493460d2070694c5a",
   202  				},
   203  			},
   204  			"name":       "Foo",
   205  			"created_at": "2018-04-13T15:06:00.012345678+01:00",
   206  			"updated_at": "2018-04-13T15:08:32.581420274+01:00",
   207  			"tags":       []interface{}{"qux", "courge"},
   208  		}
   209  		assert.NoError(t, s.CreateDir(inst, target, resolveResolution))
   210  		dir, err := inst.VFS().DirByID(idFoo)
   211  		assert.NoError(t, err)
   212  		if assert.NotNil(t, dir) {
   213  			assert.Equal(t, idFoo, dir.DocID)
   214  			assert.Equal(t, target["_rev"], dir.DocRev)
   215  			assert.Equal(t, "Foo", dir.DocName)
   216  			assert.Equal(t, "/Tree Shared with me/Test update dir/Foo", dir.Fullpath)
   217  		}
   218  
   219  		// Exclude dir from synchronization on a Desktop client
   220  		dir.NotSynchronizedOn = append(dir.NotSynchronizedOn,
   221  			couchdb.DocReference{ID: "ea24f891a41bf1f433460d20706d22c9", Type: "io.cozy.oauth.clients"},
   222  		)
   223  		err = couchdb.UpdateDoc(inst, dir)
   224  		require.NoError(t, err)
   225  		newTargetRev := strings.SplitN(dir.Rev(), "-", 2)[1]
   226  
   227  		target = map[string]interface{}{
   228  			"_id":  idFoo,
   229  			"_rev": "3-96c72d35f3ad802484a61df501b0f1bb",
   230  			"_revisions": map[string]interface{}{
   231  				"start": float64(3),
   232  				"ids": []interface{}{
   233  					"96c72d35f3ad802484a61df501b0f1bb",
   234  					newTargetRev,
   235  					"4fff5291a41bf1f493460d2070694c5a",
   236  				},
   237  			},
   238  			"name":       "Foo",
   239  			"created_at": "2018-04-13T15:06:00.012345678+01:00",
   240  			"updated_at": "2018-04-13T15:10:57.364765745+01:00",
   241  			"tags":       []interface{}{"quux", "courge"},
   242  		}
   243  		var ref SharedRef
   244  		err = couchdb.GetDoc(inst, consts.Shared, consts.Files+"/"+idFoo, &ref)
   245  		assert.NoError(t, err)
   246  		assert.NoError(t, s.UpdateDir(inst, target, dir, &ref, resolveResolution))
   247  		dir, err = inst.VFS().DirByID(idFoo)
   248  		assert.NoError(t, err)
   249  		if assert.NotNil(t, dir) {
   250  			assert.Equal(t, idFoo, dir.DocID)
   251  			assert.Equal(t, target["_rev"], dir.DocRev)
   252  			assert.Equal(t, "Foo", dir.DocName)
   253  			assert.Equal(t, "/Tree Shared with me/Test update dir/Foo", dir.Fullpath)
   254  			assert.Equal(t, "2018-04-13 15:06:00.012345678 +0100 +0100", dir.CreatedAt.String())
   255  			assert.Equal(t, "2018-04-13 15:10:57.364765745 +0100 +0100", dir.UpdatedAt.String())
   256  			assert.Equal(t, []string{"quux", "courge"}, dir.Tags)
   257  			assert.Equal(t, []couchdb.DocReference{{ID: "ea24f891a41bf1f433460d20706d22c9", Type: "io.cozy.oauth.clients"}}, dir.NotSynchronizedOn)
   258  		}
   259  	})
   260  
   261  	t.Run("countFiles", func(t *testing.T) {
   262  		tree := H{
   263  			"test-countFiles/": H{
   264  				"dir1/": H{
   265  					"subdir1/": H{
   266  						"file": nil, // 1
   267  					},
   268  					"subdir2/": H{
   269  						"subsubdir/": H{
   270  							"file": nil, // 2
   271  						},
   272  					},
   273  				},
   274  				"dir2/": H{
   275  					"foo": nil, // 3
   276  					"bar": nil, // 4
   277  					"baz": nil, // 5
   278  				},
   279  				"dir3/": H{
   280  					"courge": nil, // 6
   281  				},
   282  				"qux":  nil, // 7
   283  				"quux": nil, // 8
   284  			},
   285  		}
   286  		dir := createTree(t, inst.VFS(), tree, consts.RootDirID)
   287  
   288  		s := Sharing{
   289  			SID: uuidv7(),
   290  			Rules: []Rule{
   291  				{
   292  					Title:   "Test countFiles",
   293  					DocType: consts.Files,
   294  					Values:  []string{dir.ID()},
   295  				},
   296  			},
   297  		}
   298  		assert.Equal(t, 8, s.countFiles(inst))
   299  	})
   300  }
   301  
   302  type H map[string]H
   303  
   304  func createTree(t *testing.T, fs vfs.VFS, tree H, dirID string) *vfs.DirDoc {
   305  	t.Helper()
   306  
   307  	var err error
   308  	var dirdoc *vfs.DirDoc
   309  	for name, children := range tree {
   310  		if name[len(name)-1] == '/' {
   311  			dirdoc, err = vfs.NewDirDoc(fs, name[:len(name)-1], dirID, nil)
   312  			require.NoError(t, err)
   313  
   314  			err = fs.CreateDir(dirdoc)
   315  			require.NoError(t, err)
   316  
   317  			createTree(t, fs, children, dirdoc.ID())
   318  		} else {
   319  			mime, class := vfs.ExtractMimeAndClassFromFilename(name)
   320  			filedoc, err := vfs.NewFileDoc(name, dirID, -1, nil, mime, class, time.Now(), false, false, false, nil)
   321  			require.NoError(t, err)
   322  
   323  			f, err := fs.CreateFile(filedoc, nil)
   324  			require.NoError(t, err)
   325  
   326  			err = f.Close()
   327  			require.NoError(t, err)
   328  		}
   329  	}
   330  	return dirdoc
   331  }