package terminal
import (
"context"
"errors"
"os"
"strings"
"sync"
"testing"
"time"
"albedo/cli/internal/daemon"
)
// Real-daemon PTY tests cover approval. This checks cancellation while input is
// blocked, where a leaked reader would otherwise survive the caller's wait.
func TestRestartConfirmationCancelsBlockedInput(t *testing.T) {
input, writer, err := os.Pipe()
if err != nil {
t.Fatal(err)
}
defer input.Close()
defer writer.Close()
output := &promptSignal{ready: make(chan struct{})}
service := Service{In: input, Out: output, Capable: true}
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
result := make(chan error, 1)
go func() {
approved, err := service.ConfirmRestart(ctx, daemon.ConnectionSnapshot{Pid: 42}, "")
if approved {
result <- errors.New("cancellation approved restart")
return
}
result <- err
}()
select {
case <-output.ready:
case err := <-result:
t.Fatalf("confirmation exited before waiting: %v", err)
case <-time.After(5 * time.Second):
t.Fatal("confirmation did not begin reading input")
}
cancel()
select {
case err := <-result:
if !errors.Is(err, context.Canceled) {
t.Fatalf("want canceled confirmation, got %v", err)
}
case <-time.After(5 * time.Second):
t.Fatal("confirmation kept waiting after cancellation")
}
}
type promptSignal struct {
ready chan struct{}
once sync.Once
}
func (s *promptSignal) Write(data []byte) (int, error) {
// Bubble Tea starts the input program with terminal control sequences.
if strings.Contains(string(data), "\x1b") {
s.once.Do(func() { close(s.ready) })
}
return len(data), nil
}