Skip to content

Commit b035dc1

Browse files
committed
fix: preserve response on error in middlewares and clean up linter issues
- fix cache and repeater middlewares to return response with error per http.RoundTripper contract - fix wrapcheck linter issues by properly wrapping errors - fix testifylint issues by using proper assertion methods - update golangci-lint configuration for cleaner exclusion rules - fix unused parameter warnings in test files
1 parent 4534c14 commit b035dc1

File tree

17 files changed

+170
-176
lines changed

17 files changed

+170
-176
lines changed

.github/workflows/ci.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ jobs:
3131
TZ: "America/Chicago"
3232

3333
- name: golangci-lint
34-
uses: golangci/golangci-lint-action@v3
34+
uses: golangci/golangci-lint-action@v7
3535
with:
3636
version: latest
3737

.golangci.yml

Lines changed: 76 additions & 96 deletions
Original file line numberDiff line numberDiff line change
@@ -1,104 +1,84 @@
1-
linters-settings:
2-
govet:
3-
shadow: true
4-
golint:
5-
min-confidence: 0.6
6-
gocyclo:
7-
min-complexity: 15
8-
maligned:
9-
suggest-new: true
10-
dupl:
11-
threshold: 100
12-
goconst:
13-
min-len: 2
14-
min-occurrences: 2
15-
misspell:
16-
locale: US
17-
lll:
18-
line-length: 140
19-
gocritic:
20-
enabled-tags:
21-
- performance
22-
- style
23-
- experimental
24-
disabled-checks:
25-
- wrapperFunc
26-
- hugeParam
27-
- rangeValCopy
28-
1+
version: "2"
2+
run:
3+
concurrency: 4
294
linters:
30-
disable-all: true
5+
default: none
316
enable:
32-
- revive
33-
- govet
34-
- unconvert
35-
- gosec
36-
- unparam
37-
- unused
38-
- typecheck
39-
- ineffassign
40-
- stylecheck
7+
- contextcheck
8+
- decorder
9+
- errorlint
10+
- exptostd
11+
- gochecknoglobals
4112
- gochecknoinits
4213
- gocritic
14+
- gosec
15+
- govet
16+
- ineffassign
4317
- nakedret
44-
- gosimple
18+
- nilerr
4519
- prealloc
46-
- gofmt
47-
48-
fast: false
49-
50-
51-
run:
52-
concurrency: 4
20+
- predeclared
21+
- revive
22+
- staticcheck
23+
- testifylint
24+
- thelper
25+
- unconvert
26+
- unparam
27+
- unused
28+
- nestif
29+
- wrapcheck
30+
settings:
31+
goconst:
32+
min-len: 2
33+
min-occurrences: 2
34+
gocritic:
35+
disabled-checks:
36+
- wrapperFunc
37+
enabled-tags:
38+
- performance
39+
- style
40+
- experimental
41+
gocyclo:
42+
min-complexity: 15
43+
govet:
44+
enable-all: true
45+
disable:
46+
- fieldalignment
47+
lll:
48+
line-length: 140
49+
misspell:
50+
locale: US
5351

54-
issues:
55-
exclude-dirs:
56-
- vendor
57-
exclude-rules:
58-
- text: "should have a package comment, unless it's in another file for this package"
59-
linters:
60-
- golint
61-
- text: "exitAfterDefer:"
62-
linters:
63-
- gocritic
64-
- text: "whyNoLint: include an explanation for nolint directive"
65-
linters:
66-
- gocritic
67-
- text: "go.mongodb.org/mongo-driver/bson/primitive.E"
68-
linters:
69-
- govet
70-
- text: "weak cryptographic primitive"
71-
linters:
72-
- gosec
73-
- text: "integer overflow conversion"
74-
linters:
75-
- gosec
76-
- text: "should have a package comment"
77-
linters:
78-
- revive
79-
- text: "at least one file in a package should have a package comment"
80-
linters:
81-
- stylecheck
82-
- text: "commentedOutCode: may want to remove commented-out code"
83-
linters:
84-
- gocritic
85-
- text: "unnamedResult: consider giving a name to these results"
86-
linters:
87-
- gocritic
88-
- text: "var-naming: don't use an underscore in package name"
89-
linters:
90-
- revive
91-
- text: "should not use underscores in package names"
92-
linters:
93-
- stylecheck
94-
- text: "struct literal uses unkeyed fields"
95-
linters:
96-
- govet
97-
- linters:
98-
- unparam
99-
- unused
100-
- revive
101-
path: _test\.go$
102-
text: "unused-parameter"
103-
exclude-use-default: false
52+
exclusions:
53+
generated: lax
54+
rules:
55+
- linters:
56+
- staticcheck
57+
text: at least one file in a package should have a package comment
58+
- linters:
59+
- revive
60+
text: should have a package comment
61+
- linters:
62+
- revive
63+
text: 'unused-parameter'
64+
- linters:
65+
- dupl
66+
- gosec
67+
path: _test\.go
68+
paths:
69+
- vendor
70+
- third_party$
71+
- builtin$
72+
- examples$
10473

