Skip to content

Commit e5c0fb4

Browse files
committed
Transpile tests to kotlin and add nullable annotations in graqhql response exception
1 parent cf8fc53 commit e5c0fb4

File tree

5 files changed

+436
-438
lines changed

5 files changed

+436
-438
lines changed

aws-api/src/main/java/com/amplifyframework/api/aws/GraphQLResponseException.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,7 @@ public static final class GraphQLError {
105105
* Gets the error type (AWS AppSync extension).
106106
* @return The error type, or null if not present
107107
*/
108+
@Nullable
108109
public String getErrorType() {
109110
return errorType;
110111
}
@@ -113,6 +114,7 @@ public String getErrorType() {
113114
* Gets the error message.
114115
* @return The error message, or null if not present
115116
*/
117+
@Nullable
116118
public String getMessage() {
117119
return message;
118120
}
@@ -121,6 +123,7 @@ public String getMessage() {
121123
* Gets the error code.
122124
* @return The error code, or null if not present
123125
*/
126+
@Nullable
124127
public Integer getErrorCode() {
125128
return errorCode;
126129
}

aws-api/src/test/java/com/amplifyframework/api/aws/AppSyncErrorPropagationTest.java

Lines changed: 0 additions & 157 deletions
This file was deleted.
Lines changed: 151 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,151 @@
1+
/*
2+
* Copyright 2026 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License").
5+
* You may not use this file except in compliance with the License.
6+
* A copy of the License is located at
7+
*
8+
* http://aws.amazon.com/apache2.0
9+
*
10+
* or in the "license" file accompanying this file. This file is distributed
11+
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
12+
* express or implied. See the License for the specific language governing
13+
* permissions and limitations under the License.
14+
*/
15+
16+
package com.amplifyframework.api.aws
17+
18+
import androidx.test.core.app.ApplicationProvider
19+
import com.amplifyframework.AmplifyException
20+
import com.amplifyframework.api.ApiException
21+
import com.amplifyframework.api.graphql.model.ModelQuery
22+
import com.amplifyframework.testutils.Await
23+
import io.kotest.matchers.nulls.shouldNotBeNull
24+
import io.kotest.matchers.shouldBe
25+
import io.kotest.matchers.types.shouldBeInstanceOf
26+
import okhttp3.HttpUrl
27+
import okhttp3.mockwebserver.MockResponse
28+
import okhttp3.mockwebserver.MockWebServer
29+
import org.json.JSONException
30+
import org.json.JSONObject
31+
import org.junit.After
32+
import org.junit.Before
33+
import org.junit.Test
34+
import org.junit.runner.RunWith
35+
import org.robolectric.RobolectricTestRunner
36+
import java.io.IOException
37+
38+
/**
39+
* Integration tests for error cause propagation in GraphQL operations.
40+
* Tests that exception causes are properly preserved when API calls fail.
41+
*/
42+
@RunWith(RobolectricTestRunner::class)
43+
class AppSyncErrorPropagationTest {
44+
private lateinit var webServer: MockWebServer
45+
private lateinit var baseUrl: HttpUrl
46+
private lateinit var plugin: AWSApiPlugin
47+
48+
/**
49+
* Sets up the test with a mock web server.
50+
* @throws IOException On failure to start web server
51+
* @throws JSONException On failure to arrange configuration JSON
52+
* @throws AmplifyException On failure to configure plugin
53+
*/
54+
@Before
55+
@Throws(AmplifyException::class, IOException::class, JSONException::class)
56+
fun setup() {
57+
webServer = MockWebServer()
58+
webServer.start(8080)
59+
baseUrl = webServer.url("/")
60+
61+
val configuration = JSONObject()
62+
.put("graphQlApi", JSONObject()
63+
.put("endpointType", "GraphQL")
64+
.put("endpoint", baseUrl.toUrl())
65+
.put("region", "us-east-1")
66+
.put("authorizationType", "API_KEY")
67+
.put("apiKey", "FAKE-API-KEY"))
68+
69+
plugin = AWSApiPlugin.builder().build()
70+
plugin.configure(configuration, ApplicationProvider.getApplicationContext())
71+
}
72+
73+
/**
74+
* Stop the [MockWebServer] that was started in [setup].
75+
* @throws IOException On failure to shutdown the MockWebServer
76+
*/
77+
@After
78+
@Throws(IOException::class)
79+
fun cleanup() {
80+
webServer.shutdown()
81+
}
82+
83+
/**
84+
* Tests that query 401 errors preserve the GraphQL error response as the cause.
85+
* The cause should be a GraphQLResponseException containing the error details.
86+
*/
87+
@Test
88+
fun queryUnauthorizedErrorPreservesCause() {
89+
// Arrange - mock a 401 response with GraphQL error payload
90+
val errorResponse = """
91+
{
92+
"errors": [{
93+
"errorType": "UnauthorizedException",
94+
"message": "You are not authorized to make this call."
95+
}]
96+
}
97+
""".trimIndent()
98+
99+
webServer.enqueue(MockResponse()
100+
.setResponseCode(401)
101+
.setBody(errorResponse))
102+
103+
// Act - execute query and capture error
104+
val error = Await.error { onResult, onError ->
105+
plugin.query(ModelQuery.list(Todo::class.java), onResult, onError)
106+
}
107+
108+
// Assert - verify error has proper cause chain
109+
error.shouldNotBeNull()
110+
error.shouldBeInstanceOf<ApiException.NonRetryableException>()
111+
112+
val cause = error.cause
113+
cause.shouldNotBeNull()
114+
cause.shouldBeInstanceOf<GraphQLResponseException>()
115+
116+
cause.errors.size.shouldBe(1)
117+
118+
val firstError = cause.errors[0]
119+
firstError.errorType.shouldBe("UnauthorizedException")
120+
firstError.message.shouldBe("You are not authorized to make this call.")
121+
}
122+
123+
/**
124+
* Tests that query errors with invalid JSON fall back to IOException with JSONException cause.
125+
*/
126+
@Test
127+
fun queryInvalidJsonPreservesJsonException() {
128+
// Arrange - mock a 400 response with invalid JSON
129+
val invalidJson = "not valid json at all"
130+
131+
webServer.enqueue(MockResponse()
132+
.setResponseCode(400)
133+
.setBody(invalidJson))
134+
135+
// Act
136+
val error = Await.error { onResult, onError ->
137+
plugin.query(ModelQuery.list(Todo::class.java), onResult, onError)
138+
}
139+
140+
// Assert
141+
error.shouldNotBeNull()
142+
val cause = error.cause
143+
cause.shouldNotBeNull()
144+
cause.shouldBeInstanceOf<IOException>()
145+
146+
// The IOException should have a JSONException as its cause
147+
val jsonCause = cause.cause
148+
jsonCause.shouldNotBeNull()
149+
jsonCause.shouldBeInstanceOf<JSONException>()
150+
}
151+
}

0 commit comments

Comments
 (0)