77import ch .cyberduck .core .*;
88import ch .cyberduck .core .exception .BackgroundException ;
99import ch .cyberduck .core .features .Bulk ;
10+ import ch .cyberduck .core .features .Delete ;
1011import ch .cyberduck .core .features .Directory ;
1112import ch .cyberduck .core .features .Home ;
13+ import ch .cyberduck .core .features .Move ;
1214import ch .cyberduck .core .features .Read ;
1315import ch .cyberduck .core .features .Vault ;
1416import ch .cyberduck .core .features .Write ;
3638
3739import java .io .ByteArrayInputStream ;
3840import java .io .IOException ;
41+ import java .io .InputStream ;
3942import java .util .Arrays ;
4043import java .util .Collections ;
4144import java .util .EnumSet ;
@@ -71,8 +74,6 @@ public abstract class AbstractHubSynchronizeTest extends AbstractHubTest {
7174
7275 /**
7376 * Start with unattended setup (e.g. UnattendedMinio) and then run tests with corresponding attended setup (e.g. AttendedMinio) to save startup times at every test execution.
74- *
75- * @throws InterruptedException
7677 */
7778 @ Test
7879 @ Disabled
@@ -279,9 +280,11 @@ public void test03AddVault(final HubTestConfig config) throws Exception {
279280 assertEquals (file .getName (), list .get (0 ).getName ());
280281
281282 byte [] actual = new byte [300 ];
282- int l = session .getFeature (Read .class ).read (file , new TransferStatus (), new DisabledConnectionCallback ()).read (actual );
283- assert l == 234 ;
284- assertArrayEquals (content , Arrays .copyOfRange (actual , 0 , l ));
283+ try (final InputStream inputStream = session .getFeature (Read .class ).read (file , new TransferStatus (), new DisabledConnectionCallback ())) {
284+ int l = inputStream .read (actual );
285+ assert l == 234 ;
286+ assertArrayEquals (content , Arrays .copyOfRange (actual , 0 , l ));
287+ }
285288 }
286289 {
287290 // encrypted directory creation and listing
@@ -290,7 +293,7 @@ public void test03AddVault(final HubTestConfig config) throws Exception {
290293
291294 session .getFeature (Directory .class ).mkdir (folder , new TransferStatus ());
292295 final AttributedList <Path > list = session .getFeature (ListService .class ).list (bucket , new DisabledListProgressListener ());
293- assertEquals (2 , list .size ());
296+ assertEquals (2 , list .size ()); // a file and a folder
294297
295298 {
296299 // encrypted file upload in subfolder
@@ -301,22 +304,77 @@ public void test03AddVault(final HubTestConfig config) throws Exception {
301304 assertEquals (file .getName (), sublist .get (0 ).getName ());
302305
303306 byte [] actual = new byte [600 ];
304- int l = session .getFeature (Read .class ).read (file , new TransferStatus (), new DisabledConnectionCallback ()).read (actual );
305- assert l == 555 ;
306- assertArrayEquals (content , Arrays .copyOfRange (actual , 0 , l ));
307+ try (final InputStream inputStream = session .getFeature (Read .class ).read (file , new TransferStatus (), new DisabledConnectionCallback ())) {
308+ int l = inputStream .read (actual );
309+ assert l == 555 ;
310+ assertArrayEquals (content , Arrays .copyOfRange (actual , 0 , l ));
311+ }
312+
313+ // move operation to root folder and read again
314+ session .getFeature (Move .class ).move (file , new Path (home , file .getName (), EnumSet .of (AbstractPath .Type .file )), new TransferStatus (), new Delete .DisabledCallback (), new DisabledConnectionCallback ());
315+
316+ final AttributedList <Path > list2 = session .getFeature (ListService .class ).list (home , new DisabledListProgressListener ());
317+ assertEquals (3 , list2 .size ()); // 1 subfolder and 2 files
318+
319+ assertEquals (1 , list2 .toStream ().map (Path ::isDirectory ).filter (Boolean ::booleanValue ).count ());
320+ assertEquals (2 , list2 .toStream ().map (Path ::isFile ).filter (Boolean ::booleanValue ).count ());
307321 }
308322 }
309323 {
310324 // raw listing encrypted file names
325+ // aka. ciphertext directory structure
326+ // see https://github.com/encryption-alliance/unified-vault-format/blob/develop/file%20name%20encryption/AES-SIV-512-B64URL.md#ciphertext-directory-structure
311327 vaultRegistry .close (bucket );
312328 assertSame (Vault .DISABLED , vaultRegistry .find (session , bucket ));
313329 assertTrue (vaultRegistry .isEmpty ());
314330
315- final AttributedList <Path > list = session .getFeature (ListService .class ).list (bucket , new DisabledListProgressListener ());
316- assertFalse (list .isEmpty ());
317- assertEquals (2 , list .size ());
318- assertNotNull (list .find (new SimplePathPredicate (new Path (bucket , "d" , EnumSet .of (Path .Type .directory , AbstractPath .Type .placeholder )))));
319- assertNotNull (list .find (new SimplePathPredicate (new Path (bucket , PreferencesFactory .get ().getProperty ("cryptomator.vault.config.filename" ), EnumSet .of (Path .Type .file )))));
331+ {
332+ final AttributedList <Path > list = session .getFeature (ListService .class ).list (bucket , new DisabledListProgressListener ());
333+ assertFalse (list .isEmpty ());
334+ assertEquals (2 , list .size ());
335+ // /<bucket>/d/
336+ assertNotNull (list .find (new SimplePathPredicate (new Path (bucket , "d" , EnumSet .of (Path .Type .directory , AbstractPath .Type .placeholder )))));
337+ // /<bucket>/vault.uvf
338+ assertNotNull (list .find (new SimplePathPredicate (new Path (bucket , PreferencesFactory .get ().getProperty ("cryptomator.vault.config.filename" ), EnumSet .of (Path .Type .file )))));
339+ }
340+ {
341+ // level 2: /<bucket>/d/
342+ final AttributedList <Path > level2List = session .getFeature (ListService .class ).list (new Path (bucket , "d" , EnumSet .of (Path .Type .directory , AbstractPath .Type .placeholder )), new DisabledListProgressListener ());
343+ assertFalse (level2List .isEmpty ());
344+ assertEquals (2 , level2List .size ());
345+ for (final Path level3 : level2List ) {
346+ // level 3: /<bucket>/d/<2-letter-folder>/
347+ final AttributedList <Path > level3List = session .getFeature (ListService .class ).list (level3 , new DisabledListProgressListener ());
348+ // by hashing, only 1 sub-folder expected
349+ assertEquals (1 , level3List .size ());
350+ for (final Path level4 : level3List ) {
351+ // level 4: /<bucket>/d/<2-letter-folder>/<30-letter-folder/
352+ final AttributedList <Path > level4List = session .getFeature (ListService .class ).list (level4 , new DisabledListProgressListener ());
353+ assertTrue (level4List .toStream ().map (Path ::getName ).allMatch (n -> n .endsWith (".uvf" )));
354+ // empty sub-folder
355+ log .info ("level4List.size()={}" , level4List .size ());
356+ assert (level4List .size () >= 2 );
357+ // root folder contains two files and a sub-folder
358+ assertTrue (level4List .size () <= 3 );
359+ if (level4List .size () == 2 ) {
360+ // MiniO versioned API returns a first version with the file content and a second empty version upon deletion
361+ assertTrue (level4List .toStream ().allMatch (p -> p .attributes ().isDuplicate ()));
362+ }
363+ else if (level4List .size () == 3 ) {
364+ // the root directory -> contains two files...
365+ assertEquals (2 , level4List .toStream ().map (p -> p .isFile () && p .getName ().endsWith (".uvf" )).filter (Boolean ::booleanValue ).count ());
366+ assertEquals (1 , level4List .toStream ().map (p -> p .isDirectory () && p .getName ().endsWith (".uvf" )).filter (Boolean ::booleanValue ).count ());
367+ // ... and a subfolder with a dir.uvf in it
368+ final Path level5 = level4List .toStream ().filter (Path ::isDirectory ).findFirst ().get ();
369+ final AttributedList <Path > level5list = session .getFeature (ListService .class ).list (level5 , new DisabledListProgressListener ());
370+ assertEquals (1 , level5list .size ());
371+ final Path level6 = level5list .get (0 );
372+ assertEquals ("dir.uvf" , level6 .getName ());
373+ assertTrue (level6 .isFile ());
374+ }
375+ }
376+ }
377+ }
320378 }
321379 }
322380 finally {
0 commit comments