Skip to content

Commit e7638de

Browse files
feat: Add unit tests for PAR (Pushed Authorization Request) flow in PARCodeManager
1 parent 6b9dc1c commit e7638de

File tree

1 file changed

+185
-0
lines changed

1 file changed

+185
-0
lines changed
Lines changed: 185 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,185 @@
1+
package com.auth0.android.provider
2+
3+
import android.app.Activity
4+
import android.content.Intent
5+
import android.net.Uri
6+
import com.auth0.android.Auth0
7+
import com.auth0.android.authentication.AuthenticationException
8+
import com.auth0.android.callback.Callback
9+
import com.auth0.android.provider.WebAuthProvider.par
10+
import com.auth0.android.provider.WebAuthProvider.resume
11+
import com.auth0.android.request.internal.ThreadSwitcherShadow
12+
import com.auth0.android.result.AuthorizationCode
13+
import com.nhaarman.mockitokotlin2.*
14+
import org.hamcrest.CoreMatchers.`is`
15+
import org.hamcrest.CoreMatchers.notNullValue
16+
import org.hamcrest.MatcherAssert.assertThat
17+
import org.junit.Assert
18+
import org.junit.Before
19+
import org.junit.Test
20+
import org.junit.runner.RunWith
21+
import org.mockito.ArgumentMatchers
22+
import org.mockito.Mock
23+
import org.mockito.Mockito
24+
import org.mockito.MockitoAnnotations
25+
import org.robolectric.Robolectric
26+
import org.robolectric.RobolectricTestRunner
27+
import org.robolectric.annotation.Config
28+
29+
@RunWith(RobolectricTestRunner::class)
30+
@Config(shadows = [ThreadSwitcherShadow::class])
31+
public class PARCodeManagerTest {
32+
33+
@Mock
34+
private lateinit var callback: Callback<AuthorizationCode, AuthenticationException>
35+
36+
private lateinit var activity: Activity
37+
private lateinit var account: Auth0
38+
39+
private val authCodeCaptor: KArgumentCaptor<AuthorizationCode> = argumentCaptor()
40+
private val authExceptionCaptor: KArgumentCaptor<AuthenticationException> = argumentCaptor()
41+
private val intentCaptor: KArgumentCaptor<Intent> = argumentCaptor()
42+
43+
private companion object {
44+
private const val DOMAIN = "samples.auth0.com"
45+
private const val CLIENT_ID = "test-client-id"
46+
private const val REQUEST_URI = "urn:ietf:params:oauth:request_uri:6esc_11ACC5bwc014ltc14eY22c"
47+
private const val AUTH_CODE = "test-authorization-code"
48+
}
49+
50+
@Before
51+
public fun setUp() {
52+
MockitoAnnotations.openMocks(this)
53+
activity = Mockito.spy(Robolectric.buildActivity(Activity::class.java).get())
54+
account = Auth0.getInstance(CLIENT_ID, DOMAIN)
55+
56+
// Prevent CustomTabService from being bound to Test environment
57+
Mockito.doReturn(false).`when`(activity).bindService(
58+
any(),
59+
any(),
60+
ArgumentMatchers.anyInt()
61+
)
62+
BrowserPickerTest.setupBrowserContext(
63+
activity,
64+
listOf("com.auth0.browser"),
65+
null,
66+
null
67+
)
68+
}
69+
70+
@Test
71+
public fun shouldStartPARFlowWithCorrectAuthorizeUri() {
72+
par(account)
73+
.start(activity, REQUEST_URI, callback)
74+
75+
Assert.assertNotNull(WebAuthProvider.managerInstance)
76+
77+
verify(activity).startActivity(intentCaptor.capture())
78+
val uri = intentCaptor.firstValue.getParcelableExtra<Uri>(AuthenticationActivity.EXTRA_AUTHORIZE_URI)
79+
80+
assertThat(uri, `is`(notNullValue()))
81+
assertThat(uri?.scheme, `is`("https"))
82+
assertThat(uri?.host, `is`(DOMAIN))
83+
assertThat(uri?.path, `is`("/authorize"))
84+
assertThat(uri?.getQueryParameter("client_id"), `is`(CLIENT_ID))
85+
assertThat(uri?.getQueryParameter("request_uri"), `is`(REQUEST_URI))
86+
}
87+
88+
@Test
89+
public fun shouldResumeWithValidCode() {
90+
par(account)
91+
.start(activity, REQUEST_URI, callback)
92+
93+
verify(activity).startActivity(intentCaptor.capture())
94+
95+
// Create callback intent with code
96+
val intent = createAuthIntent("code=$AUTH_CODE")
97+
98+
Assert.assertTrue(resume(intent))
99+
100+
verify(callback).onSuccess(authCodeCaptor.capture())
101+
val authCode = authCodeCaptor.firstValue
102+
assertThat(authCode, `is`(notNullValue()))
103+
assertThat(authCode.code, `is`(AUTH_CODE))
104+
}
105+
106+
@Test
107+
public fun shouldFailWithMissingCode() {
108+
par(account)
109+
.start(activity, REQUEST_URI, callback)
110+
111+
verify(activity).startActivity(intentCaptor.capture())
112+
113+
// Create callback intent without code
114+
val intent = createAuthIntent("foo=bar")
115+
116+
Assert.assertTrue(resume(intent))
117+
118+
verify(callback).onFailure(authExceptionCaptor.capture())
119+
val exception = authExceptionCaptor.firstValue
120+
assertThat(exception, `is`(notNullValue()))
121+
assertThat(exception.isAccessDenied, `is`(true))
122+
}
123+
124+
@Test
125+
public fun shouldFailWithErrorResponse() {
126+
par(account)
127+
.start(activity, REQUEST_URI, callback)
128+
129+
verify(activity).startActivity(intentCaptor.capture())
130+
131+
// Create callback intent with error
132+
val intent = createAuthIntent("error=access_denied&error_description=User%20denied%20access")
133+
134+
Assert.assertTrue(resume(intent))
135+
136+
verify(callback).onFailure(authExceptionCaptor.capture())
137+
val exception = authExceptionCaptor.firstValue
138+
assertThat(exception, `is`(notNullValue()))
139+
assertThat(exception.getCode(), `is`("access_denied"))
140+
}
141+
142+
@Test
143+
public fun shouldHandleCanceledAuthentication() {
144+
par(account)
145+
.start(activity, REQUEST_URI, callback)
146+
147+
verify(activity).startActivity(intentCaptor.capture())
148+
149+
// Create canceled intent (null data)
150+
val intent = Intent()
151+
152+
Assert.assertTrue(resume(intent))
153+
154+
verify(callback).onFailure(authExceptionCaptor.capture())
155+
val exception = authExceptionCaptor.firstValue
156+
assertThat(exception, `is`(notNullValue()))
157+
assertThat(exception.isCanceled, `is`(true))
158+
}
159+
160+
@Test
161+
public fun shouldFailWhenNoBrowserAvailable() {
162+
// Setup context without browser
163+
BrowserPickerTest.setupBrowserContext(
164+
activity,
165+
emptyList(),
166+
null,
167+
null
168+
)
169+
170+
par(account)
171+
.start(activity, REQUEST_URI, callback)
172+
173+
verify(callback).onFailure(authExceptionCaptor.capture())
174+
val exception = authExceptionCaptor.firstValue
175+
assertThat(exception, `is`(notNullValue()))
176+
assertThat(exception.isBrowserAppNotAvailable, `is`(true))
177+
}
178+
179+
private fun createAuthIntent(queryString: String): Intent {
180+
val uri = Uri.parse("https://$DOMAIN/android/com.auth0.test/callback?$queryString")
181+
return Intent().apply {
182+
data = uri
183+
}
184+
}
185+
}

0 commit comments

Comments
 (0)