Skip to content

Commit a6df9f8

Browse files
committed
Add integration test to verify LocalDate is not shifted when read and also improve intergration tests setup
1 parent 304a258 commit a6df9f8

5 files changed

Lines changed: 117 additions & 104 deletions

File tree

mflix/server/java-spring/pom.xml

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
<impsort.plugin.version>1.13.0</impsort.plugin.version>
2727
<byte-buddy.version>1.17.8</byte-buddy.version>
2828
<langchain4j.version>1.11.0-beta19</langchain4j.version>
29+
<testcontainers.version>1.21.4</testcontainers.version>
2930
</properties>
3031

3132
<dependencies>
@@ -88,6 +89,20 @@
8889
<scope>test</scope>
8990
</dependency>
9091

92+
<!-- Testcontainers JUnit Jupiter integration -->
93+
<dependency>
94+
<groupId>org.testcontainers</groupId>
95+
<artifactId>junit-jupiter</artifactId>
96+
<scope>test</scope>
97+
</dependency>
98+
99+
<!-- MongoDB Atlas Local container for Testcontainers -->
100+
<dependency>
101+
<groupId>org.testcontainers</groupId>
102+
<artifactId>mongodb</artifactId>
103+
<scope>test</scope>
104+
</dependency>
105+
91106
<!-- Jackson Databind for JSON serialization/deserialization -->
92107
<dependency>
93108
<groupId>com.fasterxml.jackson.core</groupId>

mflix/server/java-spring/src/test/java/com/mongodb/samplemflix/integration/MongoDBSearchIntegrationTest.java

