package co.codewizards.cloudstore.local.transport;
import static co.codewizards.cloudstore.core.io.StreamUtil.*;
import static co.codewizards.cloudstore.core.oio.OioFileFactory.*;
import static org.assertj.core.api.Assertions.*;
import java.io.OutputStream;
import java.net.URL;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.UUID;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import co.codewizards.cloudstore.core.dto.ChangeSetDto;
import co.codewizards.cloudstore.core.dto.DeleteModificationDto;
import co.codewizards.cloudstore.core.dto.ModificationDto;
import co.codewizards.cloudstore.core.dto.RepoFileDto;
import co.codewizards.cloudstore.core.dto.RepoFileDtoTreeNode;
import co.codewizards.cloudstore.core.oio.File;
import co.codewizards.cloudstore.core.progress.LoggerProgressMonitor;
import co.codewizards.cloudstore.core.repo.local.LocalRepoManager;
import co.codewizards.cloudstore.core.repo.local.LocalRepoTransaction;
import co.codewizards.cloudstore.core.repo.transport.RepoTransport;
import co.codewizards.cloudstore.core.repo.transport.RepoTransportFactory;
import co.codewizards.cloudstore.core.repo.transport.RepoTransportFactoryRegistry;
import co.codewizards.cloudstore.local.AbstractTest;
import co.codewizards.cloudstore.local.persistence.LastSyncToRemoteRepo;
import co.codewizards.cloudstore.local.persistence.LastSyncToRemoteRepoDao;
import co.codewizards.cloudstore.local.persistence.LocalRepository;
import co.codewizards.cloudstore.local.persistence.LocalRepositoryDao;
import co.codewizards.cloudstore.local.persistence.RemoteRepository;
import co.codewizards.cloudstore.local.persistence.RemoteRepositoryDao;
public class FileRepoTransportTest extends AbstractTest {
private static final Logger logger = LoggerFactory.getLogger(FileRepoTransportTest.class);
private File remoteRoot;
private UUID remoteRepositoryId;
private File localRoot;
private UUID localRepositoryId;
private ChangeSetDto changeSetResponse1;
@Test
public void getChangeSetForEntireRepository() throws Exception {
remoteRoot = newTestRepositoryLocalRoot("remote");
assertThat(remoteRoot.exists()).isFalse();;
remoteRoot.mkdirs();
assertThat(remoteRoot.isDirectory()).isTrue();
final LocalRepoManager localRepoManager = localRepoManagerFactory.createLocalRepoManagerForNewRepository(remoteRoot);
assertThat(localRepoManager).isNotNull();
remoteRepositoryId = localRepoManager.getRepositoryId();
assertThat(remoteRepositoryId).isNotNull();
localRoot = newTestRepositoryLocalRoot("local");
assertThat(localRoot.exists()).isFalse();;
localRoot.mkdirs();
final LocalRepoManager toLocalRepoManager = localRepoManagerFactory.createLocalRepoManagerForNewRepository(localRoot);
localRepoManager.putRemoteRepository(toLocalRepoManager.getRepositoryId(), null, toLocalRepoManager.getPublicKey(), "");
toLocalRepoManager.putRemoteRepository(localRepoManager.getRepositoryId(), null, localRepoManager.getPublicKey(), "");
localRepositoryId = toLocalRepoManager.getRepositoryId();
toLocalRepoManager.close();
final File child_1 = createDirectory(remoteRoot, "1");
createFileWithRandomContent(child_1, "a");
createFileWithRandomContent(child_1, "b");
createFileWithRandomContent(child_1, "c");
final File child_2 = createDirectory(remoteRoot, "2");
createFileWithRandomContent(child_2, "a");
final File child_2_1 = createDirectory(child_2, "1");
createFileWithRandomContent(child_2_1, "a");
createFileWithRandomContent(child_2_1, "b");
final File child_3 = createDirectory(remoteRoot, "3");
createFileWithRandomContent(child_3, "a");
createFileWithRandomContent(child_3, "b");
createFileWithRandomContent(child_3, "c+");
createFileWithRandomContent(child_3, "d#");
localRepoManager.localSync(new LoggerProgressMonitor(logger));
assertThatFilesInRepoAreCorrect(remoteRoot);
final URL remoteRootURL = remoteRoot.toURI().toURL();
final RepoTransportFactory repoTransportFactory = RepoTransportFactoryRegistry.getInstance().getRepoTransportFactory(remoteRootURL);
final RepoTransport repoTransport = repoTransportFactory.createRepoTransport(remoteRootURL, localRepositoryId);
changeSetResponse1 = repoTransport.getChangeSetDto(false);
repoTransport.close();
assertThat(changeSetResponse1).isNotNull();
assertThat(changeSetResponse1.getRepoFileDtos()).isNotNull().isNotEmpty();
assertThat(changeSetResponse1.getRepositoryDto()).isNotNull();
assertThat(changeSetResponse1.getRepositoryDto().getRepositoryId()).isNotNull();
// changeSetResponse1 should contain the entire repository - including the root -, because really
// every localRevision must be > -1.
assertThat(changeSetResponse1.getRepoFileDtos()).hasSize(15);
final Set<String> paths = getPaths(changeSetResponse1.getRepoFileDtos());
assertThat(paths).containsOnly("/1/a", "/1/b", "/1/c", "/2/a", "/2/1/a", "/2/1/b", "/3/a", "/3/b", "/3/c+", "/3/d#");
final LocalRepoTransaction transaction = localRepoManager.beginWriteTransaction();
try {
final LocalRepositoryDao localRepositoryDao = transaction.getDao(LocalRepositoryDao.class);
final RemoteRepositoryDao remoteRepositoryDao = transaction.getDao(RemoteRepositoryDao.class);
final LastSyncToRemoteRepoDao lastSyncToRemoteRepoDao = transaction.getDao(LastSyncToRemoteRepoDao.class);
final LocalRepository localRepository = localRepositoryDao.getLocalRepositoryOrFail();
final RemoteRepository toRemoteRepository = remoteRepositoryDao.getRemoteRepositoryOrFail(localRepositoryId);
final LastSyncToRemoteRepo lastSyncToRemoteRepo = lastSyncToRemoteRepoDao.getLastSyncToRemoteRepoOrFail(toRemoteRepository);
lastSyncToRemoteRepo.setRemoteRepository(toRemoteRepository);
lastSyncToRemoteRepo.setLocalRepositoryRevisionSynced(localRepository.getRevision());
lastSyncToRemoteRepoDao.makePersistent(lastSyncToRemoteRepo);
transaction.commit();
} finally {
transaction.rollbackIfActive();
}
localRepoManager.close();
}
@Test
public void getChangeSetForAddedFile() throws Exception {
getChangeSetForEntireRepository();
final LocalRepoManager localRepoManager = localRepoManagerFactory.createLocalRepoManagerForExistingRepository(remoteRoot);
final File child_2 = createFile(remoteRoot, "2");
final File child_2_1 = createFile(child_2, "1");
createFileWithRandomContent(child_2_1, "c");
localRepoManager.localSync(new LoggerProgressMonitor(logger));
assertThatFilesInRepoAreCorrect(remoteRoot);
final URL remoteRootURL = remoteRoot.toURI().toURL();
final RepoTransportFactory repoTransportFactory = RepoTransportFactoryRegistry.getInstance().getRepoTransportFactory(remoteRootURL);
final RepoTransport repoTransport = repoTransportFactory.createRepoTransport(remoteRootURL, localRepositoryId);
final ChangeSetDto changeSetResponse2 = repoTransport.getChangeSetDto(false);
repoTransport.close();
assertThat(changeSetResponse2).isNotNull();
assertThat(changeSetResponse2.getRepoFileDtos()).isNotNull().isNotEmpty();
assertThat(changeSetResponse2.getRepositoryDto()).isNotNull();
assertThat(changeSetResponse2.getRepositoryDto().getRepositoryId()).isNotNull();
// We expect the added file and its direct parent, because they are both modified (new localRevision).
// Additionally, we expect all parent-directories (recursively) until (including) the root, because they
// are required to have a complete relative path for each modified RepoFile.
assertThat(changeSetResponse2.getRepoFileDtos()).hasSize(4);
final Set<String> paths = getPaths(changeSetResponse2.getRepoFileDtos());
assertThat(paths).hasSize(1);
assertThat(paths.iterator().next()).isEqualTo("/2/1/c");
localRepoManager.close();
}
@Test
public void getChangeSetForModifiedFile() throws Exception {
getChangeSetForEntireRepository();
final LocalRepoManager localRepoManager = localRepoManagerFactory.createLocalRepoManagerForExistingRepository(remoteRoot);
final File child_2 = createFile(remoteRoot, "2");
final File child_2_1 = createFile(child_2, "1");
final File child_2_1_b = createFile(child_2_1, "b");
final OutputStream out = castStream(child_2_1_b.createOutputStream(true));
out.write(4);
out.close();
localRepoManager.localSync(new LoggerProgressMonitor(logger));
assertThatFilesInRepoAreCorrect(remoteRoot);
final URL remoteRootURL = remoteRoot.toURI().toURL();
final RepoTransportFactory repoTransportFactory = RepoTransportFactoryRegistry.getInstance().getRepoTransportFactory(remoteRootURL);
final RepoTransport repoTransport = repoTransportFactory.createRepoTransport(remoteRootURL, localRepositoryId);
final ChangeSetDto changeSetResponse2 = repoTransport.getChangeSetDto(false);
repoTransport.close();
assertThat(changeSetResponse2).isNotNull();
assertThat(changeSetResponse2.getRepoFileDtos()).isNotNull().isNotEmpty();
assertThat(changeSetResponse2.getRepositoryDto()).isNotNull();
assertThat(changeSetResponse2.getRepositoryDto().getRepositoryId()).isNotNull();
// We expect the changed file and all parent-directories (recursively) until (including) the
// root, because they are required to have a complete relative path for each modified RepoFile.
assertThat(changeSetResponse2.getRepoFileDtos()).hasSize(4);
final Set<String> paths = getPaths(changeSetResponse2.getRepoFileDtos());
assertThat(paths).hasSize(1);
assertThat(paths.iterator().next()).isEqualTo("/2/1/b");
localRepoManager.close();
}
@Test
public void getChangeSetForDeletedFile() throws Exception {
getChangeSetForEntireRepository();
final LocalRepoManager localRepoManager = localRepoManagerFactory.createLocalRepoManagerForExistingRepository(remoteRoot);
final File child_2 = createFile(remoteRoot, "2");
final File child_2_1 = createFile(child_2, "1");
final File child_2_1_b = createFile(child_2_1, "b");
final long child_2_1LastModifiedBeforeModification = child_2_1.lastModified();
deleteFile(child_2_1_b);
// In GNU/Linux, the parent-directory's last-modified timestamp is changed, if a child is added or removed.
// To make sure, this has no influence on our test, we reset this timestamp after our change.
child_2_1.setLastModified(child_2_1LastModifiedBeforeModification);
localRepoManager.localSync(new LoggerProgressMonitor(logger));
assertThatFilesInRepoAreCorrect(remoteRoot);
final URL remoteRootURL = remoteRoot.toURI().toURL();
final RepoTransportFactory repoTransportFactory = RepoTransportFactoryRegistry.getInstance().getRepoTransportFactory(remoteRootURL);
final RepoTransport repoTransport = repoTransportFactory.createRepoTransport(remoteRootURL, localRepositoryId);
final ChangeSetDto changeSetResponse2 = repoTransport.getChangeSetDto(false);
repoTransport.close();
assertThat(changeSetResponse2).isNotNull();
assertThat(changeSetResponse2.getRepoFileDtos()).isNotNull().isEmpty();
assertThat(changeSetResponse2.getRepositoryDto()).isNotNull();
assertThat(changeSetResponse2.getRepositoryDto().getRepositoryId()).isNotNull();
// We expect the DeleteModificationDto
assertThat(changeSetResponse2.getModificationDtos()).hasSize(1);
final ModificationDto modificationDto = changeSetResponse2.getModificationDtos().get(0);
assertThat(modificationDto).isNotNull().isInstanceOf(DeleteModificationDto.class);
final DeleteModificationDto deleteModificationDto = (DeleteModificationDto) modificationDto;
assertThat(deleteModificationDto.getPath()).isEqualTo("/2/1/b");
localRepoManager.close();
}
private Set<String> getPaths(final Collection<RepoFileDto> repoFileDtos) {
assertThat(repoFileDtos).isNotNull().isNotEmpty();
final RepoFileDtoTreeNode rootNode = RepoFileDtoTreeNode.createTree(repoFileDtos);
assertThat(rootNode).isNotNull();
assertThat(rootNode.getRepoFileDto().getName()).isEqualTo("");
final List<RepoFileDtoTreeNode> leafs = rootNode.getLeafs();
final Set<String> paths = new HashSet<String>(leafs.size());
for (final RepoFileDtoTreeNode leaf : leafs) {
paths.add(leaf.getPath());
}
return paths;
}
}