Files

148 lines
3.5 KiB
Go

package emotion
import (
"errors"
"fmt"
"sync"
"testing"
)
func mustMachine(t *testing.T, s EmotionState) *Machine {
t.Helper()
m, err := NewMachine(Params{InitialState: s})
if err != nil {
t.Fatalf("NewMachine(%q) error: %v", s, err)
}
return m
}
func TestManager_EmotionFollowsPersona(t *testing.T) {
t.Parallel()
mg := NewManager()
a, err := NewAffection(50, 0, 100)
if err != nil {
t.Fatalf("NewAffection() error: %v", err)
}
mg.Set("personaA", mustMachine(t, Excited), a)
mg.Set("personaB", mustMachine(t, Calm), nil)
// Transition personaA; personaB must be untouched.
if err := mg.Transition("personaA", Happy); err != nil {
t.Fatalf("Transition(personaA, happy) error: %v", err)
}
mA, _, ok := mg.Get("personaA")
if !ok {
t.Fatal("personaA not found")
}
if mA.Current() != Happy {
t.Fatalf("personaA Current() = %q, want %q", mA.Current(), Happy)
}
mB, _, _ := mg.Get("personaB")
if mB.Current() != Calm {
t.Fatalf("personaB Current() = %q, want %q (isolation)", mB.Current(), Calm)
}
}
func TestManager_AdjustAffectionIsolated(t *testing.T) {
t.Parallel()
mg := NewManager()
affA, err := NewAffection(40, 0, 100)
if err != nil {
t.Fatalf("NewAffection() error: %v", err)
}
affB, err := NewAffection(10, 0, 100)
if err != nil {
t.Fatalf("NewAffection() error: %v", err)
}
mg.Set("personaA", mustMachine(t, Calm), affA)
mg.Set("personaB", mustMachine(t, Calm), affB)
before, after, err := mg.AdjustAffection("personaA", 30)
if err != nil {
t.Fatalf("AdjustAffection(personaA) error: %v", err)
}
if before != 40 || after != 70 {
t.Fatalf("AdjustAffection(personaA) before/after = %d/%d, want 40/70", before, after)
}
if got := affB.Value(); got != 10 {
t.Fatalf("personaB value = %d, want 10 (isolation)", got)
}
}
func TestManager_TransitionUnknownPersona(t *testing.T) {
t.Parallel()
mg := NewManager()
mg.Set("personaA", mustMachine(t, Calm), nil)
err := mg.Transition("ghost", Happy)
if !errors.Is(err, ErrUnknownPersona) {
t.Fatalf("Transition(ghost) error = %v, want ErrUnknownPersona", err)
}
if _, _, err := mg.AdjustAffection("ghost", 1); !errors.Is(err, ErrUnknownPersona) {
t.Fatalf("AdjustAffection(ghost) error = %v, want ErrUnknownPersona", err)
}
}
func TestManager_SetGetRemoveCount(t *testing.T) {
t.Parallel()
mg := NewManager()
mg.Set("a", mustMachine(t, Calm), nil)
mg.Set("b", mustMachine(t, Happy), nil)
if got := mg.Count(); got != 2 {
t.Fatalf("Count() = %d, want 2", got)
}
if _, _, ok := mg.Get("a"); !ok {
t.Fatal("Get(a) not found")
}
if !mg.Remove("a") {
t.Fatal("Remove(a) should report true")
}
if mg.Remove("a") {
t.Fatal("Remove(a) again should report false")
}
if got := mg.Count(); got != 1 {
t.Fatalf("Count() after remove = %d, want 1", got)
}
}
func TestManager_ConcurrentAccess(t *testing.T) {
t.Parallel()
mg := NewManager()
const n = 8
for i := 0; i < n; i++ {
id := idOf(i)
aff, _ := NewAffection(50, 0, 100)
mg.Set(id, mustMachine(t, Excited), aff)
}
var wg sync.WaitGroup
for i := 0; i < n; i++ {
id := idOf(i)
wg.Add(1)
go func() {
defer wg.Done()
_, _, _ = mg.Get(id)
_, _, _ = mg.AdjustAffection(id, 5)
_ = mg.Transition(id, Happy)
}()
}
wg.Wait()
for i := 0; i < n; i++ {
id := idOf(i)
m, _, ok := mg.Get(id)
if !ok {
t.Fatalf("persona %s missing after concurrent access", id)
}
if m.Current() != Happy {
t.Fatalf("persona %s Current() = %q, want %q", id, m.Current(), Happy)
}
}
}
func idOf(i int) string { return fmt.Sprintf("persona%d", i) }