//2012-12-05 johnpfeiffer
package net.kittyandbear;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.URI;
import java.net.URISyntaxException;
import java.rmi.UnexpectedException;
import java.util.Map;
import java.util.Properties;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.client.utils.URIBuilder;
import org.apache.log4j.ConsoleAppender;
import org.apache.log4j.Level;
import org.apache.log4j.PatternLayout;
import org.apache.log4j.PropertyConfigurator;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Ignore;
import org.junit.Test;
import com.acme.storagegateway.adaptor.impl.NirvanixStorageConnector.RuleException;
public class NirvanixStorageConnectorTest {
private NirvanixStorageConnectorStub defaultTester;
@BeforeClass
public static void setUpBeforeClass() throws Exception {
// Configure and bind slf4j logging to use log4j
Class<?>[] loggedClasses = { NirvanixStorageConnector.class };
String loggingLevel = Level.INFO.toString();
Properties props = new Properties();
props.put( "log4j.rootLogger", loggingLevel + ", " + "R" );
props.put( "log4j.appender.R", ConsoleAppender.class.getName() );
props.put( "log4j.appender.R.encoding", "UTF-8" );
props.put( "log4j.appender.R.layout", PatternLayout.class.getName() );
props.put( "log4j.appender.R.layout.ConversionPattern", "%d %5p (%F:%L) - %m%n" );
for( @SuppressWarnings( "rawtypes" )
Class clazz : loggedClasses ) {
props.put( "log4j.logger." + clazz.getName(), loggingLevel );
}
PropertyConfigurator.configure( props );
}
@Before
public void setUp() throws Exception {
defaultTester = new NirvanixStorageConnectorStub( "apiHostname", "https" );
}
// High Value Happy Path Tests
// TODO: verify setting http for data transfer
// TODO: verify metadata map is created and converted to JSON correctly
@Test
public void testValidCredentialsCanSuccessfullyGetSession() throws Exception {
// Given: valid Nirvanix Response containing a session token
defaultTester.setExpectedGetResponse( "{\"ResponseCode\":0,\"SessionToken\":\"7f4dcfdc-5952-4d54-8474-55a9bcc9febe\"}" );
// When
String sessionToken = defaultTester.getSession( "appKey", "username", "password" );
// Then
assertEquals( "7f4dcfdc-5952-4d54-8474-55a9bcc9febe", sessionToken );
}
@Test
public void testPutFile() throws Exception {
// Given: a good uploadToken the response after the Put should be a success from Nirvanix
defaultTester
.setExpectedGetResponse( "{\"ResponseCode\":0,\"GetStorageNode\":{\"UploadHost\":\"node10.nirvanix.com\",\"UploadToken\":\"OWP1_MXr~HaKbzYYef9~FODoYMDq~pS5_E3yjFR~Y6eY99Gs4hWTcptQeQ\"}}" );
defaultTester
.setExpectedPutResponse( "<Response>\n\t<ResponseCode>0</ResponseCode>\n\t<FilesUploaded>1</FilesUploaded>\n\t<BytesUploaded>105450</BytesUploaded>\n</Response>" );
// When
defaultTester.putFile( "filename", "destFolderPath", null, new ByteArrayInputStream( "testdata".getBytes() ), "sessionToken",
"testdata".getBytes().length );
// Then : Should complete with no Exceptions
}
@Test( expected = IOException.class )
public void testPutFileReceivesInvalidPathError() throws Exception {
// Given: a good uploadToken but Nirvanix response is code "upload aborted" which parses and translates into an IOException
defaultTester
.setExpectedGetResponse( "{\"ResponseCode\":0,\"GetStorageNode\":{\"UploadHost\":\"node10.nirvanix.com\",\"UploadToken\":\"OWP1_MXr~HaKbzYYef9~FODoYMDq~pS5_E3yjFR~Y6eY99Gs4hWTcptQeQ\"}}" );
defaultTester
.setExpectedPutResponse( "<Response>\n\t<ResponseCode>70122</ResponseCode>\n\t<FilesUploaded>1</FilesUploaded>\n\t<BytesUploaded>105450</BytesUploaded>\n</Response>" );
// When
defaultTester.putFile( "filename", "destFolderPath", null, new ByteArrayInputStream( "testdata".getBytes() ), "sessionToken",
"testdata".getBytes().length );
// Then : should throw IOException
}
@Test
public void testGetSingleMetadataValueUsingMetadataKeyLengthOneHundredCharacters() throws Exception {
// Given: a single reponse with metadata "testkey"
String testKey = "yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy";
String testValue = "zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz";
defaultTester
.setExpectedGetResponse( "{\"ResponseCode\":0,\"Metadata\":[{\"Type\":\"MD5\",\"Value\":\"md5stringblahblah\"},{\"Type\":\""
+ testKey + "\",\"Value\":\"" + testValue + "\"}]}\"" );
// When
// Then: verify successfully received a map with the correct metadata
assertEquals( testValue, defaultTester.getMetadata( "fileId", testKey, "sessionToken" ) );
}
@Test( expected = RuleException.class )
public void testMetadataKeyMissingThrowsRuleException() throws Exception {
// Given: a single reponse without metadata "testkey"
defaultTester
.setExpectedGetResponse( "{\"ResponseCode\":0,\"Metadata\":[{\"Type\":\"MD5\",\"Value\":\"md5stringblahblah\"},{\"Type\":\"NOTtestkey\",\"Value\":\"testvalue\"}]}\"" );
// When
// Then: verify successfully received a map with the correct metadata
assertEquals( "testvalue", defaultTester.getMetadata( "fileId", "testkey", "sessionToken" ) ); // ResponseCategory.GetMetadataValueNotFound.code
}
@Test
public void testGetThreeMetadataOfLengthOneHundredCharacters() throws Exception {
// Given: a successful reponse with three metadata entries of 100 characters each
String meta1 = "1111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111";
String meta2 = "2222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222";
String meta3 = "3333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333";
defaultTester
.setExpectedGetResponse( "{\"ResponseCode\":0,\"Metadata\":[{\"Type\":\"MD5\",\"Value\":\"0TOiwDQuBcUX9d9EzRwSHA==\"},{\"Type\":\""
+ meta1
+ "\",\"Value\":\""
+ meta1
+ "\"},{\"Type\":\""
+ meta2
+ "\",\"Value\":\""
+ meta2
+ "\"},{\"Type\":\""
+ meta3 + "\",\"Value\":\"" + meta3 + "\"}]} " );
// When
Map<String, String> metadataMap = defaultTester.getMetadata( "fileId", "sessionToken" );
// Then: verify successfully received a map with the correct metadata
assertEquals( meta1, metadataMap.get( meta1 ) );
assertEquals( meta2, metadataMap.get( meta2 ) );
assertEquals( meta3, metadataMap.get( meta3 ) );
}
@Test
public void testGetFile() throws Exception {
// Given: an inner class to override the expected response when querying for a downloadURL and successful download response
class NirvanixRestConnectorStubStream extends NirvanixStorageConnector {
InputStream expectedResult;
String expectedDownloadUrl;
public NirvanixRestConnectorStubStream( String apiHostname, String dataTransferProtocol ) throws RuleException {
super();
}
void setExpectedGetResponse( InputStream expected ) {
this.expectedResult = expected;
}
void setExpectedDownloadUrl( String expected ) {
this.expectedDownloadUrl = expected;
}
@Override
protected URI getDownloadURI( String filePath, String sessionToken ) throws SystemException, UnexpectedException, IOException {
URI uri = null;
try {
uri = new URI( this.expectedDownloadUrl );
} catch( URISyntaxException e ) {
e.printStackTrace();
}
return uri;
}
@Override
protected InputStream executeHttpAndGetResponseAsStream( URI uri ) throws ClientProtocolException, IOException {
return expectedResult;
}
}
NirvanixRestConnectorStubStream test = new NirvanixRestConnectorStubStream( "apiHostname", "https" );
// Given: a downloadURL in the correct format
test.setExpectedDownloadUrl( "https://node10.nirvanix.com/rW_DJwIA~gjwilCCs_n~sxObI6dk~5TZAaSO2B-~l30COJw/Test_QA/TESTQA/acme-test-bucket/f83bdd54-7472-431f-922f-58796d957662" );
// Given: test data as a stream
String testdata = "testdatastream\nsecond line\n";
InputStream testdataInputStream = new ByteArrayInputStream( testdata.getBytes() );
test.setExpectedGetResponse( testdataInputStream );
// When
InputStream testGet = test.getFile( "fileId", "sessionToken" );
StringBuilder resultData = new StringBuilder();
BufferedReader bufferedReader = new BufferedReader( new InputStreamReader( testGet ) );
if( bufferedReader != null ) {
try {
String line = null;
while( (line = bufferedReader.readLine()) != null ) {
resultData.append( line ).append( "\n" );
}
} finally {
bufferedReader.close();
}
}
// Then: the stream we read out was the same we started with
assertEquals( testdata, resultData.toString() );
}
@Test
public void testDeleteExistingFile() throws Exception {
// Given: a valid success response from Nirvanix
defaultTester.setExpectedGetResponse( "{\"ResponseCode\":0}" );
String knownExistingFile = "Test_QA/17aec2e6-d125-4174-8c55-9f70b77269e4";
// When
defaultTester.delete( knownExistingFile, "sessionToken" );
// Then : should complete with no error
}
@Test( expected = UnexpectedException.class )
public void testGetsUnexpectedException() throws Exception {
// Given: invalid error message code response from Nirvanix, InvalidImageDimensions(70111) has nothing to do with delete
defaultTester.setExpectedGetResponse( "{\"ResponseCode\":70111}" );
String knownExistingFile = "Test_QA/17aec2e6-d125-4174-8c55-9f70b77269e4";
// When
try {
defaultTester.delete( knownExistingFile, "sessionToken" );
} catch( UnexpectedException e ) { // Then: output the Exception to console for manual verification
System.out.println( "SUCCESSFULLY threw UnexpectedException," + e.getMessage() + "," + e.getClass().getName() );
defaultTester.delete( "fileId", "sessionToken" ); // repeat in order to pass the test
}
}
@Test( expected = NirvanixStorageConnector.SystemException.class )
public void testGetsSystemException() throws Exception {
// Given: error response from Nirvanix RestrictedIPAddress(80104) means the Nirvanix Support must modify the Customer's Account
defaultTester.setExpectedGetResponse( "{\"ResponseCode\":80104}" );
// When
try {
defaultTester.delete( "fileId", "sessionToken" );
} catch( NirvanixStorageConnector.SystemException e ) { // Then: output the Exception to console for manual verification
System.out.println( "SUCCESSFULLY threw SystemException," + e.getMessage() + "," + e.getClass().getName() );
defaultTester.delete( "fileId", "sessionToken" ); // repeat in order to pass the test
}
}
// Lower Value but still useful (input validation tests and edge case exceptions)
@Test( expected = UnexpectedException.class )
public void testJSONExceptionThrownAsUnexpectedException() throws Exception {
// Given: an invalid JSON Object
defaultTester.setExpectedGetResponse( "{\"ResponseCode\"::80104}" );
// When
try {
defaultTester.delete( "fileId", "sessionToken" );
} catch( UnexpectedException e ) { // Then: output the Exception to console for manual verification
System.out.println( "SUCCESSFULLY threw UnexpectedException," + e.getMessage() + "," + e.getClass().getName() );
defaultTester.delete( "fileId", "sessionToken" ); // repeat in order to pass the test
}
}
@Test
@Ignore( "" )
public void testConstructorInstantiatesAnHttpClient() {
assertNotNull( NirvanixStorageConnector.httpclient );
}
@Test( expected = NirvanixStorageConnector.SystemException.class )
public void testConstructorApiHostnameNullThrowsSystemException() throws Exception {
NirvanixStorageConnector connector = new NirvanixStorageConnector();
connector.setApiHostname( null );
connector.initialization();
}
@Test( expected = NirvanixStorageConnector.SystemException.class )
public void testConstructorApiHostnameEmptyThrowsSystemException() throws Exception {
NirvanixStorageConnector connector = new NirvanixStorageConnector();
connector.setApiHostname( "" );
connector.initialization();
}
@Test( expected = NirvanixStorageConnector.RuleException.class )
@Ignore( "NirvanixConnector uses https by default for data transfer - setHttps is optional " )
public void testConstructorDataTransferProtocolNullThrowsRuleException() throws Exception {
NirvanixStorageConnector connector = new NirvanixStorageConnector();
connector.setHttps( true );
connector.initialization();
}
@Test( expected = NirvanixStorageConnector.RuleException.class )
@Ignore( "NirvanixConnector uses https by default for data transfer - setHttps is optional " )
public void testConstructorDataTransferProtocolEmptyThrowsRuleException() throws Exception {
NirvanixStorageConnector connector = new NirvanixStorageConnector();
connector.setHttps( false );
connector.initialization();
}
@Test( expected = NirvanixStorageConnector.RuleException.class )
public void testGetSessionAppKeyNullThrowsRuleException() throws Exception {
defaultTester.getSession( null, "username", "password" );
}
@Test( expected = NirvanixStorageConnector.RuleException.class )
public void testGetSessionAppKeyEmptyThrowsRuleException() throws Exception {
defaultTester.getSession( "", "username", "password" );
}
@Test( expected = NirvanixStorageConnector.RuleException.class )
public void testGetSessionUsernameNullThrowsRuleException() throws Exception {
defaultTester.getSession( "appKey", null, "password" );
}
@Test( expected = NirvanixStorageConnector.RuleException.class )
public void testGetSessionUsernameEmptyThrowsRuleException() throws Exception {
defaultTester.getSession( "appKey", "", "password" );
}
@Test( expected = NirvanixStorageConnector.RuleException.class )
public void testGetSessionPasswordNullThrowsRuleException() throws Exception {
defaultTester.getSession( "appKey", "username", null );
}
@Test( expected = NirvanixStorageConnector.RuleException.class )
public void testGetSessionPasswordEmptyThrowsRuleException() throws Exception {
defaultTester.getSession( "appKey", "username", "" );
}
@Test( expected = NirvanixStorageConnector.RuleException.class )
public void testPutFileFilenameNull() throws Exception {
defaultTester.putFile( null, "destFolderPath", null, new ByteArrayInputStream( "testdata".getBytes() ), "sessionToken",
"testdata".getBytes().length );
}
@Test( expected = NirvanixStorageConnector.RuleException.class )
public void testPutFileFilenameEmpty() throws Exception {
defaultTester.putFile( "", "destFolderPath", null, new ByteArrayInputStream( "testdata".getBytes() ), "sessionToken",
"testdata".getBytes().length );
}
@Test( expected = NirvanixStorageConnector.RuleException.class )
public void testPutFileDestPathNull() throws Exception {
defaultTester.putFile( "filename", null, null, new ByteArrayInputStream( "testdata".getBytes() ), "sessionToken",
"testdata".getBytes().length );
}
@Test( expected = NirvanixStorageConnector.RuleException.class )
public void testPutFileDestPathEmpty() throws Exception {
defaultTester.putFile( "filename", "", null, new ByteArrayInputStream( "testdata".getBytes() ), "sessionToken",
"testdata".getBytes().length );
}
@Test( expected = NirvanixStorageConnector.RuleException.class )
public void testPutFileSessionTokenNull() throws Exception {
defaultTester.putFile( "filename", "destFolderPath", null, new ByteArrayInputStream( "testdata".getBytes() ), null,
"testdata".getBytes().length );
}
@Test( expected = NirvanixStorageConnector.RuleException.class )
public void testPutFileSessionTokenEmpty() throws Exception {
defaultTester.putFile( "filename", "destFolderPath", null, new ByteArrayInputStream( "testdata".getBytes() ), "",
"testdata".getBytes().length );
}
@Test( expected = NirvanixStorageConnector.RuleException.class )
public void testPutFileDataNull() throws Exception {
defaultTester.putFile( "filename", "destFolderPath", null, null, "sessionToken", "testdata".getBytes().length );
}
@Test( expected = NirvanixStorageConnector.RuleException.class )
public void testGetMetadataFilenameNull() throws Exception {
defaultTester.getMetadata( null, "key", "sessionToken" );
}
@Test( expected = NirvanixStorageConnector.RuleException.class )
public void testGetMetadataFilenameEmpty() throws Exception {
defaultTester.getMetadata( "", "key", "sessionToken" );
}
@Test( expected = NirvanixStorageConnector.RuleException.class )
public void testGetMetadataKeyNull() throws Exception {
defaultTester.getMetadata( "", null, "sessionToken" );
}
@Test( expected = NirvanixStorageConnector.RuleException.class )
public void testGetMetadataKeyEmpty() throws Exception {
defaultTester.getMetadata( "", "", "sessionToken" );
}
@Test( expected = NirvanixStorageConnector.RuleException.class )
public void testGetMetadataSessionTokenNull() throws Exception {
defaultTester.getMetadata( "filename", "key", null );
}
@Test( expected = NirvanixStorageConnector.RuleException.class )
public void testGetMetadataSessionTokenEmpty() throws Exception {
defaultTester.getMetadata( "filename", "key", "" );
}
@Test( expected = NirvanixStorageConnector.RuleException.class )
public void testGetMetadataMapFilenameNull() throws Exception {
defaultTester.getMetadata( null, "sessionToken" );
}
@Test( expected = NirvanixStorageConnector.RuleException.class )
public void testGetMetadataMapFilenameEmpty() throws Exception {
defaultTester.getMetadata( "", "sessionToken" );
}
@Test( expected = NirvanixStorageConnector.RuleException.class )
public void testGetMetadataMapSessionTokenNull() throws Exception {
defaultTester.getMetadata( "filename", null );
}
@Test( expected = NirvanixStorageConnector.RuleException.class )
public void testGetMetadataMapSessionTokenEmpty() throws Exception {
defaultTester.getMetadata( "filename", "" );
}
@Test( expected = NirvanixStorageConnector.RuleException.class )
public void testGetFileFileIdNull() throws Exception {
defaultTester.getFile( null, "sessionToken" );
}
@Test( expected = NirvanixStorageConnector.RuleException.class )
public void testGetFileFileIdEmpty() throws Exception {
defaultTester.getFile( "", "sessionToken" );
}
@Test( expected = NirvanixStorageConnector.RuleException.class )
public void testGetFileSessionTokenNull() throws Exception {
defaultTester.getFile( "filename", null );
}
@Test( expected = NirvanixStorageConnector.RuleException.class )
public void testGetFileSessionTokenEmpty() throws Exception {
defaultTester.getFile( "filename", "" );
}
@Test( expected = NirvanixStorageConnector.RuleException.class )
public void testDeleteFileIdNull() throws Exception {
defaultTester.delete( null, "sessionToken" );
}
@Test( expected = NirvanixStorageConnector.RuleException.class )
public void testDeleteFileIdEmpty() throws Exception {
defaultTester.delete( "", "sessionToken" );
}
@Test( expected = NirvanixStorageConnector.RuleException.class )
public void testDeleteSessionTokenNull() throws Exception {
defaultTester.delete( "filename", null );
}
@Test( expected = NirvanixStorageConnector.RuleException.class )
public void testDeleteSessionTokenEmpty() throws Exception {
defaultTester.delete( "filename", "" );
}
// Helper stub used in many test methods to return expected responses
class NirvanixStorageConnectorStub extends NirvanixStorageConnector {
String expectedGetResult;
String expectedPutResult;
public NirvanixStorageConnectorStub( String apiHostname, String dataTransferProtocol ) throws SystemException {
super();
this.setApiHostname( "validApiHostname" );
this.initialization();
}
void setExpectedGetResponse( String expected ) {
this.expectedGetResult = expected;
}
void setExpectedPutResponse( String expected ) {
this.expectedPutResult = expected;
}
@Override
protected String executeHttp( URIBuilder uriBuilder ) throws ClientProtocolException, IOException {
return expectedGetResult;
}
@Override
protected String executeHttpPost( HttpUriRequest method ) throws ClientProtocolException, IOException {
return expectedPutResult;
}
}
} // end class