Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -318,4 +318,157 @@ void testCopyPreservesAllFields() {
assertThat(copied.getToolContext()).isNotSameAs(original.getToolContext());
}

@Test
void testBoundaryValues() {
AnthropicChatOptions options = AnthropicChatOptions.builder()
.maxTokens(Integer.MAX_VALUE)
.temperature(1.0)
.topP(1.0)
.topK(Integer.MAX_VALUE)
.build();

assertThat(options.getMaxTokens()).isEqualTo(Integer.MAX_VALUE);
assertThat(options.getTemperature()).isEqualTo(1.0);
assertThat(options.getTopP()).isEqualTo(1.0);
assertThat(options.getTopK()).isEqualTo(Integer.MAX_VALUE);
}

@Test
void testToolContextWithVariousValueTypes() {
Map<String, Object> mixedMap = Map.of("string", "value", "number", 42, "boolean", true, "null_value", "null",
"nested_list", List.of("a", "b", "c"), "nested_map", Map.of("inner", "value"));

AnthropicChatOptions options = AnthropicChatOptions.builder().toolContext(mixedMap).build();

assertThat(options.getToolContext()).containsAllEntriesOf(mixedMap);
assertThat(options.getToolContext().get("string")).isEqualTo("value");
assertThat(options.getToolContext().get("number")).isEqualTo(42);
assertThat(options.getToolContext().get("boolean")).isEqualTo(true);
}

@Test
void testCopyWithMutableCollections() {
List<String> mutableStops = new java.util.ArrayList<>(List.of("stop1", "stop2"));
Map<String, Object> mutableContext = new java.util.HashMap<>(Map.of("key", "value"));

AnthropicChatOptions original = AnthropicChatOptions.builder()
.stopSequences(mutableStops)
.toolContext(mutableContext)
.build();

AnthropicChatOptions copied = original.copy();

// Modify original collections
mutableStops.add("stop3");
mutableContext.put("new_key", "new_value");

// Copied instance should not be affected
assertThat(copied.getStopSequences()).hasSize(2);
assertThat(copied.getToolContext()).hasSize(1);
assertThat(copied.getStopSequences()).doesNotContain("stop3");
assertThat(copied.getToolContext()).doesNotContainKey("new_key");
}

@Test
void testEqualsWithNullFields() {
AnthropicChatOptions options1 = new AnthropicChatOptions();
AnthropicChatOptions options2 = new AnthropicChatOptions();

assertThat(options1).isEqualTo(options2);
assertThat(options1.hashCode()).isEqualTo(options2.hashCode());
}

@Test
void testEqualsWithMixedNullAndNonNullFields() {
AnthropicChatOptions options1 = AnthropicChatOptions.builder()
.model("test")
.maxTokens(null)
.temperature(0.5)
.build();

AnthropicChatOptions options2 = AnthropicChatOptions.builder()
.model("test")
.maxTokens(null)
.temperature(0.5)
.build();

AnthropicChatOptions options3 = AnthropicChatOptions.builder()
.model("test")
.maxTokens(100)
.temperature(0.5)
.build();

assertThat(options1).isEqualTo(options2);
assertThat(options1).isNotEqualTo(options3);
}

@Test
void testCopyDoesNotShareMetadataReference() {
Metadata originalMetadata = new Metadata("user_123");
AnthropicChatOptions original = AnthropicChatOptions.builder().metadata(originalMetadata).build();

AnthropicChatOptions copied = original.copy();

// Metadata should be the same value but potentially different reference
assertThat(copied.getMetadata()).isEqualTo(original.getMetadata());

// Verify changing original doesn't affect copy
original.setMetadata(new Metadata("different_user"));
assertThat(copied.getMetadata()).isEqualTo(originalMetadata);
}

@Test
void testEqualsWithSelf() {
AnthropicChatOptions options = AnthropicChatOptions.builder().model("test").build();

assertThat(options).isEqualTo(options);
assertThat(options.hashCode()).isEqualTo(options.hashCode());
}

@Test
void testEqualsWithNull() {
AnthropicChatOptions options = AnthropicChatOptions.builder().model("test").build();

assertThat(options).isNotEqualTo(null);
}

@Test
void testEqualsWithDifferentClass() {
AnthropicChatOptions options = AnthropicChatOptions.builder().model("test").build();

assertThat(options).isNotEqualTo("not an AnthropicChatOptions");
assertThat(options).isNotEqualTo(1);
}

