Skip to content

Commit ea7e049

Browse files
committed
Updated CreateAndProcessTaskIntegrationTest to use authenticated GraphQL task creation and assert user ownership and eventual processing, removing legacy REST and in-memory stuff. Shared authentication and GraphQL helper methods were factored out into a new file (AbstractAuthenticatedIntegrationTest).
1 parent 246b7a8 commit ea7e049

4 files changed

Lines changed: 170 additions & 79 deletions

File tree

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
package com.springqprobackend.springqpro.integration;
2+
3+
import com.springqprobackend.springqpro.security.dto.AuthResponse;
4+
import com.springqprobackend.springqpro.testcontainers.IntegrationTestBase;
5+
import org.springframework.beans.factory.annotation.Autowired;
6+
import org.springframework.boot.test.autoconfigure.web.reactive.AutoConfigureWebTestClient;
7+
import org.springframework.http.HttpHeaders;
8+
import org.springframework.http.MediaType;
9+
import org.springframework.test.web.reactive.server.WebTestClient;
10+
11+
import java.util.Map;
12+
13+
@AutoConfigureWebTestClient
14+
public abstract class AbstractAuthenticatedIntegrationTest extends IntegrationTestBase {
15+
@Autowired
16+
protected WebTestClient webTestClient;
17+
18+
// auth helper methods
19+
protected void register(String email, String password) {
20+
webTestClient.post()
21+
.uri("/auth/register")
22+
.contentType(MediaType.APPLICATION_JSON)
23+
.bodyValue(Map.of(
24+
"email", email,
25+
"password", password
26+
))
27+
.exchange()
28+
.expectStatus().isCreated();
29+
}
30+
31+
protected AuthResponse login(String email, String password) {
32+
return webTestClient.post()
33+
.uri("/auth/login")
34+
.contentType(MediaType.APPLICATION_JSON)
35+
.bodyValue(Map.of(
36+
"email", email,
37+
"password", password
38+
))
39+
.exchange()
40+
.expectStatus().isOk()
41+
.expectBody(AuthResponse.class)
42+
.returnResult()
43+
.getResponseBody();
44+
}
45+
46+
protected WebTestClient.ResponseSpec graphQLWithToken(String token, String query) {
47+
return webTestClient.post()
48+
.uri("/graphql")
49+
.header(HttpHeaders.AUTHORIZATION, "Bearer " + token)
50+
.contentType(MediaType.APPLICATION_JSON)
51+
.bodyValue(Map.of("query", query))
52+
.exchange();
53+
}
54+
55+
protected AuthResponse registerAndLogin(String email, String password) {
56+
register(email, password);
57+
return login(email, password);
58+
}
59+
}

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

