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 }