Skip to content
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

Automate authenticator oauth tests #432

Merged
merged 31 commits into from
May 29, 2024

Conversation

hud10837
Copy link
Collaborator

@hud10837 hud10837 commented May 16, 2024

Adds tests for OAuth sign in. This covers the manual test cases 1 and 2. Like the ServerTrustTests, the test logic is generally implemented in a single util function that is called by each test along with the corresponding test action (eg. enter credentials and sign in)

I found that because we are calling handleArcGISAuthenticationChallenge directly, the tests were catching any OperationCancelledExceptions that were thrown by that method. It seems like this was an oversight so I have changed the implementation and it seems to be working (at least, the tests are passing)

Currently the signIn tests will fail because they don't have working credentials until we come up with a solution for test credentials in the toolkit.

This also uses the microapp's oauth configuration as well as the sample repo's oauth configuration in order to avoid a launcher disambiguation dialog in the tests. This is something we should confirm with @shubham7109 is okay or we should create new redirect urls and clientIds.

The tests use two new OAuth clientId/redirectUrls.

Comment on lines +52 to +55
packagingOptions {
exclude("META-INF/LICENSE-notice.md")
exclude("META-INF/LICENSE.md")
}
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I couldn't compile after adding uiautomator and mockk without this, but I'm not sure if that's just a local issue

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that change is correct.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Test Case 1

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Test Case 2

Comment on lines 183 to 189
}.getOrElse {
if (it is OperationCancelledException) {
return ArcGISAuthenticationChallengeResponse.Cancel
} else {
return ArcGISAuthenticationChallengeResponse.ContinueAndFailWithError(it)
}
}
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should discuss this. Because the tests use handleArcGISAuthenticationChallenge() directly, any exceptions that get thrown are thrown into the test. Normally the caller of handleArcGISAuthenticationChallenge will handle the exceptions but it seems to me that we should be handling it here anyway.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think this change is needed. Any exception thrown, would be handled in the SDK's AuthenticationChallengeQueue. In the test code, since we bypass that code, we should just catch the exception there.

@hud10837 hud10837 marked this pull request as ready for review May 16, 2024 13:12
Copy link
Collaborator

@gunt0001 gunt0001 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@hud10837 looks good, a few comments.


<data
android:host="auth"
android:scheme="kotlin-authentication-test-1" />
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

