16580: Merge branch 'master' into 16580-remove-python2-packages
[arvados.git] / lib / controller / router / request_test.go
1 // Copyright (C) The Arvados Authors. All rights reserved.
2 //
3 // SPDX-License-Identifier: AGPL-3.0
4
5 package router
6
7 import (
8         "bytes"
9         "encoding/json"
10         "io"
11         "net/http"
12         "net/http/httptest"
13         "net/url"
14
15         "git.arvados.org/arvados.git/sdk/go/arvadostest"
16         check "gopkg.in/check.v1"
17 )
18
19 type testReq struct {
20         method   string
21         path     string
22         token    string // default is ActiveTokenV2; use noToken to omit
23         param    map[string]interface{}
24         attrs    map[string]interface{}
25         attrsKey string
26         header   http.Header
27
28         // variations on request formatting
29         json            bool
30         jsonAttrsTop    bool
31         jsonStringParam bool
32         tokenInBody     bool
33         tokenInQuery    bool
34         noContentType   bool
35
36         body *bytes.Buffer
37 }
38
39 const noToken = "(no token)"
40
41 func (tr *testReq) Request() *http.Request {
42         param := map[string]interface{}{}
43         for k, v := range tr.param {
44                 param[k] = v
45         }
46
47         if tr.body != nil {
48                 // caller provided a buffer
49         } else if tr.json {
50                 if tr.jsonAttrsTop {
51                         for k, v := range tr.attrs {
52                                 if tr.jsonStringParam {
53                                         j, err := json.Marshal(v)
54                                         if err != nil {
55                                                 panic(err)
56                                         }
57                                         param[k] = string(j)
58                                 } else {
59                                         param[k] = v
60                                 }
61                         }
62                 } else if tr.attrs != nil {
63                         if tr.jsonStringParam {
64                                 j, err := json.Marshal(tr.attrs)
65                                 if err != nil {
66                                         panic(err)
67                                 }
68                                 param[tr.attrsKey] = string(j)
69                         } else {
70                                 param[tr.attrsKey] = tr.attrs
71                         }
72                 }
73                 tr.body = bytes.NewBuffer(nil)
74                 err := json.NewEncoder(tr.body).Encode(param)
75                 if err != nil {
76                         panic(err)
77                 }
78         } else {
79                 values := make(url.Values)
80                 for k, v := range param {
81                         if vs, ok := v.(string); ok && !tr.jsonStringParam {
82                                 values.Set(k, vs)
83                         } else {
84                                 jv, err := json.Marshal(v)
85                                 if err != nil {
86                                         panic(err)
87                                 }
88                                 values.Set(k, string(jv))
89                         }
90                 }
91                 if tr.attrs != nil {
92                         jattrs, err := json.Marshal(tr.attrs)
93                         if err != nil {
94                                 panic(err)
95                         }
96                         values.Set(tr.attrsKey, string(jattrs))
97                 }
98                 tr.body = bytes.NewBuffer(nil)
99                 io.WriteString(tr.body, values.Encode())
100         }
101         method := tr.method
102         if method == "" {
103                 method = "GET"
104         }
105         path := tr.path
106         if path == "" {
107                 path = "example/test/path"
108         }
109         req := httptest.NewRequest(method, "https://an.example/"+path, tr.body)
110         token := tr.token
111         if token == "" {
112                 token = arvadostest.ActiveTokenV2
113         }
114         if token != noToken {
115                 req.Header.Set("Authorization", "Bearer "+token)
116         }
117         if tr.json {
118                 req.Header.Set("Content-Type", "application/json")
119         } else {
120                 req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
121         }
122         for k, v := range tr.header {
123                 req.Header[k] = append([]string(nil), v...)
124         }
125         return req
126 }
127
128 func (tr *testReq) bodyContent() string {
129         return string(tr.body.Bytes())
130 }
131
132 func (s *RouterSuite) TestAttrsInBody(c *check.C) {
133         attrs := map[string]interface{}{"foo": "bar"}
134         for _, tr := range []testReq{
135                 {attrsKey: "model_name", json: true, attrs: attrs},
136                 {attrsKey: "model_name", json: true, attrs: attrs, jsonAttrsTop: true},
137                 {attrsKey: "model_name", json: true, attrs: attrs, jsonAttrsTop: true, jsonStringParam: true},
138                 {attrsKey: "model_name", json: true, attrs: attrs, jsonAttrsTop: false, jsonStringParam: true},
139         } {
140                 c.Logf("tr: %#v", tr)
141                 req := tr.Request()
142                 params, err := s.rtr.loadRequestParams(req, tr.attrsKey)
143                 c.Logf("params: %#v", params)
144                 c.Assert(err, check.IsNil)
145                 c.Check(params, check.NotNil)
146                 c.Assert(params["attrs"], check.FitsTypeOf, map[string]interface{}{})
147                 c.Check(params["attrs"].(map[string]interface{})["foo"], check.Equals, "bar")
148         }
149 }
150
151 func (s *RouterSuite) TestBoolParam(c *check.C) {
152         testKey := "ensure_unique_name"
153
154         for i, tr := range []testReq{
155                 {method: "POST", param: map[string]interface{}{testKey: false}, json: true},
156                 {method: "POST", param: map[string]interface{}{testKey: false}},
157                 {method: "POST", param: map[string]interface{}{testKey: "false"}},
158                 {method: "POST", param: map[string]interface{}{testKey: "0"}},
159                 {method: "POST", param: map[string]interface{}{testKey: ""}},
160         } {
161                 c.Logf("#%d, tr: %#v", i, tr)
162                 req := tr.Request()
163                 c.Logf("tr.body: %s", tr.bodyContent())
164                 params, err := s.rtr.loadRequestParams(req, tr.attrsKey)
165                 c.Logf("params: %#v", params)
166                 c.Assert(err, check.IsNil)
167                 c.Check(params, check.NotNil)
168                 c.Check(params[testKey], check.Equals, false)
169         }
170
171         for i, tr := range []testReq{
172                 {method: "POST", param: map[string]interface{}{testKey: true}, json: true},
173                 {method: "POST", param: map[string]interface{}{testKey: true}},
174                 {method: "POST", param: map[string]interface{}{testKey: "true"}},
175                 {method: "POST", param: map[string]interface{}{testKey: "1"}},
176         } {
177                 c.Logf("#%d, tr: %#v", i, tr)
178                 req := tr.Request()
179                 c.Logf("tr.body: %s", tr.bodyContent())
180                 params, err := s.rtr.loadRequestParams(req, tr.attrsKey)
181                 c.Logf("params: %#v", params)
182                 c.Assert(err, check.IsNil)
183                 c.Check(params, check.NotNil)
184                 c.Check(params[testKey], check.Equals, true)
185         }
186 }
187
188 func (s *RouterSuite) TestOrderParam(c *check.C) {
189         for i, tr := range []testReq{
190                 {method: "POST", param: map[string]interface{}{"order": ""}, json: true},
191                 {method: "POST", param: map[string]interface{}{"order": ""}, json: false},
192                 {method: "POST", param: map[string]interface{}{"order": []string{}}, json: true},
193                 {method: "POST", param: map[string]interface{}{"order": []string{}}, json: false},
194                 {method: "POST", param: map[string]interface{}{}, json: true},
195                 {method: "POST", param: map[string]interface{}{}, json: false},
196         } {
197                 c.Logf("#%d, tr: %#v", i, tr)
198                 req := tr.Request()
199                 params, err := s.rtr.loadRequestParams(req, tr.attrsKey)
200                 c.Assert(err, check.IsNil)
201                 c.Assert(params, check.NotNil)
202                 if order, ok := params["order"]; ok && order != nil {
203                         c.Check(order, check.DeepEquals, []interface{}{})
204                 }
205         }
206
207         for i, tr := range []testReq{
208                 {method: "POST", param: map[string]interface{}{"order": "foo,bar desc"}, json: true},
209                 {method: "POST", param: map[string]interface{}{"order": "foo,bar desc"}, json: false},
210                 {method: "POST", param: map[string]interface{}{"order": "[\"foo\", \"bar desc\"]"}, json: false},
211                 {method: "POST", param: map[string]interface{}{"order": []string{"foo", "bar desc"}}, json: true},
212                 {method: "POST", param: map[string]interface{}{"order": []string{"foo", "bar desc"}}, json: false},
213         } {
214                 c.Logf("#%d, tr: %#v", i, tr)
215                 req := tr.Request()
216                 params, err := s.rtr.loadRequestParams(req, tr.attrsKey)
217                 c.Assert(err, check.IsNil)
218                 if _, ok := params["order"].([]string); ok {
219                         c.Check(params["order"], check.DeepEquals, []string{"foo", "bar desc"})
220                 } else {
221                         c.Check(params["order"], check.DeepEquals, []interface{}{"foo", "bar desc"})
222                 }
223         }
224 }