Lines changed: 16 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -66,83 +66,34 @@ the dummy HTTP client for testing (use it to hit the HTTP endpoints, send header
6666
*/
6767
@AutoConfigureWebTestClient
6868
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
69-
public class AuthJwtIntegrationTest extends IntegrationTestBase {
70-
@Autowired
71-
private WebTestClient webTestClient;
69+
public class AuthJwtIntegrationTest extends AbstractAuthenticatedIntegrationTest {
70+
//@Autowired
71+
//private WebTestClient webTestClient;
7272
@Autowired
7373
private StringRedisTemplate redis;
7474
@Value("${jwt.secret}")
7575
private String jwtSecret; // will be used for test that forges an expired JWT.
7676

77-
// HELPER METHODS (pretty self-explanatory):
78-
// [1] - Register Attempt:
79-
private void register(String email, String password) {
80-
webTestClient.post()
81-
.uri("/auth/register")
82-
.contentType(MediaType.APPLICATION_JSON)
83-
.bodyValue(Map.of(
84-
"email", email,
85-
"password", password
86-
))
87-
.exchange()
88-
.expectStatus().isCreated();
89-
}
90-
91-
// [2] - Login Attempt:
92-
private AuthResponse login(String email, String password) {
93-
return webTestClient.post()
94-
.uri("/auth/login")
95-
.contentType(MediaType.APPLICATION_JSON)
96-
.bodyValue(Map.of(
97-
"email", email,
98-
"password", password
99-
))
100-
.exchange()
101-
.expectStatus().isOk()
102-
.expectBody(AuthResponse.class)
103-
.returnResult()
104-
.getResponseBody();
105-
}
106-
107-
// [3] - Sending a GraphQL query with authenticataion token:
108-
private WebTestClient.ResponseSpec graphQLWithToken(String token, String query) {
109-
return webTestClient.post()
110-
.uri("/graphql")
111-
.header(HttpHeaders.AUTHORIZATION, "Bearer " + token)
112-
.contentType(MediaType.APPLICATION_JSON)
113-
.bodyValue(Map.of("query", query))
114-
.exchange();
115-
}
116-
11777
// TESTS:
11878
@Test
11979
@Order(1)
12080
// NOTE: Related to the enforcing order thing.
12181
void fullLoginFlow_shouldAllowAccessToProtectedGraphQL() {
122-
String email = "random_email@gmail.com";
123-
String password = "i_am_wanted_in_delaware";
124-
125-
// 1. Register:
126-
register(email, password);
127-
// 2. Login and get tokens:
128-
AuthResponse auth = login(email, password);
129-
assertThat(auth).isNotNull();
130-
assertThat(auth.accessToken()).isNotBlank();
131-
assertThat(auth.refreshToken()).isNotBlank();
132-
133-
String accessToken = auth.accessToken();
82+
AuthResponse auth = registerAndLogin(
83+
"random_email@gmail.com",
84+
"i_am_wanted_in_delaware"
85+
);
13486

135-
// 3. Call protected GraphQL with access token
13687
String query = """
137-
query {
138-
tasks {
139-
id
140-
status
141-
}
142-
}
143-
""";
144-
145-
graphQLWithToken(accessToken, query)
88+
query {
89+
tasks {
90+
id
91+
status
92+
}
93+
}
94+
""";
95+
96+
graphQLWithToken(auth.accessToken(), query)
14697
.expectStatus().isOk()
14798
.expectBody()
14899
.jsonPath("$.data.tasks").exists();

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

Lines changed: 88 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -3,21 +3,21 @@
33
import com.springqprobackend.springqpro.domain.entity.TaskEntity;
44
import com.springqprobackend.springqpro.enums.TaskStatus;
55
import com.springqprobackend.springqpro.repository.TaskRepository;
6+
import com.springqprobackend.springqpro.security.dto.AuthResponse;
67
import com.springqprobackend.springqpro.testcontainers.IntegrationTestBase;
78
import org.junit.jupiter.api.BeforeEach;
8-
import org.junit.jupiter.api.Disabled;
9-
import org.junit.jupiter.api.Tag;
109
import org.springframework.beans.factory.annotation.Autowired;
11-
import org.springframework.boot.test.autoconfigure.data.redis.DataRedisTest;
12-
import org.springframework.boot.test.web.client.TestRestTemplate;
13-
import org.springframework.http.ResponseEntity;
10+
import org.springframework.boot.test.context.SpringBootTest;
11+
import org.springframework.http.HttpHeaders;
12+
import org.springframework.http.MediaType;
1413
import org.junit.jupiter.api.Test;
15-
import org.testcontainers.junit.jupiter.Testcontainers;
14+
import org.springframework.test.web.reactive.server.WebTestClient;
1615
import org.testcontainers.shaded.org.awaitility.Awaitility;
1716

1817
import java.time.Duration;
1918
import java.util.Map;
2019
import java.util.Optional;
20+
import java.util.concurrent.atomic.AtomicReference;
2121

2222
import static org.assertj.core.api.Assertions.*;
2323

@@ -55,10 +55,12 @@ public void sweepQueuedTasks() {
5555
*/
5656
//@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
5757
//@Tag("disable_temp")
58-
class CreateAndProcessTaskIntegrationTest extends IntegrationTestBase {
59-
@Autowired
60-
private TestRestTemplate rest;
61-
58+
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
59+
class CreateAndProcessTaskIntegrationTest extends AbstractAuthenticatedIntegrationTest {
60+
//@Autowired
61+
//private TestRestTemplate rest;
62+
//@Autowired
63+
//private WebTestClient webTestClient;
6264
@Autowired
6365
private TaskRepository taskRepository;
6466

@@ -67,16 +69,88 @@ void cleanDb() {
6769
taskRepository.deleteAll();
6870
}
6971

70-
@Disabled("Outdated architecture — will fix later")
72+
// HELPER METHODS - CreateAndProcessTaskIntegrationTest-specific:
73+
// [1] - GraphQL task creation mutation template function:
74+
private String createTaskAs(String email, String password, String payload) {
75+
AuthResponse auth = registerAndLogin(email, password);
76+
String mutation = """
77+
mutation {
78+
createTask(input: {
79+
payload: "%s"
80+
type: EMAIL
81+
}) {
82+
id
83+
status
84+
createdBy
85+
}
86+
}
87+
""".formatted(payload);
88+
89+
AtomicReference<String> taskIdRef = new AtomicReference<>();
90+
graphQLWithToken(auth.accessToken(), mutation)
91+
.expectStatus().isOk()
92+
.expectBody()
93+
.jsonPath("$.data.createTask.id")
94+
.value(id -> taskIdRef.set((String) id));
95+
return taskIdRef.get();
96+
}
97+
98+
// TEST(S):
99+
@Test
100+
void authenticatedUser_canCreateTask_andTaskIsEventuallyProcessed() {
101+
String email = "worker@test.com";
102+
String taskId = createTaskAs(email, "secure-pass", "integration-test");
103+
104+
// Assert persistence + ownership
105+
Awaitility.await()
106+
.atMost(Duration.ofSeconds(3))
107+
.untilAsserted(() -> {
108+
TaskEntity task = taskRepository.findById(taskId).orElseThrow();
109+
assertThat(task.getCreatedBy()).isEqualTo(email);
110+
assertThat(task.getStatus()).isEqualTo(TaskStatus.QUEUED);
111+
});
112+
113+
// Assert eventual processing
114+
Awaitility.await()
115+
.atMost(Duration.ofSeconds(5))
116+
.untilAsserted(() -> {
117+
TaskEntity task = taskRepository.findById(taskId).orElseThrow();
118+
assertThat(task.getStatus())
119+
.isIn(TaskStatus.COMPLETED, TaskStatus.FAILED);
120+
});
121+
}
122+
123+
@Test
124+
void createTask_setsCreatedByToAuthenticatedUser() {
125+
String taskId = createTaskAs("a@test.com", "pw", "ownership-test");
126+
127+
TaskEntity task = taskRepository.findById(taskId).orElseThrow();
128+
assertThat(task.getCreatedBy()).isEqualTo("a@test.com");
129+
}
130+
131+
@Test
132+
void createdTask_doesNotRemainQueuedForever() {
133+
String taskId = createTaskAs("queue@test.com", "pw", "queue-drain-test");
134+
135+
Awaitility.await()
136+
.atMost(Duration.ofSeconds(5))
137+
.untilAsserted(() -> {
138+
TaskEntity task = taskRepository.findById(taskId).orElseThrow();
139+
assertThat(task.getStatus()).isNotEqualTo(TaskStatus.QUEUED);
140+
});
141+
}
142+
143+
// OLD STUFF:
144+
/*@Disabled("Outdated architecture")
71145
@Test
72146
void createTask_isPersisted_andEventuallyProcessed() {
73147
// Starting w/ creating Task via REST endpoint (my ProducerController's POST /enqueue):
74148
Map<String, Object> req = Map.of("payload", "integration-test", "type", "EMAIL");
75149
ResponseEntity<Map> response = rest.postForEntity("/api/enqueue", req, Map.class);
76150
assertThat(response.getStatusCode().is2xxSuccessful());
77151
78-
/* After the Request is enqueued, that Task is saved to the DataBase and sent to the in-memory QueueService pool.
79-
To make sure that the Task was persisted, we can check the most recent row in our DB: */
152+
/ After the Request is enqueued, that Task is saved to the DataBase and sent to the in-memory QueueService pool.
153+
To make sure that the Task was persisted, we can check the most recent row in our DB: /
80154
Awaitility.await()
81155
.atMost(Duration.ofSeconds(5))
82156
.pollInterval(Duration.ofMillis(200))
@@ -96,5 +170,5 @@ void createTask_isPersisted_andEventuallyProcessed() {
96170
Optional<TaskEntity> refreshed = taskRepository.findById(entity.getId());
97171
return refreshed.map(e -> e.getStatus() == TaskStatus.COMPLETED || e.getStatus() == TaskStatus.FAILED).orElse(false);
98172
});
99-
}
173+
}*/
100174
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,10 @@
11
spring:
22
main:
33
allow-bean-definition-overriding: true
4+
5+
t-handler:
6+
default-sleep-time: 5
7+
email-sleep-time: 5
8+
sms-sleep-time: 5
9+
report-sleep-time: 5
10+
takes-long-sleep-time: 5

0 commit comments

Comments
 (0)