Lines changed: 21 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,9 @@
1414
import java.util.Collections;
1515
import java.util.List;
1616
import java.util.TimeZone;
17+
import lombok.extern.slf4j.Slf4j;
1718
import org.bson.BsonDateTime;
1819
import org.bson.Document;
19-
import org.bson.types.ObjectId;
2020
import org.junit.jupiter.api.AfterAll;
2121
import org.junit.jupiter.api.BeforeAll;
2222
import org.junit.jupiter.api.DisplayName;
@@ -32,21 +32,18 @@
3232
/**
3333
* Integration tests for MongoDB Search functionality.
3434
*
35-
* <p>These tests verify the MongoDB Search endpoints work correctly with a real MongoDB Atlas instance.
36-
* The tests require:
37-
* <ul>
38-
* <li>A MongoDB Atlas cluster (not local MongoDB)</li>
39-
* <li>MONGODB_URI environment variable pointing to Atlas</li>
40-
* <li>MongoDB Search index creation and polling for readiness</li>
41-
* </ul>
35+
* <p>These tests verify the MongoDB Search endpoints work correctly with a real
36+
* MongoDB instance. By default, a MongoDBAtlasLocalContainer (Docker) is started
37+
* automatically, providing a local Atlas environment with Search support.
4238
*
43-
* <p>Note: These tests are disabled by default and should only be run against a test Atlas cluster.
44-
* To enable, set the environment variable ENABLE_SEARCH_TESTS=true
39+
* <p>To run against an external MongoDB Atlas cluster instead, set the
40+
* {@code MONGODB_URI} environment variable. When set, no container is started.
4541
*/
46-
@SpringBootTest
42+
@SpringBootTest(classes = MongoDBTestContainersConfig.class, webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
4743
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
4844
@ActiveProfiles("test")
4945
@DisplayName("MongoDB Search Integration Tests")
46+
@Slf4j
5047
class MongoDBSearchIntegrationTest {
5148

5249
@Autowired
@@ -64,13 +61,7 @@ class MongoDBSearchIntegrationTest {
6461

6562
@BeforeAll
6663
void setUp() throws Exception {
67-
// Skip tests if not running against Atlas
68-
if (!isSearchEnabled()) {
69-
System.out.println("Skipping MongoDB Search tests - ENABLE_SEARCH_TESTS not set");
70-
return;
71-
}
72-
73-
System.out.println("Setting up MongoDB Search integration tests...");
64+
log.info("Setting up MongoDB Search integration tests...");
7465

7566
// Create test data
7667
createTestMovies();
@@ -83,23 +74,19 @@ void setUp() throws Exception {
8374

8475
// Wait a bit for the newly created documents to be indexed
8576
// MongoDB Search indexes documents asynchronously
86-
System.out.println("Waiting for test documents to be indexed...");
77+
log.info("Waiting for test documents to be indexed...");
8778
try {
8879
Thread.sleep(10000); // Wait 10 seconds for indexing
8980
} catch (InterruptedException e) {
9081
Thread.currentThread().interrupt();
9182
}
9283

93-
System.out.println("MongoDB Search index is ready for testing");
84+
log.info("MongoDB Search index is ready for testing");
9485
}
9586

9687
@AfterAll
9788
void tearDown() {
98-
if (!isSearchEnabled()) {
99-
return;
100-
}
101-
102-
System.out.println("Cleaning up MongoDB Search test data...");
89+
log.info("Cleaning up MongoDB Search test data...");
10390

10491
// Clean up test movies
10592
if (!testMovieIds.isEmpty()) {
@@ -116,11 +103,6 @@ void tearDown() {
116103
@Test
117104
@DisplayName("Should search movies by plot using MongoDB Search")
118105
void testSearchMoviesByPlot_Success() {
119-
if (!isSearchEnabled()) {
120-
System.out.println("Skipping test - Search not enabled");
121-
return;
122-
}
123-
124106
// Act
125107
com.mongodb.samplemflix.model.dto.MovieSearchRequest searchRequest =
126108
com.mongodb.samplemflix.model.dto.MovieSearchRequest.builder()
@@ -145,11 +127,6 @@ void testSearchMoviesByPlot_Success() {
145127
@Test
146128
@DisplayName("Should return empty list when no movies match search query")
147129
void testSearchMoviesByPlot_NoResults() {
148-
if (!isSearchEnabled()) {
149-
System.out.println("Skipping test - Search not enabled");
150-
return;
151-
}
152-
153130
// Act - search for something that definitely doesn't exist
154131
com.mongodb.samplemflix.model.dto.MovieSearchRequest searchRequest =
155132
com.mongodb.samplemflix.model.dto.MovieSearchRequest.builder()
@@ -168,11 +145,6 @@ void testSearchMoviesByPlot_NoResults() {
168145
@Test
169146
@DisplayName("Should respect limit parameter in search")
170147
void testSearchMoviesByPlot_WithLimit() {
171-
if (!isSearchEnabled()) {
172-
System.out.println("Skipping test - Search not enabled");
173-
return;
174-
}
175-
176148
// Act
177149
com.mongodb.samplemflix.model.dto.MovieSearchRequest searchRequest =
178150
com.mongodb.samplemflix.model.dto.MovieSearchRequest.builder()
@@ -191,11 +163,6 @@ void testSearchMoviesByPlot_WithLimit() {
191163
@Test
192164
@DisplayName("Should support pagination with skip parameter")
193165
void testSearchMoviesByPlot_WithPagination() {
194-
if (!isSearchEnabled()) {
195-
System.out.println("Skipping test - Search not enabled");
196-
return;
197-
}
198-
199166
// Act - Get first page
200167
com.mongodb.samplemflix.model.dto.MovieSearchRequest firstPageRequest =
201168
com.mongodb.samplemflix.model.dto.MovieSearchRequest.builder()
@@ -235,11 +202,6 @@ void testSearchMoviesByPlot_WithPagination() {
235202
@Test
236203
@DisplayName("Should read BSON DateTime at midnight UTC as the correct LocalDate without date shift")
237204
void testReleasedFieldRoundTrip_NoDateShift() {
238-
if (!isSearchEnabled()) {
239-
System.out.println("Skipping test - Search not enabled");
240-
return;
241-
}
242-
243205
// The test movies were inserted with known BSON DateTime values at midnight UTC:
244206
// "Test Space Adventure" -> 2024-01-01T00:00:00Z (1704067200000)
245207
// "Test Mystery Movie" -> 2024-03-31T00:00:00Z (1711843200000)
@@ -284,13 +246,9 @@ private Movie findTestMovieByTitle(String title) {
284246

285247
// ==================== HELPER METHODS ====================
286248

287-
private boolean isSearchEnabled() {
288-
String enabled = System.getenv("ENABLE_SEARCH_TESTS");
289-
return "true".equalsIgnoreCase(enabled);
290-
}
291249

292250
private void createTestMovies() {
293-
System.out.println("Creating test movies...");
251+
log.info("Creating test movies...");
294252

295253
MongoCollection<Document> collection = mongoTemplate.getCollection("movies");
296254

@@ -320,11 +278,11 @@ private void createTestMovies() {
320278
testMovieIds.add(movie.getObjectId("_id").toHexString());
321279
});
322280

323-
System.out.println("Created " + testMovieIds.size() + " test movies");
281+
log.info("Created {} test movies", testMovieIds.size());
324282
}
325283

326284
private void createSearchIndex() throws Exception {
327-
System.out.println("Creating Search index...");
285+
log.info("Creating Search index...");
328286

329287
MongoCollection<Document> collection = mongoTemplate.getCollection("movies");
330288

@@ -336,7 +294,7 @@ private void createSearchIndex() throws Exception {
336294
.anyMatch(idx -> SEARCH_INDEX_NAME.equals(idx.getString("name")));
337295

338296
if (indexExists) {
339-
System.out.println("Search index already exists");
297+
log.info("Search index already exists");
340298
return;
341299
}
342300

@@ -371,15 +329,15 @@ private void createSearchIndex() throws Exception {
371329

372330
try {
373331
mongoTemplate.getDb().runCommand(createIndexCommand);
374-
System.out.println("Search index creation initiated");
332+
log.info("Search index creation initiated");
375333
} catch (Exception e) {
376-
System.err.println("Error creating search index: " + e.getMessage());
334+
log.error("Error creating search index: {}", e.getMessage());
377335
throw e;
378336
}
379337
}
380338

381339
private void waitForSearchIndexReady() throws Exception {
382-
System.out.println("Waiting for search index to be ready...");
340+
log.info("Waiting for search index to be ready...");
383341

384342
MongoCollection<Document> collection = mongoTemplate.getCollection("movies");
385343
long startTime = System.currentTimeMillis();
@@ -396,10 +354,10 @@ private void waitForSearchIndexReady() throws Exception {
396354

397355
if (searchIndex != null) {
398356
String status = searchIndex.getString("status");
399-
System.out.println("Index status: " + status);
357+
log.info("Index status: {}", status);
400358

401359
if ("READY".equals(status)) {
402-
System.out.println("Search index is ready!");
360+
log.info("Search index is ready!");
403361
return;
404362
}
405363
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
package com.mongodb.samplemflix.integration;
2+
3+
import org.springframework.boot.test.context.TestConfiguration;
4+
import org.springframework.context.annotation.Bean;
5+
import org.springframework.context.annotation.Condition;
6+
import org.springframework.context.annotation.ConditionContext;
7+
import org.springframework.context.annotation.Conditional;
8+
import org.springframework.core.type.AnnotatedTypeMetadata;
9+
import org.springframework.lang.NonNull;
10+
import org.springframework.test.context.DynamicPropertyRegistrar;
11+
import org.testcontainers.mongodb.MongoDBAtlasLocalContainer;
12+
13+
@TestConfiguration
14+
public class MongoDBTestContainersConfig {
15+
16+
@Bean
17+
@Conditional(MongoUriMissingCondition.class)
18+
public MongoDBAtlasLocalContainer mongoDbContainer() {
19+
return new MongoDBAtlasLocalContainer("mongodb/mongodb-atlas-local:8");
20+
}
21+
22+
@Bean
23+
@Conditional(MongoUriMissingCondition.class)
24+
public DynamicPropertyRegistrar mongoDbProperties(MongoDBAtlasLocalContainer mongoDBContainer) {
25+
mongoDBContainer.start();
26+
return (registry) -> {
27+
registry.add("spring.data.mongodb.uri", mongoDBContainer::getConnectionString);
28+
};
29+
}
30+
31+
static class MongoUriMissingCondition implements Condition {
32+
@Override
33+
public boolean matches(ConditionContext context, @NonNull AnnotatedTypeMetadata metadata) {
34+
String uri = context.getEnvironment().getProperty("spring.data.mongodb.uri");
35+
return uri == null || uri.isBlank();
36+
}
37+
}
38+
}

0 commit comments

Comments
 (0)