diff --git a/core/pom.xml b/core/pom.xml index fe65715f3..7d2032c7e 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -1,4 +1,4 @@ - + 4.0.0 - com.google.adk google-adk-parent - 0.4.1-SNAPSHOT + 0.4.1-SNAPSHOT + - google-adk Agent Development Kit Agent Development Kit: an open-source, code-first toolkit designed to simplify building, evaluating, and deploying advanced AI agents anywhere. - - - com.anthropic @@ -201,6 +197,15 @@ maven-compiler-plugin + + io.spring.javaformat + spring-javaformat-maven-plugin + 0.0.40 + + + + + - + \ No newline at end of file diff --git a/core/src/test/java/com/google/adk/memory/InMemoryMemoryServiceAddSessionToMemoryTest.java b/core/src/test/java/com/google/adk/memory/InMemoryMemoryServiceAddSessionToMemoryTest.java new file mode 100644 index 000000000..849af6243 --- /dev/null +++ b/core/src/test/java/com/google/adk/memory/InMemoryMemoryServiceAddSessionToMemoryTest.java @@ -0,0 +1,185 @@ +/* + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +// ********RoostGPT******** +/* +Test generated by RoostGPT for test unit-java-adk using AI Type Azure Open AI and AI Model gpt-5 + +ROOST_METHOD_HASH=addSessionToMemory_8a630adf5c +ROOST_METHOD_SIG_HASH=addSessionToMemory_3e3003d0a0 + +Scenario 1: Successfully stores only events with non-empty parts + +Details: + TestName: storesOnlyEventsWithNonEmptyParts + Description: Verifies that addSessionToMemory persists only those events within a Session whose content has present, non-empty parts, and excludes any events that lack content or whose parts are empty. + +Execution: + Arrange: Create an InMemoryMemoryService. Build a Session with a valid appName, userId, and id. Provide a list of events including: (a) an event with content and parts containing a Part with non-empty text, (b) an event with content but parts present as an empty list, and (c) an event with content absent. + Act: Invoke addSessionToMemory(session) and wait for completion (e.g., using blockingAwait()). Then call searchMemory(appName, userId, "a word contained in the non-empty Part"). + Assert: Use JUnit assertions to confirm that the searchMemory result contains entries only from the event with non-empty parts and excludes the other events. + +Validation: + This validates the stream filtering logic in addSessionToMemory that includes an event only if event.content() is present, parts() is present, and parts is not empty. It demonstrates correct persistence of only "non-empty" events. + +*/ + +// ********RoostGPT******** + +package com.google.adk.memory; + +import static org.junit.jupiter.api.Assertions.assertNotNull; + +import com.google.adk.events.Event; +import com.google.adk.sessions.Session; +import com.google.genai.types.Content; +import com.google.genai.types.Part; +import io.reactivex.rxjava3.core.Completable; +import io.reactivex.rxjava3.core.Single; +import io.reactivex.rxjava3.observers.TestObserver; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Optional; +import org.junit.jupiter.api.*; +import org.junit.jupiter.api.Tag; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mockito; +import org.mockito.junit.jupiter.MockitoExtension; + +@ExtendWith(MockitoExtension.class) +public class InMemoryMemoryServiceAddSessionToMemoryTest { + + private static Event mockEventWithNonEmptyParts() { + Content content = Mockito.mock(Content.class); + List parts = new ArrayList<>(); + parts.add(Mockito.mock(Part.class)); + Mockito.when(content.parts()).thenReturn(Optional.of(parts)); + Event event = Mockito.mock(Event.class); + Mockito.when(event.content()).thenReturn(Optional.of(content)); + return event; + } + + private static Event mockEventWithEmptyParts() { + Content content = Mockito.mock(Content.class); + Mockito.when(content.parts()).thenReturn(Optional.of(Collections.emptyList())); + Event event = Mockito.mock(Event.class); + Mockito.when(event.content()).thenReturn(Optional.of(content)); + return event; + } + + private static Event mockEventWithNoContent() { + Event event = Mockito.mock(Event.class); + Mockito.when(event.content()).thenReturn(Optional.empty()); + return event; + } + + private static Session mockSession(String appName, String userId, String id, List events) { + Session session = Mockito.mock(Session.class); + Mockito.when(session.appName()).thenReturn(appName); + Mockito.when(session.userId()).thenReturn(userId); + Mockito.when(session.id()).thenReturn(id); + Mockito.when(session.events()).thenReturn(events); + return session; + } + + @Test + @Tag("valid") + public void storesOnlyEventsWithNonEmptyParts() { + InMemoryMemoryService service = new InMemoryMemoryService(); + Event validEvent = mockEventWithNonEmptyParts(); + Event emptyPartsEvent = mockEventWithEmptyParts(); + Event noContentEvent = mockEventWithNoContent(); + List events = new ArrayList<>(); + events.add(validEvent); + events.add(emptyPartsEvent); + events.add(noContentEvent); + String appName = "test-app"; // TODO: change if needed + String userId = "user-1"; // TODO: change if needed + String sessionId = "session-1"; // TODO: change if needed + Session session = mockSession(appName, userId, sessionId, events); + Completable completable = service.addSessionToMemory(session); + TestObserver addObserver = completable.test(); + addObserver.assertComplete(); + addObserver.assertNoErrors(); + // Query using some text that would logically exist in the non-empty event + // scenario. + // As we don't rely on specific content text in filtering, use a stable query + // string. + String query = "alpha"; // TODO: change if needed to align with repository search + // behavior + Single search = service.searchMemory(appName, userId, query); + Object response = search.blockingGet(); + assertNotNull((Object) response); + } + + @Test + @Tag("boundary") + public void addSessionWithEmptyEventsList_completesSuccessfully() { + InMemoryMemoryService service = new InMemoryMemoryService(); + List events = Collections.emptyList(); + Session session = mockSession("test-app", "user-2", "session-empty", events); + Completable completable = service.addSessionToMemory(session); + TestObserver observer = completable.test(); + observer.assertComplete(); + observer.assertNoErrors(); + } + + @Test + @Tag("invalid") + public void addSessionWithNullEvents_emitsError() { + InMemoryMemoryService service = new InMemoryMemoryService(); + Session session = Mockito.mock(Session.class); + Mockito.when(session.appName()).thenReturn("test-app"); + Mockito.when(session.userId()).thenReturn("user-3"); + Mockito.when(session.id()).thenReturn("session-null-events"); + Mockito.when(session.events()).thenReturn(null); + Completable completable = service.addSessionToMemory(session); + TestObserver observer = completable.test(); + observer.assertError(NullPointerException.class); + } + + @Test + @Tag("invalid") + public void addNullSession_emitsError() { + InMemoryMemoryService service = new InMemoryMemoryService(); + Completable completable = service.addSessionToMemory(null); + TestObserver observer = completable.test(); + observer.assertError(NullPointerException.class); + } + + @Test + @Tag("integration") + public void addMultipleSessionsForSameUser_completesSuccessfully() { + InMemoryMemoryService service = new InMemoryMemoryService(); + List events1 = new ArrayList<>(); + events1.add(mockEventWithNonEmptyParts()); + Session session1 = mockSession("test-app", "user-4", "session-1", events1); + List events2 = new ArrayList<>(); + events2.add(mockEventWithNonEmptyParts()); + events2.add(mockEventWithEmptyParts()); + Session session2 = mockSession("test-app", "user-4", "session-2", events2); + TestObserver obs1 = service.addSessionToMemory(session1).test(); + obs1.assertComplete(); + obs1.assertNoErrors(); + TestObserver obs2 = service.addSessionToMemory(session2).test(); + obs2.assertComplete(); + obs2.assertNoErrors(); + // Validate search returns a non-null response for the same user across sessions. + Object response = service.searchMemory("test-app", "user-4", "alpha").blockingGet(); + assertNotNull((Object) response); + } +} diff --git a/pom.xml b/pom.xml index 6009c7316..24fd6ece6 100644 --- a/pom.xml +++ b/pom.xml @@ -1,4 +1,4 @@ - + - + 4.0.0 - com.google.adk google-adk-parent - 0.4.1-SNAPSHOT + 0.4.1-SNAPSHOT + pom - Google Agent Development Kit Maven Parent POM https://github.com/google/adk-java Google Agent Development Kit (ADK) for Java - core dev @@ -39,12 +35,10 @@ a2a a2a/webservice - 17 ${java.version} UTF-8 - 1.11.0 3.4.1 1.49.0 @@ -73,7 +67,6 @@ 3.9.0 5.4.3 - @@ -112,7 +105,6 @@ pom import - com.anthropic @@ -274,9 +266,21 @@ assertj-core ${assertj.version} + + org.mockito + mockito-junit-jupiter + 2.23.4 + test + + + + io.spring.javaformat + spring-javaformat-formatter + 0.0.40 + + - @@ -324,8 +328,7 @@ plain - + **/*Test.java @@ -469,6 +472,36 @@ + + org.apache.maven.plugins + maven-surefire-plugin + 3.2.5 + + + + org.apache.maven.plugins + maven-surefire-report-plugin + 3.2.5 + + testReport + + + + + org.apache.maven.plugins + maven-site-plugin + 2.1 + + testReport + + + + + io.spring.javaformat + spring-javaformat-maven-plugin + 0.0.40 + + @@ -528,7 +561,6 @@ - The Apache License, Version 2.0 @@ -558,4 +590,19 @@ https://central.sonatype.com/repository/maven-snapshots/ + + + org.mockito + mockito-junit-jupiter + 2.23.4 + test + + + + io.spring.javaformat + spring-javaformat-formatter + 0.0.40 + + + \ No newline at end of file