FIX BUILD: Fix code coverage and some improvements (#2010)
## Issue Build was failing due to low test coverage after [this change](https://github.com/langchain4j/langchain4j/pull/1987/files#diff-9a5519a26a4b6d2fd412c877de4459c2a119e793fece9385f558a93a7e0aee5aR273-R275). The hotfix is to enable tracing for logger with mockStatic for in the affected DefaultRetrievalAugmentorTest. Other changes: * **Refine integration test run conditions to exclude experimental builds and require the presence of the `OPENAI_API_KEY` secret.** * [Upgrade mockito instrumentation](https://javadoc.io/doc/org.mockito/mockito-core/latest/org/mockito/Mockito.html#0.3) (javaagent) setup to make it compatible with new JDKs * Updated `pom.xml` to upgrade `jacoco-maven-plugin` version. * Enhanced `README.md` with additional badges for nightly build and Codacy dashboard. (`[README.mdL3-R6](diffhunk://#diff-b335630551682c19a781afebcf4d07bf978fb1f8ac04c6bf87428ed5106870f5L3-R6)`) * Introduced new tests for comparison filters * Added tests for logical filters * Added configuration for external dependencies in `.idea/externalDependencies.xml` to include SonarLint and SpotBugs plugins. ## General checklist - [x] There are no breaking changes - [x] I have added unit and integration tests for my change - [ ] I have manually run all the unit and integration tests in the module I have added/changed, and they are all green - [ ] I have manually run all the unit and integration tests in the [core](https://github.com/langchain4j/langchain4j/tree/main/langchain4j-core) and [main](https://github.com/langchain4j/langchain4j/tree/main/langchain4j) modules, and they are all green - [ ] I have added/updated the [documentation](https://github.com/langchain4j/langchain4j/tree/main/docs/docs) - [ ] I have added an example in the [examples repo](https://github.com/langchain4j/langchain4j-examples) (only for "big" features) - [ ] I have added/updated [Spring Boot starter(s)](https://github.com/langchain4j/langchain4j-spring) (if applicable)
This commit is contained in:
parent
aa0e488166
commit
9178367109
|
|
@ -46,6 +46,8 @@ jobs:
|
|||
- java_version: 23
|
||||
mvn_opts: ''
|
||||
experimental: true
|
||||
env:
|
||||
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
|
|
@ -69,11 +71,10 @@ jobs:
|
|||
${{ matrix.mvn_opts }}
|
||||
|
||||
- name: Integration test with JDK ${{ matrix.java_version }}
|
||||
## allow builds to run only:
|
||||
## - on the main branch when it is not a pull request,
|
||||
## - for pull requests from the same repository
|
||||
## - when triggered manually
|
||||
if: ${{ !matrix.experimental }} && (github.event_name == 'pull_request' && github.head_repo.full_name == github.repository) || (github.event_name == 'push' && github.ref == 'refs/heads/main') || github.event_name == 'workflow_dispatch'
|
||||
## The step or job will only run if the `experimental` variable
|
||||
## in the matrix is false (not set to true)
|
||||
## and the OPENAI_API_KEY secret is available and not empty.
|
||||
if: ${{ !matrix.experimental && env.OPENAI_API_KEY != '' }}
|
||||
run: |
|
||||
mvn -B -U verify \
|
||||
-Dgib.disable=false -Dgib.referenceBranch=__branch_before \
|
||||
|
|
|
|||
|
|
@ -0,0 +1,7 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ExternalDependencies">
|
||||
<plugin id="org.sonarlint.idea" />
|
||||
<plugin id="org.jetbrains.plugins.spotbugs" />
|
||||
</component>
|
||||
</project>
|
||||
|
|
@ -1,6 +1,9 @@
|
|||
# LangChain for Java: Supercharge your Java application with the power of LLMs
|
||||
|
||||
[](https://github.com/langchain4j/langchain4j/actions/workflows/main.yaml)
|
||||
[](https://github.com/langchain4j/langchain4j/actions/workflows/main.yaml)
|
||||
[](https://github.com/langchain4j/langchain4j/actions/workflows/nightly.yaml)
|
||||
[](https://app.codacy.com/gh/langchain4j/langchain4j/dashboard)
|
||||
|
||||
[](https://discord.gg/JzTFvyjG6R)
|
||||
[](https://x.com/langchain4j)
|
||||
[](https://search.maven.org/#search|gav|1|g:"dev.langchain4j"%20AND%20a:"langchain4j")
|
||||
|
|
|
|||
|
|
@ -22,11 +22,11 @@ public class GraalVmJavaScriptExecutionEngine implements CodeExecutionEngine {
|
|||
public String execute(String code) {
|
||||
OutputStream outputStream = new ByteArrayOutputStream();
|
||||
try (Context context = Context.newBuilder("js")
|
||||
.sandbox(CONSTRAINED)
|
||||
.allowHostAccess(UNTRUSTED)
|
||||
.out(outputStream)
|
||||
.err(outputStream)
|
||||
.build()) {
|
||||
.sandbox(CONSTRAINED)
|
||||
.allowHostAccess(UNTRUSTED)
|
||||
.out(outputStream)
|
||||
.err(outputStream)
|
||||
.build()) {
|
||||
Object result = context.eval("js", code).as(Object.class);
|
||||
return String.valueOf(result);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,11 +22,11 @@ public class GraalVmPythonExecutionEngine implements CodeExecutionEngine {
|
|||
public String execute(String code) {
|
||||
OutputStream outputStream = new ByteArrayOutputStream();
|
||||
try (Context context = Context.newBuilder("python")
|
||||
.sandbox(TRUSTED)
|
||||
.allowHostAccess(UNTRUSTED)
|
||||
.out(outputStream)
|
||||
.err(outputStream)
|
||||
.build()) {
|
||||
.sandbox(TRUSTED)
|
||||
.allowHostAccess(UNTRUSTED)
|
||||
.out(outputStream)
|
||||
.err(outputStream)
|
||||
.build()) {
|
||||
Object result = context.eval("python", code).as(Object.class);
|
||||
return String.valueOf(result);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,11 +25,11 @@ class Judge0JavaScriptEngine implements CodeExecutionEngine {
|
|||
this.apiKey = apiKey;
|
||||
this.languageId = languageId;
|
||||
this.client = new OkHttpClient.Builder()
|
||||
.connectTimeout(timeout)
|
||||
.readTimeout(timeout)
|
||||
.writeTimeout(timeout)
|
||||
.callTimeout(timeout)
|
||||
.build();
|
||||
.connectTimeout(timeout)
|
||||
.readTimeout(timeout)
|
||||
.writeTimeout(timeout)
|
||||
.callTimeout(timeout)
|
||||
.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -42,10 +42,10 @@ class Judge0JavaScriptEngine implements CodeExecutionEngine {
|
|||
RequestBody requestBody = RequestBody.create(Json.toJson(submission), MEDIA_TYPE);
|
||||
|
||||
Request request = new Request.Builder()
|
||||
.url("https://judge0-ce.p.rapidapi.com/submissions?base64_encoded=true&wait=true&fields=*")
|
||||
.addHeader("X-RapidAPI-Key", apiKey)
|
||||
.post(requestBody)
|
||||
.build();
|
||||
.url("https://judge0-ce.p.rapidapi.com/submissions?base64_encoded=true&wait=true&fields=*")
|
||||
.addHeader("X-RapidAPI-Key", apiKey)
|
||||
.post(requestBody)
|
||||
.build();
|
||||
|
||||
try {
|
||||
Response response = client.newCall(request).execute();
|
||||
|
|
|
|||
|
|
@ -34,6 +34,7 @@
|
|||
<artifactId>slf4j-api</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- Test dependencies -->
|
||||
<dependency>
|
||||
<groupId>org.junit.jupiter</groupId>
|
||||
<artifactId>junit-jupiter-engine</artifactId>
|
||||
|
|
@ -85,7 +86,6 @@
|
|||
|
||||
<build>
|
||||
<plugins>
|
||||
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-jar-plugin</artifactId>
|
||||
|
|
@ -101,7 +101,7 @@
|
|||
<plugin>
|
||||
<groupId>org.jacoco</groupId>
|
||||
<artifactId>jacoco-maven-plugin</artifactId>
|
||||
<version>0.8.11</version>
|
||||
<version>0.8.12</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>prepare-agent</id>
|
||||
|
|
@ -209,4 +209,21 @@
|
|||
</license>
|
||||
</licenses>
|
||||
|
||||
<reporting>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.jacoco</groupId>
|
||||
<artifactId>jacoco-maven-plugin</artifactId>
|
||||
<reportSets>
|
||||
<reportSet>
|
||||
<reports>
|
||||
<!-- select non-aggregate reports -->
|
||||
<report>report</report>
|
||||
</reports>
|
||||
</reportSet>
|
||||
</reportSets>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</reporting>
|
||||
|
||||
</project>
|
||||
|
|
|
|||
|
|
@ -12,9 +12,14 @@ import dev.langchain4j.rag.query.router.DefaultQueryRouter;
|
|||
import dev.langchain4j.rag.query.router.QueryRouter;
|
||||
import dev.langchain4j.rag.query.transformer.DefaultQueryTransformer;
|
||||
import dev.langchain4j.rag.query.transformer.QueryTransformer;
|
||||
import org.junit.jupiter.api.AfterAll;
|
||||
import org.junit.jupiter.api.BeforeAll;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.MethodSource;
|
||||
import org.mockito.MockedStatic;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
|
|
@ -32,14 +37,31 @@ import static java.util.stream.Collectors.toList;
|
|||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.Mockito.any;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.mockStatic;
|
||||
import static org.mockito.Mockito.spy;
|
||||
import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.verifyNoInteractions;
|
||||
import static org.mockito.Mockito.verifyNoMoreInteractions;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
class DefaultRetrievalAugmentorTest {
|
||||
|
||||
private static MockedStatic<LoggerFactory> loggerFactoryMock;
|
||||
|
||||
@BeforeAll
|
||||
static void mockLogger() {
|
||||
loggerFactoryMock = mockStatic(LoggerFactory.class);
|
||||
Logger logger = mock(Logger.class);
|
||||
when(LoggerFactory.getLogger(DefaultRetrievalAugmentor.class)).thenReturn(logger);
|
||||
when(logger.isTraceEnabled()).thenReturn(true);
|
||||
}
|
||||
|
||||
@AfterAll
|
||||
static void releaseLogger() {
|
||||
loggerFactoryMock.close();
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("executors")
|
||||
void should_augment_user_message__multiple_queries_multiple_retrievers(Executor executor) {
|
||||
|
|
@ -64,12 +86,12 @@ class DefaultRetrievalAugmentorTest {
|
|||
ContentInjector contentInjector = spy(new TestContentInjector());
|
||||
|
||||
RetrievalAugmentor retrievalAugmentor = DefaultRetrievalAugmentor.builder()
|
||||
.queryTransformer(queryTransformer)
|
||||
.queryRouter(queryRouter)
|
||||
.contentAggregator(contentAggregator)
|
||||
.contentInjector(contentInjector)
|
||||
.executor(executor)
|
||||
.build();
|
||||
.queryTransformer(queryTransformer)
|
||||
.queryRouter(queryRouter)
|
||||
.contentAggregator(contentAggregator)
|
||||
.contentInjector(contentInjector)
|
||||
.executor(executor)
|
||||
.build();
|
||||
|
||||
UserMessage userMessage = UserMessage.from("query");
|
||||
|
||||
|
|
@ -80,7 +102,7 @@ class DefaultRetrievalAugmentorTest {
|
|||
|
||||
// then
|
||||
assertThat(augmented.singleText()).isEqualTo(
|
||||
"""
|
||||
"""
|
||||
query
|
||||
content 1
|
||||
content 2
|
||||
|
|
@ -109,25 +131,25 @@ class DefaultRetrievalAugmentorTest {
|
|||
|
||||
Map<Query, Collection<List<Content>>> queryToContents = new HashMap<>();
|
||||
queryToContents.put(query1, asList(
|
||||
asList(content1, content2),
|
||||
asList(content3, content4)
|
||||
asList(content1, content2),
|
||||
asList(content3, content4)
|
||||
|
||||
));
|
||||
queryToContents.put(query2, asList(
|
||||
asList(content1, content2),
|
||||
asList(content3, content4)
|
||||
asList(content1, content2),
|
||||
asList(content3, content4)
|
||||
|
||||
));
|
||||
verify(contentAggregator).aggregate(queryToContents);
|
||||
verifyNoMoreInteractions(contentAggregator);
|
||||
|
||||
verify(contentInjector).inject(asList(
|
||||
content1, content2, content3, content4,
|
||||
content1, content2, content3, content4
|
||||
content1, content2, content3, content4,
|
||||
content1, content2, content3, content4
|
||||
), userMessage);
|
||||
verify(contentInjector).inject(asList(
|
||||
content1, content2, content3, content4,
|
||||
content1, content2, content3, content4
|
||||
content1, content2, content3, content4,
|
||||
content1, content2, content3, content4
|
||||
), (ChatMessage) userMessage);
|
||||
verifyNoMoreInteractions(contentInjector);
|
||||
}
|
||||
|
|
@ -155,12 +177,12 @@ class DefaultRetrievalAugmentorTest {
|
|||
Executor executor = spy(new TestExecutor());
|
||||
|
||||
RetrievalAugmentor retrievalAugmentor = DefaultRetrievalAugmentor.builder()
|
||||
.queryTransformer(queryTransformer)
|
||||
.queryRouter(queryRouter)
|
||||
.contentAggregator(contentAggregator)
|
||||
.contentInjector(contentInjector)
|
||||
.executor(executor)
|
||||
.build();
|
||||
.queryTransformer(queryTransformer)
|
||||
.queryRouter(queryRouter)
|
||||
.contentAggregator(contentAggregator)
|
||||
.contentInjector(contentInjector)
|
||||
.executor(executor)
|
||||
.build();
|
||||
|
||||
UserMessage userMessage = UserMessage.from("query");
|
||||
|
||||
|
|
@ -171,7 +193,7 @@ class DefaultRetrievalAugmentorTest {
|
|||
|
||||
// then
|
||||
assertThat(augmented.singleText()).isEqualTo(
|
||||
"""
|
||||
"""
|
||||
query
|
||||
content 1
|
||||
content 2
|
||||
|
|
@ -194,8 +216,8 @@ class DefaultRetrievalAugmentorTest {
|
|||
|
||||
Map<Query, Collection<List<Content>>> queryToContents = new HashMap<>();
|
||||
queryToContents.put(query, asList(
|
||||
asList(content1, content2),
|
||||
asList(content3, content4)
|
||||
asList(content1, content2),
|
||||
asList(content3, content4)
|
||||
|
||||
));
|
||||
verify(contentAggregator).aggregate(queryToContents);
|
||||
|
|
@ -236,12 +258,12 @@ class DefaultRetrievalAugmentorTest {
|
|||
Executor executor = mock(Executor.class);
|
||||
|
||||
RetrievalAugmentor retrievalAugmentor = DefaultRetrievalAugmentor.builder()
|
||||
.queryTransformer(queryTransformer)
|
||||
.queryRouter(queryRouter)
|
||||
.contentAggregator(contentAggregator)
|
||||
.contentInjector(contentInjector)
|
||||
.executor(executor)
|
||||
.build();
|
||||
.queryTransformer(queryTransformer)
|
||||
.queryRouter(queryRouter)
|
||||
.contentAggregator(contentAggregator)
|
||||
.contentInjector(contentInjector)
|
||||
.executor(executor)
|
||||
.build();
|
||||
|
||||
UserMessage userMessage = UserMessage.from("query");
|
||||
|
||||
|
|
@ -252,7 +274,7 @@ class DefaultRetrievalAugmentorTest {
|
|||
|
||||
// then
|
||||
assertThat(augmented.singleText()).isEqualTo(
|
||||
"""
|
||||
"""
|
||||
query
|
||||
content 1
|
||||
content 2"""
|
||||
|
|
@ -289,9 +311,9 @@ class DefaultRetrievalAugmentorTest {
|
|||
QueryRouter queryRouter = spy(new TestQueryRouter(retrievers));
|
||||
|
||||
RetrievalAugmentor retrievalAugmentor = DefaultRetrievalAugmentor.builder()
|
||||
.queryRouter(queryRouter)
|
||||
.executor(executor)
|
||||
.build();
|
||||
.queryRouter(queryRouter)
|
||||
.executor(executor)
|
||||
.build();
|
||||
|
||||
UserMessage userMessage = UserMessage.from("query");
|
||||
|
||||
|
|
@ -309,14 +331,14 @@ class DefaultRetrievalAugmentorTest {
|
|||
|
||||
static Stream<Executor> executors() {
|
||||
return Stream.<Executor>builder()
|
||||
.add(Executors.newCachedThreadPool())
|
||||
.add(Executors.newFixedThreadPool(1))
|
||||
.add(Executors.newFixedThreadPool(2))
|
||||
.add(Executors.newFixedThreadPool(3))
|
||||
.add(Executors.newFixedThreadPool(4))
|
||||
.add(Runnable::run) // same thread executor
|
||||
.add(null) // to use default Executor in DefaultRetrievalAugmentor
|
||||
.build();
|
||||
.add(Executors.newCachedThreadPool())
|
||||
.add(Executors.newFixedThreadPool(1))
|
||||
.add(Executors.newFixedThreadPool(2))
|
||||
.add(Executors.newFixedThreadPool(3))
|
||||
.add(Executors.newFixedThreadPool(4))
|
||||
.add(Runnable::run) // same thread executor
|
||||
.add(null) // to use default Executor in DefaultRetrievalAugmentor
|
||||
.build();
|
||||
}
|
||||
|
||||
static class TestQueryTransformer implements QueryTransformer {
|
||||
|
|
@ -366,10 +388,10 @@ class DefaultRetrievalAugmentorTest {
|
|||
@Override
|
||||
public List<Content> aggregate(Map<Query, Collection<List<Content>>> queryToContents) {
|
||||
return queryToContents.values()
|
||||
.stream()
|
||||
.flatMap(Collection::stream)
|
||||
.flatMap(List::stream)
|
||||
.collect(toList());
|
||||
.stream()
|
||||
.flatMap(Collection::stream)
|
||||
.flatMap(List::stream)
|
||||
.collect(toList());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -378,8 +400,8 @@ class DefaultRetrievalAugmentorTest {
|
|||
@Override
|
||||
public UserMessage inject(List<Content> contents, UserMessage userMessage) {
|
||||
String joinedContents = contents.stream()
|
||||
.map(it -> it.textSegment().text())
|
||||
.collect(joining("\n"));
|
||||
.map(it -> it.textSegment().text())
|
||||
.collect(joining("\n"));
|
||||
return UserMessage.from(userMessage.text() + "\n" + joinedContents);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -159,9 +159,10 @@ class LanguageModelQueryRouterTest {
|
|||
|
||||
ChatModelMock model = ChatModelMock.thatAlwaysResponds("Sorry, I don't know");
|
||||
|
||||
Map<ContentRetriever, String> retrieverToDescription = new LinkedHashMap<>();
|
||||
retrieverToDescription.put(catArticlesRetriever, "articles about cats");
|
||||
retrieverToDescription.put(dogArticlesRetriever, "articles about dogs");
|
||||
final var retrieverToDescription = Map.of(
|
||||
catArticlesRetriever, "articles about cats",
|
||||
dogArticlesRetriever, "articles about dogs"
|
||||
);
|
||||
|
||||
QueryRouter router = new LanguageModelQueryRouter(model, retrieverToDescription);
|
||||
|
||||
|
|
@ -290,4 +291,4 @@ class LanguageModelQueryRouterTest {
|
|||
.isExactlyInstanceOf(RuntimeException.class)
|
||||
.hasMessageContaining("Something went wrong");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,27 @@
|
|||
package dev.langchain4j.store.embedding.filter.comparison;
|
||||
|
||||
import dev.langchain4j.data.document.Metadata;
|
||||
import dev.langchain4j.store.embedding.filter.Filter;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
abstract class AbstractComparisonTest<T extends Filter> {
|
||||
|
||||
protected T subject;
|
||||
|
||||
@Test
|
||||
void shouldReturnsFalseWhenObjectIsNotMetadata() {
|
||||
assertThat(subject.test(new Object())).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldReturnsFalseWhenMetadataDoesNotContainKey() {
|
||||
Metadata metadata = mock(Metadata.class);
|
||||
when(metadata.containsKey("key")).thenReturn(false);
|
||||
|
||||
assertThat(subject.test(metadata)).isFalse();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,52 @@
|
|||
package dev.langchain4j.store.embedding.filter.comparison;
|
||||
|
||||
import dev.langchain4j.data.document.Metadata;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
class IsEqualToTest {
|
||||
|
||||
@Test
|
||||
void testShouldReturnFalseWhenNotMetadata() {
|
||||
IsEqualTo isEqualTo = new IsEqualTo("key", "value");
|
||||
assertThat(isEqualTo.test("notMetadata")).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testShouldReturnFalseWhenKeyNotFound() {
|
||||
IsEqualTo isEqualTo = new IsEqualTo("key", "value");
|
||||
Metadata metadata = new Metadata(Map.of());
|
||||
assertThat(isEqualTo.test(metadata)).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testShouldReturnTrueWhenValuesAreNumbers() {
|
||||
IsEqualTo isEqualTo = new IsEqualTo("key", 2);
|
||||
Metadata metadata = new Metadata(Map.of("key", 2));
|
||||
assertThat(isEqualTo.test(metadata)).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testShouldReturnTrueWhenValuesAreStrings() {
|
||||
IsEqualTo isEqualTo = new IsEqualTo("key", "value");
|
||||
Metadata metadata = new Metadata(new HashMap<>() {{
|
||||
put("key", "value");
|
||||
}});
|
||||
assertThat(isEqualTo.test(metadata)).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testShouldReturnTrueWhenActualValueIsUUIDAsString() {
|
||||
UUID uuid = UUID.randomUUID();
|
||||
IsEqualTo isEqualTo = new IsEqualTo("key", uuid);
|
||||
Metadata metadata = new Metadata(new HashMap<>() {{
|
||||
put("key", uuid.toString());
|
||||
}});
|
||||
assertThat(isEqualTo.test(metadata)).isTrue();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
package dev.langchain4j.store.embedding.filter.comparison;
|
||||
|
||||
import dev.langchain4j.data.document.Metadata;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.CsvSource;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
class IsGreaterThanOrEqualToTest extends AbstractComparisonTest<IsGreaterThanOrEqualTo> {
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
subject = new IsGreaterThanOrEqualTo("key", 5);
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@CsvSource({
|
||||
"4, false",
|
||||
"5, true",
|
||||
"6, true"
|
||||
})
|
||||
void testComparisonValue(Integer value, boolean expectedResult) {
|
||||
Metadata metadata = Metadata.from(Map.of("key", value));
|
||||
assertThat(subject.test(metadata)).isEqualTo(expectedResult);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,34 @@
|
|||
package dev.langchain4j.store.embedding.filter.comparison;
|
||||
|
||||
import dev.langchain4j.data.document.Metadata;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.CsvSource;
|
||||
import org.mockito.MockedConstruction;
|
||||
import org.mockito.Mockito;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
class IsGreaterThanTest extends AbstractComparisonTest<IsGreaterThan>{
|
||||
|
||||
@BeforeEach
|
||||
void beforeEach() {
|
||||
subject = new IsGreaterThan("key", 5);
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@CsvSource({
|
||||
"0, false",
|
||||
"4, false",
|
||||
"5, false",
|
||||
"6, true"
|
||||
})
|
||||
void testComparisonValue(Integer value, boolean expectedResult) {
|
||||
Metadata metadata = Metadata.from(Map.of("key", value));
|
||||
assertThat(subject.test(metadata)).isEqualTo(expectedResult);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
package dev.langchain4j.store.embedding.filter.comparison;
|
||||
|
||||
import dev.langchain4j.data.document.Metadata;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.CsvSource;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
class IsLessThanOrEqualToTest extends AbstractComparisonTest<IsLessThanOrEqualTo> {
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
subject = new IsLessThanOrEqualTo("key", 5);
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@CsvSource({
|
||||
"0, true",
|
||||
"4, true",
|
||||
"5, true",
|
||||
"6, false"
|
||||
})
|
||||
void testComparisonValue(Integer value, boolean expectedResult) {
|
||||
Metadata metadata = Metadata.from(Map.of("key", value));
|
||||
assertThat(subject.test(metadata)).isEqualTo(expectedResult);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
package dev.langchain4j.store.embedding.filter.comparison;
|
||||
|
||||
import dev.langchain4j.data.document.Metadata;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.CsvSource;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
class IsLessThanTest extends AbstractComparisonTest<IsLessThan> {
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
subject = new IsLessThan("key", 5);
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@CsvSource({
|
||||
"0, true",
|
||||
"4, true",
|
||||
"5, false",
|
||||
"6, false"
|
||||
})
|
||||
void testComparisonValue(Integer value, boolean expectedResult) {
|
||||
Metadata metadata = Metadata.from(Map.of("key", value));
|
||||
assertThat(subject.test(metadata)).isEqualTo(expectedResult);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,71 @@
|
|||
package dev.langchain4j.store.embedding.filter.comparison;
|
||||
|
||||
import dev.langchain4j.data.document.Metadata;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.UUID;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
class IsNotEqualToTest {
|
||||
|
||||
@Test
|
||||
void testIsNotEqualToFilter() {
|
||||
String key = "testKey";
|
||||
String unequalValue = "notEqual";
|
||||
|
||||
IsNotEqualTo subject = new IsNotEqualTo(key, "testValue");
|
||||
|
||||
assertIsNotEqualToObject(subject);
|
||||
assertMetadataDoesNotContainKey(subject);
|
||||
assertValuesAreNumbersNotEqual(key);
|
||||
assertUuidAndStringComparison(key);
|
||||
assertUnequalStringValues(key, unequalValue);
|
||||
assertEqualStringValues(key);
|
||||
}
|
||||
|
||||
private void assertIsNotEqualToObject(IsNotEqualTo subject) {
|
||||
// Testing when object is not an instance of Metadata
|
||||
assertFalse(subject.test(new Object()));
|
||||
}
|
||||
|
||||
private void assertMetadataDoesNotContainKey(IsNotEqualTo subject) {
|
||||
// Testing when Metadata does not contain key
|
||||
assertTrue(subject.test(new Metadata(new HashMap<>())));
|
||||
}
|
||||
|
||||
private void assertValuesAreNumbersNotEqual(String key) {
|
||||
// Testing when actual value is Number
|
||||
Metadata metadata = new Metadata(new HashMap<>());
|
||||
metadata.put(key, 123);
|
||||
IsNotEqualTo subject = new IsNotEqualTo(key, 1234);
|
||||
assertTrue(subject.test(metadata));
|
||||
}
|
||||
|
||||
private void assertUuidAndStringComparison(String key) {
|
||||
// Testing when comparisonValue is instance of UUID and actualValue is instance of String
|
||||
UUID uuid = UUID.randomUUID();
|
||||
IsNotEqualTo subject = new IsNotEqualTo(key, uuid);
|
||||
Metadata metadata = new Metadata(new HashMap<>());
|
||||
metadata.put(key, uuid.toString() + "extra");
|
||||
assertTrue(subject.test(metadata));
|
||||
}
|
||||
|
||||
private void assertUnequalStringValues(String key, String unequalValue) {
|
||||
// Testing when values are not equal
|
||||
IsNotEqualTo subject = new IsNotEqualTo(key, unequalValue);
|
||||
Metadata metadata = new Metadata(new HashMap<>());
|
||||
metadata.put(key, "testValue");
|
||||
assertTrue(subject.test(metadata));
|
||||
}
|
||||
|
||||
private void assertEqualStringValues(String key) {
|
||||
// Testing when values are equal
|
||||
IsNotEqualTo subject = new IsNotEqualTo(key, "testValue");
|
||||
Metadata metadata = new Metadata(new HashMap<>());
|
||||
metadata.put(key, "testValue");
|
||||
assertFalse(subject.test(metadata));
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,75 @@
|
|||
package dev.langchain4j.store.embedding.filter.logical;
|
||||
|
||||
import dev.langchain4j.store.embedding.filter.Filter;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
class AndTest {
|
||||
|
||||
@Mock
|
||||
private Filter mockFilterPasses;
|
||||
|
||||
@Mock
|
||||
private Filter mockFilterFails;
|
||||
|
||||
@Test
|
||||
void testBothFiltersPass() {
|
||||
when(mockFilterPasses.test(any())).thenReturn(true);
|
||||
|
||||
And andFilter = new And(mockFilterPasses, mockFilterPasses);
|
||||
|
||||
assertThat(andFilter.test(new Object())).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testLeftFilterFails() {
|
||||
when(mockFilterFails.test(any())).thenReturn(false);
|
||||
|
||||
And andFilter = new And(mockFilterFails, mockFilterPasses);
|
||||
|
||||
assertThat(andFilter.test(new Object())).isFalse();
|
||||
verifyNoInteractions(mockFilterPasses);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testRightFilterFails() {
|
||||
when(mockFilterPasses.test(any())).thenReturn(true);
|
||||
when(mockFilterFails.test(any())).thenReturn(false);
|
||||
|
||||
And andFilter = new And(mockFilterPasses, mockFilterFails);
|
||||
|
||||
assertThat(andFilter.test(new Object())).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testBothFiltersFail() {
|
||||
when(mockFilterFails.test(any())).thenReturn(false);
|
||||
|
||||
And andFilter = new And(mockFilterFails, mockFilterFails);
|
||||
|
||||
assertThat(andFilter.test(new Object())).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testEqualsAndHashCode() {
|
||||
And andFilter = new And(mockFilterPasses, mockFilterPasses);
|
||||
And sameAndFilter = new And(mockFilterPasses, mockFilterPasses);
|
||||
|
||||
assertThat(andFilter)
|
||||
.isEqualTo(sameAndFilter)
|
||||
.hasSameHashCodeAs(sameAndFilter);
|
||||
}
|
||||
|
||||
@Test
|
||||
void checkToStringImplemented() {
|
||||
And andFilter = new And(mockFilterPasses, mockFilterPasses);
|
||||
|
||||
assertThat(andFilter).hasToString("And(left=mockFilterPasses, right=mockFilterPasses)");
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,78 @@
|
|||
package dev.langchain4j.store.embedding.filter.logical;
|
||||
|
||||
import dev.langchain4j.store.embedding.filter.Filter;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.Mockito;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
class NotTest {
|
||||
|
||||
@Mock
|
||||
private Filter filter;
|
||||
|
||||
private Not subject;
|
||||
|
||||
@BeforeEach
|
||||
void beforeEach() {
|
||||
subject = new Not(filter);
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldReturnFalseWhenFilterReturnsTrue() {
|
||||
Mockito.when(filter.test(any())).thenReturn(true);
|
||||
|
||||
boolean result = subject.test(new Object());
|
||||
|
||||
assertThat(result).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldReturnTrueWhenFilterReturnsFalse() {
|
||||
Mockito.when(filter.test(any())).thenReturn(false);
|
||||
|
||||
boolean result = subject.test(new Object());
|
||||
|
||||
assertThat(result).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldReturnCorrectExpression() {
|
||||
Filter result = subject.expression();
|
||||
|
||||
assertThat(result).isEqualTo(filter);
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldHaveCorrectToStringImplementation() {
|
||||
assertThat(subject).hasToString("Not(expression=" + filter + ")");
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldReturnTrueForEqualsWithSameExpression() {
|
||||
Not anotherWithSameExp = new Not(filter);
|
||||
|
||||
assertThat(subject)
|
||||
.isEqualTo(anotherWithSameExp)
|
||||
.hasSameHashCodeAs(anotherWithSameExp);
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldReturnFalseForEqualsWithDifferentExpression() {
|
||||
Not anotherWithDiffExp = new Not(Mockito.mock(Filter.class));
|
||||
assertThat(subject).isNotEqualTo(anotherWithDiffExp);
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldHaveConsistentHashCode() {
|
||||
int initialHashCode = subject.hashCode();
|
||||
|
||||
assertThat(subject.hashCode()).isEqualTo(initialHashCode);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,74 @@
|
|||
package dev.langchain4j.store.embedding.filter.logical;
|
||||
|
||||
import dev.langchain4j.store.embedding.filter.Filter;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.Mockito;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.Mockito.verifyNoInteractions;
|
||||
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
class OrTest {
|
||||
|
||||
@Mock
|
||||
Filter mockFilterTrue;
|
||||
@Mock
|
||||
Filter mockFilterFalse;
|
||||
|
||||
@Test
|
||||
void testShouldReturnTrueWhenLeftIsTrue() {
|
||||
Mockito.when(mockFilterTrue.test(Mockito.any())).thenReturn(true);
|
||||
|
||||
Or or = new Or(mockFilterTrue, mockFilterFalse);
|
||||
|
||||
assertThat(or.test(new Object())).isTrue();
|
||||
verifyNoInteractions(mockFilterFalse);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testShouldReturnTrueWhenRightIsTrue() {
|
||||
Mockito.when(mockFilterTrue.test(Mockito.any())).thenReturn(true);
|
||||
Mockito.when(mockFilterFalse.test(Mockito.any())).thenReturn(false);
|
||||
|
||||
Or or = new Or(mockFilterFalse, mockFilterTrue);
|
||||
|
||||
assertThat(or.test(new Object())).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testShouldReturnFalseWhenBothAreFalse() {
|
||||
Mockito.when(mockFilterFalse.test(Mockito.any())).thenReturn(false);
|
||||
|
||||
Or or = new Or(mockFilterFalse, mockFilterFalse);
|
||||
|
||||
assertThat(or.test(new Object())).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testEqualsMethodShouldReturnTrueForNonDistinctObjects() {
|
||||
Or or1 = new Or(mockFilterTrue, mockFilterFalse);
|
||||
Or or2 = new Or(mockFilterTrue, mockFilterFalse);
|
||||
|
||||
assertThat(or1).isEqualTo(or2);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testEqualsAndHashCode() {
|
||||
Or orFilter = new Or(mockFilterTrue, mockFilterFalse);
|
||||
Or sameOrFilter = new Or(mockFilterTrue, mockFilterFalse);
|
||||
|
||||
assertThat(orFilter)
|
||||
.isEqualTo(sameOrFilter)
|
||||
.hasSameHashCodeAs(sameOrFilter);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testToStringMethodShouldReturnExpectedValue() {
|
||||
Or or = new Or(mockFilterTrue, mockFilterFalse);
|
||||
|
||||
assertThat(or).hasToString("Or(left=" + mockFilterTrue + ", right=" + mockFilterFalse + ")");
|
||||
}
|
||||
}
|
||||
|
|
@ -18,6 +18,7 @@
|
|||
<maven.compiler.release>${java.version}</maven.compiler.release>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
<project.build.outputTimestamp>1714382357</project.build.outputTimestamp>
|
||||
<argLine></argLine>
|
||||
<!-- Dependency Versions -->
|
||||
<openai4j.version>0.23.0</openai4j.version>
|
||||
<azure-ai-openai.version>1.0.0-beta.11</azure-ai-openai.version>
|
||||
|
|
@ -33,7 +34,7 @@
|
|||
<gson.version>2.10.1</gson.version>
|
||||
<junit.version>5.11.2</junit.version>
|
||||
<testcontainers.version>1.20.2</testcontainers.version>
|
||||
<bytebuddy.version>1.14.10</bytebuddy.version>
|
||||
<bytebuddy.version>1.15.7</bytebuddy.version>
|
||||
<mockito.version>5.14.1</mockito.version>
|
||||
<assertj.version>3.24.2</assertj.version>
|
||||
<tinylog.version>2.6.2</tinylog.version>
|
||||
|
|
@ -436,6 +437,14 @@
|
|||
</dependencies>
|
||||
</dependencyManagement>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.mockito</groupId>
|
||||
<artifactId>mockito-core</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<pluginManagement>
|
||||
<plugins>
|
||||
|
|
@ -493,6 +502,12 @@
|
|||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-dependency-plugin</artifactId>
|
||||
<executions>
|
||||
<execution>
|
||||
<goals>
|
||||
<goal>properties</goal>
|
||||
</goals>
|
||||
|
||||
</execution>
|
||||
<execution>
|
||||
<id>detect-unused-dependencies</id>
|
||||
<goals>
|
||||
|
|
@ -517,6 +532,7 @@
|
|||
<artifactId>maven-surefire-plugin</artifactId>
|
||||
<version>3.3.1</version>
|
||||
<configuration combine.children="append">
|
||||
<argLine>@{argLine} -javaagent:${org.mockito:mockito-core:jar}</argLine>
|
||||
<systemPropertyVariables>
|
||||
<tinylog.level>info</tinylog.level>
|
||||
</systemPropertyVariables>
|
||||
|
|
|
|||
Loading…
Reference in New Issue