1
1
package httptest
2
2
3
3
import (
4
+ "bytes"
4
5
"errors"
5
6
"fmt"
7
+ "io"
6
8
"net"
7
9
"net/http"
8
- "regexp "
10
+ "net/url "
9
11
"sync"
12
+
13
+ "github.com/gin-gonic/gin"
10
14
)
11
15
12
16
// Server is a mock http server for testing.
13
17
type Server struct {
14
18
httpServer * http.Server
15
- // calls store map[method][path]count
16
- calls map [string ]map [string ]int
19
+ engine * gin.Engine
20
+ // nCalls store map[method][path]count
21
+ nCalls map [string ]map [string ]int
17
22
// routes store map[method][path]handler
18
23
routes map [string ]map [string ]ServerHandlerFunc
24
+ calls map [string ]map [string ][]RequestMade
19
25
20
26
mu sync.Mutex
21
27
}
@@ -25,6 +31,13 @@ type Request struct {
25
31
Params Params
26
32
}
27
33
34
+ type RequestMade struct {
35
+ Body []byte
36
+ Headers http.Header
37
+ Query url.Values
38
+ Params map [string ]string
39
+ }
40
+
28
41
// ServerHandlerFunc is the interface of the handler function.
29
42
type ServerHandlerFunc func (w ResponseWriter , r * Request )
30
43
@@ -35,20 +48,28 @@ type ServerConfig struct {
35
48
// NewServer creates and starts new http test server.
36
49
// address is the address to listen on, e.g. "localhost:3001".
37
50
func NewServer (address string , config ServerConfig ) (* Server , error ) {
51
+ // Set gin to release mode to avoid unnecessary logs.
52
+ gin .SetMode (gin .ReleaseMode )
53
+
54
+ // Start listener first to make sure the address is available.
38
55
l , err := net .Listen ("tcp" , address )
39
56
if err != nil {
40
- panic ( err )
57
+ return nil , fmt . Errorf ( "net.Listen: %w" , err )
41
58
}
42
59
60
+ r := gin .Default ()
61
+
43
62
httpServer := & http.Server {
44
63
Addr : address ,
45
- Handler : http . NewServeMux (),
64
+ Handler : r . Handler (),
46
65
}
47
66
48
67
server := & Server {
68
+ engine : r ,
49
69
httpServer : httpServer ,
50
- calls : map [string ]map [string ]int {},
70
+ nCalls : map [string ]map [string ]int {},
51
71
routes : map [string ]map [string ]ServerHandlerFunc {},
72
+ calls : map [string ]map [string ][]RequestMade {},
52
73
}
53
74
54
75
go func () {
@@ -65,24 +86,43 @@ func (s *Server) Close() error {
65
86
return s .httpServer .Close ()
66
87
}
67
88
68
- // GetNCalls returns the number of calls for a path.
89
+ // GetNCalls returns the number of nCalls for a path.
69
90
func (s * Server ) GetNCalls (method , path string ) int {
70
- calls , ok := s.calls [method ][path ]
91
+ calls , ok := s.nCalls [method ][path ]
71
92
if ! ok {
72
93
return 0
73
94
}
74
95
75
96
return calls
76
97
}
77
98
78
- // ResetNCalls resets the number of calls for all paths.
99
+ // ResetNCalls resets the number of nCalls for all paths.
79
100
func (s * Server ) ResetNCalls () {
80
101
s .mu .Lock ()
81
102
defer s .mu .Unlock ()
82
103
104
+ for path := range s .nCalls {
105
+ for method := range s .nCalls [path ] {
106
+ s.nCalls [path ][method ] = 0
107
+ }
108
+ }
109
+ }
110
+
111
+ // GetCalls returns the calls for a path.
112
+ func (s * Server ) GetCalls (method , path string ) []RequestMade {
113
+ return s.calls [method ][path ]
114
+ }
115
+
116
+ // ResetCalls resets the calls & nCalls for all paths.
117
+ // It does not reset the handlers.
118
+ func (s * Server ) ResetCalls () {
119
+ s .mu .Lock ()
120
+ defer s .mu .Unlock ()
121
+
122
+ s .ResetNCalls ()
83
123
for path := range s .calls {
84
124
for method := range s .calls [path ] {
85
- s.calls [path ][method ] = 0
125
+ s.calls [path ][method ] = [] RequestMade {}
86
126
}
87
127
}
88
128
}
@@ -93,69 +133,92 @@ func (s *Server) RegisterHandler(method string, path string, handler ServerHandl
93
133
s .mu .Lock ()
94
134
defer s .mu .Unlock ()
95
135
96
- if s .calls [method ] == nil {
97
- s .calls [method ] = map [string ]int {}
136
+ if s .nCalls [method ] == nil {
137
+ s .nCalls [method ] = map [string ]int {}
98
138
}
99
139
if s .routes [method ] == nil {
100
140
s .routes [method ] = map [string ]ServerHandlerFunc {}
101
141
}
142
+ if s .calls [method ] == nil {
143
+ s .calls [method ] = map [string ][]RequestMade {}
144
+ }
102
145
103
146
if s .routes [method ] != nil && s.routes [method ][path ] != nil {
104
147
// Regenerate the handler and re-register all handlers.
105
- serveMux := http . NewServeMux ()
148
+ s . engine = gin . Default ()
106
149
for m , p := range s .routes {
107
150
for k , v := range p {
151
+ // Skip same one, we'll register it below.
108
152
if m == method && k == path {
109
153
continue
110
154
}
111
- serveMux .HandleFunc (k , func (w http.ResponseWriter , r * http.Request ) {
112
- s .IncrNCalls (m , k )
113
- v (ResponseWriter {w : w }, & Request {Request : r , Params : Params {request : r }})
155
+ s .engine .Handle (m , k , func (c * gin.Context ) {
156
+ s .incrNCalls (m , k )
157
+ s .storeCall (m , k , c )
158
+ v (ResponseWriter {w : c .Writer }, & Request {Request : c .Request , Params : Params {ginContext : c }})
114
159
})
115
160
}
116
161
}
117
- s .httpServer .Handler = serveMux
162
+ s .httpServer .Handler = s . engine . Handler ()
118
163
}
119
164
s.routes [method ][path ] = handler
120
165
121
- serveMux := s . httpServer . Handler .( * http. ServeMux )
122
- serveMux . HandleFunc ( s . combinePath (method , path ), func ( w http. ResponseWriter , r * http. Request ) {
123
- s .IncrNCalls (method , path )
124
- handler (ResponseWriter {w : w }, & Request {Request : r , Params : Params {request : r }})
166
+ s . engine . Handle ( method , path , func ( c * gin. Context ) {
167
+ s . incrNCalls (method , path )
168
+ s .storeCall (method , path , c )
169
+ handler (ResponseWriter {w : c . Writer }, & Request {Request : c . Request , Params : Params {ginContext : c }})
125
170
})
126
171
127
- s .httpServer .Handler = serveMux
172
+ s .httpServer .Handler = s . engine . Handler ()
128
173
}
129
174
130
- // IncrNCalls increments the number of calls for a path.
131
- func (s * Server ) IncrNCalls (method , path string ) {
175
+ // incrNCalls increments the number of nCalls for a path.
176
+ func (s * Server ) incrNCalls (method , path string ) {
132
177
s .mu .Lock ()
133
178
defer s .mu .Unlock ()
134
179
135
- s.calls [method ][path ]++
180
+ s.nCalls [method ][path ]++
136
181
}
137
182
138
- // ResetAll resets all the calls and handlers .
139
- func (s * Server ) ResetAll ( ) {
183
+ // storeCall stores the call for a path .
184
+ func (s * Server ) storeCall ( method , path string , c * gin. Context ) {
140
185
s .mu .Lock ()
141
186
defer s .mu .Unlock ()
142
187
143
- s .httpServer .Handler = http .NewServeMux ()
144
- s .calls = map [string ]map [string ]int {}
145
- s .routes = map [string ]map [string ]ServerHandlerFunc {}
188
+ // If body is not empty, read it into byte.
189
+ var body []byte
190
+ if c .Request .Body != nil {
191
+ body , _ = io .ReadAll (c .Request .Body )
192
+ // Restore the body.
193
+ c .Request .Body = io .NopCloser (bytes .NewBuffer (body ))
194
+ }
195
+
196
+ s.calls [method ][path ] = append (s.calls [method ][path ], RequestMade {
197
+ Body : body ,
198
+ Headers : c .Request .Header ,
199
+ Query : c .Request .URL .Query (),
200
+ Params : s .getAllParams (c ),
201
+ })
146
202
}
147
203
148
- // combinePath combines method and path & converts the path.
149
- // Example: combinePath(GET, /path/:param) -> GET /path/{param}
150
- func (s * Server ) combinePath (method , path string ) string {
151
- return fmt .Sprintf ("%s %s" , method , s .convertPathParams (path ))
204
+ func (s * Server ) getAllParams (c * gin.Context ) map [string ]string {
205
+ params := make (map [string ]string )
206
+
207
+ for _ , param := range c .Params {
208
+ params [param .Key ] = param .Value
209
+ }
210
+
211
+ return params
152
212
}
153
213
154
- // convertPathParams converts from "/path/:param" to "/path/{param}" to comply with the standard library.
155
- func (s * Server ) convertPathParams (input string ) string {
156
- pattern := `:([^/]+)`
157
- re := regexp .MustCompile (pattern )
158
- converted := re .ReplaceAllString (input , "{$1}" )
214
+ // ResetAll resets all the nCalls, handlers, and calls.
215
+ func (s * Server ) ResetAll () {
216
+ s .mu .Lock ()
217
+ defer s .mu .Unlock ()
159
218
160
- return converted
219
+ s .engine = gin .Default ()
220
+ s .httpServer .Handler = s .engine .Handler ()
221
+ s .nCalls = map [string ]map [string ]int {}
222
+ s .routes = map [string ]map [string ]ServerHandlerFunc {}
223
+ s .calls = map [string ]map [string ][]RequestMade {}
161
224
}
0 commit comments