Skip to content

Commit fed60c8

Browse files
committed
Revised OwnershipGraphQLIntegrationTest to extend AbstractAuthenticatedIntegrationTest, removing duplicated auth logic, static state, enforced test ordering, and manual container wiring. Also, should have noted in prior commit -- adjusted some assertion logic after realizing CI (GitHub Actions) runs faster than my laptop.
1 parent b11c2c0 commit fed60c8

File tree

1 file changed

+125
-184
lines changed

1 file changed

+125
-184
lines changed

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

Lines changed: 125 additions & 184 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,7 @@
55
import com.fasterxml.jackson.databind.JsonNode;
66
import com.redis.testcontainers.RedisContainer;
77
import com.springqprobackend.springqpro.security.dto.AuthResponse;
8-
import org.junit.jupiter.api.MethodOrderer;
9-
import org.junit.jupiter.api.Order;
10-
import org.junit.jupiter.api.Test;
11-
import org.junit.jupiter.api.TestMethodOrder;
8+
import org.junit.jupiter.api.*;
129
import org.springframework.beans.factory.annotation.Autowired;
1310
import org.springframework.boot.test.autoconfigure.web.reactive.AutoConfigureWebTestClient;
1411
import org.springframework.boot.test.context.SpringBootTest;
@@ -24,210 +21,154 @@
2421
import org.testcontainers.utility.DockerImageName;
2522

2623
import java.util.Map;
24+
import java.util.concurrent.atomic.AtomicReference;
25+
2726
import static org.assertj.core.api.Assertions.*;
2827

