|
29 | 29 |
|
30 | 30 | import android.app.Activity; |
31 | 31 | import android.content.Intent; |
| 32 | +import android.net.Uri; |
32 | 33 | import android.os.Bundle; |
33 | 34 |
|
34 | 35 | import com.spotify.sdk.android.auth.AuthorizationRequest; |
@@ -126,4 +127,95 @@ public void shouldReturnResultOkForTechnicalErrors() { |
126 | 127 | assertCompletion(setup, response, Activity.RESULT_OK); |
127 | 128 | } |
128 | 129 |
|
| 130 | + @Test |
| 131 | + public void shouldDetectCancellationAfterRecreationWithoutResponse() { |
| 132 | + Activity context = Robolectric.buildActivity(Activity.class).create().get(); |
| 133 | + |
| 134 | + PKCEInformation pkceInfo = PKCEInformation.sha256("test_verifier", "test_challenge"); |
| 135 | + AuthorizationRequest request = new AuthorizationRequest.Builder( |
| 136 | + "test", AuthorizationResponse.Type.TOKEN, "test://test") |
| 137 | + .setPkceInformation(pkceInfo) |
| 138 | + .build(); |
| 139 | + |
| 140 | + Bundle requestBundle = new Bundle(); |
| 141 | + requestBundle.putParcelable(LoginActivity.REQUEST_KEY, request); |
| 142 | + |
| 143 | + Intent intent = new Intent(context, LoginActivity.class); |
| 144 | + intent.putExtra(LoginActivity.EXTRA_AUTH_REQUEST, requestBundle); |
| 145 | + |
| 146 | + // Create and initialize the first activity (triggers authorize, sets authInProgress=true) |
| 147 | + ActivityController<LoginActivity> controller = buildActivity(LoginActivity.class, intent); |
| 148 | + shadowOf(controller.get()).setCallingActivity(context.getComponentName()); |
| 149 | + controller.create(); |
| 150 | + |
| 151 | + // Save instance state before "destruction" |
| 152 | + Bundle savedState = new Bundle(); |
| 153 | + controller.saveInstanceState(savedState); |
| 154 | + |
| 155 | + // Simulate recreation: new activity with same intent but no response URI |
| 156 | + Intent recreatedIntent = new Intent(context, LoginActivity.class); |
| 157 | + recreatedIntent.putExtra(LoginActivity.EXTRA_AUTH_REQUEST, requestBundle); |
| 158 | + |
| 159 | + ActivityController<LoginActivity> controller2 = buildActivity(LoginActivity.class, recreatedIntent); |
| 160 | + LoginActivity recreatedActivity = controller2.get(); |
| 161 | + ShadowActivity recreatedShadow = shadowOf(recreatedActivity); |
| 162 | + recreatedShadow.setCallingActivity(context.getComponentName()); |
| 163 | + controller2.create(savedState).start().resume(); |
| 164 | + |
| 165 | + // onResume should detect authInProgress=true with no handler and deliver CANCELLED |
| 166 | + assertTrue(recreatedActivity.isFinishing()); |
| 167 | + assertEquals(Activity.RESULT_CANCELED, recreatedShadow.getResultCode()); |
| 168 | + AuthorizationResponse response = recreatedShadow.getResultIntent() |
| 169 | + .getBundleExtra(LoginActivity.EXTRA_AUTH_RESPONSE) |
| 170 | + .getParcelable(LoginActivity.RESPONSE_KEY); |
| 171 | + assertEquals(AuthorizationResponse.Type.CANCELLED, response.getType()); |
| 172 | + } |
| 173 | + |
| 174 | + @Test |
| 175 | + public void shouldProcessResponseInIntentDataAfterRecreation() { |
| 176 | + Activity context = Robolectric.buildActivity(Activity.class).create().get(); |
| 177 | + |
| 178 | + PKCEInformation pkceInfo = PKCEInformation.sha256("test_verifier", "test_challenge"); |
| 179 | + AuthorizationRequest request = new AuthorizationRequest.Builder( |
| 180 | + "test", AuthorizationResponse.Type.TOKEN, "test://test") |
| 181 | + .setPkceInformation(pkceInfo) |
| 182 | + .build(); |
| 183 | + |
| 184 | + Bundle requestBundle = new Bundle(); |
| 185 | + requestBundle.putParcelable(LoginActivity.REQUEST_KEY, request); |
| 186 | + |
| 187 | + Intent intent = new Intent(context, LoginActivity.class); |
| 188 | + intent.putExtra(LoginActivity.EXTRA_AUTH_REQUEST, requestBundle); |
| 189 | + |
| 190 | + // Create and initialize the first activity |
| 191 | + ActivityController<LoginActivity> controller = buildActivity(LoginActivity.class, intent); |
| 192 | + shadowOf(controller.get()).setCallingActivity(context.getComponentName()); |
| 193 | + controller.create(); |
| 194 | + |
| 195 | + // Save instance state before "destruction" |
| 196 | + Bundle savedState = new Bundle(); |
| 197 | + controller.saveInstanceState(savedState); |
| 198 | + |
| 199 | + // Simulate recreation with a response URI in the intent |
| 200 | + // (redirect arrived while activity was destroyed) |
| 201 | + Intent recreatedIntent = new Intent(context, LoginActivity.class); |
| 202 | + recreatedIntent.putExtra(LoginActivity.EXTRA_AUTH_REQUEST, requestBundle); |
| 203 | + recreatedIntent.setData(Uri.parse("test://test?code=test_code")); |
| 204 | + |
| 205 | + ActivityController<LoginActivity> controller2 = buildActivity(LoginActivity.class, recreatedIntent); |
| 206 | + LoginActivity recreatedActivity = controller2.get(); |
| 207 | + ShadowActivity recreatedShadow = shadowOf(recreatedActivity); |
| 208 | + recreatedShadow.setCallingActivity(context.getComponentName()); |
| 209 | + controller2.create(savedState); |
| 210 | + |
| 211 | + // Response should be processed in onCreate, activity should finish with RESULT_OK |
| 212 | + assertTrue(recreatedActivity.isFinishing()); |
| 213 | + assertEquals(Activity.RESULT_OK, recreatedShadow.getResultCode()); |
| 214 | + AuthorizationResponse response = recreatedShadow.getResultIntent() |
| 215 | + .getBundleExtra(LoginActivity.EXTRA_AUTH_RESPONSE) |
| 216 | + .getParcelable(LoginActivity.RESPONSE_KEY); |
| 217 | + assertEquals(AuthorizationResponse.Type.CODE, response.getType()); |
| 218 | + assertEquals("test_code", response.getCode()); |
| 219 | + } |
| 220 | + |
129 | 221 | } |
0 commit comments