From c430a00ab6b62e74545658df243f0bc8c391495b Mon Sep 17 00:00:00 2001 From: Eric Bottard Date: Fri, 14 Nov 2025 17:23:47 +0100 Subject: [PATCH] Configure ErrorProne + Nullaway Fix various ErrorProne issues across the codebase. Signed-off-by: Eric Bottard --- ...nctionCallbackWithPlainFunctionBeanIT.java | 2 +- .../CassandraChatMemoryRepositoryIT.java | 10 ++-- .../neo4j/Neo4jChatMemoryRepositoryIT.java | 1 + .../anthropic/AnthropicChatOptionsTests.java | 1 + .../AnthropicPromptCachingMockTest.java | 31 +++++++----- .../azure/openai/AzureOpenAiChatModelIT.java | 2 +- .../AzureOpenAiChatModelFunctionCallIT.java | 3 +- .../DeepSeekChatModelFunctionCallingIT.java | 3 +- .../ai/deepseek/chat/DeepSeekChatModelIT.java | 2 +- .../google/genai/GoogleGenAiChatOptions.java | 2 +- .../mistralai/MistralAiModerationModelIT.java | 12 ++--- .../ollama/OllamaChatModelMetadataTests.java | 2 +- .../ai/openai/OpenAiChatOptionsTests.java | 1 + .../ai/openai/OpenAiImageOptionsTests.java | 1 + .../chat/OpenAiChatModelResponseFormatIT.java | 2 +- .../moderation/OpenAiModerationModelIT.java | 48 ++++++------------- .../ResourceCacheServiceTests.java | 23 +++++---- .../gemini/VertexAiGeminiChatModel.java | 7 +-- .../gemini/VertexAiGeminiChatOptions.java | 2 +- .../gemini/CreateGeminiRequestTests.java | 1 - .../ai/zhipuai/ZhiPuAiChatOptionsTests.java | 1 + pom.xml | 31 +++++++++++- .../ai/chat/metadata/DefaultUsageTests.java | 1 + .../mariadb/MariaDBStoreTests.java | 2 +- 24 files changed, 102 insertions(+), 89 deletions(-) diff --git a/auto-configurations/models/spring-ai-autoconfigure-model-openai/src/test/java/org/springframework/ai/model/openai/autoconfigure/tool/FunctionCallbackWithPlainFunctionBeanIT.java b/auto-configurations/models/spring-ai-autoconfigure-model-openai/src/test/java/org/springframework/ai/model/openai/autoconfigure/tool/FunctionCallbackWithPlainFunctionBeanIT.java index 9402c13d122..e3ea965f9cf 100644 --- a/auto-configurations/models/spring-ai-autoconfigure-model-openai/src/test/java/org/springframework/ai/model/openai/autoconfigure/tool/FunctionCallbackWithPlainFunctionBeanIT.java +++ b/auto-configurations/models/spring-ai-autoconfigure-model-openai/src/test/java/org/springframework/ai/model/openai/autoconfigure/tool/FunctionCallbackWithPlainFunctionBeanIT.java @@ -317,7 +317,7 @@ void streamFunctionCallTest() { .collect(Collectors.joining()); logger.info("Response: {}", content); - assertThat(content).isNotEmpty().withFailMessage("Content returned from OpenAI model is empty"); + assertThat(content).withFailMessage("Content returned from OpenAI model is empty").isNotEmpty(); assertThat(content).contains("30", "10", "15"); }); diff --git a/memory/repository/spring-ai-model-chat-memory-repository-cassandra/src/test/java/org/springframework/ai/chat/memory/repository/cassandra/CassandraChatMemoryRepositoryIT.java b/memory/repository/spring-ai-model-chat-memory-repository-cassandra/src/test/java/org/springframework/ai/chat/memory/repository/cassandra/CassandraChatMemoryRepositoryIT.java index 217d23b18c0..a73321cd43c 100644 --- a/memory/repository/spring-ai-model-chat-memory-repository-cassandra/src/test/java/org/springframework/ai/chat/memory/repository/cassandra/CassandraChatMemoryRepositoryIT.java +++ b/memory/repository/spring-ai-model-chat-memory-repository-cassandra/src/test/java/org/springframework/ai/chat/memory/repository/cassandra/CassandraChatMemoryRepositoryIT.java @@ -73,7 +73,7 @@ void ensureBeansGetsCreated() { void add_shouldInsertSingleMessage(String content, MessageType messageType) { this.contextRunner.run(context -> { var chatMemory = context.getBean(ChatMemoryRepository.class); - assertThat(chatMemory instanceof CassandraChatMemoryRepository); + assertThat(chatMemory).isInstanceOf(CassandraChatMemoryRepository.class); var sessionId = UUID.randomUUID().toString(); var message = switch (messageType) { case ASSISTANT -> new AssistantMessage(content); @@ -109,7 +109,7 @@ void add_shouldInsertSingleMessage(String content, MessageType messageType) { void add_shouldInsertMessages() { this.contextRunner.run(context -> { var chatMemory = context.getBean(ChatMemoryRepository.class); - assertThat(chatMemory instanceof CassandraChatMemoryRepository); + assertThat(chatMemory).isInstanceOf(CassandraChatMemoryRepository.class); var sessionId = UUID.randomUUID().toString(); var messages = List.of(new AssistantMessage("Message from assistant"), new UserMessage("Message from user")); @@ -147,7 +147,7 @@ void add_shouldInsertMessages() { void get_shouldReturnMessages() { this.contextRunner.run(context -> { var chatMemory = context.getBean(ChatMemoryRepository.class); - assertThat(chatMemory instanceof CassandraChatMemoryRepository); + assertThat(chatMemory).isInstanceOf(CassandraChatMemoryRepository.class); var sessionId = UUID.randomUUID().toString(); var messages = List.of(new AssistantMessage("Message from assistant 1 - " + sessionId), @@ -174,7 +174,7 @@ void get_shouldReturnMessages() { void get_afterMultipleAdds_shouldReturnMessagesInSameOrder() { this.contextRunner.run(context -> { var chatMemory = context.getBean(ChatMemoryRepository.class); - assertThat(chatMemory instanceof CassandraChatMemoryRepository); + assertThat(chatMemory).isInstanceOf(CassandraChatMemoryRepository.class); var sessionId = UUID.randomUUID().toString(); var userMessage = new UserMessage("Message from user - " + sessionId); var assistantMessage = new AssistantMessage("Message from assistant - " + sessionId); @@ -200,7 +200,7 @@ void get_afterMultipleAdds_shouldReturnMessagesInSameOrder() { void clear_shouldDeleteMessages() { this.contextRunner.run(context -> { var chatMemory = context.getBean(ChatMemoryRepository.class); - assertThat(chatMemory instanceof CassandraChatMemoryRepository); + assertThat(chatMemory).isInstanceOf(CassandraChatMemoryRepository.class); var sessionId = UUID.randomUUID().toString(); var messages = List.of(new AssistantMessage("Message from assistant - " + sessionId), diff --git a/memory/repository/spring-ai-model-chat-memory-repository-neo4j/src/test/java/org/springframework/ai/chat/memory/repository/neo4j/Neo4jChatMemoryRepositoryIT.java b/memory/repository/spring-ai-model-chat-memory-repository-neo4j/src/test/java/org/springframework/ai/chat/memory/repository/neo4j/Neo4jChatMemoryRepositoryIT.java index bee7a5d5888..95282f11ffc 100644 --- a/memory/repository/spring-ai-model-chat-memory-repository-neo4j/src/test/java/org/springframework/ai/chat/memory/repository/neo4j/Neo4jChatMemoryRepositoryIT.java +++ b/memory/repository/spring-ai-model-chat-memory-repository-neo4j/src/test/java/org/springframework/ai/chat/memory/repository/neo4j/Neo4jChatMemoryRepositoryIT.java @@ -306,6 +306,7 @@ void handleToolResponseMessage() { } @Test + @SuppressWarnings("DoubleBraceInitialization") void saveAndFindSystemMessageWithMetadata() { var conversationId = UUID.randomUUID().toString(); Map customMetadata = Map.of("priority", "high", "source", "test"); diff --git a/models/spring-ai-anthropic/src/test/java/org/springframework/ai/anthropic/AnthropicChatOptionsTests.java b/models/spring-ai-anthropic/src/test/java/org/springframework/ai/anthropic/AnthropicChatOptionsTests.java index 9014c5ae359..2a7bde8f647 100644 --- a/models/spring-ai-anthropic/src/test/java/org/springframework/ai/anthropic/AnthropicChatOptionsTests.java +++ b/models/spring-ai-anthropic/src/test/java/org/springframework/ai/anthropic/AnthropicChatOptionsTests.java @@ -424,6 +424,7 @@ void testCopyDoesNotShareMetadataReference() { } @Test + @SuppressWarnings("SelfAssertion") void testEqualsWithSelf() { AnthropicChatOptions options = AnthropicChatOptions.builder().model("test").build(); diff --git a/models/spring-ai-anthropic/src/test/java/org/springframework/ai/anthropic/AnthropicPromptCachingMockTest.java b/models/spring-ai-anthropic/src/test/java/org/springframework/ai/anthropic/AnthropicPromptCachingMockTest.java index 1e5ec58fa9d..ff4791e57d9 100644 --- a/models/spring-ai-anthropic/src/test/java/org/springframework/ai/anthropic/AnthropicPromptCachingMockTest.java +++ b/models/spring-ai-anthropic/src/test/java/org/springframework/ai/anthropic/AnthropicPromptCachingMockTest.java @@ -540,8 +540,9 @@ void testFourBreakpointLimitEnforcement() throws Exception { int cacheControlCount = countCacheControlOccurrences(requestBody); // Verify we don't exceed Anthropic's 4-breakpoint limit - assertThat(cacheControlCount).isLessThanOrEqualTo(4) - .withFailMessage("Cache breakpoints should not exceed 4, but found %d", cacheControlCount); + assertThat(cacheControlCount) + .withFailMessage("Cache breakpoints should not exceed 4, but found %d", cacheControlCount) + .isLessThanOrEqualTo(4); } @Test @@ -608,8 +609,9 @@ else if (systemNode.isTextual()) { // Simple text system message should still have cache_control applied at the // message level // Check if there's a cache_control field at the system level or in a wrapper - assertThat(requestBody.toString()).contains("cache_control") - .withFailMessage("SYSTEM_ONLY strategy should include cache_control in wire format"); + assertThat(requestBody.toString()) + .withFailMessage("SYSTEM_ONLY strategy should include cache_control in wire format") + .contains("cache_control"); } } @@ -690,8 +692,9 @@ void testComplexMultiBreakpointScenario() throws Exception { // Verify proper ordering and cache control placement int cacheControlCount = countCacheControlOccurrences(requestBody); - assertThat(cacheControlCount).isLessThanOrEqualTo(4) - .withFailMessage("Complex scenario should not exceed 4 cache breakpoints, found %d", cacheControlCount); + assertThat(cacheControlCount) + .withFailMessage("Complex scenario should not exceed 4 cache breakpoints, found %d", cacheControlCount) + .isLessThanOrEqualTo(4); // Verify cache_control is only on the LAST blocks of each section (system, tools) // This ensures proper breakpoint placement according to Anthropic's requirements @@ -732,9 +735,9 @@ private void verifyCacheControlPlacement(JsonNode requestBody) { if (systemNode.isArray()) { for (int i = 0; i < systemNode.size() - 1; i++) { JsonNode systemBlock = systemNode.get(i); - assertThat(systemBlock.has("cache_control")).isFalse() - .withFailMessage("Only the last system block should have cache_control, but block %d has it", - i); + assertThat(systemBlock.has("cache_control")) + .withFailMessage("Only the last system block should have cache_control, but block %d has it", i) + .isFalse(); } } } @@ -745,8 +748,9 @@ private void verifyCacheControlPlacement(JsonNode requestBody) { if (toolsArray.isArray()) { for (int i = 0; i < toolsArray.size() - 1; i++) { JsonNode tool = toolsArray.get(i); - assertThat(tool.has("cache_control")).isFalse() - .withFailMessage("Only the last tool should have cache_control, but tool %d has it", i); + assertThat(tool.has("cache_control")) + .withFailMessage("Only the last tool should have cache_control, but tool %d has it", i) + .isFalse(); } } } @@ -767,9 +771,10 @@ private void verifyCacheControlPlacement(JsonNode requestBody) { if (i != messagesArray.size() - 2 || j != contentArray.size() - 1) { // Only the last content block of the second-to-last // message should have cache_control - assertThat(contentBlock.has("cache_control")).isFalse() + assertThat(contentBlock.has("cache_control")) .withFailMessage( - "Unexpected cache_control placement in message %d, content block %d", i, j); + "Unexpected cache_control placement in message %d, content block %d", i, j) + .isFalse(); } } } diff --git a/models/spring-ai-azure-openai/src/test/java/org/springframework/ai/azure/openai/AzureOpenAiChatModelIT.java b/models/spring-ai-azure-openai/src/test/java/org/springframework/ai/azure/openai/AzureOpenAiChatModelIT.java index f637bae027e..4a40e001abb 100644 --- a/models/spring-ai-azure-openai/src/test/java/org/springframework/ai/azure/openai/AzureOpenAiChatModelIT.java +++ b/models/spring-ai-azure-openai/src/test/java/org/springframework/ai/azure/openai/AzureOpenAiChatModelIT.java @@ -123,7 +123,7 @@ void testStreaming() { .collect(Collectors.joining()); logger.info("Response: {}", content); - assertThat(counter.get()).isGreaterThan(8).as("More than 8 chuncks because there are 8 planets"); + assertThat(counter.get()).withFailMessage("More than 8 chunks because there are 8 planets").isGreaterThan(8); assertThat(content).contains("Earth", "Mars", "Jupiter"); } diff --git a/models/spring-ai-azure-openai/src/test/java/org/springframework/ai/azure/openai/function/AzureOpenAiChatModelFunctionCallIT.java b/models/spring-ai-azure-openai/src/test/java/org/springframework/ai/azure/openai/function/AzureOpenAiChatModelFunctionCallIT.java index 76f36b9dc3f..1e60668cccc 100644 --- a/models/spring-ai-azure-openai/src/test/java/org/springframework/ai/azure/openai/function/AzureOpenAiChatModelFunctionCallIT.java +++ b/models/spring-ai-azure-openai/src/test/java/org/springframework/ai/azure/openai/function/AzureOpenAiChatModelFunctionCallIT.java @@ -139,7 +139,8 @@ void streamFunctionCallTest() { .collect(Collectors.joining()); logger.info("Response: {}", content); - assertThat(counter.get()).isGreaterThan(30).as("The response should be chunked in more than 30 messages"); + assertThat(counter.get()).withFailMessage("The response should be chunked in more than 30 messages") + .isGreaterThan(30); assertThat(content).contains("30", "10", "15"); diff --git a/models/spring-ai-deepseek/src/test/java/org/springframework/ai/deepseek/chat/DeepSeekChatModelFunctionCallingIT.java b/models/spring-ai-deepseek/src/test/java/org/springframework/ai/deepseek/chat/DeepSeekChatModelFunctionCallingIT.java index f562719835b..36103a66f32 100644 --- a/models/spring-ai-deepseek/src/test/java/org/springframework/ai/deepseek/chat/DeepSeekChatModelFunctionCallingIT.java +++ b/models/spring-ai-deepseek/src/test/java/org/springframework/ai/deepseek/chat/DeepSeekChatModelFunctionCallingIT.java @@ -155,10 +155,9 @@ public void toolFunctionCallWithUsage() { ChatResponse chatResponse = this.chatModel.call(prompt); assertThat(chatResponse).isNotNull(); - assertThat(chatResponse.getResult().getOutput()); + assertThat(chatResponse.getResult().getOutput()).isNotNull(); assertThat(chatResponse.getResult().getOutput().getText()).contains("San Francisco"); assertThat(chatResponse.getResult().getOutput().getText()).contains("30"); - // 这个 total token 是第一次 chat 以及 tool call 之后的两次请求 token 总和 // the total token is first chat and tool call request assertThat(chatResponse.getMetadata().getUsage().getTotalTokens()).isLessThan(700).isGreaterThan(280); diff --git a/models/spring-ai-deepseek/src/test/java/org/springframework/ai/deepseek/chat/DeepSeekChatModelIT.java b/models/spring-ai-deepseek/src/test/java/org/springframework/ai/deepseek/chat/DeepSeekChatModelIT.java index edb92a4f98a..c9d682cf0b6 100644 --- a/models/spring-ai-deepseek/src/test/java/org/springframework/ai/deepseek/chat/DeepSeekChatModelIT.java +++ b/models/spring-ai-deepseek/src/test/java/org/springframework/ai/deepseek/chat/DeepSeekChatModelIT.java @@ -229,7 +229,7 @@ void prefixCompletionTest() { .build(); Prompt prompt = new Prompt(List.of(userMessage, assistantMessage)); ChatResponse response = this.chatModel.call(prompt); - assertThat(response.getResult().getOutput().getText().equals(",2,3]}}")); + assertThat(response.getResult().getOutput().getText()).isEqualTo(",2,3]}}"); } /** diff --git a/models/spring-ai-google-genai/src/main/java/org/springframework/ai/google/genai/GoogleGenAiChatOptions.java b/models/spring-ai-google-genai/src/main/java/org/springframework/ai/google/genai/GoogleGenAiChatOptions.java index d242c9ce2e0..4799e3101a2 100644 --- a/models/spring-ai-google-genai/src/main/java/org/springframework/ai/google/genai/GoogleGenAiChatOptions.java +++ b/models/spring-ai-google-genai/src/main/java/org/springframework/ai/google/genai/GoogleGenAiChatOptions.java @@ -441,7 +441,7 @@ public boolean equals(Object o) { if (!(o instanceof GoogleGenAiChatOptions that)) { return false; } - return this.googleSearchRetrieval == that.googleSearchRetrieval + return Objects.equals(this.googleSearchRetrieval, that.googleSearchRetrieval) && Objects.equals(this.stopSequences, that.stopSequences) && Objects.equals(this.temperature, that.temperature) && Objects.equals(this.topP, that.topP) && Objects.equals(this.topK, that.topK) && Objects.equals(this.candidateCount, that.candidateCount) diff --git a/models/spring-ai-mistral-ai/src/test/java/org/springframework/ai/mistralai/MistralAiModerationModelIT.java b/models/spring-ai-mistral-ai/src/test/java/org/springframework/ai/mistralai/MistralAiModerationModelIT.java index 3df1766ec08..1e9910a8e91 100644 --- a/models/spring-ai-mistral-ai/src/test/java/org/springframework/ai/mistralai/MistralAiModerationModelIT.java +++ b/models/spring-ai-mistral-ai/src/test/java/org/springframework/ai/mistralai/MistralAiModerationModelIT.java @@ -16,6 +16,7 @@ package org.springframework.ai.mistralai; +import org.assertj.core.data.Offset; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.condition.EnabledIfEnvironmentVariable; @@ -64,15 +65,8 @@ void moderationAsPositiveTest() { assertThat(result.isFlagged()).isTrue(); CategoryScores scores = result.getCategoryScores(); - assertThat(scores.getSexual()).isNotNull(); - assertThat(scores.getHate()).isNotNull(); - assertThat(scores.getViolence()).isNotNull(); - assertThat(scores.getDangerousAndCriminalContent()).isNotNull(); - assertThat(scores.getSelfHarm()).isNotNull(); - assertThat(scores.getHealth()).isNotNull(); - assertThat(scores.getFinancial()).isNotNull(); - assertThat(scores.getLaw()).isNotNull(); - assertThat(scores.getPii()).isNotNull(); + assertThat(scores.getSexual()).isCloseTo(0.0d, Offset.offset(0.1d)); + assertThat(scores.getViolence()).isCloseTo(1.0d, Offset.offset(0.2d)); } } diff --git a/models/spring-ai-ollama/src/test/java/org/springframework/ai/ollama/OllamaChatModelMetadataTests.java b/models/spring-ai-ollama/src/test/java/org/springframework/ai/ollama/OllamaChatModelMetadataTests.java index 0a168ae341c..b8ccdbbbbbc 100644 --- a/models/spring-ai-ollama/src/test/java/org/springframework/ai/ollama/OllamaChatModelMetadataTests.java +++ b/models/spring-ai-ollama/src/test/java/org/springframework/ai/ollama/OllamaChatModelMetadataTests.java @@ -66,7 +66,7 @@ void ollamaThinkingMetadataCaptured() { chatResponse.getResults().forEach(generation -> { ChatGenerationMetadata chatGenerationMetadata = generation.getMetadata(); assertThat(chatGenerationMetadata).isNotNull(); - assertThat(chatGenerationMetadata.containsKey("thinking")); + assertThat(chatGenerationMetadata.containsKey("thinking")).isTrue(); }); } diff --git a/models/spring-ai-openai/src/test/java/org/springframework/ai/openai/OpenAiChatOptionsTests.java b/models/spring-ai-openai/src/test/java/org/springframework/ai/openai/OpenAiChatOptionsTests.java index 118d7d19309..62212216618 100644 --- a/models/spring-ai-openai/src/test/java/org/springframework/ai/openai/OpenAiChatOptionsTests.java +++ b/models/spring-ai-openai/src/test/java/org/springframework/ai/openai/OpenAiChatOptionsTests.java @@ -293,6 +293,7 @@ void testFromOptions_webSearchOptions() { } @Test + @SuppressWarnings("SelfAssertion") void testEqualsAndHashCode() { OpenAiChatOptions options1 = OpenAiChatOptions.builder() .model("test-model") diff --git a/models/spring-ai-openai/src/test/java/org/springframework/ai/openai/OpenAiImageOptionsTests.java b/models/spring-ai-openai/src/test/java/org/springframework/ai/openai/OpenAiImageOptionsTests.java index faa266ebbeb..53277f95f19 100644 --- a/models/spring-ai-openai/src/test/java/org/springframework/ai/openai/OpenAiImageOptionsTests.java +++ b/models/spring-ai-openai/src/test/java/org/springframework/ai/openai/OpenAiImageOptionsTests.java @@ -152,6 +152,7 @@ void testBuilderWithExistingOptions() { } @Test + @SuppressWarnings("SelfAssertion") void testEqualsAndHashCode() { OpenAiImageOptions options1 = OpenAiImageOptions.builder() .N(2) diff --git a/models/spring-ai-openai/src/test/java/org/springframework/ai/openai/chat/OpenAiChatModelResponseFormatIT.java b/models/spring-ai-openai/src/test/java/org/springframework/ai/openai/chat/OpenAiChatModelResponseFormatIT.java index 02b8082a71b..59653a28792 100644 --- a/models/spring-ai-openai/src/test/java/org/springframework/ai/openai/chat/OpenAiChatModelResponseFormatIT.java +++ b/models/spring-ai-openai/src/test/java/org/springframework/ai/openai/chat/OpenAiChatModelResponseFormatIT.java @@ -264,7 +264,7 @@ record Items(@JsonProperty(required = true, value = "explanation") String explan // Check if the order is correct as specified in the schema. Steps should come // first before final answer. - assertThat(content.startsWith("{\"steps\":{\"items\":[")); + assertThat(content).startsWith("{\"steps\":{\"items\":["); MathReasoning mathReasoning = outputConverter.convert(content); diff --git a/models/spring-ai-openai/src/test/java/org/springframework/ai/openai/moderation/OpenAiModerationModelIT.java b/models/spring-ai-openai/src/test/java/org/springframework/ai/openai/moderation/OpenAiModerationModelIT.java index 6fe5f640949..8bcfd4b12eb 100644 --- a/models/spring-ai-openai/src/test/java/org/springframework/ai/openai/moderation/OpenAiModerationModelIT.java +++ b/models/spring-ai-openai/src/test/java/org/springframework/ai/openai/moderation/OpenAiModerationModelIT.java @@ -16,6 +16,7 @@ package org.springframework.ai.openai.moderation; +import org.assertj.core.data.Offset; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.condition.EnabledIfEnvironmentVariable; @@ -59,7 +60,6 @@ void moderationAsUrlTestPositive() { assertThat(moderation.getId()).isNotEmpty(); assertThat(moderation.getResults()).isNotNull(); assertThat(moderation.getResults().size()).isNotZero(); - System.out.println(moderation.getResults().toString()); assertThat(moderation.getId()).isNotNull(); assertThat(moderation.getModel()).isNotNull(); @@ -68,30 +68,11 @@ void moderationAsUrlTestPositive() { assertThat(result.isFlagged()).isTrue(); Categories categories = result.getCategories(); assertThat(categories).isNotNull(); - assertThat(categories.isSexual()).isNotNull(); - assertThat(categories.isHate()).isNotNull(); - assertThat(categories.isHarassment()).isNotNull(); - assertThat(categories.isSelfHarm()).isNotNull(); - assertThat(categories.isSexualMinors()).isNotNull(); - assertThat(categories.isHateThreatening()).isNotNull(); - assertThat(categories.isViolenceGraphic()).isNotNull(); - assertThat(categories.isSelfHarmIntent()).isNotNull(); - assertThat(categories.isSelfHarmInstructions()).isNotNull(); - assertThat(categories.isHarassmentThreatening()).isNotNull(); assertThat(categories.isViolence()).isTrue(); CategoryScores scores = result.getCategoryScores(); - assertThat(scores.getSexual()).isNotNull(); - assertThat(scores.getHate()).isNotNull(); - assertThat(scores.getHarassment()).isNotNull(); - assertThat(scores.getSelfHarm()).isNotNull(); - assertThat(scores.getSexualMinors()).isNotNull(); - assertThat(scores.getHateThreatening()).isNotNull(); - assertThat(scores.getViolenceGraphic()).isNotNull(); - assertThat(scores.getSelfHarmIntent()).isNotNull(); - assertThat(scores.getSelfHarmInstructions()).isNotNull(); - assertThat(scores.getHarassmentThreatening()).isNotNull(); - assertThat(scores.getViolence()).isNotNull(); + assertThat(scores.getSexual()).isCloseTo(0.0d, Offset.offset(0.1d)); + assertThat(scores.getViolence()).isCloseTo(1.0d, Offset.offset(0.2d)); } @@ -113,7 +94,6 @@ void moderationAsUrlTestNegative() { assertThat(moderation.getId()).isNotEmpty(); assertThat(moderation.getResults()).isNotNull(); assertThat(moderation.getResults().size()).isNotZero(); - System.out.println(moderation.getResults().toString()); assertThat(moderation.getId()).isNotNull(); assertThat(moderation.getModel()).isNotNull(); @@ -134,17 +114,17 @@ void moderationAsUrlTestNegative() { assertThat(categories.isViolence()).isFalse(); CategoryScores scores = result.getCategoryScores(); - assertThat(scores.getSexual()).isNotNull(); - assertThat(scores.getHate()).isNotNull(); - assertThat(scores.getHarassment()).isNotNull(); - assertThat(scores.getSelfHarm()).isNotNull(); - assertThat(scores.getSexualMinors()).isNotNull(); - assertThat(scores.getHateThreatening()).isNotNull(); - assertThat(scores.getViolenceGraphic()).isNotNull(); - assertThat(scores.getSelfHarmIntent()).isNotNull(); - assertThat(scores.getSelfHarmInstructions()).isNotNull(); - assertThat(scores.getHarassmentThreatening()).isNotNull(); - assertThat(scores.getViolence()).isNotNull(); + assertThat(scores.getSexual()).isCloseTo(0.0d, Offset.offset(0.1d)); + assertThat(scores.getHate()).isCloseTo(0.0d, Offset.offset(0.1d)); + assertThat(scores.getHarassment()).isCloseTo(0.0d, Offset.offset(0.1d)); + assertThat(scores.getSelfHarm()).isCloseTo(0.0d, Offset.offset(0.1d)); + assertThat(scores.getSexualMinors()).isCloseTo(0.0d, Offset.offset(0.1d)); + assertThat(scores.getHateThreatening()).isCloseTo(0.0d, Offset.offset(0.1d)); + assertThat(scores.getViolenceGraphic()).isCloseTo(0.0d, Offset.offset(0.1d)); + assertThat(scores.getSelfHarmIntent()).isCloseTo(0.0d, Offset.offset(0.1d)); + assertThat(scores.getSelfHarmInstructions()).isCloseTo(0.0d, Offset.offset(0.1d)); + assertThat(scores.getHarassmentThreatening()).isCloseTo(0.0d, Offset.offset(0.1d)); + assertThat(scores.getViolence()).isCloseTo(0.0d, Offset.offset(0.1d)); } diff --git a/models/spring-ai-transformers/src/test/java/org/springframework/ai/transformers/ResourceCacheServiceTests.java b/models/spring-ai-transformers/src/test/java/org/springframework/ai/transformers/ResourceCacheServiceTests.java index 80f79aa6818..b7a0f4a4544 100644 --- a/models/spring-ai-transformers/src/test/java/org/springframework/ai/transformers/ResourceCacheServiceTests.java +++ b/models/spring-ai-transformers/src/test/java/org/springframework/ai/transformers/ResourceCacheServiceTests.java @@ -19,7 +19,9 @@ import java.io.File; import java.io.IOException; import java.nio.file.Files; +import java.nio.file.Path; import java.util.List; +import java.util.stream.Stream; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.io.TempDir; @@ -44,7 +46,9 @@ public void fileResourcesAreExcludedByDefault() throws IOException { var cachedResource = cache.getCachedResource(originalResourceUri); assertThat(cachedResource).isEqualTo(new DefaultResourceLoader().getResource(originalResourceUri)); - assertThat(Files.list(this.tempDir.toPath()).count()).isEqualTo(0); + try (Stream paths = Files.list(this.tempDir.toPath())) { + assertThat(paths.count()).isEqualTo(0); + } } @Test @@ -58,8 +62,8 @@ public void cacheFileResources() throws IOException { var cachedResource1 = cache.getCachedResource(originalResourceUri); assertThat(cachedResource1).isNotEqualTo(new DefaultResourceLoader().getResource(originalResourceUri)); - assertThat(Files.list(this.tempDir.toPath()).count()).isEqualTo(1); - assertThat(Files.list(Files.list(this.tempDir.toPath()).iterator().next()).count()).isEqualTo(1); + assertThat(this.tempDir.listFiles()).hasSize(1); + assertThat(this.tempDir.listFiles()[0].listFiles()).hasSize(1); // Attempt to cache the same resource again should return the already cached // resource. @@ -68,8 +72,8 @@ public void cacheFileResources() throws IOException { assertThat(cachedResource2).isNotEqualTo(new DefaultResourceLoader().getResource(originalResourceUri)); assertThat(cachedResource2).isEqualTo(cachedResource1); - assertThat(Files.list(this.tempDir.toPath()).count()).isEqualTo(1); - assertThat(Files.list(Files.list(this.tempDir.toPath()).iterator().next()).count()).isEqualTo(1); + assertThat(this.tempDir.listFiles()).hasSize(1); + assertThat(this.tempDir.listFiles()[0].listFiles()).hasSize(1); } @@ -91,11 +95,10 @@ public void cacheFileResourcesFromSameParentFolder() throws IOException { assertThat(cachedResource2).isNotEqualTo(new DefaultResourceLoader().getResource(originalResourceUri1)); assertThat(cachedResource2).isNotEqualTo(cachedResource1); - assertThat(Files.list(this.tempDir.toPath()).count()).isEqualTo(1) + assertThat(this.tempDir.listFiles()).hasSize(1) .describedAs( "As both resources come from the same parent segments they should be cached in a single common parent."); - assertThat(Files.list(Files.list(this.tempDir.toPath()).iterator().next()).count()).isEqualTo(2); - + assertThat(this.tempDir.listFiles()[0].listFiles()).hasSize(2); } @Test @@ -106,8 +109,8 @@ public void cacheHttpResources() throws IOException { var cachedResource1 = cache.getCachedResource(originalResourceUri1); assertThat(cachedResource1).isNotEqualTo(new DefaultResourceLoader().getResource(originalResourceUri1)); - assertThat(Files.list(this.tempDir.toPath()).count()).isEqualTo(1); - assertThat(Files.list(Files.list(this.tempDir.toPath()).iterator().next()).count()).isEqualTo(1); + assertThat(this.tempDir.listFiles()).hasSize(1); + assertThat(this.tempDir.listFiles()[0].listFiles()).hasSize(1); } @Test diff --git a/models/spring-ai-vertex-ai-gemini/src/main/java/org/springframework/ai/vertexai/gemini/VertexAiGeminiChatModel.java b/models/spring-ai-vertex-ai-gemini/src/main/java/org/springframework/ai/vertexai/gemini/VertexAiGeminiChatModel.java index a32dd3fb5d9..56589b39d44 100644 --- a/models/spring-ai-vertex-ai-gemini/src/main/java/org/springframework/ai/vertexai/gemini/VertexAiGeminiChatModel.java +++ b/models/spring-ai-vertex-ai-gemini/src/main/java/org/springframework/ai/vertexai/gemini/VertexAiGeminiChatModel.java @@ -61,7 +61,6 @@ import org.springframework.ai.chat.metadata.ChatGenerationMetadata; import org.springframework.ai.chat.metadata.ChatResponseMetadata; import org.springframework.ai.chat.metadata.DefaultUsage; -import org.springframework.ai.chat.metadata.EmptyUsage; import org.springframework.ai.chat.metadata.Usage; import org.springframework.ai.chat.model.ChatModel; import org.springframework.ai.chat.model.ChatResponse; @@ -404,9 +403,7 @@ private ChatResponse internalCall(Prompt prompt, ChatResponse previousChatRespon .toList(); GenerateContentResponse.UsageMetadata usage = generateContentResponse.getUsageMetadata(); - Usage currentUsage = (usage != null) - ? new DefaultUsage(usage.getPromptTokenCount(), usage.getCandidatesTokenCount()) - : new EmptyUsage(); + Usage currentUsage = new DefaultUsage(usage.getPromptTokenCount(), usage.getCandidatesTokenCount()); Usage cumulativeUsage = UsageCalculator.getCumulativeUsage(currentUsage, previousChatResponse); ChatResponse chatResponse = new ChatResponse(generations, toChatResponseMetadata(cumulativeUsage)); @@ -520,7 +517,7 @@ public Flux internalStream(Prompt prompt, ChatResponse previousCha .toList(); GenerateContentResponse.UsageMetadata usage = response.getUsageMetadata(); - Usage currentUsage = (usage != null) ? getDefaultUsage(usage) : new EmptyUsage(); + Usage currentUsage = getDefaultUsage(usage); Usage cumulativeUsage = UsageCalculator.getCumulativeUsage(currentUsage, previousChatResponse); ChatResponse chatResponse = new ChatResponse(generations, toChatResponseMetadata(cumulativeUsage)); return Flux.just(chatResponse); diff --git a/models/spring-ai-vertex-ai-gemini/src/main/java/org/springframework/ai/vertexai/gemini/VertexAiGeminiChatOptions.java b/models/spring-ai-vertex-ai-gemini/src/main/java/org/springframework/ai/vertexai/gemini/VertexAiGeminiChatOptions.java index bbdf7cdbd9a..53716dfec3d 100644 --- a/models/spring-ai-vertex-ai-gemini/src/main/java/org/springframework/ai/vertexai/gemini/VertexAiGeminiChatOptions.java +++ b/models/spring-ai-vertex-ai-gemini/src/main/java/org/springframework/ai/vertexai/gemini/VertexAiGeminiChatOptions.java @@ -379,7 +379,7 @@ public boolean equals(Object o) { if (!(o instanceof VertexAiGeminiChatOptions that)) { return false; } - return this.googleSearchRetrieval == that.googleSearchRetrieval + return Objects.equals(this.googleSearchRetrieval, that.googleSearchRetrieval) && Objects.equals(this.stopSequences, that.stopSequences) && Objects.equals(this.temperature, that.temperature) && Objects.equals(this.topP, that.topP) && Objects.equals(this.topK, that.topK) && Objects.equals(this.candidateCount, that.candidateCount) diff --git a/models/spring-ai-vertex-ai-gemini/src/test/java/org/springframework/ai/vertexai/gemini/CreateGeminiRequestTests.java b/models/spring-ai-vertex-ai-gemini/src/test/java/org/springframework/ai/vertexai/gemini/CreateGeminiRequestTests.java index 3203ffa67ab..5c64f23d8c9 100644 --- a/models/spring-ai-vertex-ai-gemini/src/test/java/org/springframework/ai/vertexai/gemini/CreateGeminiRequestTests.java +++ b/models/spring-ai-vertex-ai-gemini/src/test/java/org/springframework/ai/vertexai/gemini/CreateGeminiRequestTests.java @@ -134,7 +134,6 @@ public void createRequestWithSystemMessage() throws MalformedURLException { assertThat(textPart.getText()).isEqualTo("User Message Text"); Part mediaPart = content.getParts(1); - assertThat(mediaPart.getFileData()).isNotNull(); assertThat(mediaPart.getFileData().getFileUri()).isEqualTo("http://example.com"); assertThat(mediaPart.getFileData().getMimeType()).isEqualTo(MimeTypeUtils.IMAGE_PNG.toString()); System.out.println(mediaPart); diff --git a/models/spring-ai-zhipuai/src/test/java/org/springframework/ai/zhipuai/ZhiPuAiChatOptionsTests.java b/models/spring-ai-zhipuai/src/test/java/org/springframework/ai/zhipuai/ZhiPuAiChatOptionsTests.java index 435ca9c24f4..1c7096df25d 100644 --- a/models/spring-ai-zhipuai/src/test/java/org/springframework/ai/zhipuai/ZhiPuAiChatOptionsTests.java +++ b/models/spring-ai-zhipuai/src/test/java/org/springframework/ai/zhipuai/ZhiPuAiChatOptionsTests.java @@ -183,6 +183,7 @@ void testDefaultValues() { } @Test + @SuppressWarnings("SelfAssertion") void testEqualsAndHashCode() { ZhiPuAiChatOptions options1 = ZhiPuAiChatOptions.builder() .model("test-model") diff --git a/pom.xml b/pom.xml index 2986c7185a5..ca92d2e6191 100644 --- a/pom.xml +++ b/pom.xml @@ -372,6 +372,9 @@ 0.0.43 3.2.8 + 2.44.0 + 0.12.12 + false @@ -483,9 +486,35 @@ ${maven-compiler-plugin.version} ${java.version} + true - -parameters + -parameters + -XDcompilePolicy=simple + --should-stop=ifError=FLOW + -Xplugin:ErrorProne -Xep:NullAway:ERROR -XepOpt:NullAway:OnlyNullMarked -XepOpt:NullAway:JSpecifyMode=true + -J--add-exports=jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED + -J--add-exports=jdk.compiler/com.sun.tools.javac.file=ALL-UNNAMED + -J--add-exports=jdk.compiler/com.sun.tools.javac.main=ALL-UNNAMED + -J--add-exports=jdk.compiler/com.sun.tools.javac.model=ALL-UNNAMED + -J--add-exports=jdk.compiler/com.sun.tools.javac.parser=ALL-UNNAMED + -J--add-exports=jdk.compiler/com.sun.tools.javac.processing=ALL-UNNAMED + -J--add-exports=jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED + -J--add-exports=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED + -J--add-opens=jdk.compiler/com.sun.tools.javac.code=ALL-UNNAMED + -J--add-opens=jdk.compiler/com.sun.tools.javac.comp=ALL-UNNAMED + + + com.google.errorprone + error_prone_core + ${error-prone.version} + + + com.uber.nullaway + nullaway + ${nullaway.version} + + diff --git a/spring-ai-model/src/test/java/org/springframework/ai/chat/metadata/DefaultUsageTests.java b/spring-ai-model/src/test/java/org/springframework/ai/chat/metadata/DefaultUsageTests.java index 8f1f39deca7..cada62d555c 100644 --- a/spring-ai-model/src/test/java/org/springframework/ai/chat/metadata/DefaultUsageTests.java +++ b/spring-ai-model/src/test/java/org/springframework/ai/chat/metadata/DefaultUsageTests.java @@ -183,6 +183,7 @@ void testArbitraryNativeUsageMap() throws Exception { } @Test + @SuppressWarnings("SelfAssertion") void testEqualsAndHashCode() { DefaultUsage usage1 = new DefaultUsage(Integer.valueOf(100), Integer.valueOf(50), Integer.valueOf(150)); DefaultUsage usage2 = new DefaultUsage(Integer.valueOf(100), Integer.valueOf(50), Integer.valueOf(150)); diff --git a/vector-stores/spring-ai-mariadb-store/src/test/java/org/springframework/ai/vectorstore/mariadb/MariaDBStoreTests.java b/vector-stores/spring-ai-mariadb-store/src/test/java/org/springframework/ai/vectorstore/mariadb/MariaDBStoreTests.java index 952c6c98a32..8c389bd367e 100644 --- a/vector-stores/spring-ai-mariadb-store/src/test/java/org/springframework/ai/vectorstore/mariadb/MariaDBStoreTests.java +++ b/vector-stores/spring-ai-mariadb-store/src/test/java/org/springframework/ai/vectorstore/mariadb/MariaDBStoreTests.java @@ -51,7 +51,7 @@ public class MariaDBStoreTests { "user_data, false, user_data", "test123, false, test123", "valid_table_name, false, valid_table_name", "1234567890123456789012345678901234567890123456789012345678901234, false, `1234567890123456789012345678901234567890123456789012345678901234`" }) void enquoteIdentifier(String tableName, boolean alwaysQuote, String expected) { - assertThat(MariaDBSchemaValidator.validateAndEnquoteIdentifier(tableName, alwaysQuote)); + assertThat(MariaDBSchemaValidator.validateAndEnquoteIdentifier(tableName, alwaysQuote)).isEqualTo(expected); } @ParameterizedTest(name = "{0} - error identifier validation")