1package confirm
2
3import (
4 "bytes"
5 "context"
6 "errors"
7 "io"
8 "strings"
9 "testing"
10 "time"
11)
12
13func TestConfirmerAcceptsYes(t *testing.T) {
14 var out bytes.Buffer
15 ok, err := NewConfirmer(strings.NewReader("y\n"), &out).Confirm("Kill daemon?")
16 if err != nil {
17 t.Fatalf("Confirm returned error: %v", err)
18 }
19 if !ok {
20 t.Fatal("Confirm returned false, want true")
21 }
22 if got := out.String(); !strings.Contains(got, "Kill daemon?") || !strings.Contains(got, "[y/N]") {
23 t.Fatalf("prompt output = %q, want question and default", got)
24 }
25}
26
27func TestConfirmerDefaultsNo(t *testing.T) {
28 ok, err := NewConfirmer(strings.NewReader("\n"), &bytes.Buffer{}).Confirm("Kill daemon?")
29 if err != nil {
30 t.Fatalf("Confirm returned error: %v", err)
31 }
32 if ok {
33 t.Fatal("Confirm returned true for empty answer, want false")
34 }
35}
36
37func TestConfirmerDefaultsNoOnEOF(t *testing.T) {
38 ok, err := NewConfirmer(strings.NewReader(""), &bytes.Buffer{}).Confirm("Kill daemon?")
39 if err != nil {
40 t.Fatalf("Confirm returned error: %v", err)
41 }
42 if ok {
43 t.Fatal("Confirm returned true for EOF, want false")
44 }
45}
46
47func TestConfirmerRejectsUnknownAnswer(t *testing.T) {
48 ok, err := NewConfirmer(strings.NewReader("later\n"), &bytes.Buffer{}).Confirm("Kill daemon?")
49 if err != nil {
50 t.Fatalf("Confirm returned error: %v", err)
51 }
52 if ok {
53 t.Fatal("Confirm returned true for unknown answer, want false")
54 }
55}
56
57func TestConfirmerReleasesOnCancel(t *testing.T) {
58 reader, writer := io.Pipe()
59 defer func() { _ = reader.Close() }()
60 defer func() { _ = writer.Close() }()
61 ctx, cancel := context.WithCancel(context.Background())
62 defer cancel()
63 wrote := make(chan struct{}, 1)
64 out := writeFunc(func(p []byte) (int, error) {
65 select {
66 case wrote <- struct{}{}:
67 default:
68 }
69 return io.Discard.Write(p)
70 })
71 done := make(chan error, 1)
72 go func() {
73 _, err := NewConfirmer(reader, out).ConfirmContext(ctx, "create?")
74 done <- err
75 }()
76 // Wait until the prompt write lands before cancelling, so the test
77 // exercises release from a blocked read rather than startup.
78 select {
79 case <-wrote:
80 case <-time.After(5 * time.Second):
81 t.Fatal("confirm did not write its prompt")
82 }
83 cancel()
84 select {
85 case err := <-done:
86 if !errors.Is(err, context.Canceled) {
87 t.Fatalf("ConfirmContext error = %v, want context.Canceled", err)
88 }
89 case <-time.After(5 * time.Second):
90 t.Fatal("confirm did not release on context cancellation")
91 }
92}
93
94type writeFunc func([]byte) (int, error)
95
96func (f writeFunc) Write(p []byte) (int, error) { return f(p) }