-
Notifications
You must be signed in to change notification settings - Fork 617
FIS getAuthToken implementation. #769
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 7 commits
c54292e
b4b6ba8
b8bfc1a
7a2a112
42c20f6
46d936c
48fd7fe
82fb69e
fe04e15
e91cb7e
8569952
b74b163
58a30e3
96e30c2
9e3e34d
824cb63
90673c3
f5b4f8c
3e29941
75f2581
91e49bc
4578880
7d19b50
fe04884
f1d1d37
ec65040
396d273
81b4aeb
3932d3b
8d4d811
3f44b77
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -15,6 +15,7 @@ | |
package com.google.firebase.installations; | ||
|
||
import static com.google.common.truth.Truth.assertWithMessage; | ||
import static com.google.firebase.installations.FisAndroidTestConstants.TEST_API_KEY; | ||
import static com.google.firebase.installations.FisAndroidTestConstants.TEST_APP_ID_1; | ||
import static com.google.firebase.installations.FisAndroidTestConstants.TEST_AUTH_TOKEN; | ||
import static com.google.firebase.installations.FisAndroidTestConstants.TEST_AUTH_TOKEN_2; | ||
|
@@ -30,12 +31,11 @@ | |
import static org.junit.Assert.fail; | ||
import static org.mockito.ArgumentMatchers.any; | ||
import static org.mockito.ArgumentMatchers.anyString; | ||
import static org.mockito.Mockito.doAnswer; | ||
import static org.mockito.Mockito.times; | ||
import static org.mockito.Mockito.verify; | ||
import static org.mockito.Mockito.when; | ||
|
||
import android.util.Log; | ||
import androidx.annotation.NonNull; | ||
import androidx.test.core.app.ApplicationProvider; | ||
import androidx.test.runner.AndroidJUnit4; | ||
import com.google.android.gms.common.util.Clock; | ||
|
@@ -61,6 +61,8 @@ | |
import org.junit.runners.MethodSorters; | ||
import org.mockito.Mock; | ||
import org.mockito.MockitoAnnotations; | ||
import org.mockito.internal.stubbing.answers.AnswersWithDelay; | ||
import org.mockito.internal.stubbing.answers.Returns; | ||
|
||
/** | ||
* Instrumented test, which will execute on an Android device. | ||
|
@@ -131,7 +133,7 @@ public void setUp() throws FirebaseInstallationServiceException { | |
new FirebaseOptions.Builder() | ||
.setApplicationId(TEST_APP_ID_1) | ||
.setProjectId(TEST_PROJECT_ID) | ||
.setApiKey("api_key") | ||
.setApiKey(TEST_API_KEY) | ||
.build()); | ||
persistedFid = new PersistedFid(firebaseApp); | ||
when(backendClientReturnsOk.createFirebaseInstallation( | ||
|
@@ -264,11 +266,12 @@ public void testGetId_PersistedFidError_BackendOk() throws InterruptedException | |
// Expect exception | ||
try { | ||
Tasks.await(firebaseInstallations.getId()); | ||
fail(); | ||
fail("Could not update local storage."); | ||
} catch (ExecutionException expected) { | ||
Throwable cause = expected.getCause(); | ||
ankitaj224 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
assertWithMessage("Exception class doesn't match") | ||
.that(cause) | ||
.that(expected) | ||
.hasCauseThat() | ||
.isInstanceOf(FirebaseInstallationsException.class); | ||
assertWithMessage("Exception status doesn't match") | ||
.that(((FirebaseInstallationsException) cause).getStatus()) | ||
|
@@ -305,11 +308,12 @@ public void testGetAuthToken_PersistedFidError_failure() throws Exception { | |
try { | ||
Tasks.await( | ||
firebaseInstallations.getAuthToken(FirebaseInstallationsApi.DO_NOT_FORCE_REFRESH)); | ||
fail(); | ||
fail("Could not update local storage."); | ||
} catch (ExecutionException expected) { | ||
Throwable cause = expected.getCause(); | ||
assertWithMessage("Exception class doesn't match") | ||
.that(cause) | ||
.that(expected) | ||
.hasCauseThat() | ||
.isInstanceOf(FirebaseInstallationsException.class); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Truth has built-in support for unpacking exceptions assertWithMessage("w/e").that(expected).hasCauseThat().isInstanceOf(FirebaseInstallationsException.class) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done. Did you also want the status to be changed? I tried but dint find a easy way. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You'd have to create a custom truth subject for that and tbh I don't think it's worth it atm |
||
assertWithMessage("Exception status doesn't match") | ||
.that(((FirebaseInstallationsException) cause).getStatus()) | ||
|
@@ -351,6 +355,9 @@ public void testGetAuthToken_expiredAuthToken_fetchedNewTokenFromFIS() throws Ex | |
|
||
@Test | ||
public void testGetAuthToken_unregisteredFid_fetchedNewTokenFromFIS() throws Exception { | ||
// Using mockPersistedFid to ensure the order of returning persistedFidEntry. This test | ||
// validates that getAuthToken calls getId to ensure FID registration and returns a valid auth | ||
// token. | ||
when(mockPersistedFid.readPersistedFidEntryValue()) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. imo this test could do with some comments explaining what you're testing There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done. PTAL. |
||
.thenReturn(UNREGISTERED_FID_ENTRY, REGISTERED_FID_ENTRY); | ||
FirebaseInstallations firebaseInstallations = | ||
|
@@ -386,11 +393,12 @@ public void testGetAuthToken_serverError_failure() throws Exception { | |
// Expect exception | ||
try { | ||
Tasks.await(firebaseInstallations.getAuthToken(FirebaseInstallationsApi.FORCE_REFRESH)); | ||
fail(); | ||
fail("getAuthToken() failed due to Server Error."); | ||
} catch (ExecutionException expected) { | ||
Throwable cause = expected.getCause(); | ||
assertWithMessage("Exception class doesn't match") | ||
.that(cause) | ||
.that(expected) | ||
.hasCauseThat() | ||
.isInstanceOf(FirebaseInstallationsException.class); | ||
assertWithMessage("Exception status doesn't match") | ||
.that(((FirebaseInstallationsException) cause).getStatus()) | ||
|
@@ -399,7 +407,11 @@ public void testGetAuthToken_serverError_failure() throws Exception { | |
} | ||
|
||
@Test | ||
public void testGetAuthToken_multipleCalls_fetchedNewTokenOnce() throws Exception { | ||
public void testGetAuthToken_multipleCallsDoNotForceRefresh_fetchedNewTokenOnce() | ||
throws Exception { | ||
// Using mockPersistedFid to ensure the order of returning persistedFidEntry to 2 tasks | ||
ankitaj224 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
// triggered simultaneously. Task2 waits for Task1 to complete.On Task1 completion, task2 reads | ||
ankitaj224 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
// the UPDATED_AUTH_TOKEN_FID_ENTRY by Task1 on execution. | ||
when(mockPersistedFid.readPersistedFidEntryValue()) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. add a comment explaining what this means |
||
.thenReturn( | ||
EXPIRED_AUTH_TOKEN_ENTRY, | ||
|
@@ -425,67 +437,54 @@ public void testGetAuthToken_multipleCalls_fetchedNewTokenOnce() throws Exceptio | |
.that(task2.getResult().getToken()) | ||
.isEqualTo(TEST_AUTH_TOKEN_2); | ||
verify(backendClientReturnsOk, times(1)) | ||
.generateAuthToken(anyString(), anyString(), anyString(), anyString()); | ||
.generateAuthToken(TEST_API_KEY, TEST_FID_1, TEST_PROJECT_ID, TEST_REFRESH_TOKEN); | ||
} | ||
|
||
@Test | ||
public void testGetAuthToken_multipleCallsForceRefresh_fetchedNewTokenTwice() throws Exception { | ||
when(mockPersistedFid.readPersistedFidEntryValue()).thenReturn(REGISTERED_FID_ENTRY); | ||
// Use a custom ServiceClient to mock the network calls ensuring task1 is not completed | ||
// before task2. Hence, we can test multiple calls to getAUthToken() and verify task2 waits for | ||
// task1 to complete. | ||
ServiceClient serviceClient = new ServiceClient(); | ||
// Use a mock ServiceClient for network calls with delay(1000ms) to ensure first task is not | ||
// completed before the second task starts. Hence, we can test multiple calls to getAUthToken() | ||
ankitaj224 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
// and verify one task waits for another task to complete. | ||
|
||
doAnswer( | ||
new AnswersWithDelay( | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm not 100% sure here, but I think we're supposed to use the AdditionalAnswers class to create these instead of reaching into the mockito internal package: https://static.javadoc.io/org.mockito/mockito-core/2.9.0/org/mockito/AdditionalAnswers.html There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Thanks for the link. I dint realize it was internal package. Updated. |
||
1000, | ||
new Returns( | ||
ankitaj224 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
InstallationTokenResult.builder() | ||
.setToken(TEST_AUTH_TOKEN_3) | ||
.setTokenExpirationInSecs(TEST_TOKEN_EXPIRATION_TIMESTAMP) | ||
.build()))) | ||
.doAnswer( | ||
new AnswersWithDelay( | ||
1000, | ||
new Returns( | ||
InstallationTokenResult.builder() | ||
.setToken(TEST_AUTH_TOKEN_4) | ||
.setTokenExpirationInSecs(TEST_TOKEN_EXPIRATION_TIMESTAMP) | ||
.build()))) | ||
.when(backendClientReturnsOk) | ||
.generateAuthToken(anyString(), anyString(), anyString(), anyString()); | ||
|
||
FirebaseInstallations firebaseInstallations = | ||
new FirebaseInstallations( | ||
mockClock, executor, firebaseApp, serviceClient, mockPersistedFid, mockUtils); | ||
mockClock, executor, firebaseApp, backendClientReturnsOk, mockPersistedFid, mockUtils); | ||
|
||
// Call getAuthToken multiple times with FORCE_REFRESH option. Also, sleep for 500ms in between | ||
// the calls to ensure tasks are called in order. | ||
// Call getAuthToken multiple times with FORCE_REFRESH option. | ||
Task<InstallationTokenResult> task1 = | ||
firebaseInstallations.getAuthToken(FirebaseInstallationsApi.FORCE_REFRESH); | ||
Thread.sleep(500); | ||
Task<InstallationTokenResult> task2 = | ||
firebaseInstallations.getAuthToken(FirebaseInstallationsApi.FORCE_REFRESH); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. we shouldn't have to sleep in tests. Can we somehow use the injected to ensure these tasks are run? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I wanted the sleep to ensure task executed in order. But I removed it, instead of validating on both expected values There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why don't they execute in order at the moment? Is it because the executor has 4 threads? Could we use a single threaded executor to make this deterministic? |
||
Tasks.await(Tasks.whenAllComplete(task1, task2)); | ||
|
||
// As we cannot ensure which task got executed first, verifying with both expected values | ||
assertWithMessage("Persisted Auth Token doesn't match") | ||
.that(task1.getResult().getToken()) | ||
.isEqualTo(TEST_AUTH_TOKEN_3); | ||
.isAnyOf(TEST_AUTH_TOKEN_3, TEST_AUTH_TOKEN_4); | ||
assertWithMessage("Persisted Auth Token doesn't match") | ||
.that(task2.getResult().getToken()) | ||
.isEqualTo(TEST_AUTH_TOKEN_4); | ||
} | ||
|
||
class ServiceClient extends FirebaseInstallationServiceClient { | ||
|
||
private boolean secondCall; | ||
|
||
@Override | ||
@NonNull | ||
public InstallationTokenResult generateAuthToken( | ||
@NonNull String apiKey, | ||
@NonNull String fid, | ||
@NonNull String projectID, | ||
@NonNull String refreshToken) | ||
throws FirebaseInstallationServiceException { | ||
try { | ||
Thread.sleep(2000); | ||
} catch (InterruptedException e) { | ||
Log.e("InterruptedException", e.getMessage()); | ||
} | ||
|
||
if (!secondCall) { | ||
secondCall = true; | ||
return InstallationTokenResult.builder() | ||
.setToken(TEST_AUTH_TOKEN_3) | ||
.setTokenExpirationInSecs(TEST_TOKEN_EXPIRATION_TIMESTAMP) | ||
.build(); | ||
} | ||
|
||
return InstallationTokenResult.builder() | ||
.setToken(TEST_AUTH_TOKEN_4) | ||
.setTokenExpirationInSecs(TEST_TOKEN_EXPIRATION_TIMESTAMP) | ||
.build(); | ||
} | ||
.isAnyOf(TEST_AUTH_TOKEN_4, TEST_AUTH_TOKEN_3); | ||
verify(backendClientReturnsOk, times(2)) | ||
.generateAuthToken(TEST_API_KEY, TEST_FID_1, TEST_PROJECT_ID, TEST_REFRESH_TOKEN); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -21,18 +21,18 @@ | |
import java.util.concurrent.TimeUnit; | ||
|
||
final class AwaitListener implements OnCompleteListener<Void> { | ||
private final CountDownLatch mLatch = new CountDownLatch(1); | ||
private final CountDownLatch latch = new CountDownLatch(1); | ||
|
||
public void onSuccess() { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this isn't actually used, right? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Its used. If the status is REGISTERED, instead of waiting for 10s, I am using this method to update the awaitlistener. FirebaseInstalllations.java line 261 |
||
mLatch.countDown(); | ||
latch.countDown(); | ||
} | ||
|
||
public boolean await(long timeout, TimeUnit unit) throws InterruptedException { | ||
return mLatch.await(timeout, unit); | ||
return latch.await(timeout, unit); | ||
} | ||
|
||
@Override | ||
public void onComplete(@NonNull Task<Void> task) { | ||
mLatch.countDown(); | ||
latch.countDown(); | ||
} | ||
} |
Uh oh!
There was an error while loading. Please reload this page.