Skip to content

Commit 9952fcc

Browse files
committed
actually add tests
1 parent 1ef5de2 commit 9952fcc

1 file changed

Lines changed: 295 additions & 0 deletions

File tree

Lines changed: 295 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,295 @@
1+
import io.github.jan.supabase.SupabaseClient
2+
import io.github.jan.supabase.SupabaseClientBuilder
3+
import io.github.jan.supabase.auth.Auth
4+
import io.github.jan.supabase.auth.auth
5+
import io.github.jan.supabase.auth.minimalConfig
6+
import io.github.jan.supabase.auth.user.UserInfo
7+
import io.github.jan.supabase.auth.user.UserSession
8+
import io.github.jan.supabase.testing.assertMethodIs
9+
import io.github.jan.supabase.testing.assertPathIs
10+
import io.github.jan.supabase.testing.createMockedSupabaseClient
11+
import io.github.jan.supabase.testing.pathAfterVersion
12+
import io.github.jan.supabase.testing.respondJson
13+
import io.github.jan.supabase.testing.toJsonElement
14+
import io.ktor.http.HttpMethod
15+
import kotlinx.coroutines.test.runTest
16+
import kotlinx.serialization.json.Json
17+
import kotlinx.serialization.json.jsonObject
18+
import kotlinx.serialization.json.jsonPrimitive
19+
import kotlin.test.AfterTest
20+
import kotlin.test.Test
21+
import kotlin.test.assertEquals
22+
import kotlin.test.assertNull
23+
24+
class AuthPasskeyApiTest {
25+
26+
private val configuration: SupabaseClientBuilder.() -> Unit = {
27+
install(Auth) {
28+
minimalConfig()
29+
}
30+
}
31+
private lateinit var client: SupabaseClient
32+
33+
@Test
34+
fun testStartRegistration() = runTest {
35+
val expectedChallengeId = "challenge-123"
36+
val expectedExpiresAt = "2024-12-31T23:59:59Z"
37+
client = createMockedSupabaseClient(
38+
configuration = configuration
39+
) {
40+
assertPathIs("/passkeys/registration/options", it.url.pathAfterVersion())
41+
assertMethodIs(HttpMethod.Post, it.method)
42+
respondJson("""
43+
{
44+
"challenge_id": "$expectedChallengeId",
45+
"expires_at": "$expectedExpiresAt",
46+
"options": {
47+
"challenge": "dGVzdGNoYWxsZW5nZQ==",
48+
"timeout": 60000,
49+
"attestation": "direct",
50+
"user": {
51+
"id": "dXNlcmlk",
52+
"name": "user@example.com",
53+
"displayName": "User Name"
54+
},
55+
"pubKeyCredParams": [
56+
{
57+
"alg": -7,
58+
"type": "public-key"
59+
}
60+
],
61+
"rp": {
62+
"name": "Example",
63+
"id": "example.com"
64+
}
65+
}
66+
}
67+
""".trimIndent())
68+
}
69+
client.auth.awaitInitialization()
70+
val response = client.auth.passkeys.startRegistration()
71+
assertEquals(expectedChallengeId, response.challengeId)
72+
assertEquals(expectedExpiresAt, response.expiresAt.toString())
73+
}
74+
75+
@Test
76+
fun testVerifyRegistration() = runTest {
77+
val challengeId = "challenge-123"
78+
val credential = """{"id":"test","type":"public-key","response":{"clientDataJSON":"eyJ0eXBlIjoid2ViYXV0aG4uY3JlYXRlIiwiY2hhbGxlbmdlIjoiZEdWemRBPT0iLCJvcmlnaW4iOiJodHRwczovL2V4YW1wbGUuY29tIn0=","attestationObject":"o2NmbXRmcGFja2VkZ2F0dFN0bXSjY2FsZyZjc2lnWEcwRQIhAJZ7VN4v..."}}"""
79+
val expectedId = "passkey-123"
80+
val expectedFriendlyName = "My Passkey"
81+
val expectedCreatedAt = "2024-01-01T00:00:00Z"
82+
client = createMockedSupabaseClient(
83+
configuration = configuration
84+
) {
85+
assertPathIs("/passkeys/registration/verify", it.url.pathAfterVersion())
86+
assertMethodIs(HttpMethod.Post, it.method)
87+
val body = it.body.toJsonElement().jsonObject
88+
assertEquals(challengeId, body["challenge_id"]?.jsonPrimitive?.content)
89+
respondJson("""
90+
{
91+
"id": "$expectedId",
92+
"friendly_name": "$expectedFriendlyName",
93+
"created_at": "$expectedCreatedAt"
94+
}
95+
""".trimIndent())
96+
}
97+
client.auth.awaitInitialization()
98+
val response = client.auth.passkeys.verifyRegistration(challengeId, credential)
99+
assertEquals(expectedId, response.id)
100+
assertEquals(expectedFriendlyName, response.friendlyName)
101+
}
102+
103+
@Test
104+
fun testStartAuthenticationWithoutCaptcha() = runTest {
105+
val expectedChallengeId = "challenge-456"
106+
val expectedExpiresAt = 1735689600L
107+
client = createMockedSupabaseClient(
108+
configuration = configuration
109+
) {
110+
assertPathIs("/passkeys/authentication/options", it.url.pathAfterVersion())
111+
assertMethodIs(HttpMethod.Post, it.method)
112+
val body = it.body.toJsonElement().jsonObject
113+
assertNull(body["gotrue_meta_security"])
114+
respondJson("""
115+
{
116+
"challenge_id": "$expectedChallengeId",
117+
"expires_at": $expectedExpiresAt,
118+
"options": {
119+
"challenge": "dGVzdGNoYWxsZW5nZQ==",
120+
"timeout": 60000,
121+
"userVerification": "preferred",
122+
"rpId": "example.com",
123+
"allowCredentials": []
124+
}
125+
}
126+
""".trimIndent())
127+
}
128+
client.auth.awaitInitialization()
129+
val response = client.auth.passkeys.startAuthentication()
130+
assertEquals(expectedChallengeId, response.challengeId)
131+
assertEquals(expectedExpiresAt, response.expiresAt.epochSeconds)
132+
}
133+
134+
@Test
135+
fun testStartAuthenticationWithCaptcha() = runTest {
136+
val expectedChallengeId = "challenge-456"
137+
val expectedExpiresAt = 1735689600
138+
val captchaToken = "captcha-token-xyz"
139+
client = createMockedSupabaseClient(
140+
configuration = configuration
141+
) {
142+
assertPathIs("/passkeys/authentication/options", it.url.pathAfterVersion())
143+
assertMethodIs(HttpMethod.Post, it.method)
144+
val body = it.body.toJsonElement().jsonObject
145+
assertEquals(captchaToken, body["gotrue_meta_security"]?.jsonObject?.get("captcha_token")?.jsonPrimitive?.content)
146+
respondJson("""
147+
{
148+
"challenge_id": "$expectedChallengeId",
149+
"expires_at": $expectedExpiresAt,
150+
"options": {
151+
"challenge": "dGVzdGNoYWxsZW5nZQ==",
152+
"timeout": 60000,
153+
"userVerification": "preferred",
154+
"rpId": "example.com",
155+
"allowCredentials": []
156+
}
157+
}
158+
""".trimIndent())
159+
}
160+
client.auth.awaitInitialization()
161+
val response = client.auth.passkeys.startAuthentication {
162+
this.captchaToken = captchaToken
163+
}
164+
assertEquals(expectedChallengeId, response.challengeId)
165+
}
166+
167+
@Test
168+
fun testVerifyAuthentication() = runTest {
169+
val challengeId = "challenge-456"
170+
val credential = """{"id":"test","type":"public-key","response":{"clientDataJSON":"eyJ0eXBlIjoid2ViYXV0aG4uZ2V0IiwiY2hhbGxlbmdlIjoiZEdWemRBPT0iLCJvcmlnaW4iOiJodHRwczovL2V4YW1wbGUuY29tIn0=","authenticatorData":"SZYN5OtPZAElAwgsB0AxvUSBY2BhWaOmFfHqMx7XsHcFAAAAAA==","signature":"MEQCIB..."}}"""
171+
val expectedSession = userSession()
172+
val expectedUser = UserInfo(id = "user-123", aud = "aud")
173+
client = createMockedSupabaseClient(
174+
configuration = configuration
175+
) {
176+
assertPathIs("/passkeys/authentication/verify", it.url.pathAfterVersion())
177+
assertMethodIs(HttpMethod.Post, it.method)
178+
val body = it.body.toJsonElement().jsonObject
179+
assertEquals(challengeId, body["challenge_id"]?.jsonPrimitive?.content)
180+
respondJson("""
181+
{
182+
"session": ${Json.encodeToString(UserSession.serializer(), expectedSession)},
183+
"user": ${Json.encodeToString(UserInfo.serializer(), expectedUser)}
184+
}
185+
""".trimIndent())
186+
}
187+
client.auth.awaitInitialization()
188+
val response = client.auth.passkeys.verifyAuthentication(challengeId, credential)
189+
assertEquals(expectedSession.accessToken, response.session?.accessToken)
190+
assertEquals(expectedUser.id, response.user?.id)
191+
}
192+
193+
@Test
194+
fun testList() = runTest {
195+
val passkeyId1 = "passkey-1"
196+
val passkeyId2 = "passkey-2"
197+
val friendlyName1 = "Work Passkey"
198+
val friendlyName2 = "Mobile Passkey"
199+
val createdAt = "2024-01-01T00:00:00Z"
200+
client = createMockedSupabaseClient(
201+
configuration = configuration
202+
) {
203+
assertPathIs("/passkeys/", it.url.pathAfterVersion())
204+
assertMethodIs(HttpMethod.Get, it.method)
205+
respondJson("""
206+
[
207+
{
208+
"id": "$passkeyId1",
209+
"friendly_name": "$friendlyName1",
210+
"created_at": "$createdAt",
211+
"last_used_at": null
212+
},
213+
{
214+
"id": "$passkeyId2",
215+
"friendly_name": "$friendlyName2",
216+
"created_at": "$createdAt",
217+
"last_used_at": "$createdAt"
218+
}
219+
]
220+
""".trimIndent())
221+
}
222+
client.auth.awaitInitialization()
223+
val passkeys = client.auth.passkeys.list()
224+
assertEquals(2, passkeys.size)
225+
assertEquals(passkeyId1, passkeys[0].id)
226+
assertEquals(friendlyName1, passkeys[0].friendlyName)
227+
assertEquals(passkeyId2, passkeys[1].id)
228+
assertEquals(friendlyName2, passkeys[1].friendlyName)
229+
}
230+
231+
@Test
232+
fun testListEmpty() = runTest {
233+
client = createMockedSupabaseClient(
234+
configuration = configuration
235+
) {
236+
assertPathIs("/passkeys/", it.url.pathAfterVersion())
237+
assertMethodIs(HttpMethod.Get, it.method)
238+
respondJson("[]")
239+
}
240+
client.auth.awaitInitialization()
241+
val passkeys = client.auth.passkeys.list()
242+
assertEquals(0, passkeys.size)
243+
}
244+
245+
@Test
246+
fun testDelete() = runTest {
247+
val passkeyId = "passkey-123"
248+
client = createMockedSupabaseClient(
249+
configuration = configuration
250+
) {
251+
assertPathIs("/passkeys/$passkeyId", it.url.pathAfterVersion())
252+
assertMethodIs(HttpMethod.Delete, it.method)
253+
respondJson("")
254+
}
255+
client.auth.awaitInitialization()
256+
client.auth.passkeys.delete(passkeyId)
257+
}
258+
259+
@Test
260+
fun testUpdate() = runTest {
261+
val passkeyId = "passkey-123"
262+
val newFriendlyName = "Updated Passkey Name"
263+
val createdAt = "2024-01-01T00:00:00Z"
264+
client = createMockedSupabaseClient(
265+
configuration = configuration
266+
) {
267+
assertPathIs("/passkeys/$passkeyId", it.url.pathAfterVersion())
268+
assertMethodIs(HttpMethod.Patch, it.method)
269+
val body = it.body.toJsonElement().jsonObject
270+
assertEquals(newFriendlyName, body["friendly_name"]?.jsonPrimitive?.content)
271+
respondJson("""
272+
{
273+
"id": "$passkeyId",
274+
"friendly_name": "$newFriendlyName",
275+
"created_at": "$createdAt",
276+
"last_used_at": null
277+
}
278+
""".trimIndent())
279+
}
280+
client.auth.awaitInitialization()
281+
val updatedPasskey = client.auth.passkeys.update(passkeyId, newFriendlyName)
282+
assertEquals(passkeyId, updatedPasskey.id)
283+
assertEquals(newFriendlyName, updatedPasskey.friendlyName)
284+
}
285+
286+
@AfterTest
287+
fun cleanup() {
288+
runTest {
289+
if (::client.isInitialized) {
290+
client.close()
291+
}
292+
}
293+
}
294+
295+
}

0 commit comments

Comments
 (0)