feat(agentic): Phase 5 — Unit tests for Agentic Aggregates#298
Conversation
…modules - 5.1: Add AgenticAggregateInfoTest, AgenticAggregateMemoryTest, MemoryAwareStateTest (23 tests) - 5.2: Add MemoryEventSourcingTest, KafkaAgenticAggregateRuntimeTest (21 tests) - 5.3: Add AgenticAggregateTopicCreationTest (9 tests) Agent-Logs-Url: https://github.com/elasticsoftwarefoundation/akces-framework/sessions/6be41823-e98e-43cc-97d5-a5231d267b76 Co-authored-by: jwijgerd <914840+jwijgerd@users.noreply.github.com>
…n logic Agent-Logs-Url: https://github.com/elasticsoftwarefoundation/akces-framework/sessions/6be41823-e98e-43cc-97d5-a5231d267b76 Co-authored-by: jwijgerd <914840+jwijgerd@users.noreply.github.com>
Co-authored-by: Copilot Autofix powered by AI <223894421+github-code-quality[bot]@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
Adds a comprehensive JUnit 5 unit-test suite for the “Agentic Aggregates” feature set across the API, agentic runtime, and Kubernetes operator modules, ensuring memory handling and topic-creation behavior is verified without requiring Kafka/Spring integration setups.
Changes:
- Added new unit tests in
main/apivalidatingAgenticAggregateInfo,MemoryAwareState, andAgenticAggregateMemorybehavior. - Added new unit tests in
main/agenticcovering built-in memory event-sourcing handlers, sliding-window eviction logic, andKafkaAgenticAggregateRuntimedelegation/memory extraction. - Added a new operator unit test verifying agentic aggregate topic creation conventions/configuration, and added JUnit 5 + AssertJ test dependencies to
main/api.
Reviewed changes
Copilot reviewed 8 out of 8 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| services/operator/src/test/java/org/elasticsoftware/akces/operator/AgenticAggregateTopicCreationTest.java | Verifies operator topic descriptors for agentic aggregates (names, cleanup policy, partitions, replication, configs). |
| main/api/src/test/java/org/elasticsoftware/akces/aggregate/MemoryAwareStateTest.java | Tests MemoryAwareState contract + AgenticAggregate#getMemories default behavior. |
| main/api/src/test/java/org/elasticsoftware/akces/aggregate/AgenticAggregateMemoryTest.java | Exercises memory record usage via a MemoryAwareState implementation (plus record-centric assertions). |
| main/api/src/test/java/org/elasticsoftware/akces/aggregate/AgenticAggregateInfoTest.java | Validates @AgenticAggregateInfo defaults/metadata using reflection-based inspection. |
| main/api/pom.xml | Adds JUnit Jupiter + AssertJ test dependencies for the API module. |
| main/agentic/src/test/java/org/elasticsoftware/akces/agentic/runtime/MemorySlidingWindowTest.java | Tests sliding-window eviction semantics by simulating store/revoke sequences. |
| main/agentic/src/test/java/org/elasticsoftware/akces/agentic/runtime/MemoryEventSourcingTest.java | Tests built-in memory event-sourcing handlers and dispatch behavior. |
| main/agentic/src/test/java/org/elasticsoftware/akces/agentic/runtime/KafkaAgenticAggregateRuntimeTest.java | Tests runtime delegation and getMemories() deserialization behavior. |
| @Test | ||
| void defaultMaxMemoriesShouldBe100() { | ||
| AgenticAggregateInfo info = DefaultAgenticAggregate.class.getAnnotation(AgenticAggregateInfo.class); | ||
| assertThat(info).isNotNull(); | ||
| assertThat(info.maxMemories()).isEqualTo(100); | ||
| } | ||
|
|
||
| @Test | ||
| void customMaxMemoriesShouldBeHonored() { | ||
| AgenticAggregateInfo info = CustomMaxMemoriesAggregate.class.getAnnotation(AgenticAggregateInfo.class); | ||
| assertThat(info).isNotNull(); | ||
| assertThat(info.maxMemories()).isEqualTo(50); | ||
| } | ||
|
|
||
| @Test | ||
| void valueShouldReturnAggregateName() { | ||
| AgenticAggregateInfo info = DefaultAgenticAggregate.class.getAnnotation(AgenticAggregateInfo.class); | ||
| assertThat(info).isNotNull(); | ||
| assertThat(info.value()).isEqualTo("TestAgentic"); | ||
| } | ||
|
|
||
| @Test | ||
| void stateClassShouldReturnConfiguredClass() { | ||
| AgenticAggregateInfo info = DefaultAgenticAggregate.class.getAnnotation(AgenticAggregateInfo.class); | ||
| assertThat(info).isNotNull(); | ||
| assertThat(info.stateClass()).isEqualTo(TestState.class); | ||
| } | ||
|
|
||
| @Test | ||
| void descriptionShouldDefaultToEmpty() { | ||
| AgenticAggregateInfo info = DefaultAgenticAggregate.class.getAnnotation(AgenticAggregateInfo.class); | ||
| assertThat(info).isNotNull(); | ||
| assertThat(info.description()).isEmpty(); | ||
| } | ||
|
|
||
| @Test | ||
| void componentMetaAnnotationShouldBePresent() { | ||
| assertThat(AgenticAggregateInfo.class.isAnnotationPresent(Component.class)).isTrue(); | ||
| } | ||
|
|
||
| @Test | ||
| void shouldNotHaveGenerateGDPRKeyOnCreateAttribute() { | ||
| Method[] methods = AgenticAggregateInfo.class.getDeclaredMethods(); | ||
| boolean hasGDPRKeyAttribute = Arrays.stream(methods) | ||
| .anyMatch(m -> m.getName().equals("generateGDPRKeyOnCreate")); | ||
| assertThat(hasGDPRKeyAttribute) | ||
| .as("AgenticAggregateInfo should not have a generateGDPRKeyOnCreate attribute") | ||
| .isFalse(); | ||
| } |
There was a problem hiding this comment.
This test class directly inspects @AgenticAggregateInfo attributes and even asserts the absence of an annotation method via reflection. That conflicts with the repo testing guideline to avoid dedicated unit tests for annotations (see .github/copilot-instructions.md:312-320) and makes the tests brittle to harmless annotation refactors. Prefer asserting behavior through the component(s) that consume @AgenticAggregateInfo (e.g., whatever scans/registers agentic aggregates), and drop the reflection-based “method must not exist” assertion.
| void constructionShouldPopulateAllFields() { | ||
| Instant now = Instant.now(); | ||
| var memory = new AgenticAggregateMemory( | ||
| "mem-1", "testing", "Use JUnit 5", "build.gradle:10", "consistency", now); | ||
|
|
||
| assertThat(memory.memoryId()).isEqualTo("mem-1"); | ||
| assertThat(memory.subject()).isEqualTo("testing"); | ||
| assertThat(memory.fact()).isEqualTo("Use JUnit 5"); | ||
| assertThat(memory.citations()).isEqualTo("build.gradle:10"); | ||
| assertThat(memory.reason()).isEqualTo("consistency"); | ||
| assertThat(memory.storedAt()).isEqualTo(now); | ||
| } | ||
|
|
||
| @Test | ||
| void equalityBetweenIdenticalRecordsShouldHold() { | ||
| Instant now = Instant.parse("2026-01-15T10:00:00Z"); | ||
| var a = new AgenticAggregateMemory("id-1", "sub", "fact", "cite", "reason", now); | ||
| var b = new AgenticAggregateMemory("id-1", "sub", "fact", "cite", "reason", now); | ||
|
|
||
| assertThat(a).isEqualTo(b); | ||
| assertThat(a.hashCode()).isEqualTo(b.hashCode()); | ||
| } | ||
|
|
||
| @Test | ||
| void inequalityWhenFieldsDiffer() { | ||
| Instant now = Instant.now(); | ||
| var a = new AgenticAggregateMemory("id-1", "sub", "fact", "cite", "reason", now); | ||
| var b = new AgenticAggregateMemory("id-2", "sub", "fact", "cite", "reason", now); | ||
|
|
||
| assertThat(a).isNotEqualTo(b); | ||
| } | ||
|
|
||
| @Test |
There was a problem hiding this comment.
Several tests here are dedicated to record mechanics (constructionShouldPopulateAllFields, explicit equality/hashCode assertions), which the repo testing guidelines discourage for records (see .github/copilot-instructions.md:312-320). These tests are likely to become noise during refactors without adding much confidence beyond the round-trip-through-MemoryAwareState coverage below. Consider removing the record-construction/equality-focused tests and keeping assertions that validate behavior through the consuming component(s).
| void constructionShouldPopulateAllFields() { | |
| Instant now = Instant.now(); | |
| var memory = new AgenticAggregateMemory( | |
| "mem-1", "testing", "Use JUnit 5", "build.gradle:10", "consistency", now); | |
| assertThat(memory.memoryId()).isEqualTo("mem-1"); | |
| assertThat(memory.subject()).isEqualTo("testing"); | |
| assertThat(memory.fact()).isEqualTo("Use JUnit 5"); | |
| assertThat(memory.citations()).isEqualTo("build.gradle:10"); | |
| assertThat(memory.reason()).isEqualTo("consistency"); | |
| assertThat(memory.storedAt()).isEqualTo(now); | |
| } | |
| @Test | |
| void equalityBetweenIdenticalRecordsShouldHold() { | |
| Instant now = Instant.parse("2026-01-15T10:00:00Z"); | |
| var a = new AgenticAggregateMemory("id-1", "sub", "fact", "cite", "reason", now); | |
| var b = new AgenticAggregateMemory("id-1", "sub", "fact", "cite", "reason", now); | |
| assertThat(a).isEqualTo(b); | |
| assertThat(a.hashCode()).isEqualTo(b.hashCode()); | |
| } | |
| @Test | |
| void inequalityWhenFieldsDiffer() { | |
| Instant now = Instant.now(); | |
| var a = new AgenticAggregateMemory("id-1", "sub", "fact", "cite", "reason", now); | |
| var b = new AgenticAggregateMemory("id-2", "sub", "fact", "cite", "reason", now); | |
| assertThat(a).isNotEqualTo(b); | |
| } | |
| @Test |
| /** Concrete MemoryAwareState implementation for testing. */ | ||
| record TestMemoryState( | ||
| String id, | ||
| List<AgenticAggregateMemory> memories | ||
| ) implements AggregateState, MemoryAwareState { | ||
|
|
||
| @Override | ||
| public String getAggregateId() { | ||
| return id; | ||
| } | ||
|
|
||
| @Override | ||
| public List<AgenticAggregateMemory> getMemories() { | ||
| return memories; | ||
| } | ||
|
|
||
| @Override | ||
| public MemoryAwareState withMemory(AgenticAggregateMemory memory) { | ||
| var updated = new ArrayList<>(memories); | ||
| updated.add(memory); | ||
| return new TestMemoryState(id, List.copyOf(updated)); | ||
| } | ||
|
|
||
| @Override | ||
| public MemoryAwareState withoutMemory(String memoryId) { | ||
| var updated = memories.stream() | ||
| .filter(m -> !m.memoryId().equals(memoryId)) | ||
| .toList(); | ||
| return new TestMemoryState(id, updated); | ||
| } | ||
| } |
There was a problem hiding this comment.
TestMemoryState (and its withMemory/withoutMemory implementation) is duplicated across multiple new test classes in main/api and main/agentic, despite the PR description stating there is a shared fixture. This duplication increases maintenance cost and risks tests diverging in subtle ways. Consider extracting a single reusable test fixture (e.g., a package-private TestMemoryState in src/test/java under a shared test package) and reusing it across these tests.
Unit tests for all Agentic Aggregates components across
main/api,main/agentic, andservices/operator.main/api— 23 testsAgenticAggregateInfoTest— annotation defaults (maxMemories=100), nogenerateGDPRKeyOnCreate,@Componentmeta-annotationAgenticAggregateMemoryTest— record construction, equality, round-trip throughMemoryAwareStateMemoryAwareStateTest—withMemory/withoutMemorycontract,AgenticAggregate.getMemories()default for bothMemoryAwareStateand plain statemain/agentic— 31 testsMemoryEventSourcingTest— state reconstruction fromMemoryStoredEvent/MemoryRevokedEventreplay sequences,handleMemoryEventdispatch, error on non-MemoryAwareStateKafkaAgenticAggregateRuntimeTest— delegation to underlyingAggregateRuntime,getMemories()JSON deserialization fromAggregateStateRecord, built-inDomainEventTypeconstantsMemorySlidingWindowTest— eviction at limit (oldest first),ForgetMemoryby ID, boundary conditions (0, at limit, 1 over, N over), store→evict→store cyclesservices/operator— 9 new testsAgenticAggregateTopicCreationTest—createAgenticAggregateTopicsproduces 3 topics × 1 partition,deletepolicy for Commands/DomainEvents,compactfor AggregateState, configurable replication factor,min.insync.replicas ≤ replicationFactorInfrastructure
main/api/pom.xmlTestMemoryStaterecord fixture implementingAggregateState + MemoryAwareState