package com.klarna.ondemand; import android.util.Base64; import com.klarna.ondemand.crypto.Crypto; import com.klarna.ondemand.crypto.CryptoBase; import com.klarna.ondemand.crypto.CryptoFactory; import com.klarna.ondemand.crypto.SharedPreferencesCryptoImpl; import org.json.JSONException; import org.json.JSONObject; import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; import org.powermock.core.classloader.annotations.PowerMockIgnore; import org.powermock.core.classloader.annotations.PrepareForTest; import org.powermock.modules.junit4.rule.PowerMockRule; import org.robolectric.Robolectric; import org.robolectric.RobolectricTestRunner; import org.robolectric.annotation.Config; import java.security.InvalidKeyException; import java.security.NoSuchAlgorithmException; import java.security.PrivateKey; import java.security.SignatureException; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Matchers.anyString; import static org.mockito.Matchers.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; import static org.powermock.api.mockito.PowerMockito.mockStatic; @RunWith(RobolectricTestRunner.class) @Config(emulateSdk = 18) @PrepareForTest({CryptoFactory.class, CryptoBase.class}) @PowerMockIgnore({ "org.mockito.*", "org.robolectric.*", "android.*", "org.json.*" }) public class OriginProofTest { private static final String UUID_PATTERN = "[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}"; private android.content.Context context; @Rule public PowerMockRule rule = new PowerMockRule(); //region Before all @Before public void init() throws NoSuchAlgorithmException, InvalidKeyException, SignatureException { context = Robolectric.application.getApplicationContext(); } //endregion //region OriginProof @Test public void constructor_shouldReturnAbase64EncodedJsonInTheCorrectFormat() throws Exception { Crypto cryptoMock = mock(Crypto.class); when(cryptoMock.sign(anyString())).thenReturn("my_signature"); mockStatic(CryptoFactory.class); when(CryptoFactory.getInstance(context)).thenReturn(cryptoMock); OriginProof originProof = new OriginProof(3600, "SEK", "my_token", context); JSONObject originProofJson = getOriginProofJson(originProof); assertThat(originProofJson.getString("signature")).isEqualTo("my_signature"); assertOriginProofData(originProof, 3600, "SEK", "my_token"); } @Test public void constructor_shouldGenerateADifferentIdForEachOriginProof() throws Exception { OriginProof originProofA = new OriginProof(3600, "SEK", "my_token", context); JSONObject dataA = getDataJson(originProofA); OriginProof originProofB = new OriginProof(3600, "SEK", "my_token", context); JSONObject dataB = getDataJson(originProofB); assertThat(dataA.getString("id")).isNotEqualTo(dataB.getString("id")); } @Test(expected=RuntimeException.class) public void constructor_ShouldThrowExceptionWhenItCantGenerateSignature() throws Exception { Crypto cryptoMock = mock(Crypto.class); when(cryptoMock.sign(anyString())).thenThrow(new SignatureException()); when(CryptoFactory.getInstance(context)).thenReturn(cryptoMock); new OriginProof(3600, "SEK", "my_token", context); } //endregion //region OriginProof with a provided external private key @Test public void constructor_shouldReturnAbase64EncodedJsonInTheCorrectFormat_whenGivenExternalPrivateKey() throws Exception { PrivateKey externalPrivateKey = SharedPreferencesCryptoImpl.generateKeyPair().getPrivate(); mockStatic(CryptoBase.class); when(CryptoBase.sign(anyString(), eq(externalPrivateKey))).thenReturn("my_external_private_key_signature"); OriginProof originProof = new OriginProof(3600, "SEK", "my_token", externalPrivateKey); JSONObject originProofJson = getOriginProofJson(originProof); assertThat(originProofJson.getString("signature")).isEqualTo("my_external_private_key_signature"); assertOriginProofData(originProof, 3600, "SEK", "my_token"); } @Test public void constructor_shouldGenerateADifferentIdForEachOriginProof_whenGivenExternalPrivateKey() throws Exception { OriginProof originProofA = new OriginProof(3600, "SEK", "my_token", SharedPreferencesCryptoImpl.generateKeyPair().getPrivate()); JSONObject dataA = getDataJson(originProofA); OriginProof originProofB = new OriginProof(3600, "SEK", "my_token", SharedPreferencesCryptoImpl.generateKeyPair().getPrivate()); JSONObject dataB = getDataJson(originProofB); assertThat(dataA.getString("id")).isNotEqualTo(dataB.getString("id")); } @Test(expected=RuntimeException.class) public void constructor_ShouldThrowExceptionWhenItCantGenerateSignature_whenGivenExternalPrivateKey() throws Exception { PrivateKey externalPrivateKey = SharedPreferencesCryptoImpl.generateKeyPair().getPrivate(); mockStatic(CryptoBase.class); when(CryptoBase.sign(anyString(), eq(externalPrivateKey))).thenThrow(new SignatureException()); new OriginProof(3600, "SEK", "my_token", externalPrivateKey); } //endregion //region Helper methods private JSONObject getOriginProofJson(OriginProof originProof) throws JSONException { String decodedOriginProof = new String(Base64.decode(originProof.toString(), Base64.DEFAULT)); return new JSONObject(decodedOriginProof); } private JSONObject getDataJson(OriginProof originProof) throws JSONException { JSONObject originProofJson = getOriginProofJson(originProof); return new JSONObject(originProofJson.getString("data")); } private void assertOriginProofData(OriginProof originProof, int amount, String currency, String userToken) throws JSONException { JSONObject data = getDataJson(originProof); assertThat(data.getInt("amount")).isEqualTo(3600); assertThat(data.getString("currency")).isEqualTo("SEK"); assertThat(data.getString("user_token")).isEqualTo("my_token"); assertThat(data.getString("id")).matches(UUID_PATTERN); } //endregion }