-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathtest_request.go
More file actions
121 lines (104 loc) · 3.46 KB
/
Copy pathtest_request.go
File metadata and controls
121 lines (104 loc) · 3.46 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
package routeit
import (
"crypto/tls"
"fmt"
"strings"
"testing"
"github.com/sktylr/routeit/internal/headers"
)
// The [TestRequestOptions] allow you to specify certain traits the request
// will have under test. You do not need to use any of the fields, though it
// can be helpful to set certain state on the request, such as a specific
// header, if the unit under test relies on the state to perform an action.
type TestRequestOptions struct {
// The raw body of the request. This must be marshalled by the user of
// [TestRequestOptions] and the corresponding Content-Type header should be
// included in [TestRequestOptions.Headers] if the handler or middleware
// uses safe body loads. Additionally, the Content-Length header should
// also be provided.
Body []byte
// The headers of the request, if any. These should be specific as key,
// value pairs in order. For example:
// []string{"Authorization", "Bearer foo", "Content-Type": "text/plain"}
Headers []string
// The Ip address of the client. This may be useful for security middleware
Ip string
// Specific path parameters that the URI should contain and extract.
// Currently this needs to be explicitly defined since unit tests do not go
// through the entire handling flow, meaning requests are not routed
// properly so path parameters are not extracted.
PathParams map[string]string
// The TLS connection state. Use this if the middleware or handler under
// test is TLS aware (e.g. middleware that may block clients if they are
// not using TLS)
TlsConnectionState *tls.ConnectionState
}
// The [TestRequest] object can be used when unit testing specific components
// of a routeit application, such as middleware. It should be constructed
// using [NewTestRequest] and provides ways to verify behaviour that happens to
// a request, for example if a context value was added properly.
type TestRequest struct {
req *Request
}
type testRequest struct {
path string
headers headers.Headers
method HttpMethod
body []byte
}
// This will create a new test request object that can be used in tests, for
// example when unit testing middleware.
func NewTestRequest(t testing.TB, path string, m HttpMethod, opts TestRequestOptions) *TestRequest {
if !strings.HasPrefix(path, "/") {
path = "/" + path
}
uri, err := parseUri(path)
if err != nil {
panic(fmt.Errorf(`failed to construct uri from path %#q: %+v`, path, err))
}
if opts.PathParams != nil {
uri.pathParams = opts.PathParams
}
headers := &RequestHeaders{constructTestHeaders(opts.Headers...)}
req := &Request{
ctx: t.Context(),
mthd: m,
uri: *uri,
headers: headers,
body: opts.Body,
ip: opts.Ip,
accept: parseAcceptHeader(headers),
tlsState: opts.TlsConnectionState,
}
if host, hasHost := headers.First("Host"); hasHost {
req.host = host
} else {
req.host = "localhost:8080"
}
if ct, hasCt := headers.First("Content-Type"); hasCt {
req.ct = parseContentType(ct)
}
if req.ip == "" {
req.ip = "127.0.0.1"
}
if req.userAgent == "" {
req.userAgent = "routeit-test"
}
return &TestRequest{req: req}
}
func (tr *TestRequest) NewContextValue(key string, val any) {
tr.req.NewContextValue(key, val)
}
func (tr *TestRequest) ContextValue(key string) (any, bool) {
return tr.req.ContextValue(key)
}
func constructTestHeaders(h ...string) headers.Headers {
i := 0
total := len(h)
headers := headers.NewHeaders()
for i < total-1 {
headers.Append(h[i], h[i+1])
i += 2
}
return headers
}