Skip to content

Commit 266232c

Browse files
alxkmchedim
authored andcommitted
test: Enhance test coverage for image model observations (spring-projects#4313)
1 parent 3f6319d commit 266232c

File tree

3 files changed

+159
-0
lines changed

3 files changed

+159
-0
lines changed

spring-ai-model/src/test/java/org/springframework/ai/image/observation/DefaultImageModelObservationConventionTests.java

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,83 @@ void shouldNotHaveKeyValuesWhenEmptyValues() {
120120
HighCardinalityKeyNames.REQUEST_IMAGE_STYLE.asString());
121121
}
122122

123+
@Test
124+
void shouldHandleNullModel() {
125+
ImageModelObservationContext observationContext = ImageModelObservationContext.builder()
126+
.imagePrompt(new ImagePrompt("test prompt"))
127+
.provider("test-provider")
128+
.build();
129+
130+
assertThat(this.observationConvention.getContextualName(observationContext)).isEqualTo("image");
131+
assertThat(this.observationConvention.getLowCardinalityKeyValues(observationContext))
132+
.contains(KeyValue.of(AiObservationAttributes.REQUEST_MODEL.value(), KeyValue.NONE_VALUE));
133+
}
134+
135+
@Test
136+
void shouldHandleEmptyModel() {
137+
ImageModelObservationContext observationContext = ImageModelObservationContext.builder()
138+
.imagePrompt(generateImagePrompt(ImageOptionsBuilder.builder().model("").build()))
139+
.provider("test-provider")
140+
.build();
141+
142+
assertThat(this.observationConvention.getContextualName(observationContext)).isEqualTo("image");
143+
}
144+
145+
@Test
146+
void shouldHandleBlankModel() {
147+
ImageModelObservationContext observationContext = ImageModelObservationContext.builder()
148+
.imagePrompt(generateImagePrompt(ImageOptionsBuilder.builder().model(" ").build()))
149+
.provider("test-provider")
150+
.build();
151+
152+
assertThat(this.observationConvention.getContextualName(observationContext)).isEqualTo("image");
153+
}
154+
155+
@Test
156+
void shouldHandleEmptyStyle() {
157+
var imageOptions = ImageOptionsBuilder.builder().model("test-model").style("").build();
158+
159+
ImageModelObservationContext observationContext = ImageModelObservationContext.builder()
160+
.imagePrompt(generateImagePrompt(imageOptions))
161+
.provider("test-provider")
162+
.build();
163+
164+
// Empty style should not be included
165+
assertThat(this.observationConvention.getHighCardinalityKeyValues(observationContext)
166+
.stream()
167+
.map(KeyValue::getKey)
168+
.toList()).doesNotContain(HighCardinalityKeyNames.REQUEST_IMAGE_STYLE.asString());
169+
}
170+
171+
@Test
172+
void shouldHandleEmptyResponseFormat() {
173+
var imageOptions = ImageOptionsBuilder.builder().model("test-model").responseFormat("").build();
174+
175+
ImageModelObservationContext observationContext = ImageModelObservationContext.builder()
176+
.imagePrompt(generateImagePrompt(imageOptions))
177+
.provider("test-provider")
178+
.build();
179+
180+
// Empty response format should not be included
181+
assertThat(this.observationConvention.getHighCardinalityKeyValues(observationContext)
182+
.stream()
183+
.map(KeyValue::getKey)
184+
.toList()).doesNotContain(HighCardinalityKeyNames.REQUEST_IMAGE_RESPONSE_FORMAT.asString());
185+
}
186+
187+
@Test
188+
void shouldHandleImagePromptWithoutOptions() {
189+
ImageModelObservationContext observationContext = ImageModelObservationContext.builder()
190+
.imagePrompt(new ImagePrompt("simple prompt"))
191+
.provider("simple-provider")
192+
.build();
193+
194+
assertThat(this.observationConvention.getContextualName(observationContext)).isEqualTo("image");
195+
assertThat(this.observationConvention.getLowCardinalityKeyValues(observationContext))
196+
.contains(KeyValue.of(AiObservationAttributes.REQUEST_MODEL.value(), KeyValue.NONE_VALUE));
197+
assertThat(this.observationConvention.getHighCardinalityKeyValues(observationContext)).isEmpty();
198+
}
199+
123200
private ImagePrompt generateImagePrompt(ImageOptions imageOptions) {
124201
return new ImagePrompt("here comes the sun", imageOptions);
125202
}

