~julian-edwards/gwacl/fix-block-lengths

14.2.1 by Julian Edwards
Add storage beginning
1
// Copyright 2013 Canonical Ltd.  This software is licensed under the
2
// GNU Lesser General Public License version 3 (see the file COPYING).
3
4
package gwacl
5
6
import (
42.1.1 by Julian Edwards
Add PutBlock
7
    "bytes"
14.2.10 by Julian Edwards
Add composeAuthHeader
8
    "encoding/base64"
14.2.3 by Julian Edwards
add TestComposeCanonicalizedResource
9
    "fmt"
17.1.3 by Julian Edwards
Add Canonicalized Headers computation
10
    "io/ioutil"
14.2.1 by Julian Edwards
Add storage beginning
11
    . "launchpad.net/gocheck"
45.1.1 by Julian Edwards
Add a PutBlockList call
12
    "launchpad.net/gwacl/dedent"
14.2.1 by Julian Edwards
Add storage beginning
13
    "net/http"
14.2.5 by Julian Edwards
add toLowerKeys
14
    "net/url"
14.2.9 by Julian Edwards
add composeStringToSign
15
    "strings"
17.1.5 by Julian Edwards
Add Date header
16
    "time"
14.2.1 by Julian Edwards
Add storage beginning
17
)
18
60.2.2 by Jeroen Vermeulen
Unexport TestComposeHeaders.
19
type testComposeHeaders struct{}
20
21
var _ = Suite(&testComposeHeaders{})
22
122.2.1 by Raphael Badin
Propagate Azure errors, add IsNotFoundError() method.
23
func makeHttpResponse(status int, body string) *http.Response {
24
    return &http.Response{
25
        Status:     fmt.Sprintf("%d", status),
26
        StatusCode: status,
27
        Body:       makeResponseBody(body),
28
    }
29
}
30
60.2.2 by Jeroen Vermeulen
Unexport TestComposeHeaders.
31
func (suite *testComposeHeaders) TestNoHeaders(c *C) {
14.2.1 by Julian Edwards
Add storage beginning
32
    req, err := http.NewRequest("GET", "http://example.com", nil)
33
    c.Assert(err, IsNil)
34
35
    observed := composeHeaders(req)
17.1.4 by Julian Edwards
Add newlines consistently in each processed section
36
    expected := "\n\n\n\n\n\n\n\n\n\n\n"
14.2.1 by Julian Edwards
Add storage beginning
37
38
    c.Assert(observed, Equals, expected)
39
}
14.2.2 by Julian Edwards
Add Test_composeHeaders
40
60.2.2 by Jeroen Vermeulen
Unexport TestComposeHeaders.
41
func (suite *testComposeHeaders) TestCreatesHeaders(c *C) {
14.2.2 by Julian Edwards
Add Test_composeHeaders
42
    req, err := http.NewRequest("GET", "http://example.com", nil)
43
    c.Assert(err, IsNil)
44
14.2.9 by Julian Edwards
add composeStringToSign
45
    var items []string
139.1.1 by Gavin Panella
Camel-case everything, clean-up some comments.
46
    for i, headerName := range headersToSign {
14.2.9 by Julian Edwards
add composeStringToSign
47
        v := fmt.Sprintf("%d", i)
139.1.1 by Gavin Panella
Camel-case everything, clean-up some comments.
48
        req.Header.Set(headerName, v)
17.1.4 by Julian Edwards
Add newlines consistently in each processed section
49
        items = append(items, v+"\n")
14.2.2 by Julian Edwards
Add Test_composeHeaders
50
    }
17.1.4 by Julian Edwards
Add newlines consistently in each processed section
51
    expected := strings.Join(items, "")
14.2.2 by Julian Edwards
Add Test_composeHeaders
52
53
    observed := composeHeaders(req)
54
    c.Assert(observed, Equals, expected)
55
}
14.2.3 by Julian Edwards
add TestComposeCanonicalizedResource
56
60.2.2 by Jeroen Vermeulen
Unexport TestComposeHeaders.
57
func (suite *testComposeHeaders) TestCanonicalizedHeaders(c *C) {
17.1.3 by Julian Edwards
Add Canonicalized Headers computation
58
    req, err := http.NewRequest("GET", "http://example.com", nil)
59
    c.Assert(err, IsNil)
60
    req.Header.Set("x-ms-why", "aye")
61
    req.Header.Set("x-ms-foo", "bar")
62
    req.Header.Set("invalid", "blah")
63
64
    expected := "x-ms-foo:bar\nx-ms-why:aye\n"
65
    observed := composeCanonicalizedHeaders(req)
66
    c.Check(observed, Equals, expected)
67
}
68
14.2.3 by Julian Edwards
add TestComposeCanonicalizedResource
69
type TestComposeCanonicalizedResource struct{}
70
71
var _ = Suite(&TestComposeCanonicalizedResource{})
72
73
func (suite *TestComposeCanonicalizedResource) TestPrependsSlash(c *C) {
14.2.4 by Julian Edwards
Add TestPrependsSlash
74
    req, err := http.NewRequest("GET", "http://example.com", nil)
75
    c.Assert(err, IsNil)
76
    path := MakeRandomString(10)
77
    req.URL.Path = path
139.1.1 by Gavin Panella
Camel-case everything, clean-up some comments.
78
    accountName := MakeRandomString(10)
79
    observed := composeCanonicalizedResource(req, accountName)
80
    expected := "/" + accountName + "/" + path
14.2.4 by Julian Edwards
Add TestPrependsSlash
81
    c.Assert(observed, Equals, expected)
14.2.3 by Julian Edwards
add TestComposeCanonicalizedResource
82
}
83
14.2.13 by Julian Edwards
rvb's review suggestions
84
func (suite *TestComposeCanonicalizedResource) TestCreatesResource(c *C) {
14.2.3 by Julian Edwards
add TestComposeCanonicalizedResource
85
    path := MakeRandomString(5)
86
    req, err := http.NewRequest("GET", fmt.Sprintf("http://example.com/%s", path), nil)
87
    c.Assert(err, IsNil)
88
139.1.1 by Gavin Panella
Camel-case everything, clean-up some comments.
89
    accountName := MakeRandomString(10)
90
    observed := composeCanonicalizedResource(req, accountName)
91
    expected := "/" + accountName + "/" + path
14.2.3 by Julian Edwards
add TestComposeCanonicalizedResource
92
    c.Assert(observed, Equals, expected)
93
}
14.2.5 by Julian Edwards
add toLowerKeys
94
95
func (suite *TestComposeCanonicalizedResource) TestQueryParams(c *C) {
96
    req, err := http.NewRequest(
97
        "GET", "http://example.com/?Kevin=Perry&foo=bar", nil)
98
    c.Assert(err, IsNil)
99
139.1.1 by Gavin Panella
Camel-case everything, clean-up some comments.
100
    accountName := MakeRandomString(10)
101
    observed := composeCanonicalizedResource(req, accountName)
102
    expected := "/" + accountName + "/\n" + "foo:bar" + "\n" + "kevin:Perry"
14.2.5 by Julian Edwards
add toLowerKeys
103
    c.Assert(observed, Equals, expected)
104
}
105
106
func (suite *TestComposeCanonicalizedResource) TestToLowerKeys(c *C) {
14.2.6 by Julian Edwards
Add test for encodeParams
107
    values := url.Values{
108
        "foo":   []string{"bar", "baz"},
109
        "alpha": []string{"gamma", "delta"},
110
        "quux":  []string{"flobble"},
111
        "BIG":   []string{"Big", "Data"},
112
        "big":   []string{"Big", "Little"},
14.2.5 by Julian Edwards
add toLowerKeys
113
    }
114
115
    observed := toLowerKeys(values)
14.2.6 by Julian Edwards
Add test for encodeParams
116
    expected := map[string][]string{
72.3.1 by Jeroen Vermeulen
gofmt -s
117
        "foo":   {"bar", "baz"},
118
        "alpha": {"delta", "gamma"},
119
        "quux":  {"flobble"},
120
        "big":   {"Big", "Big", "Data", "Little"},
14.2.5 by Julian Edwards
add toLowerKeys
121
    }
122
123
    c.Check(observed, DeepEquals, expected)
124
}
14.2.6 by Julian Edwards
Add test for encodeParams
125
126
func (suite *TestComposeCanonicalizedResource) TestEncodeParams(c *C) {
127
    input := map[string][]string{
72.3.1 by Jeroen Vermeulen
gofmt -s
128
        "foo":   {"bar", "baz"},
129
        "alpha": {"delta", "gamma"},
130
        "quux":  {"flobble"},
131
        "big":   {"Big", "Big", "Data", "Little"},
14.2.6 by Julian Edwards
Add test for encodeParams
132
    }
133
134
    observed := encodeParams(input)
135
    expected := ("alpha:delta,gamma\nbig:Big,Big,Data,Little\n" +
136
        "foo:bar,baz\nquux:flobble")
137
    c.Assert(observed, Equals, expected)
138
}
14.2.7 by Julian Edwards
Add TestEncodeParamsEmpty
139
140
func (suite *TestComposeCanonicalizedResource) TestEncodeParamsEmpty(c *C) {
141
    input := map[string][]string{}
142
    observed := encodeParams(input)
143
    expected := ""
144
    c.Assert(observed, Equals, expected)
145
}
14.2.9 by Julian Edwards
add composeStringToSign
146
147
type TestComposeStringToSign struct{}
148
149
var _ = Suite(&TestComposeStringToSign{})
150
151
func (suite *TestComposeStringToSign) TestFullRequest(c *C) {
152
    req, err := http.NewRequest(
153
        "GET", "http://example.com/mypath?Kevin=Perry&foo=bar", nil)
154
    c.Assert(err, IsNil)
139.1.1 by Gavin Panella
Camel-case everything, clean-up some comments.
155
    for i, headerName := range headersToSign {
156
        req.Header.Set(headerName, fmt.Sprintf("%v", i))
14.2.9 by Julian Edwards
add composeStringToSign
157
    }
17.1.5 by Julian Edwards
Add Date header
158
    req.Header.Set("x-ms-testing", "foo")
159
    expected := "GET\n0\n1\n2\n3\n4\n5\n6\n7\n8\n9\n10\nx-ms-testing:foo\n/myaccount/mypath\nfoo:bar\nkevin:Perry"
14.2.9 by Julian Edwards
add composeStringToSign
160
    observed := composeStringToSign(req, "myaccount")
161
    c.Assert(observed, Equals, expected)
162
}
14.2.10 by Julian Edwards
Add composeAuthHeader
163
199.2.7 by Raphael Badin
Review fixes.
164
type TestSign struct{}
165
166
var _ = Suite(&TestSign{})
167
168
func (suite *TestSign) TestSign(c *C) {
169
    key := base64.StdEncoding.EncodeToString([]byte("dummykey"))
170
    signable := "a-string-to-sign"
171
172
    observed := sign(key, signable)
173
    expected := "5j1DSsm07IEh3u9JQQd0KPwtM6pEGChzrAF7Zf/LxLc="
174
    c.Assert(observed, Equals, expected)
175
}
176
14.2.10 by Julian Edwards
Add composeAuthHeader
177
type TestComposeAuthHeader struct{}
178
179
var _ = Suite(&TestComposeAuthHeader{})
180
14.2.13 by Julian Edwards
rvb's review suggestions
181
func (suite *TestComposeAuthHeader) TestCreatesHeaderString(c *C) {
14.2.10 by Julian Edwards
Add composeAuthHeader
182
    req, err := http.NewRequest(
183
        "GET", "http://example.com/mypath?Kevin=Perry&foo=bar", nil)
184
    c.Assert(err, IsNil)
185
186
    key := base64.StdEncoding.EncodeToString([]byte("dummykey"))
187
188
    observed := composeAuthHeader(req, "myname", key)
189
    expected := "SharedKey myname:Xf9hWQ99mM0IyEOL6rNeAUdTQlixVqiYnt2TpLCCpY0="
190
    c.Assert(observed, Equals, expected)
191
}
14.2.11 by Julian Edwards
Add signRequest
192
193
type TestSignRequest struct{}
194
195
var _ = Suite(&TestSignRequest{})
196
14.2.13 by Julian Edwards
rvb's review suggestions
197
func (suite *TestSignRequest) TestAddsHeaderToRequest(c *C) {
14.2.11 by Julian Edwards
Add signRequest
198
    req, err := http.NewRequest(
199
        "GET", "http://example.com/mypath?Kevin=Perry&foo=bar", nil)
200
    c.Assert(err, IsNil)
201
    c.Assert(req.Header.Get("Authorization"), Equals, "")
202
203
    key := base64.StdEncoding.EncodeToString([]byte("dummykey"))
101.1.9 by Jeroen Vermeulen
Removed a bit of Java-like silliness.
204
    context := StorageContext{client: nil, Account: "myname", Key: key}
101.1.1 by Jeroen Vermeulen
Extract signRequest from addStandardHeaders, and make it a method.
205
    context.signRequest(req)
14.2.11 by Julian Edwards
Add signRequest
206
207
    expected := "SharedKey myname:Xf9hWQ99mM0IyEOL6rNeAUdTQlixVqiYnt2TpLCCpY0="
208
    c.Assert(req.Header.Get("Authorization"), Equals, expected)
209
}
17.1.1 by Julian Edwards
Add addVersionHeader
210
127.2.1 by Raphael Badin
If key is empty: anon access.
211
func (suite *TestSignRequest) TestDoesNotAddHeaderIfEmptyKey(c *C) {
212
    req, err := http.NewRequest(
213
        "GET", "http://example.com/mypath?Kevin=Perry&foo=bar", nil)
214
    c.Assert(err, IsNil)
215
    c.Assert(req.Header.Get("Authorization"), Equals, "")
216
217
    context := StorageContext{client: nil, Account: "myname", Key: ""}
218
    context.signRequest(req)
219
220
    c.Assert(req.Header.Get("Authorization"), Equals, "")
221
}
222
17.1.3 by Julian Edwards
Add Canonicalized Headers computation
223
type TestRequestHeaders struct{}
224
17.1.1 by Julian Edwards
Add addVersionHeader
225
var _ = Suite(&TestRequestHeaders{})
226
227
func (suite *TestRequestHeaders) TestAddsVersionHeaderToRequest(c *C) {
228
    req, err := http.NewRequest("GET", "http://example.com/", nil)
229
    c.Assert(err, IsNil)
17.1.5 by Julian Edwards
Add Date header
230
    addVersionHeader(req, "2012-02-12")
17.1.1 by Julian Edwards
Add addVersionHeader
231
    c.Assert(req.Header.Get("x-ms-version"), Equals, "2012-02-12")
232
}
233
43.2.1 by Julian Edwards
Add content-length to outgoing requests
234
func (suite *TestRequestHeaders) TestContentHeader(c *C) {
17.1.2 by Julian Edwards
Add addMD5Header
235
    data := "test data"
45.1.3 by Julian Edwards
changes to make PutBlock work - prevent Go from chunking the data
236
    req, err := http.NewRequest("PUT", "http://example.com/", strings.NewReader(data))
17.1.2 by Julian Edwards
Add addMD5Header
237
    c.Assert(err, IsNil)
43.2.1 by Julian Edwards
Add content-length to outgoing requests
238
    addContentHeaders(req)
239
    c.Assert(
240
        req.Header.Get("Content-Length"), Equals, fmt.Sprintf("%d", len(data)))
17.1.2 by Julian Edwards
Add addMD5Header
241
242
    // Ensure that reading the request data didn't destroy it.
243
    reqdata, _ := ioutil.ReadAll(req.Body)
244
    c.Assert(data, Equals, string(reqdata))
245
}
17.1.5 by Julian Edwards
Add Date header
246
45.1.3 by Julian Edwards
changes to make PutBlock work - prevent Go from chunking the data
247
func (suite *TestRequestHeaders) TestLengthHeaderNotSetForGET(c *C) {
248
    req, err := http.NewRequest("GET", "http://example.com/", nil)
249
    c.Assert(err, IsNil)
250
    addContentHeaders(req)
139.1.1 by Gavin Panella
Camel-case everything, clean-up some comments.
251
    _, lengthPresent := req.Header[http.CanonicalHeaderKey("Content-Length")]
252
    c.Assert(lengthPresent, Equals, false)
45.1.3 by Julian Edwards
changes to make PutBlock work - prevent Go from chunking the data
253
}
254
43.2.1 by Julian Edwards
Add content-length to outgoing requests
255
func (suite *TestRequestHeaders) TestContentHeaderWithNoBody(c *C) {
45.1.3 by Julian Edwards
changes to make PutBlock work - prevent Go from chunking the data
256
    req, err := http.NewRequest("PUT", "http://example.com/", nil)
43.2.1 by Julian Edwards
Add content-length to outgoing requests
257
    c.Assert(err, IsNil)
258
    addContentHeaders(req)
139.1.1 by Gavin Panella
Camel-case everything, clean-up some comments.
259
    _, md5Present := req.Header[http.CanonicalHeaderKey("Content-MD5")]
260
    c.Check(md5Present, Equals, false)
45.1.3 by Julian Edwards
changes to make PutBlock work - prevent Go from chunking the data
261
    content := req.Header.Get("Content-Length")
262
    c.Check(content, Equals, "0")
43.2.1 by Julian Edwards
Add content-length to outgoing requests
263
}
264
17.1.5 by Julian Edwards
Add Date header
265
func (suite *TestRequestHeaders) TestDateHeader(c *C) {
266
    req, err := http.NewRequest("GET", "http://example.com/", nil)
267
    c.Assert(err, IsNil)
45.1.5 by Julian Edwards
Tweaks as per allenap
268
    c.Assert(req.Header.Get("Date"), Equals, "")
17.1.5 by Julian Edwards
Add Date header
269
    addDateHeader(req)
45.1.5 by Julian Edwards
Tweaks as per allenap
270
    observed := req.Header.Get("Date")
139.1.1 by Gavin Panella
Camel-case everything, clean-up some comments.
271
    observedTime, err := time.Parse(time.RFC1123, observed)
17.1.6 by Julian Edwards
allenap's review suggestions
272
    c.Assert(err, IsNil)
139.1.1 by Gavin Panella
Camel-case everything, clean-up some comments.
273
    difference := time.Now().UTC().Sub(observedTime)
17.1.5 by Julian Edwards
Add Date header
274
    if difference.Minutes() > 1.0 {
275
        c.FailNow()
276
    }
277
}
26.1.1 by Gavin Panella
Add StorageContext.
278
279
type TestStorageContext struct{}
280
281
var _ = Suite(&TestStorageContext{})
282
98.3.8 by Jeroen Vermeulen
Whittle down tests for StorageContext.get*URL.
283
// makeNastyURLUnfriendlyString returns a string that really needs escaping
284
// before it can be included in a URL.
285
func makeNastyURLUnfriendlyString() string {
286
    return MakeRandomString(3) + "?&" + MakeRandomString(3) + "$%"
287
}
288
289
func (suite *TestStorageContext) TestGetAccountURL(c *C) {
290
    account := makeNastyURLUnfriendlyString()
291
    context := StorageContext{Account: account}
292
    c.Check(
293
        context.getAccountURL(),
294
        Equals,
295
        "http://"+url.QueryEscape(account)+".blob.core.windows.net/")
296
}
297
298
func (suite *TestStorageContext) TestGetContainerURL(c *C) {
299
    account := makeNastyURLUnfriendlyString()
300
    container := makeNastyURLUnfriendlyString()
301
    context := StorageContext{Account: account}
302
    c.Check(
303
        context.getContainerURL(container),
304
        Equals,
305
        "http://"+url.QueryEscape(account)+".blob.core.windows.net/"+url.QueryEscape(container))
306
}
307
308
func (suite *TestStorageContext) TestGetFileURL(c *C) {
309
    account := makeNastyURLUnfriendlyString()
310
    container := makeNastyURLUnfriendlyString()
311
    file := makeNastyURLUnfriendlyString()
312
    context := StorageContext{Account: account}
313
    c.Check(
114.2.1 by Raphael Badin
Export getFileURL.
314
        context.GetFileURL(container, file),
98.3.8 by Jeroen Vermeulen
Whittle down tests for StorageContext.get*URL.
315
        Equals,
316
        "http://"+url.QueryEscape(account)+".blob.core.windows.net/"+url.QueryEscape(container)+"/"+url.QueryEscape(file))
98.3.1 by Jeroen Vermeulen
Test, and create, helpers to compute the basic storage URLs.
317
}
318
199.2.5 by Raphael Badin
Rename method.
319
func (suite *TestStorageContext) TestGetSignedFileURL(c *C) {
199.2.1 by Raphael Badin
Add support for Windows Azure Shared Access Signature.
320
    account := "account"
321
    container := "container"
322
    file := "/a/file"
199.2.7 by Raphael Badin
Review fixes.
323
    key := base64.StdEncoding.EncodeToString([]byte("dummykey"))
324
    context := StorageContext{Account: account, Key: key}
199.2.2 by Raphael Badin
Add parameter to composeReadBlobValues.
325
    expires := time.Now()
199.2.1 by Raphael Badin
Add support for Windows Azure Shared Access Signature.
326
199.2.7 by Raphael Badin
Review fixes.
327
    signedURL := context.GetAnonymousFileURL(container, file, expires)
199.2.6 by Raphael Badin
Reformat test.
328
    // The only difference with the non-anon URL is the query string.
329
    parsed, err := url.Parse(signedURL)
330
    c.Assert(err, IsNil)
331
    fileURL, err := url.Parse(context.GetFileURL(container, file))
332
    c.Assert(err, IsNil)
333
    c.Check(parsed.Scheme, Equals, fileURL.Scheme)
334
    c.Check(parsed.Host, Equals, fileURL.Host)
335
    c.Check(parsed.Path, Equals, fileURL.Path)
199.2.1 by Raphael Badin
Add support for Windows Azure Shared Access Signature.
336
337
    values, err := url.ParseQuery(parsed.RawQuery)
338
    c.Assert(err, IsNil)
339
    signature := values.Get("sig")
199.2.7 by Raphael Badin
Review fixes.
340
    expectedSignature := getReadBlobAccessValues(container, file, account, key, expires).Get("sig")
341
    c.Check(signature, Equals, expectedSignature)
199.2.1 by Raphael Badin
Add support for Windows Azure Shared Access Signature.
342
}
343
26.1.1 by Gavin Panella
Add StorageContext.
344
func (suite *TestStorageContext) TestGetClientReturnsDefaultClient(c *C) {
36.1.3 by Gavin Panella
Make StorageContext.Client private.
345
    context := &StorageContext{client: nil}
26.1.1 by Gavin Panella
Add StorageContext.
346
    c.Assert(context.getClient(), Equals, http.DefaultClient)
347
}
348
349
func (suite *TestStorageContext) TestGetClientReturnsSpecifiedClient(c *C) {
36.1.3 by Gavin Panella
Make StorageContext.Client private.
350
    context := &StorageContext{client: &http.Client{}}
26.1.1 by Gavin Panella
Add StorageContext.
351
    c.Assert(context.getClient(), Not(Equals), http.DefaultClient)
36.1.3 by Gavin Panella
Make StorageContext.Client private.
352
    c.Assert(context.getClient(), Equals, context.client)
26.1.1 by Gavin Panella
Add StorageContext.
353
}
26.1.3 by Gavin Panella
Create the test transport and use it for ListContainers.
354
33.1.7 by Gavin Panella
Shuffle things about a bit.
355
type TestListContainers struct{}
356
357
var _ = Suite(&TestListContainers{})
358
33.1.2 by Gavin Panella
Comment the tests.
359
// The ListContainers Storage API call returns a ContainerEnumerationResults
360
// struct on success.
26.1.3 by Gavin Panella
Create the test transport and use it for ListContainers.
361
func (suite *TestListContainers) Test(c *C) {
122.2.1 by Raphael Badin
Propagate Azure errors, add IsNotFoundError() method.
362
    responseBody := `
26.1.5 by Gavin Panella
Use example XML to test ListContainers.
363
        <?xml version="1.0" encoding="utf-8"?>
364
        <EnumerationResults AccountName="http://myaccount.blob.core.windows.net">
365
          <Prefix>prefix-value</Prefix>
366
          <Marker>marker-value</Marker>
367
          <MaxResults>max-results-value</MaxResults>
368
          <Containers>
369
            <Container>
370
              <Name>name-value</Name>
371
              <URL>url-value</URL>
372
              <Properties>
373
                <Last-Modified>date/time-value</Last-Modified>
374
                <Etag>etag-value</Etag>
375
                <LeaseStatus>lease-status-value</LeaseStatus>
376
                <LeaseState>lease-state-value</LeaseState>
377
                <LeaseDuration>lease-duration-value</LeaseDuration>
378
              </Properties>
379
              <Metadata>
380
                <metadata-name>metadata-value</metadata-name>
381
              </Metadata>
382
            </Container>
383
          </Containers>
93.3.6 by Jeroen Vermeulen
Fix up old test that incorrectly set a NextMarker on an unbatched result.
384
          <NextMarker/>
26.1.5 by Gavin Panella
Use example XML to test ListContainers.
385
        </EnumerationResults>`
122.2.1 by Raphael Badin
Propagate Azure errors, add IsNotFoundError() method.
386
    response := makeHttpResponse(http.StatusOK, responseBody)
26.1.5 by Gavin Panella
Use example XML to test ListContainers.
387
    transport := &TestTransport{Response: response}
114.1.2 by Julian Edwards
mechanical changes to use the new test helper
388
    context := makeStorageContext(transport)
106.1.3 by Gavin Panella
New struct ListContainersRequest, similar to ListBlobsRequest.
389
    request := &ListContainersRequest{Marker: ""}
390
    results, err := context.ListContainers(request)
26.1.5 by Gavin Panella
Use example XML to test ListContainers.
391
    c.Assert(err, IsNil)
33.1.4 by Gavin Panella
Extract function makeStorageContext().
392
    c.Check(transport.Request.URL.String(), Equals, fmt.Sprintf(
393
        "http://%s.blob.core.windows.net/?comp=list", context.Account))
26.1.6 by Gavin Panella
Sanity check the request.
394
    c.Check(transport.Request.Header.Get("Authorization"), Not(Equals), "")
50.2.3 by Gavin Panella
Use NotNil instead of Not(IsNil).
395
    c.Assert(results, NotNil)
26.1.5 by Gavin Panella
Use example XML to test ListContainers.
396
    c.Assert(results.Containers[0].Name, Equals, "name-value")
26.1.3 by Gavin Panella
Create the test transport and use it for ListContainers.
397
}
33.1.1 by Gavin Panella
Tests for error conditions in ListContainers.
398
33.1.2 by Gavin Panella
Comment the tests.
399
// Client-side errors from the HTTP client are propagated back to the caller.
33.1.1 by Gavin Panella
Tests for error conditions in ListContainers.
400
func (suite *TestListContainers) TestError(c *C) {
401
    error := fmt.Errorf("canned-error")
114.1.2 by Julian Edwards
mechanical changes to use the new test helper
402
    context := makeStorageContext(&TestTransport{Error: error})
106.1.3 by Gavin Panella
New struct ListContainersRequest, similar to ListBlobsRequest.
403
    request := &ListContainersRequest{Marker: ""}
404
    _, err := context.ListContainers(request)
50.2.3 by Gavin Panella
Use NotNil instead of Not(IsNil).
405
    c.Assert(err, NotNil)
33.1.1 by Gavin Panella
Tests for error conditions in ListContainers.
406
}
407
122.2.1 by Raphael Badin
Propagate Azure errors, add IsNotFoundError() method.
408
// Azure HTTP errors (for instance 404 responses) are propagated back to
409
// the caller as ServerError objects.
410
func (suite *TestListContainers) TestServerError(c *C) {
411
    response := makeHttpResponse(http.StatusNotFound, "not found")
114.1.2 by Julian Edwards
mechanical changes to use the new test helper
412
    context := makeStorageContext(&TestTransport{Response: response})
106.1.3 by Gavin Panella
New struct ListContainersRequest, similar to ListBlobsRequest.
413
    request := &ListContainersRequest{Marker: ""}
414
    _, err := context.ListContainers(request)
122.2.1 by Raphael Badin
Propagate Azure errors, add IsNotFoundError() method.
415
    serverError, ok := err.(*ServerError)
416
    c.Check(ok, Equals, true)
417
    c.Check(serverError.HTTPStatus.StatusCode(), Equals, http.StatusNotFound)
33.1.1 by Gavin Panella
Tests for error conditions in ListContainers.
418
}
33.1.6 by Gavin Panella
Tests for ListBlobs.
419
106.1.5 by Gavin Panella
Fix test suite.
420
func (suite *TestListContainers) TestListContainersBatchPassesMarker(c *C) {
150.1.1 by Julian Edwards
Rename TestTransport2 to MockingTransport
421
    transport := &MockingTransport{}
106.1.3 by Gavin Panella
New struct ListContainersRequest, similar to ListBlobsRequest.
422
    transport.AddExchange(&http.Response{StatusCode: http.StatusOK, Body: Empty}, nil)
114.1.2 by Julian Edwards
mechanical changes to use the new test helper
423
    context := makeStorageContext(transport)
106.1.3 by Gavin Panella
New struct ListContainersRequest, similar to ListBlobsRequest.
424
425
    // Call ListContainers.  This will fail because of the empty
426
    // response, but no matter.  We only care about the request.
427
    request := &ListContainersRequest{Marker: "thismarkerhere"}
428
    _, err := context.ListContainers(request)
429
    c.Assert(err, ErrorMatches, ".*Failed to deserialize data.*")
430
    c.Assert(transport.ExchangeCount, Equals, 1)
431
432
    query := transport.Exchanges[0].Request.URL.RawQuery
433
    values, err := url.ParseQuery(query)
434
    c.Assert(err, IsNil)
435
    c.Check(values["marker"], DeepEquals, []string{"thismarkerhere"})
436
}
437
106.1.5 by Gavin Panella
Fix test suite.
438
func (suite *TestListContainers) TestListContainersBatchDoesNotPassEmptyMarker(c *C) {
150.1.1 by Julian Edwards
Rename TestTransport2 to MockingTransport
439
    transport := &MockingTransport{}
106.1.3 by Gavin Panella
New struct ListContainersRequest, similar to ListBlobsRequest.
440
    transport.AddExchange(&http.Response{StatusCode: http.StatusOK, Body: Empty}, nil)
114.1.2 by Julian Edwards
mechanical changes to use the new test helper
441
    context := makeStorageContext(transport)
106.1.3 by Gavin Panella
New struct ListContainersRequest, similar to ListBlobsRequest.
442
443
    // The error is OK.  We only care about the request.
444
    request := &ListContainersRequest{Marker: ""}
445
    _, err := context.ListContainers(request)
446
    c.Assert(err, ErrorMatches, ".*Failed to deserialize data.*")
447
    c.Assert(transport.ExchangeCount, Equals, 1)
448
449
    query := transport.Exchanges[0].Request.URL.RawQuery
450
    values, err := url.ParseQuery(query)
451
    c.Assert(err, IsNil)
452
    marker, present := values["marker"]
453
    c.Check(present, Equals, false)
454
    c.Check(marker, DeepEquals, []string(nil))
455
}
456
106.1.5 by Gavin Panella
Fix test suite.
457
func (suite *TestListContainers) TestListContainersBatchEscapesMarker(c *C) {
150.1.1 by Julian Edwards
Rename TestTransport2 to MockingTransport
458
    transport := &MockingTransport{}
106.1.3 by Gavin Panella
New struct ListContainersRequest, similar to ListBlobsRequest.
459
    transport.AddExchange(&http.Response{StatusCode: http.StatusOK, Body: Empty}, nil)
114.1.2 by Julian Edwards
mechanical changes to use the new test helper
460
    context := makeStorageContext(transport)
106.1.3 by Gavin Panella
New struct ListContainersRequest, similar to ListBlobsRequest.
461
462
    // The error is OK.  We only care about the request.
463
    request := &ListContainersRequest{Marker: "x&y"}
464
    _, err := context.ListContainers(request)
465
    c.Assert(err, ErrorMatches, ".*Failed to deserialize data.*")
466
    c.Assert(transport.ExchangeCount, Equals, 1)
467
468
    query := transport.Exchanges[0].Request.URL.RawQuery
469
    values, err := url.ParseQuery(query)
470
    c.Assert(err, IsNil)
471
    c.Check(values["marker"], DeepEquals, []string{"x&y"})
472
}
473
33.1.6 by Gavin Panella
Tests for ListBlobs.
474
type TestListBlobs struct{}
475
476
var _ = Suite(&TestListBlobs{})
477
478
// The ListBlobs Storage API call returns a BlobEnumerationResults struct on
479
// success.
480
func (suite *TestListBlobs) Test(c *C) {
122.2.1 by Raphael Badin
Propagate Azure errors, add IsNotFoundError() method.
481
    responseBody := `
33.1.6 by Gavin Panella
Tests for ListBlobs.
482
        <?xml version="1.0" encoding="utf-8"?>
483
        <EnumerationResults ContainerName="http://myaccount.blob.core.windows.net/mycontainer">
484
          <Prefix>prefix</Prefix>
485
          <Marker>marker</Marker>
486
          <MaxResults>maxresults</MaxResults>
487
          <Delimiter>delimiter</Delimiter>
488
          <Blobs>
489
            <Blob>
490
              <Name>blob-name</Name>
491
              <Snapshot>snapshot-date-time</Snapshot>
492
              <Url>blob-address</Url>
493
              <Properties>
494
                <Last-Modified>last-modified</Last-Modified>
495
                <Etag>etag</Etag>
496
                <Content-Length>size-in-bytes</Content-Length>
497
                <Content-Type>blob-content-type</Content-Type>
498
                <Content-Encoding />
499
                <Content-Language />
500
                <Content-MD5 />
501
                <Cache-Control />
502
                <x-ms-blob-sequence-number>sequence-number</x-ms-blob-sequence-number>
503
                <BlobType>blobtype</BlobType>
504
                <LeaseStatus>leasestatus</LeaseStatus>
505
                <LeaseState>leasestate</LeaseState>
506
                <LeaseDuration>leasesduration</LeaseDuration>
507
                <CopyId>id</CopyId>
508
                <CopyStatus>copystatus</CopyStatus>
509
                <CopySource>copysource</CopySource>
510
                <CopyProgress>copyprogress</CopyProgress>
511
                <CopyCompletionTime>copycompletiontime</CopyCompletionTime>
512
                <CopyStatusDescription>copydesc</CopyStatusDescription>
513
              </Properties>
514
              <Metadata>
515
                <MetaName1>metadataname1</MetaName1>
516
                <MetaName2>metadataname2</MetaName2>
517
              </Metadata>
518
            </Blob>
519
            <BlobPrefix>
520
              <Name>blob-prefix</Name>
521
            </BlobPrefix>
522
          </Blobs>
523
          <NextMarker />
524
        </EnumerationResults>`
122.2.1 by Raphael Badin
Propagate Azure errors, add IsNotFoundError() method.
525
    response := makeHttpResponse(http.StatusOK, responseBody)
33.1.6 by Gavin Panella
Tests for ListBlobs.
526
    transport := &TestTransport{Response: response}
114.1.2 by Julian Edwards
mechanical changes to use the new test helper
527
    context := makeStorageContext(transport)
528
105.1.3 by Gavin Panella
Move tests for ListAllBlobs to the right place.
529
    request := &ListBlobsRequest{Container: "container"}
530
    results, err := context.ListBlobs(request)
33.1.6 by Gavin Panella
Tests for ListBlobs.
531
    c.Assert(err, IsNil)
98.3.3 by Jeroen Vermeulen
Now fix the tests that assumed a fixed ordering or URL queries. The url package doesn't maintain any consistent order, so these started failing randomly. Bastards.
532
    c.Check(transport.Request.URL.String(), Matches, context.getContainerURL("container")+"?.*")
533
    c.Check(transport.Request.URL.Query(), DeepEquals, url.Values{
534
        "restype": {"container"},
535
        "comp":    {"list"},
536
    })
33.1.6 by Gavin Panella
Tests for ListBlobs.
537
    c.Check(transport.Request.Header.Get("Authorization"), Not(Equals), "")
50.2.3 by Gavin Panella
Use NotNil instead of Not(IsNil).
538
    c.Assert(results, NotNil)
33.1.6 by Gavin Panella
Tests for ListBlobs.
539
}
540
541
// Client-side errors from the HTTP client are propagated back to the caller.
542
func (suite *TestListBlobs) TestError(c *C) {
543
    error := fmt.Errorf("canned-error")
114.1.2 by Julian Edwards
mechanical changes to use the new test helper
544
    context := makeStorageContext(&TestTransport{Error: error})
545
105.1.3 by Gavin Panella
Move tests for ListAllBlobs to the right place.
546
    request := &ListBlobsRequest{Container: "container"}
547
    _, err := context.ListBlobs(request)
50.2.3 by Gavin Panella
Use NotNil instead of Not(IsNil).
548
    c.Assert(err, NotNil)
33.1.6 by Gavin Panella
Tests for ListBlobs.
549
}
550
122.2.1 by Raphael Badin
Propagate Azure errors, add IsNotFoundError() method.
551
// Azure HTTP errors (for instance 404 responses) are propagated back to
552
// the caller as ServerError objects.
553
func (suite *TestListBlobs) TestServerError(c *C) {
554
    response := makeHttpResponse(http.StatusNotFound, "not found")
114.1.2 by Julian Edwards
mechanical changes to use the new test helper
555
    context := makeStorageContext(&TestTransport{Response: response})
105.1.3 by Gavin Panella
Move tests for ListAllBlobs to the right place.
556
    request := &ListBlobsRequest{Container: "container"}
557
    _, err := context.ListBlobs(request)
122.2.1 by Raphael Badin
Propagate Azure errors, add IsNotFoundError() method.
558
    serverError, ok := err.(*ServerError)
559
    c.Check(ok, Equals, true)
560
    c.Check(serverError.HTTPStatus.StatusCode(), Equals, http.StatusNotFound)
33.1.6 by Gavin Panella
Tests for ListBlobs.
561
}
37.2.1 by Gavin Panella
New CreateContainer Storage API method.
562
105.1.4 by Gavin Panella
Rename a couple of tests.
563
func (suite *TestListBlobs) TestListBlobsPassesMarker(c *C) {
150.1.1 by Julian Edwards
Rename TestTransport2 to MockingTransport
564
    transport := &MockingTransport{}
98.1.1 by Jeroen Vermeulen
Copy tests from earlier, oversized branch. Split out getListBlobsBatch(), but don't add batching yet. Tests: 1 panic, 1 failure.
565
    transport.AddExchange(&http.Response{StatusCode: http.StatusOK, Body: Empty}, nil)
114.1.2 by Julian Edwards
mechanical changes to use the new test helper
566
    context := makeStorageContext(transport)
98.1.1 by Jeroen Vermeulen
Copy tests from earlier, oversized branch. Split out getListBlobsBatch(), but don't add batching yet. Tests: 1 panic, 1 failure.
567
105.1.2 by Gavin Panella
Move ListBlobs to storage.go, rename it to ListAllBlobs, and rename getListBlobBatch to ListBlobs.
568
    // Call ListBlobs.  This will fail because of the empty
98.1.1 by Jeroen Vermeulen
Copy tests from earlier, oversized branch. Split out getListBlobsBatch(), but don't add batching yet. Tests: 1 panic, 1 failure.
569
    // response, but no matter.  We only care about the request.
105.1.1 by Gavin Panella
New struct, ListBlobsRequest, for passing complex arguments.
570
    request := &ListBlobsRequest{Container: "mycontainer", Marker: "thismarkerhere"}
105.1.2 by Gavin Panella
Move ListBlobs to storage.go, rename it to ListAllBlobs, and rename getListBlobBatch to ListBlobs.
571
    _, err := context.ListBlobs(request)
98.1.1 by Jeroen Vermeulen
Copy tests from earlier, oversized branch. Split out getListBlobsBatch(), but don't add batching yet. Tests: 1 panic, 1 failure.
572
    c.Assert(err, ErrorMatches, ".*Failed to deserialize data.*")
573
    c.Assert(transport.ExchangeCount, Equals, 1)
574
575
    query := transport.Exchanges[0].Request.URL.RawQuery
576
    values, err := url.ParseQuery(query)
577
    c.Assert(err, IsNil)
578
    c.Check(values["marker"], DeepEquals, []string{"thismarkerhere"})
579
}
580
105.1.4 by Gavin Panella
Rename a couple of tests.
581
func (suite *TestListBlobs) TestListBlobsDoesNotPassEmptyMarker(c *C) {
150.1.1 by Julian Edwards
Rename TestTransport2 to MockingTransport
582
    transport := &MockingTransport{}
98.1.1 by Jeroen Vermeulen
Copy tests from earlier, oversized branch. Split out getListBlobsBatch(), but don't add batching yet. Tests: 1 panic, 1 failure.
583
    transport.AddExchange(&http.Response{StatusCode: http.StatusOK, Body: Empty}, nil)
114.1.2 by Julian Edwards
mechanical changes to use the new test helper
584
    context := makeStorageContext(transport)
98.1.1 by Jeroen Vermeulen
Copy tests from earlier, oversized branch. Split out getListBlobsBatch(), but don't add batching yet. Tests: 1 panic, 1 failure.
585
586
    // The error is OK.  We only care about the request.
105.1.1 by Gavin Panella
New struct, ListBlobsRequest, for passing complex arguments.
587
    request := &ListBlobsRequest{Container: "mycontainer"}
105.1.2 by Gavin Panella
Move ListBlobs to storage.go, rename it to ListAllBlobs, and rename getListBlobBatch to ListBlobs.
588
    _, err := context.ListBlobs(request)
98.1.1 by Jeroen Vermeulen
Copy tests from earlier, oversized branch. Split out getListBlobsBatch(), but don't add batching yet. Tests: 1 panic, 1 failure.
589
    c.Assert(err, ErrorMatches, ".*Failed to deserialize data.*")
590
    c.Assert(transport.ExchangeCount, Equals, 1)
591
592
    query := transport.Exchanges[0].Request.URL.RawQuery
593
    values, err := url.ParseQuery(query)
594
    c.Assert(err, IsNil)
595
    marker, present := values["marker"]
596
    c.Check(present, Equals, false)
597
    c.Check(marker, DeepEquals, []string(nil))
598
}
599
105.1.5 by Gavin Panella
Pass a prefix in the call if one is specified.
600
func (suite *TestListBlobs) TestListBlobsPassesPrefix(c *C) {
150.1.1 by Julian Edwards
Rename TestTransport2 to MockingTransport
601
    transport := &MockingTransport{}
105.1.5 by Gavin Panella
Pass a prefix in the call if one is specified.
602
    transport.AddExchange(&http.Response{StatusCode: http.StatusOK, Body: Empty}, nil)
114.1.2 by Julian Edwards
mechanical changes to use the new test helper
603
    context := makeStorageContext(transport)
105.1.5 by Gavin Panella
Pass a prefix in the call if one is specified.
604
605
    // Call ListBlobs.  This will fail because of the empty
606
    // response, but no matter.  We only care about the request.
607
    request := &ListBlobsRequest{Container: "mycontainer", Prefix: "thisprefixhere"}
608
    _, err := context.ListBlobs(request)
609
    c.Assert(err, ErrorMatches, ".*Failed to deserialize data.*")
610
    c.Assert(transport.ExchangeCount, Equals, 1)
611
612
    query := transport.Exchanges[0].Request.URL.RawQuery
613
    values, err := url.ParseQuery(query)
614
    c.Assert(err, IsNil)
615
    c.Check(values["prefix"], DeepEquals, []string{"thisprefixhere"})
616
}
617
618
func (suite *TestListBlobs) TestListBlobsDoesNotPassEmptyPrefix(c *C) {
150.1.1 by Julian Edwards
Rename TestTransport2 to MockingTransport
619
    transport := &MockingTransport{}
105.1.5 by Gavin Panella
Pass a prefix in the call if one is specified.
620
    transport.AddExchange(&http.Response{StatusCode: http.StatusOK, Body: Empty}, nil)
114.1.2 by Julian Edwards
mechanical changes to use the new test helper
621
    context := makeStorageContext(transport)
105.1.5 by Gavin Panella
Pass a prefix in the call if one is specified.
622
623
    // The error is OK.  We only care about the request.
624
    request := &ListBlobsRequest{Container: "mycontainer"}
625
    _, err := context.ListBlobs(request)
626
    c.Assert(err, ErrorMatches, ".*Failed to deserialize data.*")
627
    c.Assert(transport.ExchangeCount, Equals, 1)
628
629
    query := transport.Exchanges[0].Request.URL.RawQuery
630
    values, err := url.ParseQuery(query)
631
    c.Assert(err, IsNil)
632
    prefix, present := values["prefix"]
633
    c.Check(present, Equals, false)
634
    c.Check(prefix, DeepEquals, []string(nil))
635
}
636
37.2.1 by Gavin Panella
New CreateContainer Storage API method.
637
type TestCreateContainer struct{}
638
639
var _ = Suite(&TestCreateContainer{})
640
641
// The CreateContainer Storage API call returns without error when the
642
// container has been created successfully.
643
func (suite *TestCreateContainer) Test(c *C) {
122.2.1 by Raphael Badin
Propagate Azure errors, add IsNotFoundError() method.
644
    response := makeHttpResponse(http.StatusCreated, "")
37.2.1 by Gavin Panella
New CreateContainer Storage API method.
645
    transport := &TestTransport{Response: response}
114.1.2 by Julian Edwards
mechanical changes to use the new test helper
646
    context := makeStorageContext(transport)
139.1.1 by Gavin Panella
Camel-case everything, clean-up some comments.
647
    containerName := MakeRandomString(10)
648
    err := context.CreateContainer(containerName)
37.2.1 by Gavin Panella
New CreateContainer Storage API method.
649
    c.Assert(err, IsNil)
650
    c.Check(transport.Request.URL.String(), Equals, fmt.Sprintf(
651
        "http://%s.blob.core.windows.net/%s?restype=container",
139.1.1 by Gavin Panella
Camel-case everything, clean-up some comments.
652
        context.Account, containerName))
37.2.1 by Gavin Panella
New CreateContainer Storage API method.
653
    c.Check(transport.Request.Header.Get("Authorization"), Not(Equals), "")
654
}
655
656
// Client-side errors from the HTTP client are propagated back to the caller.
657
func (suite *TestCreateContainer) TestError(c *C) {
658
    error := fmt.Errorf("canned-error")
114.1.2 by Julian Edwards
mechanical changes to use the new test helper
659
    context := makeStorageContext(&TestTransport{Error: error})
37.2.1 by Gavin Panella
New CreateContainer Storage API method.
660
    err := context.CreateContainer("container")
50.2.3 by Gavin Panella
Use NotNil instead of Not(IsNil).
661
    c.Assert(err, NotNil)
37.2.1 by Gavin Panella
New CreateContainer Storage API method.
662
}
663
664
// Server-side errors are propagated back to the caller.
665
func (suite *TestCreateContainer) TestErrorResponse(c *C) {
122.2.1 by Raphael Badin
Propagate Azure errors, add IsNotFoundError() method.
666
    response := makeHttpResponse(http.StatusNotFound, "not found")
114.1.2 by Julian Edwards
mechanical changes to use the new test helper
667
    context := makeStorageContext(&TestTransport{Response: response})
37.2.1 by Gavin Panella
New CreateContainer Storage API method.
668
    err := context.CreateContainer("container")
50.2.3 by Gavin Panella
Use NotNil instead of Not(IsNil).
669
    c.Assert(err, NotNil)
37.2.1 by Gavin Panella
New CreateContainer Storage API method.
670
}
671
672
// Server-side errors are propagated back to the caller.
673
func (suite *TestCreateContainer) TestNotCreatedResponse(c *C) {
122.2.1 by Raphael Badin
Propagate Azure errors, add IsNotFoundError() method.
674
    response := makeHttpResponse(http.StatusOK, "")
114.1.2 by Julian Edwards
mechanical changes to use the new test helper
675
    context := makeStorageContext(&TestTransport{Response: response})
37.2.1 by Gavin Panella
New CreateContainer Storage API method.
676
    err := context.CreateContainer("container")
50.2.3 by Gavin Panella
Use NotNil instead of Not(IsNil).
677
    c.Assert(err, NotNil)
37.2.1 by Gavin Panella
New CreateContainer Storage API method.
678
}
37.2.4 by Gavin Panella
Merge trunk.
679
122.2.1 by Raphael Badin
Propagate Azure errors, add IsNotFoundError() method.
680
// Azure HTTP errors (for instance 404 responses) are propagated back to
681
// the caller as ServerError objects.
682
func (suite *TestCreateContainer) TestServerError(c *C) {
683
    response := makeHttpResponse(http.StatusNotFound, "not found")
684
    context := makeStorageContext(&TestTransport{Response: response})
685
    err := context.CreateContainer("container")
686
    serverError, ok := err.(*ServerError)
687
    c.Check(ok, Equals, true)
688
    c.Check(serverError.HTTPStatus.StatusCode(), Equals, http.StatusNotFound)
689
}
690
201.2.2 by Julian Edwards
Add tests
691
type TestDeleteContainer struct{}
692
693
var _ = Suite(&TestDeleteContainer{})
694
695
// The DeleteContainer Storage API call returns without error when the
696
// container has been created successfully.
697
func (suite *TestDeleteContainer) Test(c *C) {
698
    response := makeHttpResponse(http.StatusAccepted, "")
699
    transport := &TestTransport{Response: response}
700
    context := makeStorageContext(transport)
701
    containerName := MakeRandomString(10)
702
    err := context.DeleteContainer(containerName)
703
    c.Assert(err, IsNil)
704
    c.Check(transport.Request.URL.String(), Equals, fmt.Sprintf(
705
        "http://%s.blob.core.windows.net/%s?restype=container",
706
        context.Account, containerName))
707
    c.Check(transport.Request.Method, Equals, "DELETE")
708
    c.Check(transport.Request.Header.Get("Authorization"), Not(Equals), "")
709
}
710
711
// Client-side errors from the HTTP client are propagated back to the caller.
712
func (suite *TestDeleteContainer) TestError(c *C) {
713
    error := fmt.Errorf("canned-error")
714
    context := makeStorageContext(&TestTransport{Error: error})
715
    err := context.DeleteContainer("container")
201.2.3 by Julian Edwards
improve test
716
    c.Assert(err, ErrorMatches, ".*canned-error.*")
201.2.2 by Julian Edwards
Add tests
717
}
718
719
// Server-side errors are propagated back to the caller.
720
func (suite *TestDeleteContainer) TestNotCreatedResponse(c *C) {
721
    response := makeHttpResponse(http.StatusOK, "")
722
    context := makeStorageContext(&TestTransport{Response: response})
723
    err := context.DeleteContainer("container")
201.2.3 by Julian Edwards
improve test
724
    c.Assert(err, ErrorMatches, ".*Azure request failed.*")
201.2.2 by Julian Edwards
Add tests
725
}
726
727
// Azure HTTP errors (for instance 404 responses) are propagated back to
728
// the caller as ServerError objects.
729
func (suite *TestDeleteContainer) TestServerError(c *C) {
207.1.1 by Raphael Badin
Ignore 404 errors in DeleteContainer().
730
    response := makeHttpResponse(http.StatusMethodNotAllowed, "not allowed")
201.2.2 by Julian Edwards
Add tests
731
    context := makeStorageContext(&TestTransport{Response: response})
732
    err := context.DeleteContainer("container")
733
    serverError, ok := err.(*ServerError)
734
    c.Check(ok, Equals, true)
207.1.1 by Raphael Badin
Ignore 404 errors in DeleteContainer().
735
    c.Check(serverError.HTTPStatus.StatusCode(), Equals, http.StatusMethodNotAllowed)
736
}
737
207.1.2 by Raphael Badin
Fix typo.
738
func (suite *TestDeleteContainer) TestDeleteNotExistentContainerDoesNotFail(c *C) {
207.1.1 by Raphael Badin
Ignore 404 errors in DeleteContainer().
739
    response := makeHttpResponse(http.StatusNotFound, "not found")
740
    context := makeStorageContext(&TestTransport{Response: response})
741
    err := context.DeleteContainer("container")
742
    c.Assert(err, IsNil)
201.2.2 by Julian Edwards
Add tests
743
}
744
205.1.2 by Julian Edwards
implement GetContainerProperties
745
type TestGetContainerProperties struct{}
746
747
var _ = Suite(&TestGetContainerProperties{})
748
749
// The GetContainerProperties Storage API call returns without error when the
750
// container has been created successfully.
751
func (suite *TestGetContainerProperties) Test(c *C) {
752
    header := make(http.Header)
753
    header.Add("Last-Modified", "last-modified")
754
    header.Add("ETag", "etag")
755
    header.Add("X-Ms-Lease-Status", "status")
756
    header.Add("X-Ms-Lease-State", "state")
757
    header.Add("X-Ms-Lease-Duration", "duration")
758
    response := &http.Response{
759
        Status:     fmt.Sprintf("%d", http.StatusOK),
760
        StatusCode: http.StatusOK,
761
        Body:       makeResponseBody(""),
762
        Header:     header,
763
    }
764
765
    transport := &TestTransport{Response: response}
766
    context := makeStorageContext(transport)
767
    containerName := MakeRandomString(10)
768
    props, err := context.GetContainerProperties(containerName)
769
    c.Assert(err, IsNil)
770
    c.Check(transport.Request.URL.String(), Equals, fmt.Sprintf(
771
        "http://%s.blob.core.windows.net/%s?restype=container",
772
        context.Account, containerName))
773
    c.Check(transport.Request.Method, Equals, "GET")
774
    c.Check(transport.Request.Header.Get("Authorization"), Not(Equals), "")
775
776
    c.Check(props.LastModified, Equals, "last-modified")
777
    c.Check(props.ETag, Equals, "etag")
778
    c.Check(props.LeaseStatus, Equals, "status")
779
    c.Check(props.LeaseState, Equals, "state")
780
    c.Check(props.LeaseDuration, Equals, "duration")
781
}
782
206.1.1 by Julian Edwards
Allow for optional headers when parsing result of GetContainerProperties!
783
func (suite *TestGetContainerProperties) TestWithoutAllHeaders(c *C) {
784
    response := &http.Response{
785
        Status:     fmt.Sprintf("%d", http.StatusOK),
786
        StatusCode: http.StatusOK,
787
        Body:       makeResponseBody(""),
788
    }
789
790
    transport := &TestTransport{Response: response}
791
    context := makeStorageContext(transport)
792
    containerName := MakeRandomString(10)
793
    props, err := context.GetContainerProperties(containerName)
794
    c.Assert(err, IsNil)
795
796
    c.Check(props.LastModified, Equals, "")
797
    c.Check(props.ETag, Equals, "")
798
    c.Check(props.LeaseStatus, Equals, "")
799
    c.Check(props.LeaseState, Equals, "")
800
    c.Check(props.LeaseDuration, Equals, "")
801
}
802
205.1.2 by Julian Edwards
implement GetContainerProperties
803
// Client-side errors from the HTTP client are propagated back to the caller.
804
func (suite *TestGetContainerProperties) TestError(c *C) {
805
    error := fmt.Errorf("canned-error")
806
    context := makeStorageContext(&TestTransport{Error: error})
807
    _, err := context.GetContainerProperties("container")
808
    c.Assert(err, ErrorMatches, ".*canned-error.*")
809
}
810
811
// Server-side errors are propagated back to the caller.
812
func (suite *TestGetContainerProperties) TestErrorResponse(c *C) {
813
    response := makeHttpResponse(http.StatusNotFound, "not found")
814
    context := makeStorageContext(&TestTransport{Response: response})
815
    _, err := context.GetContainerProperties("container")
816
    c.Assert(err, ErrorMatches, ".*Not Found.*")
817
}
818
819
// Azure HTTP errors (for instance 404 responses) are propagated back to
820
// the caller as ServerError objects.
821
func (suite *TestGetContainerProperties) TestServerError(c *C) {
822
    response := makeHttpResponse(http.StatusNotFound, "not found")
823
    context := makeStorageContext(&TestTransport{Response: response})
824
    _, err := context.GetContainerProperties("container")
825
    serverError, ok := err.(*ServerError)
205.1.4 by Julian Edwards
review changes
826
    c.Assert(ok, Equals, true)
205.1.2 by Julian Edwards
implement GetContainerProperties
827
    c.Check(serverError.HTTPStatus.StatusCode(), Equals, http.StatusNotFound)
828
}
829
113.1.1 by Julian Edwards
Add happy path test for putpage
830
type TestPutPage struct{}
831
832
var _ = Suite(&TestPutPage{})
833
834
// Basic happy path testing.
835
func (suite *TestPutPage) TestHappyPath(c *C) {
122.2.1 by Raphael Badin
Propagate Azure errors, add IsNotFoundError() method.
836
    response := makeHttpResponse(http.StatusCreated, "")
113.1.1 by Julian Edwards
Add happy path test for putpage
837
    transport := &TestTransport{Response: response}
114.1.2 by Julian Edwards
mechanical changes to use the new test helper
838
    context := makeStorageContext(transport)
113.1.1 by Julian Edwards
Add happy path test for putpage
839
    randomData := MakeRandomByteSlice(10)
840
    dataReader := bytes.NewReader(randomData)
841
842
    err := context.PutPage(&PutPageRequest{
843
        Container: "container", Filename: "filename", StartRange: 0,
127.3.1 by Julian Edwards
Add failing test
844
        EndRange: 511, Data: dataReader})
113.1.1 by Julian Edwards
Add happy path test for putpage
845
    c.Assert(err, IsNil)
846
847
    // Ensure that container was set right.
114.2.1 by Raphael Badin
Export getFileURL.
848
    c.Check(transport.Request.URL.String(), Matches, context.GetFileURL("container", "filename")+"?.*")
113.1.1 by Julian Edwards
Add happy path test for putpage
849
    // Ensure that the Authorization header is set.
850
    c.Check(transport.Request.Header.Get("Authorization"), Not(Equals), "")
851
    // Check the range is set.
127.3.1 by Julian Edwards
Add failing test
852
    c.Check(transport.Request.Header.Get("x-ms-range"), Equals, "bytes=0-511")
113.1.1 by Julian Edwards
Add happy path test for putpage
853
    // Check special page write header.
854
    c.Check(transport.Request.Header.Get("x-ms-page-write"), Equals, "update")
855
    // "?comp=page" should be part of the URL.
856
    c.Check(transport.Request.URL.Query(), DeepEquals, url.Values{
857
        "comp": {"page"},
858
    })
859
    // Check the data payload.
860
    data, err := ioutil.ReadAll(transport.Request.Body)
861
    c.Assert(err, IsNil)
862
    c.Check(data, DeepEquals, randomData)
863
}
864
113.1.3 by Julian Edwards
Add generic error testing
865
// Client-side errors from the HTTP client are propagated back to the caller.
866
func (suite *TestPutPage) TestError(c *C) {
867
    cannedError := fmt.Errorf("canned-error")
114.1.2 by Julian Edwards
mechanical changes to use the new test helper
868
    context := makeStorageContext(&TestTransport{Error: cannedError})
113.1.3 by Julian Edwards
Add generic error testing
869
    err := context.PutPage(&PutPageRequest{
870
        Container: "container", Filename: "filename", StartRange: 0,
127.3.1 by Julian Edwards
Add failing test
871
        EndRange: 511, Data: nil})
113.1.3 by Julian Edwards
Add generic error testing
872
    c.Assert(err, NotNil)
873
}
874
875
// Server-side errors are propagated back to the caller.
876
func (suite *TestPutPage) TestErrorResponse(c *C) {
122.2.1 by Raphael Badin
Propagate Azure errors, add IsNotFoundError() method.
877
    responseBody := "<Error><Code>Frotzed</Code><Message>failed to put blob</Message></Error>"
878
    response := makeHttpResponse(102, responseBody)
114.1.2 by Julian Edwards
mechanical changes to use the new test helper
879
    context := makeStorageContext(&TestTransport{Response: response})
113.1.3 by Julian Edwards
Add generic error testing
880
    err := context.PutPage(&PutPageRequest{
881
        Container: "container", Filename: "filename", StartRange: 0,
127.3.1 by Julian Edwards
Add failing test
882
        EndRange: 511, Data: nil})
113.1.3 by Julian Edwards
Add generic error testing
883
    c.Assert(err, NotNil)
884
    c.Check(err, ErrorMatches, ".*102.*")
885
    c.Check(err, ErrorMatches, ".*Frotzed.*")
886
    c.Check(err, ErrorMatches, ".*failed to put blob.*")
887
}
888
122.2.1 by Raphael Badin
Propagate Azure errors, add IsNotFoundError() method.
889
// Azure HTTP errors (for instance 404 responses) are propagated back to
890
// the caller as ServerError objects.
891
func (suite *TestPutPage) TestServerError(c *C) {
892
    response := makeHttpResponse(http.StatusNotFound, "not found")
893
    context := makeStorageContext(&TestTransport{Response: response})
894
    err := context.PutPage(&PutPageRequest{
895
        Container: "container", Filename: "filename", StartRange: 0,
127.3.1 by Julian Edwards
Add failing test
896
        EndRange: 511, Data: nil})
122.2.1 by Raphael Badin
Propagate Azure errors, add IsNotFoundError() method.
897
    serverError, ok := err.(*ServerError)
898
    c.Check(ok, Equals, true)
899
    c.Check(serverError.HTTPStatus.StatusCode(), Equals, http.StatusNotFound)
900
}
901
127.3.1 by Julian Edwards
Add failing test
902
// Range values outside the limits should get rejected.
903
func (suite *TestPutPage) TestRangeLimits(c *C) {
904
    context := makeStorageContext(&TestTransport{})
905
    err := context.PutPage(&PutPageRequest{
906
        StartRange: 513, EndRange: 555})
907
    c.Assert(err, NotNil)
127.3.5 by Julian Edwards
gavin's review comments
908
    c.Check(err, ErrorMatches, ".*StartRange must be a multiple of 512, EndRange must be one less than a multiple of 512.*")
127.3.1 by Julian Edwards
Add failing test
909
}
910
37.1.1 by Julian Edwards
make PutBlobk
911
type TestPutBlob struct{}
912
913
var _ = Suite(&TestPutBlob{})
914
108.3.4 by Julian Edwards
jtv's review comments addressed
915
// Test basic PutBlob happy path functionality.
108.3.3 by Julian Edwards
add a test for page block requests
916
func (suite *TestPutBlob) TestPutBlockBlob(c *C) {
122.2.1 by Raphael Badin
Propagate Azure errors, add IsNotFoundError() method.
917
    response := makeHttpResponse(http.StatusCreated, "")
37.1.1 by Julian Edwards
make PutBlobk
918
    transport := &TestTransport{Response: response}
114.1.2 by Julian Edwards
mechanical changes to use the new test helper
919
    context := makeStorageContext(transport)
920
108.3.1 by Julian Edwards
Use struct as request params for PutBlob and add a blob_type
921
    err := context.PutBlob(&PutBlobRequest{
108.3.4 by Julian Edwards
jtv's review comments addressed
922
        Container: "container", BlobType: "block", Filename: "blobname"})
37.1.1 by Julian Edwards
make PutBlobk
923
    c.Assert(err, IsNil)
108.3.4 by Julian Edwards
jtv's review comments addressed
924
    // Ensure that container was set right.
114.2.1 by Raphael Badin
Export getFileURL.
925
    c.Check(transport.Request.URL.String(), Equals, context.GetFileURL("container", "blobname"))
108.3.4 by Julian Edwards
jtv's review comments addressed
926
    // Ensure that the Authorization header is set.
37.1.1 by Julian Edwards
make PutBlobk
927
    c.Check(transport.Request.Header.Get("Authorization"), Not(Equals), "")
108.3.4 by Julian Edwards
jtv's review comments addressed
928
    // The blob type should be a block.
108.3.2 by Julian Edwards
Add x-ms-blob-type header to PutBlob request
929
    c.Check(transport.Request.Header.Get("x-ms-blob-type"), Equals, "BlockBlob")
37.1.1 by Julian Edwards
make PutBlobk
930
}
931
108.3.4 by Julian Edwards
jtv's review comments addressed
932
// PutBlob should set x-ms-blob-type to PageBlob for Page Blobs.
108.3.3 by Julian Edwards
add a test for page block requests
933
func (suite *TestPutBlob) TestPutPageBlob(c *C) {
122.2.1 by Raphael Badin
Propagate Azure errors, add IsNotFoundError() method.
934
    response := makeHttpResponse(http.StatusCreated, "")
108.3.3 by Julian Edwards
add a test for page block requests
935
    transport := &TestTransport{Response: response}
114.1.2 by Julian Edwards
mechanical changes to use the new test helper
936
    context := makeStorageContext(transport)
108.3.3 by Julian Edwards
add a test for page block requests
937
    err := context.PutBlob(&PutBlobRequest{
115.1.1 by Julian Edwards
Add tests for size parameter on pubblob for a page
938
        Container: "container", BlobType: "page", Filename: "blobname",
939
        Size: 512})
108.3.3 by Julian Edwards
add a test for page block requests
940
    c.Assert(err, IsNil)
941
    c.Check(transport.Request.Header.Get("x-ms-blob-type"), Equals, "PageBlob")
115.1.1 by Julian Edwards
Add tests for size parameter on pubblob for a page
942
    c.Check(transport.Request.Header.Get("x-ms-blob-content-length"), Equals, "512")
943
}
944
945
// PutBlob for a page should return an error when Size is not specified.
946
func (suite *TestPutBlob) TestPutPageBlobWithSizeOmitted(c *C) {
947
    context := makeStorageContext(&TestTransport{})
948
    err := context.PutBlob(&PutBlobRequest{
949
        Container: "container", BlobType: "page", Filename: "blob"})
950
    c.Assert(err, ErrorMatches, "Must supply a size for a page blob")
108.3.3 by Julian Edwards
add a test for page block requests
951
}
952
127.3.3 by Julian Edwards
failing test
953
// PutBlob for a page should return an error when Size is not a multiple
954
// of 512 bytes.
955
func (suite *TestPutBlob) TestPutPageBlobWithInvalidSiuze(c *C) {
956
    context := makeStorageContext(&TestTransport{})
957
    err := context.PutBlob(&PutBlobRequest{
958
        Container: "container", BlobType: "page", Filename: "blob",
959
        Size: 1015})
127.3.5 by Julian Edwards
gavin's review comments
960
    c.Assert(err, ErrorMatches, "Size must be a multiple of 512 bytes")
127.3.3 by Julian Edwards
failing test
961
}
962
108.3.4 by Julian Edwards
jtv's review comments addressed
963
// Passing a BlobType other than page or block results in a panic.
108.3.1 by Julian Edwards
Use struct as request params for PutBlob and add a blob_type
964
func (suite *TestPutBlob) TestBlobType(c *C) {
108.3.4 by Julian Edwards
jtv's review comments addressed
965
    defer func() {
966
        err := recover()
139.1.1 by Gavin Panella
Camel-case everything, clean-up some comments.
967
        c.Assert(err, Equals, "blockType must be 'page' or 'block'")
108.3.4 by Julian Edwards
jtv's review comments addressed
968
    }()
114.1.2 by Julian Edwards
mechanical changes to use the new test helper
969
    context := makeStorageContext(&TestTransport{})
108.3.4 by Julian Edwards
jtv's review comments addressed
970
    context.PutBlob(&PutBlobRequest{
971
        Container: "container", BlobType: "invalid-blob-type",
972
        Filename: "blobname"})
973
    c.Assert("This should have panicked", Equals, "But it didn't.")
108.3.1 by Julian Edwards
Use struct as request params for PutBlob and add a blob_type
974
}
975
37.1.1 by Julian Edwards
make PutBlobk
976
// Client-side errors from the HTTP client are propagated back to the caller.
977
func (suite *TestPutBlob) TestError(c *C) {
978
    error := fmt.Errorf("canned-error")
114.1.2 by Julian Edwards
mechanical changes to use the new test helper
979
    context := makeStorageContext(&TestTransport{Error: error})
108.3.1 by Julian Edwards
Use struct as request params for PutBlob and add a blob_type
980
    err := context.PutBlob(&PutBlobRequest{
108.3.4 by Julian Edwards
jtv's review comments addressed
981
        Container: "container", BlobType: "block", Filename: "blobname"})
50.2.3 by Gavin Panella
Use NotNil instead of Not(IsNil).
982
    c.Assert(err, NotNil)
37.1.1 by Julian Edwards
make PutBlobk
983
}
984
985
// Server-side errors are propagated back to the caller.
986
func (suite *TestPutBlob) TestErrorResponse(c *C) {
122.2.1 by Raphael Badin
Propagate Azure errors, add IsNotFoundError() method.
987
    responseBody := "<Error><Code>Frotzed</Code><Message>failed to put blob</Message></Error>"
988
    response := makeHttpResponse(102, responseBody)
114.1.2 by Julian Edwards
mechanical changes to use the new test helper
989
    context := makeStorageContext(&TestTransport{Response: response})
108.3.1 by Julian Edwards
Use struct as request params for PutBlob and add a blob_type
990
    err := context.PutBlob(&PutBlobRequest{
108.3.4 by Julian Edwards
jtv's review comments addressed
991
        Container: "container", BlobType: "block", Filename: "blobname"})
65.1.1 by Jeroen Vermeulen
Unify Error and ServerError as HTTPError, with different implementations based on what information is available at runtime.
992
    c.Assert(err, NotNil)
993
    c.Check(err, ErrorMatches, ".*102.*")
994
    c.Check(err, ErrorMatches, ".*Frotzed.*")
995
    c.Check(err, ErrorMatches, ".*failed to put blob.*")
37.1.1 by Julian Edwards
make PutBlobk
996
}
42.1.1 by Julian Edwards
Add PutBlock
997
122.2.1 by Raphael Badin
Propagate Azure errors, add IsNotFoundError() method.
998
// Azure HTTP errors (for instance 404 responses) are propagated back to
999
// the caller as ServerError objects.
1000
func (suite *TestPutBlob) TestServerError(c *C) {
1001
    response := makeHttpResponse(http.StatusNotFound, "not found")
1002
    context := makeStorageContext(&TestTransport{Response: response})
1003
    err := context.PutBlob(&PutBlobRequest{
1004
        Container: "container", BlobType: "block", Filename: "blobname"})
1005
    serverError, ok := err.(*ServerError)
1006
    c.Check(ok, Equals, true)
1007
    c.Check(serverError.HTTPStatus.StatusCode(), Equals, http.StatusNotFound)
1008
}
1009
42.1.1 by Julian Edwards
Add PutBlock
1010
type TestPutBlock struct{}
1011
1012
var _ = Suite(&TestPutBlock{})
1013
1014
func (suite *TestPutBlock) Test(c *C) {
122.2.1 by Raphael Badin
Propagate Azure errors, add IsNotFoundError() method.
1015
    response := makeHttpResponse(http.StatusCreated, "")
42.1.1 by Julian Edwards
Add PutBlock
1016
    transport := &TestTransport{Response: response}
114.1.2 by Julian Edwards
mechanical changes to use the new test helper
1017
    context := makeStorageContext(transport)
42.1.1 by Julian Edwards
Add PutBlock
1018
    blockid := "\x1b\xea\xf7Mv\xb5\xddH\xebm"
139.1.1 by Gavin Panella
Camel-case everything, clean-up some comments.
1019
    randomData := MakeRandomByteSlice(10)
1020
    dataReader := bytes.NewReader(randomData)
1021
    err := context.PutBlock("container", "blobname", blockid, dataReader)
42.1.1 by Julian Edwards
Add PutBlock
1022
    c.Assert(err, IsNil)
1023
1024
    // The blockid should have been base64 encoded and url escaped.
139.1.1 by Gavin Panella
Camel-case everything, clean-up some comments.
1025
    base64ID := base64.StdEncoding.EncodeToString([]byte(blockid))
114.2.1 by Raphael Badin
Export getFileURL.
1026
    c.Check(transport.Request.URL.String(), Matches, context.GetFileURL("container", "blobname")+"?.*")
98.3.3 by Jeroen Vermeulen
Now fix the tests that assumed a fixed ordering or URL queries. The url package doesn't maintain any consistent order, so these started failing randomly. Bastards.
1027
    c.Check(transport.Request.URL.Query(), DeepEquals, url.Values{
1028
        "comp":    {"block"},
139.1.1 by Gavin Panella
Camel-case everything, clean-up some comments.
1029
        "blockid": {base64ID},
98.3.3 by Jeroen Vermeulen
Now fix the tests that assumed a fixed ordering or URL queries. The url package doesn't maintain any consistent order, so these started failing randomly. Bastards.
1030
    })
42.1.1 by Julian Edwards
Add PutBlock
1031
    c.Check(transport.Request.Header.Get("Authorization"), Not(Equals), "")
1032
1033
    data, err := ioutil.ReadAll(transport.Request.Body)
1034
    c.Assert(err, IsNil)
139.1.1 by Gavin Panella
Camel-case everything, clean-up some comments.
1035
    c.Check(data, DeepEquals, randomData)
42.1.1 by Julian Edwards
Add PutBlock
1036
}
42.1.2 by Julian Edwards
add extra tests for PutBlock
1037
1038
// Client-side errors from the HTTP client are propagated back to the caller.
1039
func (suite *TestPutBlock) TestError(c *C) {
1040
    error := fmt.Errorf("canned-error")
114.1.2 by Julian Edwards
mechanical changes to use the new test helper
1041
    context := makeStorageContext(&TestTransport{Error: error})
139.1.1 by Gavin Panella
Camel-case everything, clean-up some comments.
1042
    dataReader := bytes.NewReader(MakeRandomByteSlice(10))
1043
    err := context.PutBlock("container", "blobname", "blockid", dataReader)
50.2.3 by Gavin Panella
Use NotNil instead of Not(IsNil).
1044
    c.Assert(err, NotNil)
42.1.2 by Julian Edwards
add extra tests for PutBlock
1045
}
1046
1047
// Server-side errors are propagated back to the caller.
1048
func (suite *TestPutBlock) TestErrorResponse(c *C) {
122.2.1 by Raphael Badin
Propagate Azure errors, add IsNotFoundError() method.
1049
    responseBody := "<Error><Code>Frotzed</Code><Message>failed to put block</Message></Error>"
1050
    response := makeHttpResponse(102, responseBody)
114.1.2 by Julian Edwards
mechanical changes to use the new test helper
1051
    context := makeStorageContext(&TestTransport{Response: response})
139.1.1 by Gavin Panella
Camel-case everything, clean-up some comments.
1052
    dataReader := bytes.NewReader(MakeRandomByteSlice(10))
1053
    err := context.PutBlock("container", "blobname", "blockid", dataReader)
65.1.1 by Jeroen Vermeulen
Unify Error and ServerError as HTTPError, with different implementations based on what information is available at runtime.
1054
    c.Assert(err, NotNil)
1055
    c.Check(err, ErrorMatches, ".*102.*")
1056
    c.Check(err, ErrorMatches, ".*Frotzed.*")
1057
    c.Check(err, ErrorMatches, ".*failed to put block.*")
42.1.2 by Julian Edwards
add extra tests for PutBlock
1058
}
45.1.1 by Julian Edwards
Add a PutBlockList call
1059
122.2.1 by Raphael Badin
Propagate Azure errors, add IsNotFoundError() method.
1060
// Azure HTTP errors (for instance 404 responses) are propagated back to
1061
// the caller as ServerError objects.
1062
func (suite *TestPutBlock) TestServerError(c *C) {
1063
    response := makeHttpResponse(http.StatusNotFound, "not found")
1064
    context := makeStorageContext(&TestTransport{Response: response})
139.1.1 by Gavin Panella
Camel-case everything, clean-up some comments.
1065
    dataReader := bytes.NewReader(MakeRandomByteSlice(10))
1066
    err := context.PutBlock("container", "blobname", "blockid", dataReader)
122.2.1 by Raphael Badin
Propagate Azure errors, add IsNotFoundError() method.
1067
    serverError, ok := err.(*ServerError)
1068
    c.Check(ok, Equals, true)
1069
    c.Check(serverError.HTTPStatus.StatusCode(), Equals, http.StatusNotFound)
1070
}
1071
45.1.1 by Julian Edwards
Add a PutBlockList call
1072
type TestPutBlockList struct{}
1073
1074
var _ = Suite(&TestPutBlockList{})
1075
1076
func (suite *TestPutBlockList) Test(c *C) {
122.2.1 by Raphael Badin
Propagate Azure errors, add IsNotFoundError() method.
1077
    response := makeHttpResponse(http.StatusCreated, "")
45.1.1 by Julian Edwards
Add a PutBlockList call
1078
    transport := &TestTransport{Response: response}
114.1.2 by Julian Edwards
mechanical changes to use the new test helper
1079
    context := makeStorageContext(transport)
45.1.1 by Julian Edwards
Add a PutBlockList call
1080
    blocklist := &BlockList{}
1081
    blocklist.Add(BlockListLatest, "b1")
1082
    blocklist.Add(BlockListLatest, "b2")
1083
    err := context.PutBlockList("container", "blobname", blocklist)
1084
    c.Assert(err, IsNil)
1085
45.1.2 by Julian Edwards
check request method
1086
    c.Check(transport.Request.Method, Equals, "PUT")
45.1.1 by Julian Edwards
Add a PutBlockList call
1087
    c.Check(transport.Request.URL.String(), Equals, fmt.Sprintf(
1088
        "http://%s.blob.core.windows.net/container/blobname?comp=blocklist",
1089
        context.Account))
1090
    c.Check(transport.Request.Header.Get("Authorization"), Not(Equals), "")
1091
1092
    data, err := ioutil.ReadAll(transport.Request.Body)
1093
    c.Assert(err, IsNil)
1094
    expected := dedent.Dedent(`
1095
        <BlockList>
45.1.3 by Julian Edwards
changes to make PutBlock work - prevent Go from chunking the data
1096
          <Latest>YjE=</Latest>
1097
          <Latest>YjI=</Latest>
45.1.1 by Julian Edwards
Add a PutBlockList call
1098
        </BlockList>`)
186.1.1 by jtv at canonical
Fix test failures (but not the crashes) from upgrade to go1.1.1.
1099
    c.Check(strings.TrimSpace(string(data)), Equals, strings.TrimSpace(expected))
45.1.1 by Julian Edwards
Add a PutBlockList call
1100
}
1101
1102
// Client-side errors from the HTTP client are propagated back to the caller.
1103
func (suite *TestPutBlockList) TestError(c *C) {
1104
    error := fmt.Errorf("canned-error")
114.1.2 by Julian Edwards
mechanical changes to use the new test helper
1105
    context := makeStorageContext(&TestTransport{Error: error})
45.1.1 by Julian Edwards
Add a PutBlockList call
1106
    blocklist := &BlockList{}
1107
    err := context.PutBlockList("container", "blobname", blocklist)
50.2.3 by Gavin Panella
Use NotNil instead of Not(IsNil).
1108
    c.Assert(err, NotNil)
45.1.1 by Julian Edwards
Add a PutBlockList call
1109
}
1110
1111
// Server-side errors are propagated back to the caller.
1112
func (suite *TestPutBlockList) TestErrorResponse(c *C) {
122.2.1 by Raphael Badin
Propagate Azure errors, add IsNotFoundError() method.
1113
    responseBody := "<Error><Code>Frotzed</Code><Message>failed to put blocklist</Message></Error>"
1114
    response := makeHttpResponse(102, responseBody)
114.1.2 by Julian Edwards
mechanical changes to use the new test helper
1115
    context := makeStorageContext(&TestTransport{Response: response})
45.1.1 by Julian Edwards
Add a PutBlockList call
1116
    blocklist := &BlockList{}
1117
    err := context.PutBlockList("container", "blobname", blocklist)
65.1.1 by Jeroen Vermeulen
Unify Error and ServerError as HTTPError, with different implementations based on what information is available at runtime.
1118
    c.Assert(err, NotNil)
1119
    c.Check(err, ErrorMatches, ".*102.*")
1120
    c.Check(err, ErrorMatches, ".*Frotzed.*")
1121
    c.Check(err, ErrorMatches, ".*failed to put blocklist.*")
45.1.1 by Julian Edwards
Add a PutBlockList call
1122
}
50.2.1 by Gavin Panella
New Storage API operation, DeleteBlob().
1123
122.2.1 by Raphael Badin
Propagate Azure errors, add IsNotFoundError() method.
1124
// Azure HTTP errors (for instance 404 responses) are propagated back to
1125
// the caller as ServerError objects.
1126
func (suite *TestPutBlockList) TestServerError(c *C) {
1127
    response := makeHttpResponse(http.StatusNotFound, "not found")
1128
    context := makeStorageContext(&TestTransport{Response: response})
1129
    blocklist := &BlockList{}
1130
    err := context.PutBlockList("container", "blobname", blocklist)
1131
    serverError, ok := err.(*ServerError)
1132
    c.Check(ok, Equals, true)
1133
    c.Check(serverError.HTTPStatus.StatusCode(), Equals, http.StatusNotFound)
1134
}
1135
51.1.2 by Julian Edwards
add GetBlockList
1136
type TestGetBlockList struct{}
1137
1138
var _ = Suite(&TestGetBlockList{})
1139
1140
// The GetBlockList Storage API call returns a GetBlockList struct on
1141
// success.
1142
func (suite *TestGetBlockList) Test(c *C) {
122.2.1 by Raphael Badin
Propagate Azure errors, add IsNotFoundError() method.
1143
    responseBody := `
51.1.2 by Julian Edwards
add GetBlockList
1144
        <?xml version="1.0" encoding="utf-8"?>
1145
        <BlockList>
1146
          <CommittedBlocks>
1147
            <Block>
1148
              <Name>BlockId001</Name>
1149
              <Size>4194304</Size>
1150
            </Block>
1151
          </CommittedBlocks>
1152
          <UncommittedBlocks>
1153
            <Block>
1154
              <Name>BlockId002</Name>
1155
              <Size>1024</Size>
1156
            </Block>
1157
          </UncommittedBlocks>
1158
        </BlockList>`
1159
122.2.1 by Raphael Badin
Propagate Azure errors, add IsNotFoundError() method.
1160
    response := makeHttpResponse(http.StatusOK, responseBody)
51.1.2 by Julian Edwards
add GetBlockList
1161
    transport := &TestTransport{Response: response}
114.1.2 by Julian Edwards
mechanical changes to use the new test helper
1162
    context := makeStorageContext(transport)
51.1.2 by Julian Edwards
add GetBlockList
1163
    results, err := context.GetBlockList("container", "myfilename")
1164
    c.Assert(err, IsNil)
114.2.1 by Raphael Badin
Export getFileURL.
1165
    c.Check(transport.Request.URL.String(), Matches, context.GetFileURL("container", "myfilename")+"?.*")
98.3.3 by Jeroen Vermeulen
Now fix the tests that assumed a fixed ordering or URL queries. The url package doesn't maintain any consistent order, so these started failing randomly. Bastards.
1166
    c.Check(transport.Request.URL.Query(), DeepEquals, url.Values{
1167
        "comp":          {"blocklist"},
1168
        "blocklisttype": {"all"},
1169
    })
51.1.2 by Julian Edwards
add GetBlockList
1170
    c.Check(transport.Request.Header.Get("Authorization"), Not(Equals), "")
51.1.3 by Julian Edwards
improve test
1171
    c.Assert(results, NotNil)
51.1.2 by Julian Edwards
add GetBlockList
1172
}
1173
1174
// Client-side errors from the HTTP client are propagated back to the caller.
1175
func (suite *TestGetBlockList) TestError(c *C) {
1176
    error := fmt.Errorf("canned-error")
114.1.2 by Julian Edwards
mechanical changes to use the new test helper
1177
    context := makeStorageContext(&TestTransport{Error: error})
51.1.2 by Julian Edwards
add GetBlockList
1178
    _, err := context.GetBlockList("container", "myfilename")
51.1.3 by Julian Edwards
improve test
1179
    c.Assert(err, NotNil)
51.1.2 by Julian Edwards
add GetBlockList
1180
}
1181
122.2.1 by Raphael Badin
Propagate Azure errors, add IsNotFoundError() method.
1182
// Azure HTTP errors (for instance 404 responses) are propagated back to
1183
// the caller as ServerError objects.
1184
func (suite *TestGetBlockList) TestServerError(c *C) {
1185
    response := makeHttpResponse(http.StatusNotFound, "not found")
114.1.2 by Julian Edwards
mechanical changes to use the new test helper
1186
    context := makeStorageContext(&TestTransport{Response: response})
122.2.1 by Raphael Badin
Propagate Azure errors, add IsNotFoundError() method.
1187
    _, err := context.GetBlockList("container", "blobname")
1188
    serverError, ok := err.(*ServerError)
1189
    c.Check(ok, Equals, true)
1190
    c.Check(serverError.HTTPStatus.StatusCode(), Equals, http.StatusNotFound)
51.1.2 by Julian Edwards
add GetBlockList
1191
}
51.1.4 by Julian Edwards
merge trunk to resolve conflict
1192
50.2.1 by Gavin Panella
New Storage API operation, DeleteBlob().
1193
type TestDeleteBlob struct{}
1194
1195
var _ = Suite(&TestDeleteBlob{})
1196
1197
func (suite *TestDeleteBlob) Test(c *C) {
122.2.1 by Raphael Badin
Propagate Azure errors, add IsNotFoundError() method.
1198
    response := makeHttpResponse(http.StatusAccepted, "")
50.2.1 by Gavin Panella
New Storage API operation, DeleteBlob().
1199
    transport := &TestTransport{Response: response}
114.1.2 by Julian Edwards
mechanical changes to use the new test helper
1200
    context := makeStorageContext(transport)
50.2.1 by Gavin Panella
New Storage API operation, DeleteBlob().
1201
    err := context.DeleteBlob("container", "blobname")
1202
    c.Assert(err, IsNil)
1203
1204
    c.Check(transport.Request.Method, Equals, "DELETE")
114.2.1 by Raphael Badin
Export getFileURL.
1205
    c.Check(transport.Request.URL.String(), Equals, context.GetFileURL("container", "blobname"))
50.2.1 by Gavin Panella
New Storage API operation, DeleteBlob().
1206
    c.Check(transport.Request.Header.Get("Authorization"), Not(Equals), "")
1207
    c.Check(transport.Request.Body, IsNil)
1208
}
1209
1210
// Client-side errors from the HTTP client are propagated back to the caller.
1211
func (suite *TestDeleteBlob) TestError(c *C) {
1212
    error := fmt.Errorf("canned-error")
114.1.2 by Julian Edwards
mechanical changes to use the new test helper
1213
    context := makeStorageContext(&TestTransport{Error: error})
50.2.1 by Gavin Panella
New Storage API operation, DeleteBlob().
1214
    err := context.DeleteBlob("container", "blobname")
50.2.3 by Gavin Panella
Use NotNil instead of Not(IsNil).
1215
    c.Assert(err, NotNil)
50.2.1 by Gavin Panella
New Storage API operation, DeleteBlob().
1216
}
1217
122.2.1 by Raphael Badin
Propagate Azure errors, add IsNotFoundError() method.
1218
// Azure HTTP errors (for instance 404 responses) are propagated back to
1219
// the caller as ServerError objects.
1220
func (suite *TestDeleteBlob) TestServerError(c *C) {
124.1.3 by Raphael Badin
Improve comment.
1221
    // We're not using http.StatusNotFound for the test here because
124.1.1 by Raphael Badin
Deleting a non-existant blob does not return an error.
1222
    // 404 errors are handled in a special way by DeleteBlob().  See the test
207.1.2 by Raphael Badin
Fix typo.
1223
    // TestDeleteNotExistentBlobDoesNotFail.
124.1.2 by Raphael Badin
Tiny consistency fix.
1224
    response := makeHttpResponse(http.StatusMethodNotAllowed, "not allowed")
122.2.1 by Raphael Badin
Propagate Azure errors, add IsNotFoundError() method.
1225
    context := makeStorageContext(&TestTransport{Response: response})
1226
    err := context.DeleteBlob("container", "blobname")
1227
    serverError, ok := err.(*ServerError)
1228
    c.Check(ok, Equals, true)
124.1.1 by Raphael Badin
Deleting a non-existant blob does not return an error.
1229
    c.Check(serverError.HTTPStatus.StatusCode(), Equals, http.StatusMethodNotAllowed)
1230
}
1231
207.1.2 by Raphael Badin
Fix typo.
1232
func (suite *TestDeleteBlob) TestDeleteNotExistentBlobDoesNotFail(c *C) {
124.1.1 by Raphael Badin
Deleting a non-existant blob does not return an error.
1233
    response := makeHttpResponse(http.StatusNotFound, "not found")
1234
    context := makeStorageContext(&TestTransport{Response: response})
1235
    err := context.DeleteBlob("container", "blobname")
1236
    c.Assert(err, IsNil)
122.2.1 by Raphael Badin
Propagate Azure errors, add IsNotFoundError() method.
1237
}
1238
50.2.1 by Gavin Panella
New Storage API operation, DeleteBlob().
1239
// Server-side errors are propagated back to the caller.
1240
func (suite *TestDeleteBlob) TestErrorResponse(c *C) {
122.2.1 by Raphael Badin
Propagate Azure errors, add IsNotFoundError() method.
1241
    responseBody := "<Error><Code>Frotzed</Code><Message>failed to delete blob</Message></Error>"
1242
    response := makeHttpResponse(146, responseBody)
114.1.2 by Julian Edwards
mechanical changes to use the new test helper
1243
    context := makeStorageContext(&TestTransport{Response: response})
50.2.1 by Gavin Panella
New Storage API operation, DeleteBlob().
1244
    err := context.DeleteBlob("container", "blobname")
65.1.1 by Jeroen Vermeulen
Unify Error and ServerError as HTTPError, with different implementations based on what information is available at runtime.
1245
    c.Assert(err, NotNil)
1246
    c.Check(err, ErrorMatches, ".*146.*")
1247
    c.Check(err, ErrorMatches, ".*Frotzed.*")
1248
    c.Check(err, ErrorMatches, ".*failed to delete blob.*")
50.2.1 by Gavin Panella
New Storage API operation, DeleteBlob().
1249
}
54.1.1 by Gavin Panella
New Storage API operation, GetBlob().
1250
1251
type TestGetBlob struct{}
1252
1253
var _ = Suite(&TestGetBlob{})
1254
1255
func (suite *TestGetBlob) Test(c *C) {
122.2.1 by Raphael Badin
Propagate Azure errors, add IsNotFoundError() method.
1256
    responseBody := "blob-in-a-can"
1257
    response := makeHttpResponse(http.StatusOK, responseBody)
54.1.1 by Gavin Panella
New Storage API operation, GetBlob().
1258
    transport := &TestTransport{Response: response}
114.1.2 by Julian Edwards
mechanical changes to use the new test helper
1259
    context := makeStorageContext(transport)
54.1.1 by Gavin Panella
New Storage API operation, GetBlob().
1260
    reader, err := context.GetBlob("container", "blobname")
59.1.1 by Julian Edwards
Add error handling to the storage context send()
1261
    c.Assert(err, IsNil)
54.1.2 by Gavin Panella
Assert instead of Check that the reader is not nil.
1262
    c.Assert(reader, NotNil)
54.1.1 by Gavin Panella
New Storage API operation, GetBlob().
1263
    defer reader.Close()
1264
1265
    c.Check(transport.Request.Method, Equals, "GET")
114.2.1 by Raphael Badin
Export getFileURL.
1266
    c.Check(transport.Request.URL.String(), Equals, context.GetFileURL("container", "blobname"))
54.1.1 by Gavin Panella
New Storage API operation, GetBlob().
1267
    c.Check(transport.Request.Header.Get("Authorization"), Not(Equals), "")
1268
1269
    data, err := ioutil.ReadAll(reader)
1270
    c.Assert(err, IsNil)
122.2.1 by Raphael Badin
Propagate Azure errors, add IsNotFoundError() method.
1271
    c.Check(string(data), Equals, responseBody)
54.1.1 by Gavin Panella
New Storage API operation, GetBlob().
1272
}
1273
1274
// Client-side errors from the HTTP client are propagated back to the caller.
1275
func (suite *TestGetBlob) TestError(c *C) {
1276
    error := fmt.Errorf("canned-error")
114.1.2 by Julian Edwards
mechanical changes to use the new test helper
1277
    context := makeStorageContext(&TestTransport{Error: error})
54.1.1 by Gavin Panella
New Storage API operation, GetBlob().
1278
    reader, err := context.GetBlob("container", "blobname")
1279
    c.Check(reader, IsNil)
1280
    c.Assert(err, NotNil)
1281
}
1282
122.2.1 by Raphael Badin
Propagate Azure errors, add IsNotFoundError() method.
1283
// Azure HTTP errors (for instance 404 responses) are propagated back to
1284
// the caller as ServerError objects.
1285
func (suite *TestGetBlob) TestServerError(c *C) {
1286
    response := makeHttpResponse(http.StatusNotFound, "not found")
1287
    context := makeStorageContext(&TestTransport{Response: response})
1288
    reader, err := context.GetBlob("container", "blobname")
1289
    c.Check(reader, IsNil)
1290
    c.Assert(err, NotNil)
1291
    serverError, ok := err.(*ServerError)
1292
    c.Check(ok, Equals, true)
1293
    c.Check(serverError.HTTPStatus.StatusCode(), Equals, http.StatusNotFound)
1294
    c.Check(IsNotFoundError(err), Equals, true)
1295
}
1296
54.1.1 by Gavin Panella
New Storage API operation, GetBlob().
1297
// Server-side errors are propagated back to the caller.
1298
func (suite *TestGetBlob) TestErrorResponse(c *C) {
1299
    response := &http.Response{
1300
        Status:     "246 Frotzed",
1301
        StatusCode: 246,
93.3.4 by Jeroen Vermeulen
Test batching of ListContainers results. Tests: 1 panic.
1302
        Body:       makeResponseBody("<Error><Code>Frotzed</Code><Message>failed to get blob</Message></Error>"),
54.1.1 by Gavin Panella
New Storage API operation, GetBlob().
1303
    }
114.1.2 by Julian Edwards
mechanical changes to use the new test helper
1304
    context := makeStorageContext(&TestTransport{Response: response})
54.1.1 by Gavin Panella
New Storage API operation, GetBlob().
1305
    reader, err := context.GetBlob("container", "blobname")
1306
    c.Check(reader, IsNil)
1307
    c.Assert(err, NotNil)
65.1.1 by Jeroen Vermeulen
Unify Error and ServerError as HTTPError, with different implementations based on what information is available at runtime.
1308
    c.Check(err, ErrorMatches, ".*246.*")
1309
    c.Check(err, ErrorMatches, ".*Frotzed.*")
1310
    c.Check(err, ErrorMatches, ".*failed to get blob.*")
54.1.1 by Gavin Panella
New Storage API operation, GetBlob().
1311
}
127.4.1 by Julian Edwards
happy path test for SetContainerACL
1312
1313
type TestSetContainerACL struct{}
1314
1315
var _ = Suite(&TestSetContainerACL{})
1316
1317
func (suite *TestSetContainerACL) TestHappyPath(c *C) {
1318
    response := makeHttpResponse(http.StatusOK, "")
1319
    transport := &TestTransport{Response: response}
1320
    context := makeStorageContext(transport)
1321
    err := context.SetContainerACL(&SetContainerACLRequest{
1322
        Container: "mycontainer", Access: "container"})
1323
1324
    c.Assert(err, IsNil)
1325
    c.Check(transport.Request.Method, Equals, "PUT")
127.4.5 by Julian Edwards
more tests and test fixes
1326
    c.Check(transport.Request.URL.String(), Matches,
127.4.1 by Julian Edwards
happy path test for SetContainerACL
1327
        fmt.Sprintf(
127.4.5 by Julian Edwards
more tests and test fixes
1328
            "http://%s.blob.core.windows.net/mycontainer?.*", context.Account))
1329
    c.Check(transport.Request.URL.Query(), DeepEquals, url.Values{
1330
        "comp":    {"acl"},
1331
        "restype": {"container"},
1332
    })
1333
127.4.1 by Julian Edwards
happy path test for SetContainerACL
1334
    c.Check(transport.Request.Header.Get("Authorization"), Not(Equals), "")
1335
    c.Check(transport.Request.Header.Get("x-ms-blob-public-access"), Equals, "container")
1336
}
127.4.4 by Julian Edwards
Add test for omitting header when private
1337
127.4.6 by Julian Edwards
moar tests
1338
func (suite *TestSetContainerACL) TestAcceptsBlobAccess(c *C) {
1339
    response := makeHttpResponse(http.StatusOK, "")
1340
    transport := &TestTransport{Response: response}
1341
    context := makeStorageContext(transport)
1342
    err := context.SetContainerACL(&SetContainerACLRequest{
1343
        Container: "mycontainer", Access: "blob"})
1344
    c.Assert(err, IsNil)
1345
    c.Check(transport.Request.Header.Get("x-ms-blob-public-access"), Equals, "blob")
1346
}
1347
127.4.4 by Julian Edwards
Add test for omitting header when private
1348
func (suite *TestSetContainerACL) TestAccessHeaderOmittedWhenPrivate(c *C) {
1349
    response := makeHttpResponse(http.StatusOK, "")
1350
    transport := &TestTransport{Response: response}
1351
    context := makeStorageContext(transport)
1352
    err := context.SetContainerACL(&SetContainerACLRequest{
1353
        Container: "mycontainer", Access: "private"})
1354
1355
    c.Assert(err, IsNil)
1356
    c.Check(transport.Request.Header.Get("x-ms-blob-public-access"), Equals, "")
1357
}
127.4.5 by Julian Edwards
more tests and test fixes
1358
127.4.6 by Julian Edwards
moar tests
1359
func (suite *TestSetContainerACL) TestInvalidAccessTypePanics(c *C) {
1360
    defer func() {
1361
        err := recover()
1362
        c.Assert(err, Equals, "Access must be one of 'container', 'blob' or 'private'")
1363
    }()
1364
    context := makeStorageContext(&TestTransport{})
1365
    context.SetContainerACL(&SetContainerACLRequest{
1366
        Container: "mycontainer", Access: "thisisnotvalid"})
1367
    c.Assert("This test failed", Equals, "because there was no panic")
1368
}
1369
127.4.5 by Julian Edwards
more tests and test fixes
1370
func (suite *TestSetContainerACL) TestClientSideError(c *C) {
1371
    error := fmt.Errorf("canned-error")
1372
    context := makeStorageContext(&TestTransport{Error: error})
1373
    err := context.SetContainerACL(&SetContainerACLRequest{
1374
        Container: "mycontainer", Access: "private"})
1375
    c.Assert(err, NotNil)
1376
}
1377
1378
// Azure HTTP errors (for instance 404 responses) are propagated back to
1379
// the caller as ServerError objects.
1380
func (suite *TestSetContainerACL) TestServerError(c *C) {
1381
    response := makeHttpResponse(http.StatusNotFound, "not found")
1382
    context := makeStorageContext(&TestTransport{Response: response})
1383
    err := context.SetContainerACL(&SetContainerACLRequest{
1384
        Container: "mycontainer", Access: "private"})
1385
    c.Assert(err, NotNil)
1386
    serverError, ok := err.(*ServerError)
1387
    c.Check(ok, Equals, true)
1388
    c.Check(serverError.HTTPStatus.StatusCode(), Equals, http.StatusNotFound)
1389
    c.Check(IsNotFoundError(err), Equals, true)
1390
}