package cz.cuni.mff.d3s.been.swrepository; import java.io.*; import java.net.InetAddress; import java.net.InetSocketAddress; import java.net.ServerSocket; import java.net.UnknownHostException; import java.nio.file.FileSystems; import java.nio.file.Path; import java.util.*; import org.apache.commons.io.IOUtils; import org.apache.maven.artifact.Artifact; import org.codehaus.plexus.util.FileUtils; import org.junit.*; import org.junit.rules.TestRule; import org.junit.runner.Description; import org.junit.runners.model.Statement; import cz.cuni.mff.d3s.been.bpk.ArtifactIdentifier; import cz.cuni.mff.d3s.been.bpk.Bpk; import cz.cuni.mff.d3s.been.bpk.BpkIdentifier; import cz.cuni.mff.d3s.been.datastore.SoftwareStore; import cz.cuni.mff.d3s.been.swrepoclient.SwRepoClient; import cz.cuni.mff.d3s.been.swrepoclient.SwRepoClientFactory; import cz.cuni.mff.d3s.been.swrepoclient.SwRepositoryClientException; import cz.cuni.mff.d3s.been.swrepository.httpserver.HttpServer; import cz.cuni.mff.d3s.been.swrepository.httpserver.HttpServerException; /** * A simulation of actual software repository use-cases. Consists in running a * {@link SoftwareRepository} server and launching clients against it, testing * whether the right responses and files are obtained. * * @author darklight * */ public class TestSoftwareRepositoryTransportByHTTP extends Assert { private class RunningServerStatement extends Statement { private final Statement base; private final ServerAllocatorRule rule; private HttpServer server = null; RunningServerStatement(Statement base, ServerAllocatorRule rule) { this.base = base; this.rule = rule; } @Override public void evaluate() throws Throwable { startServer(); base.evaluate(); stopServer(); } private void startServer() throws IOException, HttpServerException { // find a random free socket ServerSocket probeSocket = new ServerSocket(0); final int port = probeSocket.getLocalPort(); final InetAddress addr = probeSocket.getInetAddress(); probeSocket.close(); server = new HttpServer(new InetSocketAddress(addr, port)); SoftwareStore dataStore = FSBasedStore.createServer(new Properties()); server.getResolver().register("/bpk*", new BpkRequestHandler(dataStore)); server.getResolver().register("/artifact*", new ArtifactRequestHandler(dataStore)); server.start(); rule.host = server.getHost().getHostName(); rule.port = server.getPort(); } private void stopServer() { server.stop(); server = null; } } private class ServerAllocatorRule implements TestRule { private String host; private int port; public String getHost() { return host; } public int getPort() { return port; } @Override public Statement apply(Statement base, Description description) { if (description.getMethodName() != null && description.getMethodName().endsWith("_serverDown")) { host = "localhost"; port = 0; // markup for random free port allocation return base; } else { return new RunningServerStatement(base, this); } } } /** Root folder of SWRepo's persistence. */ private static final File SERVER_PERSISTENCE_ROOT_FOLDER = new File(".swrepository"); /** Root folder of the client's persistence. */ private static final File CLIENT_PERSISTENCE_ROOT_FOLDER = new File(".swcache"); /** Software package store */ private final SoftwareStore dataStore; /** software repository client factory */ private final SwRepoClientFactory clientFactory; public TestSoftwareRepositoryTransportByHTTP() { dataStore = FSBasedStore.createCache(new Properties()); clientFactory = new SwRepoClientFactory(dataStore); } private SwRepoClient client = null; private File randomContentFile = null; private BpkIdentifier bpkId = null; private ArtifactIdentifier artifactId = null; @Rule public ServerAllocatorRule serverAllocatorRule = new ServerAllocatorRule(); /** * Fill test fields. * * @throws IOException * On failure when creating test files */ @Before public void fillFields() throws IOException { randomContentFile = File.createTempFile("testSwRepoTraffic", "randomContent"); FileWriter fw = new FileWriter(randomContentFile); Random random = new Random(System.currentTimeMillis()); for (int i = 0; i < 1024; ++i) { fw.write(random.nextInt()); } fw.flush(); fw.close(); bpkId = new BpkIdentifier(); bpkId.setBpkId("evil-package"); bpkId.setGroupId("cz.cuni.mff.d3s.been.swrepository.test"); bpkId.setVersion("0.0.7"); artifactId = new ArtifactIdentifier(); artifactId.setArtifactId("bigBaddaBoom"); artifactId.setGroupId("cz.cuni.mff.d3s.been.swrepository.test"); artifactId.setVersion("3.2.1..."); } @Before public void setUpClient() throws UnknownHostException { // assuming JUnit4 runner executes @Rules before @Before methods client = clientFactory.getClient(serverAllocatorRule.getHost(), serverAllocatorRule.getPort()); } /** * Scratch the test fields. */ @After public void scratchFields() { randomContentFile = null; bpkId = null; } /** * Clean up the persistence folder. * * @throws IOException * When problems come up when cleaning the folder */ @After public void scratchPersistence() throws IOException { FileUtils.deleteDirectory(SERVER_PERSISTENCE_ROOT_FOLDER); FileUtils.deleteDirectory(CLIENT_PERSISTENCE_ROOT_FOLDER); } @After public void tearDownClient() { client = null; } @Test public void testUploadBpk() throws Exception { client.putBpk(bpkId, new FileInputStream(randomContentFile)); assertFilePresent( SERVER_PERSISTENCE_ROOT_FOLDER, String.format("%s-%s.bpk", bpkId.getBpkId(), bpkId.getVersion()), "bpks", randomContentFile, bpkId.getGroupId(), bpkId.getBpkId(), bpkId.getVersion()); } @Test(expected = SwRepositoryClientException.class) public void testUploadBpk_duplicateEntry() throws Exception { try { client.putBpk(bpkId, new FileInputStream(randomContentFile)); } catch (Exception e) { fail("unexpected exception"); } client.putBpk(bpkId, new FileInputStream(randomContentFile)); } @Test(expected = SwRepositoryClientException.class) public void testUploadBpk_badIdentifier() throws Exception { bpkId.setBpkId(null); client.putBpk(bpkId, new FileInputStream(randomContentFile)); } @Test(expected = SwRepositoryClientException.class) public void testUploadBpk_serverDown() throws Exception { client.putBpk(bpkId, new FileInputStream(randomContentFile)); } // test delete bpk that exists // test delete bpk that doesn't exist @Test public void testDownloadBpk() throws IOException { File persistedFile = getFileFromPathAndName( SERVER_PERSISTENCE_ROOT_FOLDER, String.format("%s-%s.bpk", bpkId.getBpkId(), bpkId.getVersion()), "bpks", bpkId.getGroupId(), bpkId.getBpkId(), bpkId.getVersion()); persistedFile.getParentFile().mkdirs(); persistedFile.createNewFile(); FileWriter fw = new FileWriter(persistedFile); final String evilContent = "THIS CONTENT IS EVIL, DON'T READ IT!"; fw.write(evilContent); fw.close(); Bpk bpk = client.getBpk(bpkId); assertNotNull(bpk); InputStream bpkIs = bpk.getInputStream(); final String downloadedContent = IOUtils.toString(bpkIs); bpkIs.close(); assertEquals(evilContent, downloadedContent); } @Test public void testDownloadBpk_badIdentifier() { bpkId.setBpkId(null); assertNull(client.getBpk(bpkId)); } @Test public void testDownloadBpk_serverDown() { assertNull(client.getBpk(bpkId)); } @Test public void testDownloadBpkInCache_serverDown() throws IOException { File fileInCache = getFileFromPathAndName( CLIENT_PERSISTENCE_ROOT_FOLDER, String.format("%s-%s.bpk", bpkId.getBpkId(), bpkId.getVersion()), "bpks", bpkId.getGroupId(), bpkId.getBpkId(), bpkId.getVersion()); fileInCache.getParentFile().mkdirs(); fileInCache.createNewFile(); FileWriter fw = new FileWriter(fileInCache); final String cacheContent = "I'm in cache and I know it!"; fw.write(cacheContent); fw.close(); Bpk bpk = client.getBpk(bpkId); assertNotNull(bpk); InputStream bpkIs = bpk.getInputStream(); assertNotNull(bpkIs); assertEquals(cacheContent, IOUtils.toString(bpkIs)); bpkIs.close(); } @Test public void testDownloadedBpkCaching() throws IOException { File serverFile = getFileFromPathAndName( SERVER_PERSISTENCE_ROOT_FOLDER, String.format("%s-%s.bpk", bpkId.getBpkId(), bpkId.getVersion()), "bpks", bpkId.getGroupId(), bpkId.getBpkId(), bpkId.getVersion()); serverFile.getParentFile().mkdirs(); serverFile.createNewFile(); FileWriter fw = new FileWriter(serverFile); fw.write("I'm a server file and I'm proud of it."); fw.close(); Bpk bpk = client.getBpk(bpkId); assertNotNull(bpk); InputStream bpkFileStream = bpk.getInputStream(); File fileInCache = getFileFromPathAndName( CLIENT_PERSISTENCE_ROOT_FOLDER, String.format("%s-%s.bpk", bpkId.getBpkId(), bpkId.getVersion()), "bpks", bpkId.getGroupId(), bpkId.getBpkId(), bpkId.getVersion()); assertNotNull(fileInCache); assertTrue(fileInCache.exists()); assertEquals(IOUtils.toString(bpkFileStream), FileUtils.fileRead(fileInCache)); } @Test public void testDownloadArtifact() throws IOException { File serverFile = getFileFromPathAndName( SERVER_PERSISTENCE_ROOT_FOLDER, String.format("%s-%s.jar", artifactId.getArtifactId(), artifactId.getVersion()), "artifacts", artifactId.getGroupId(), artifactId.getArtifactId(), artifactId.getVersion()); serverFile.getParentFile().mkdirs(); serverFile.createNewFile(); FileWriter fw = new FileWriter(serverFile); fw.write("KABOOOOOOOOOOOOOOOOOOOOOOM!!!"); fw.close(); Artifact artifact = client.getArtifact(artifactId); assertNotNull(artifact); assertFalse(artifact.getFile().equals(serverFile)); assertEquals(FileUtils.fileRead(serverFile), FileUtils.fileRead(artifact.getFile())); } @Test public void testDownloadArtifact_badIdentifier() { artifactId.setArtifactId(null); assertNull(client.getArtifact(artifactId)); } @Test public void testDownloadArtifact_serverDown() throws IOException { File serverFile = getFileFromPathAndName( SERVER_PERSISTENCE_ROOT_FOLDER, String.format("%s-%s.jar", artifactId.getArtifactId(), artifactId.getVersion()), "artifacts", artifactId.getGroupId(), artifactId.getArtifactId(), artifactId.getVersion()); serverFile.getParentFile().mkdirs(); serverFile.createNewFile(); FileWriter fw = new FileWriter(serverFile); fw.write("KABOOOOOOOOOOOOOOOOOOOOOOM!!!"); fw.close(); Artifact artifact = client.getArtifact(artifactId); assertNull(artifact); } @Test public void testDownloadArtifactInCache_serverDown() throws IOException { File cacheFile = getFileFromPathAndName( CLIENT_PERSISTENCE_ROOT_FOLDER, String.format("%s-%s.jar", artifactId.getArtifactId(), artifactId.getVersion()), "artifacts", artifactId.getGroupId(), artifactId.getArtifactId(), artifactId.getVersion()); cacheFile.getParentFile().mkdirs(); cacheFile.createNewFile(); FileWriter fw = new FileWriter(cacheFile); fw.write("KABOOOOOOOOOOOOOOOOOOOOOOM!!!"); fw.close(); Artifact artifact = client.getArtifact(artifactId); assertNotNull(artifact); assertNotNull(artifact.getFile()); assertTrue(artifact.getFile().exists()); assertEquals(FileUtils.fileRead(cacheFile), FileUtils.fileRead(artifact.getFile())); } @Test public void testUploadArtifact() throws Exception { client.putArtifact(artifactId, new FileInputStream(randomContentFile)); File serverItem = getFileFromPathAndName( SERVER_PERSISTENCE_ROOT_FOLDER, String.format("%s-%s.jar", artifactId.getArtifactId(), artifactId.getVersion()), "artifacts", artifactId.getGroupId(), artifactId.getArtifactId(), artifactId.getVersion()); assertNotNull(serverItem); assertTrue(serverItem.exists()); assertEquals(FileUtils.fileRead(randomContentFile), FileUtils.fileRead(serverItem)); } @Test(expected = SwRepositoryClientException.class) public void testUploadArtifact_serverDown() throws Exception { client.putArtifact(artifactId, new FileInputStream(randomContentFile)); } /* will be useful to assertFalse once the behavior has changed to the desired version @Test (expected = SwRepositoryClientException.class) public void testUploadArtifact_duplicateEntry() throws Exception { try { client.putArtifact(artifactId, new FileInputStream(randomContentFile)); } catch (Exception e) { fail("unexpected exception"); } client.putArtifact(artifactId, new FileInputStream(randomContentFile)); } */ /** * Assert that a file can be found in the server persistence and that its * content is equal to the content of the reference file. * * @param root * FS root of the data store we should look in * @param fileName * The name of the file we're expecting to find * @param storeName * Name of the store this file lies in * @param referenceFile * Reference content of the file we're expecting to find * @param groupId * groupId of tested file * @param itemId * itemId of tested file * @param itemVersion * itemVersion of tested file * * @throws IOException * On reference or actual file read error */ public void assertFilePresent(File root, String fileName, String storeName, File referenceFile, String groupId, String itemId, String itemVersion) throws IOException { final File file = getFileFromPathAndName(root, fileName, storeName, groupId, itemId, itemVersion); assertTrue(file.exists()); final String actualFileContent = FileUtils.fileRead(referenceFile); final String referenceFileContent = FileUtils.fileRead(file); assertEquals(referenceFileContent, actualFileContent); } /** * Assert that a file can not be found in the server persistence. * * @param root * FS root of the data store we should look in * @param fileName * Name of the file we're not expecting to find * @param storeName * Name of the store this file lies in * @param groupId * groupId of tested file * @param itemId * itemId of tested file * @param itemVersion * itemVersion of tested file */ public void assertFileAbsent(File root, String fileName, String storeName, String groupId, String itemId, String itemVersion) { final File file = getFileFromPathAndName(root, fileName, storeName, groupId, itemId, itemVersion); assertFalse(file.exists()); } private File getFileFromPathAndName(File root, String fileName, String storeName, String groupId, String itemId, String itemVersion) { List<String> pathItemList = new LinkedList(); pathItemList.addAll(Arrays.asList(groupId.split("\\."))); pathItemList.add(itemId); pathItemList.add(itemVersion); Path path = FileSystems.getDefault().getPath( root.getPath() + File.separator + storeName, pathItemList.toArray(new String[pathItemList.size()])); return new File(path.toFile(), fileName); } }