/*******************************************************************************
* Copyright (c) 2015 IBM Corp.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*******************************************************************************/
package com.ibm.ws.lars.upload.cli;
import static org.hamcrest.CoreMatchers.containsString;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.fail;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.Console;
import java.io.File;
import java.io.FileFilter;
import java.io.PrintStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import mockit.Invocation;
import mockit.Mock;
import mockit.MockUp;
import org.junit.Test;
import com.ibm.ws.lars.upload.cli.ClientException.HelpDisplay;
import com.ibm.ws.massive.esa.MassiveEsa;
import com.ibm.ws.repository.common.enums.ResourceType;
import com.ibm.ws.repository.common.enums.State;
import com.ibm.ws.repository.connections.RepositoryConnection;
import com.ibm.ws.repository.connections.RestRepositoryConnection;
import com.ibm.ws.repository.resources.EsaResource;
import com.ibm.ws.repository.resources.RepositoryResource;
import com.ibm.ws.repository.resources.internal.RepositoryResourceImpl;
import com.ibm.ws.repository.strategies.writeable.AddThenDeleteStrategy;
import com.ibm.ws.repository.strategies.writeable.UploadStrategy;
/**
* Test the upload action
*/
public class UploadTest {
/**
* Mock uploader which captures the login info and the names of files which are uploaded.
*/
public static class MockUploader extends MockUp<MassiveEsa> {
private RepositoryConnection repoConnection = null;
private final List<String> filesUploaded = new ArrayList<>();
@Mock
public void $init(RepositoryConnection repoConnection) {
this.repoConnection = repoConnection;
}
@Mock
public Collection<EsaResource> addEsasToMassive(Collection<File> esas, UploadStrategy strategy) {
for (File file : esas) {
filesUploaded.add(file.getName());
}
return null;
}
public RepositoryConnection getLoginInfoEntry() {
return repoConnection;
}
public List<String> getFilesUploaded() {
return filesUploaded;
}
}
/**
* Mock file that reports that any file exists, except for "InvalidFile.esa"
*/
public static class MockFile extends MockUp<File> {
@Mock
public boolean canRead(Invocation inv) {
System.out.println("File is " + inv.<File> getInvokedInstance().getName());
if (inv.<File> getInvokedInstance().getName().equals("InvalidFile.esa")) {
return false;
} else {
return true;
}
}
}
public static class MockEmptyDirectory extends MockUp<File> {
@Mock
public boolean canRead(Invocation inv) {
return true;
}
@Mock
public boolean isDirectory(Invocation inv) {
return true;
}
}
/**
* Mocks up a directory structure that looks like this:
*
* <pre>
* superDirectory
* cheese.esa
* ston.ese
* I_am_not_a_feature.txt
* subDirectory
* dontUploadThis.esa
* </pre>
*/
public static class MockDirectoryWithFiles extends MockUp<File> {
@Mock
public boolean canRead(Invocation inv) {
return true;
}
@Mock
public boolean isDirectory(Invocation inv) {
String name = ((File) inv.getInvokedInstance()).getName();
return "superDirectory".equals(name) ||
"subDirectory".equals(name);
}
@Mock
public File[] listFiles(Invocation inv, FileFilter filter) {
final List<File> files;
File thiz = (File) inv.getInvokedInstance();
String name = thiz.getName();
if ("superDirectory".equals(name)) {
files = Arrays.asList(new File(thiz, "cheese.esa"),
new File(thiz, "ston.esa"),
new File(thiz, "I_am_not_a_feature.txt"),
new File(thiz, "subDirectory"));
} else if ("subDirectory".equals(name)) {
files = Arrays.asList(new File(thiz, "dontUploadThis.esa"));
} else {
return null;
}
List<File> filteredFiles = new ArrayList<File>();
for (File f : files) {
if (filter.accept(f)) {
filteredFiles.add(f);
}
}
return filteredFiles.toArray(new File[filteredFiles.size()]);
}
}
/**
* Mocked up AddThenDeleteStrategy that claims to have deleted the specified number of
* resources.
*/
public static class MockAddThenDeleteStrategy extends MockUp<AddThenDeleteStrategy> {
private final int numDeletedResources;
public MockAddThenDeleteStrategy(int numDeletedResources) {
this.numDeletedResources = numDeletedResources;
}
@Mock
public void $init(State desiredStateIfMatchingFound, State desiredStateIfNoMatchingFound, boolean forceReplace,
RepositoryResourceImpl matchingResource, List<RepositoryResource> deletedResources) {
if (deletedResources != null) {
MockEsaResource mockESA = new MockEsaResource();
for (int i = 0; i < numDeletedResources; i++) {
deletedResources.add(mockESA.getMockInstance());
}
}
}
}
public static class MockEsaResource extends MockUp<EsaResource> {
@Mock
public String getName() {
return "a-fake-name";
}
@Mock
public ResourceType getType() {
return ResourceType.FEATURE;
}
@Mock
public String getAppliesTo() {
return "fake-applies-to";
}
@Mock
public String getVersion() {
return "1.2.3.4";
}
@Mock
public String getProvideFeature() {
return "a-fake-feature";
}
}
@Test
public void testNoFiles() {
new MockUploader();
ByteArrayOutputStream out = new ByteArrayOutputStream();
Main main = new Main(new ByteArrayInputStream(new byte[0]), new PrintStream(out));
try {
main.run(new String[] { "--upload", "--url=http://example.org/" });
fail("ClientException not thrown");
} catch (ClientException ex) {
assertEquals("Wrong return code", 1, ex.getReturnCode());
assertEquals("Wrong error message", Main.NO_FILES, ex.getMessage());
assertEquals("Wrong help display", HelpDisplay.SHOW_HELP, ex.getHelpDisplay());
}
}
@Test
public void testEmptyDirectoryShouldResultInNoFilesError() {
new MockUploader();
new MockEmptyDirectory();
ByteArrayOutputStream out = new ByteArrayOutputStream();
Main main = new Main(new ByteArrayInputStream(new byte[0]), new PrintStream(out));
try {
main.run(new String[] { "--upload", "--url=http://example.org", "TestFile.esa" });
fail("ClientException not thrown");
} catch (ClientException ex) {
assertEquals("Wrong return code", 1, ex.getReturnCode());
assertEquals("Wrong error message", Main.NO_FILES, ex.getMessage());
assertEquals("Wrong help display", HelpDisplay.SHOW_HELP, ex.getHelpDisplay());
}
}
@Test
public void testNoUrl() {
new MockUploader();
ByteArrayOutputStream out = new ByteArrayOutputStream();
Main main = new Main(new ByteArrayInputStream(new byte[0]), new PrintStream(out));
try {
main.run(new String[] { "--upload", "TestFile.esa" });
fail("ClientException not thrown");
} catch (ClientException ex) {
assertEquals("Wrong return code", 1, ex.getReturnCode());
assertEquals("Wrong error message", "The repository url must be provided, either as an argument or in a configuration file.", ex.getMessage());
assertEquals("Wrong help display", HelpDisplay.SHOW_HELP, ex.getHelpDisplay());
}
}
@Test
public void testInvalidFile() {
new MockUploader();
new MockFile();
ByteArrayOutputStream out = new ByteArrayOutputStream();
Main main = new Main(new ByteArrayInputStream(new byte[0]), new PrintStream(out));
try {
main.run(new String[] { "--upload", "--url=http://example.org", "TestFile.esa", "InvalidFile.esa" });
fail("ClientException not thrown");
} catch (ClientException ex) {
assertEquals("Wrong return code", 1, ex.getReturnCode());
assertEquals("Wrong error message", "File InvalidFile.esa can't be read", ex.getMessage());
assertEquals("Wrong help display", HelpDisplay.NO_HELP, ex.getHelpDisplay());
}
}
@Test
public void testUpload() throws ClientException {
new MockFile();
MockUploader uploader = new MockUploader();
ByteArrayOutputStream out = new ByteArrayOutputStream();
Main main = new Main(new ByteArrayInputStream(new byte[0]), new PrintStream(out));
main.run(new String[] { "--upload", "--url=http://example.org", "TestFile.esa" });
assertEquals("Wrong files uploaded", Arrays.asList("TestFile.esa"), uploader.getFilesUploaded());
assertThat("Output incorrect", out.toString(), containsString("Uploading TestFile.esa ... done"));
}
@Test
public void testMultipleUpload() throws ClientException {
new MockFile();
MockUploader uploader = new MockUploader();
ByteArrayOutputStream out = new ByteArrayOutputStream();
Main main = new Main(new ByteArrayInputStream(new byte[0]), new PrintStream(out));
main.run(new String[] { "--upload", "--url=http://example.org", "TestFile.esa", "TestFile2.esa", "TestFile3.esa" });
assertEquals("Wrong files uploaded", Arrays.asList("TestFile.esa", "TestFile2.esa", "TestFile3.esa"), uploader.getFilesUploaded());
assertThat("Output incorrect", out.toString(), containsString("Uploading TestFile.esa ... done"));
assertThat("Output incorrect", out.toString(), containsString("Uploading TestFile2.esa ... done"));
assertThat("Output incorrect", out.toString(), containsString("Uploading TestFile3.esa ... done"));
}
@Test
public void testCredentials() throws ClientException {
new MockFile();
MockUploader uploader = new MockUploader();
ByteArrayOutputStream out = new ByteArrayOutputStream();
Main main = new Main(new ByteArrayInputStream(new byte[0]), new PrintStream(out));
main.run(new String[] { "--upload", "--url=http://example.org", "--username=jbloggs", "--password=foobar", "TestFile.esa" });
RestRepositoryConnection repoConnection = (RestRepositoryConnection) uploader.getLoginInfoEntry();
assertEquals("Wrong files uploaded", Arrays.asList("TestFile.esa"), uploader.getFilesUploaded());
assertThat("Output incorrect", out.toString(), containsString("Uploading TestFile.esa ... done"));
assertEquals("Wrong username", "jbloggs", repoConnection.getUserId());
assertEquals("Wrong password", "foobar", repoConnection.getPassword());
assertNull("Softlayer username incorrectly set", repoConnection.getSoftlayerUserId());
assertNull("Softlayer password incorrectly set", repoConnection.getSoftlayerPassword());
assertNull("Attachment username incorrectly set", repoConnection.getAttachmentBasicAuthUserId());
assertNull("Attachment password incorrectly set", repoConnection.getAttachmentBasicAuthPassword());
assertNotNull("ApiKey is null", repoConnection.getApiKey());
}
/**
* Test that we prompt on the command line for a password when requested.
*/
@Test
public void testPasswordPrompt() throws ClientException {
new MockFile();
MockUploader uploader = new MockUploader();
// Mock console that returns a fixed password
class MockConsole extends MockUp<Console> {
public int count = 0;
@Mock
public char[] readPassword(String fmt, Object... args) {
count++;
return "thePassw0rd".toCharArray();
}
}
final MockConsole mockConsole = new MockConsole();
new MockUp<Main>() {
@Mock
private Console getConsole() {
return mockConsole.getMockInstance();
}
};
ByteArrayOutputStream out = new ByteArrayOutputStream();
// test with a password prompt
Main main = new Main(new ByteArrayInputStream(new byte[0]), new PrintStream(out));
main.run(new String[] { "--upload", "--url=http://example.org", "--username=jbloggs", "--password", "TestFile.esa" });
RestRepositoryConnection repoConnection = (RestRepositoryConnection) uploader.getLoginInfoEntry();
assertEquals("Wrong files uploaded", Arrays.asList("TestFile.esa"), uploader.getFilesUploaded());
assertThat("Output incorrect", out.toString(), containsString("Uploading TestFile.esa ... done"));
assertEquals("Wrong username", "jbloggs", repoConnection.getUserId());
assertEquals("Wrong password", "thePassw0rd", repoConnection.getPassword());
assertEquals("Wrong number of calls to readPassword", 1, mockConsole.count);
assertNull("Softlayer username incorrectly set", repoConnection.getSoftlayerUserId());
assertNull("Softlayer password incorrectly set", repoConnection.getSoftlayerPassword());
assertNull("Attachment username incorrectly set", repoConnection.getAttachmentBasicAuthUserId());
assertNull("Attachment password incorrectly set", repoConnection.getAttachmentBasicAuthPassword());
assertNotNull("ApiKey is null", repoConnection.getApiKey());
// test that we don't prompt when the password is provided
main.run(new String[] { "--upload", "--url=http://example.org", "--username=jbloggs", "--password=foobar", "TestFile.esa" });
// call count should not have increased here
assertEquals("Wrong number of calls to readPassword", 1, mockConsole.count);
// test that we don't prompt when the password is not mentioned
main.run(new String[] { "--upload", "--url=http://example.org", "TestFile.esa" });
// call count should not have increased here
assertEquals("Wrong number of calls to readPassword", 1, mockConsole.count);
}
@Test
public void testShouldUploadContentsOfDirectory() throws Exception {
MockUploader uploader = new MockUploader();
new MockDirectoryWithFiles();
ByteArrayOutputStream out = new ByteArrayOutputStream();
Main main = new Main(new ByteArrayInputStream(new byte[0]), new PrintStream(out));
main.run(new String[] { "--upload", "--url=http://example.org", "superDirectory" });
assertEquals("Wrong files uploaded", Arrays.asList("cheese.esa", "ston.esa"), uploader.getFilesUploaded());
assertThat("Output incorrect", out.toString(), containsString("Uploading superDirectory" + File.separator + "cheese.esa ... done"));
assertThat("Output incorrect", out.toString(), containsString("Uploading superDirectory" + File.separator + "ston.esa ... done"));
}
@Test
public void testShouldGiveProgressIndication() throws Exception {
new MockUploader();
new MockDirectoryWithFiles();
ByteArrayOutputStream outStream = new ByteArrayOutputStream();
Main main = new Main(new ByteArrayInputStream(new byte[0]), new PrintStream(outStream));
main.run(new String[] { "--upload", "--url=http://example.org", "superDirectory", "i-am-a-feature.esa" });
String output = outStream.toString();
assertThat("Output incorrect", output, containsString("1 of 3: Uploading superDirectory" + File.separator + "cheese.esa"));
assertThat("Output incorrect", output, containsString("2 of 3: Uploading superDirectory" + File.separator + "ston.esa"));
assertThat("Output incorrect", output, containsString("3 of 3: Uploading i-am-a-feature.esa"));
}
@Test
public void shouldGiveIndicationIfExistingResourceReplaced() throws Exception {
new MockUploader();
new MockDirectoryWithFiles();
new MockAddThenDeleteStrategy(1);
new MockEsaResource();
// The mocks that we define above make the uploader think that an existing ESA
// has been deleted when we upload our new resource.
// We verify that we get a message telling us that the pre-existing file was deleted
ByteArrayOutputStream outStream = new ByteArrayOutputStream();
Main main2 = new Main(new ByteArrayInputStream(new byte[0]), new PrintStream(outStream));
main2.run(new String[] { "--upload", "--url=http://example.org", "superDirectory/cheese.esa" });
assertThat("Output incorrect", outStream.toString(),
containsString("1 of 1: Uploading superDirectory" + File.separator + "cheese.esa ... done, replacing existing asset " +
"a-fake-name type=FEATURE, appliesTo=fake-applies-to, " +
"version=1.2.3.4, provideFeature=a-fake-feature"));
}
@Test
public void shouldGiveIndicationIfMultipleExistingResourceReplaced() throws Exception {
new MockUploader();
new MockDirectoryWithFiles();
new MockAddThenDeleteStrategy(2);
new MockEsaResource();
// The mocks that we define above make the uploader think that two existing ESAs
// have been deleted when we upload our new resource.
// We verify that we get a message telling us that the pre-existing files were deleted
ByteArrayOutputStream outStream = new ByteArrayOutputStream();
Main main2 = new Main(new ByteArrayInputStream(new byte[0]), new PrintStream(outStream));
main2.run(new String[] { "--upload", "--url=http://example.org", "superDirectory/cheese.esa" });
assertEquals("Output incorrect",
"1 of 1: Uploading superDirectory" + File.separator + "cheese.esa ... done, replacing multiple duplicate assets:" +
System.getProperty("line.separator") +
"a-fake-name type=FEATURE, appliesTo=fake-applies-to, version=1.2.3.4, provideFeature=a-fake-feature" +
System.getProperty("line.separator") +
"a-fake-name type=FEATURE, appliesTo=fake-applies-to, version=1.2.3.4, provideFeature=a-fake-feature" +
System.getProperty("line.separator"),
outStream.toString());
}
}