74+
formatters:
75+
enable:
76+
- gofmt
77+
exclusions:
78+
generated: lax
79+
paths:
80+
- vendor
81+
- third_party$
82+
- builtin$
83+
- examples$
84+
- mocks$

middleware/cache.go

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ type CacheMiddleware struct {
3131
allowedMethods []string
3232

3333
cache map[string]CacheEntry
34-
keys []string // Maintains insertion order
34+
keys []string // maintains insertion order
3535
mu sync.Mutex
3636
}
3737

@@ -46,7 +46,11 @@ func (c *CacheMiddleware) RoundTrip(req *http.Request) (*http.Response, error) {
4646
}
4747
}
4848
if !methodAllowed {
49-
return c.next.RoundTrip(req)
49+
resp, err := c.next.RoundTrip(req)
50+
if err != nil {
51+
return resp, fmt.Errorf("cache: transport error: %w", err)
52+
}
53+
return resp, nil
5054
}
5155

5256
key := c.makeKey(req) // generate cache key based on request
@@ -56,7 +60,7 @@ func (c *CacheMiddleware) RoundTrip(req *http.Request) (*http.Response, error) {
5660
for len(c.keys) > 0 {
5761
oldestKey := c.keys[0]
5862
if time.Since(c.cache[oldestKey].createdAt) < c.ttl {
59-
break // Stop once we find a non-expired entry
63+
break // stop once we find a non-expired entry
6064
}
6165
delete(c.cache, oldestKey)
6266
c.keys = c.keys[1:]
@@ -83,7 +87,7 @@ func (c *CacheMiddleware) RoundTrip(req *http.Request) (*http.Response, error) {
8387
// fetch fresh response
8488
resp, err := c.next.RoundTrip(req)
8589
if err != nil {
86-
return resp, err
90+
return resp, fmt.Errorf("cache: transport error: %w", err)
8791
}
8892

8993
// check if response code is allowed for caching
@@ -94,7 +98,7 @@ func (c *CacheMiddleware) RoundTrip(req *http.Request) (*http.Response, error) {
9498
// read and store response body
9599
body, err := io.ReadAll(resp.Body)
96100
if err != nil {
97-
return resp, err
101+
return resp, fmt.Errorf("cache: failed to read response body: %w", err)
98102
}
99103
_ = resp.Body.Close()
100104
resp.Body = io.NopCloser(bytes.NewReader(body))

middleware/cache/cache.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ func (m *Middleware) Middleware(next http.RoundTripper) http.RoundTripper {
7676
cachedResp, e := m.Get(key, func() (interface{}, error) {
7777
resp, err = next.RoundTrip(req)
7878
if err != nil {
79-
return nil, err
79+
return nil, fmt.Errorf("cache: transport error: %w", err)
8080
}
8181
if resp.Body == nil {
8282
return nil, nil
@@ -101,7 +101,7 @@ func (m *Middleware) extractCacheKey(req *http.Request) (key string, err error)
101101
}
102102
reqBody, e := io.ReadAll(io.LimitReader(req.Body, maxBodySize))
103103
if e != nil {
104-
return "", e
104+
return "", fmt.Errorf("cache: failed to read body: %w", e)
105105
}
106106
_ = req.Body.Close()
107107
req.Body = io.NopCloser(bytes.NewReader(reqBody))

middleware/cache/cache_test.go

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ import (
1919
func Test_extractCacheKey(t *testing.T) {
2020
makeReq := func(method, url string, body io.Reader, headers http.Header) *http.Request {
2121
res, err := http.NewRequest(method, url, body)
22-
require.NoError(t, err)
22+
assert.NoError(t, err)
2323
if headers != nil {
2424
res.Header = headers
2525
}
@@ -104,12 +104,12 @@ func Test_extractCacheKey(t *testing.T) {
104104
c := New(nil, tt.opts...)
105105
c.dbg = true
106106
keyDbg, err := c.extractCacheKey(tt.req)
107-
require.NoError(t, err)
107+
assert.NoError(t, err)
108108
assert.Equal(t, tt.keyDbg, keyDbg)
109109

110110
c.dbg = false
111111
keyHash, err := c.extractCacheKey(tt.req)
112-
require.NoError(t, err)
112+
assert.NoError(t, err)
113113
assert.Equal(t, tt.keyHash, keyHash)
114114

115115
})
@@ -118,7 +118,7 @@ func Test_extractCacheKey(t *testing.T) {
118118
}
119119

120120
func TestMiddleware_Handle(t *testing.T) {
121-
cacheMock := mocks.CacheSvc{GetFunc: func(key string, fn func() (interface{}, error)) (interface{}, error) {
121+
cacheMock := mocks.CacheSvc{GetFunc: func(_ string, fn func() (interface{}, error)) (interface{}, error) {
122122
return fn()
123123
}}
124124
c := New(&cacheMock)
@@ -127,7 +127,7 @@ func TestMiddleware_Handle(t *testing.T) {
127127
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
128128
w.Header().Set("k1", "v1")
129129
_, err := w.Write([]byte("something"))
130-
require.NoError(t, err)
130+
assert.NoError(t, err)
131131
}))
132132

133133
client := http.Client{Transport: c.Middleware(http.DefaultTransport)}
@@ -142,7 +142,7 @@ func TestMiddleware_Handle(t *testing.T) {
142142
require.NoError(t, err)
143143
assert.Equal(t, "something", string(v))
144144
assert.Equal(t, "v1", resp.Header.Get("k1"))
145-
assert.Equal(t, 1, len(cacheMock.GetCalls()))
145+
assert.Len(t, cacheMock.GetCalls(), 1)
146146
assert.Contains(t, cacheMock.GetCalls()[0].Key, "?k=v##GET####")
147147

148148
req, err = http.NewRequest("GET", ts.URL+"?k=v", http.NoBody)
@@ -155,12 +155,12 @@ func TestMiddleware_Handle(t *testing.T) {
155155
v, err = io.ReadAll(resp.Body)
156156
require.NoError(t, err)
157157
assert.Equal(t, "something", string(v))
158-
assert.Equal(t, 2, len(cacheMock.GetCalls()))
158+
assert.Len(t, cacheMock.GetCalls(), 2)
159159
assert.Contains(t, cacheMock.GetCalls()[1].Key, "?k=v##GET####")
160160
}
161161

162162
func TestMiddleware_HandleMethodDisabled(t *testing.T) {
163-
cacheMock := mocks.CacheSvc{GetFunc: func(key string, fn func() (interface{}, error)) (interface{}, error) {
163+
cacheMock := mocks.CacheSvc{GetFunc: func(_ string, fn func() (interface{}, error)) (interface{}, error) {
164164
return fn()
165165
}}
166166
c := New(&cacheMock, Methods("PUT"))
@@ -169,7 +169,7 @@ func TestMiddleware_HandleMethodDisabled(t *testing.T) {
169169
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
170170
w.Header().Set("k1", "v1")
171171
_, err := w.Write([]byte("something"))
172-
require.NoError(t, err)
172+
assert.NoError(t, err)
173173
}))
174174

175175
client := http.Client{Transport: c.Middleware(http.DefaultTransport)}
@@ -179,14 +179,14 @@ func TestMiddleware_HandleMethodDisabled(t *testing.T) {
179179
resp, err := client.Do(req)
180180
require.NoError(t, err)
181181
assert.Equal(t, 200, resp.StatusCode)
182-
assert.Equal(t, 0, len(cacheMock.GetCalls()))
182+
assert.Empty(t, cacheMock.GetCalls())
183183

184184
req, err = http.NewRequest("PUT", ts.URL+"?k=v", http.NoBody)
185185
require.NoError(t, err)
186186
resp, err = client.Do(req)
187187
require.NoError(t, err)
188188
assert.Equal(t, 200, resp.StatusCode)
189-
assert.Equal(t, 1, len(cacheMock.GetCalls()))
189+
assert.Len(t, cacheMock.GetCalls(), 1)
190190
}
191191

192192
func TestMiddleware_EdgeCases(t *testing.T) {
@@ -261,6 +261,6 @@ type errorReader struct {
261261
err error
262262
}
263263

264-
func (e *errorReader) Read(p []byte) (n int, err error) {
264+
func (e *errorReader) Read(_ []byte) (n int, err error) {
265265
return 0, e.err
266266
}

middleware/cache_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -155,8 +155,8 @@ func TestCache_Options(t *testing.T) {
155155

156156
// second request - should be cached
157157
req2, _ := http.NewRequest(http.MethodGet, "http://example.com/2", http.NoBody)
158-
_, _ = h.RoundTrip(req2) // First call: should hit the backend
159-
_, _ = h.RoundTrip(req2) // Second call: should be served from cache
158+
_, _ = h.RoundTrip(req2) // first call: should hit the backend
159+
_, _ = h.RoundTrip(req2) // second call: should be served from cache
160160

161161
// third request - triggers eviction of first request
162162
req3, _ := http.NewRequest(http.MethodGet, "http://example.com/3", http.NoBody)

middleware/circuit_breaker_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,5 +35,5 @@ func TestCircuitBreaker(t *testing.T) {
3535
assert.Equal(t, 201, resp.StatusCode)
3636

3737
assert.Equal(t, 1, rmock.Calls())
38-
assert.Equal(t, 1, len(cbMock.ExecuteCalls()))
38+
assert.Len(t, cbMock.ExecuteCalls(), 1)
3939
}

0 commit comments

Comments
 (0)