Skip to content

Commit 43528cf

Browse files
alxkmilayaperumalg
authored andcommitted
test: Add comprehensive test coverage for observation and vector store components
Signed-off-by: Oleksandr Klymenko <alexanderklmn@gmail.com> Co-authored-by: Oleksandr Klymenko <alexanderklmn@gmail.com>
1 parent e56eb12 commit 43528cf

File tree

3 files changed

+258
-0
lines changed

3 files changed

+258
-0
lines changed

spring-ai-model/src/test/java/org/springframework/ai/chat/observation/ChatModelMeterObservationHandlerTests.java

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,88 @@ void shouldCreateAllMetersDuringAnObservation() {
9393
.meters()).hasSize(1);
9494
}
9595

96+
@Test
97+
void shouldHandleNullUsageGracefully() {
98+
var observationContext = generateObservationContext();
99+
var observation = Observation
100+
.createNotStarted(new DefaultChatModelObservationConvention(), () -> observationContext,
101+
this.observationRegistry)
102+
.start();
103+
104+
observationContext.setResponse(new ChatResponse(List.of(new Generation(new AssistantMessage("test"))),
105+
ChatResponseMetadata.builder().model("model").usage(null).build()));
106+
107+
observation.stop();
108+
109+
assertThat(this.meterRegistry.getMeters())
110+
.noneMatch(meter -> meter.getId().getName().equals(AiObservationMetricNames.TOKEN_USAGE.value()));
111+
}
112+
113+
@Test
114+
void shouldHandleNullResponseMetadata() {
115+
var observationContext = generateObservationContext();
116+
var observation = Observation
117+
.createNotStarted(new DefaultChatModelObservationConvention(), () -> observationContext,
118+
this.observationRegistry)
119+
.start();
120+
121+
observationContext.setResponse(new ChatResponse(List.of(new Generation(new AssistantMessage("test"))), null));
122+
123+
observation.stop();
124+
125+
assertThat(this.meterRegistry.getMeters())
126+
.noneMatch(meter -> meter.getId().getName().equals(AiObservationMetricNames.TOKEN_USAGE.value()));
127+
}
128+
129+
@Test
130+
void shouldHandleEmptyGenerations() {
131+
var observationContext = generateObservationContext();
132+
var observation = Observation
133+
.createNotStarted(new DefaultChatModelObservationConvention(), () -> observationContext,
134+
this.observationRegistry)
135+
.start();
136+
137+
observationContext.setResponse(new ChatResponse(List.of(),
138+
ChatResponseMetadata.builder().model("model").usage(new TestUsage()).build()));
139+
140+
observation.stop();
141+
142+
assertThat(this.meterRegistry.get(AiObservationMetricNames.TOKEN_USAGE.value()).meters()).hasSize(3);
143+
}
144+
145+
@Test
146+
void shouldHandleMultipleGenerations() {
147+
var observationContext = generateObservationContext();
148+
var observation = Observation
149+
.createNotStarted(new DefaultChatModelObservationConvention(), () -> observationContext,
150+
this.observationRegistry)
151+
.start();
152+
153+
var generations = List.of(new Generation(new AssistantMessage("response1")),
154+
new Generation(new AssistantMessage("response2")), new Generation(new AssistantMessage("response3")));
155+
156+
observationContext.setResponse(new ChatResponse(generations,
157+
ChatResponseMetadata.builder().model("model").usage(new TestUsage()).build()));
158+
159+
observation.stop();
160+
161+
assertThat(this.meterRegistry.get(AiObservationMetricNames.TOKEN_USAGE.value()).meters()).hasSize(3);
162+
}
163+
164+
@Test
165+
void shouldHandleObservationWithoutResponse() {
166+
var observationContext = generateObservationContext();
167+
var observation = Observation
168+
.createNotStarted(new DefaultChatModelObservationConvention(), () -> observationContext,
169+
this.observationRegistry)
170+
.start();
171+
172+
observation.stop();
173+
174+
assertThat(this.meterRegistry.getMeters())
175+
.noneMatch(meter -> meter.getId().getName().equals(AiObservationMetricNames.TOKEN_USAGE.value()));
176+
}
177+
96178
private ChatModelObservationContext generateObservationContext() {
97179
return ChatModelObservationContext.builder()
98180
.prompt(generatePrompt(ChatOptions.builder().model("mistral").build()))

spring-ai-vector-store/src/test/java/org/springframework/ai/vectorstore/SimpleVectorStoreSimilarityTests.java

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
import org.springframework.ai.document.Document;
2525

2626
import static org.assertj.core.api.Assertions.assertThat;
27+
import static org.assertj.core.api.Assertions.assertThatThrownBy;
2728

2829
/**
2930
* @author Ilayaperumal Gopinathan
@@ -46,4 +47,88 @@ public void testSimilarity() {
4647
assertThat(document.getMetadata().get("foo")).isEqualTo("bar");
4748
}
4849

50+
@Test
51+
public void testEmptyId() {
52+
Map<String, Object> metadata = new HashMap<>();
53+
float[] embedding = new float[] { 1.0f };
54+
55+
assertThatThrownBy(() -> new SimpleVectorStoreContent("", "text content", metadata, embedding))
56+
.isInstanceOf(IllegalArgumentException.class)
57+
.hasMessageContaining("id must not be null or empty");
58+
}
59+
60+
@Test
61+
public void testEmptyEmbeddingArray() {
62+
Map<String, Object> metadata = new HashMap<>();
63+
float[] emptyEmbedding = new float[0];
64+
65+
assertThatThrownBy(() -> new SimpleVectorStoreContent("valid-id", "text content", metadata, emptyEmbedding))
66+
.isInstanceOf(IllegalArgumentException.class)
67+
.hasMessageContaining("embedding vector must not be empty");
68+
}
69+
70+
@Test
71+
public void testSingleElementEmbedding() {
72+
Map<String, Object> metadata = new HashMap<>();
73+
float[] singleEmbedding = new float[] { 0.1f };
74+
75+
SimpleVectorStoreContent storeContent = new SimpleVectorStoreContent("id-1", "text", metadata, singleEmbedding);
76+
Document document = storeContent.toDocument(0.1);
77+
78+
assertThat(document).isNotNull();
79+
assertThat(document.getScore()).isEqualTo(0.1);
80+
}
81+
82+
@Test
83+
public void testNullMetadata() {
84+
float[] embedding = new float[] { 1.0f };
85+
86+
assertThatThrownBy(() -> new SimpleVectorStoreContent("id-1", "text", null, embedding))
87+
.isInstanceOf(IllegalArgumentException.class)
88+
.hasMessageContaining("metadata must not be null");
89+
}
90+
91+
@Test
92+
public void testMetadataImmutability() {
93+
Map<String, Object> originalMetadata = new HashMap<>();
94+
originalMetadata.put("key", "original");
95+
float[] embedding = new float[] { 1.0f };
96+
97+
SimpleVectorStoreContent storeContent = new SimpleVectorStoreContent("id-1", "text", originalMetadata,
98+
embedding);
99+
100+
originalMetadata.put("key", "modified");
101+
originalMetadata.put("new", "value");
102+
103+
Document document = storeContent.toDocument(0.5);
104+
105+
assertThat(document.getMetadata().get("key")).isEqualTo("original");
106+
assertThat(document.getMetadata()).doesNotContainKey("new");
107+
}
108+
109+
@Test
110+
public void testWhitespaceOnlyText() {
111+
Map<String, Object> metadata = new HashMap<>();
112+
float[] embedding = new float[] { 1.0f };
113+
String[] whitespaceTexts = { " ", "\t\t", "\n\n", "\r\n", " \t\n\r " };
114+
115+
for (String whitespace : whitespaceTexts) {
116+
SimpleVectorStoreContent storeContent = new SimpleVectorStoreContent("ws-id", whitespace, metadata,
117+
embedding);
118+
Document document = storeContent.toDocument(0.1);
119+
assertThat(document.getText()).isEqualTo(whitespace);
120+
}
121+
}
122+
123+
@Test
124+
public void testEmptyStringText() {
125+
Map<String, Object> metadata = new HashMap<>();
126+
float[] embedding = new float[] { 1.0f };
127+
128+
SimpleVectorStoreContent storeContent = new SimpleVectorStoreContent("empty-id", "", metadata, embedding);
129+
Document document = storeContent.toDocument(0.1);
130+
131+
assertThat(document.getText()).isEmpty();
132+
}
133+
49134
}

spring-ai-vector-store/src/test/java/org/springframework/ai/vectorstore/observation/VectorStoreObservationContextTests.java

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,4 +50,95 @@ void whenOperationNameIsNullThenThrow() {
5050
.hasMessageContaining("operationName cannot be null or empty");
5151
}
5252

53+
@Test
54+
void whenEmptyDbSystemThenThrow() {
55+
assertThatThrownBy(
56+
() -> VectorStoreObservationContext.builder("", VectorStoreObservationContext.Operation.ADD).build())
57+
.isInstanceOf(IllegalArgumentException.class)
58+
.hasMessageContaining("databaseSystem cannot be null or empty");
59+
}
60+
61+
@Test
62+
void whenWhitespaceDbSystemThenThrow() {
63+
assertThatThrownBy(
64+
() -> VectorStoreObservationContext.builder(" ", VectorStoreObservationContext.Operation.ADD).build())
65+
.isInstanceOf(IllegalArgumentException.class)
66+
.hasMessageContaining("databaseSystem cannot be null or empty");
67+
}
68+
69+
@Test
70+
void whenStringOperationNameUsedThenCorrectValue() {
71+
var observationContext = VectorStoreObservationContext.builder("testdb", "custom_operation").build();
72+
assertThat(observationContext.getDatabaseSystem()).isEqualTo("testdb");
73+
assertThat(observationContext.getOperationName()).isEqualTo("custom_operation");
74+
}
75+
76+
@Test
77+
void whenCollectionNameProvidedThenSet() {
78+
var observationContext = VectorStoreObservationContext
79+
.builder("db", VectorStoreObservationContext.Operation.ADD)
80+
.collectionName("documents")
81+
.build();
82+
83+
assertThat(observationContext.getCollectionName()).isEqualTo("documents");
84+
}
85+
86+
@Test
87+
void whenNoCollectionNameProvidedThenNull() {
88+
var observationContext = VectorStoreObservationContext
89+
.builder("db", VectorStoreObservationContext.Operation.ADD)
90+
.build();
91+
92+
assertThat(observationContext.getCollectionName()).isNull();
93+
}
94+
95+
@Test
96+
void whenNoDimensionsProvidedThenNull() {
97+
var observationContext = VectorStoreObservationContext
98+
.builder("db", VectorStoreObservationContext.Operation.QUERY)
99+
.build();
100+
101+
assertThat(observationContext.getDimensions()).isNull();
102+
}
103+
104+
@Test
105+
void whenFieldNameProvidedThenSet() {
106+
var observationContext = VectorStoreObservationContext
107+
.builder("db", VectorStoreObservationContext.Operation.QUERY)
108+
.fieldName("embedding_vector")
109+
.build();
110+
111+
assertThat(observationContext.getFieldName()).isEqualTo("embedding_vector");
112+
}
113+
114+
@Test
115+
void whenNamespaceProvidedThenSet() {
116+
var observationContext = VectorStoreObservationContext
117+
.builder("db", VectorStoreObservationContext.Operation.ADD)
118+
.namespace("production")
119+
.build();
120+
121+
assertThat(observationContext.getNamespace()).isEqualTo("production");
122+
}
123+
124+
@Test
125+
void whenSimilarityMetricProvidedThenSet() {
126+
var observationContext = VectorStoreObservationContext
127+
.builder("db", VectorStoreObservationContext.Operation.QUERY)
128+
.similarityMetric("cosine")
129+
.build();
130+
131+
assertThat(observationContext.getSimilarityMetric()).isEqualTo("cosine");
132+
}
133+
134+
@Test
135+
void whenEmptyCollectionNameThenSet() {
136+
var observationContext = VectorStoreObservationContext
137+
.builder("db", VectorStoreObservationContext.Operation.ADD)
138+
.collectionName("")
139+
.build();
140+
141+
assertThat(observationContext.getCollectionName()).isEmpty();
142+
}
143+
53144
}

0 commit comments

Comments
 (0)