Skip to content

Commit 7295ed2

Browse files
committed
Revising ProcessingConcurrencyIntegrationTest and fixing OwnershipGraphQLIntegrationTest.
1 parent fed60c8 commit 7295ed2

2 files changed

Lines changed: 59 additions & 30 deletions

File tree

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

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
import com.fasterxml.jackson.databind.ObjectMapper;
2121
import org.testcontainers.utility.DockerImageName;
2222

23+
import java.util.List;
2324
import java.util.Map;
2425
import java.util.concurrent.atomic.AtomicReference;
2526

@@ -80,7 +81,9 @@ void userCanSeeOnlyTheirOwnTasks() {
8081
.expectStatus().isOk()
8182
.expectBody()
8283
.jsonPath("$.data.tasks[*].id")
83-
.value(ids -> assertThat((Iterable<?>) ids).contains(aliceTaskId));
84+
.value(ids -> {
85+
List<String> idList = (List<String>) ids;
86+
assertThat(idList).contains(aliceTaskId); });
8487

8588
// Simon does NOT see Alice's task
8689
graphQLWithToken(simon.accessToken(), """
@@ -93,7 +96,9 @@ void userCanSeeOnlyTheirOwnTasks() {
9396
.expectStatus().isOk()
9497
.expectBody()
9598
.jsonPath("$.data.tasks[*].id")
96-
.value(ids -> assertThat((Iterable<?>) ids).doesNotContain(aliceTaskId));
99+
.value(ids -> {
100+
List<String> idList = (List<String>) ids;
101+
assertThat(idList).doesNotContain(aliceTaskId); });
97102
}
98103

99104
@Test
@@ -167,8 +172,10 @@ void userCannotRetryAnotherUsersTask() {
167172
retryTask(id: "%s")
168173
}
169174
""".formatted(aliceTaskId))
170-
.expectStatus().isOk()
171-
.expectBody()
172-
.jsonPath("$.data.retryTask").isEqualTo(false);
175+
.expectStatus().isOk()
176+
.expectBody()
177+
.jsonPath("$.data").isEqualTo(null)
178+
.jsonPath("$.errors").isArray()
179+
.jsonPath("$.errors.length()").isEqualTo(1);
173180
}
174181
}

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

Lines changed: 47 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
package com.springqprobackend.springqpro.integration;
22

33
import com.springqprobackend.springqpro.domain.entity.TaskEntity;
4+
import com.springqprobackend.springqpro.enums.TaskStatus;
45
import com.springqprobackend.springqpro.enums.TaskType;
56
import com.springqprobackend.springqpro.redis.RedisDistributedLock;
67
import com.springqprobackend.springqpro.repository.TaskRepository;
78
import com.springqprobackend.springqpro.service.ProcessingService;
9+
import com.springqprobackend.springqpro.service.QueueService;
810
import com.springqprobackend.springqpro.service.TaskService;
911
import com.springqprobackend.springqpro.testcontainers.IntegrationTestBase;
1012
import org.junit.jupiter.api.BeforeEach;
@@ -14,6 +16,11 @@
1416
import org.slf4j.LoggerFactory;
1517
import org.springframework.beans.factory.annotation.Autowired;
1618
import org.junit.jupiter.api.Test;
19+
import org.springframework.boot.test.context.SpringBootTest;
20+
import org.springframework.test.context.ActiveProfiles;
21+
import org.testcontainers.shaded.org.awaitility.Awaitility;
22+
23+
import java.time.Duration;
1724
import java.util.concurrent.*;
1825

1926
import static org.assertj.core.api.AssertionsForClassTypes.assertThat;
@@ -52,43 +59,58 @@ These three Integration test that I have (ProcessingConcurrencyIntegrationTest.j
5259
*/
5360
//@Testcontainers
5461
//@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.NONE)
55-
@Tag("disable_temp")
62+
@SpringBootTest
63+
@ActiveProfiles("test")
5664
class ProcessingConcurrencyIntegrationTest extends IntegrationTestBase {
57-
// Field(s):
58-
private static final Logger logger = LoggerFactory.getLogger(ProcessingConcurrencyIntegrationTest.class);
5965
@Autowired
6066
private TaskService taskService;
67+
6168
@Autowired
6269
private TaskRepository taskRepository;
70+
6371
@Autowired
64-
private ProcessingService processingService;
65-
@Autowired
66-
private RedisDistributedLock redisLock;
72+
private QueueService queueService;
6773

6874
@BeforeEach
6975
void cleanDb() {
7076
taskRepository.deleteAll();
7177
}
7278

73-
@Disabled("Outdated architecture — will fix later")
7479
@Test
75-
void twoThreads_tryToClaim_sameTask_onlyOneSucceeds() throws InterruptedException, ExecutionException {
76-
TaskEntity entity = taskService.createTaskForUser("concurrency-test", TaskType.EMAIL, "random_email@gmail.com");
77-
String id = entity.getId();
78-
ExecutorService esDummy = Executors.newFixedThreadPool(2);
79-
Callable<Void> c = () -> {
80-
processingService.claimAndProcess(id);
81-
return null;
82-
};
83-
Future<Void> f1 = esDummy.submit(c);
84-
Future<Void> f2 = esDummy.submit(c);
85-
f1.get();
86-
f2.get();
87-
// reload:
88-
TaskEntity reloaded = taskRepository.findById(id).orElseThrow();
89-
// ATTEMPTS SHOULD BE 1.
90-
assertThat(reloaded.getAttempts()).isGreaterThanOrEqualTo(1);
91-
assertThat(reloaded.getStatus()).isNotNull(); // No double-claiming anomalies or corrupted states.
92-
esDummy.shutdown();
80+
void concurrentEnqueue_sameTask_doesNotProcessTwice() throws Exception {
81+
// create task using TaskService:
82+
TaskEntity task = taskService.createTaskForUser(
83+
"concurrency-test",
84+
TaskType.EMAIL,
85+
"concurrency@test.com"
86+
);
87+
String taskId = task.getId();
88+
89+
// enqueue same task concurrently:
90+
ExecutorService executor = Executors.newFixedThreadPool(2);
91+
92+
Runnable enqueue = () -> queueService.enqueueById(taskId);
93+
94+
executor.submit(enqueue);
95+
executor.submit(enqueue);
96+
97+
executor.shutdown();
98+
executor.awaitTermination(5, TimeUnit.SECONDS);
99+
100+
// Assert: task eventually reaches a terminal state
101+
Awaitility.await()
102+
.atMost(Duration.ofSeconds(5))
103+
.untilAsserted(() -> {
104+
TaskEntity reloaded = taskRepository.findById(taskId).orElseThrow();
105+
assertThat(reloaded.getStatus())
106+
.isIn(TaskStatus.COMPLETED, TaskStatus.FAILED);
107+
});
108+
109+
// Assert: task is not duplicated or corrupted
110+
TaskEntity finalState = taskRepository.findById(taskId).orElseThrow();
111+
112+
assertThat(finalState.getId()).isEqualTo(taskId);
113+
assertThat(finalState.getStatus()).isNotEqualTo(TaskStatus.QUEUED);
114+
assertThat(finalState.getStatus()).isNotEqualTo(TaskStatus.INPROGRESS);
93115
}
94116
}

0 commit comments

Comments
 (0)