]> git.giacomolli.uk - onechat/commitdiff
Name private helpers for their owning feature
authorAnderson A. Giacomolli <redacted>
Wed, 11 Nov 2026 14:11:27 +0000 (11:11 -0300)
committerAnderson A. Giacomolli <redacted>
Wed, 11 Nov 2026 14:11:27 +0000 (11:11 -0300)
Daemon log formatting and single-consumer test helpers identify their
owning feature. Memory replay helpers identify the memory test owner.
WhatsApp tests reuse the shared discard logger instead of a duplicate
logger factory. The renamed functions keep their behavior.

Issue: 947bddb

daemon_log.go
daemon_start_helper_test.go
memory_test.go
probe_log_helper_test.go
turn_test.go
whatsapp_test.go

index 771fef49a054a20d1761cd3aad2f1ef284f65c36..1ff6aa4efb5a87c6e0b5c66bc83fc5e9bba94cbe 100644 (file)
@@ -138,15 +138,15 @@ func daemonLogAttr(line *strings.Builder, group string, attr slog.Attr, cause *s
        line.WriteByte('=')
 
        if attr.Value.Kind() == slog.KindDuration {
        line.WriteByte('=')
 
        if attr.Value.Kind() == slog.KindDuration {
-               line.WriteString(durationText(attr.Value.Duration()))
+               line.WriteString(daemonLogDuration(attr.Value.Duration()))
        } else {
                fmt.Fprint(line, attr.Value.Any())
        }
 }
 
        } else {
                fmt.Fprint(line, attr.Value.Any())
        }
 }
 
-// durationText returns d for a log line: rounded to the second, or to the
+// daemonLogDuration returns d for a log line: rounded to the second, or to the
 // millisecond when it is shorter than a second.
 // millisecond when it is shorter than a second.
