Skip to content

Commit 768aa27

Browse files
committed
Revised TaskGraphQLIntegrationTest to be up-to-date with modern project architecture. Audited remaining integration tests, determined that RetryBehaviorIntegrationTest is obsolete and entirely deprecated.
1 parent 7295ed2 commit 768aa27

2 files changed

Lines changed: 141 additions & 156 deletions

File tree

springqpro-backend/src/test/java/com/springqprobackend/springqpro/integration/RetryBehaviorIntegrationTest.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,13 @@ causes ProcessingService to schedule a retry (and that attempts are incremented,
3131
- FailHandler is overridden so its handle() method throws immediately, this was done for deterministic test behavior.
3232
*/
3333

34+
/* NOTE: As of 2026-01-16 -- this test file is effectively obsolete. Its tests test for
35+
outdated architectural behavior. Everything that it could test is covered by
36+
- CreateAndProcessTaskIntegrationTest
37+
- RedisDistributedLockIntegrationTest
38+
- ProcessingConcurrencyIntegrationTest
39+
*/
40+
3441
/* NOTE(S)-TO-SELF:
3542
- @TestConfiguration is a specialized version of @Configuration designed specifically for defining beans
3643
and customizations in a test environment. Meant to provide test-specific configurations without interfering
@@ -55,6 +62,7 @@ causes ProcessingService to schedule a retry (and that attempts are incremented,
5562
properties = "spring.main.allow-bean-definition-overriding=true"
5663
)
5764
@Tag("disable_temp")
65+
@Deprecated
5866
class RetryBehaviorIntegrationTest extends IntegrationTestBase {
5967
// Field(s):
6068
private static final Logger logger = LoggerFactory.getLogger(RetryBehaviorIntegrationTest.class);
Lines changed: 133 additions & 156 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
package com.springqprobackend.springqpro.integration;
22

33
import java.time.Instant;
4+
import java.util.List;
45

6+
import com.springqprobackend.springqpro.security.dto.AuthResponse;
57
import org.junit.jupiter.api.BeforeEach;
68
import org.junit.jupiter.api.Test;
79
import org.springframework.beans.factory.annotation.Autowired;
@@ -22,6 +24,9 @@
2224
import com.springqprobackend.springqpro.repository.UserRepository;
2325
import com.springqprobackend.springqpro.security.JwtUtil;
2426
import com.springqprobackend.springqpro.testcontainers.IntegrationTestBase;
27+
import java.util.concurrent.atomic.AtomicReference;
28+
29+
import static org.assertj.core.api.Assertions.*;
2530

2631
/* 2025-11-17-NOTE(S)-TO-SELF:
2732
- GraphQlTester is Spring's testing utility for GraphQL endpoints.
@@ -59,222 +64,194 @@ Test completes (and succeeds) and after the Spring test context begins shutting
5964
}
6065
)
6166
@ActiveProfiles("test")
62-
@DirtiesContext(classMode = DirtiesContext.ClassMode.BEFORE_EACH_TEST_METHOD)
63-
class TaskGraphQLIntegrationTest extends IntegrationTestBase {
64-
65-
private GraphQlTester graphQlTester;
66-
67-
@LocalServerPort
68-
private int port;
67+
class TaskGraphQLIntegrationTest extends AbstractAuthenticatedIntegrationTest {
6968

7069
@Autowired
7170
private TaskRepository taskRepository;
7271

7372
@Autowired
7473
private UserRepository userRepository;
7574

76-
@Autowired
77-
private JwtUtil jwtUtil;
78-
7975
private static final String TEST_EMAIL = "graphql-test@example.com";
76+
private static final String PASSWORD = "password";
8077

8178
@BeforeEach
82-
void init() {
79+
void setup() {
8380
taskRepository.deleteAll();
8481
userRepository.deleteAll();
85-
// Create a user so SecurityContext + ownership is valid
86-
userRepository.save(new UserEntity(
87-
TEST_EMAIL,
88-
"{noop}password" // password not used here; we just need a valid user record
89-
));
90-
// Generate a valid JWT for that user
91-
String token = jwtUtil.generateAccessToken(TEST_EMAIL);
92-
93-
WebTestClient client = WebTestClient.bindToServer()
94-
.baseUrl("http://localhost:" + port + "/graphql")
95-
.exchangeStrategies(
96-
ExchangeStrategies.builder()
97-
.codecs(c -> c.defaultCodecs().maxInMemorySize(5_000_000))
98-
.build()
99-
)
100-
.defaultHeader("Authorization", "Bearer " + token)
101-
.build();
102-
this.graphQlTester = HttpGraphQlTester.create(client);
10382
}
10483

105-
// Test #1 - Create Task, Query the Task, then verify its Status, Type, and Payload:
84+
// Test #1: createTask + fetch by id
10685
@Test
107-
void taskCreation_succeeds_isRetrieved() {
108-
String mutation = """
86+
void createTask_thenQueryById_returnsCorrectTask() {
87+
AuthResponse auth = registerAndLogin(TEST_EMAIL, PASSWORD);
88+
89+
String createMutation = """
10990
mutation {
110-
createTask(input:{
111-
payload:"send-an-email"
112-
type:EMAIL
113-
}) {
114-
id
115-
payload
116-
type
117-
status
118-
attempts
119-
maxRetries
120-
}
91+
createTask(input: {
92+
payload: "send-an-email"
93+
type: EMAIL
94+
}) {
95+
id
96+
payload
97+
type
98+
status
99+
}
121100
}
122101
""";
123-
/*2025 - 11 - 17 - NOTE(S) - TO - SELF:+DEBUG:
124-
-graphQlTester.document(mutation) prepares a GraphQL mutation with String mutation, which is just a String
125-
containing
126-
a proper GraphQL mutation obv.So graphQlTester runs it by running my actual Spring Boot app(which runs my real
127-
GraphQL controller, my real service layer, and uses the real database(Testcontainers in this
128-
case)).
129-
- .execute() clearly runs it and you can inspect the fields.*/
130-
var created = graphQlTester.document(mutation)
131-
.execute()
132-
.path("createTask.payload").entity(String.class).isEqualTo("send-an-email")
133-
.path("createTask.type").entity(String.class).isEqualTo("EMAIL")
134-
.path("createTask.status").entity(String.class).isEqualTo("QUEUED");
135-
// Extract the id from the same GraphQL mutation:
136-
String id = graphQlTester.document(mutation)
137-
.execute()
138-
.path("createTask.id").entity(String.class).get();
139-
// Search for that Task w/ a GraphQL query:
102+
103+
AtomicReference<String> taskIdRef = new AtomicReference<>();
104+
105+
graphQLWithToken(auth.accessToken(), createMutation)
106+
.expectStatus().isOk()
107+
.expectBody()
108+
.jsonPath("$.data.createTask.id")
109+
.value(id -> taskIdRef.set((String) id))
110+
.jsonPath("$.data.createTask.payload").isEqualTo("send-an-email")
111+
.jsonPath("$.data.createTask.type").isEqualTo("EMAIL")
112+
.jsonPath("$.data.createTask.status").isEqualTo("QUEUED");
113+
114+
String taskId = taskIdRef.get();
115+
140116
String query = """
141117
query {
142-
task(id: "%s") {
143-
id
144-
payload
145-
type
146-
status
147-
}
118+
task(id: "%s") {
119+
id
120+
payload
121+
type
122+
status
123+
}
148124
}
149-
""".formatted(id);
150-
graphQlTester.document(query)
151-
.execute()
152-
.path("task.payload").entity(String.class).isEqualTo("send-an-email")
153-
.path("task.type").entity(String.class).isEqualTo("EMAIL");
125+
""".formatted(taskId);
126+
127+
graphQLWithToken(auth.accessToken(), query)
128+
.expectStatus().isOk()
129+
.expectBody()
130+
.jsonPath("$.data.task.id").isEqualTo(taskId)
131+
.jsonPath("$.data.task.payload").isEqualTo("send-an-email")
132+
.jsonPath("$.data.task.type").isEqualTo("EMAIL");
154133
}
155134

156-
// Test #2 - Update Task (Partially), Verify Change in DB, Query Returns Updated:
135+
// Test #2: updateTask reflects in DB + query
157136
@Test
158-
void updateTask_verifyDBChange_retrieveByQuery() {
159-
TaskEntity entity = new TaskEntity(
160-
"Task-ArbitraryTaskId",
161-
"Send an email",
137+
void updateTask_updatesDatabase_andQueryReflectsChange() {
138+
AuthResponse auth = registerAndLogin(TEST_EMAIL, PASSWORD);
139+
140+
TaskEntity task = new TaskEntity(
141+
"Task-Update-1",
142+
"original payload",
162143
TaskType.EMAIL,
163144
TaskStatus.QUEUED,
164145
0,
165146
3,
166147
Instant.now(),
167148
TEST_EMAIL
168149
);
169-
taskRepository.save(entity);
170-
// Manually change the status of entity to COMPLETED and increment attempts:
171-
// NOTE: When you want to specify integer in mutation, put %d -- "%d" will return you a integer wrapped in a String (aka just a String)
150+
taskRepository.save(task);
151+
172152
String mutation = """
173153
mutation {
174-
updateTask(input:{
175-
id:"%s",
176-
status:COMPLETED,
177-
attempts:%d
178-
}) {
179-
id
180-
status
181-
attempts
182-
}
154+
updateTask(input: {
155+
id: "%s"
156+
status: COMPLETED
157+
attempts: 1
158+
}) {
159+
id
160+
status
161+
attempts
162+
}
183163
}
184-
""".formatted(entity.getId(), entity.getAttempts()+1);
185-
graphQlTester.document(mutation).execute()
186-
.path("updateTask.status").entity(String.class).isEqualTo("COMPLETED")
187-
.path("updateTask.attempts").entity(Integer.class).isEqualTo(1);
188-
// Verify that the change happened in the DataBase w/ taskRepository:
189-
TaskEntity getEntity = taskRepository.findById(entity.getId()).orElseThrow();
190-
assert getEntity.getStatus() == TaskStatus.COMPLETED;
191-
assert getEntity.getAttempts() == entity.getAttempts() + 1;
164+
""".formatted(task.getId());
165+
166+
graphQLWithToken(auth.accessToken(), mutation)
167+
.expectStatus().isOk()
168+
.expectBody()
169+
.jsonPath("$.data.updateTask.status").isEqualTo("COMPLETED")
170+
.jsonPath("$.data.updateTask.attempts").isEqualTo(1);
171+
172+
TaskEntity updated = taskRepository.findById(task.getId()).orElseThrow();
173+
assertThat(updated.getStatus()).isEqualTo(TaskStatus.COMPLETED);
174+
assertThat(updated.getAttempts()).isEqualTo(1);
192175
}
193176

