Skip to content

Commit 26edee0

Browse files
authored
fix: Add tests for empty body with ok status; Fix race (#211)
* chore: Add tests for empty body with ok status * fix: Race in 2016/day01 solution * chore: Don't use cache for regression tests * fix: Handle empty response * chore: Add message to error * fix: Check error if it was not expected
1 parent 57b53ca commit 26edee0

File tree

7 files changed

+190
-133
lines changed

7 files changed

+190
-133
lines changed

internal/command/command.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ func Run(ctx context.Context, year, day string) (puzzles.Result, error) {
2727
Day: day,
2828
}, SessionFromContext(ctx))
2929
if err != nil {
30-
return puzzles.Result{}, err
30+
return puzzles.Result{}, fmt.Errorf("failed to get input for puzzle: %w", err)
3131
}
3232

3333
opts := OptionsFromContext(ctx)

internal/command/command_test.go

Lines changed: 98 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,34 @@ type mockHTTPClient struct {
2323
MockDo dofunc
2424
}
2525

26+
type returnParams struct {
27+
status int
28+
body io.ReadCloser
29+
}
30+
31+
func newMockHTTPClient(p returnParams) *mockHTTPClient {
32+
return &mockHTTPClient{
33+
MockDo: func(req *http.Request) (*http.Response, error) {
34+
return &http.Response{
35+
Status: http.StatusText(p.status),
36+
StatusCode: p.status,
37+
Proto: "HTTP/1.0",
38+
ProtoMajor: 1,
39+
ProtoMinor: 0,
40+
Header: nil,
41+
Body: p.body,
42+
ContentLength: 0,
43+
TransferEncoding: nil,
44+
Close: false,
45+
Uncompressed: false,
46+
Trailer: nil,
47+
Request: nil,
48+
TLS: nil,
49+
}, nil
50+
},
51+
}
52+
}
53+
2654
// Overriding what the Do function should "do" in our MockClient
2755
func (m *mockHTTPClient) Do(req *http.Request) (*http.Response, error) {
2856
return m.MockDo(req)
@@ -89,36 +117,79 @@ func TestRun(t *testing.T) {
89117
puzzles.UnregisterAllSolvers(t)
90118
})
91119

92-
r := io.NopCloser(strings.NewReader("1,2,3"))
120+
type expected struct {
121+
result puzzles.Result
122+
wantErr assert.ErrorAssertionFunc
123+
}
93124

