Skip to content

Commit 31ae0cd

Browse files
authored
[chore] add document service tests (#21)
1 parent 9330e3a commit 31ae0cd

4 files changed

Lines changed: 269 additions & 1 deletion

File tree

server/document-service/build.gradle

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,9 @@ dependencies {
5353

5454
testImplementation 'org.springframework.boot:spring-boot-starter-test'
5555
testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
56+
testImplementation 'org.testcontainers:junit-jupiter:1.21.2'
57+
testImplementation 'org.testcontainers:minio:1.21.2'
58+
testImplementation 'org.testcontainers:weaviate:1.21.2'
5659
}
5760

5861
dependencyManagement {
Lines changed: 176 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,176 @@
1+
package de.tum.team_sigma.document_service.integration;
2+
3+
import io.minio.*;
4+
import io.minio.errors.ErrorResponseException;
5+
import io.weaviate.client.WeaviateClient;
6+
import io.weaviate.client.base.Result;
7+
import io.weaviate.client.v1.data.model.WeaviateObject;
8+
import io.weaviate.client.v1.schema.model.Property;
9+
import io.weaviate.client.v1.schema.model.WeaviateClass;
10+
import org.junit.jupiter.api.Test;
11+
import org.junit.jupiter.api.BeforeAll;
12+
import org.junit.jupiter.api.Disabled;
13+
import org.springframework.beans.factory.annotation.Autowired;
14+
import org.springframework.boot.test.context.SpringBootTest;
15+
import org.springframework.test.context.ActiveProfiles;
16+
import org.springframework.test.context.DynamicPropertyRegistry;
17+
import org.springframework.test.context.DynamicPropertySource;
18+
import org.testcontainers.containers.MinIOContainer;
19+
import org.testcontainers.junit.jupiter.Container;
20+
import org.testcontainers.junit.jupiter.Testcontainers;
21+
import org.testcontainers.weaviate.WeaviateContainer;
22+
23+
import java.io.ByteArrayInputStream;
24+
import java.nio.charset.StandardCharsets;
25+
import java.util.Map;
26+
import java.util.UUID;
27+
28+
import static org.junit.jupiter.api.Assertions.*;
29+
30+
/**
31+
* Integration tests that require real MinIO and Weaviate containers.
32+
* These tests are disabled by default due to longer startup times.
33+
* Enable with @EnabledIf or remove @Disabled to run.
34+
*/
35+
@Testcontainers
36+
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.NONE)
37+
@ActiveProfiles("test")
38+
@Disabled("Enable manually when testing with real containers")
39+
class ContainerIntegrationTest {
40+
41+
private static final String TEST_BUCKET = "testbucket";
42+
43+
@Container
44+
static final MinIOContainer MINIO = new MinIOContainer("minio/minio:RELEASE.2023-09-04T19-57-37Z");
45+
46+
@Container
47+
static final WeaviateContainer WEAVIATE = new WeaviateContainer("cr.weaviate.io/semitechnologies/weaviate:1.25.5");
48+
49+
@DynamicPropertySource
50+
static void registerProperties(DynamicPropertyRegistry registry) {
51+
registry.add("minio.url", MINIO::getS3URL);
52+
registry.add("minio.access-key", MINIO::getUserName);
53+
registry.add("minio.secret-key", MINIO::getPassword);
54+
registry.add("minio.bucket-name", () -> TEST_BUCKET);
55+
registry.add("weaviate.url", () -> WEAVIATE.getHost() + ":" + WEAVIATE.getMappedPort(8080));
56+
}
57+
58+
@Autowired
59+
private MinioClient minioClient;
60+
61+
@Autowired
62+
private WeaviateClient weaviateClient;
63+
64+
@BeforeAll
65+
void setUp() throws Exception {
66+
// Create test bucket in MinIO
67+
boolean found = minioClient.bucketExists(BucketExistsArgs.builder().bucket(TEST_BUCKET).build());
68+
if (!found) {
69+
minioClient.makeBucket(MakeBucketArgs.builder().bucket(TEST_BUCKET).build());
70+
}
71+
}
72+
73+
/* ----------------- MinIO Integration Tests ----------------- */
74+
@Test
75+
void minioShouldPutAndRemoveObject() throws Exception {
76+
String objectKey = "integration/hello.txt";
77+
byte[] data = "Hello, MinIO!".getBytes(StandardCharsets.UTF_8);
78+
79+
// Upload object
80+
minioClient.putObject(
81+
PutObjectArgs.builder()
82+
.bucket(TEST_BUCKET)
83+
.object(objectKey)
84+
.stream(new ByteArrayInputStream(data), data.length, -1)
85+
.contentType("text/plain")
86+
.build()
87+
);
88+
89+
// Verify exists
90+
StatObjectResponse stat = minioClient.statObject(
91+
StatObjectArgs.builder().bucket(TEST_BUCKET).object(objectKey).build()
92+
);
93+
assertNotNull(stat);
94+
assertEquals(data.length, stat.size());
95+
96+
// Download and verify content
97+
try (var inputStream = minioClient.getObject(
98+
GetObjectArgs.builder().bucket(TEST_BUCKET).object(objectKey).build())) {
99+
String content = new String(inputStream.readAllBytes(), StandardCharsets.UTF_8);
100+
assertEquals("Hello, MinIO!", content);
101+
}
102+
103+
// Delete object
104+
minioClient.removeObject(
105+
RemoveObjectArgs.builder().bucket(TEST_BUCKET).object(objectKey).build()
106+
);
107+
108+
// Expect deletion
109+
assertThrows(ErrorResponseException.class, () ->
110+
minioClient.statObject(
111+
StatObjectArgs.builder().bucket(TEST_BUCKET).object(objectKey).build()
112+
));
113+
}
114+
115+
/* ----------------- Weaviate Integration Tests ----------------- */
116+
@Test
117+
void weaviateShouldInsertAndDeleteObject() {
118+
String className = "TestEntity";
119+
String id = UUID.randomUUID().toString();
120+
121+
// Ensure class exists (idempotent)
122+
try {
123+
WeaviateClass clazz = WeaviateClass.builder()
124+
.className(className)
125+
.vectorizer("none")
126+
.properties(java.util.List.of(
127+
Property.builder()
128+
.name("text")
129+
.dataType(java.util.List.of("text"))
130+
.build()
131+
))
132+
.build();
133+
weaviateClient.schema().classCreator().withClass(clazz).run();
134+
} catch (Exception ignored) {
135+
// class may already exist; ignore
136+
}
137+
138+
Float[] vector = new Float[]{0.1f, 0.2f, 0.3f, 0.4f, 0.5f};
139+
140+
// Insert object
141+
Result<WeaviateObject> createResult = weaviateClient.data().creator()
142+
.withClassName(className)
143+
.withID(id)
144+
.withVector(vector)
145+
.withProperties(Map.of("text", "hello world"))
146+
.run();
147+
assertFalse(createResult.hasErrors(), "Object creation should succeed: " + createResult.getError());
148+
149+
// Retrieve object
150+
Result<java.util.List<WeaviateObject>> getResult = weaviateClient.data().objectsGetter()
151+
.withID(id)
152+
.run();
153+
assertFalse(getResult.hasErrors(), "Object retrieval should succeed: " + getResult.getError());
154+
assertEquals(1, getResult.getResult().size(), "Should retrieve exactly one object");
155+
156+
// Verify the retrieved object
157+
WeaviateObject retrievedObject = getResult.getResult().get(0);
158+
assertEquals(id, retrievedObject.getId());
159+
assertEquals("hello world", retrievedObject.getProperties().get("text"));
160+
161+
// Delete object
162+
Result<Boolean> deleteResult = weaviateClient.data().deleter()
163+
.withID(id)
164+
.withClassName(className)
165+
.run();
166+
assertFalse(deleteResult.hasErrors(), "Deletion should succeed: " + deleteResult.getError());
167+
assertTrue(deleteResult.getResult(), "Deletion result should be true");
168+
169+
// Confirm deletion (should return empty list)
170+
Result<java.util.List<WeaviateObject>> afterDelete = weaviateClient.data().objectsGetter()
171+
.withID(id)
172+
.run();
173+
assertTrue(afterDelete.getResult() == null || afterDelete.getResult().isEmpty(),
174+
"Object should no longer exist");
175+
}
176+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
package de.tum.team_sigma.document_service.integration;
2+
3+
import dev.langchain4j.data.embedding.Embedding;
4+
import dev.langchain4j.model.embedding.EmbeddingModel;
5+
import io.minio.*;
6+
import io.minio.errors.ErrorResponseException;
7+
import io.weaviate.client.WeaviateClient;
8+
import io.weaviate.client.base.Result;
9+
import io.weaviate.client.v1.data.model.WeaviateObject;
10+
import io.weaviate.client.v1.schema.model.Property;
11+
import io.weaviate.client.v1.schema.model.WeaviateClass;
12+
import io.swagger.v3.oas.models.OpenAPI;
13+
import org.junit.jupiter.api.Test;
14+
import org.junit.jupiter.api.TestInstance;
15+
import org.junit.jupiter.api.BeforeAll;
16+
import org.springframework.beans.factory.annotation.Autowired;
17+
import org.springframework.boot.test.context.SpringBootTest;
18+
import org.springframework.test.context.DynamicPropertyRegistry;
19+
import org.springframework.test.context.DynamicPropertySource;
20+
import org.springframework.test.context.ActiveProfiles;
21+
import org.testcontainers.junit.jupiter.Container;
22+
import org.testcontainers.junit.jupiter.Testcontainers;
23+
import org.testcontainers.containers.MinIOContainer;
24+
import org.testcontainers.weaviate.WeaviateContainer;
25+
26+
import java.io.ByteArrayInputStream;
27+
import java.nio.charset.StandardCharsets;
28+
import java.util.Map;
29+
import java.util.UUID;
30+
31+
import static org.junit.jupiter.api.Assertions.*;
32+
33+
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.NONE)
34+
@ActiveProfiles("test")
35+
class ExternalServicesIntegrationTest {
36+
37+
@Autowired
38+
private EmbeddingModel embeddingModel;
39+
40+
@Autowired
41+
private OpenAPI openAPI;
42+
43+
/* ----------------- Embedding ----------------- */
44+
@Test
45+
void embeddingModelShouldReturnVector() {
46+
Embedding embedding = embeddingModel.embed("Hello vector world").content();
47+
assertNotNull(embedding, "Embedding should not be null");
48+
assertTrue(embedding.vector().length > 0, "Vector should contain values");
49+
50+
// Test that different texts produce different vectors
51+
Embedding embedding2 = embeddingModel.embed("Different text").content();
52+
assertNotEquals(embedding.vector()[0], embedding2.vector()[0], "Different texts should produce different vectors");
53+
}
54+
55+
/* ----------------- OpenAPI ----------------- */
56+
@Test
57+
void openApiBeanShouldBeConfigured() {
58+
assertNotNull(openAPI, "OpenAPI bean should be present");
59+
assertEquals("Document Service API", openAPI.getInfo().getTitle());
60+
assertNotNull(openAPI.getInfo().getVersion(), "Version should be set");
61+
assertNotNull(openAPI.getInfo().getDescription(), "Description should be set");
62+
}
63+
64+
/* ----------------- Configuration Tests ----------------- */
65+
@Test
66+
void embeddingModelShouldProduceConsistentVectorSizes() {
67+
// Test that all embeddings have the same vector size
68+
Embedding embedding1 = embeddingModel.embed("First test text").content();
69+
Embedding embedding2 = embeddingModel.embed("Second test text").content();
70+
Embedding embedding3 = embeddingModel.embed("Third test text").content();
71+
72+
assertEquals(embedding1.vector().length, embedding2.vector().length,
73+
"All embeddings should have the same vector size");
74+
assertEquals(embedding2.vector().length, embedding3.vector().length,
75+
"All embeddings should have the same vector size");
76+
77+
// Verify vector size is reasonable (typical embedding models produce 384 or 768 dimensions)
78+
assertTrue(embedding1.vector().length >= 100, "Vector should have reasonable size");
79+
}
80+
}

server/document-service/src/test/resources/application-test.properties

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,13 @@ spring.datasource.driver-class-name=org.h2.Driver
33
spring.datasource.username=sa
44
spring.datasource.password=
55
spring.jpa.database-platform=org.hibernate.dialect.H2Dialect
6-
spring.jpa.hibernate.ddl-auto=update
6+
spring.jpa.hibernate.ddl-auto=update
7+
8+
# Disable Eureka client for tests
9+
eureka.client.enabled=false
10+
eureka.client.register-with-eureka=false
11+
eureka.client.fetch-registry=false
12+
13+
# Disable other unnecessary services for integration tests
14+
spring.cloud.discovery.enabled=false
15+
management.endpoints.enabled=false

0 commit comments

Comments
 (0)