194-
// Test #3 - Delete Task, Verify Change in DB, Query Returns NULL (or whatever):
177+
// Test #3: deleteTask removes entity
195178
@Test
196-
void deleteTask_verifyDBChange_queryRetrieveNull() {
197-
TaskEntity entity = new TaskEntity(
198-
"Task-ArbitraryTaskId",
199-
"Send an email",
179+
void deleteTask_removesTaskFromDatabase() {
180+
AuthResponse auth = registerAndLogin(TEST_EMAIL, PASSWORD);
181+
182+
TaskEntity task = new TaskEntity(
183+
"Task-Delete-1",
184+
"to delete",
200185
TaskType.EMAIL,
201186
TaskStatus.QUEUED,
202187
0,
203188
3,
204189
Instant.now(),
205190
TEST_EMAIL
206191
);
207-
taskRepository.save(entity);
192+
taskRepository.save(task);
193+
208194
String mutation = """
209195
mutation {
210-
deleteTask(id:"%s")
196+
deleteTask(id: "%s")
211197
}
212-
""".formatted(entity.getId());
213-
graphQlTester.document(mutation)
214-
.execute()
215-
.path("deleteTask").entity(Boolean.class).isEqualTo(true);
216-
assert taskRepository.findById(entity.getId()).isEmpty();
198+
""".formatted(task.getId());
199+
200+
graphQLWithToken(auth.accessToken(), mutation)
201+
.expectStatus().isOk()
202+
.expectBody()
203+
.jsonPath("$.data.deleteTask").isEqualTo(true);
204+
205+
assertThat(taskRepository.findById(task.getId())).isEmpty();
217206
}
218207

219-
// Test #4 - Being able to filter Tasks w/ Status:
208+
// Test #4: filter tasks by status
220209
@Test
221-
void filterTasks_byStatusQuery_returnsCorrectList() {
222-
// Task #1:
223-
taskRepository.save(
224-
new TaskEntity("Task-ArbitraryTaskId-1","Send an email", TaskType.EMAIL, TaskStatus.QUEUED, 0, 3, Instant.now(), TEST_EMAIL)
225-
);
226-
// Task #2:
227-
taskRepository.save(
228-
new TaskEntity("Task-ArbitraryTaskId-2", "Send an email 2", TaskType.EMAIL, TaskStatus.COMPLETED, 1, 3, Instant.now(), TEST_EMAIL)
229-
);
230-
// Task #3:
231-
taskRepository.save(
232-
new TaskEntity("Task-ArbitaryTaskId-3", "Send an SMS or whatever", TaskType.SMS, TaskStatus.QUEUED, 0, 3, Instant.now(), TEST_EMAIL)
233-
);
210+
void tasksQuery_filtersByStatus() {
211+
AuthResponse auth = registerAndLogin(TEST_EMAIL, PASSWORD);
212+
213+
taskRepository.saveAll(List.of(
214+
new TaskEntity("t1", "a", TaskType.EMAIL, TaskStatus.QUEUED, 0, 3, Instant.now(), TEST_EMAIL),
215+
new TaskEntity("t2", "b", TaskType.EMAIL, TaskStatus.COMPLETED, 1, 3, Instant.now(), TEST_EMAIL),
216+
new TaskEntity("t3", "c", TaskType.SMS, TaskStatus.QUEUED, 0, 3, Instant.now(), TEST_EMAIL)
217+
));
218+
234219
String query = """
235220
query {
236-
tasks(status: QUEUED) {
237-
id
238-
payload
239-
status
240-
}
221+
tasks(status: QUEUED) {
222+
id
223+
}
241224
}
242225
""";
243-
graphQlTester.document(query)
244-
.execute()
245-
.path("tasks").entityList(TaskEntity.class).hasSize(2);
226+
227+
graphQLWithToken(auth.accessToken(), query)
228+
.expectStatus().isOk()
229+
.expectBody()
230+
.jsonPath("$.data.tasks.length()").isEqualTo(2);
246231
}
247232

248-
// Test #5 - Being able to filter Tasks w/ Type:
233+
// Test #5: filter tasks by type
249234
@Test
250-
void filterTasks_byTypeQuery_returnsCorrectList() {
251-
// Task #1:
252-
taskRepository.save(
253-
new TaskEntity("Task-ArbitraryTaskId-1","Do something NEWSLETTER related, I don't know.", TaskType.NEWSLETTER, TaskStatus.QUEUED, 0, 3, Instant.now(), TEST_EMAIL)
254-
);
255-
// Task #2:
256-
taskRepository.save(
257-
new TaskEntity("Task-ArbitraryTaskId-2", "Do something NEWSLETTER related, I don't know 2.", TaskType.NEWSLETTER, TaskStatus.COMPLETED, 1, 3, Instant.now(), TEST_EMAIL)
258-
);
259-
// Task #3:
260-
taskRepository.save(
261-
new TaskEntity("Task-ArbitraryTaskId-3", "Send an SMS or whatever", TaskType.SMS, TaskStatus.QUEUED, 0, 3, Instant.now(), TEST_EMAIL)
262-
);
263-
// Task #4:
264-
taskRepository.save(
265-
new TaskEntity("Task-ArbitraryTaskId-4", "Do something NEWSLETTER related, I don't know 3.", TaskType.NEWSLETTER, TaskStatus.QUEUED, 0, 3, Instant.now(), TEST_EMAIL)
266-
);
235+
void tasksTypeQuery_filtersByType() {
236+
AuthResponse auth = registerAndLogin(TEST_EMAIL, PASSWORD);
237+
238+
taskRepository.saveAll(List.of(
239+
new TaskEntity("t1", "n1", TaskType.NEWSLETTER, TaskStatus.QUEUED, 0, 3, Instant.now(), TEST_EMAIL),
240+
new TaskEntity("t2", "n2", TaskType.NEWSLETTER, TaskStatus.COMPLETED, 1, 3, Instant.now(), TEST_EMAIL),
241+
new TaskEntity("t3", "s1", TaskType.SMS, TaskStatus.QUEUED, 0, 3, Instant.now(), TEST_EMAIL)
242+
));
243+
267244
String query = """
268245
query {
269-
tasksType(type: NEWSLETTER) {
270-
id
271-
payload
272-
status
273-
}
246+
tasksType(type: NEWSLETTER) {
247+
id
248+
}
274249
}
275250
""";
276-
graphQlTester.document(query)
277-
.execute()
278-
.path("tasksType").entityList(TaskEntity.class).hasSize(3);
251+
252+
graphQLWithToken(auth.accessToken(), query)
253+
.expectStatus().isOk()
254+
.expectBody()
255+
.jsonPath("$.data.tasksType.length()").isEqualTo(2);
279256
}
280257
}

0 commit comments

Comments
 (0)