spring-ai-model/src/test/java/org/springframework/ai/image/observation/ImageModelObservationContextTests.java

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
import org.springframework.ai.image.ImagePrompt;
2424

2525
import static org.assertj.core.api.Assertions.assertThat;
26+
import static org.assertj.core.api.Assertions.assertThatThrownBy;
2627

2728
/**
2829
* Unit tests for {@link ImageModelObservationContext}.
@@ -41,6 +42,75 @@ void whenMandatoryRequestOptionsThenReturn() {
4142
assertThat(observationContext).isNotNull();
4243
}
4344

45+
@Test
46+
void shouldBuildContextWithImageOptions() {
47+
var imageOptions = ImageOptionsBuilder.builder().model("test-model").build();
48+
var imagePrompt = new ImagePrompt("test prompt", imageOptions);
49+
50+
var observationContext = ImageModelObservationContext.builder()
51+
.imagePrompt(imagePrompt)
52+
.provider("test-provider")
53+
.build();
54+
55+
assertThat(observationContext).isNotNull();
56+
}
57+
58+
@Test
59+
void shouldThrowExceptionWhenImagePromptIsNull() {
60+
assertThatThrownBy(
61+
() -> ImageModelObservationContext.builder().imagePrompt(null).provider("test-provider").build())
62+
.isInstanceOf(IllegalArgumentException.class)
63+
.hasMessage("request cannot be null");
64+
}
65+
66+
@Test
67+
void shouldThrowExceptionWhenProviderIsNull() {
68+
var imagePrompt = new ImagePrompt("test prompt");
69+
70+
assertThatThrownBy(() -> ImageModelObservationContext.builder().imagePrompt(imagePrompt).provider(null).build())
71+
.isInstanceOf(IllegalArgumentException.class)
72+
.hasMessage("provider cannot be null or empty");
73+
}
74+
75+
@Test
76+
void shouldThrowExceptionWhenProviderIsEmpty() {
77+
var imagePrompt = new ImagePrompt("test prompt");
78+
79+
assertThatThrownBy(() -> ImageModelObservationContext.builder().imagePrompt(imagePrompt).provider("").build())
80+
.isInstanceOf(IllegalArgumentException.class)
81+
.hasMessage("provider cannot be null or empty");
82+
}
83+
84+
@Test
85+
void shouldThrowExceptionWhenProviderIsBlank() {
86+
var imagePrompt = new ImagePrompt("test prompt");
87+
88+
assertThatThrownBy(
89+
() -> ImageModelObservationContext.builder().imagePrompt(imagePrompt).provider(" ").build())
90+
.isInstanceOf(IllegalArgumentException.class)
91+
.hasMessage("provider cannot be null or empty");
92+
}
93+
94+
@Test
95+
void shouldBuildMultipleContextsIndependently() {
96+
var imagePrompt1 = new ImagePrompt("first prompt");
97+
var imagePrompt2 = new ImagePrompt("second prompt");
98+
99+
var context1 = ImageModelObservationContext.builder()
100+
.imagePrompt(imagePrompt1)
101+
.provider("provider-alpha")
102+
.build();
103+
104+
var context2 = ImageModelObservationContext.builder()
105+
.imagePrompt(imagePrompt2)
106+
.provider("provider-beta")
107+
.build();
108+
109+
assertThat(context1).isNotNull();
110+
assertThat(context2).isNotNull();
111+
assertThat(context1).isNotEqualTo(context2);
112+
}
113+
44114
private ImagePrompt generateImagePrompt(ImageOptions imageOptions) {
45115
return new ImagePrompt("here comes the sun");
46116
}

spring-ai-model/src/test/java/org/springframework/ai/tool/function/FunctionToolCallbackTest.java

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,18 @@ void testThrowToolExecutionException() {
138138
.isInstanceOf(ToolExecutionException.class);
139139
}
140140

141+
@Test
142+
void testEmptyStringInput() {
143+
TestFunctionTool tool = new TestFunctionTool();
144+
FunctionToolCallback<String, Void> callback = FunctionToolCallback.builder("testTool", tool.stringConsumer())
145+
.description("test empty string")
146+
.inputType(String.class)
147+
.build();
148+
149+
callback.call("\"\"");
150+
assertEquals("", tool.calledValue.get());
151+
}
152+
141153
static class TestFunctionTool {
142154

143155
AtomicReference<Object> calledValue = new AtomicReference<>();

0 commit comments

Comments
 (0)