package main import ( "testing" "time" "github.com/kami/maven/internal/dialogue" "github.com/kami/maven/internal/router" ) func TestFollowUpMerge(t *testing.T) { base := time.Date(2026, 7, 6, 12, 0, 0, 0, time.UTC) fireAt := base.Add(24 * time.Hour) // prior turn: a reminder that resolved a fire time. prev := &dialogue.Session{ Intent: dialogue.IntentReminder, Slots: dialogue.Slots{Time: fireAt, HasTime: true, Text: "старый текст"}, Timestamp: base, TTL: 2 * time.Minute, } t.Run("same intent inherits the missing time", func(t *testing.T) { // follow-up reminder with text but no parsed time. cur := router.Decision{ Intent: router.IntentReminder, Slots: router.Slots{Text: "позвонить маме"}, } got := followUpMerge(prev, cur, base.Add(30*time.Second)) if !got.Slots.HasTime || !got.Slots.Time.Equal(fireAt) { t.Errorf("time not inherited: HasTime=%v Time=%v", got.Slots.HasTime, got.Slots.Time) } if got.Slots.Text != "позвонить маме" { t.Errorf("current text was overwritten: %q", got.Slots.Text) } }) t.Run("current slot wins over prior (gaps only)", func(t *testing.T) { own := base.Add(48 * time.Hour) cur := router.Decision{ Intent: router.IntentReminder, Slots: router.Slots{Time: own, HasTime: true}, } got := followUpMerge(prev, cur, base.Add(30*time.Second)) if !got.Slots.Time.Equal(own) { t.Errorf("current time clobbered by prior: %v", got.Slots.Time) } }) t.Run("different intent does not inherit", func(t *testing.T) { cur := router.Decision{Intent: router.IntentFact, Slots: router.Slots{Key: "water", HasKey: true}} got := followUpMerge(prev, cur, base.Add(30*time.Second)) if got.Slots.HasTime { t.Error("time bled across a different intent") } }) t.Run("clarify turn does not inherit", func(t *testing.T) { cur := router.Decision{Intent: router.IntentReminder, Clarify: true} got := followUpMerge(prev, cur, base.Add(30*time.Second)) if got.Slots.HasTime { t.Error("clarify turn inherited slots") } }) t.Run("expired prior does not inherit", func(t *testing.T) { cur := router.Decision{Intent: router.IntentReminder, Slots: router.Slots{Text: "x"}} got := followUpMerge(prev, cur, base.Add(3*time.Minute)) // past the 2-min TTL if got.Slots.HasTime { t.Error("expired session still inherited") } }) t.Run("nil prior is a no-op", func(t *testing.T) { cur := router.Decision{Intent: router.IntentReminder, Slots: router.Slots{Text: "x"}} got := followUpMerge(nil, cur, base) if got.Slots.HasTime { t.Error("nil prior produced inheritance") } }) t.Run("router-only Value survives the round-trip", func(t *testing.T) { cur := router.Decision{ Intent: router.IntentFact, Slots: router.Slots{Key: "sleep", HasKey: true, Value: "6h"}, } factPrev := &dialogue.Session{ Intent: dialogue.IntentFact, Slots: dialogue.Slots{Text: "спал"}, Timestamp: base, TTL: 2 * time.Minute, } got := followUpMerge(factPrev, cur, base.Add(10*time.Second)) if got.Slots.Value != "6h" { t.Errorf("Value lost through dialogue conversion: %q", got.Slots.Value) } }) }