package dialogue import ( "testing" "time" ) func TestSessionExpiry(t *testing.T) { now := time.Date(2026, 7, 6, 12, 0, 0, 0, time.UTC) sess := &Session{ Intent: IntentQuery, Timestamp: now, TTL: 2 * time.Minute, } if sess.IsExpired(now) { t.Error("session should not be expired at creation time") } if sess.IsExpired(now.Add(1 * time.Minute)) { t.Error("session should not be expired after 1 minute") } if !sess.IsExpired(now.Add(3 * time.Minute)) { t.Error("session should be expired after 3 minutes") } } func TestSessionStorePutGet(t *testing.T) { now := time.Date(2026, 7, 6, 12, 0, 0, 0, time.UTC) store := NewSessionStore(2 * time.Minute) if s := store.Get("nonexistent", now); s != nil { t.Error("expected nil for non-existent session") } sess := &Session{Intent: IntentQuery, Timestamp: now} store.Put("user1", sess) got := store.Get("user1", now) if got == nil { t.Fatal("expected session after Put") } if got.Intent != IntentQuery { t.Errorf("intent = %q, want query", got.Intent) } later := now.Add(5 * time.Minute) if s := store.Get("user1", later); s != nil { t.Error("expected nil for expired session") } } func TestSessionStoreDefaultTTL(t *testing.T) { store := NewSessionStore(0) if store.defaultTTL != 2*time.Minute { t.Errorf("defaultTTL = %v, want 2m", store.defaultTTL) } } func TestSessionStoreCustomTTL(t *testing.T) { // A session with an explicit TTL should use that instead of the default. now := time.Date(2026, 7, 6, 12, 0, 0, 0, time.UTC) store := NewSessionStore(2 * time.Minute) sess := &Session{Intent: IntentQuery, Timestamp: now, TTL: 15 * time.Minute} store.Put("chatty", sess) // Should still be alive at 10 minutes (past the 2m default). if s := store.Get("chatty", now.Add(10*time.Minute)); s == nil { t.Error("chat session with 15m TTL expired at 10m — custom TTL not applied") } // Should be expired after 20 minutes. if s := store.Get("chatty", now.Add(20*time.Minute)); s != nil { t.Error("chat session with 15m TTL should be expired at 20m") } } func TestInheritSlots(t *testing.T) { now := time.Date(2026, 7, 6, 0, 0, 0, 0, time.UTC) prev := Slots{Text: "Moscow", HasKey: true, Key: "location"} cur := Slots{} inherited := InheritSlots(prev, cur) if !inherited.HasKey || inherited.Key != "location" { t.Error("should inherit key from previous") } cur2 := Slots{HasKey: true, Key: "London"} inherited2 := InheritSlots(prev, cur2) if inherited2.Key != "London" { t.Error("should keep current key when present") } prevTime := Slots{HasTime: true, Time: now} inherited3 := InheritSlots(prevTime, Slots{}) if !inherited3.HasTime || !inherited3.Time.Equal(now) { t.Error("should inherit time") } prevFn := Slots{HasFn: true, Fn: "status", Args: []string{"nginx"}} inherited4 := InheritSlots(prevFn, Slots{}) if !inherited4.HasFn || inherited4.Fn != "status" { t.Error("should inherit fn") } if len(inherited4.Args) != 1 || inherited4.Args[0] != "nginx" { t.Error("should inherit args") } inherited5 := InheritSlots(prevFn, Slots{HasFn: true, Fn: "restart", Args: []string{"docker"}}) if len(inherited5.Args) != 1 || inherited5.Args[0] != "docker" { t.Error("should keep current args") } prevText := Slots{Text: "какая погода в москве"} inherited6 := InheritSlots(prevText, Slots{Text: ""}) if inherited6.Text != "какая погода в москве" { t.Error("should inherit text when current is empty") } prevValue := Slots{Key: "water", HasKey: true, Value: `"drank"`} inherited7 := InheritSlots(prevValue, Slots{}) if inherited7.Value != `"drank"` { t.Error("should inherit value when current is empty") } kept := InheritSlots(prevValue, Slots{Value: "2l"}) if kept.Value != "2l" { t.Error("should keep current value") } }