2928
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
30-
@AutoConfigureWebTestClient
31-
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
32-
@Testcontainers
3329
@ActiveProfiles("test")
34-
public class OwnershipGraphQLIntegrationTest {
35-
// Field(s):
36-
@Autowired
37-
private WebTestClient webTestClient;
38-
@Autowired
39-
private ObjectMapper objectMapper;
40-
41-
/* 2025-11-26-NOTE:[BELOW] I AM RUSHING TO MVP COMPLETION, I HAVE GONE SEVERELY OVERTIME WITH THIS PROJECT AND I WANT TO
42-
WRAP IT UP AS SOON AS POSSIBLE READY FOR PRODUCTION AND DISPLAY FOR RECRUITERS AND READY FOR MY RESUME! THUS, MY
43-
INTEGRATION TESTS ARE A HOT MESS. I WILL COME BACK IN THE NEAR-FUTURE TO REFACTOR AND TIDY THEM. THE TWO CONTAINERS
44-
BELOW SHOULD 100% BE MODULARIZED SOMEWHERE -- BUT I AM ON A TIME CRUNCH AND I CAN'T BE ASKED (RIGHT NOW): */
45-
// DEBUG: BELOW! TEMPORARY - FIX PROPERLY LATER!
46-
@ServiceConnection
47-
protected static final PostgreSQLContainer<?> POSTGRES =
48-
new PostgreSQLContainer<>("postgres:18")
49-
.withDatabaseName("springqpro")
50-
.withUsername("springqpro")
51-
.withPassword("springqpro")
52-
.withReuse(true);
53-
@Container
54-
@ServiceConnection
55-
static final RedisContainer REDIS = new RedisContainer(DockerImageName.parse("redis:7.2"));
56-
// DEBUG: ABOVE! TEMPORARY - FIX PROPERLY LATER!
57-
/* 2025-11-26-NOTE: AS NOTED IN A COMMENT ABOVE THE CLASS, MY TEST STARTS THE FULL SPRING CONTEXT. BUT DON'T FORGET
58-
THAT I NEED TO SPIN UP THE REDIS AND POSTGRES CONTAINERS MYSELF!
59-
NOTE: ALSO, THE @Testcontainers annotation at the top is needed too for this stuff. */
60-
30+
public class OwnershipGraphQLIntegrationTest extends AbstractAuthenticatedIntegrationTest {
6131
// For the tests:
6232
private static String aliceToken;
6333
private static String simonToken;
6434
private static String aliceTaskId;
65-
private static String simonTaskId;
66-
private static final String password = "who_cares";
67-
private static final String alice_email = "alice@gmail.com";
68-
private static final String simon_email = "simon@gmail.com";
69-
70-
// HELPER METHODS (again, there's a lot of repeated boilerplate code here and in the other JWT test file -- modularize later, I'm rushing):
71-
// [1] - REGISTER:
72-
private void register(String email, String password) {
73-
webTestClient.post()
74-
.uri("/auth/register")
75-
.contentType(MediaType.APPLICATION_JSON)
76-
.bodyValue(Map.of(
77-
"email", email,
78-
"password", password
79-
))
80-
.exchange()
81-
.expectStatus().isCreated();
82-
}
83-
// [2] - LOGIN:
84-
private AuthResponse login(String email, String password) {
85-
return webTestClient.post()
86-
.uri("/auth/login")
87-
.contentType(MediaType.APPLICATION_JSON)
88-
.bodyValue(Map.of(
89-
"email", email,
90-
"password", password
91-
))
92-
.exchange()
93-
.expectStatus().isOk()
94-
.expectBody(AuthResponse.class)
95-
.returnResult()
96-
.getResponseBody();
97-
}
98-
// [3] - GRAPHQL QUERY:
99-
private String graphQLQuery(String token, String query) {
100-
return webTestClient.post()
101-
.uri("/graphql")
102-
.header(HttpHeaders.AUTHORIZATION, "Bearer " + token)
103-
.contentType(MediaType.APPLICATION_JSON)
104-
.bodyValue(Map.of("query", query))
105-
.exchange()
106-
.expectStatus().isOk()
107-
.expectBody(String.class)
108-
.returnResult()
109-
.getResponseBody();
110-
}
111-
// [4] - CREATE TASK (for specific User):
112-
private String createTaskForUser(String token, String payload, String type) throws Exception {
35+
//private static String simonTaskId;
36+
//private static final String password = "who_cares";
37+
private static String alice_email; // = "alice@gmail.com";
38+
private static String simon_email; // = "simon@gmail.com";
39+
40+
// HELPER METHODS (specific to this test file):
41+
private String createTask(String token, String payload) {
11342
String mutation = """
114-
mutation {
115-
createTask(input: { payload: "%s", type: %s }) {
116-
id
117-
status
118-
}
119-
}
120-
""".formatted(payload, type);
121-
String json = graphQLQuery(token, mutation);
122-
JsonNode root = objectMapper.readTree(json);
123-
JsonNode idNode = root.path("data").path("createTask").path("id");
124-
assertThat(idNode.isMissingNode()).isFalse();
125-
return idNode.asText();
43+
mutation {
44+
createTask(input: {
45+
payload: "%s"
46+
type: EMAIL
47+
}) {
48+
id
49+
}
50+
}
51+
""".formatted(payload);
52+
53+
AtomicReference<String> taskId = new AtomicReference<>();
54+
55+
graphQLWithToken(token, mutation)
56+
.expectStatus().isOk()
57+
.expectBody()
58+
.jsonPath("$.data.createTask.id")
59+
.value(id -> taskId.set((String) id));
60+
61+
return taskId.get();
12662
}
12763

128-
// TESTS (they'll work in sequence with enforced ordering. When I refactor and clean things up, this ordering stays):
129-
// Test #1 - Setup: Register both users, login, and then store tokens:
64+
// TESTS:
13065
@Test
131-
@Order(1)
132-
void registerTestUsers_andLogin_thenStoreTokens() {
133-
register(alice_email, password); // Register Alice.
134-
register(simon_email, password); // Register Simon.
135-
// Login and then record tokens in the global variables.
136-
AuthResponse aliceAuth = login(alice_email, password);
137-
AuthResponse simonAuth = login(simon_email, password);
138-
aliceToken = aliceAuth.accessToken();
139-
simonToken = simonAuth.accessToken();
140-
// Make sure the login worked and returned tokens basically:
141-
assertThat(aliceToken).isNotBlank();
142-
assertThat(simonToken).isNotBlank();
66+
void userCanSeeOnlyTheirOwnTasks() {
67+
AuthResponse alice = registerAndLogin("alice@test.com", "pw");
68+
AuthResponse simon = registerAndLogin("simon@test.com", "pw");
69+
70+
String aliceTaskId = createTask(alice.accessToken(), "alice-task");
71+
72+
// Alice sees her task
73+
graphQLWithToken(alice.accessToken(), """
74+
query {
75+
tasks {
76+
id
77+
}
78+
}
79+
""")
80+
.expectStatus().isOk()
81+
.expectBody()
82+
.jsonPath("$.data.tasks[*].id")
83+
.value(ids -> assertThat((Iterable<?>) ids).contains(aliceTaskId));
84+
85+
// Simon does NOT see Alice's task
86+
graphQLWithToken(simon.accessToken(), """
87+
query {
88+
tasks {
89+
id
90+
}
91+
}
92+
""")
93+
.expectStatus().isOk()
94+
.expectBody()
95+
.jsonPath("$.data.tasks[*].id")
96+
.value(ids -> assertThat((Iterable<?>) ids).doesNotContain(aliceTaskId));
14397
}
14498

145-
// Test #2 - Alice creates a task and can see it.
14699
@Test
147-
@Order(2)
148-
void userOne_CreatesTask_andCanSeeIt() throws Exception {
149-
aliceTaskId = createTaskForUser(aliceToken, "Alice send email", "EMAIL");
150-
assertThat(aliceTaskId).isNotBlank();
151-
String json = graphQLQuery(aliceToken, """
152-
query {
153-
tasks {
154-
id
155-
status
156-
}
157-
}
158-
""");
159-
JsonNode root = objectMapper.readTree(json);
160-
JsonNode arr = root.path("data").path("tasks");
161-
assertThat(arr.isArray()).isTrue();
162-
assertThat(arr.size()).isGreaterThanOrEqualTo(1);
163-
boolean found = false;
164-
for (JsonNode t : arr) {
165-
if (aliceTaskId.equals(t.path("id").asText())) {
166-
found = true;
167-
break;
100+
void userCannotFetchAnotherUsersTaskById() {
101+
AuthResponse alice = registerAndLogin("alice2@test.com", "pw");
102+
AuthResponse simon = registerAndLogin("simon2@test.com", "pw");
103+
104+
String aliceTaskId = createTask(alice.accessToken(), "private-task");
105+
106+
graphQLWithToken(simon.accessToken(), """
107+
query {
108+
task(id: "%s") {
109+
id
110+
}
168111
}
169-
}
170-
assertThat(found).isTrue();
112+
""".formatted(aliceTaskId))
113+
.expectStatus().isOk()
114+
.expectBody()
115+
.jsonPath("$.data.task").isEmpty();
171116
}
172117

173-
// Test #3 - Simon cannot see Alice's created task.
174118
@Test
175-
@Order(3)
176-
void userTwo_cannotSee_priorTask() throws Exception {
177-
String json = graphQLQuery(simonToken, """
178-
query {
179-
tasks {
180-
id
181-
status
182-
}
183-
}
184-
""");
185-
JsonNode root = objectMapper.readTree(json);
186-
JsonNode arr = root.path("data").path("tasks");
187-
assertThat(arr.isArray()).isTrue();
188-
for (JsonNode t : arr) {
189-
assertThat(t.path("id").asText()).isNotEqualTo(aliceTaskId);
190-
}
119+
void userCannotUpdateAnotherUsersTask() {
120+
AuthResponse alice = registerAndLogin("alice3@test.com", "pw");
121+
AuthResponse simon = registerAndLogin("simon3@test.com", "pw");
122+
123+
String aliceTaskId = createTask(alice.accessToken(), "immutable-task");
124+
125+
graphQLWithToken(simon.accessToken(), """
126+
mutation {
127+
updateTask(input: {
128+
id: "%s",
129+
status: COMPLETED,
130+
attempts: 999
131+
}) {
132+
id
133+
}
134+
}
135+
""".formatted(aliceTaskId))
136+
.expectStatus().isOk()
137+
.expectBody()
138+
.jsonPath("$.data.updateTask").isEmpty();
191139
}
192140

193-
// Test #4 - Simon cannot fetch Alice's task by ID.
194141
@Test
195-
@Order(4)
196-
void userTwo_cannotFetch_priorTaskById() throws Exception {
197-
String json = graphQLQuery(simonToken, """
198-
query {
199-
task(id: "%s") {
200-
id
201-
status
202-
}
203-
}
204-
""".formatted(aliceTaskId));
205-
JsonNode root = objectMapper.readTree(json);
206-
JsonNode node = root.path("data").path("task");
207-
assertThat(node.isNull()).isTrue();
142+
void userCannotDeleteAnotherUsersTask() {
143+
AuthResponse alice = registerAndLogin("alice4@test.com", "pw");
144+
AuthResponse simon = registerAndLogin("simon4@test.com", "pw");
145+
146+
String aliceTaskId = createTask(alice.accessToken(), "delete-protected");
147+
148+
graphQLWithToken(simon.accessToken(), """
149+
mutation {
150+
deleteTask(id: "%s")
151+
}
152+
""".formatted(aliceTaskId))
153+
.expectStatus().isOk()
154+
.expectBody()
155+
.jsonPath("$.data.deleteTask").isEqualTo(false);
208156
}
209157

210-
// Test #5 - Simon cannot update Alice's task.
211158
@Test
212-
@Order(5)
213-
void userTwo_cannotUpdate_priorTask() throws Exception {
214-
String mutation = """
215-
mutation {
216-
updateTask(input: {
217-
id: "%s",
218-
status: COMPLETED,
219-
attempts: 999
220-
}) {
221-
id
222-
status
223-
}
224-
}
225-
""".formatted(aliceTaskId);
226-
String json = graphQLQuery(simonToken, mutation);
227-
JsonNode root = objectMapper.readTree(json);
228-
JsonNode dataNode = root.path("data").path("updateTask");
229-
assertThat(dataNode.isNull()).isTrue();
230-
JsonNode errors = root.path("errors");
231-
assertThat(errors.isMissingNode() || errors.isArray()).isTrue();
159+
void userCannotRetryAnotherUsersTask() {
160+
AuthResponse alice = registerAndLogin("alice5@test.com", "pw");
161+
AuthResponse simon = registerAndLogin("simon5@test.com", "pw");
162+
163+
String aliceTaskId = createTask(alice.accessToken(), "retry-protected");
164+
165+
graphQLWithToken(simon.accessToken(), """
166+
mutation {
167+
retryTask(id: "%s")
168+
}
169+
""".formatted(aliceTaskId))
170+
.expectStatus().isOk()
171+
.expectBody()
172+
.jsonPath("$.data.retryTask").isEqualTo(false);
232173
}
233174
}

0 commit comments

Comments
 (0)