]> git.giacomolli.uk - onechat/blobdiff - memory_test.go
Show an input prompt while the agent is idle
[onechat] / memory_test.go
index 001f852293d427e053e5f6e964f6e741147cf0be..202d4da3648b5e30b02de9e6fd3fd43d470c423e 100644 (file)
@@ -1,6 +1,7 @@
 package main
 
 import (
 package main
 
 import (
+       "context"
        "fmt"
        "maps"
        "os"
        "fmt"
        "maps"
        "os"
@@ -16,11 +17,12 @@ import (
 func TestMemoryRestartLoadsTheSameView(t *testing.T) {
        root := t.TempDir()
        clock := newTestClock()
 func TestMemoryRestartLoadsTheSameView(t *testing.T) {
        root := t.TempDir()
        clock := newTestClock()
-       memory, err := openMemory(t.Context(), root, clock.Now, newFakeSummarizer())
+       memory, err := openMemory(t.Context(), root, clock.Now, newFakeSummarizer(), discardLog())
        require.NoError(t, err)
 
        memory.chat.high = 200
        memory.chat.low = 100
        require.NoError(t, err)
 
        memory.chat.high = 200
        memory.chat.low = 100
+
        memory.cview.high = 80
        memory.cview.low = 40
        for n := range 100 {
        memory.cview.high = 80
        memory.cview.low = 40
        for n := range 100 {
@@ -35,7 +37,7 @@ func TestMemoryRestartLoadsTheSameView(t *testing.T) {
        require.Less(t, len(memory.chat.lines), 100)
        require.NoError(t, memory.Close())
 
        require.Less(t, len(memory.chat.lines), 100)
        require.NoError(t, memory.Close())
 
-       memory, err = openMemory(t.Context(), root, clock.Now, newFakeSummarizer())
+       memory, err = openMemory(t.Context(), root, clock.Now, newFakeSummarizer(), discardLog())
        require.NoError(t, err)
 
        defer func() {
        require.NoError(t, err)
 
        defer func() {
@@ -53,7 +55,7 @@ func TestMemoryRestartLoadsTheSameView(t *testing.T) {
 func TestMemoryOpensAfterACrashBeforeTheViewSave(t *testing.T) {
        root := t.TempDir()
        clock := newTestClock()
 func TestMemoryOpensAfterACrashBeforeTheViewSave(t *testing.T) {
        root := t.TempDir()
        clock := newTestClock()
-       memory, err := openMemory(t.Context(), root, clock.Now, newFakeSummarizer())
+       memory, err := openMemory(t.Context(), root, clock.Now, newFakeSummarizer(), discardLog())
        require.NoError(t, err)
 
        _, err = memory.Append("user", "first")
        require.NoError(t, err)
 
        _, err = memory.Append("user", "first")
@@ -71,14 +73,14 @@ func TestMemoryOpensAfterACrashBeforeTheViewSave(t *testing.T) {
        // The log and the tree hold message 1, but the view does not.
        require.NoError(t, os.WriteFile(filepath.Join(root, "view.json"), saved, 0o600))
 
        // The log and the tree hold message 1, but the view does not.
        require.NoError(t, os.WriteFile(filepath.Join(root, "view.json"), saved, 0o600))
 
-       memory, err = openMemory(t.Context(), root, clock.Now, newFakeSummarizer())
+       memory, err = openMemory(t.Context(), root, clock.Now, newFakeSummarizer(), discardLog())
        require.NoError(t, err)
        require.Equal(t, view, memory.View())
        require.NoError(t, memory.Close())
 }
 
 func TestMemoryKeepsTheCompactionViewCoarser(t *testing.T) {
        require.NoError(t, err)
        require.Equal(t, view, memory.View())
        require.NoError(t, memory.Close())
 }
 
 func TestMemoryKeepsTheCompactionViewCoarser(t *testing.T) {
-       memory, err := openMemory(t.Context(), t.TempDir(), newTestClock().Now, newFakeSummarizer())
+       memory, err := openMemory(t.Context(), t.TempDir(), newTestClock().Now, newFakeSummarizer(), discardLog())
        require.NoError(t, err)
 
        defer func() {
        require.NoError(t, err)
 
        defer func() {
@@ -87,6 +89,7 @@ func TestMemoryKeepsTheCompactionViewCoarser(t *testing.T) {
 
        memory.chat.high = 200
        memory.chat.low = 100
 
        memory.chat.high = 200
        memory.chat.low = 100
+
        memory.cview.high = 80
        memory.cview.low = 40
        for n := range 64 {
        memory.cview.high = 80
        memory.cview.low = 40
        for n := range 64 {
@@ -109,7 +112,7 @@ func TestMemoryKeepsTheCompactionViewCoarser(t *testing.T) {
 func TestMemoryRunsAtMostEightBuilds(t *testing.T) {
        summarizer := newFakeSummarizer()
        summarizer.gate = make(chan struct{})
 func TestMemoryRunsAtMostEightBuilds(t *testing.T) {
        summarizer := newFakeSummarizer()
        summarizer.gate = make(chan struct{})
-       memory, err := openMemory(t.Context(), t.TempDir(), newTestClock().Now, summarizer)
+       memory, err := openMemory(t.Context(), t.TempDir(), newTestClock().Now, summarizer, discardLog())
        require.NoError(t, err)
 
        defer func() {
        require.NoError(t, err)
 
        defer func() {
@@ -122,6 +125,7 @@ func TestMemoryRunsAtMostEightBuilds(t *testing.T) {
        }
 
        memory.mutex.Lock()
        }
 
        memory.mutex.Lock()
+
        running := slices.Collect(maps.Keys(memory.queue.running))
        memory.mutex.Unlock()
        slices.SortFunc(running, func(a, b NodeKey) int {
        running := slices.Collect(maps.Keys(memory.queue.running))
        memory.mutex.Unlock()
        slices.SortFunc(running, func(a, b NodeKey) int {
@@ -144,10 +148,84 @@ func TestMemoryRunsAtMostEightBuilds(t *testing.T) {
        }
 }
 
        }
 }
 
+func TestMemorySettleWaitsForThePendingLines(t *testing.T) {
+       summarizer := newFakeSummarizer()
+       summarizer.gate = make(chan struct{})
+       memory, err := openMemory(t.Context(), t.TempDir(), newTestClock().Now, summarizer, discardLog())
+       require.NoError(t, err)
+
+       defer func() {
+               require.NoError(t, memory.Close())
+       }()
+
+       _, err = memory.Append("echo", memoryLongText(0))
+       require.NoError(t, err)
+       require.Contains(t, memory.View(), viewPending)
+
+       settled := make(chan error, 1)
+       go func() {
+               settled <- memory.Settle(t.Context())
+       }()
+
+       select {
+       case err = <-settled:
+               require.Fail(t, "Settle returned before the build", "error: %v", err)
+       case <-time.After(50 * time.Millisecond):
+       }
+
+       close(summarizer.gate)
+       require.NoError(t, <-settled)
+       require.NotContains(t, memory.View(), viewPending)
+}
+
+func TestMemorySettleFailsWhileABuildFailed(t *testing.T) {
+       summarizer := newFakeSummarizer()
+       summarizer.SetFailing(true)
+
+       logs := &SyncBuffer{}
+       memory, err := openMemory(t.Context(), t.TempDir(), newTestClock().Now, summarizer, daemonTestLogger(logs))
+       require.NoError(t, err)
+
+       defer func() {
+               require.NoError(t, memory.Close())
+       }()
+
+       _, err = memory.Append("echo", memoryLongText(0))
+       require.NoError(t, err)
+       require.ErrorIs(t, memory.Settle(t.Context()), errMemoryUnsettled)
+       require.Equal(t, "<4>summary failed level=0 index=0: fake failure\n", logs.String())
+
+       summarizer.SetFailing(false)
+
+       _, err = memory.Append("user", "retry")
+       require.NoError(t, err)
+       require.NoError(t, memory.Settle(t.Context()))
+}
+
+func TestMemorySettleEndsWithItsContext(t *testing.T) {
+       summarizer := newFakeSummarizer()
+       summarizer.gate = make(chan struct{})
+       memory, err := openMemory(t.Context(), t.TempDir(), newTestClock().Now, summarizer, discardLog())
+       require.NoError(t, err)
+
+       defer func() {
+               require.NoError(t, memory.Close())
+       }()
+
+       _, err = memory.Append("echo", memoryLongText(0))
+       require.NoError(t, err)
+
+       ctx, cancel := context.WithTimeout(t.Context(), 50*time.Millisecond)
+       defer cancel()
+
+       require.ErrorIs(t, memory.Settle(ctx), context.DeadlineExceeded)
+}
+
 func TestMemoryStartsALeafAfterFewerThanEightUnbuilt(t *testing.T) {
        summarizer := newFakeSummarizer()
        summarizer.SetFailing(true)
 func TestMemoryStartsALeafAfterFewerThanEightUnbuilt(t *testing.T) {
        summarizer := newFakeSummarizer()
        summarizer.SetFailing(true)
-       memory, err := openMemory(t.Context(), t.TempDir(), newTestClock().Now, summarizer)
+
+       memory, err := openMemory(t.Context(), t.TempDir(), newTestClock().Now, summarizer, discardLog())
        require.NoError(t, err)
 
        defer func() {
        require.NoError(t, err)
 
        defer func() {
@@ -160,6 +238,7 @@ func TestMemoryStartsALeafAfterFewerThanEightUnbuilt(t *testing.T) {
        }
 
        require.NoError(t, memory.Wait(t.Context()))
        }
 
        require.NoError(t, memory.Wait(t.Context()))
+
        for _, task := range summarizer.Tasks() {
                require.Less(t, task.Index, 8)
        }
        for _, task := range summarizer.Tasks() {
                require.Less(t, task.Index, 8)
        }
@@ -176,7 +255,7 @@ func TestMemoryStartsALeafAfterFewerThanEightUnbuilt(t *testing.T) {
 
 func TestMemoryMergesAfterBothHalves(t *testing.T) {
        summarizer := newFakeSummarizer()
 
 func TestMemoryMergesAfterBothHalves(t *testing.T) {
        summarizer := newFakeSummarizer()
-       memory, err := openMemory(t.Context(), t.TempDir(), newTestClock().Now, summarizer)
+       memory, err := openMemory(t.Context(), t.TempDir(), newTestClock().Now, summarizer, discardLog())
        require.NoError(t, err)
 
        defer func() {
        require.NoError(t, err)
 
        defer func() {
@@ -205,7 +284,8 @@ func TestMemoryRestartRestoresTheQueue(t *testing.T) {
        clock := newTestClock()
        summarizer := newFakeSummarizer()
        summarizer.SetFailing(true)
        clock := newTestClock()
        summarizer := newFakeSummarizer()
        summarizer.SetFailing(true)
-       memory, err := openMemory(t.Context(), root, clock.Now, summarizer)
+
+       memory, err := openMemory(t.Context(), root, clock.Now, summarizer, discardLog())
        require.NoError(t, err)
 
        for n := range 4 {
        require.NoError(t, err)
 
        for n := range 4 {
@@ -216,7 +296,7 @@ func TestMemoryRestartRestoresTheQueue(t *testing.T) {
        require.NoError(t, memory.Wait(t.Context()))
        require.NoError(t, memory.Close())
 
        require.NoError(t, memory.Wait(t.Context()))
        require.NoError(t, memory.Close())
 
-       memory, err = openMemory(t.Context(), root, clock.Now, newFakeSummarizer())
+       memory, err = openMemory(t.Context(), root, clock.Now, newFakeSummarizer(), discardLog())
        require.NoError(t, err)
 
        defer func() {
        require.NoError(t, err)
 
        defer func() {
@@ -228,7 +308,7 @@ func TestMemoryRestartRestoresTheQueue(t *testing.T) {
 }
 
 func TestMemoryBuildsFreeParents(t *testing.T) {
 }
 
 func TestMemoryBuildsFreeParents(t *testing.T) {
-       memory, err := openMemory(t.Context(), t.TempDir(), newTestClock().Now, newFakeSummarizer())
+       memory, err := openMemory(t.Context(), t.TempDir(), newTestClock().Now, newFakeSummarizer(), discardLog())
        require.NoError(t, err)
 
        defer func() {
        require.NoError(t, err)
 
        defer func() {
@@ -247,7 +327,8 @@ func TestMemoryBuildsFreeParents(t *testing.T) {
 func TestMemoryKeepsALongMessageOutOfTheView(t *testing.T) {
        summarizer := newFakeSummarizer()
        summarizer.SetFailing(true)
 func TestMemoryKeepsALongMessageOutOfTheView(t *testing.T) {
        summarizer := newFakeSummarizer()
        summarizer.SetFailing(true)
-       memory, err := openMemory(t.Context(), t.TempDir(), newTestClock().Now, summarizer)
+
+       memory, err := openMemory(t.Context(), t.TempDir(), newTestClock().Now, summarizer, discardLog())
        require.NoError(t, err)
 
        defer func() {
        require.NoError(t, err)
 
        defer func() {
@@ -273,7 +354,7 @@ func TestMemoryKeepsALongMessageOutOfTheView(t *testing.T) {
 func TestMemoryCoversMessagesAfterTheSavedView(t *testing.T) {
        root := t.TempDir()
        clock := newTestClock()
 func TestMemoryCoversMessagesAfterTheSavedView(t *testing.T) {
        root := t.TempDir()
        clock := newTestClock()
-       memory, err := openMemory(t.Context(), root, clock.Now, newFakeSummarizer())
+       memory, err := openMemory(t.Context(), root, clock.Now, newFakeSummarizer(), discardLog())
        require.NoError(t, err)
 
        _, err = memory.Append("user", "a")
        require.NoError(t, err)
 
        _, err = memory.Append("user", "a")
@@ -283,7 +364,7 @@ func TestMemoryCoversMessagesAfterTheSavedView(t *testing.T) {
        require.NoError(t, err)
        require.NoError(t, memory.Close())
 
        require.NoError(t, err)
        require.NoError(t, memory.Close())
 
-       memory, err = openMemory(t.Context(), root, clock.Now, newFakeSummarizer())
+       memory, err = openMemory(t.Context(), root, clock.Now, newFakeSummarizer(), discardLog())
        require.NoError(t, err)
 
        defer func() {
        require.NoError(t, err)
 
        defer func() {
@@ -296,7 +377,7 @@ func TestMemoryCoversMessagesAfterTheSavedView(t *testing.T) {
 func TestMemoryRefusesToRebuildAMissingView(t *testing.T) {
        root := t.TempDir()
        clock := newTestClock()
 func TestMemoryRefusesToRebuildAMissingView(t *testing.T) {
        root := t.TempDir()
        clock := newTestClock()
-       memory, err := openMemory(t.Context(), root, clock.Now, newFakeSummarizer())
+       memory, err := openMemory(t.Context(), root, clock.Now, newFakeSummarizer(), discardLog())
        require.NoError(t, err)
 
        _, err = memory.Append("user", "a")
        require.NoError(t, err)
 
        _, err = memory.Append("user", "a")
@@ -304,7 +385,7 @@ func TestMemoryRefusesToRebuildAMissingView(t *testing.T) {
        require.NoError(t, memory.Close())
        require.NoError(t, os.Remove(filepath.Join(root, "view.json")))
 
        require.NoError(t, memory.Close())
        require.NoError(t, os.Remove(filepath.Join(root, "view.json")))
 
-       _, err = openMemory(t.Context(), root, clock.Now, newFakeSummarizer())
+       _, err = openMemory(t.Context(), root, clock.Now, newFakeSummarizer(), discardLog())
        require.ErrorIs(t, err, errMemoryNoView)
 }
 
        require.ErrorIs(t, err, errMemoryNoView)
 }