okami.mom/albedo

git clone https://git.nkp.pet/okami.mom/albedo

dawndaemon: record a content digest of the running build, so the CLI can prove a difference without labels979f217

main
1.6 KiB66 linesraw
1package terminal
2
3import (
4	"context"
5	"errors"
6	"os"
7	"strings"
8	"sync"
9	"testing"
10	"time"
11
12	"albedo/cli/internal/daemon"
13)
14
15// Real-daemon PTY tests cover approval. This checks cancellation while input is
16// blocked, where a leaked reader would otherwise survive the caller's wait.
17func TestRestartConfirmationCancelsBlockedInput(t *testing.T) {
18	input, writer, err := os.Pipe()
19	if err != nil {
20		t.Fatal(err)
21	}
22	defer input.Close()
23	defer writer.Close()
24	output := &promptSignal{ready: make(chan struct{})}
25	service := Service{In: input, Out: output, Capable: true}
26	ctx, cancel := context.WithCancel(context.Background())
27	defer cancel()
28	result := make(chan error, 1)
29	go func() {
30		approved, err := service.ConfirmRestart(ctx, daemon.ConnectionSnapshot{Pid: 42}, daemon.BuildIdentity{})
31		if approved {
32			result <- errors.New("cancellation approved restart")
33			return
34		}
35		result <- err
36	}()
37	select {
38	case <-output.ready:
39	case err := <-result:
40		t.Fatalf("confirmation exited before waiting: %v", err)
41	case <-time.After(5 * time.Second):
42		t.Fatal("confirmation did not begin reading input")
43	}
44	cancel()
45	select {
46	case err := <-result:
47		if !errors.Is(err, context.Canceled) {
48			t.Fatalf("want canceled confirmation, got %v", err)
49		}
50	case <-time.After(5 * time.Second):
51		t.Fatal("confirmation kept waiting after cancellation")
52	}
53}
54
55type promptSignal struct {
56	ready chan struct{}
57	once  sync.Once
58}
59
60func (s *promptSignal) Write(data []byte) (int, error) {
61	// Bubble Tea starts the input program with terminal control sequences.
62	if strings.Contains(string(data), "\x1b") {
63		s.once.Do(func() { close(s.ready) })
64	}
65	return len(data), nil
66}