Left: | ||
Right: |
OLD | NEW |
---|---|
1 // Nova double testing service - HTTP API tests | 1 // Nova double testing service - HTTP API tests |
2 | 2 |
3 package novaservice | 3 package novaservice |
4 | 4 |
5 import ( | 5 import ( |
6 "bytes" | |
7 "encoding/json" | |
8 "io/ioutil" | |
6 . "launchpad.net/gocheck" | 9 . "launchpad.net/gocheck" |
10 "launchpad.net/goose/nova" | |
7 "launchpad.net/goose/testing/httpsuite" | 11 "launchpad.net/goose/testing/httpsuite" |
12 "net/http" | |
13 "strconv" | |
14 "strings" | |
8 ) | 15 ) |
9 | 16 |
10 type NovaHTTPSuite struct { | 17 type NovaHTTPSuite struct { |
11 httpsuite.HTTPSuite | 18 httpsuite.HTTPSuite |
12 service *Nova | 19 service *Nova |
13 } | 20 } |
14 | 21 |
15 var _ = Suite(&NovaHTTPSuite{}) | 22 var _ = Suite(&NovaHTTPSuite{}) |
16 | 23 |
17 func (s *NovaHTTPSuite) SetUpSuite(c *C) { | 24 func (s *NovaHTTPSuite) SetUpSuite(c *C) { |
18 s.HTTPSuite.SetUpSuite(c) | 25 s.HTTPSuite.SetUpSuite(c) |
19 » s.service = New(s.Server.URL, baseURL, token) | 26 » s.service = New(s.Server.URL, baseURL, token, tenantId) |
20 } | 27 } |
21 | 28 |
22 func (s *NovaHTTPSuite) TearDownSuite(c *C) { | 29 func (s *NovaHTTPSuite) TearDownSuite(c *C) { |
23 s.HTTPSuite.TearDownSuite(c) | 30 s.HTTPSuite.TearDownSuite(c) |
24 } | 31 } |
25 | 32 |
26 func (s *NovaHTTPSuite) SetUpTest(c *C) { | 33 func (s *NovaHTTPSuite) SetUpTest(c *C) { |
27 s.HTTPSuite.SetUpTest(c) | 34 s.HTTPSuite.SetUpTest(c) |
28 » s.Mux.Handle(baseURL, s.service) | 35 » s.service.setupHTTP(s.Mux) |
29 } | 36 } |
30 | 37 |
31 func (s *NovaHTTPSuite) TearDownTest(c *C) { | 38 func (s *NovaHTTPSuite) TearDownTest(c *C) { |
32 s.HTTPSuite.TearDownTest(c) | 39 s.HTTPSuite.TearDownTest(c) |
33 } | 40 } |
41 | |
42 // assertJSON asserts the passed http.Response's body can be | |
43 // unmarshalled into the passed expected object. | |
44 func (s *NovaHTTPSuite) assertJSON(c *C, resp *http.Response, expected interface {}) { | |
fwereade
2012/12/12 15:18:44
Doesn't need to be a method.
dimitern
2012/12/12 17:32:24
Done.
| |
45 body, err := ioutil.ReadAll(resp.Body) | |
46 defer resp.Body.Close() | |
47 c.Assert(err, IsNil) | |
48 err = json.Unmarshal(body, &expected) | |
49 c.Assert(err, IsNil) | |
50 } | |
51 | |
52 // assertBody asserts the passed http.Response's body matches the | |
53 // expected response, replacing any variables in the expected body. | |
54 func (s *NovaHTTPSuite) assertBody(c *C, resp *http.Response, expected response) { | |
fwereade
2012/12/12 15:18:44
ditto
dimitern
2012/12/12 17:32:24
Done.
| |
55 body, err := ioutil.ReadAll(resp.Body) | |
56 defer resp.Body.Close() | |
57 c.Assert(err, IsNil) | |
58 expBody := expected.replaceVars(resp.Request) | |
59 // cast to string for easier asserts debugging | |
60 c.Assert(string(body), Equals, string(expBody)) | |
61 } | |
62 | |
63 // sendRequest constructs an HTTP request from the parameters and | |
64 // sends it, returning the response or an error. | |
65 func (s *NovaHTTPSuite) sendRequest(method, url string, body []byte, headers htt p.Header) (*http.Response, error) { | |
66 if !strings.HasPrefix(url, s.service.hostname) { | |
67 url = s.service.hostname + strings.TrimLeft(url, "/") | |
68 } | |
69 bodyReader := bytes.NewReader(body) | |
rog
2012/12/12 15:02:33
or inline
dimitern
2012/12/12 17:32:24
Done.
| |
70 req, err := http.NewRequest(method, url, bodyReader) | |
71 if err != nil { | |
72 return nil, err | |
73 } | |
74 if headers != nil { | |
rog
2012/12/12 15:02:33
d
dimitern
2012/12/12 17:32:24
Done.
| |
75 for header, values := range headers { | |
76 for _, value := range values { | |
77 req.Header.Add(header, value) | |
78 } | |
79 } | |
80 } | |
81 // workaround for https://code.google.com/p/go/issues/detail?id=4454 | |
rog
2012/12/12 15:02:33
i thought that issue was triggered by the NoConten
dimitern
2012/12/12 17:32:24
Any time when len(body) == 0, hence the transfer-e
rog
2012/12/12 18:12:13
i'd be surprised if that was true. can you reprodu
| |
82 req.Header.Set("Content-Length", strconv.Itoa(len(body))) | |
83 return http.DefaultClient.Do(req) | |
84 } | |
85 | |
86 // authRequest is a shortcut for sending requests with pre-set token | |
87 // header and correct version prefix and tenant ID in the URL. | |
88 func (s *NovaHTTPSuite) authRequest(method, path string, body []byte, headers ht tp.Header) (*http.Response, error) { | |
89 if headers == nil { | |
90 headers = make(http.Header) | |
91 } | |
92 headers.Set(authToken, s.service.token) | |
93 url := s.service.endpoint(true, path) | |
94 return s.sendRequest(method, url, body, headers) | |
95 } | |
96 | |
97 // jsonRequest serializes the passed body object to JSON and sends a | |
98 // the request with authRequest(). | |
99 func (s *NovaHTTPSuite) jsonRequest(method, path string, body interface{}, heade rs http.Header) (*http.Response, error) { | |
100 jsonBody, err := json.Marshal(body) | |
101 if err != nil { | |
102 return nil, err | |
103 } | |
104 return s.authRequest(method, path, jsonBody, headers) | |
105 } | |
106 | |
107 func (s *NovaHTTPSuite) TestUnauthorizedResponse(c *C) { | |
108 resp, err := s.sendRequest("GET", "/any", nil, nil) | |
109 c.Assert(err, IsNil) | |
110 c.Assert(resp.StatusCode, Equals, http.StatusUnauthorized) | |
111 headers := make(http.Header) | |
112 headers.Set(authToken, "phony") | |
113 resp, err = s.sendRequest("POST", "/any", nil, headers) | |
114 c.Assert(err, IsNil) | |
115 c.Assert(resp.StatusCode, Equals, http.StatusUnauthorized) | |
116 s.assertBody(c, resp, unauthorizedResponse) | |
117 } | |
118 | |
119 func (s *NovaHTTPSuite) TestNoVersionResponse(c *C) { | |
120 headers := make(http.Header) | |
121 headers.Set(authToken, s.service.token) | |
122 resp, err := s.sendRequest("GET", "/", nil, headers) | |
123 c.Assert(err, IsNil) | |
124 c.Assert(resp.StatusCode, Equals, http.StatusOK) | |
125 s.assertBody(c, resp, noVersionResponse) | |
126 } | |
127 | |
128 func (s *NovaHTTPSuite) TestMultipleChoicesResponse(c *C) { | |
129 headers := make(http.Header) | |
130 headers.Set(authToken, s.service.token) | |
131 resp, err := s.sendRequest("GET", "/any", nil, headers) | |
132 c.Assert(err, IsNil) | |
133 c.Assert(resp.StatusCode, Equals, http.StatusMultipleChoices) | |
134 s.assertBody(c, resp, multipleChoicesResponse) | |
135 resp, err = s.sendRequest("POST", "/any/other/one", nil, headers) | |
136 c.Assert(err, IsNil) | |
137 c.Assert(resp.StatusCode, Equals, http.StatusMultipleChoices) | |
138 s.assertBody(c, resp, multipleChoicesResponse) | |
139 } | |
140 | |
141 func (s *NovaHTTPSuite) TestNotFoundResponse(c *C) { | |
142 resp, err := s.authRequest("GET", "/flavors/", nil, nil) | |
143 c.Assert(err, IsNil) | |
144 c.Assert(resp.StatusCode, Equals, http.StatusNotFound) | |
145 resp, err = s.authRequest("POST", "/any/unknown/one", nil, nil) | |
146 c.Assert(err, IsNil) | |
147 c.Assert(resp.StatusCode, Equals, http.StatusNotFound) | |
148 resp, err = s.authRequest("GET", "/flavors/id", nil, nil) | |
149 c.Assert(err, IsNil) | |
150 c.Assert(resp.StatusCode, Equals, http.StatusNotFound) | |
151 } | |
152 | |
153 func (s *NovaHTTPSuite) TestBadRequestResponse(c *C) { | |
154 headers := make(http.Header) | |
155 headers.Set(authToken, token) | |
156 resp, err := s.sendRequest("GET", s.service.baseURL+"/phony_token", nil, headers) | |
157 c.Assert(err, IsNil) | |
158 c.Assert(resp.StatusCode, Equals, http.StatusBadRequest) | |
159 s.assertBody(c, resp, badRequestResponse) | |
rog
2012/12/12 15:02:33
it seems to me that all these bad-status tests cou
dimitern
2012/12/12 17:32:24
I was thinking of it, but was not sure whether it'
| |
160 } | |
161 | |
162 func (s *NovaHTTPSuite) TestGetFlavors(c *C) { | |
163 entities := s.service.allFlavorsAsEntities() | |
164 c.Assert(entities, HasLen, 0) | |
165 resp, err := s.authRequest("GET", "/flavors", nil, nil) | |
166 c.Assert(err, IsNil) | |
167 c.Assert(resp.StatusCode, Equals, http.StatusNoContent) | |
168 flavors := []nova.FlavorDetail{ | |
169 nova.FlavorDetail{Id: "fl1", Name: "flavor 1"}, | |
170 nova.FlavorDetail{Id: "fl2", Name: "flavor 2"}, | |
171 } | |
172 for _, flavor := range flavors { | |
173 s.service.buildFlavorLinks(&flavor) | |
174 err = s.service.addFlavor(flavor) | |
175 defer s.service.removeFlavor(flavor.Id) | |
176 c.Assert(err, IsNil) | |
177 } | |
178 entities = s.service.allFlavorsAsEntities() | |
179 resp, err = s.authRequest("GET", "/flavors", nil, nil) | |
180 c.Assert(err, IsNil) | |
181 c.Assert(resp.StatusCode, Equals, http.StatusOK) | |
182 var expected struct { | |
183 Flavors []nova.Entity | |
184 } | |
185 s.assertJSON(c, resp, expected) | |
186 } | |
187 | |
188 func (s *NovaHTTPSuite) TestGetInvalidFlavorsFails(c *C) { | |
189 flavor := nova.FlavorDetail{Id: "1"} | |
190 err := s.service.addFlavor(flavor) | |
191 c.Assert(err, IsNil) | |
192 defer s.service.removeFlavor("1") | |
193 resp, err := s.authRequest("GET", "/flavors/invalid", nil, nil) | |
194 c.Assert(err, IsNil) | |
195 c.Assert(resp.StatusCode, Equals, http.StatusNotFound) | |
196 s.assertBody(c, resp, notFoundResponse) | |
197 } | |
198 | |
199 func (s *NovaHTTPSuite) TestPostInvalidFlavorsFails(c *C) { | |
200 resp, err := s.authRequest("POST", "/flavors/invalid", nil, nil) | |
201 c.Assert(err, IsNil) | |
202 c.Assert(resp.StatusCode, Equals, http.StatusNotFound) | |
203 s.assertBody(c, resp, notFoundResponse) | |
204 } | |
205 | |
206 func (s *NovaHTTPSuite) TestPostEmptyFlavorsFails(c *C) { | |
207 resp, err := s.authRequest("POST", "/flavors", nil, nil) | |
208 c.Assert(err, IsNil) | |
209 c.Assert(resp.StatusCode, Equals, http.StatusBadRequest) | |
210 s.assertBody(c, resp, badRequest2Response) | |
211 } | |
212 | |
213 func (s *NovaHTTPSuite) TestPostValidFlavorSucceeds(c *C) { | |
214 _, err := s.service.flavor("fl1") | |
215 c.Assert(err, NotNil) | |
216 var req struct { | |
217 Flavor nova.FlavorDetail `json:"flavor"` | |
218 } | |
219 req.Flavor = nova.FlavorDetail{Id: "fl1", Name: "flavor 1"} | |
220 resp, err := s.jsonRequest("POST", "/flavors", req, nil) | |
221 c.Assert(err, IsNil) | |
222 c.Assert(resp.StatusCode, Equals, http.StatusCreated) | |
223 s.assertBody(c, resp, createdResponse) | |
224 _, err = s.service.flavor("fl1") | |
225 c.Assert(err, IsNil) | |
226 s.service.removeFlavor("fl1") | |
227 } | |
228 | |
229 func (s *NovaHTTPSuite) TestPutFlavorsFails(c *C) { | |
230 resp, err := s.authRequest("PUT", "/flavors", nil, nil) | |
231 c.Assert(err, IsNil) | |
232 c.Assert(resp.StatusCode, Equals, http.StatusNotFound) | |
233 s.assertBody(c, resp, notFoundResponse) | |
234 } | |
235 | |
236 func (s *NovaHTTPSuite) TestDeleteFlavorsFails(c *C) { | |
237 resp, err := s.authRequest("DELETE", "/flavors", nil, nil) | |
238 c.Assert(err, IsNil) | |
239 c.Assert(resp.StatusCode, Equals, http.StatusNotFound) | |
240 s.assertBody(c, resp, notFoundResponse) | |
241 } | |
242 | |
243 func (s *NovaHTTPSuite) TestGetFlavorsDetail(c *C) { | |
244 flavors := s.service.allFlavors() | |
245 c.Assert(flavors, HasLen, 0) | |
246 resp, err := s.authRequest("GET", "/flavors/detail", nil, nil) | |
247 c.Assert(err, IsNil) | |
248 c.Assert(resp.StatusCode, Equals, http.StatusNoContent) | |
249 flavors = []nova.FlavorDetail{ | |
250 nova.FlavorDetail{Id: "fl1", Name: "flavor 1"}, | |
251 nova.FlavorDetail{Id: "fl2", Name: "flavor 2"}, | |
252 } | |
253 for _, flavor := range flavors { | |
254 s.service.buildFlavorLinks(&flavor) | |
255 err = s.service.addFlavor(flavor) | |
256 defer s.service.removeFlavor(flavor.Id) | |
257 c.Assert(err, IsNil) | |
258 } | |
259 resp, err = s.authRequest("GET", "/flavors/detail", nil, nil) | |
260 c.Assert(err, IsNil) | |
261 c.Assert(resp.StatusCode, Equals, http.StatusOK) | |
262 var expected struct { | |
263 Flavors []nova.FlavorDetail | |
264 } | |
265 s.assertJSON(c, resp, expected) | |
266 } | |
267 | |
268 func (s *NovaHTTPSuite) TestPostFlavorsDetailFails(c *C) { | |
269 resp, err := s.authRequest("POST", "/flavors/detail", nil, nil) | |
270 c.Assert(err, IsNil) | |
271 c.Assert(resp.StatusCode, Equals, http.StatusNotFound) | |
272 s.assertBody(c, resp, notFoundResponse) | |
273 } | |
274 | |
275 func (s *NovaHTTPSuite) TestPutFlavorsDetailFails(c *C) { | |
276 resp, err := s.authRequest("PUT", "/flavors/detail", nil, nil) | |
277 c.Assert(err, IsNil) | |
278 c.Assert(resp.StatusCode, Equals, http.StatusNotFound) | |
279 s.assertBody(c, resp, notFoundJSONResponse) | |
280 } | |
281 | |
282 func (s *NovaHTTPSuite) TestDeleteFlavorsDetailFails(c *C) { | |
283 resp, err := s.authRequest("DELETE", "/flavors/detail", nil, nil) | |
284 c.Assert(err, IsNil) | |
285 c.Assert(resp.StatusCode, Equals, http.StatusForbidden) | |
286 s.assertBody(c, resp, forbiddenResponse) | |
287 } | |
fwereade
2012/12/12 15:18:44
These quick error tests would be great for a table
dimitern
2012/12/12 17:32:24
As discussed, I can implement table-based approach
| |
OLD | NEW |