94-
input.Client = &mockHTTPClient{
95-
MockDo: func(req *http.Request) (*http.Response, error) {
96-
return &http.Response{
97-
Status: http.StatusText(http.StatusOK),
98-
StatusCode: http.StatusOK,
99-
Proto: "HTTP/1.0",
100-
ProtoMajor: 1,
101-
ProtoMinor: 0,
102-
Header: nil,
103-
Body: r,
104-
ContentLength: 0,
105-
TransferEncoding: nil,
106-
Close: false,
107-
Uncompressed: false,
108-
Trailer: nil,
109-
Request: nil,
110-
TLS: nil,
111-
}, nil
125+
var tests = []struct {
126+
name string
127+
returnParams returnParams
128+
expected expected
129+
}{
130+
{
131+
name: "",
132+
returnParams: returnParams{
133+
status: http.StatusOK,
134+
body: io.NopCloser(strings.NewReader("1,2,3")),
135+
},
136+
expected: expected{
137+
result: puzzles.Result{
138+
Year: "1992",
139+
Name: "31",
140+
Part1: "2",
141+
Part2: "3",
142+
},
143+
wantErr: assert.NoError,
144+
},
145+
},
146+
{
147+
name: "",
148+
returnParams: returnParams{
149+
status: http.StatusNotFound,
150+
body: http.NoBody,
151+
},
152+
expected: expected{
153+
result: puzzles.Result{},
154+
wantErr: assert.Error,
155+
},
156+
},
157+
{
158+
name: "",
159+
returnParams: returnParams{
160+
status: http.StatusUnauthorized,
161+
body: http.NoBody,
162+
},
163+
expected: expected{
164+
result: puzzles.Result{},
165+
wantErr: assert.Error,
166+
},
167+
},
168+
{
169+
name: "",
170+
returnParams: returnParams{
171+
status: http.StatusOK,
172+
body: http.NoBody,
173+
},
174+
expected: expected{
175+
result: puzzles.Result{},
176+
wantErr: assert.Error,
177+
},
112178
},
113179
}
114180

115-
got, err := command.Run(ctx, year, day)
116-
assert.NoError(t, err)
181+
for i := range tests {
182+
tt := tests[i]
183+
184+
t.Run(tt.name, func(t *testing.T) {
185+
input.Client = newMockHTTPClient(tt.returnParams)
117186

118-
assert.Equal(t, puzzles.Result{
119-
Year: "1992",
120-
Name: "31",
121-
Part1: "2",
122-
Part2: "3",
123-
}, got)
187+
got, err := command.Run(ctx, year, day)
188+
if !tt.expected.wantErr(t, err) {
189+
return
190+
}
191+
192+
assert.Equal(t, tt.expected.result, got)
193+
})
194+
}
124195
}

internal/puzzles/input/content.go

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import (
99
"net/http"
1010
"net/url"
1111
"path"
12+
"strings"
1213
"time"
1314

1415
"github.com/obalunenko/logger"
@@ -73,6 +74,10 @@ func Get(ctx context.Context, d Date, session string) ([]byte, error) {
7374

7475
switch resp.StatusCode {
7576
case http.StatusOK:
77+
if strings.TrimSpace(string(body)) == "" {
78+
return nil, fmt.Errorf("empty response received")
79+
}
80+
7681
return body, nil
7782
case http.StatusNotFound:
7883
return nil, fmt.Errorf("[%s]: %w", d, ErrNotFound)
@@ -101,7 +106,7 @@ func createInputReq(ctx context.Context, d Date, sessionID string) (*http.Reques
101106

102107
req, err := http.NewRequestWithContext(ctx, http.MethodGet, u.String(), http.NoBody)
103108
if err != nil {
104-
return nil, err
109+
return nil, fmt.Errorf("create request: %w", err)
105110
}
106111

107112
req.AddCookie(&http.Cookie{

internal/puzzles/input/content_test.go

Lines changed: 66 additions & 99 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,34 @@ type mockHTTPClient struct {
2222
MockDo dofunc
2323
}
2424

25+
type returnParams struct {
26+
status int
27+
body io.ReadCloser
28+
}
29+
30+
func newMockHTTPClient(p returnParams) *mockHTTPClient {
31+
return &mockHTTPClient{
32+
MockDo: func(req *http.Request) (*http.Response, error) {
33+
return &http.Response{
34+
Status: http.StatusText(p.status),
35+
StatusCode: p.status,
36+
Proto: "HTTP/1.0",
37+
ProtoMajor: 1,
38+
ProtoMinor: 0,
39+
Header: nil,
40+
Body: p.body,
41+
ContentLength: 0,
42+
TransferEncoding: nil,
43+
Close: false,
44+
Uncompressed: false,
45+
Trailer: nil,
46+
Request: nil,
47+
TLS: nil,
48+
}, nil
49+
},
50+
}
51+
}
52+
2553
// Overriding what the Do function should "do" in our MockClient
2654
func (m *mockHTTPClient) Do(req *http.Request) (*http.Response, error) {
2755
return m.MockDo(req)
@@ -54,26 +82,10 @@ func TestGet(t *testing.T) {
5482
{
5583
name: "",
5684
client: client{
57-
ClientDo: &mockHTTPClient{
58-
MockDo: func(req *http.Request) (*http.Response, error) {
59-
return &http.Response{
60-
Status: http.StatusText(http.StatusOK),
61-
StatusCode: http.StatusOK,
62-
Proto: "HTTP/1.0",
63-
ProtoMajor: 1,
64-
ProtoMinor: 0,
65-
Header: nil,
66-
Body: io.NopCloser(strings.NewReader("test")),
67-
ContentLength: 0,
68-
TransferEncoding: nil,
69-
Close: false,
70-
Uncompressed: false,
71-
Trailer: nil,
72-
Request: nil,
73-
TLS: nil,
74-
}, nil
75-
},
76-
},
85+
ClientDo: newMockHTTPClient(returnParams{
86+
status: http.StatusOK,
87+
body: io.NopCloser(strings.NewReader("test")),
88+
}),
7789
},
7890
args: args{
7991
ctx: context.Background(),
@@ -89,26 +101,10 @@ func TestGet(t *testing.T) {
89101
{
90102
name: "",
91103
client: client{
92-
ClientDo: &mockHTTPClient{
93-
MockDo: func(req *http.Request) (*http.Response, error) {
94-
return &http.Response{
95-
Status: http.StatusText(http.StatusNotFound),
96-
StatusCode: http.StatusNotFound,
97-
Proto: "HTTP/1.0",
98-
ProtoMajor: 1,
99-
ProtoMinor: 0,
100-
Header: nil,
101-
Body: http.NoBody,
102-
ContentLength: 0,
103-
TransferEncoding: nil,
104-
Close: false,
105-
Uncompressed: false,
106-
Trailer: nil,
107-
Request: nil,
108-
TLS: nil,
109-
}, nil
110-
},
111-
},
104+
ClientDo: newMockHTTPClient(returnParams{
105+
status: http.StatusOK,
106+
body: io.NopCloser(strings.NewReader("")),
107+
}),
112108
},
113109
args: args{
114110
ctx: context.Background(),
@@ -124,26 +120,10 @@ func TestGet(t *testing.T) {
124120
{
125121
name: "",
126122
client: client{
127-
ClientDo: &mockHTTPClient{
128-
MockDo: func(req *http.Request) (*http.Response, error) {
129-
return &http.Response{
130-
Status: http.StatusText(http.StatusBadRequest),
131-
StatusCode: http.StatusBadRequest,
132-
Proto: "HTTP/1.0",
133-
ProtoMajor: 1,
134-
ProtoMinor: 0,
135-
Header: nil,
136-
Body: io.NopCloser(strings.NewReader("no session")),
137-
ContentLength: 0,
138-
TransferEncoding: nil,
139-
Close: false,
140-
Uncompressed: false,
141-
Trailer: nil,
142-
Request: nil,
143-
TLS: nil,
144-
}, nil
145-
},
146-
},
123+
ClientDo: newMockHTTPClient(returnParams{
124+
status: http.StatusNotFound,
125+
body: http.NoBody,
126+
}),
147127
},
148128
args: args{
149129
ctx: context.Background(),
@@ -159,26 +139,29 @@ func TestGet(t *testing.T) {
159139
{
160140
name: "",
161141
client: client{
162-
ClientDo: &mockHTTPClient{
163-
MockDo: func(req *http.Request) (*http.Response, error) {
164-
return &http.Response{
165-
Status: http.StatusText(http.StatusInternalServerError),
166-
StatusCode: http.StatusInternalServerError,
167-
Proto: "HTTP/1.0",
168-
ProtoMajor: 1,
169-
ProtoMinor: 0,
170-
Header: nil,
171-
Body: io.NopCloser(strings.NewReader("no session")),
172-
ContentLength: 0,
173-
TransferEncoding: nil,
174-
Close: false,
175-
Uncompressed: false,
176-
Trailer: nil,
177-
Request: nil,
178-
TLS: nil,
179-
}, nil
180-
},
142+
ClientDo: newMockHTTPClient(returnParams{
143+
status: http.StatusBadRequest,
144+
body: io.NopCloser(strings.NewReader("no session")),
145+
}),
146+
},
147+
args: args{
148+
ctx: context.Background(),
149+
d: input.Date{
150+
Year: "2021",
151+
Day: "25",
181152
},
153+
session: "123",
154+
},
155+
want: nil,
156+
wantErr: assert.Error,
157+
},
158+
{
159+
name: "",
160+
client: client{
161+
ClientDo: newMockHTTPClient(returnParams{
162+
status: http.StatusInternalServerError,
163+
body: io.NopCloser(strings.NewReader("no session")),
164+
}),
182165
},
183166
args: args{
184167
ctx: context.Background(),
@@ -214,26 +197,10 @@ func TestGet(t *testing.T) {
214197
{
215198
name: "",
216199
client: client{
217-
ClientDo: &mockHTTPClient{
218-
MockDo: func(req *http.Request) (*http.Response, error) {
219-
return &http.Response{
220-
Status: http.StatusText(http.StatusOK),
221-
StatusCode: http.StatusOK,
222-
Proto: "HTTP/1.0",
223-
ProtoMajor: 1,
224-
ProtoMinor: 0,
225-
Header: nil,
226-
Body: io.NopCloser(iotest.ErrReader(errors.New("custom error"))),
227-
ContentLength: 0,
228-
TransferEncoding: nil,
229-
Close: false,
230-
Uncompressed: false,
231-
Trailer: nil,
232-
Request: nil,
233-
TLS: nil,
234-
}, nil
235-
},
236-
},
200+
ClientDo: newMockHTTPClient(returnParams{
201+
status: http.StatusOK,
202+
body: io.NopCloser(iotest.ErrReader(errors.New("custom error"))),
203+
}),
237204
},
238205
args: args{
239206
ctx: context.Background(),

0 commit comments

Comments
 (0)