| OLD | NEW |
| 1 // Copyright 2014 The Chromium Authors. All rights reserved. | 1 // Copyright 2014 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 package cipd | 5 package cipd |
| 6 | 6 |
| 7 import ( | 7 import ( |
| 8 "fmt" | |
| 9 "io/ioutil" | |
| 10 "net/http" | |
| 11 "net/http/httptest" | |
| 12 "net/url" | 8 "net/url" |
| 13 "reflect" | |
| 14 "strings" | 9 "strings" |
| 15 "testing" | 10 "testing" |
| 16 "time" | 11 "time" |
| 17 | 12 |
| 18 » "infra/libs/logging" | 13 » . "github.com/smartystreets/goconvey/convey" |
| 19 | 14 |
| 20 » . "github.com/smartystreets/goconvey/convey" | 15 » . "infra/tools/cipd/common" |
| 21 ) | 16 ) |
| 22 | 17 |
| 23 func TestRemoteService(t *testing.T) { | 18 func TestRemoteImpl(t *testing.T) { |
| 24 » mockInitiateUpload := func(c C, response string) (*uploadSession, error)
{ | 19 » mockInitiateUpload := func(c C, reply string) (*UploadSession, error) { |
| 25 » » remote := mockRemoteService(func(w http.ResponseWriter, r *http.
Request) { | 20 » » remote := mockRemoteImpl(c, []expectedHTTPCall{ |
| 26 » » » c.So(r.URL.Path, ShouldEqual, "/_ah/api/cas/v1/upload/SH
A1/abc") | 21 » » » { |
| 27 » » » c.So(r.Method, ShouldEqual, "POST") | 22 » » » » Method: "POST", |
| 28 » » » w.Write([]byte(response)) | 23 » » » » Path: "/_ah/api/cas/v1/upload/SHA1/abc", |
| 24 » » » » Reply: reply, |
| 25 » » » }, |
| 29 }) | 26 }) |
| 30 return remote.initiateUpload("abc") | 27 return remote.initiateUpload("abc") |
| 31 } | 28 } |
| 32 | 29 |
| 33 » mockFinalizeUpload := func(c C, response string) (bool, error) { | 30 » mockFinalizeUpload := func(c C, reply string) (bool, error) { |
| 34 » » remote := mockRemoteService(func(w http.ResponseWriter, r *http.
Request) { | 31 » » remote := mockRemoteImpl(c, []expectedHTTPCall{ |
| 35 » » » c.So(r.URL.Path, ShouldEqual, "/_ah/api/cas/v1/finalize/
abc") | 32 » » » { |
| 36 » » » c.So(r.Method, ShouldEqual, "POST") | 33 » » » » Method: "POST", |
| 37 » » » w.Write([]byte(response)) | 34 » » » » Path: "/_ah/api/cas/v1/finalize/abc", |
| 35 » » » » Reply: reply, |
| 36 » » » }, |
| 38 }) | 37 }) |
| 39 return remote.finalizeUpload("abc") | 38 return remote.finalizeUpload("abc") |
| 40 } | 39 } |
| 41 | 40 |
| 42 » mockRegisterInstance := func(c C, response string) (*registerInstanceRes
ponse, error) { | 41 » mockRegisterInstance := func(c C, reply string) (*registerInstanceRespon
se, error) { |
| 43 » » remote := mockRemoteService(func(w http.ResponseWriter, r *http.
Request) { | 42 » » remote := mockRemoteImpl(c, []expectedHTTPCall{ |
| 44 » » » c.So(r.URL.Path, ShouldEqual, "/_ah/api/repo/v1/instance
") | 43 » » » { |
| 45 » » » c.So(r.URL.Query().Get("package_name"), ShouldEqual, "pk
gname") | 44 » » » » Method: "POST", |
| 46 » » » c.So(r.URL.Query().Get("instance_id"), ShouldEqual, "aaa
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa") | 45 » » » » Path: "/_ah/api/repo/v1/instance", |
| 47 » » » c.So(r.Method, ShouldEqual, "POST") | 46 » » » » Query: url.Values{ |
| 48 » » » w.Write([]byte(response)) | 47 » » » » » "package_name": []string{"pkgname"}, |
| 48 » » » » » "instance_id": []string{"aaaaaaaaaaaaaa
aaaaaaaaaaaaaaaaaaaaaaaaaa"}, |
| 49 » » » » }, |
| 50 » » » » Reply: reply, |
| 51 » » » }, |
| 49 }) | 52 }) |
| 50 » » return remote.registerInstance("pkgname", "aaaaaaaaaaaaaaaaaaaaa
aaaaaaaaaaaaaaaaaaa") | 53 » » return remote.registerInstance(Pin{"pkgname", "aaaaaaaaaaaaaaaaa
aaaaaaaaaaaaaaaaaaaaaaa"}) |
| 51 } | 54 } |
| 52 | 55 |
| 53 » mockFetchInstance := func(c C, response string) (*fetchInstanceResponse,
error) { | 56 » mockFetchInstance := func(c C, reply string) (*fetchInstanceResponse, er
ror) { |
| 54 » » remote := mockRemoteService(func(w http.ResponseWriter, r *http.
Request) { | 57 » » remote := mockRemoteImpl(c, []expectedHTTPCall{ |
| 55 » » » c.So(r.URL.Path, ShouldEqual, "/_ah/api/repo/v1/instance
") | 58 » » » { |
| 56 » » » c.So(r.URL.Query().Get("package_name"), ShouldEqual, "pk
gname") | 59 » » » » Method: "GET", |
| 57 » » » c.So(r.URL.Query().Get("instance_id"), ShouldEqual, "aaa
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa") | 60 » » » » Path: "/_ah/api/repo/v1/instance", |
| 58 » » » c.So(r.Method, ShouldEqual, "GET") | 61 » » » » Query: url.Values{ |
| 59 » » » w.Write([]byte(response)) | 62 » » » » » "package_name": []string{"pkgname"}, |
| 63 » » » » » "instance_id": []string{"aaaaaaaaaaaaaa
aaaaaaaaaaaaaaaaaaaaaaaaaa"}, |
| 64 » » » » }, |
| 65 » » » » Reply: reply, |
| 66 » » » }, |
| 60 }) | 67 }) |
| 61 » » return remote.fetchInstance("pkgname", "aaaaaaaaaaaaaaaaaaaaaaaa
aaaaaaaaaaaaaaaa") | 68 » » return remote.fetchInstance(Pin{"pkgname", "aaaaaaaaaaaaaaaaaaaa
aaaaaaaaaaaaaaaaaaaa"}) |
| 62 } | 69 } |
| 63 | 70 |
| 64 » mockFetchACL := func(c C, response string) ([]PackageACL, error) { | 71 » mockFetchACL := func(c C, reply string) ([]PackageACL, error) { |
| 65 » » remote := mockRemoteService(func(w http.ResponseWriter, r *http.
Request) { | 72 » » remote := mockRemoteImpl(c, []expectedHTTPCall{ |
| 66 » » » c.So(r.URL.Path, ShouldEqual, "/_ah/api/repo/v1/acl") | 73 » » » { |
| 67 » » » c.So(r.URL.Query().Get("package_path"), ShouldEqual, "pk
gname") | 74 » » » » Method: "GET", |
| 68 » » » c.So(r.Method, ShouldEqual, "GET") | 75 » » » » Path: "/_ah/api/repo/v1/acl", |
| 69 » » » w.Write([]byte(response)) | 76 » » » » Query: url.Values{"package_path": []string{"pkg
name"}}, |
| 77 » » » » Reply: reply, |
| 78 » » » }, |
| 70 }) | 79 }) |
| 71 return remote.fetchACL("pkgname") | 80 return remote.fetchACL("pkgname") |
| 72 } | 81 } |
| 73 | 82 |
| 74 » mockModifyACL := func(c C, changes []PackageACLChange, request, response
string) error { | 83 » mockModifyACL := func(c C, changes []PackageACLChange, body, reply strin
g) error { |
| 75 » » remote := mockRemoteService(func(w http.ResponseWriter, r *http.
Request) { | 84 » » remote := mockRemoteImpl(c, []expectedHTTPCall{ |
| 76 » » » c.So(r.URL.Path, ShouldEqual, "/_ah/api/repo/v1/acl") | 85 » » » { |
| 77 » » » c.So(r.URL.Query().Get("package_path"), ShouldEqual, "pk
gname") | 86 » » » » Method: "POST", |
| 78 » » » c.So(r.Method, ShouldEqual, "POST") | 87 » » » » Path: "/_ah/api/repo/v1/acl", |
| 79 » » » body, err := ioutil.ReadAll(r.Body) | 88 » » » » Query: url.Values{"package_path": []string{"pkg
name"}}, |
| 80 » » » c.So(err, ShouldBeNil) | 89 » » » » Body: body, |
| 81 » » » c.So(string(body), ShouldEqual, request) | 90 » » » » Reply: reply, |
| 82 » » » w.Write([]byte(response)) | 91 » » » }, |
| 83 }) | 92 }) |
| 84 return remote.modifyACL("pkgname", changes) | 93 return remote.modifyACL("pkgname", changes) |
| 85 } | 94 } |
| 86 | 95 |
| 87 » mockAttachTags := func(c C, tags []string, request, response string) err
or { | 96 » mockAttachTags := func(c C, tags []string, body, reply string) error { |
| 88 » » remote := mockRemoteService(func(w http.ResponseWriter, r *http.
Request) { | 97 » » remote := mockRemoteImpl(c, []expectedHTTPCall{ |
| 89 » » » c.So(r.URL.Path, ShouldEqual, "/_ah/api/repo/v1/tags") | 98 » » » { |
| 90 » » » c.So(r.URL.Query().Get("package_name"), ShouldEqual, "pk
gname") | 99 » » » » Method: "POST", |
| 91 » » » c.So(r.URL.Query().Get("instance_id"), ShouldEqual, "aaa
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa") | 100 » » » » Path: "/_ah/api/repo/v1/tags", |
| 92 » » » c.So(r.Method, ShouldEqual, "POST") | 101 » » » » Query: url.Values{ |
| 93 » » » body, err := ioutil.ReadAll(r.Body) | 102 » » » » » "package_name": []string{"pkgname"}, |
| 94 » » » c.So(err, ShouldBeNil) | 103 » » » » » "instance_id": []string{"aaaaaaaaaaaaaa
aaaaaaaaaaaaaaaaaaaaaaaaaa"}, |
| 95 » » » c.So(string(body), ShouldEqual, request) | 104 » » » » }, |
| 96 » » » w.Write([]byte(response)) | 105 » » » » Body: body, |
| 106 » » » » Reply: reply, |
| 107 » » » }, |
| 97 }) | 108 }) |
| 98 » » return remote.attachTags("pkgname", "aaaaaaaaaaaaaaaaaaaaaaaaaaa
aaaaaaaaaaaaa", tags) | 109 » » return remote.attachTags(Pin{"pkgname", "aaaaaaaaaaaaaaaaaaaaaaa
aaaaaaaaaaaaaaaaa"}, tags) |
| 99 } | 110 } |
| 100 | 111 |
| 101 Convey("makeRequest POST works", t, func(c C) { | 112 Convey("makeRequest POST works", t, func(c C) { |
| 102 » » remote := mockRemoteService(func(w http.ResponseWriter, r *http.
Request) { | 113 » » remote := mockRemoteImpl(c, []expectedHTTPCall{ |
| 103 » » » c.So(r.Method, ShouldEqual, "POST") | 114 » » » { |
| 104 » » » c.So(r.URL.Path, ShouldEqual, "/_ah/api/cas/v1/method") | 115 » » » » Method: "POST", |
| 105 » » » w.Write([]byte(`{"value":"123"}`)) | 116 » » » » Path: "/_ah/api/cas/v1/method", |
| 117 » » » » Reply: `{"value":"123"}`, |
| 118 » » » }, |
| 106 }) | 119 }) |
| 107 var reply struct { | 120 var reply struct { |
| 108 Value string `json:"value"` | 121 Value string `json:"value"` |
| 109 } | 122 } |
| 110 err := remote.makeRequest("cas/v1/method", "POST", nil, &reply) | 123 err := remote.makeRequest("cas/v1/method", "POST", nil, &reply) |
| 111 So(err, ShouldBeNil) | 124 So(err, ShouldBeNil) |
| 112 So(reply.Value, ShouldEqual, "123") | 125 So(reply.Value, ShouldEqual, "123") |
| 113 }) | 126 }) |
| 114 | 127 |
| 115 Convey("makeRequest GET works", t, func(c C) { | 128 Convey("makeRequest GET works", t, func(c C) { |
| 116 » » remote := mockRemoteService(func(w http.ResponseWriter, r *http.
Request) { | 129 » » remote := mockRemoteImpl(c, []expectedHTTPCall{ |
| 117 » » » c.So(r.Method, ShouldEqual, "GET") | 130 » » » { |
| 118 » » » c.So(r.URL.Path, ShouldEqual, "/_ah/api/cas/v1/method") | 131 » » » » Method: "GET", |
| 119 » » » w.Write([]byte(`{"value":"123"}`)) | 132 » » » » Path: "/_ah/api/cas/v1/method", |
| 133 » » » » Reply: `{"value":"123"}`, |
| 134 » » » }, |
| 120 }) | 135 }) |
| 121 var reply struct { | 136 var reply struct { |
| 122 Value string `json:"value"` | 137 Value string `json:"value"` |
| 123 } | 138 } |
| 124 err := remote.makeRequest("cas/v1/method", "GET", nil, &reply) | 139 err := remote.makeRequest("cas/v1/method", "GET", nil, &reply) |
| 125 So(err, ShouldBeNil) | 140 So(err, ShouldBeNil) |
| 126 So(reply.Value, ShouldEqual, "123") | 141 So(reply.Value, ShouldEqual, "123") |
| 127 }) | 142 }) |
| 128 | 143 |
| 129 » Convey("makeRequest handles fatal error", t, func() { | 144 » Convey("makeRequest handles fatal error", t, func(c C) { |
| 130 » » calls := 0 | 145 » » remote := mockRemoteImpl(c, []expectedHTTPCall{ |
| 131 » » remote := mockRemoteService(func(w http.ResponseWriter, r *http.
Request) { | 146 » » » { |
| 132 » » » calls++ | 147 » » » » Method: "POST", |
| 133 » » » w.WriteHeader(403) | 148 » » » » Path: "/_ah/api/cas/v1/method", |
| 149 » » » » Status: 403, |
| 150 » » » }, |
| 134 }) | 151 }) |
| 135 var reply struct{} | 152 var reply struct{} |
| 136 err := remote.makeRequest("cas/v1/method", "POST", nil, &reply) | 153 err := remote.makeRequest("cas/v1/method", "POST", nil, &reply) |
| 137 So(err, ShouldNotBeNil) | 154 So(err, ShouldNotBeNil) |
| 138 So(calls, ShouldEqual, 1) | |
| 139 }) | 155 }) |
| 140 | 156 |
| 141 » Convey("makeRequest handles retries", t, func() { | 157 » Convey("makeRequest handles retries", t, func(c C) { |
| 142 » » mockClock(time.Now()) | 158 » » remote := mockRemoteImpl(c, []expectedHTTPCall{ |
| 143 » » calls := 0 | 159 » » » { |
| 144 » » remote := mockRemoteService(func(w http.ResponseWriter, r *http.
Request) { | 160 » » » » Method: "POST", |
| 145 » » » calls++ | 161 » » » » Path: "/_ah/api/cas/v1/method", |
| 146 » » » if calls == 1 { | 162 » » » » Status: 500, |
| 147 » » » » w.WriteHeader(500) | 163 » » » }, |
| 148 » » » } else { | 164 » » » { |
| 149 » » » » w.Write([]byte(`{}`)) | 165 » » » » Method: "POST", |
| 150 » » » } | 166 » » » » Path: "/_ah/api/cas/v1/method", |
| 167 » » » » Reply: `{}`, |
| 168 » » » }, |
| 151 }) | 169 }) |
| 152 var reply struct{} | 170 var reply struct{} |
| 153 err := remote.makeRequest("cas/v1/method", "POST", nil, &reply) | 171 err := remote.makeRequest("cas/v1/method", "POST", nil, &reply) |
| 154 So(err, ShouldBeNil) | 172 So(err, ShouldBeNil) |
| 155 So(calls, ShouldEqual, 2) | |
| 156 }) | 173 }) |
| 157 | 174 |
| 158 » Convey("makeRequest gives up trying", t, func() { | 175 » Convey("makeRequest gives up trying", t, func(c C) { |
| 159 » » mockClock(time.Now()) | 176 » » calls := []expectedHTTPCall{} |
| 160 » » calls := 0 | 177 » » for i := 0; i < remoteMaxRetries; i++ { |
| 161 » » remote := mockRemoteService(func(w http.ResponseWriter, r *http.
Request) { | 178 » » » calls = append(calls, expectedHTTPCall{ |
| 162 » » » calls++ | 179 » » » » Method: "POST", |
| 163 » » » w.WriteHeader(500) | 180 » » » » Path: "/_ah/api/cas/v1/method", |
| 164 » » }) | 181 » » » » Status: 500, |
| 182 » » » }) |
| 183 » » } |
| 184 » » remote := mockRemoteImpl(c, calls) |
| 165 var reply struct{} | 185 var reply struct{} |
| 166 err := remote.makeRequest("cas/v1/method", "POST", nil, &reply) | 186 err := remote.makeRequest("cas/v1/method", "POST", nil, &reply) |
| 167 So(err, ShouldNotBeNil) | 187 So(err, ShouldNotBeNil) |
| 168 So(calls, ShouldEqual, 10) | |
| 169 }) | 188 }) |
| 170 | 189 |
| 171 Convey("initiateUpload ALREADY_UPLOADED", t, func(c C) { | 190 Convey("initiateUpload ALREADY_UPLOADED", t, func(c C) { |
| 172 s, err := mockInitiateUpload(c, `{"status":"ALREADY_UPLOADED"}`) | 191 s, err := mockInitiateUpload(c, `{"status":"ALREADY_UPLOADED"}`) |
| 173 So(err, ShouldBeNil) | 192 So(err, ShouldBeNil) |
| 174 So(s, ShouldBeNil) | 193 So(s, ShouldBeNil) |
| 175 }) | 194 }) |
| 176 | 195 |
| 177 Convey("initiateUpload SUCCESS", t, func(c C) { | 196 Convey("initiateUpload SUCCESS", t, func(c C) { |
| 178 s, err := mockInitiateUpload(c, `{"status":"SUCCESS","upload_ses
sion_id":"123","upload_url":"http://localhost"}`) | 197 s, err := mockInitiateUpload(c, `{"status":"SUCCESS","upload_ses
sion_id":"123","upload_url":"http://localhost"}`) |
| 179 So(err, ShouldBeNil) | 198 So(err, ShouldBeNil) |
| 180 » » So(s, ShouldResemble, &uploadSession{ | 199 » » So(s, ShouldResemble, &UploadSession{"123", "http://localhost"}) |
| 181 » » » ID: "123", | |
| 182 » » » URL: "http://localhost", | |
| 183 » » }) | |
| 184 }) | 200 }) |
| 185 | 201 |
| 186 Convey("initiateUpload ERROR", t, func(c C) { | 202 Convey("initiateUpload ERROR", t, func(c C) { |
| 187 s, err := mockInitiateUpload(c, `{"status":"ERROR","error_messag
e":"boo"}`) | 203 s, err := mockInitiateUpload(c, `{"status":"ERROR","error_messag
e":"boo"}`) |
| 188 So(err, ShouldNotBeNil) | 204 So(err, ShouldNotBeNil) |
| 189 So(s, ShouldBeNil) | 205 So(s, ShouldBeNil) |
| 190 }) | 206 }) |
| 191 | 207 |
| 192 Convey("initiateUpload unknown status", t, func(c C) { | 208 Convey("initiateUpload unknown status", t, func(c C) { |
| 193 s, err := mockInitiateUpload(c, `{"status":"???"}`) | 209 s, err := mockInitiateUpload(c, `{"status":"???"}`) |
| 194 So(err, ShouldNotBeNil) | 210 So(err, ShouldNotBeNil) |
| 195 So(s, ShouldBeNil) | 211 So(s, ShouldBeNil) |
| 196 }) | 212 }) |
| 197 | 213 |
| 198 » Convey("initiateUpload bad reply", t, func() { | 214 » Convey("initiateUpload bad reply", t, func(c C) { |
| 199 » » remote := mockRemoteService(func(w http.ResponseWriter, r *http.
Request) { | 215 » » remote := mockRemoteImpl(c, []expectedHTTPCall{ |
| 200 » » » So(r.URL.Path, ShouldEqual, "/_ah/api/cas/v1/upload/SHA1
/abc") | 216 » » » { |
| 201 » » » w.WriteHeader(403) | 217 » » » » Method: "POST", |
| 218 » » » » Path: "/_ah/api/cas/v1/upload/SHA1/abc", |
| 219 » » » » Status: 403, |
| 220 » » » }, |
| 202 }) | 221 }) |
| 203 s, err := remote.initiateUpload("abc") | 222 s, err := remote.initiateUpload("abc") |
| 204 So(err, ShouldNotBeNil) | 223 So(err, ShouldNotBeNil) |
| 205 So(s, ShouldBeNil) | 224 So(s, ShouldBeNil) |
| 206 }) | 225 }) |
| 207 | 226 |
| 208 Convey("finalizeUpload MISSING", t, func(c C) { | 227 Convey("finalizeUpload MISSING", t, func(c C) { |
| 209 finished, err := mockFinalizeUpload(c, `{"status":"MISSING"}`) | 228 finished, err := mockFinalizeUpload(c, `{"status":"MISSING"}`) |
| 210 So(err, ShouldNotBeNil) | 229 So(err, ShouldNotBeNil) |
| 211 So(finished, ShouldBeFalse) | 230 So(finished, ShouldBeFalse) |
| (...skipping 23 matching lines...) Expand all Loading... |
| 235 So(finished, ShouldBeFalse) | 254 So(finished, ShouldBeFalse) |
| 236 }) | 255 }) |
| 237 | 256 |
| 238 Convey("finalizeUpload unknown status", t, func(c C) { | 257 Convey("finalizeUpload unknown status", t, func(c C) { |
| 239 finished, err := mockFinalizeUpload(c, `{"status":"???"}`) | 258 finished, err := mockFinalizeUpload(c, `{"status":"???"}`) |
| 240 So(err, ShouldNotBeNil) | 259 So(err, ShouldNotBeNil) |
| 241 So(finished, ShouldBeFalse) | 260 So(finished, ShouldBeFalse) |
| 242 }) | 261 }) |
| 243 | 262 |
| 244 Convey("finalizeUpload bad reply", t, func(c C) { | 263 Convey("finalizeUpload bad reply", t, func(c C) { |
| 245 » » remote := mockRemoteService(func(w http.ResponseWriter, r *http.
Request) { | 264 » » remote := mockRemoteImpl(c, []expectedHTTPCall{ |
| 246 » » » c.So(r.URL.Path, ShouldEqual, "/_ah/api/cas/v1/finalize/
abc") | 265 » » » { |
| 247 » » » w.WriteHeader(403) | 266 » » » » Method: "POST", |
| 267 » » » » Path: "/_ah/api/cas/v1/finalize/abc", |
| 268 » » » » Status: 403, |
| 269 » » » }, |
| 248 }) | 270 }) |
| 249 finished, err := remote.finalizeUpload("abc") | 271 finished, err := remote.finalizeUpload("abc") |
| 250 So(err, ShouldNotBeNil) | 272 So(err, ShouldNotBeNil) |
| 251 So(finished, ShouldBeFalse) | 273 So(finished, ShouldBeFalse) |
| 252 }) | 274 }) |
| 253 | 275 |
| 254 Convey("registerInstance REGISTERED", t, func(c C) { | 276 Convey("registerInstance REGISTERED", t, func(c C) { |
| 255 result, err := mockRegisterInstance(c, `{ | 277 result, err := mockRegisterInstance(c, `{ |
| 256 » » » "status": "REGISTERED", | 278 » » » » "status": "REGISTERED", |
| 257 » » » "instance": { | 279 » » » » "instance": { |
| 258 » » » » "registered_by": "user:abc@example.com", | 280 » » » » » "registered_by": "user:abc@example.com", |
| 259 » » » » "registered_ts": "1420244414571500" | 281 » » » » » "registered_ts": "1420244414571500" |
| 260 » » » } | 282 » » » » } |
| 261 » » }`) | 283 » » » }`) |
| 262 So(err, ShouldBeNil) | 284 So(err, ShouldBeNil) |
| 263 So(result, ShouldResemble, ®isterInstanceResponse{ | 285 So(result, ShouldResemble, ®isterInstanceResponse{ |
| 264 » » » Info: packageInstanceInfo{ | 286 » » » registeredBy: "user:abc@example.com", |
| 265 » » » » RegisteredBy: "user:abc@example.com", | 287 » » » registeredTs: time.Unix(0, 1420244414571500000), |
| 266 » » » » RegisteredTs: time.Unix(0, 1420244414571500000), | |
| 267 » » » }, | |
| 268 }) | 288 }) |
| 269 }) | 289 }) |
| 270 | 290 |
| 271 Convey("registerInstance ALREADY_REGISTERED", t, func(c C) { | 291 Convey("registerInstance ALREADY_REGISTERED", t, func(c C) { |
| 272 result, err := mockRegisterInstance(c, `{ | 292 result, err := mockRegisterInstance(c, `{ |
| 273 » » » "status": "ALREADY_REGISTERED", | 293 » » » » "status": "ALREADY_REGISTERED", |
| 274 » » » "instance": { | 294 » » » » "instance": { |
| 275 » » » » "registered_by": "user:abc@example.com", | 295 » » » » » "registered_by": "user:abc@example.com", |
| 276 » » » » "registered_ts": "1420244414571500" | 296 » » » » » "registered_ts": "1420244414571500" |
| 277 » » » } | 297 » » » » } |
| 278 » » }`) | 298 » » » }`) |
| 279 So(err, ShouldBeNil) | 299 So(err, ShouldBeNil) |
| 280 So(result, ShouldResemble, ®isterInstanceResponse{ | 300 So(result, ShouldResemble, ®isterInstanceResponse{ |
| 281 » » » AlreadyRegistered: true, | 301 » » » alreadyRegistered: true, |
| 282 » » » Info: packageInstanceInfo{ | 302 » » » registeredBy: "user:abc@example.com", |
| 283 » » » » RegisteredBy: "user:abc@example.com", | 303 » » » registeredTs: time.Unix(0, 1420244414571500000), |
| 284 » » » » RegisteredTs: time.Unix(0, 1420244414571500000), | |
| 285 » » » }, | |
| 286 }) | 304 }) |
| 287 }) | 305 }) |
| 288 | 306 |
| 289 Convey("registerInstance UPLOAD_FIRST", t, func(c C) { | 307 Convey("registerInstance UPLOAD_FIRST", t, func(c C) { |
| 290 result, err := mockRegisterInstance(c, `{ | 308 result, err := mockRegisterInstance(c, `{ |
| 291 » » » "status": "UPLOAD_FIRST", | 309 » » » » "status": "UPLOAD_FIRST", |
| 292 » » » "upload_session_id": "upload_session_id", | 310 » » » » "upload_session_id": "upload_session_id", |
| 293 » » » "upload_url": "http://upload_url" | 311 » » » » "upload_url": "http://upload_url" |
| 294 » » }`) | 312 » » » }`) |
| 295 So(err, ShouldBeNil) | 313 So(err, ShouldBeNil) |
| 296 So(result, ShouldResemble, ®isterInstanceResponse{ | 314 So(result, ShouldResemble, ®isterInstanceResponse{ |
| 297 » » » UploadSession: &uploadSession{ | 315 » » » uploadSession: &UploadSession{"upload_session_id", "http
://upload_url"}, |
| 298 » » » » ID: "upload_session_id", | |
| 299 » » » » URL: "http://upload_url", | |
| 300 » » » }, | |
| 301 }) | 316 }) |
| 302 }) | 317 }) |
| 303 | 318 |
| 304 Convey("registerInstance ERROR", t, func(c C) { | 319 Convey("registerInstance ERROR", t, func(c C) { |
| 305 result, err := mockRegisterInstance(c, `{ | 320 result, err := mockRegisterInstance(c, `{ |
| 306 » » » "status": "ERROR", | 321 » » » » "status": "ERROR", |
| 307 » » » "error_message": "Some error message" | 322 » » » » "error_message": "Some error message" |
| 308 » » }`) | 323 » » » }`) |
| 309 So(err, ShouldNotBeNil) | 324 So(err, ShouldNotBeNil) |
| 310 So(result, ShouldBeNil) | 325 So(result, ShouldBeNil) |
| 311 }) | 326 }) |
| 312 | 327 |
| 313 Convey("registerInstance unknown status", t, func(c C) { | 328 Convey("registerInstance unknown status", t, func(c C) { |
| 314 result, err := mockRegisterInstance(c, `{"status":"???"}`) | 329 result, err := mockRegisterInstance(c, `{"status":"???"}`) |
| 315 So(err, ShouldNotBeNil) | 330 So(err, ShouldNotBeNil) |
| 316 So(result, ShouldBeNil) | 331 So(result, ShouldBeNil) |
| 317 }) | 332 }) |
| 318 | 333 |
| 319 Convey("fetchInstance SUCCESS", t, func(c C) { | 334 Convey("fetchInstance SUCCESS", t, func(c C) { |
| 320 result, err := mockFetchInstance(c, `{ | 335 result, err := mockFetchInstance(c, `{ |
| 321 » » » "status": "SUCCESS", | 336 » » » » "status": "SUCCESS", |
| 322 » » » "instance": { | 337 » » » » "instance": { |
| 323 » » » » "registered_by": "user:abc@example.com", | 338 » » » » » "registered_by": "user:abc@example.com", |
| 324 » » » » "registered_ts": "1420244414571500" | 339 » » » » » "registered_ts": "1420244414571500" |
| 325 » » » }, | 340 » » » » }, |
| 326 » » » "fetch_url": "https://fetch_url" | 341 » » » » "fetch_url": "https://fetch_url" |
| 327 » » }`) | 342 » » » }`) |
| 328 So(err, ShouldBeNil) | 343 So(err, ShouldBeNil) |
| 329 So(result, ShouldResemble, &fetchInstanceResponse{ | 344 So(result, ShouldResemble, &fetchInstanceResponse{ |
| 330 » » » Info: packageInstanceInfo{ | 345 » » » registeredBy: "user:abc@example.com", |
| 331 » » » » RegisteredBy: "user:abc@example.com", | 346 » » » registeredTs: time.Unix(0, 1420244414571500000), |
| 332 » » » » RegisteredTs: time.Unix(0, 1420244414571500000), | 347 » » » fetchURL: "https://fetch_url", |
| 333 » » » }, | |
| 334 » » » FetchURL: "https://fetch_url", | |
| 335 }) | 348 }) |
| 336 }) | 349 }) |
| 337 | 350 |
| 338 Convey("fetchInstance PACKAGE_NOT_FOUND", t, func(c C) { | 351 Convey("fetchInstance PACKAGE_NOT_FOUND", t, func(c C) { |
| 339 result, err := mockFetchInstance(c, `{"status": "PACKAGE_NOT_FOU
ND"}`) | 352 result, err := mockFetchInstance(c, `{"status": "PACKAGE_NOT_FOU
ND"}`) |
| 340 So(err, ShouldNotBeNil) | 353 So(err, ShouldNotBeNil) |
| 341 So(result, ShouldBeNil) | 354 So(result, ShouldBeNil) |
| 342 }) | 355 }) |
| 343 | 356 |
| 344 Convey("fetchInstance INSTANCE_NOT_FOUND", t, func(c C) { | 357 Convey("fetchInstance INSTANCE_NOT_FOUND", t, func(c C) { |
| 345 result, err := mockFetchInstance(c, `{"status": "INSTANCE_NOT_FO
UND"}`) | 358 result, err := mockFetchInstance(c, `{"status": "INSTANCE_NOT_FO
UND"}`) |
| 346 So(err, ShouldNotBeNil) | 359 So(err, ShouldNotBeNil) |
| 347 So(result, ShouldBeNil) | 360 So(result, ShouldBeNil) |
| 348 }) | 361 }) |
| 349 | 362 |
| 350 Convey("fetchInstance ERROR", t, func(c C) { | 363 Convey("fetchInstance ERROR", t, func(c C) { |
| 351 result, err := mockFetchInstance(c, `{ | 364 result, err := mockFetchInstance(c, `{ |
| 352 » » » "status": "ERROR", | 365 » » » » "status": "ERROR", |
| 353 » » » "error_message": "Some error message" | 366 » » » » "error_message": "Some error message" |
| 354 » » }`) | 367 » » » }`) |
| 355 So(err, ShouldNotBeNil) | 368 So(err, ShouldNotBeNil) |
| 356 So(result, ShouldBeNil) | 369 So(result, ShouldBeNil) |
| 357 }) | 370 }) |
| 358 | 371 |
| 359 Convey("fetchACL SUCCESS", t, func(c C) { | 372 Convey("fetchACL SUCCESS", t, func(c C) { |
| 360 result, err := mockFetchACL(c, `{ | 373 result, err := mockFetchACL(c, `{ |
| 361 » » » "status": "SUCCESS", | 374 » » » » "status": "SUCCESS", |
| 362 » » » "acls": { | 375 » » » » "acls": { |
| 363 » » » » "acls": [ | 376 » » » » » "acls": [ |
| 364 » » » » » { | 377 » » » » » » { |
| 365 » » » » » » "package_path": "a", | 378 » » » » » » » "package_path": "a", |
| 366 » » » » » » "role": "OWNER", | 379 » » » » » » » "role": "OWNER", |
| 367 » » » » » » "principals": ["user:a", "group:
b"], | 380 » » » » » » » "principals": ["user:a",
"group:b"], |
| 368 » » » » » » "modified_by": "user:abc@example
.com", | 381 » » » » » » » "modified_by": "user:abc
@example.com", |
| 369 » » » » » » "modified_ts": "1420244414571500
" | 382 » » » » » » » "modified_ts": "14202444
14571500" |
| 370 » » » » » }, | 383 » » » » » » }, |
| 371 » » » » » { | 384 » » » » » » { |
| 372 » » » » » » "package_path": "a/b", | 385 » » » » » » » "package_path": "a/b", |
| 373 » » » » » » "role": "READER", | 386 » » » » » » » "role": "READER", |
| 374 » » » » » » "principals": ["group:c"], | 387 » » » » » » » "principals": ["group:c"
], |
| 375 » » » » » » "modified_by": "user:abc@example
.com", | 388 » » » » » » » "modified_by": "user:abc
@example.com", |
| 376 » » » » » » "modified_ts": "1420244414571500
" | 389 » » » » » » » "modified_ts": "14202444
14571500" |
| 377 » » » » » } | 390 » » » » » » } |
| 378 » » » » ] | 391 » » » » » ] |
| 379 » » » } | 392 » » » » } |
| 380 » » }`) | 393 » » » }`) |
| 381 So(err, ShouldBeNil) | 394 So(err, ShouldBeNil) |
| 382 So(result, ShouldResemble, []PackageACL{ | 395 So(result, ShouldResemble, []PackageACL{ |
| 383 { | 396 { |
| 384 PackagePath: "a", | 397 PackagePath: "a", |
| 385 Role: "OWNER", | 398 Role: "OWNER", |
| 386 Principals: []string{"user:a", "group:b"}, | 399 Principals: []string{"user:a", "group:b"}, |
| 387 ModifiedBy: "user:abc@example.com", | 400 ModifiedBy: "user:abc@example.com", |
| 388 ModifiedTs: time.Unix(0, 1420244414571500000), | 401 ModifiedTs: time.Unix(0, 1420244414571500000), |
| 389 }, | 402 }, |
| 390 { | 403 { |
| 391 PackagePath: "a/b", | 404 PackagePath: "a/b", |
| 392 Role: "READER", | 405 Role: "READER", |
| 393 Principals: []string{"group:c"}, | 406 Principals: []string{"group:c"}, |
| 394 ModifiedBy: "user:abc@example.com", | 407 ModifiedBy: "user:abc@example.com", |
| 395 ModifiedTs: time.Unix(0, 1420244414571500000), | 408 ModifiedTs: time.Unix(0, 1420244414571500000), |
| 396 }, | 409 }, |
| 397 }) | 410 }) |
| 398 }) | 411 }) |
| 399 | 412 |
| 400 Convey("fetchACL ERROR", t, func(c C) { | 413 Convey("fetchACL ERROR", t, func(c C) { |
| 401 result, err := mockFetchACL(c, `{ | 414 result, err := mockFetchACL(c, `{ |
| 402 » » » "status": "ERROR", | 415 » » » » "status": "ERROR", |
| 403 » » » "error_message": "Some error message" | 416 » » » » "error_message": "Some error message" |
| 404 » » }`) | 417 » » » }`) |
| 405 So(err, ShouldNotBeNil) | 418 So(err, ShouldNotBeNil) |
| 406 So(result, ShouldBeNil) | 419 So(result, ShouldBeNil) |
| 407 }) | 420 }) |
| 408 | 421 |
| 409 Convey("modifyACL SUCCESS", t, func(c C) { | 422 Convey("modifyACL SUCCESS", t, func(c C) { |
| 410 expected := `{ | 423 expected := `{ |
| 411 » » » "changes": [ | 424 » » » » "changes": [ |
| 412 » » » » { | 425 » » » » » { |
| 413 » » » » » "action": "GRANT", | 426 » » » » » » "action": "GRANT", |
| 414 » » » » » "role": "OWNER", | 427 » » » » » » "role": "OWNER", |
| 415 » » » » » "principal": "user:a@example.com" | 428 » » » » » » "principal": "user:a@example.com
" |
| 416 » » » » }, | 429 » » » » » }, |
| 417 » » » » { | 430 » » » » » { |
| 418 » » » » » "action": "REVOKE", | 431 » » » » » » "action": "REVOKE", |
| 419 » » » » » "role": "READER", | 432 » » » » » » "role": "READER", |
| 420 » » » » » "principal": "user:b@example.com" | 433 » » » » » » "principal": "user:b@example.com
" |
| 421 » » » » } | 434 » » » » » } |
| 422 » » » ] | 435 » » » » ] |
| 423 » » }` | 436 » » » }` |
| 424 // Strip " ", "\t" and "\n". | 437 // Strip " ", "\t" and "\n". |
| 425 expected = strings.Replace(expected, " ", "", -1) | 438 expected = strings.Replace(expected, " ", "", -1) |
| 426 expected = strings.Replace(expected, "\n", "", -1) | 439 expected = strings.Replace(expected, "\n", "", -1) |
| 427 expected = strings.Replace(expected, "\t", "", -1) | 440 expected = strings.Replace(expected, "\t", "", -1) |
| 428 | 441 |
| 429 err := mockModifyACL(c, []PackageACLChange{ | 442 err := mockModifyACL(c, []PackageACLChange{ |
| 430 { | 443 { |
| 431 Action: GrantRole, | 444 Action: GrantRole, |
| 432 Role: "OWNER", | 445 Role: "OWNER", |
| 433 Principal: "user:a@example.com", | 446 Principal: "user:a@example.com", |
| 434 }, | 447 }, |
| 435 { | 448 { |
| 436 Action: RevokeRole, | 449 Action: RevokeRole, |
| 437 Role: "READER", | 450 Role: "READER", |
| 438 Principal: "user:b@example.com", | 451 Principal: "user:b@example.com", |
| 439 }, | 452 }, |
| 440 }, expected, `{"status":"SUCCESS"}`) | 453 }, expected, `{"status":"SUCCESS"}`) |
| 441 So(err, ShouldBeNil) | 454 So(err, ShouldBeNil) |
| 442 }) | 455 }) |
| 443 | 456 |
| 444 Convey("modifyACL ERROR", t, func(c C) { | 457 Convey("modifyACL ERROR", t, func(c C) { |
| 445 err := mockModifyACL(c, []PackageACLChange{}, `{"changes":null}`
, `{ | 458 err := mockModifyACL(c, []PackageACLChange{}, `{"changes":null}`
, `{ |
| 446 » » » "status": "ERROR", | 459 » » » » "status": "ERROR", |
| 447 » » » "error_message": "Error message" | 460 » » » » "error_message": "Error message" |
| 448 » » }`) | 461 » » » }`) |
| 449 So(err, ShouldNotBeNil) | 462 So(err, ShouldNotBeNil) |
| 450 }) | 463 }) |
| 451 | 464 |
| 452 Convey("attachTags SUCCESS", t, func(c C) { | 465 Convey("attachTags SUCCESS", t, func(c C) { |
| 453 err := mockAttachTags( | 466 err := mockAttachTags( |
| 454 c, []string{"tag1:value1", "tag2:value2"}, | 467 c, []string{"tag1:value1", "tag2:value2"}, |
| 455 `{"tags":["tag1:value1","tag2:value2"]}`, | 468 `{"tags":["tag1:value1","tag2:value2"]}`, |
| 456 `{"status":"SUCCESS"}`) | 469 `{"status":"SUCCESS"}`) |
| 457 So(err, ShouldBeNil) | 470 So(err, ShouldBeNil) |
| 458 }) | 471 }) |
| 459 | 472 |
| 460 » Convey("attachTags no tags", t, func(c C) { | 473 » Convey("attachTags bad tag", t, func(c C) { |
| 461 » » err := mockAttachTags(c, nil, "", "") | 474 » » err := mockRemoteImpl(c, nil).attachTags( |
| 475 » » » Pin{"pkgname", "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
"}, |
| 476 » » » []string{"BADTAG"}) |
| 462 So(err, ShouldNotBeNil) | 477 So(err, ShouldNotBeNil) |
| 463 }) | 478 }) |
| 464 | 479 |
| 465 Convey("attachTags bad tag", t, func(c C) { | |
| 466 err := mockAttachTags(c, []string{"BADTAG"}, "", "") | |
| 467 So(err, ShouldNotBeNil) | |
| 468 }) | |
| 469 | |
| 470 Convey("attachTags PROCESSING_NOT_FINISHED_YET", t, func(c C) { | 480 Convey("attachTags PROCESSING_NOT_FINISHED_YET", t, func(c C) { |
| 471 err := mockAttachTags( | 481 err := mockAttachTags( |
| 472 c, []string{"tag1:value1", "tag2:value2"}, | 482 c, []string{"tag1:value1", "tag2:value2"}, |
| 473 `{"tags":["tag1:value1","tag2:value2"]}`, | 483 `{"tags":["tag1:value1","tag2:value2"]}`, |
| 474 `{"status":"PROCESSING_NOT_FINISHED_YET", "error_message
":"Blah"}`) | 484 `{"status":"PROCESSING_NOT_FINISHED_YET", "error_message
":"Blah"}`) |
| 475 So(err, ShouldResemble, &pendingProcessingError{message: "Blah"}
) | 485 So(err, ShouldResemble, &pendingProcessingError{message: "Blah"}
) |
| 476 }) | 486 }) |
| 477 | 487 |
| 478 Convey("attachTags ERROR", t, func(c C) { | 488 Convey("attachTags ERROR", t, func(c C) { |
| 479 err := mockAttachTags( | 489 err := mockAttachTags( |
| 480 c, []string{"tag1:value1", "tag2:value2"}, | 490 c, []string{"tag1:value1", "tag2:value2"}, |
| 481 `{"tags":["tag1:value1","tag2:value2"]}`, | 491 `{"tags":["tag1:value1","tag2:value2"]}`, |
| 482 `{"status":"ERROR", "error_message":"Blah"}`) | 492 `{"status":"ERROR", "error_message":"Blah"}`) |
| 483 So(err, ShouldNotBeNil) | 493 So(err, ShouldNotBeNil) |
| 484 }) | 494 }) |
| 485 } | 495 } |
| 486 | 496 |
| 487 //////////////////////////////////////////////////////////////////////////////// | 497 //////////////////////////////////////////////////////////////////////////////// |
| 488 | 498 |
| 489 type expectedHTTPCall struct { | 499 func mockRemoteImpl(c C, expectations []expectedHTTPCall) *remoteImpl { |
| 490 » Method string | 500 » return &remoteImpl{mockClient(c, expectations)} |
| 491 » Path string | |
| 492 » Reply string | |
| 493 » Query url.Values | |
| 494 » Status int | |
| 495 } | 501 } |
| 496 | |
| 497 func mockServerWithMux(mux *http.ServeMux) (*httptest.Server, *http.Client) { | |
| 498 server := httptest.NewServer(mux) | |
| 499 transport := &http.Transport{ | |
| 500 Proxy: func(req *http.Request) (*url.URL, error) { | |
| 501 return url.Parse(server.URL) | |
| 502 }, | |
| 503 } | |
| 504 Reset(func() { server.Close() }) | |
| 505 return server, &http.Client{Transport: transport} | |
| 506 } | |
| 507 | |
| 508 func mockServerWithHandler(pattern string, handler http.HandlerFunc) (*httptest.
Server, *http.Client) { | |
| 509 mux := http.NewServeMux() | |
| 510 mux.HandleFunc(pattern, handler) | |
| 511 return mockServerWithMux(mux) | |
| 512 } | |
| 513 | |
| 514 func mockRemoteService(handler http.HandlerFunc) *remoteService { | |
| 515 server, client := mockServerWithHandler("/", handler) | |
| 516 remote := &remoteService{ | |
| 517 client: client, | |
| 518 serviceURL: server.URL, | |
| 519 log: logging.DefaultLogger, | |
| 520 } | |
| 521 prev := newRemoteService | |
| 522 newRemoteService = func(client *http.Client, url string, log logging.Log
ger) *remoteService { | |
| 523 return remote | |
| 524 } | |
| 525 Reset(func() { newRemoteService = prev }) | |
| 526 return remote | |
| 527 } | |
| 528 | |
| 529 func mockRemoteServiceWithExpectations(expectations []expectedHTTPCall) *remoteS
ervice { | |
| 530 index := 0 | |
| 531 return mockRemoteService(func(w http.ResponseWriter, r *http.Request) { | |
| 532 // Can't use So(...) assertions here. They are not recognized. R
eturn | |
| 533 // errors via HTTP instead, to let the main test case catch them
. | |
| 534 msg := "" | |
| 535 exp := expectedHTTPCall{} | |
| 536 if index >= len(expectations) { | |
| 537 msg = "Unexpected call" | |
| 538 } else { | |
| 539 // Fill in defaults. | |
| 540 exp = expectations[index] | |
| 541 if exp.Method == "" { | |
| 542 exp.Method = "GET" | |
| 543 } | |
| 544 if exp.Query == nil { | |
| 545 exp.Query = url.Values{} | |
| 546 } | |
| 547 // Check that request is what it is expected to be. | |
| 548 if r.URL.Path != exp.Path { | |
| 549 msg = fmt.Sprintf("Expecting call to %s, got %s
instead", exp.Path, r.URL.Path) | |
| 550 } else if !reflect.DeepEqual(r.URL.Query(), exp.Query) { | |
| 551 msg = fmt.Sprintf("Expecting query string %v, go
t %v instead", exp.Query, r.URL.Query()) | |
| 552 } else if r.Method != exp.Method { | |
| 553 msg = fmt.Sprintf("Expecting %s to %s, got %s in
stead", exp.Method, exp.Path, r.Method) | |
| 554 } | |
| 555 } | |
| 556 | |
| 557 // Error? | |
| 558 if msg != "" { | |
| 559 w.WriteHeader(400) | |
| 560 w.Write([]byte(msg)) | |
| 561 return | |
| 562 } | |
| 563 | |
| 564 // Mocked reply. | |
| 565 if exp.Status != 0 { | |
| 566 w.WriteHeader(exp.Status) | |
| 567 } | |
| 568 if exp.Reply != "" { | |
| 569 w.Write([]byte(exp.Reply)) | |
| 570 } | |
| 571 index++ | |
| 572 }) | |
| 573 } | |
| OLD | NEW |