4c40a183cf
- Add events table (migration #4): stores normalized (action, object, ts) triples extracted from facts, indexed for recurrence detection. - Add proposed_routines table: stores inferred recurring patterns with proposed/accepted/dismissed status and optional linked reminder. - Add pattern package: Extractor normalizes fact text into (action, object) pairs with TTS normalization; Detector groups events to find recurring patterns and proposes routines. - Add internal/ttsnorm: text normalization pipeline for Russian/English (lowercase, punctuation strip, number normalization, stopword removal). - Add chat seed file for LLM phraser.
84 lines
2.1 KiB
Go
84 lines
2.1 KiB
Go
package store
|
|
|
|
import (
|
|
"context"
|
|
"database/sql"
|
|
"testing"
|
|
"time"
|
|
)
|
|
|
|
func TestCreateAndQueryEvents(t *testing.T) {
|
|
s := newTestStore(t)
|
|
ctx := context.Background()
|
|
|
|
now := time.Date(2026, 7, 1, 12, 0, 0, 0, time.UTC)
|
|
|
|
// Write a fact first (events reference facts)
|
|
id1, err := s.WriteFact(ctx, now, KindSelf, "cat_water", "refilled", "tap:voice", 1.0, sql.NullInt64{})
|
|
if err != nil {
|
|
t.Fatalf("write fact: %v", err)
|
|
}
|
|
id2, err := s.WriteFact(ctx, now.Add(7*24*time.Hour), KindSelf, "cat_water", "refilled", "tap:voice", 1.0, sql.NullInt64{})
|
|
if err != nil {
|
|
t.Fatalf("write fact: %v", err)
|
|
}
|
|
id3, err := s.WriteFact(ctx, now.Add(14*24*time.Hour), KindSelf, "cat_water", "refilled", "tap:voice", 1.0, sql.NullInt64{})
|
|
if err != nil {
|
|
t.Fatalf("write fact: %v", err)
|
|
}
|
|
|
|
// Create events from the facts
|
|
eid1, err := s.CreateEvent(ctx, id1, "refill", "cat_water", now)
|
|
if err != nil {
|
|
t.Fatalf("create event: %v", err)
|
|
}
|
|
if eid1 == 0 {
|
|
t.Fatal("expected non-zero event id")
|
|
}
|
|
|
|
_, err = s.CreateEvent(ctx, id2, "refill", "cat_water", now.Add(7*24*time.Hour))
|
|
if err != nil {
|
|
t.Fatalf("create event: %v", err)
|
|
}
|
|
_, err = s.CreateEvent(ctx, id3, "refill", "cat_water", now.Add(14*24*time.Hour))
|
|
if err != nil {
|
|
t.Fatalf("create event: %v", err)
|
|
}
|
|
|
|
// Query events
|
|
events, err := s.EventsFor(ctx, "refill", "cat_water")
|
|
if err != nil {
|
|
t.Fatalf("EventsFor: %v", err)
|
|
}
|
|
if len(events) != 3 {
|
|
t.Fatalf("want 3 events, got %d", len(events))
|
|
}
|
|
|
|
// Must be ordered by ts ASC
|
|
if events[0].Ts.After(events[1].Ts) || events[1].Ts.After(events[2].Ts) {
|
|
t.Fatal("events not in ascending ts order")
|
|
}
|
|
|
|
// No events for a different action+object
|
|
events, err = s.EventsFor(ctx, "feed", "cat")
|
|
if err != nil {
|
|
t.Fatalf("EventsFor: %v", err)
|
|
}
|
|
if len(events) != 0 {
|
|
t.Fatalf("want 0 events, got %d", len(events))
|
|
}
|
|
}
|
|
|
|
func TestEventsForEmpty(t *testing.T) {
|
|
s := newTestStore(t)
|
|
ctx := context.Background()
|
|
|
|
events, err := s.EventsFor(ctx, "nonexistent", "nothing")
|
|
if err != nil {
|
|
t.Fatalf("EventsFor: %v", err)
|
|
}
|
|
if len(events) != 0 {
|
|
t.Fatalf("want 0 events, got %d", len(events))
|
|
}
|
|
}
|