Skip to content

Commit ac02a23

Browse files
committed
add: racy test confirm in different file; upd: output writers test, generator output config tests
1 parent 0493483 commit ac02a23

File tree

11 files changed

+294
-126
lines changed

11 files changed

+294
-126
lines changed

internal/generator/cli/commands/generate/generate_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -256,7 +256,7 @@ func TestNewGenerateCommand(t *testing.T) {
256256
cliOpts.SetOut(streams.NewOut(os.Stdout))
257257

258258
cmd := NewGenerateCommand(cliOpts)
259-
cmd.SetArgs([]string{"-F"})
259+
cmd.SetArgs([]string{"-f"})
260260

261261
err = cmd.Execute()
262262

internal/generator/cli/confirm/confirm.go

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,10 +22,13 @@ func BuildConfirmTTY(in io.Reader, out io.Writer) func(ctx context.Context, ques
2222
return func(ctx context.Context, question string) (bool, error) {
2323
fmt.Fprintln(out)
2424

25+
cancelableIn := newCancelableReader(in)
26+
defer cancelableIn.Close()
27+
2528
prompt := promptui.Prompt{
2629
Label: question + " [y/N]: ",
2730
Default: "y",
28-
Stdin: utils.DummyReadWriteCloser{Reader: in},
31+
Stdin: cancelableIn,
2932
Stdout: utils.DummyReadWriteCloser{Writer: out},
3033
}
3134
validate := func(s string) error {
Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
//go:build !race
2+
3+
package confirm
4+
5+
import (
6+
"bytes"
7+
"context"
8+
"errors"
9+
"fmt"
10+
"testing"
11+
12+
"github.com/stretchr/testify/require"
13+
)
14+
15+
func TestConfirmTTY(t *testing.T) {
16+
testCases := []struct {
17+
name string
18+
ctx context.Context
19+
question string
20+
input string
21+
expected bool
22+
expectedErr error
23+
}{
24+
{
25+
name: "Y",
26+
question: "question",
27+
input: "Y",
28+
expected: true,
29+
},
30+
{
31+
name: "y",
32+
question: "question",
33+
input: "y",
34+
expected: true,
35+
},
36+
{
37+
name: "yes",
38+
question: "question",
39+
input: "yes",
40+
expectedErr: ErrPromptFailed,
41+
},
42+
{
43+
name: "N",
44+
question: "question",
45+
input: "N",
46+
expected: false,
47+
},
48+
{
49+
name: "n",
50+
question: "question",
51+
input: "n",
52+
expected: false,
53+
},
54+
{
55+
name: "no",
56+
question: "question",
57+
input: "no",
58+
expectedErr: ErrPromptFailed,
59+
},
60+
{
61+
name: "Context canceled",
62+
expectedErr: context.Canceled,
63+
},
64+
}
65+
66+
for _, tc := range testCases {
67+
t.Run(tc.name, func(t *testing.T) {
68+
input := bytes.Buffer{}
69+
output := bytes.Buffer{}
70+
71+
confirm := BuildConfirmTTY(&input, &output)
72+
73+
ctx := context.Background()
74+
75+
if errors.Is(tc.expectedErr, context.Canceled) {
76+
var cancel context.CancelFunc
77+
78+
ctx, cancel = context.WithCancel(ctx)
79+
cancel()
80+
}
81+
82+
input.WriteString(tc.input + "\n")
83+
84+
res, err := confirm(ctx, tc.question)
85+
require.True(t, errors.Is(err, tc.expectedErr), fmt.Sprintf("expected: %v, got: %v", tc.expectedErr, err))
86+
87+
require.Equal(t, tc.expected, res)
88+
})
89+
}
90+
}

internal/generator/cli/confirm/confirm_test.go

Lines changed: 3 additions & 87 deletions
Original file line numberDiff line numberDiff line change
@@ -13,93 +13,9 @@ import (
1313
rendererMock "github.com/tarantool/sdvg/internal/generator/cli/render/mock"
1414
)
1515

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-
9616
var errMockTest = errors.New("mock test error")
9717

9818
func TestConfirmNoTTY(t *testing.T) {
99-
output := bytes.Buffer{}
100-
101-
isUpdatePaused := atomic.Bool{}
102-
10319
testCases := []struct {
10420
name string
10521
ctx context.Context
@@ -192,9 +108,11 @@ func TestConfirmNoTTY(t *testing.T) {
192108
for _, tc := range testCases {
193109
t.Run(tc.name, func(t *testing.T) {
194110
r := rendererMock.NewRenderer(t)
195-
196111
tc.mockFunc(r)
197112

113+
output := bytes.Buffer{}
114+
isUpdatePaused := atomic.Bool{}
115+
198116
confirm := BuildConfirmNoTTY(r, &output, &isUpdatePaused)
199117

200118
ctx := context.Background()
@@ -210,8 +128,6 @@ func TestConfirmNoTTY(t *testing.T) {
210128
require.True(t, errors.Is(err, tc.expectedErr), fmt.Sprintf("expected: %v, got: %v", tc.expectedErr, err))
211129

212130
require.Equal(t, tc.expected, res)
213-
214-
output.Reset()
215131
})
216132
}
217133
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
package confirm
2+
3+
import "io"
4+
5+
// cancelableReader wraps an io.Reader and can be closed to make future reads fail.
6+
type cancelableReader struct {
7+
r io.Reader
8+
closed chan struct{}
9+
}
10+
11+
// newCancelableReader creates a ReadCloser from an io.Reader.
12+
// Closing it will make subsequent Read() calls return io.EOF.
13+
func newCancelableReader(r io.Reader) io.ReadCloser {
14+
return &cancelableReader{
15+
r: r,
16+
closed: make(chan struct{}),
17+
}
18+
}
19+
20+
func (c *cancelableReader) Read(p []byte) (int, error) {
21+
select {
22+
case <-c.closed:
23+
return 0, io.EOF
24+
default:
25+
return c.r.Read(p)
26+
}
27+
}
28+
29+
func (c *cancelableReader) Close() error {
30+
select {
31+
case <-c.closed:
32+
// already closed
33+
default:
34+
close(c.closed)
35+
}
36+
return nil
37+
}

0 commit comments

Comments
 (0)