~wallyworld/gwacl/fix-request-eof

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) TestErrorResponse(c *C) {
721
    response := makeHttpResponse(http.StatusNotFound, "not found")
722
    context := makeStorageContext(&TestTransport{Response: response})
723
    err := context.DeleteContainer("container")
201.2.3 by Julian Edwards
improve test
724
    c.Assert(err, ErrorMatches, ".*Not Found.*")
201.2.2 by Julian Edwards
Add tests
725
}
726
727
// Server-side errors are propagated back to the caller.
728
func (suite *TestDeleteContainer) TestNotCreatedResponse(c *C) {
729
    response := makeHttpResponse(http.StatusOK, "")
730
    context := makeStorageContext(&TestTransport{Response: response})
731
    err := context.DeleteContainer("container")
201.2.3 by Julian Edwards
improve test
732
    c.Assert(err, ErrorMatches, ".*Azure request failed.*")
201.2.2 by Julian Edwards
Add tests
733
}
734
735
// Azure HTTP errors (for instance 404 responses) are propagated back to
736
// the caller as ServerError objects.
737
func (suite *TestDeleteContainer) TestServerError(c *C) {
738
    response := makeHttpResponse(http.StatusNotFound, "not found")
739
    context := makeStorageContext(&TestTransport{Response: response})
740
    err := context.DeleteContainer("container")
741
    serverError, ok := err.(*ServerError)
742
    c.Check(ok, Equals, true)
743
    c.Check(serverError.HTTPStatus.StatusCode(), Equals, http.StatusNotFound)
744
}
745
205.1.2 by Julian Edwards
implement GetContainerProperties
746
type TestGetContainerProperties struct{}
747
748
var _ = Suite(&TestGetContainerProperties{})
749
750
// The GetContainerProperties Storage API call returns without error when the
751
// container has been created successfully.
752
func (suite *TestGetContainerProperties) Test(c *C) {
753
    header := make(http.Header)
754
    header.Add("Last-Modified", "last-modified")
755
    header.Add("ETag", "etag")
756
    header.Add("X-Ms-Lease-Status", "status")
757
    header.Add("X-Ms-Lease-State", "state")
758
    header.Add("X-Ms-Lease-Duration", "duration")
759
    response := &http.Response{
760
        Status:     fmt.Sprintf("%d", http.StatusOK),
761
        StatusCode: http.StatusOK,
762
        Body:       makeResponseBody(""),
763
        Header:     header,
764
    }
765
766
    transport := &TestTransport{Response: response}
767
    context := makeStorageContext(transport)
768
    containerName := MakeRandomString(10)
769
    props, err := context.GetContainerProperties(containerName)
770
    c.Assert(err, IsNil)
771
    c.Check(transport.Request.URL.String(), Equals, fmt.Sprintf(
772
        "http://%s.blob.core.windows.net/%s?restype=container",
773
        context.Account, containerName))
774
    c.Check(transport.Request.Method, Equals, "GET")
775
    c.Check(transport.Request.Header.Get("Authorization"), Not(Equals), "")
776
777
    c.Check(props.LastModified, Equals, "last-modified")
778
    c.Check(props.ETag, Equals, "etag")
779
    c.Check(props.LeaseStatus, Equals, "status")
780
    c.Check(props.LeaseState, Equals, "state")
781
    c.Check(props.LeaseDuration, Equals, "duration")
782
}
783
206.1.1 by Julian Edwards
Allow for optional headers when parsing result of GetContainerProperties!
784
func (suite *TestGetContainerProperties) TestWithoutAllHeaders(c *C) {
785
    response := &http.Response{
786
        Status:     fmt.Sprintf("%d", http.StatusOK),
787
        StatusCode: http.StatusOK,
788
        Body:       makeResponseBody(""),
789
    }
790
791
    transport := &TestTransport{Response: response}
792
    context := makeStorageContext(transport)
793
    containerName := MakeRandomString(10)
794
    props, err := context.GetContainerProperties(containerName)
795
    c.Assert(err, IsNil)
796
797
    c.Check(props.LastModified, Equals, "")
798
    c.Check(props.ETag, Equals, "")
799
    c.Check(props.LeaseStatus, Equals, "")
800
    c.Check(props.LeaseState, Equals, "")
801
    c.Check(props.LeaseDuration, Equals, "")
802
}
803
804
205.1.2 by Julian Edwards
implement GetContainerProperties
805
// Client-side errors from the HTTP client are propagated back to the caller.
806
func (suite *TestGetContainerProperties) TestError(c *C) {
807
    error := fmt.Errorf("canned-error")
808
    context := makeStorageContext(&TestTransport{Error: error})
809
    _, err := context.GetContainerProperties("container")
810
    c.Assert(err, ErrorMatches, ".*canned-error.*")
811
}
812
813
// Server-side errors are propagated back to the caller.
814
func (suite *TestGetContainerProperties) TestErrorResponse(c *C) {
815
    response := makeHttpResponse(http.StatusNotFound, "not found")
816
    context := makeStorageContext(&TestTransport{Response: response})
817
    _, err := context.GetContainerProperties("container")
818
    c.Assert(err, ErrorMatches, ".*Not Found.*")
819
}
820
821
// Azure HTTP errors (for instance 404 responses) are propagated back to
822
// the caller as ServerError objects.
823
func (suite *TestGetContainerProperties) TestServerError(c *C) {
824
    response := makeHttpResponse(http.StatusNotFound, "not found")
825
    context := makeStorageContext(&TestTransport{Response: response})
826
    _, err := context.GetContainerProperties("container")
827
    serverError, ok := err.(*ServerError)
205.1.4 by Julian Edwards
review changes
828
    c.Assert(ok, Equals, true)
205.1.2 by Julian Edwards
implement GetContainerProperties
829
    c.Check(serverError.HTTPStatus.StatusCode(), Equals, http.StatusNotFound)
830
}
831
113.1.1 by Julian Edwards
Add happy path test for putpage
832
type TestPutPage struct{}
833
834
var _ = Suite(&TestPutPage{})
835
836
// Basic happy path testing.
837
func (suite *TestPutPage) TestHappyPath(c *C) {
122.2.1 by Raphael Badin
Propagate Azure errors, add IsNotFoundError() method.
838
    response := makeHttpResponse(http.StatusCreated, "")
113.1.1 by Julian Edwards
Add happy path test for putpage
839
    transport := &TestTransport{Response: response}
114.1.2 by Julian Edwards
mechanical changes to use the new test helper
840
    context := makeStorageContext(transport)
113.1.1 by Julian Edwards
Add happy path test for putpage
841
    randomData := MakeRandomByteSlice(10)
842
    dataReader := bytes.NewReader(randomData)
843
844
    err := context.PutPage(&PutPageRequest{
845
        Container: "container", Filename: "filename", StartRange: 0,
127.3.1 by Julian Edwards
Add failing test
846
        EndRange: 511, Data: dataReader})
113.1.1 by Julian Edwards
Add happy path test for putpage
847
    c.Assert(err, IsNil)
848
849
    // Ensure that container was set right.
114.2.1 by Raphael Badin
Export getFileURL.
850
    c.Check(transport.Request.URL.String(), Matches, context.GetFileURL("container", "filename")+"?.*")
113.1.1 by Julian Edwards
Add happy path test for putpage
851
    // Ensure that the Authorization header is set.
852
    c.Check(transport.Request.Header.Get("Authorization"), Not(Equals), "")
853
    // Check the range is set.
127.3.1 by Julian Edwards
Add failing test
854
    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
855
    // Check special page write header.
856
    c.Check(transport.Request.Header.Get("x-ms-page-write"), Equals, "update")
857
    // "?comp=page" should be part of the URL.
858
    c.Check(transport.Request.URL.Query(), DeepEquals, url.Values{
859
        "comp": {"page"},
860
    })
861
    // Check the data payload.
862
    data, err := ioutil.ReadAll(transport.Request.Body)
863
    c.Assert(err, IsNil)
864
    c.Check(data, DeepEquals, randomData)
865
}
866
113.1.3 by Julian Edwards
Add generic error testing
867
// Client-side errors from the HTTP client are propagated back to the caller.
868
func (suite *TestPutPage) TestError(c *C) {
869
    cannedError := fmt.Errorf("canned-error")
114.1.2 by Julian Edwards
mechanical changes to use the new test helper
870
    context := makeStorageContext(&TestTransport{Error: cannedError})
113.1.3 by Julian Edwards
Add generic error testing
871
    err := context.PutPage(&PutPageRequest{
872
        Container: "container", Filename: "filename", StartRange: 0,
127.3.1 by Julian Edwards
Add failing test
873
        EndRange: 511, Data: nil})
113.1.3 by Julian Edwards
Add generic error testing
874
    c.Assert(err, NotNil)
875
}
876
877
// Server-side errors are propagated back to the caller.
878
func (suite *TestPutPage) TestErrorResponse(c *C) {
122.2.1 by Raphael Badin
Propagate Azure errors, add IsNotFoundError() method.
879
    responseBody := "<Error><Code>Frotzed</Code><Message>failed to put blob</Message></Error>"
880
    response := makeHttpResponse(102, responseBody)
114.1.2 by Julian Edwards
mechanical changes to use the new test helper
881
    context := makeStorageContext(&TestTransport{Response: response})
113.1.3 by Julian Edwards
Add generic error testing
882
    err := context.PutPage(&PutPageRequest{
883
        Container: "container", Filename: "filename", StartRange: 0,
127.3.1 by Julian Edwards
Add failing test
884
        EndRange: 511, Data: nil})
113.1.3 by Julian Edwards
Add generic error testing
885
    c.Assert(err, NotNil)
886
    c.Check(err, ErrorMatches, ".*102.*")
887
    c.Check(err, ErrorMatches, ".*Frotzed.*")
888
    c.Check(err, ErrorMatches, ".*failed to put blob.*")
889
}
890
122.2.1 by Raphael Badin
Propagate Azure errors, add IsNotFoundError() method.
891
// Azure HTTP errors (for instance 404 responses) are propagated back to
892
// the caller as ServerError objects.
893
func (suite *TestPutPage) TestServerError(c *C) {
894
    response := makeHttpResponse(http.StatusNotFound, "not found")
895
    context := makeStorageContext(&TestTransport{Response: response})
896
    err := context.PutPage(&PutPageRequest{
897
        Container: "container", Filename: "filename", StartRange: 0,
127.3.1 by Julian Edwards
Add failing test
898
        EndRange: 511, Data: nil})
122.2.1 by Raphael Badin
Propagate Azure errors, add IsNotFoundError() method.
899
    serverError, ok := err.(*ServerError)
900
    c.Check(ok, Equals, true)
901
    c.Check(serverError.HTTPStatus.StatusCode(), Equals, http.StatusNotFound)
902
}
903
127.3.1 by Julian Edwards
Add failing test
904
// Range values outside the limits should get rejected.
905
func (suite *TestPutPage) TestRangeLimits(c *C) {
906
    context := makeStorageContext(&TestTransport{})
907
    err := context.PutPage(&PutPageRequest{
908
        StartRange: 513, EndRange: 555})
909
    c.Assert(err, NotNil)
127.3.5 by Julian Edwards
gavin's review comments
910
    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
911
}
912
37.1.1 by Julian Edwards
make PutBlobk
913
type TestPutBlob struct{}
914
915
var _ = Suite(&TestPutBlob{})
916
108.3.4 by Julian Edwards
jtv's review comments addressed
917
// Test basic PutBlob happy path functionality.
108.3.3 by Julian Edwards
add a test for page block requests
918
func (suite *TestPutBlob) TestPutBlockBlob(c *C) {
122.2.1 by Raphael Badin
Propagate Azure errors, add IsNotFoundError() method.
919
    response := makeHttpResponse(http.StatusCreated, "")
37.1.1 by Julian Edwards
make PutBlobk
920
    transport := &TestTransport{Response: response}
114.1.2 by Julian Edwards
mechanical changes to use the new test helper
921
    context := makeStorageContext(transport)
922
108.3.1 by Julian Edwards
Use struct as request params for PutBlob and add a blob_type
923
    err := context.PutBlob(&PutBlobRequest{
108.3.4 by Julian Edwards
jtv's review comments addressed
924
        Container: "container", BlobType: "block", Filename: "blobname"})
37.1.1 by Julian Edwards
make PutBlobk
925
    c.Assert(err, IsNil)
108.3.4 by Julian Edwards
jtv's review comments addressed
926
    // Ensure that container was set right.
114.2.1 by Raphael Badin
Export getFileURL.
927
    c.Check(transport.Request.URL.String(), Equals, context.GetFileURL("container", "blobname"))
108.3.4 by Julian Edwards
jtv's review comments addressed
928
    // Ensure that the Authorization header is set.
37.1.1 by Julian Edwards
make PutBlobk
929
    c.Check(transport.Request.Header.Get("Authorization"), Not(Equals), "")
108.3.4 by Julian Edwards
jtv's review comments addressed
930
    // The blob type should be a block.
108.3.2 by Julian Edwards
Add x-ms-blob-type header to PutBlob request
931
    c.Check(transport.Request.Header.Get("x-ms-blob-type"), Equals, "BlockBlob")
37.1.1 by Julian Edwards
make PutBlobk
932
}
933
108.3.4 by Julian Edwards
jtv's review comments addressed
934
// 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
935
func (suite *TestPutBlob) TestPutPageBlob(c *C) {
122.2.1 by Raphael Badin
Propagate Azure errors, add IsNotFoundError() method.
936
    response := makeHttpResponse(http.StatusCreated, "")
108.3.3 by Julian Edwards
add a test for page block requests
937
    transport := &TestTransport{Response: response}
114.1.2 by Julian Edwards
mechanical changes to use the new test helper
938
    context := makeStorageContext(transport)
108.3.3 by Julian Edwards
add a test for page block requests
939
    err := context.PutBlob(&PutBlobRequest{
115.1.1 by Julian Edwards
Add tests for size parameter on pubblob for a page
940
        Container: "container", BlobType: "page", Filename: "blobname",
941
        Size: 512})
108.3.3 by Julian Edwards
add a test for page block requests
942
    c.Assert(err, IsNil)
943
    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
944
    c.Check(transport.Request.Header.Get("x-ms-blob-content-length"), Equals, "512")
945
}
946
947
// PutBlob for a page should return an error when Size is not specified.
948
func (suite *TestPutBlob) TestPutPageBlobWithSizeOmitted(c *C) {
949
    context := makeStorageContext(&TestTransport{})
950
    err := context.PutBlob(&PutBlobRequest{
951
        Container: "container", BlobType: "page", Filename: "blob"})
952
    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
953
}
954
127.3.3 by Julian Edwards
failing test
955
// PutBlob for a page should return an error when Size is not a multiple
956
// of 512 bytes.
957
func (suite *TestPutBlob) TestPutPageBlobWithInvalidSiuze(c *C) {
958
    context := makeStorageContext(&TestTransport{})
959
    err := context.PutBlob(&PutBlobRequest{
960
        Container: "container", BlobType: "page", Filename: "blob",
961
        Size: 1015})
127.3.5 by Julian Edwards
gavin's review comments
962
    c.Assert(err, ErrorMatches, "Size must be a multiple of 512 bytes")
127.3.3 by Julian Edwards
failing test
963
}
964
108.3.4 by Julian Edwards
jtv's review comments addressed
965
// 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
966
func (suite *TestPutBlob) TestBlobType(c *C) {
108.3.4 by Julian Edwards
jtv's review comments addressed
967
    defer func() {
968
        err := recover()
139.1.1 by Gavin Panella
Camel-case everything, clean-up some comments.
969
        c.Assert(err, Equals, "blockType must be 'page' or 'block'")
108.3.4 by Julian Edwards
jtv's review comments addressed
970
    }()
114.1.2 by Julian Edwards
mechanical changes to use the new test helper
971
    context := makeStorageContext(&TestTransport{})
108.3.4 by Julian Edwards
jtv's review comments addressed
972
    context.PutBlob(&PutBlobRequest{
973
        Container: "container", BlobType: "invalid-blob-type",
974
        Filename: "blobname"})
975
    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
976
}
977
37.1.1 by Julian Edwards
make PutBlobk
978
// Client-side errors from the HTTP client are propagated back to the caller.
979
func (suite *TestPutBlob) TestError(c *C) {
980
    error := fmt.Errorf("canned-error")
114.1.2 by Julian Edwards
mechanical changes to use the new test helper
981
    context := makeStorageContext(&TestTransport{Error: error})
108.3.1 by Julian Edwards
Use struct as request params for PutBlob and add a blob_type
982
    err := context.PutBlob(&PutBlobRequest{
108.3.4 by Julian Edwards
jtv's review comments addressed
983
        Container: "container", BlobType: "block", Filename: "blobname"})
50.2.3 by Gavin Panella
Use NotNil instead of Not(IsNil).
984
    c.Assert(err, NotNil)
37.1.1 by Julian Edwards
make PutBlobk
985
}
986
987
// Server-side errors are propagated back to the caller.
988
func (suite *TestPutBlob) TestErrorResponse(c *C) {
122.2.1 by Raphael Badin
Propagate Azure errors, add IsNotFoundError() method.
989
    responseBody := "<Error><Code>Frotzed</Code><Message>failed to put blob</Message></Error>"
990
    response := makeHttpResponse(102, responseBody)
114.1.2 by Julian Edwards
mechanical changes to use the new test helper
991
    context := makeStorageContext(&TestTransport{Response: response})
108.3.1 by Julian Edwards
Use struct as request params for PutBlob and add a blob_type
992
    err := context.PutBlob(&PutBlobRequest{
108.3.4 by Julian Edwards
jtv's review comments addressed
993
        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.
994
    c.Assert(err, NotNil)
995
    c.Check(err, ErrorMatches, ".*102.*")
996
    c.Check(err, ErrorMatches, ".*Frotzed.*")
997
    c.Check(err, ErrorMatches, ".*failed to put blob.*")
37.1.1 by Julian Edwards
make PutBlobk
998
}
42.1.1 by Julian Edwards
Add PutBlock
999
122.2.1 by Raphael Badin
Propagate Azure errors, add IsNotFoundError() method.
1000
// Azure HTTP errors (for instance 404 responses) are propagated back to
1001
// the caller as ServerError objects.
1002
func (suite *TestPutBlob) TestServerError(c *C) {
1003
    response := makeHttpResponse(http.StatusNotFound, "not found")
1004
    context := makeStorageContext(&TestTransport{Response: response})
1005
    err := context.PutBlob(&PutBlobRequest{
1006
        Container: "container", BlobType: "block", Filename: "blobname"})
1007
    serverError, ok := err.(*ServerError)
1008
    c.Check(ok, Equals, true)
1009
    c.Check(serverError.HTTPStatus.StatusCode(), Equals, http.StatusNotFound)
1010
}
1011
42.1.1 by Julian Edwards
Add PutBlock
1012
type TestPutBlock struct{}
1013
1014
var _ = Suite(&TestPutBlock{})
1015
1016
func (suite *TestPutBlock) Test(c *C) {
122.2.1 by Raphael Badin
Propagate Azure errors, add IsNotFoundError() method.
1017
    response := makeHttpResponse(http.StatusCreated, "")
42.1.1 by Julian Edwards
Add PutBlock
1018
    transport := &TestTransport{Response: response}
114.1.2 by Julian Edwards
mechanical changes to use the new test helper
1019
    context := makeStorageContext(transport)
42.1.1 by Julian Edwards
Add PutBlock
1020
    blockid := "\x1b\xea\xf7Mv\xb5\xddH\xebm"
139.1.1 by Gavin Panella
Camel-case everything, clean-up some comments.
1021
    randomData := MakeRandomByteSlice(10)
1022
    dataReader := bytes.NewReader(randomData)
1023
    err := context.PutBlock("container", "blobname", blockid, dataReader)
42.1.1 by Julian Edwards
Add PutBlock
1024
    c.Assert(err, IsNil)
1025
1026
    // The blockid should have been base64 encoded and url escaped.
139.1.1 by Gavin Panella
Camel-case everything, clean-up some comments.
1027
    base64ID := base64.StdEncoding.EncodeToString([]byte(blockid))
114.2.1 by Raphael Badin
Export getFileURL.
1028
    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.
1029
    c.Check(transport.Request.URL.Query(), DeepEquals, url.Values{
1030
        "comp":    {"block"},
139.1.1 by Gavin Panella
Camel-case everything, clean-up some comments.
1031
        "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.
1032
    })
42.1.1 by Julian Edwards
Add PutBlock
1033
    c.Check(transport.Request.Header.Get("Authorization"), Not(Equals), "")
1034
1035
    data, err := ioutil.ReadAll(transport.Request.Body)
1036
    c.Assert(err, IsNil)
139.1.1 by Gavin Panella
Camel-case everything, clean-up some comments.
1037
    c.Check(data, DeepEquals, randomData)
42.1.1 by Julian Edwards
Add PutBlock
1038
}
42.1.2 by Julian Edwards
add extra tests for PutBlock
1039
1040
// Client-side errors from the HTTP client are propagated back to the caller.
1041
func (suite *TestPutBlock) TestError(c *C) {
1042
    error := fmt.Errorf("canned-error")
114.1.2 by Julian Edwards
mechanical changes to use the new test helper
1043
    context := makeStorageContext(&TestTransport{Error: error})
139.1.1 by Gavin Panella
Camel-case everything, clean-up some comments.
1044
    dataReader := bytes.NewReader(MakeRandomByteSlice(10))
1045
    err := context.PutBlock("container", "blobname", "blockid", dataReader)
50.2.3 by Gavin Panella
Use NotNil instead of Not(IsNil).
1046
    c.Assert(err, NotNil)
42.1.2 by Julian Edwards
add extra tests for PutBlock
1047
}
1048
1049
// Server-side errors are propagated back to the caller.
1050
func (suite *TestPutBlock) TestErrorResponse(c *C) {
122.2.1 by Raphael Badin
Propagate Azure errors, add IsNotFoundError() method.
1051
    responseBody := "<Error><Code>Frotzed</Code><Message>failed to put block</Message></Error>"
1052
    response := makeHttpResponse(102, responseBody)
114.1.2 by Julian Edwards
mechanical changes to use the new test helper
1053
    context := makeStorageContext(&TestTransport{Response: response})
139.1.1 by Gavin Panella
Camel-case everything, clean-up some comments.
1054
    dataReader := bytes.NewReader(MakeRandomByteSlice(10))
1055
    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.
1056
    c.Assert(err, NotNil)
1057
    c.Check(err, ErrorMatches, ".*102.*")
1058
    c.Check(err, ErrorMatches, ".*Frotzed.*")
1059
    c.Check(err, ErrorMatches, ".*failed to put block.*")
42.1.2 by Julian Edwards
add extra tests for PutBlock
1060
}
45.1.1 by Julian Edwards
Add a PutBlockList call
1061
122.2.1 by Raphael Badin
Propagate Azure errors, add IsNotFoundError() method.
1062
// Azure HTTP errors (for instance 404 responses) are propagated back to
1063
// the caller as ServerError objects.
1064
func (suite *TestPutBlock) TestServerError(c *C) {
1065
    response := makeHttpResponse(http.StatusNotFound, "not found")
1066
    context := makeStorageContext(&TestTransport{Response: response})
139.1.1 by Gavin Panella
Camel-case everything, clean-up some comments.
1067
    dataReader := bytes.NewReader(MakeRandomByteSlice(10))
1068
    err := context.PutBlock("container", "blobname", "blockid", dataReader)
122.2.1 by Raphael Badin
Propagate Azure errors, add IsNotFoundError() method.
1069
    serverError, ok := err.(*ServerError)
1070
    c.Check(ok, Equals, true)
1071
    c.Check(serverError.HTTPStatus.StatusCode(), Equals, http.StatusNotFound)
1072
}
1073
45.1.1 by Julian Edwards
Add a PutBlockList call
1074
type TestPutBlockList struct{}
1075
1076
var _ = Suite(&TestPutBlockList{})
1077
1078
func (suite *TestPutBlockList) Test(c *C) {
122.2.1 by Raphael Badin
Propagate Azure errors, add IsNotFoundError() method.
1079
    response := makeHttpResponse(http.StatusCreated, "")
45.1.1 by Julian Edwards
Add a PutBlockList call
1080
    transport := &TestTransport{Response: response}
114.1.2 by Julian Edwards
mechanical changes to use the new test helper
1081
    context := makeStorageContext(transport)
45.1.1 by Julian Edwards
Add a PutBlockList call
1082
    blocklist := &BlockList{}
1083
    blocklist.Add(BlockListLatest, "b1")
1084
    blocklist.Add(BlockListLatest, "b2")
1085
    err := context.PutBlockList("container", "blobname", blocklist)
1086
    c.Assert(err, IsNil)
1087
45.1.2 by Julian Edwards
check request method
1088
    c.Check(transport.Request.Method, Equals, "PUT")
45.1.1 by Julian Edwards
Add a PutBlockList call
1089
    c.Check(transport.Request.URL.String(), Equals, fmt.Sprintf(
1090
        "http://%s.blob.core.windows.net/container/blobname?comp=blocklist",
1091
        context.Account))
1092
    c.Check(transport.Request.Header.Get("Authorization"), Not(Equals), "")
1093
1094
    data, err := ioutil.ReadAll(transport.Request.Body)
1095
    c.Assert(err, IsNil)
1096
    expected := dedent.Dedent(`
1097
        <BlockList>
45.1.3 by Julian Edwards
changes to make PutBlock work - prevent Go from chunking the data
1098
          <Latest>YjE=</Latest>
1099
          <Latest>YjI=</Latest>
45.1.1 by Julian Edwards
Add a PutBlockList call
1100
        </BlockList>`)
186.1.1 by jtv at canonical
Fix test failures (but not the crashes) from upgrade to go1.1.1.
1101
    c.Check(strings.TrimSpace(string(data)), Equals, strings.TrimSpace(expected))
45.1.1 by Julian Edwards
Add a PutBlockList call
1102
}
1103
1104
// Client-side errors from the HTTP client are propagated back to the caller.
1105
func (suite *TestPutBlockList) TestError(c *C) {
1106
    error := fmt.Errorf("canned-error")
114.1.2 by Julian Edwards
mechanical changes to use the new test helper
1107
    context := makeStorageContext(&TestTransport{Error: error})
45.1.1 by Julian Edwards
Add a PutBlockList call
1108
    blocklist := &BlockList{}
1109
    err := context.PutBlockList("container", "blobname", blocklist)
50.2.3 by Gavin Panella
Use NotNil instead of Not(IsNil).
1110
    c.Assert(err, NotNil)
45.1.1 by Julian Edwards
Add a PutBlockList call
1111
}
1112
1113
// Server-side errors are propagated back to the caller.
1114
func (suite *TestPutBlockList) TestErrorResponse(c *C) {
122.2.1 by Raphael Badin
Propagate Azure errors, add IsNotFoundError() method.
1115
    responseBody := "<Error><Code>Frotzed</Code><Message>failed to put blocklist</Message></Error>"
1116
    response := makeHttpResponse(102, responseBody)
114.1.2 by Julian Edwards
mechanical changes to use the new test helper
1117
    context := makeStorageContext(&TestTransport{Response: response})
45.1.1 by Julian Edwards
Add a PutBlockList call
1118
    blocklist := &BlockList{}
1119
    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.
1120
    c.Assert(err, NotNil)
1121
    c.Check(err, ErrorMatches, ".*102.*")
1122
    c.Check(err, ErrorMatches, ".*Frotzed.*")
1123
    c.Check(err, ErrorMatches, ".*failed to put blocklist.*")
45.1.1 by Julian Edwards
Add a PutBlockList call
1124
}
50.2.1 by Gavin Panella
New Storage API operation, DeleteBlob().
1125
122.2.1 by Raphael Badin
Propagate Azure errors, add IsNotFoundError() method.
1126
// Azure HTTP errors (for instance 404 responses) are propagated back to
1127
// the caller as ServerError objects.
1128
func (suite *TestPutBlockList) TestServerError(c *C) {
1129
    response := makeHttpResponse(http.StatusNotFound, "not found")
1130
    context := makeStorageContext(&TestTransport{Response: response})
1131
    blocklist := &BlockList{}
1132
    err := context.PutBlockList("container", "blobname", blocklist)
1133
    serverError, ok := err.(*ServerError)
1134
    c.Check(ok, Equals, true)
1135
    c.Check(serverError.HTTPStatus.StatusCode(), Equals, http.StatusNotFound)
1136
}
1137
51.1.2 by Julian Edwards
add GetBlockList
1138
type TestGetBlockList struct{}
1139
1140
var _ = Suite(&TestGetBlockList{})
1141
1142
// The GetBlockList Storage API call returns a GetBlockList struct on
1143
// success.
1144
func (suite *TestGetBlockList) Test(c *C) {
122.2.1 by Raphael Badin
Propagate Azure errors, add IsNotFoundError() method.
1145
    responseBody := `
51.1.2 by Julian Edwards
add GetBlockList
1146
        <?xml version="1.0" encoding="utf-8"?>
1147
        <BlockList>
1148
          <CommittedBlocks>
1149
            <Block>
1150
              <Name>BlockId001</Name>
1151
              <Size>4194304</Size>
1152
            </Block>
1153
          </CommittedBlocks>
1154
          <UncommittedBlocks>
1155
            <Block>
1156
              <Name>BlockId002</Name>
1157
              <Size>1024</Size>
1158
            </Block>
1159
          </UncommittedBlocks>
1160
        </BlockList>`
1161
122.2.1 by Raphael Badin
Propagate Azure errors, add IsNotFoundError() method.
1162
    response := makeHttpResponse(http.StatusOK, responseBody)
51.1.2 by Julian Edwards
add GetBlockList
1163
    transport := &TestTransport{Response: response}
114.1.2 by Julian Edwards
mechanical changes to use the new test helper
1164
    context := makeStorageContext(transport)
51.1.2 by Julian Edwards
add GetBlockList
1165
    results, err := context.GetBlockList("container", "myfilename")
1166
    c.Assert(err, IsNil)
114.2.1 by Raphael Badin
Export getFileURL.
1167
    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.
1168
    c.Check(transport.Request.URL.Query(), DeepEquals, url.Values{
1169
        "comp":          {"blocklist"},
1170
        "blocklisttype": {"all"},
1171
    })
51.1.2 by Julian Edwards
add GetBlockList
1172
    c.Check(transport.Request.Header.Get("Authorization"), Not(Equals), "")
51.1.3 by Julian Edwards
improve test
1173
    c.Assert(results, NotNil)
51.1.2 by Julian Edwards
add GetBlockList
1174
}
1175
1176
// Client-side errors from the HTTP client are propagated back to the caller.
1177
func (suite *TestGetBlockList) TestError(c *C) {
1178
    error := fmt.Errorf("canned-error")
114.1.2 by Julian Edwards
mechanical changes to use the new test helper
1179
    context := makeStorageContext(&TestTransport{Error: error})
51.1.2 by Julian Edwards
add GetBlockList
1180
    _, err := context.GetBlockList("container", "myfilename")
51.1.3 by Julian Edwards
improve test
1181
    c.Assert(err, NotNil)
51.1.2 by Julian Edwards
add GetBlockList
1182
}
1183
122.2.1 by Raphael Badin
Propagate Azure errors, add IsNotFoundError() method.
1184
// Azure HTTP errors (for instance 404 responses) are propagated back to
1185
// the caller as ServerError objects.
1186
func (suite *TestGetBlockList) TestServerError(c *C) {
1187
    response := makeHttpResponse(http.StatusNotFound, "not found")
114.1.2 by Julian Edwards
mechanical changes to use the new test helper
1188
    context := makeStorageContext(&TestTransport{Response: response})
122.2.1 by Raphael Badin
Propagate Azure errors, add IsNotFoundError() method.
1189
    _, err := context.GetBlockList("container", "blobname")
1190
    serverError, ok := err.(*ServerError)
1191
    c.Check(ok, Equals, true)
1192
    c.Check(serverError.HTTPStatus.StatusCode(), Equals, http.StatusNotFound)
51.1.2 by Julian Edwards
add GetBlockList
1193
}
51.1.4 by Julian Edwards
merge trunk to resolve conflict
1194
50.2.1 by Gavin Panella
New Storage API operation, DeleteBlob().
1195
type TestDeleteBlob struct{}
1196
1197
var _ = Suite(&TestDeleteBlob{})
1198
1199
func (suite *TestDeleteBlob) Test(c *C) {
122.2.1 by Raphael Badin
Propagate Azure errors, add IsNotFoundError() method.
1200
    response := makeHttpResponse(http.StatusAccepted, "")
50.2.1 by Gavin Panella
New Storage API operation, DeleteBlob().
1201
    transport := &TestTransport{Response: response}
114.1.2 by Julian Edwards
mechanical changes to use the new test helper
1202
    context := makeStorageContext(transport)
50.2.1 by Gavin Panella
New Storage API operation, DeleteBlob().
1203
    err := context.DeleteBlob("container", "blobname")
1204
    c.Assert(err, IsNil)
1205
1206
    c.Check(transport.Request.Method, Equals, "DELETE")
114.2.1 by Raphael Badin
Export getFileURL.
1207
    c.Check(transport.Request.URL.String(), Equals, context.GetFileURL("container", "blobname"))
50.2.1 by Gavin Panella
New Storage API operation, DeleteBlob().
1208
    c.Check(transport.Request.Header.Get("Authorization"), Not(Equals), "")
1209
    c.Check(transport.Request.Body, IsNil)
1210
}
1211
1212
// Client-side errors from the HTTP client are propagated back to the caller.
1213
func (suite *TestDeleteBlob) TestError(c *C) {
1214
    error := fmt.Errorf("canned-error")
114.1.2 by Julian Edwards
mechanical changes to use the new test helper
1215
    context := makeStorageContext(&TestTransport{Error: error})
50.2.1 by Gavin Panella
New Storage API operation, DeleteBlob().
1216
    err := context.DeleteBlob("container", "blobname")
50.2.3 by Gavin Panella
Use NotNil instead of Not(IsNil).
1217
    c.Assert(err, NotNil)
50.2.1 by Gavin Panella
New Storage API operation, DeleteBlob().
1218
}
1219
122.2.1 by Raphael Badin
Propagate Azure errors, add IsNotFoundError() method.
1220
// Azure HTTP errors (for instance 404 responses) are propagated back to
1221
// the caller as ServerError objects.
1222
func (suite *TestDeleteBlob) TestServerError(c *C) {
124.1.3 by Raphael Badin
Improve comment.
1223
    // 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.
1224
    // 404 errors are handled in a special way by DeleteBlob().  See the test
1225
    // TestDeleteNotExistantBlobDoesNotFail.
124.1.2 by Raphael Badin
Tiny consistency fix.
1226
    response := makeHttpResponse(http.StatusMethodNotAllowed, "not allowed")
122.2.1 by Raphael Badin
Propagate Azure errors, add IsNotFoundError() method.
1227
    context := makeStorageContext(&TestTransport{Response: response})
1228
    err := context.DeleteBlob("container", "blobname")
1229
    serverError, ok := err.(*ServerError)
1230
    c.Check(ok, Equals, true)
124.1.1 by Raphael Badin
Deleting a non-existant blob does not return an error.
1231
    c.Check(serverError.HTTPStatus.StatusCode(), Equals, http.StatusMethodNotAllowed)
1232
}
1233
1234
func (suite *TestDeleteBlob) TestDeleteNotExistantBlobDoesNotFail(c *C) {
1235
    response := makeHttpResponse(http.StatusNotFound, "not found")
1236
    context := makeStorageContext(&TestTransport{Response: response})
1237
    err := context.DeleteBlob("container", "blobname")
1238
    c.Assert(err, IsNil)
122.2.1 by Raphael Badin
Propagate Azure errors, add IsNotFoundError() method.
1239
}
1240
50.2.1 by Gavin Panella
New Storage API operation, DeleteBlob().
1241
// Server-side errors are propagated back to the caller.
1242
func (suite *TestDeleteBlob) TestErrorResponse(c *C) {
122.2.1 by Raphael Badin
Propagate Azure errors, add IsNotFoundError() method.
1243
    responseBody := "<Error><Code>Frotzed</Code><Message>failed to delete blob</Message></Error>"
1244
    response := makeHttpResponse(146, responseBody)
114.1.2 by Julian Edwards
mechanical changes to use the new test helper
1245
    context := makeStorageContext(&TestTransport{Response: response})
50.2.1 by Gavin Panella
New Storage API operation, DeleteBlob().
1246
    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.
1247
    c.Assert(err, NotNil)
1248
    c.Check(err, ErrorMatches, ".*146.*")
1249
    c.Check(err, ErrorMatches, ".*Frotzed.*")
1250
    c.Check(err, ErrorMatches, ".*failed to delete blob.*")
50.2.1 by Gavin Panella
New Storage API operation, DeleteBlob().
1251
}
54.1.1 by Gavin Panella
New Storage API operation, GetBlob().
1252
1253
type TestGetBlob struct{}
1254
1255
var _ = Suite(&TestGetBlob{})
1256
1257
func (suite *TestGetBlob) Test(c *C) {
122.2.1 by Raphael Badin
Propagate Azure errors, add IsNotFoundError() method.
1258
    responseBody := "blob-in-a-can"
1259
    response := makeHttpResponse(http.StatusOK, responseBody)
54.1.1 by Gavin Panella
New Storage API operation, GetBlob().
1260
    transport := &TestTransport{Response: response}
114.1.2 by Julian Edwards
mechanical changes to use the new test helper
1261
    context := makeStorageContext(transport)
54.1.1 by Gavin Panella
New Storage API operation, GetBlob().
1262
    reader, err := context.GetBlob("container", "blobname")
59.1.1 by Julian Edwards
Add error handling to the storage context send()
1263
    c.Assert(err, IsNil)
54.1.2 by Gavin Panella
Assert instead of Check that the reader is not nil.
1264
    c.Assert(reader, NotNil)
54.1.1 by Gavin Panella
New Storage API operation, GetBlob().
1265
    defer reader.Close()
1266
1267
    c.Check(transport.Request.Method, Equals, "GET")
114.2.1 by Raphael Badin
Export getFileURL.
1268
    c.Check(transport.Request.URL.String(), Equals, context.GetFileURL("container", "blobname"))
54.1.1 by Gavin Panella
New Storage API operation, GetBlob().
1269
    c.Check(transport.Request.Header.Get("Authorization"), Not(Equals), "")
1270
1271
    data, err := ioutil.ReadAll(reader)
1272
    c.Assert(err, IsNil)
122.2.1 by Raphael Badin
Propagate Azure errors, add IsNotFoundError() method.
1273
    c.Check(string(data), Equals, responseBody)
54.1.1 by Gavin Panella
New Storage API operation, GetBlob().
1274
}
1275
1276
// Client-side errors from the HTTP client are propagated back to the caller.
1277
func (suite *TestGetBlob) TestError(c *C) {
1278
    error := fmt.Errorf("canned-error")
114.1.2 by Julian Edwards
mechanical changes to use the new test helper
1279
    context := makeStorageContext(&TestTransport{Error: error})
54.1.1 by Gavin Panella
New Storage API operation, GetBlob().
1280
    reader, err := context.GetBlob("container", "blobname")
1281
    c.Check(reader, IsNil)
1282
    c.Assert(err, NotNil)
1283
}
1284
122.2.1 by Raphael Badin
Propagate Azure errors, add IsNotFoundError() method.
1285
// Azure HTTP errors (for instance 404 responses) are propagated back to
1286
// the caller as ServerError objects.
1287
func (suite *TestGetBlob) TestServerError(c *C) {
1288
    response := makeHttpResponse(http.StatusNotFound, "not found")
1289
    context := makeStorageContext(&TestTransport{Response: response})
1290
    reader, err := context.GetBlob("container", "blobname")
1291
    c.Check(reader, IsNil)
1292
    c.Assert(err, NotNil)
1293
    serverError, ok := err.(*ServerError)
1294
    c.Check(ok, Equals, true)
1295
    c.Check(serverError.HTTPStatus.StatusCode(), Equals, http.StatusNotFound)
1296
    c.Check(IsNotFoundError(err), Equals, true)
1297
}
1298
54.1.1 by Gavin Panella
New Storage API operation, GetBlob().
1299
// Server-side errors are propagated back to the caller.
1300
func (suite *TestGetBlob) TestErrorResponse(c *C) {
1301
    response := &http.Response{
1302
        Status:     "246 Frotzed",
1303
        StatusCode: 246,
93.3.4 by Jeroen Vermeulen
Test batching of ListContainers results. Tests: 1 panic.
1304
        Body:       makeResponseBody("<Error><Code>Frotzed</Code><Message>failed to get blob</Message></Error>"),
54.1.1 by Gavin Panella
New Storage API operation, GetBlob().
1305
    }
114.1.2 by Julian Edwards
mechanical changes to use the new test helper
1306
    context := makeStorageContext(&TestTransport{Response: response})
54.1.1 by Gavin Panella
New Storage API operation, GetBlob().
1307
    reader, err := context.GetBlob("container", "blobname")
1308
    c.Check(reader, IsNil)
1309
    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.
1310
    c.Check(err, ErrorMatches, ".*246.*")
1311
    c.Check(err, ErrorMatches, ".*Frotzed.*")
1312
    c.Check(err, ErrorMatches, ".*failed to get blob.*")
54.1.1 by Gavin Panella
New Storage API operation, GetBlob().
1313
}
127.4.1 by Julian Edwards
happy path test for SetContainerACL
1314
1315
type TestSetContainerACL struct{}
1316
1317
var _ = Suite(&TestSetContainerACL{})
1318
1319
func (suite *TestSetContainerACL) TestHappyPath(c *C) {
1320
    response := makeHttpResponse(http.StatusOK, "")
1321
    transport := &TestTransport{Response: response}
1322
    context := makeStorageContext(transport)
1323
    err := context.SetContainerACL(&SetContainerACLRequest{
1324
        Container: "mycontainer", Access: "container"})
1325
1326
    c.Assert(err, IsNil)
1327
    c.Check(transport.Request.Method, Equals, "PUT")
127.4.5 by Julian Edwards
more tests and test fixes
1328
    c.Check(transport.Request.URL.String(), Matches,
127.4.1 by Julian Edwards
happy path test for SetContainerACL
1329
        fmt.Sprintf(
127.4.5 by Julian Edwards
more tests and test fixes
1330
            "http://%s.blob.core.windows.net/mycontainer?.*", context.Account))
1331
    c.Check(transport.Request.URL.Query(), DeepEquals, url.Values{
1332
        "comp":    {"acl"},
1333
        "restype": {"container"},
1334
    })
1335
127.4.1 by Julian Edwards
happy path test for SetContainerACL
1336
    c.Check(transport.Request.Header.Get("Authorization"), Not(Equals), "")
1337
    c.Check(transport.Request.Header.Get("x-ms-blob-public-access"), Equals, "container")
1338
}
127.4.4 by Julian Edwards
Add test for omitting header when private
1339
127.4.6 by Julian Edwards
moar tests
1340
func (suite *TestSetContainerACL) TestAcceptsBlobAccess(c *C) {
1341
    response := makeHttpResponse(http.StatusOK, "")
1342
    transport := &TestTransport{Response: response}
1343
    context := makeStorageContext(transport)
1344
    err := context.SetContainerACL(&SetContainerACLRequest{
1345
        Container: "mycontainer", Access: "blob"})
1346
    c.Assert(err, IsNil)
1347
    c.Check(transport.Request.Header.Get("x-ms-blob-public-access"), Equals, "blob")
1348
}
1349
127.4.4 by Julian Edwards
Add test for omitting header when private
1350
func (suite *TestSetContainerACL) TestAccessHeaderOmittedWhenPrivate(c *C) {
1351
    response := makeHttpResponse(http.StatusOK, "")
1352
    transport := &TestTransport{Response: response}
1353
    context := makeStorageContext(transport)
1354
    err := context.SetContainerACL(&SetContainerACLRequest{
1355
        Container: "mycontainer", Access: "private"})
1356
1357
    c.Assert(err, IsNil)
1358
    c.Check(transport.Request.Header.Get("x-ms-blob-public-access"), Equals, "")
1359
}
127.4.5 by Julian Edwards
more tests and test fixes
1360
127.4.6 by Julian Edwards
moar tests
1361
func (suite *TestSetContainerACL) TestInvalidAccessTypePanics(c *C) {
1362
    defer func() {
1363
        err := recover()
1364
        c.Assert(err, Equals, "Access must be one of 'container', 'blob' or 'private'")
1365
    }()
1366
    context := makeStorageContext(&TestTransport{})
1367
    context.SetContainerACL(&SetContainerACLRequest{
1368
        Container: "mycontainer", Access: "thisisnotvalid"})
1369
    c.Assert("This test failed", Equals, "because there was no panic")
1370
}
1371
127.4.5 by Julian Edwards
more tests and test fixes
1372
func (suite *TestSetContainerACL) TestClientSideError(c *C) {
1373
    error := fmt.Errorf("canned-error")
1374
    context := makeStorageContext(&TestTransport{Error: error})
1375
    err := context.SetContainerACL(&SetContainerACLRequest{
1376
        Container: "mycontainer", Access: "private"})
1377
    c.Assert(err, NotNil)
1378
}
1379
1380
// Azure HTTP errors (for instance 404 responses) are propagated back to
1381
// the caller as ServerError objects.
1382
func (suite *TestSetContainerACL) TestServerError(c *C) {
1383
    response := makeHttpResponse(http.StatusNotFound, "not found")
1384
    context := makeStorageContext(&TestTransport{Response: response})
1385
    err := context.SetContainerACL(&SetContainerACLRequest{
1386
        Container: "mycontainer", Access: "private"})
1387
    c.Assert(err, NotNil)
1388
    serverError, ok := err.(*ServerError)
1389
    c.Check(ok, Equals, true)
1390
    c.Check(serverError.HTTPStatus.StatusCode(), Equals, http.StatusNotFound)
1391
    c.Check(IsNotFoundError(err), Equals, true)
1392
}