-func durationText(d time.Duration) string {
+func daemonLogDuration(d time.Duration) string {
        if d < time.Second {
                return d.Round(time.Millisecond).String()
        }
        if d < time.Second {
                return d.Round(time.Millisecond).String()
        }
index b09c11be0679f2e2b1457e08622c98fa4ab26b24..a27967ea62ab81909e23aee2c4b02a4d3bbfa0e0 100644 (file)
@@ -36,7 +36,7 @@ func daemonStartTestWithWhatsApp(t *testing.T, server *ModelFake, bot *TelegramB
 func daemonStartTestWithSchedules(t *testing.T, server *ModelFake, bot *TelegramBot, whatsapp *WhatsApp, schedules *Schedules, clock func() time.Time) (string, *Memory) {
        t.Helper()
 
 func daemonStartTestWithSchedules(t *testing.T, server *ModelFake, bot *TelegramBot, whatsapp *WhatsApp, schedules *Schedules, clock func() time.Time) (string, *Memory) {
        t.Helper()
 
-       dir := socketTestDir(t)
+       dir := daemonTestSocketDir(t)
        agent, err := openAgent(filepath.Join(dir, daemonAgentDirName))
        require.NoError(t, err)
 
        agent, err := openAgent(filepath.Join(dir, daemonAgentDirName))
        require.NoError(t, err)
 
@@ -67,10 +67,10 @@ func daemonStartTestWithSchedules(t *testing.T, server *ModelFake, bot *Telegram
        return socket, memory
 }
 
        return socket, memory
 }
 
-// socketTestDir returns a new short directory under /tmp for data that holds a
-// daemon socket. t.TempDir holds the test name and the TMPDIR of the shell, so
-// its paths can pass the 107-byte limit of a Unix socket path.
-func socketTestDir(t *testing.T) string {
+// daemonTestSocketDir returns a new short directory under /tmp for data that
+// holds a daemon socket. t.TempDir holds the test name and the TMPDIR of the
+// shell, so its paths can pass the 107-byte limit of a Unix socket path.
+func daemonTestSocketDir(t *testing.T) string {
        t.Helper()
 
        dir, err := os.MkdirTemp("/tmp", "onechat-")
        t.Helper()
 
        dir, err := os.MkdirTemp("/tmp", "onechat-")
index d3fa6129d4cc2134dd4130f0917224268bedd6a9..6f76e5b2ebe359f31ed91158a3ee611b029aed5d 100644 (file)
@@ -18,12 +18,12 @@ import (
 )
 
 const (
 )
 
 const (
-       // replayMessages is the length of the synthetic log.
-       replayMessages = 100000
+       // memoryReplayMessages is the length of the synthetic log.
+       memoryReplayMessages = 100000
 
 
-       // replayShortMessages is the length of the synthetic log with -short.
-       // It is long enough for batches of the chat view.
-       replayShortMessages = 5000
+       // memoryReplayShortMessages is the length of the synthetic log with
+       // -short. It is long enough for batches of the chat view.
+       memoryReplayShortMessages = 5000
 )
 
 func TestMemoryRestartLoadsTheSameView(t *testing.T) {
 )
 
 func TestMemoryRestartLoadsTheSameView(t *testing.T) {
@@ -520,9 +520,9 @@ func TestQueuedAppendRefusesAnInvalidReceipt(t *testing.T) {
 // log is shorter, and the test still requires a batch of the chat view.
 func TestReplaySyntheticLog(t *testing.T) {
        start := time.Now()
 // log is shorter, and the test still requires a batch of the chat view.
 func TestReplaySyntheticLog(t *testing.T) {
        start := time.Now()
-       messages := replayMessages
+       messages := memoryReplayMessages
        if testing.Short() {
        if testing.Short() {
-               messages = replayShortMessages
+               messages = memoryReplayShortMessages
        }
 
        root := t.TempDir()
        }
 
        root := t.TempDir()
@@ -541,17 +541,17 @@ func TestReplaySyntheticLog(t *testing.T) {
        long := make([]bool, messages)
        for id := range messages {
                if id == messages/2 {
        long := make([]bool, messages)
        for id := range messages {
                if id == messages/2 {
-                       memory = replayRequireRestart(t, memory, root, clock)
+                       memory = memoryReplayRequireRestart(t, memory, root, clock)
                }
 
                clock.now = clock.now.Add(time.Minute)
                }
 
                clock.now = clock.now.Add(time.Minute)
-               kind, text := replayMessage(random, id)
+               kind, text := memoryReplayMessage(random, id)
                long[id] = len(kind)+len(": ")+len(text) > nodeBytes
                long[id] = len(kind)+len(": ")+len(text) > nodeBytes
-               lines := replayViewLines(memory)
+               lines := memoryReplayViewLines(memory)
                _, err = memory.Append(kind, text)
                require.NoError(t, err)
 
                _, err = memory.Append(kind, text)
                require.NoError(t, err)
 
-               size, merged := replayRequireViews(t, memory, lines, long, id%1000 == 0)
+               size, merged := memoryReplayRequireViews(t, memory, lines, long, id%1000 == 0)
                if merged {
                        batches++
                        low = min(low, size)
                if merged {
                        batches++
                        low = min(low, size)
@@ -560,7 +560,7 @@ func TestReplaySyntheticLog(t *testing.T) {
                require.NoError(t, memory.Wait(t.Context()))
        }
 
                require.NoError(t, memory.Wait(t.Context()))
        }
 
-       memory = replayRequireRestart(t, memory, root, clock)
+       memory = memoryReplayRequireRestart(t, memory, root, clock)
        require.Equal(t, messages, memory.log.Len())
        require.Positive(t, batches)
 
        require.Equal(t, messages, memory.log.Len())
        require.Positive(t, batches)
 
@@ -582,9 +582,9 @@ func memoryLongText(n int) string {
        return fmt.Sprintf("long %d ", n) + strings.Repeat("x", nodeBytes)
 }
 
        return fmt.Sprintf("long %d ", n) + strings.Repeat("x", nodeBytes)
 }
 
-// replayRequireRestart closes memory, opens it again, and checks that the views
-// and view.json stay the same byte for byte.
-func replayRequireRestart(t *testing.T, memory *Memory, root string, clock *TestClock) *Memory {
+// memoryReplayRequireRestart closes memory, opens it again, and checks that the
+// views and view.json stay the same byte for byte.
+func memoryReplayRequireRestart(t *testing.T, memory *Memory, root string, clock *TestClock) *Memory {
        t.Helper()
 
        require.NoError(t, memory.Wait(t.Context()))
        t.Helper()
 
        require.NoError(t, memory.Wait(t.Context()))
@@ -608,85 +608,85 @@ func replayRequireRestart(t *testing.T, memory *Memory, root string, clock *Test
        return memory
 }
 
        return memory
 }
 
-// replayMessage returns the kind and the text of message id. Most messages are
-// tool calls and their output. Users write short lines. One output in a hundred
-// is over 30 KB.
-func replayMessage(random *rand.Rand, id int) (string, string) {
+// memoryReplayMessage returns the kind and the text of message id. Most
+// messages are tool calls and their output. Users write short lines. One output
+// in a hundred is over 30 KB.
+func memoryReplayMessage(random *rand.Rand, id int) (string, string) {
        roll := random.IntN(100)
        switch {
        case roll < 5:
        roll := random.IntN(100)
        switch {
        case roll < 5:
-               return "user", replayText(id, 10+random.IntN(200))
+               return "user", memoryReplayText(id, 10+random.IntN(200))
        case roll < 10:
        case roll < 10:
-               return "reply", replayText(id, 100+random.IntN(1500))
+               return "reply", memoryReplayText(id, 100+random.IntN(1500))
        case roll < 12:
        case roll < 12:
-               return "note", replayText(id, 50+random.IntN(600))
+               return "note", memoryReplayText(id, 50+random.IntN(600))
        case roll < 13:
        case roll < 13:
-               return "work", replayText(id, 300+random.IntN(3000))
+               return "work", memoryReplayText(id, 300+random.IntN(3000))
        case roll < 56:
        case roll < 56:
-               return "tool", replayText(id, 60+random.IntN(400))
+               return "tool", memoryReplayText(id, 60+random.IntN(400))
        case roll < 57:
        case roll < 57:
-               return "echo", replayText(id, 30000+random.IntN(30000))
+               return "echo", memoryReplayText(id, 30000+random.IntN(30000))
        default:
        default:
-               return "echo", replayText(id, 20+random.IntN(4000))
+               return "echo", memoryReplayText(id, 20+random.IntN(4000))
        }
 }
 
        }
 }
 
-// replayViewLines returns the number of lines in the chat view of memory.
-func replayViewLines(memory *Memory) int {
+// memoryReplayViewLines returns the number of lines in the chat view of memory.
+func memoryReplayViewLines(memory *Memory) int {
        memory.mutex.Lock()
        defer memory.mutex.Unlock()
 
        return len(memory.chat.lines)
 }
 
        memory.mutex.Lock()
        defer memory.mutex.Unlock()
 
        return len(memory.chat.lines)
 }
 
-// replayRequireViews checks the views after a new message, and with full
+// memoryReplayRequireViews checks the views after a new message, and with full
 // compares them with the tree. Each view is at most its high limit, and a batch
 // merges the chat view to its low limit. The leaf of the new message can be
 // built after the append, so the sizes count it as the pending mark, as the
 // append did. It returns the size of the chat view and if a batch merged it,
 // from the number of lines before the message.
 // compares them with the tree. Each view is at most its high limit, and a batch
 // merges the chat view to its low limit. The leaf of the new message can be
 // built after the append, so the sizes count it as the pending mark, as the
 // append did. It returns the size of the chat view and if a batch merged it,
 // from the number of lines before the message.
-func replayRequireViews(t *testing.T, memory *Memory, lines int, long []bool, full bool) (int, bool) {
+func memoryReplayRequireViews(t *testing.T, memory *Memory, lines int, long []bool, full bool) (int, bool) {
        t.Helper()
 
        memory.mutex.Lock()
        defer memory.mutex.Unlock()
 
        t.Helper()
 
        memory.mutex.Lock()
        defer memory.mutex.Unlock()
 
-       size := replayAppendSize(memory.chat)
+       size := memoryReplayAppendSize(memory.chat)
        require.LessOrEqual(t, size, viewChatHigh)
        require.LessOrEqual(t, size, viewChatHigh)
-       require.LessOrEqual(t, replayAppendSize(memory.cview), viewCompactionHigh)
+       require.LessOrEqual(t, memoryReplayAppendSize(memory.cview), viewCompactionHigh)
 
        merged := len(memory.chat.lines) < lines
        if merged {
                require.LessOrEqual(t, size, viewChatLow)
        }
 
 
        merged := len(memory.chat.lines) < lines
        if merged {
                require.LessOrEqual(t, size, viewChatLow)
        }
 
-       replayRequireOnlySummaries(t, memory, long, full)
+       memoryReplayRequireOnlySummaries(t, memory, long, full)
 
        return size, merged
 }
 
 
        return size, merged
 }
 
-// replayText returns a text of about size bytes that starts with its message ID
-// and holds some non-ASCII characters.
-func replayText(id, size int) string {
+// memoryReplayText returns a text of about size bytes that starts with its
+// message ID and holds some non-ASCII characters.
+func memoryReplayText(id, size int) string {
        head := fmt.Sprintf("m%d ", id)
        words := "ação data line café "
 
        return head + strings.Repeat(words, max(1, (size-len(head))/len(words)))
 }
 
        head := fmt.Sprintf("m%d ", id)
        words := "ação data line café "
 
        return head + strings.Repeat(words, max(1, (size-len(head))/len(words)))
 }
 
-// replayAppendSize returns the size of view with its last line as the pending
-// mark.
-func replayAppendSize(view *View) int {
+// memoryReplayAppendSize returns the size of view with its last line as the
+// pending mark.
+func memoryReplayAppendSize(view *View) int {
        last := view.lines[len(view.lines)-1]
        return view.Size() - view.lineSize(last) + len(viewPending)
 }
 
        last := view.lines[len(view.lines)-1]
        return view.Size() - view.lineSize(last) + len(viewPending)
 }
 
-// replayRequireOnlySummaries checks each line of the chat view as the model
-// reads it. A built line holds its node, a long message has a summary of the
-// fake summarizer and not its text, and an unbuilt line holds only the pending
-// mark. With full, it also checks that the view keeps the node of each built
-// line. The caller holds the lock of memory.
-func replayRequireOnlySummaries(t *testing.T, memory *Memory, long []bool, full bool) {
+// memoryReplayRequireOnlySummaries checks each line of the chat view as the
+// model reads it. A built line holds its node, a long message has a summary of
+// the fake summarizer and not its text, and an unbuilt line holds only the
+// pending mark. With full, it also checks that the view keeps the node of each
+// built line. The caller holds the lock of memory.
+func memoryReplayRequireOnlySummaries(t *testing.T, memory *Memory, long []bool, full bool) {
        t.Helper()
 
        rendered := strings.Split(memory.chat.Render(), "\n")
        t.Helper()
 
        rendered := strings.Split(memory.chat.Render(), "\n")
index 7df9f489b3688bb2cbe56de10397934be1d30cae..acf4c8f01c15e98a2485959206bb536ffc5627a5 100644 (file)
@@ -130,8 +130,8 @@ func (g *ProbeWriter) outputLine() string {
 }
 
 // probeLog returns count messages of made-up work on a small bakery order
 }
 
 // probeLog returns count messages of made-up work on a small bakery order
-// system, with the mix of kinds and sizes of replayMessage. The same count
-// always gives the same log.
+// system, with the mix of kinds and sizes of memoryReplayMessage. The same
+// count always gives the same log.
 func probeLog(count int) []DatedMessage {
        g := ProbeWriter{
                random: rand.New(rand.NewPCG(7, 11)),
 func probeLog(count int) []DatedMessage {
        g := ProbeWriter{
                random: rand.New(rand.NewPCG(7, 11)),
index 27151a9af9d6bafb1191c5da589f6c8cfb2a43ea..3c6bb06aa1d244328442c1d8b643d02c0db22976 100644 (file)
@@ -18,7 +18,7 @@ func TestTurnRunsToolsUntilTheFinalAnswer(t *testing.T) {
        server.reply = func(w http.ResponseWriter, call ModelFakeCall) {
                if call.number == 0 {
                        modelWriteEvents(w, modelCompletedEvent([]any{
        server.reply = func(w http.ResponseWriter, call ModelFakeCall) {
                if call.number == 0 {
                        modelWriteEvents(w, modelCompletedEvent([]any{
-                               modelReasoningItem("rs_1", "opaque thought"),
+                               turnReasoningItem("rs_1", "opaque thought"),
                                modelFunctionCallItem("fc_1", "call_1", "shell", `{"command":"echo hi"}`),
                        }, 100, 0, 5))
 
                                modelFunctionCallItem("fc_1", "call_1", "shell", `{"command":"echo hi"}`),
                        }, 100, 0, 5))
 
@@ -391,7 +391,7 @@ func (r *TurnEventRecorder) LastEvent(kind string) Event {
        return Event{}
 }
 
        return Event{}
 }
 
-func modelReasoningItem(id, encrypted string) map[string]any {
+func turnReasoningItem(id, encrypted string) map[string]any {
        return map[string]any{
                "type":              "reasoning",
                "id":                id,
        return map[string]any{
                "type":              "reasoning",
                "id":                id,
index 9da1537b58e69e97f53f8f58da0fd6510e049b0f..6475805f0aac2ae17674d98343f032cf51c090b1 100644 (file)
@@ -2,7 +2,6 @@ package main
 
 import (
        "errors"
 
 import (
        "errors"
-       "log/slog"
        "os"
        "path/filepath"
        "strings"
        "os"
        "path/filepath"
        "strings"
@@ -91,7 +90,7 @@ func TestWhatsAppRetriesTheFirstConnectionWithCappedBackoff(t *testing.T) {
                time.Sleep(10 * time.Minute)
                synctest.Wait()
                require.Len(t, test.fake.Connections(), 11)
                time.Sleep(10 * time.Minute)
                synctest.Wait()
                require.Len(t, test.fake.Connections(), 11)
-               require.Equal(t, "<4>first connect failed, retrying: network unavailable\n<6>connected again after="+durationText(outage)+"\n", test.logs.String())
+               require.Equal(t, "<4>first connect failed, retrying: network unavailable\n<6>connected again after="+daemonLogDuration(outage)+"\n", test.logs.String())
        })
 }
 
        })
 }
 
@@ -357,7 +356,7 @@ func TestWhatsAppRefusesASessionThatOthersCanRead(t *testing.T) {
        whatsappTestSession(t, dir, true)
        require.NoError(t, os.Chmod(filepath.Join(dir, whatsappDirName, whatsappSessionFileName), 0o644))
 
        whatsappTestSession(t, dir, true)
        require.NoError(t, os.Chmod(filepath.Join(dir, whatsappDirName, whatsappSessionFileName), 0o644))
 
-       _, err := openWhatsApp(t.Context(), dir, time.Now, whatsappDiscardLog(), whatsappNewClient)
+       _, err := openWhatsApp(t.Context(), dir, time.Now, discardLog(), whatsappNewClient)
        require.EqualError(t, err, "whatsapp/session.db holds the WhatsApp session, so its mode must be 0600")
 }
 
        require.EqualError(t, err, "whatsapp/session.db holds the WhatsApp session, so its mode must be 0600")
 }
 
@@ -758,8 +757,3 @@ func TestSubagentHasNoWhatsAppTools(t *testing.T) {
        require.Equal(t, `error: unknown tool "whatsapp_find"`, box.run(t.Context(), "whatsapp_find", `{"name":"ana"}`))
        require.Equal(t, `error: unknown tool "whatsapp_send"`, box.run(t.Context(), "whatsapp_send", `{"id":"a","chat":"15550002222@s.whatsapp.net","text":"hi"}`))
 }
        require.Equal(t, `error: unknown tool "whatsapp_find"`, box.run(t.Context(), "whatsapp_find", `{"name":"ana"}`))
        require.Equal(t, `error: unknown tool "whatsapp_send"`, box.run(t.Context(), "whatsapp_send", `{"id":"a","chat":"15550002222@s.whatsapp.net","text":"hi"}`))
 }
-
-// whatsappDiscardLog is a logger for a WhatsApp whose log a test does not read.
-func whatsappDiscardLog() *slog.Logger {
-       return slog.New(slog.DiscardHandler)
-}