@Test
void testBuilderPartialConfiguration() {
// Test builder with only some fields set
AnthropicChatOptions onlyModel = AnthropicChatOptions.builder().model("model-only").build();

AnthropicChatOptions onlyTokens = AnthropicChatOptions.builder().maxTokens(10).build();

AnthropicChatOptions onlyTemperature = AnthropicChatOptions.builder().temperature(0.8).build();

assertThat(onlyModel.getModel()).isEqualTo("model-only");
assertThat(onlyModel.getMaxTokens()).isNull();

assertThat(onlyTokens.getModel()).isNull();
assertThat(onlyTokens.getMaxTokens()).isEqualTo(10);

assertThat(onlyTemperature.getModel()).isNull();
assertThat(onlyTemperature.getTemperature()).isEqualTo(0.8);
}

@Test
void testSetterOverwriteBehavior() {
AnthropicChatOptions options = AnthropicChatOptions.builder().model("initial-model").maxTokens(100).build();

// Overwrite with setters
options.setModel("updated-model");
options.setMaxTokens(10);

assertThat(options.getModel()).isEqualTo("updated-model");
assertThat(options.getMaxTokens()).isEqualTo(10);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -145,4 +145,86 @@ void verifyEnumTypesAreRegistered() {
assertThat(registeredTypes.contains(TypeReference.of(AnthropicApi.EventType.class))).isTrue();
}

@Test
void verifyNestedClassesAreRegistered() {
anthropicRuntimeHints.registerHints(runtimeHints, null);

Set<TypeReference> registeredTypes = new HashSet<>();
runtimeHints.reflection().typeHints().forEach(typeHint -> registeredTypes.add(typeHint.getType()));

// Verify nested classes within AnthropicApi are registered
assertThat(registeredTypes.contains(TypeReference.of(AnthropicApi.ChatCompletionRequest.class))).isTrue();
assertThat(registeredTypes.contains(TypeReference.of(AnthropicApi.AnthropicMessage.class))).isTrue();
assertThat(registeredTypes.contains(TypeReference.of(AnthropicApi.ContentBlock.class))).isTrue();
}

@Test
void verifyNoProxyHintsAreRegistered() {
anthropicRuntimeHints.registerHints(runtimeHints, null);

// This implementation should only register reflection hints, not proxy hints
long proxyHintCount = runtimeHints.proxies().jdkProxyHints().count();
assertThat(proxyHintCount).isEqualTo(0);
}

@Test
void verifyNoSerializationHintsAreRegistered() {
anthropicRuntimeHints.registerHints(runtimeHints, null);

// This implementation should only register reflection hints, not serialization
// hints
long serializationHintCount = runtimeHints.serialization().javaSerializationHints().count();
assertThat(serializationHintCount).isEqualTo(0);
}

@Test
void verifyJsonAnnotatedClassesContainExpectedTypes() {
Set<TypeReference> jsonAnnotatedClasses = findJsonAnnotatedClassesInPackage("org.springframework.ai.anthropic");

// Verify that key API classes are found
boolean containsApiClass = jsonAnnotatedClasses.stream()
.anyMatch(typeRef -> typeRef.getName().contains("AnthropicApi")
|| typeRef.getName().contains("ChatCompletion") || typeRef.getName().contains("AnthropicMessage"));

assertThat(containsApiClass).isTrue();
}

@Test
void verifyConsistencyAcrossInstances() {
RuntimeHints hints1 = new RuntimeHints();
RuntimeHints hints2 = new RuntimeHints();

AnthropicRuntimeHints anthropicHints1 = new AnthropicRuntimeHints();
AnthropicRuntimeHints anthropicHints2 = new AnthropicRuntimeHints();

anthropicHints1.registerHints(hints1, null);
anthropicHints2.registerHints(hints2, null);

// Different instances should register the same hints
Set<TypeReference> types1 = new HashSet<>();
Set<TypeReference> types2 = new HashSet<>();

hints1.reflection().typeHints().forEach(hint -> types1.add(hint.getType()));
hints2.reflection().typeHints().forEach(hint -> types2.add(hint.getType()));

assertThat(types1).isEqualTo(types2);
}

@Test
void verifyPackageSpecificity() {
Set<TypeReference> jsonAnnotatedClasses = findJsonAnnotatedClassesInPackage("org.springframework.ai.anthropic");

// All found classes should be from the anthropic package specifically
for (TypeReference classRef : jsonAnnotatedClasses) {
assertThat(classRef.getName()).startsWith("org.springframework.ai.anthropic");
}

// Should not include classes from other AI packages
for (TypeReference classRef : jsonAnnotatedClasses) {
assertThat(classRef.getName()).doesNotContain("vertexai");
assertThat(classRef.getName()).doesNotContain("openai");
assertThat(classRef.getName()).doesNotContain("ollama");
}
}

}