Files
Maven/internal/store/nudges_snooze_test.go
T
kami 32687b3712 Read the recorded snooze outcomes back out of the nudges table (#364)
The gate honours State.SnoozeUntil but nothing ever filled it. New
store.SnoozedUntil returns, per rule, when the newest snooze runs out.
Reviewer: the fixed 2h SnoozeDuration and its reasoning in nudges.go —
nothing upstream can supply a per-nudge length, so no new column.
Expired snoozes are dropped in SQL, so silence can never be permanent.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CGeSZxh1DCtRxmFVSYVGvJ
2026-07-31 02:29:51 +04:00

105 lines
2.9 KiB
Go

package store
import (
"context"
"testing"
"time"
)
// snoozeNudge records a nudge and immediately snoozes it at ts.
func snoozeNudge(t *testing.T, s *Store, rule string, ts time.Time) {
t.Helper()
ctx := context.Background()
id, err := s.RecordNudge(ctx, rule, "voice", "drink water", ts)
if err != nil {
t.Fatalf("RecordNudge: %v", err)
}
if err := s.ResolveNudge(ctx, id, NudgeSnoozed, ts); err != nil {
t.Fatalf("ResolveNudge: %v", err)
}
}
func TestSnoozedUntilPerRule(t *testing.T) {
s := newTestStore(t)
now := time.Now().UTC().Truncate(time.Millisecond)
snoozeNudge(t, s, "water", now.Add(-10*time.Minute))
snoozeNudge(t, s, "break", now.Add(-30*time.Minute))
got, err := s.SnoozedUntil(context.Background(), now)
if err != nil {
t.Fatalf("SnoozedUntil: %v", err)
}
if len(got) != 2 {
t.Fatalf("want 2 snoozed rules, got %v", got)
}
wantWater := now.Add(-10 * time.Minute).Add(SnoozeDuration)
if !got["water"].Equal(wantWater) {
t.Fatalf("water until = %v, want %v", got["water"], wantWater)
}
}
// The map must only ever hold the newest snooze for a rule, so a stale one
// can't shorten (or lengthen) the live one.
func TestSnoozedUntilUsesNewestSnooze(t *testing.T) {
s := newTestStore(t)
now := time.Now().UTC().Truncate(time.Millisecond)
snoozeNudge(t, s, "water", now.Add(-90*time.Minute))
snoozeNudge(t, s, "water", now.Add(-5*time.Minute))
got, err := s.SnoozedUntil(context.Background(), now)
if err != nil {
t.Fatalf("SnoozedUntil: %v", err)
}
want := now.Add(-5 * time.Minute).Add(SnoozeDuration)
if !got["water"].Equal(want) {
t.Fatalf("water until = %v, want %v", got["water"], want)
}
}
// A snooze must expire. If this ever regresses Maven goes quiet forever and
// nobody can tell why.
func TestSnoozedUntilExpires(t *testing.T) {
s := newTestStore(t)
now := time.Now().UTC().Truncate(time.Millisecond)
snoozeNudge(t, s, "water", now.Add(-SnoozeDuration-time.Minute))
got, err := s.SnoozedUntil(context.Background(), now)
if err != nil {
t.Fatalf("SnoozedUntil: %v", err)
}
if _, ok := got["water"]; ok {
t.Fatalf("expired snooze still active: %v", got)
}
}
// Other outcomes are not snoozes.
func TestSnoozedUntilIgnoresOtherOutcomes(t *testing.T) {
s := newTestStore(t)
ctx := context.Background()
now := time.Now().UTC().Truncate(time.Millisecond)
for _, outcome := range []string{NudgeActed, NudgeIgnored} {
id, err := s.RecordNudge(ctx, "water", "voice", "drink water", now)
if err != nil {
t.Fatalf("RecordNudge: %v", err)
}
if err := s.ResolveNudge(ctx, id, outcome, now); err != nil {
t.Fatalf("ResolveNudge: %v", err)
}
}
if _, err := s.RecordNudge(ctx, "break", "voice", "stand up", now); err != nil {
t.Fatalf("RecordNudge: %v", err)
}
got, err := s.SnoozedUntil(ctx, now)
if err != nil {
t.Fatalf("SnoozedUntil: %v", err)
}
if len(got) != 0 {
t.Fatalf("want no snoozes, got %v", got)
}
}