@Before
fun signOut() {
runBlocking {
ArcGISEnvironment.authenticationManager.signOut()
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Call sign out also in @After?

@After
fun closeBrowser() {
try {
UiDevice.getInstance(InstrumentationRegistry.getInstrumentation()).pressBack()
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this required? As long as the test uses a CCT, it should close automatically at the end of each test?

enterCredentialsOnBrowser("username", "password", composeTestRule.activity)
clickByText("Sign In")
}.await()
assert(response is ArcGISAuthenticationChallengeResponse.ContinueWithCredential)

This comment was marked as resolved.

val composeTestRule = createAndroidComposeRule<ComponentActivity>()

private val arcGISOnline = "https://arcgis.com"
private val authenticatorState = AuthenticatorState().apply {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this state object should be created locally within the scope of testOAuthChallengeWithStateRestoration(), so that a new Authenticator() call is passed a new instance of AuthenticatorState.

@After
fun closeBrowser() {
try {
UiDevice.getInstance(InstrumentationRegistry.getInstrumentation()).pressBack()
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we don't do this, does this cause any issues?

@Before
fun signOut() {
runBlocking {
ArcGISEnvironment.authenticationManager.signOut()

This comment was marked as resolved.

enterCredentialsOnBrowser("username", "password", composeTestRule.activity)
clickByText("Sign In")
}.await()
assert(response is ArcGISAuthenticationChallengeResponse.ContinueWithCredential)

This comment was marked as resolved.

@@ -18,5 +18,6 @@
-->

<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<uses-permission android:name="android.permission.INTERNET" />
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The toolkit should not set any permissions in its manifest. This is the responsibility of the user.

Comment on lines 183 to 189
}.getOrElse {
if (it is OperationCancelledException) {
return ArcGISAuthenticationChallengeResponse.Cancel
} else {
return ArcGISAuthenticationChallengeResponse.ContinueAndFailWithError(it)
}
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think this change is needed. Any exception thrown, would be handled in the SDK's AuthenticationChallengeQueue. In the test code, since we bypass that code, we should just catch the exception there.

@hud10837 hud10837 requested a review from gunt0001 May 21, 2024 14:22
@hud10837
Copy link
Collaborator Author

Thanks @gunt0001 I believe I have addressed all of your comments

Comment on lines +52 to +55
packagingOptions {
exclude("META-INF/LICENSE-notice.md")
exclude("META-INF/LICENSE.md")
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that change is correct.

@Test
fun successfulSignIn() = runTest {
val response = testOAuthChallengeWithStateRestoration {
InstrumentationRegistry.getInstrumentation().context.startActivity(getSuccessfulRedirectIntent("kotlin-authentication-test-1://auth"))
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you add a comment here, something along the lines of...

Suggested change
InstrumentationRegistry.getInstrumentation().context.startActivity(getSuccessfulRedirectIntent("kotlin-authentication-test-1://auth"))
// When the OAuth sign in dialog shows up, we simulate successful sign in by launching an intent with the redirect URl, which otherwise would be sent from the Portal server, but would require valid credentials
InstrumentationRegistry.getInstrumentation().context.startActivity(getSuccessfulRedirectIntent("kotlin-authentication-test-1://auth"))

): Deferred<Result<ArcGISAuthenticationChallengeResponse>> {
ArcGISEnvironment.configureArcGISHttpClient {
interceptTokenRequests()
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suggest to move this part of setting up an interceptor out of this function and call this individually as part of each test function. I don't think it belongs into this function here.

oAuthUserConfiguration = OAuthUserConfiguration(
"https://arcgis.com",
// This client ID is for demo purposes only. For use of the Authenticator in your own app,
// // create your own client ID. For more info see: https://developers.arcgis.com/documentation/mapping-apis-and-services/security/tutorials/register-your-application/
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comment needs formating

Suggested change
// // create your own client ID. For more info see: https://developers.arcgis.com/documentation/mapping-apis-and-services/security/tutorials/register-your-application/
// create your own client ID. For more info see: https://developers.arcgis.com/documentation/mapping-apis-and-services/security/tutorials/register-your-application/

modifier = Modifier.testTag("Authenticator")
)
}
// isse the OAuth challenge
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
// isse the OAuth challenge
// issue the OAuth challenge

*
* @since 200.5.0
*/
fun ArcGISHttpClient.Builder.interceptTokenRequests() {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nitpick naming suggestion:

Suggested change
fun ArcGISHttpClient.Builder.interceptTokenRequests() {
fun ArcGISHttpClient.Builder.setupTokenRequestInterceptor() {

*
* @since 200.5.0
*/
fun Request.getFakeTokenResponse(): Response =
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nitpick naming suggestion:

Suggested change
fun Request.getFakeTokenResponse(): Response =
fun Request.createFakeTokenResponse(): Response =

addHeader("transfer-encoding", "chunked")
addHeader("vary", "X-Esri-Authorization")
addHeader("x-content-type-options", "nosniff")
}.build()
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

missing newline

@@ -0,0 +1,80 @@
package com.arcgismaps.toolkit.authentication
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

missing copyright info

* found.
*
* @param packageId the view's package Id to wait for.
* @since 200.4.0
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Needs updating also in other places in this file...

Suggested change
* @since 200.4.0
* @since 200.5.0

Copy link
Collaborator

@gunt0001 gunt0001 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approving so this can move to second review once comments have been addressed.

@alan-edi alan-edi self-assigned this May 28, 2024
Copy link
Collaborator

@alan-edi alan-edi left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@hud10837 Looks good, just a few minor comments to consider

oAuthUserConfiguration = OAuthUserConfiguration(
"https://arcgis.com",
// This client ID is for demo purposes only. For use of the Authenticator in your own app,
// create your own client ID. For more info see: https://developers.arcgis.com/documentation/mapping-apis-and-services/security/tutorials/register-your-application/
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Newline before the URL, to avoid line being unnecessarily long?

oAuthUserConfiguration = OAuthUserConfiguration(
"https://arcgis.com/",
// This client ID is for demo purposes only. For use of the Authenticator in your own app,
// create your own client ID. For more info see: https://developers.arcgis.com/documentation/mapping-apis-and-services/security/tutorials/register-your-application/
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Newline before the URL?



/**
* Waits for the View with the matching package to be visible. Throws an error if the view can't be
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

package -> [packageId] and delete the @param below?

*
* @since 200.5.0
*/
fun UiDevice.enterCredentialsOnBrowser(username: String, password: String, activity: Activity) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems this function, and the functions below that it calls, isn't used, but I guess you expect to use it later when we come up with a solution for test credentials in the toolkit?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I will remove them for now, can always return to this PR to copy them.

@alan-edi
Copy link
Collaborator

Oh, one other thing - there are no tests for the following sub-cases, but I guess you have good reasons:

  • 1.2 Incorrect Credentials - maybe do it later when we come up with a solution for test credentials in the toolkit?
  • 1.5 Pressing the Close Button - is it practicable to automate that?
  • 1.6 Press the Home Button - again, is it practicable?

@hud10837
Copy link
Collaborator Author

@alan-edi thanks for the review. Regarding those test cases:

  • 1.2 Incorrect Credentials - I'm not sure there's any value in testing this (also true for the manual tests) because there is no interaction with the Authenticator when you enter invalid credentials -- everything continues to happen in the browser, and if you enter the wrong credentials too many times, it keeps the browser open and locks you out for 15 minutes, so there is no "invalid credentials" redirect back to the app.
  • 1.5 Pressing the Close Button - It's possible to automate this, but a bit difficult, and the behavior should be identical to the back button. But I am happy to add this test case if you think it's a good idea
  • 1.6 Press the Home Button - The goal of this in the manual tests was to test the activity being destroyed and put in the background, then recreated, which also happens during configuration changes. I think this is very difficult to automate though, so I think that testing only device rotation is sufficient (as it should be the same behavior anyway) What do you think?

@alan-edi
Copy link
Collaborator

@hud10837 I'm happy with what you say re test cases 1.2, 1.5 and 1.6, thanks

@alan-edi alan-edi removed their assignment May 29, 2024
@hud10837
Copy link
Collaborator Author

Thanks @alan-edi @gunt0001 !

@hud10837 hud10837 merged commit 0de21d1 into v.next May 29, 2024
@hud10837 hud10837 deleted the hud10837/automate-authenticator-oauth-tests branch May 29, 2024 08:58
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants