Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(621)

Side by Side Diff: go/src/infra/tools/cipd/uploader_test.go

Issue 1129043003: cipd: Refactor client to make it more readable. (Closed) Base URL: https://chromium.googlesource.com/infra/infra.git@master
Patch Set: Created 5 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « go/src/infra/tools/cipd/uploader.go ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
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
3 // found in the LICENSE file.
4
5 package cipd
6
7 import (
8 "bytes"
9 "fmt"
10 "io/ioutil"
11 "net/http"
12 "net/url"
13 "testing"
14 "time"
15
16 "infra/libs/logging"
17
18 . "github.com/smartystreets/goconvey/convey"
19 )
20
21 func TestUploadToCAS(t *testing.T) {
22 Convey("Given a mocked service and clock", t, func() {
23 mockClock(time.Now())
24 mockResumableUpload()
25
26 Convey("UploadToCAS full flow", func() {
27 mockRemoteServiceWithExpectations([]expectedHTTPCall{
28 {
29 Method: "POST",
30 Path: "/_ah/api/cas/v1/upload/SHA1/abc ",
31 Reply: `{"status":"SUCCESS","upload_ses sion_id":"12345","upload_url":"http://localhost"}`,
32 },
33 {
34 Method: "POST",
35 Path: "/_ah/api/cas/v1/finalize/12345" ,
36 Reply: `{"status":"VERIFYING"}`,
37 },
38 {
39 Method: "POST",
40 Path: "/_ah/api/cas/v1/finalize/12345" ,
41 Reply: `{"status":"PUBLISHED"}`,
42 },
43 })
44 err := UploadToCAS(UploadToCASOptions{SHA1: "abc"})
45 So(err, ShouldBeNil)
46 })
47
48 Convey("UploadToCAS timeout", func() {
49 // Append a bunch of "still verifying" responses at the end.
50 calls := []expectedHTTPCall{
51 {
52 Method: "POST",
53 Path: "/_ah/api/cas/v1/upload/SHA1/abc ",
54 Reply: `{"status":"SUCCESS","upload_ses sion_id":"12345","upload_url":"http://localhost"}`,
55 },
56 }
57 for i := 0; i < 19; i++ {
58 calls = append(calls, expectedHTTPCall{
59 Method: "POST",
60 Path: "/_ah/api/cas/v1/finalize/12345" ,
61 Reply: `{"status":"VERIFYING"}`,
62 })
63 }
64 mockRemoteServiceWithExpectations(calls)
65 err := UploadToCAS(UploadToCASOptions{SHA1: "abc"})
66 So(err, ShouldEqual, ErrFinalizationTimeout)
67 })
68 })
69 }
70
71 func TestRegisterInstance(t *testing.T) {
72 Convey("Mocking remote service", t, func() {
73 mockClock(time.Now())
74 mockResumableUpload()
75
76 // Build an empty package to be uploaded.
77 out := bytes.Buffer{}
78 err := BuildInstance(BuildInstanceOptions{
79 Input: []File{},
80 Output: &out,
81 PackageName: "testing",
82 })
83 So(err, ShouldBeNil)
84
85 // Open it for reading.
86 inst, err := OpenInstance(bytes.NewReader(out.Bytes()), "")
87 So(err, ShouldBeNil)
88 Reset(func() { inst.Close() })
89
90 Convey("RegisterInstance full flow", func() {
91 mockRemoteServiceWithExpectations([]expectedHTTPCall{
92 {
93 Method: "POST",
94 Path: "/_ah/api/repo/v1/instance",
95 Query: url.Values{
96 "instance_id": []string{inst.In stanceID()},
97 "package_name": []string{inst.Pa ckageName()},
98 },
99 Reply: `{
100 "status": "UPLOAD_FIRST",
101 "upload_session_id": "12345",
102 "upload_url": "http://localhost"
103 }`,
104 },
105 {
106 Method: "POST",
107 Path: "/_ah/api/cas/v1/finalize/12345" ,
108 Reply: `{"status":"PUBLISHED"}`,
109 },
110 {
111 Method: "POST",
112 Path: "/_ah/api/repo/v1/instance",
113 Query: url.Values{
114 "instance_id": []string{inst.In stanceID()},
115 "package_name": []string{inst.Pa ckageName()},
116 },
117 Reply: `{
118 "status": "REGISTERED",
119 "instance": {
120 "registered_by": "user:a @example.com",
121 "registered_ts": "0"
122 }
123 }`,
124 },
125 })
126 err = RegisterInstance(RegisterInstanceOptions{PackageIn stance: inst})
127 So(err, ShouldBeNil)
128 })
129
130 Convey("RegisterInstance already registered", func() {
131 mockRemoteServiceWithExpectations([]expectedHTTPCall{
132 {
133 Method: "POST",
134 Path: "/_ah/api/repo/v1/instance",
135 Query: url.Values{
136 "instance_id": []string{inst.In stanceID()},
137 "package_name": []string{inst.Pa ckageName()},
138 },
139 Reply: `{
140 "status": "ALREADY_REGISTERED",
141 "instance": {
142 "registered_by": "user:a @example.com",
143 "registered_ts": "0"
144 }
145 }`,
146 },
147 })
148 err = RegisterInstance(RegisterInstanceOptions{PackageIn stance: inst})
149 So(err, ShouldBeNil)
150 })
151 })
152 }
153
154 func TestResumableUpload(t *testing.T) {
155 Convey("Resumable upload full flow", t, func(c C) {
156 mockClock(time.Now())
157
158 dataToUpload := "0123456789abcdef"
159 totalLen := len(dataToUpload)
160 uploaded := bytes.NewBuffer(nil)
161 errors := 0
162
163 server, client := mockServerWithHandler("/", func(w http.Respons eWriter, r *http.Request) {
164 c.So(r.URL.Path, ShouldEqual, "/upl")
165 c.So(r.Method, ShouldEqual, "PUT")
166
167 rangeHeader := r.Header.Get("Content-Range")
168 body, err := ioutil.ReadAll(r.Body)
169 c.So(err, ShouldBeNil)
170
171 // Insert a bunch of consecutive transient errors in the middle.
172 cur := uploaded.Len()
173 if cur > totalLen/2 && errors < 3 {
174 errors++
175 w.WriteHeader(500)
176 return
177 }
178
179 // Request for uploaded offset.
180 if len(body) == 0 {
181 c.So(rangeHeader, ShouldEqual, fmt.Sprintf("byte s */%d", totalLen))
182 if cur == totalLen {
183 w.WriteHeader(200)
184 return
185 }
186 if cur != 0 {
187 w.Header().Set("Range", fmt.Sprintf("byt es=0-%d", cur-1))
188 }
189 w.WriteHeader(308)
190 return
191 }
192
193 // Uploading next chunk.
194 c.So(rangeHeader, ShouldEqual, fmt.Sprintf("bytes %d-%d/ %d", cur, cur+len(body)-1, totalLen))
195 _, err = uploaded.Write(body)
196 c.So(err, ShouldBeNil)
197 if uploaded.Len() == totalLen {
198 w.WriteHeader(200)
199 } else {
200 w.WriteHeader(308)
201 }
202 })
203
204 err := resumableUpload(server.URL+"/upl", 3, UploadToCASOptions{
205 SHA1: "abc",
206 Data: bytes.NewReader([]byte(dataToUpload)),
207 UploadOptions: UploadOptions{
208 Client: client,
209 Log: logging.DefaultLogger,
210 },
211 })
212 So(err, ShouldBeNil)
213 So(uploaded.Bytes(), ShouldResemble, []byte(dataToUpload))
214 })
215 }
216
217 func TestAttachTagsWhenReady(t *testing.T) {
218 Convey("Mocking clock", t, func() {
219 mockClock(time.Now())
220
221 Convey("attachTagsWhenReady works", func() {
222 remote := mockRemoteServiceWithExpectations([]expectedHT TPCall{
223 {
224 Method: "POST",
225 Path: "/_ah/api/repo/v1/tags",
226 Query: url.Values{
227 "instance_id": []string{"aaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"},
228 "package_name": []string{"pkgnam e"},
229 },
230 Reply: `{"status": "PROCESSING_NOT_FINIS HED_YET"}`,
231 },
232 {
233 Method: "POST",
234 Path: "/_ah/api/repo/v1/tags",
235 Query: url.Values{
236 "instance_id": []string{"aaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"},
237 "package_name": []string{"pkgnam e"},
238 },
239 Reply: `{"status": "SUCCESS"}`,
240 },
241 })
242 err := attachTagsWhenReady(
243 remote, "pkgname", "aaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaa",
244 []string{"tag1:value1"}, logging.DefaultLogger)
245 So(err, ShouldBeNil)
246 })
247
248 Convey("attachTagsWhenReady timeout", func() {
249 calls := []expectedHTTPCall{}
250 for i := 0; i < 20; i++ {
251 calls = append(calls, expectedHTTPCall{
252 Method: "POST",
253 Path: "/_ah/api/repo/v1/tags",
254 Query: url.Values{
255 "instance_id": []string{"aaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"},
256 "package_name": []string{"pkgnam e"},
257 },
258 Reply: `{"status": "PROCESSING_NOT_FINIS HED_YET"}`,
259 })
260 }
261 remote := mockRemoteServiceWithExpectations(calls)
262 err := attachTagsWhenReady(
263 remote, "pkgname", "aaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaa",
264 []string{"tag1:value1"}, logging.DefaultLogger)
265 So(err, ShouldEqual, ErrAttachTagsTimeout)
266 })
267 })
268 }
269
270 func mockResumableUpload() {
271 prev := resumableUpload
272 resumableUpload = func(string, int64, UploadToCASOptions) error {
273 return nil
274 }
275 Reset(func() { resumableUpload = prev })
276 }
OLDNEW
« no previous file with comments | « go/src/infra/tools/cipd/uploader.go ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698