Files
kami 4c40a183cf feat: event store, pattern inference, and routine proposals
- 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.
2026-07-10 15:49:03 +04:00

57 lines
1.7 KiB
Go

package pattern
import (
"testing"
"time"
)
func TestExtractSimpleAction(t *testing.T) {
now := time.Now().UTC()
tests := []struct {
name string
key string
value string
wantAction string
wantObject string
wantNil bool
}{
{"refill english", "water_fountain", "refilled", "refill", "water_fountain", false},
{"refill russian", "cat_water", "налил", "refill", "cat_water", false},
{"fed cat", "cat_food", "fed", "feed", "cat_food", false},
{"clean litter", "litter_box", "почистил", "clean", "litter_box", false},
{"walked dog", "dog_walk", "walked", "walk", "dog_walk", false},
{"took medicine", "medicine", "took", "take", "medicine", false},
{"watered plants", "plants", "watered", "water", "plants", false},
{"json value skipped", "weight", `{"kg": 82}`, "", "", true},
{"empty value skipped", "something", "", "", "", true},
{"unrecognized action", "door", "opened", "", "", true},
{"case insensitive", "WATER_FOUNTAIN", "REFILLED", "refill", "water_fountain", false},
{"whitespace trimmed", " cat_bed ", " cleaned ", "clean", "cat_bed", false},
}
for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
ev := Extract(42, tc.key, tc.value, now)
if tc.wantNil {
if ev != nil {
t.Fatalf("want nil, got %+v", ev)
}
return
}
if ev == nil {
t.Fatal("want event, got nil")
}
if ev.Action != tc.wantAction {
t.Fatalf("action: want %q, got %q", tc.wantAction, ev.Action)
}
if ev.Object != tc.wantObject {
t.Fatalf("object: want %q, got %q", tc.wantObject, ev.Object)
}
if ev.FactID != 42 {
t.Fatalf("fact_id: want 42, got %d", ev.FactID)
}
})
}
}