Skip to content

Commit 0b2dd04

Browse files
committed
add: confirm_test; upd: renderer mock
1 parent bf8b280 commit 0b2dd04

File tree

3 files changed

+328
-2
lines changed

3 files changed

+328
-2
lines changed

internal/generator/cli/confirm/confirm.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ import (
1313
"github.com/tarantool/sdvg/internal/generator/cli/utils"
1414
)
1515

16+
var ErrPromptFailed = errors.New("prompt failed")
17+
1618
// Confirm asks user a yes/no question. Returns true for “yes”.
1719
type Confirm func(ctx context.Context, question string) (bool, error)
1820

@@ -53,7 +55,7 @@ func BuildConfirmTTY(in io.Reader, out io.Writer) func(ctx context.Context, ques
5355
}
5456

5557
if err != nil {
56-
return false, errors.WithMessage(err, "confirm prompt failed")
58+
return false, fmt.Errorf("%w: %v", ErrPromptFailed, err)
5759
}
5860

5961
return strings.Contains("Yy", input), nil
Lines changed: 251 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,251 @@
1+
package confirm
2+
3+
import (
4+
"bytes"
5+
"context"
6+
"errors"
7+
"fmt"
8+
"sync/atomic"
9+
"testing"
10+
"time"
11+
12+
"github.com/stretchr/testify/require"
13+
rendererMock "github.com/tarantool/sdvg/internal/generator/cli/render/mock"
14+
)
15+
16+
func TestConfirmTTY(t *testing.T) {
17+
input := bytes.Buffer{}
18+
output := bytes.Buffer{}
19+
20+
confirm := BuildConfirmTTY(&input, &output)
21+
22+
testCases := []struct {
23+
name string
24+
ctx context.Context
25+
question string
26+
input string
27+
expected bool
28+
expectedErr error
29+
}{
30+
{
31+
name: "Y",
32+
question: "question",
33+
input: "Y",
34+
expected: true,
35+
},
36+
{
37+
name: "y",
38+
question: "question",
39+
input: "y",
40+
expected: true,
41+
},
42+
{
43+
name: "yes",
44+
question: "question",
45+
input: "yes",
46+
expectedErr: ErrPromptFailed,
47+
},
48+
{
49+
name: "N",
50+
question: "question",
51+
input: "N",
52+
expected: false,
53+
},
54+
{
55+
name: "n",
56+
question: "question",
57+
input: "n",
58+
expected: false,
59+
},
60+
{
61+
name: "no",
62+
question: "question",
63+
input: "no",
64+
expectedErr: ErrPromptFailed,
65+
},
66+
{
67+
name: "Context canceled",
68+
expectedErr: context.Canceled,
69+
},
70+
}
71+
72+
for _, tc := range testCases {
73+
t.Run(tc.name, func(t *testing.T) {
74+
ctx := context.Background()
75+
76+
if errors.Is(tc.expectedErr, context.Canceled) {
77+
var cancel context.CancelFunc
78+
79+
ctx, cancel = context.WithCancel(ctx)
80+
cancel()
81+
}
82+
83+
input.WriteString(tc.input + "\n")
84+
85+
res, err := confirm(ctx, tc.question)
86+
require.True(t, errors.Is(err, tc.expectedErr), fmt.Sprintf("expected: %v, got: %v", tc.expectedErr, err))
87+
88+
require.Equal(t, tc.expected, res)
89+
90+
input.Reset()
91+
output.Reset()
92+
})
93+
}
94+
}
95+
96+
var errMockTest = errors.New("mock test error")
97+
98+
func TestConfirmNoTTY(t *testing.T) {
99+
output := bytes.Buffer{}
100+
101+
isUpdatePaused := atomic.Bool{}
102+
103+
testCases := []struct {
104+
name string
105+
ctx context.Context
106+
question string
107+
ch chan time.Time
108+
expected bool
109+
expectedErr error
110+
mockFunc func(r *rendererMock.Renderer)
111+
}{
112+
{
113+
name: "Y",
114+
question: "question",
115+
expected: true,
116+
mockFunc: func(r *rendererMock.Renderer) {
117+
r.On("ReadLine").
118+
Return("Y"+"\n", nil)
119+
120+
r.
121+
On("IsTerminal").
122+
Return(true)
123+
},
124+
},
125+
{
126+
name: "y",
127+
question: "question",
128+
expected: true,
129+
mockFunc: func(r *rendererMock.Renderer) {
130+
r.On("ReadLine").
131+
Return("y"+"\n", nil)
132+
133+
r.
134+
On("IsTerminal").
135+
Return(true)
136+
},
137+
},
138+
{
139+
name: "yes",
140+
question: "question",
141+
mockFunc: func(r *rendererMock.Renderer) {
142+
r.On("ReadLine").
143+
Return("yes"+"\n", errMockTest)
144+
},
145+
expectedErr: errMockTest,
146+
},
147+
{
148+
name: "N",
149+
question: "question",
150+
mockFunc: func(r *rendererMock.Renderer) {
151+
r.On("ReadLine").
152+
Return("N"+"\n", nil)
153+
154+
r.
155+
On("IsTerminal").
156+
Return(true)
157+
},
158+
expected: false,
159+
},
160+
{
161+
name: "n",
162+
question: "question",
163+
mockFunc: func(r *rendererMock.Renderer) {
164+
r.On("ReadLine").
165+
Return("n"+"\n", nil)
166+
167+
r.
168+
On("IsTerminal").
169+
Return(true)
170+
},
171+
expected: false,
172+
},
173+
{
174+
name: "no",
175+
question: "question",
176+
mockFunc: func(r *rendererMock.Renderer) {
177+
r.On("ReadLine").
178+
Return("no"+"\n", errMockTest)
179+
},
180+
expectedErr: errMockTest,
181+
},
182+
{
183+
name: "Context canceled",
184+
mockFunc: func(r *rendererMock.Renderer) {
185+
r.On("ReadLine").
186+
Return("", nil).Maybe()
187+
},
188+
expectedErr: context.Canceled,
189+
},
190+
}
191+
192+
for _, tc := range testCases {
193+
t.Run(tc.name, func(t *testing.T) {
194+
r := rendererMock.NewRenderer(t)
195+
196+
tc.mockFunc(r)
197+
198+
confirm := BuildConfirmNoTTY(r, &output, &isUpdatePaused)
199+
200+
ctx := context.Background()
201+
202+
if errors.Is(tc.expectedErr, context.Canceled) {
203+
var cancel context.CancelFunc
204+
205+
ctx, cancel = context.WithCancel(ctx)
206+
cancel()
207+
}
208+
209+
res, err := confirm(ctx, tc.question)
210+
require.True(t, errors.Is(err, tc.expectedErr), fmt.Sprintf("expected: %v, got: %v", tc.expectedErr, err))
211+
212+
require.Equal(t, tc.expected, res)
213+
214+
output.Reset()
215+
})
216+
}
217+
}
218+
219+
func TestConfirmNoTTY_IsUpdatePaused(t *testing.T) {
220+
output := bytes.Buffer{}
221+
222+
isUpdatePaused := atomic.Bool{}
223+
224+
r := rendererMock.NewRenderer(t)
225+
226+
confirm := BuildConfirmNoTTY(r, &output, &isUpdatePaused)
227+
228+
mockFunc := func(r *rendererMock.Renderer, ch chan time.Time) {
229+
r.On("ReadLine").WaitUntil(ch).
230+
Return("Y"+"\n", nil)
231+
232+
r.
233+
On("IsTerminal").
234+
Return(true)
235+
}
236+
237+
ch := make(chan time.Time)
238+
239+
mockFunc(r, ch)
240+
241+
go confirm(context.Background(), "")
242+
243+
start := time.Now()
244+
ch <- start
245+
246+
for isUpdatePaused.Load() {
247+
if time.Now().Sub(start) > 2*time.Second {
248+
t.Fatal("isUpdatePaused has not been called")
249+
}
250+
}
251+
}

internal/generator/cli/render/mock/renderer.go

Lines changed: 74 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)