Skip to content

Commit 2dceb35

Browse files
committed
test(harness): add regression test for memory consolidation glob
1 parent 28ef28a commit 2dceb35

1 file changed

Lines changed: 40 additions & 27 deletions

File tree

agentscope-harness/src/test/java/io/agentscope/harness/agent/memory/MemoryConsolidatorFilesystemTest.java

Lines changed: 40 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,15 @@
1818
import static org.junit.jupiter.api.Assertions.assertEquals;
1919
import static org.junit.jupiter.api.Assertions.assertFalse;
2020
import static org.junit.jupiter.api.Assertions.assertTrue;
21-
21+
import static org.mockito.ArgumentMatchers.any;
22+
import static org.mockito.ArgumentMatchers.anyList;
23+
import static org.mockito.Mockito.mock;
24+
import static org.mockito.Mockito.when;
25+
26+
import io.agentscope.core.message.TextBlock;
27+
import io.agentscope.core.model.ChatResponse;
28+
import io.agentscope.core.model.Model;
29+
import io.agentscope.harness.agent.filesystem.local.LocalFilesystem;
2230
import io.agentscope.harness.agent.filesystem.remote.RemoteFilesystem;
2331
import io.agentscope.harness.agent.store.InMemoryStore;
2432
import io.agentscope.harness.agent.workspace.WorkspaceManager;
@@ -29,11 +37,11 @@
2937
import java.util.Map;
3038
import org.junit.jupiter.api.Test;
3139
import org.junit.jupiter.api.io.TempDir;
40+
import reactor.core.publisher.Flux;
3241

3342
/**
3443
* Verifies that {@link MemoryConsolidator} reads daily ledgers and writes watermark / MEMORY.md
35-
* entirely through {@link io.agentscope.harness.agent.filesystem.AbstractFilesystem}, making it
36-
* backend-agnostic.
44+
* through the filesystem layer.
3745
*/
3846
class MemoryConsolidatorFilesystemTest {
3947

@@ -50,10 +58,6 @@ private static void seedStoreFile(
5058
store.put(ns, path, value);
5159
}
5260

53-
// ======================================================================
54-
// readWatermark: returns EPOCH when state file absent
55-
// ======================================================================
56-
5761
@Test
5862
void readWatermark_returnsEpochWhenStateAbsent(@TempDir Path tmp) {
5963
InMemoryStore store = new InMemoryStore();
@@ -66,10 +70,6 @@ void readWatermark_returnsEpochWhenStateAbsent(@TempDir Path tmp) {
6670
assertEquals(Instant.EPOCH, consolidator.readWatermark());
6771
}
6872

69-
// ======================================================================
70-
// readWatermark / writeWatermark round-trip through filesystem
71-
// ======================================================================
72-
7373
@Test
7474
void watermark_roundTripThroughFilesystem(@TempDir Path tmp) {
7575
InMemoryStore store = new InMemoryStore();
@@ -85,10 +85,6 @@ void watermark_roundTripThroughFilesystem(@TempDir Path tmp) {
8585
assertEquals(ts, consolidator.readWatermark());
8686
}
8787

88-
// ======================================================================
89-
// readWatermark: no local file is touched — only the filesystem
90-
// ======================================================================
91-
9288
@Test
9389
void watermark_doesNotCreateLocalFile(@TempDir Path tmp) {
9490
InMemoryStore store = new InMemoryStore();
@@ -101,48 +97,65 @@ void watermark_doesNotCreateLocalFile(@TempDir Path tmp) {
10197
Instant ts = Instant.now();
10298
wsm.writeUtf8WorkspaceRelative(MemoryConsolidator.STATE_REL_PATH, ts.toString());
10399

104-
// local disk must NOT have the state file — it lives only in the store
105100
Path localState = tmp.resolve("memory").resolve(MemoryConsolidator.STATE_FILE);
106101
assertFalse(
107102
Files.exists(localState),
108103
"state file should not be written to local disk when using RemoteFilesystem");
109104

110-
// but consolidator reads it correctly from the store
111105
assertEquals(ts, consolidator.readWatermark());
112106
}
113107

114-
// ======================================================================
115-
// STATE_FILE constant is preserved
116-
// ======================================================================
117-
118108
@Test
119109
void stateFileRelPath_matchesConstant() {
120110
assertEquals("memory/" + MemoryConsolidator.STATE_FILE, MemoryConsolidator.STATE_REL_PATH);
121111
}
122112

123-
// ======================================================================
124-
// Local filesystem (no store) — watermark uses local disk via WorkspaceManager
125-
// ======================================================================
113+
@Test
114+
void consolidate_readsRootDailyLedgerAndWritesMemoryMd(@TempDir Path tmp) throws Exception {
115+
LocalFilesystem fs = new LocalFilesystem(tmp);
116+
WorkspaceManager wsm = new WorkspaceManager(tmp, fs);
117+
118+
Path memoryDir = Files.createDirectories(tmp.resolve("memory"));
119+
Files.writeString(memoryDir.resolve("2026-05-20.md"), "root daily entry");
120+
121+
MemoryConsolidator consolidator = new MemoryConsolidator(wsm, stubModel("updated memory"));
122+
123+
consolidator.consolidate().block();
124+
125+
assertEquals("updated memory", wsm.readMemoryMd());
126+
assertTrue(consolidator.readWatermark().isAfter(Instant.EPOCH));
127+
}
126128

127129
@Test
128130
void watermark_localFallback_whenNoFilesystem(@TempDir Path tmp) throws Exception {
129131
WorkspaceManager wsm = new WorkspaceManager(tmp);
130132

131133
MemoryConsolidator consolidator = new MemoryConsolidator(wsm, null);
132134

133-
// No file → EPOCH
134135
assertEquals(Instant.EPOCH, consolidator.readWatermark());
135136

136-
// Write via WorkspaceManager (falls to local disk)
137137
Instant ts = Instant.parse("2025-03-10T09:00:00Z");
138138
wsm.writeUtf8WorkspaceRelative(MemoryConsolidator.STATE_REL_PATH, ts.toString());
139139

140140
assertEquals(ts, consolidator.readWatermark());
141141

142-
// Verify the local file actually exists
143142
Path localState = tmp.resolve("memory").resolve(MemoryConsolidator.STATE_FILE);
144143
assertTrue(
145144
Files.exists(localState),
146145
"state file should be written to local disk when no filesystem is configured");
147146
}
147+
148+
private static Model stubModel(String assistantText) {
149+
Model model = mock(Model.class);
150+
when(model.getModelName()).thenReturn("stub-model");
151+
ChatResponse chunk =
152+
new ChatResponse(
153+
"stub-id",
154+
List.of(TextBlock.builder().text(assistantText).build()),
155+
null,
156+
Map.of(),
157+
"stop");
158+
when(model.stream(anyList(), any(), any())).thenReturn(Flux.just(chunk));
159+
return model;
160+
}
148161
}

0 commit comments

Comments
 (0)