package devagents import ( "context" "testing" "time" ) // TestAgentStopAndDone verifies that Stop() cancels Run and Done() closes. // Uses a minimal Agent (no Matrix, no LLM) via direct struct init so the test // doesn't require network or external dependencies. func TestAgentStopAndDone(t *testing.T) { a := &Agent{ done: make(chan struct{}), } // Simulate Run: create the cancel, then immediately block on ctx. ctx, cancel := context.WithCancel(context.Background()) a.cancel = cancel started := make(chan struct{}) go func() { close(started) // Mimic what Run does: block on ctx, then close done. <-ctx.Done() close(a.done) }() <-started // Stop must unblock the goroutine above. a.Stop() select { case <-a.Done(): // ok case <-time.After(2 * time.Second): t.Fatal("Done() did not close within 2s after Stop()") } } // TestAgentStopIdempotent verifies that calling Stop() multiple times is safe. func TestAgentStopIdempotent(t *testing.T) { a := &Agent{ done: make(chan struct{}), } _, cancel := context.WithCancel(context.Background()) a.cancel = cancel defer cancel() // Should not panic when called multiple times. a.Stop() a.Stop() a.Stop() } // TestAgentStopNilCancel verifies Stop() is safe when cancel is nil. func TestAgentStopNilCancel(t *testing.T) { a := &Agent{ done: make(chan struct{}), } // cancel is nil — must not panic. a.Stop() }