81 lines
2.5 KiB
Go
81 lines
2.5 KiB
Go
package lifecycle
|
|
|
|
import (
|
|
"errors"
|
|
"testing"
|
|
)
|
|
|
|
// TestExecutionStore_AddRejectsDuplicateSession is the regression test for the
|
|
// process-leak bug where two paths created executions for the same session
|
|
// concurrently and Add silently overwrote the bySession index, orphaning the
|
|
// first execution's agent subprocess.
|
|
func TestExecutionStore_AddRejectsDuplicateSession(t *testing.T) {
|
|
store := NewExecutionStore()
|
|
|
|
first := &AgentExecution{ID: "exec-1", SessionID: "session-1"}
|
|
if err := store.Add(first); err != nil {
|
|
t.Fatalf("first Add: unexpected error: %v", err)
|
|
}
|
|
|
|
second := &AgentExecution{ID: "exec-2", SessionID: "session-1"}
|
|
err := store.Add(second)
|
|
if !errors.Is(err, ErrExecutionAlreadyExistsForSession) {
|
|
t.Fatalf("second Add: want ErrExecutionAlreadyExistsForSession, got %v", err)
|
|
}
|
|
|
|
got, ok := store.GetBySessionID("session-1")
|
|
if !ok {
|
|
t.Fatalf("GetBySessionID: not found")
|
|
}
|
|
if got.ID != "exec-1" {
|
|
t.Errorf("bySession index: want exec-1, got %s (overwrite was supposed to be rejected)", got.ID)
|
|
}
|
|
// Second execution must not be in the executions map either — otherwise
|
|
// it'd live as an unreachable orphan.
|
|
if _, ok := store.Get("exec-2"); ok {
|
|
t.Errorf("Get(exec-2): rejected execution must not be tracked")
|
|
}
|
|
}
|
|
|
|
func TestExecutionStore_AddSameExecutionTwiceIsIdempotent(t *testing.T) {
|
|
store := NewExecutionStore()
|
|
|
|
exec := &AgentExecution{ID: "exec-1", SessionID: "session-1"}
|
|
if err := store.Add(exec); err != nil {
|
|
t.Fatalf("first Add: %v", err)
|
|
}
|
|
if err := store.Add(exec); err != nil {
|
|
t.Errorf("re-adding the SAME execution must be a no-op, got %v", err)
|
|
}
|
|
}
|
|
|
|
func TestExecutionStore_AddReplaceAfterRemove(t *testing.T) {
|
|
store := NewExecutionStore()
|
|
|
|
first := &AgentExecution{ID: "exec-1", SessionID: "session-1"}
|
|
if err := store.Add(first); err != nil {
|
|
t.Fatalf("first Add: %v", err)
|
|
}
|
|
store.Remove("exec-1")
|
|
|
|
second := &AgentExecution{ID: "exec-2", SessionID: "session-1"}
|
|
if err := store.Add(second); err != nil {
|
|
t.Errorf("Add after Remove must succeed, got %v", err)
|
|
}
|
|
got, _ := store.GetBySessionID("session-1")
|
|
if got == nil || got.ID != "exec-2" {
|
|
t.Errorf("after Remove+Add: want exec-2, got %v", got)
|
|
}
|
|
}
|
|
|
|
func TestExecutionStore_AddNoSessionIDAlwaysSucceeds(t *testing.T) {
|
|
store := NewExecutionStore()
|
|
|
|
if err := store.Add(&AgentExecution{ID: "exec-a"}); err != nil {
|
|
t.Errorf("Add without SessionID: %v", err)
|
|
}
|
|
if err := store.Add(&AgentExecution{ID: "exec-b"}); err != nil {
|
|
t.Errorf("Add without SessionID (second): %v", err)
|
|
}
|
|
}
|