// engine_session_id_test.go covers the session id semantics that // ADR-0008 r30 实证 surfaced as easy-to-misuse: NewSession produces // distinct sessions on each call (no accidental collision under // parallel use) while Session(id) is get-or-create (same id returns // same instance accumulating history). // // engine_session_id_test.go 覆盖 ADR-0008 r30 实证暴露的 session id // 语义易踩坑点: NewSession 每次调返不同会话 (并发也不撞), Session(id) // 同 id 返同实例累积 history. package engine import ( "strings" "sync" "testing" ) // TestNewAutoSessionID_FormatAndUnique verifies the auto id generator // returns the expected "session--" form and is unique // across rapid sequential calls (nano timestamp can collide on fast // hardware -- the random suffix is what makes the guarantee). // // TestNewAutoSessionID_FormatAndUnique 验证自动 id 生成器形态正确且 // 高速串行调用下唯一 (纳秒时间戳在快机器上可能撞, 随机后缀是真正 // 防撞). func TestNewAutoSessionID_FormatAndUnique(t *testing.T) { const n = 1000 seen := make(map[string]struct{}, n) for i := 0; i < n; i++ { id := newAutoSessionID() if !strings.HasPrefix(id, "session-") { t.Fatalf("id %q missing 'session-' prefix", id) } if _, dup := seen[id]; dup { t.Fatalf("duplicate id %q at iteration %d", id, i) } seen[id] = struct{}{} } if len(seen) != n { t.Errorf("got %d unique ids, want %d", len(seen), n) } } // TestNewAutoSessionID_ConcurrentUnique verifies the auto id // generator is safe for parallel callers (NewSession may be called // from multiple goroutines orchestrating concurrent sub-agents). // // TestNewAutoSessionID_ConcurrentUnique 验证 id 生成器并发安全 // (NewSession 可能从并发 goroutine 调度 sub-agent). func TestNewAutoSessionID_ConcurrentUnique(t *testing.T) { const goroutines = 50 const perG = 50 var ( mu sync.Mutex seen = make(map[string]struct{}, goroutines*perG) wg sync.WaitGroup ) wg.Add(goroutines) for g := 0; g < goroutines; g++ { go func() { defer wg.Done() local := make([]string, 0, perG) for i := 0; i < perG; i++ { local = append(local, newAutoSessionID()) } mu.Lock() for _, id := range local { if _, dup := seen[id]; dup { t.Errorf("duplicate id %q across goroutines", id) } seen[id] = struct{}{} } mu.Unlock() }() } wg.Wait() if len(seen) != goroutines*perG { t.Errorf("got %d unique ids, want %d", len(seen), goroutines*perG) } } // TestEngine_NewSession_AndSessionGetOrCreate verifies the two // methods integrate correctly: NewSession returns a session whose ID // can later be passed to Session() to get the same instance back // (same in-memory pointer), while two NewSession calls return // distinct sessions. // // TestEngine_NewSession_AndSessionGetOrCreate 验证两方法协作: NewSession // 返回的 session.ID() 后续传给 Session() 拿回同实例 (同指针), // NewSession 多次调返不同会话. func TestEngine_NewSession_AndSessionGetOrCreate(t *testing.T) { eng, err := New(testConfig()) if err != nil { t.Fatalf("New: %v", err) } defer eng.Close() s1 := eng.NewSession() s2 := eng.NewSession() if s1 == s2 { t.Fatal("NewSession returned same pointer on two calls; expected distinct sessions") } if s1.ID() == s2.ID() { t.Errorf("NewSession returned same id %q twice", s1.ID()) } got := eng.Session(s1.ID()) if got != s1 { t.Errorf("Session(s1.ID()) returned different pointer; expected get-or-create to return original session") } } // TestEngine_Session_SameIDReturnsSameInstance verifies Session(id) // is get-or-create across multiple calls with the same id. // // TestEngine_Session_SameIDReturnsSameInstance 验证 Session(id) 是 // get-or-create. func TestEngine_Session_SameIDReturnsSameInstance(t *testing.T) { eng, err := New(testConfig()) if err != nil { t.Fatalf("New: %v", err) } defer eng.Close() s1 := eng.Session("my-conv-id") s2 := eng.Session("my-conv-id") if s1 != s2 { t.Errorf("Session(\"my-conv-id\") returned distinct pointers on second call; expected same instance") } if s1.ID() != "my-conv-id" { t.Errorf("Session.ID() = %q, want %q", s1.ID(), "my-conv-id") } }