1package azblob_test
2
3import (
4	"context"
5	"encoding/base64"
6	"fmt"
7	"io/ioutil"
8	"time"
9
10	"crypto/md5"
11
12	"bytes"
13	"strings"
14
15	"github.com/Azure/azure-storage-blob-go/azblob"
16	chk "gopkg.in/check.v1" // go get gopkg.in/check.v1
17)
18
19func (s *aztestsSuite) TestStageGetBlocks(c *chk.C) {
20	bsu := getBSU()
21	container, _ := createNewContainer(c, bsu)
22	defer delContainer(c, container)
23
24	blob := container.NewBlockBlobURL(generateBlobName())
25
26	blockID := base64.StdEncoding.EncodeToString([]byte(fmt.Sprintf("%6d", 0)))
27
28	putResp, err := blob.StageBlock(context.Background(), blockID, getReaderToRandomBytes(1024), azblob.LeaseAccessConditions{}, nil)
29	c.Assert(err, chk.IsNil)
30	c.Assert(putResp.Response().StatusCode, chk.Equals, 201)
31	c.Assert(putResp.ContentMD5(), chk.Not(chk.Equals), "")
32	c.Assert(putResp.RequestID(), chk.Not(chk.Equals), "")
33	c.Assert(putResp.Version(), chk.Not(chk.Equals), "")
34	c.Assert(putResp.Date().IsZero(), chk.Equals, false)
35
36	blockList, err := blob.GetBlockList(context.Background(), azblob.BlockListAll, azblob.LeaseAccessConditions{})
37	c.Assert(err, chk.IsNil)
38	c.Assert(blockList.Response().StatusCode, chk.Equals, 200)
39	c.Assert(blockList.LastModified().IsZero(), chk.Equals, true)
40	c.Assert(blockList.ETag(), chk.Equals, azblob.ETagNone)
41	c.Assert(blockList.ContentType(), chk.Not(chk.Equals), "")
42	c.Assert(blockList.BlobContentLength(), chk.Equals, int64(-1))
43	c.Assert(blockList.RequestID(), chk.Not(chk.Equals), "")
44	c.Assert(blockList.Version(), chk.Not(chk.Equals), "")
45	c.Assert(blockList.Date().IsZero(), chk.Equals, false)
46	c.Assert(blockList.CommittedBlocks, chk.HasLen, 0)
47	c.Assert(blockList.UncommittedBlocks, chk.HasLen, 1)
48
49	listResp, err := blob.CommitBlockList(context.Background(), []string{blockID}, azblob.BlobHTTPHeaders{}, nil, azblob.BlobAccessConditions{})
50	c.Assert(err, chk.IsNil)
51	c.Assert(listResp.Response().StatusCode, chk.Equals, 201)
52	c.Assert(listResp.LastModified().IsZero(), chk.Equals, false)
53	c.Assert(listResp.ETag(), chk.Not(chk.Equals), azblob.ETagNone)
54	c.Assert(listResp.ContentMD5(), chk.Not(chk.Equals), "")
55	c.Assert(listResp.RequestID(), chk.Not(chk.Equals), "")
56	c.Assert(listResp.Version(), chk.Not(chk.Equals), "")
57	c.Assert(listResp.Date().IsZero(), chk.Equals, false)
58
59	blockList, err = blob.GetBlockList(context.Background(), azblob.BlockListAll, azblob.LeaseAccessConditions{})
60	c.Assert(err, chk.IsNil)
61	c.Assert(blockList.Response().StatusCode, chk.Equals, 200)
62	c.Assert(blockList.LastModified().IsZero(), chk.Equals, false)
63	c.Assert(blockList.ETag(), chk.Not(chk.Equals), azblob.ETagNone)
64	c.Assert(blockList.ContentType(), chk.Not(chk.Equals), "")
65	c.Assert(blockList.BlobContentLength(), chk.Equals, int64(1024))
66	c.Assert(blockList.RequestID(), chk.Not(chk.Equals), "")
67	c.Assert(blockList.Version(), chk.Not(chk.Equals), "")
68	c.Assert(blockList.Date().IsZero(), chk.Equals, false)
69	c.Assert(blockList.CommittedBlocks, chk.HasLen, 1)
70	c.Assert(blockList.UncommittedBlocks, chk.HasLen, 0)
71}
72
73func (s *aztestsSuite) TestStageBlockFromURL(c *chk.C) {
74	bsu := getBSU()
75	credential, err := getGenericCredential("")
76	if err != nil {
77		c.Fatal("Invalid credential")
78	}
79	container, _ := createNewContainer(c, bsu)
80	defer delContainer(c, container)
81
82	testSize := 8 * 1024 * 1024 // 8MB
83	r, sourceData := getRandomDataAndReader(testSize)
84	ctx := context.Background() // Use default Background context
85	srcBlob := container.NewBlockBlobURL(generateBlobName())
86	destBlob := container.NewBlockBlobURL(generateBlobName())
87
88	// Prepare source blob for copy.
89	uploadSrcResp, err := srcBlob.Upload(ctx, r, azblob.BlobHTTPHeaders{}, azblob.Metadata{}, azblob.BlobAccessConditions{})
90	c.Assert(err, chk.IsNil)
91	c.Assert(uploadSrcResp.Response().StatusCode, chk.Equals, 201)
92
93	// Get source blob URL with SAS for StageFromURL.
94	srcBlobParts := azblob.NewBlobURLParts(srcBlob.URL())
95
96	srcBlobParts.SAS, err = azblob.BlobSASSignatureValues{
97		Protocol:      azblob.SASProtocolHTTPS,              // Users MUST use HTTPS (not HTTP)
98		ExpiryTime:    time.Now().UTC().Add(48 * time.Hour), // 48-hours before expiration
99		ContainerName: srcBlobParts.ContainerName,
100		BlobName:      srcBlobParts.BlobName,
101		Permissions:   azblob.BlobSASPermissions{Read: true}.String(),
102	}.NewSASQueryParameters(credential)
103	if err != nil {
104		c.Fatal(err)
105	}
106
107	srcBlobURLWithSAS := srcBlobParts.URL()
108
109	// Stage blocks from URL.
110	blockID1, blockID2 := base64.StdEncoding.EncodeToString([]byte(fmt.Sprintf("%6d", 0))), base64.StdEncoding.EncodeToString([]byte(fmt.Sprintf("%6d", 1)))
111	stageResp1, err := destBlob.StageBlockFromURL(ctx, blockID1, srcBlobURLWithSAS, 0, 4*1024*1024, azblob.LeaseAccessConditions{}, azblob.ModifiedAccessConditions{})
112	c.Assert(err, chk.IsNil)
113	c.Assert(stageResp1.Response().StatusCode, chk.Equals, 201)
114	c.Assert(stageResp1.ContentMD5(), chk.Not(chk.Equals), "")
115	c.Assert(stageResp1.RequestID(), chk.Not(chk.Equals), "")
116	c.Assert(stageResp1.Version(), chk.Not(chk.Equals), "")
117	c.Assert(stageResp1.Date().IsZero(), chk.Equals, false)
118
119	stageResp2, err := destBlob.StageBlockFromURL(ctx, blockID2, srcBlobURLWithSAS, 4*1024*1024, azblob.CountToEnd, azblob.LeaseAccessConditions{}, azblob.ModifiedAccessConditions{})
120	c.Assert(err, chk.IsNil)
121	c.Assert(stageResp2.Response().StatusCode, chk.Equals, 201)
122	c.Assert(stageResp2.ContentMD5(), chk.Not(chk.Equals), "")
123	c.Assert(stageResp2.RequestID(), chk.Not(chk.Equals), "")
124	c.Assert(stageResp2.Version(), chk.Not(chk.Equals), "")
125	c.Assert(stageResp2.Date().IsZero(), chk.Equals, false)
126
127	// Check block list.
128	blockList, err := destBlob.GetBlockList(context.Background(), azblob.BlockListAll, azblob.LeaseAccessConditions{})
129	c.Assert(err, chk.IsNil)
130	c.Assert(blockList.Response().StatusCode, chk.Equals, 200)
131	c.Assert(blockList.CommittedBlocks, chk.HasLen, 0)
132	c.Assert(blockList.UncommittedBlocks, chk.HasLen, 2)
133
134	// Commit block list.
135	listResp, err := destBlob.CommitBlockList(context.Background(), []string{blockID1, blockID2}, azblob.BlobHTTPHeaders{}, nil, azblob.BlobAccessConditions{})
136	c.Assert(err, chk.IsNil)
137	c.Assert(listResp.Response().StatusCode, chk.Equals, 201)
138
139	// Check data integrity through downloading.
140	downloadResp, err := destBlob.BlobURL.Download(ctx, 0, azblob.CountToEnd, azblob.BlobAccessConditions{}, false)
141	c.Assert(err, chk.IsNil)
142	destData, err := ioutil.ReadAll(downloadResp.Body(azblob.RetryReaderOptions{}))
143	c.Assert(err, chk.IsNil)
144	c.Assert(destData, chk.DeepEquals, sourceData)
145}
146
147func (s *aztestsSuite) TestBlobSASQueryParamOverrideResponseHeaders(c *chk.C) {
148	bsu := getBSU()
149	credential, err := getGenericCredential("")
150	if err != nil {
151		c.Fatal("Invalid credential")
152	}
153	container, _ := createNewContainer(c, bsu)
154	defer delContainer(c, container)
155
156	testSize := 8 * 1024 * 1024 // 8MB
157	r, _ := getRandomDataAndReader(testSize)
158	ctx := context.Background() // Use default Background context
159	blob := container.NewBlockBlobURL(generateBlobName())
160
161	uploadResp, err := blob.Upload(ctx, r, azblob.BlobHTTPHeaders{}, azblob.Metadata{}, azblob.BlobAccessConditions{})
162	c.Assert(err, chk.IsNil)
163	c.Assert(uploadResp.Response().StatusCode, chk.Equals, 201)
164
165	// Get blob URL with SAS.
166	blobParts := azblob.NewBlobURLParts(blob.URL())
167
168	cacheControlVal := "cache-control-override"
169	contentDispositionVal := "content-disposition-override"
170	contentEncodingVal := "content-encoding-override"
171	contentLanguageVal := "content-language-override"
172	contentTypeVal := "content-type-override"
173
174	blobParts.SAS, err = azblob.BlobSASSignatureValues{
175		Protocol:           azblob.SASProtocolHTTPS,              // Users MUST use HTTPS (not HTTP)
176		ExpiryTime:         time.Now().UTC().Add(48 * time.Hour), // 48-hours before expiration
177		ContainerName:      blobParts.ContainerName,
178		BlobName:           blobParts.BlobName,
179		Permissions:        azblob.BlobSASPermissions{Read: true}.String(),
180		CacheControl:       cacheControlVal,
181		ContentDisposition: contentDispositionVal,
182		ContentEncoding:    contentEncodingVal,
183		ContentLanguage:    contentLanguageVal,
184		ContentType:        contentTypeVal,
185	}.NewSASQueryParameters(credential)
186	if err != nil {
187		c.Fatal(err)
188	}
189
190	blobURL := azblob.NewBlobURL(blobParts.URL(), azblob.NewPipeline(azblob.NewAnonymousCredential(), azblob.PipelineOptions{}))
191
192	gResp, err := blobURL.GetProperties(ctx, azblob.BlobAccessConditions{})
193	c.Assert(err, chk.IsNil)
194	c.Assert(gResp.CacheControl(), chk.Equals, cacheControlVal)
195	c.Assert(gResp.ContentDisposition(), chk.Equals, contentDispositionVal)
196	c.Assert(gResp.ContentEncoding(), chk.Equals, contentEncodingVal)
197	c.Assert(gResp.ContentLanguage(), chk.Equals, contentLanguageVal)
198	c.Assert(gResp.ContentType(), chk.Equals, contentTypeVal)
199}
200
201func (s *aztestsSuite) TestStageBlockWithMD5(c *chk.C) {
202	bsu := getBSU()
203	container, _ := createNewContainer(c, bsu)
204	defer delContainer(c, container)
205
206	blob := container.NewBlockBlobURL(generateBlobName())
207	blockID := base64.StdEncoding.EncodeToString([]byte(fmt.Sprintf("%6d", 0)))
208
209	// test put block with valid MD5 value
210	readerToBody, body := getRandomDataAndReader(1024)
211	md5Value := md5.Sum(body)
212	putResp, err := blob.StageBlock(context.Background(), blockID, readerToBody, azblob.LeaseAccessConditions{}, md5Value[:])
213	c.Assert(err, chk.IsNil)
214	c.Assert(putResp.Response().StatusCode, chk.Equals, 201)
215	c.Assert(putResp.ContentMD5(), chk.DeepEquals, md5Value[:])
216	c.Assert(putResp.RequestID(), chk.Not(chk.Equals), "")
217	c.Assert(putResp.Version(), chk.Not(chk.Equals), "")
218	c.Assert(putResp.Date().IsZero(), chk.Equals, false)
219
220	// test put block with bad MD5 value
221	readerToBody, body = getRandomDataAndReader(1024)
222	_, badMD5 := getRandomDataAndReader(16)
223	putResp, err = blob.StageBlock(context.Background(), blockID, readerToBody, azblob.LeaseAccessConditions{}, badMD5[:])
224	validateStorageError(c, err, azblob.ServiceCodeMd5Mismatch)
225}
226
227func (s *aztestsSuite) TestBlobPutBlobNonEmptyBody(c *chk.C) {
228	bsu := getBSU()
229	containerURL, _ := createNewContainer(c, bsu)
230	defer deleteContainer(c, containerURL)
231	blobURL, _ := getBlockBlobURL(c, containerURL)
232
233	_, err := blobURL.Upload(ctx, strings.NewReader(blockBlobDefaultData), azblob.BlobHTTPHeaders{}, nil, azblob.BlobAccessConditions{})
234	c.Assert(err, chk.IsNil)
235
236	resp, err := blobURL.Download(ctx, 0, 0, azblob.BlobAccessConditions{}, false)
237	c.Assert(err, chk.IsNil)
238	data, err := ioutil.ReadAll(resp.Response().Body)
239	c.Assert(string(data), chk.Equals, blockBlobDefaultData)
240}
241
242func (s *aztestsSuite) TestBlobPutBlobHTTPHeaders(c *chk.C) {
243	bsu := getBSU()
244	containerURL, _ := createNewContainer(c, bsu)
245	defer deleteContainer(c, containerURL)
246	blobURL, _ := getBlockBlobURL(c, containerURL)
247
248	_, err := blobURL.Upload(ctx, bytes.NewReader(nil), basicHeaders, nil, azblob.BlobAccessConditions{})
249	c.Assert(err, chk.IsNil)
250
251	resp, err := blobURL.GetProperties(ctx, azblob.BlobAccessConditions{})
252	c.Assert(err, chk.IsNil)
253	h := resp.NewHTTPHeaders()
254	h.ContentMD5 = nil // the service generates a MD5 value, omit before comparing
255	c.Assert(h, chk.DeepEquals, basicHeaders)
256}
257
258func (s *aztestsSuite) TestBlobPutBlobMetadataNotEmpty(c *chk.C) {
259	bsu := getBSU()
260	containerURL, _ := createNewContainer(c, bsu)
261	defer deleteContainer(c, containerURL)
262	blobURL, _ := getBlockBlobURL(c, containerURL)
263
264	_, err := blobURL.Upload(ctx, bytes.NewReader(nil), azblob.BlobHTTPHeaders{}, basicMetadata, azblob.BlobAccessConditions{})
265	c.Assert(err, chk.IsNil)
266
267	resp, err := blobURL.GetProperties(ctx, azblob.BlobAccessConditions{})
268	c.Assert(err, chk.IsNil)
269	c.Assert(resp.NewMetadata(), chk.DeepEquals, basicMetadata)
270}
271
272func (s *aztestsSuite) TestBlobPutBlobMetadataEmpty(c *chk.C) {
273	bsu := getBSU()
274	containerURL, _ := createNewContainer(c, bsu)
275	defer deleteContainer(c, containerURL)
276	blobURL, _ := getBlockBlobURL(c, containerURL)
277
278	_, err := blobURL.Upload(ctx, bytes.NewReader(nil), azblob.BlobHTTPHeaders{}, azblob.Metadata{}, azblob.BlobAccessConditions{})
279	c.Assert(err, chk.IsNil)
280
281	resp, err := blobURL.GetProperties(ctx, azblob.BlobAccessConditions{})
282	c.Assert(err, chk.IsNil)
283	c.Assert(resp.NewMetadata(), chk.HasLen, 0)
284}
285
286func (s *aztestsSuite) TestBlobPutBlobMetadataInvalid(c *chk.C) {
287	bsu := getBSU()
288	containerURL, _ := createNewContainer(c, bsu)
289	defer deleteContainer(c, containerURL)
290	blobURL, _ := getBlockBlobURL(c, containerURL)
291
292	_, err := blobURL.Upload(ctx, nil, azblob.BlobHTTPHeaders{}, azblob.Metadata{"In valid!": "bar"}, azblob.BlobAccessConditions{})
293	c.Assert(strings.Contains(err.Error(), validationErrorSubstring), chk.Equals, true)
294}
295
296func (s *aztestsSuite) TestBlobPutBlobIfModifiedSinceTrue(c *chk.C) {
297	bsu := getBSU()
298	containerURL, _ := createNewContainer(c, bsu)
299	defer deleteContainer(c, containerURL)
300	blobURL, _ := createNewBlockBlob(c, containerURL)
301
302	currentTime := getRelativeTimeGMT(-10)
303
304	_, err := blobURL.Upload(ctx, bytes.NewReader(nil), azblob.BlobHTTPHeaders{}, nil,
305		azblob.BlobAccessConditions{ModifiedAccessConditions: azblob.ModifiedAccessConditions{IfModifiedSince: currentTime}})
306	c.Assert(err, chk.IsNil)
307
308	validateUpload(c, blobURL)
309}
310
311func (s *aztestsSuite) TestBlobPutBlobIfModifiedSinceFalse(c *chk.C) {
312	bsu := getBSU()
313	containerURL, _ := createNewContainer(c, bsu)
314	defer deleteContainer(c, containerURL)
315	blobURL, _ := createNewBlockBlob(c, containerURL)
316
317	currentTime := getRelativeTimeGMT(10)
318
319	_, err := blobURL.Upload(ctx, bytes.NewReader(nil), azblob.BlobHTTPHeaders{}, nil,
320		azblob.BlobAccessConditions{ModifiedAccessConditions: azblob.ModifiedAccessConditions{IfModifiedSince: currentTime}})
321	validateStorageError(c, err, azblob.ServiceCodeConditionNotMet)
322}
323
324func (s *aztestsSuite) TestBlobPutBlobIfUnmodifiedSinceTrue(c *chk.C) {
325	bsu := getBSU()
326	containerURL, _ := createNewContainer(c, bsu)
327	defer deleteContainer(c, containerURL)
328	blobURL, _ := createNewBlockBlob(c, containerURL)
329
330	currentTime := getRelativeTimeGMT(10)
331
332	_, err := blobURL.Upload(ctx, bytes.NewReader(nil), azblob.BlobHTTPHeaders{}, nil,
333		azblob.BlobAccessConditions{ModifiedAccessConditions: azblob.ModifiedAccessConditions{IfUnmodifiedSince: currentTime}})
334	c.Assert(err, chk.IsNil)
335
336	validateUpload(c, blobURL)
337}
338
339func (s *aztestsSuite) TestBlobPutBlobIfUnmodifiedSinceFalse(c *chk.C) {
340	bsu := getBSU()
341	containerURL, _ := createNewContainer(c, bsu)
342	defer deleteContainer(c, containerURL)
343	blobURL, _ := createNewBlockBlob(c, containerURL)
344
345	currentTime := getRelativeTimeGMT(-10)
346
347	_, err := blobURL.Upload(ctx, bytes.NewReader(nil), azblob.BlobHTTPHeaders{}, nil,
348		azblob.BlobAccessConditions{ModifiedAccessConditions: azblob.ModifiedAccessConditions{IfUnmodifiedSince: currentTime}})
349	validateStorageError(c, err, azblob.ServiceCodeConditionNotMet)
350}
351
352func (s *aztestsSuite) TestBlobPutBlobIfMatchTrue(c *chk.C) {
353	bsu := getBSU()
354	containerURL, _ := createNewContainer(c, bsu)
355	defer deleteContainer(c, containerURL)
356	blobURL, _ := createNewBlockBlob(c, containerURL)
357
358	resp, err := blobURL.GetProperties(ctx, azblob.BlobAccessConditions{})
359	c.Assert(err, chk.IsNil)
360
361	_, err = blobURL.Upload(ctx, bytes.NewReader(nil), azblob.BlobHTTPHeaders{}, nil,
362		azblob.BlobAccessConditions{ModifiedAccessConditions: azblob.ModifiedAccessConditions{IfMatch: resp.ETag()}})
363	c.Assert(err, chk.IsNil)
364
365	validateUpload(c, blobURL)
366}
367
368func (s *aztestsSuite) TestBlobPutBlobIfMatchFalse(c *chk.C) {
369	bsu := getBSU()
370	containerURL, _ := createNewContainer(c, bsu)
371	defer deleteContainer(c, containerURL)
372	blobURL, _ := createNewBlockBlob(c, containerURL)
373
374	_, err := blobURL.GetProperties(ctx, azblob.BlobAccessConditions{})
375	c.Assert(err, chk.IsNil)
376
377	_, err = blobURL.Upload(ctx, bytes.NewReader(nil), azblob.BlobHTTPHeaders{}, nil,
378		azblob.BlobAccessConditions{ModifiedAccessConditions: azblob.ModifiedAccessConditions{IfMatch: azblob.ETag("garbage")}})
379	validateStorageError(c, err, azblob.ServiceCodeConditionNotMet)
380}
381
382func (s *aztestsSuite) TestBlobPutBlobIfNoneMatchTrue(c *chk.C) {
383	bsu := getBSU()
384	containerURL, _ := createNewContainer(c, bsu)
385	defer deleteContainer(c, containerURL)
386	blobURL, _ := createNewBlockBlob(c, containerURL)
387
388	_, err := blobURL.GetProperties(ctx, azblob.BlobAccessConditions{})
389	c.Assert(err, chk.IsNil)
390
391	_, err = blobURL.Upload(ctx, bytes.NewReader(nil), azblob.BlobHTTPHeaders{}, nil,
392		azblob.BlobAccessConditions{ModifiedAccessConditions: azblob.ModifiedAccessConditions{IfNoneMatch: azblob.ETag("garbage")}})
393	c.Assert(err, chk.IsNil)
394
395	validateUpload(c, blobURL)
396}
397
398func (s *aztestsSuite) TestBlobPutBlobIfNoneMatchFalse(c *chk.C) {
399	bsu := getBSU()
400	containerURL, _ := createNewContainer(c, bsu)
401	defer deleteContainer(c, containerURL)
402	blobURL, _ := createNewBlockBlob(c, containerURL)
403
404	resp, err := blobURL.GetProperties(ctx, azblob.BlobAccessConditions{})
405	c.Assert(err, chk.IsNil)
406
407	_, err = blobURL.Upload(ctx, bytes.NewReader(nil), azblob.BlobHTTPHeaders{}, nil,
408		azblob.BlobAccessConditions{ModifiedAccessConditions: azblob.ModifiedAccessConditions{IfNoneMatch: resp.ETag()}})
409	validateStorageError(c, err, azblob.ServiceCodeConditionNotMet)
410}
411
412func (s *aztestsSuite) TestBlobGetBlockListNone(c *chk.C) {
413	bsu := getBSU()
414	containerURL, _ := createNewContainer(c, bsu)
415	defer deleteContainer(c, containerURL)
416	blobURL, _ := getBlockBlobURL(c, containerURL)
417
418	_, err := blobURL.StageBlock(ctx, azblob.BlockID{0}.ToBase64(), strings.NewReader(blockBlobDefaultData), azblob.LeaseAccessConditions{}, nil)
419	c.Assert(err, chk.IsNil)
420
421	resp, err := blobURL.GetBlockList(ctx, azblob.BlockListNone, azblob.LeaseAccessConditions{})
422	c.Assert(err, chk.IsNil)
423	c.Assert(resp.CommittedBlocks, chk.HasLen, 0)
424	c.Assert(resp.UncommittedBlocks, chk.HasLen, 0) // Not specifying a block list type should default to only returning committed blocks
425}
426
427func (s *aztestsSuite) TestBlobGetBlockListUncommitted(c *chk.C) {
428	bsu := getBSU()
429	containerURL, _ := createNewContainer(c, bsu)
430	defer deleteContainer(c, containerURL)
431	blobURL, _ := getBlockBlobURL(c, containerURL)
432
433	_, err := blobURL.StageBlock(ctx, azblob.BlockID{0}.ToBase64(), strings.NewReader(blockBlobDefaultData), azblob.LeaseAccessConditions{}, nil)
434	c.Assert(err, chk.IsNil)
435
436	resp, err := blobURL.GetBlockList(ctx, azblob.BlockListUncommitted, azblob.LeaseAccessConditions{})
437	c.Assert(err, chk.IsNil)
438	c.Assert(resp.CommittedBlocks, chk.HasLen, 0)
439	c.Assert(resp.UncommittedBlocks, chk.HasLen, 1)
440}
441
442func (s *aztestsSuite) TestBlobGetBlockListCommitted(c *chk.C) {
443	bsu := getBSU()
444	containerURL, _ := createNewContainer(c, bsu)
445	defer deleteContainer(c, containerURL)
446	blobURL, _ := getBlockBlobURL(c, containerURL)
447
448	_, err := blobURL.StageBlock(ctx, azblob.BlockID{0}.ToBase64(), strings.NewReader(blockBlobDefaultData), azblob.LeaseAccessConditions{}, nil)
449	c.Assert(err, chk.IsNil)
450
451	_, err = blobURL.CommitBlockList(ctx, []string{azblob.BlockID{0}.ToBase64()}, azblob.BlobHTTPHeaders{}, nil, azblob.BlobAccessConditions{})
452
453	resp, err := blobURL.GetBlockList(ctx, azblob.BlockListCommitted, azblob.LeaseAccessConditions{})
454	c.Assert(err, chk.IsNil)
455	c.Assert(resp.CommittedBlocks, chk.HasLen, 1)
456	c.Assert(resp.UncommittedBlocks, chk.HasLen, 0)
457}
458
459func (s *aztestsSuite) TestBlobGetBlockListCommittedEmpty(c *chk.C) {
460	bsu := getBSU()
461	containerURL, _ := createNewContainer(c, bsu)
462	defer deleteContainer(c, containerURL)
463	blobURL, _ := getBlockBlobURL(c, containerURL)
464
465	_, err := blobURL.StageBlock(ctx, azblob.BlockID{0}.ToBase64(), strings.NewReader(blockBlobDefaultData), azblob.LeaseAccessConditions{}, nil)
466	c.Assert(err, chk.IsNil)
467
468	resp, err := blobURL.GetBlockList(ctx, azblob.BlockListCommitted, azblob.LeaseAccessConditions{})
469	c.Assert(err, chk.IsNil)
470	c.Assert(resp.CommittedBlocks, chk.HasLen, 0)
471	c.Assert(resp.UncommittedBlocks, chk.HasLen, 0)
472}
473
474func (s *aztestsSuite) TestBlobGetBlockListBothEmpty(c *chk.C) {
475	bsu := getBSU()
476	containerURL, _ := createNewContainer(c, bsu)
477	defer deleteContainer(c, containerURL)
478	blobURL, _ := getBlockBlobURL(c, containerURL)
479
480	_, err := blobURL.GetBlockList(ctx, azblob.BlockListAll, azblob.LeaseAccessConditions{})
481	validateStorageError(c, err, azblob.ServiceCodeBlobNotFound)
482}
483
484func (s *aztestsSuite) TestBlobGetBlockListBothNotEmpty(c *chk.C) {
485	bsu := getBSU()
486	containerURL, _ := createNewContainer(c, bsu)
487	defer deleteContainer(c, containerURL)
488	blobURL, _ := getBlockBlobURL(c, containerURL)
489
490	// Put and commit two blocks
491	_, err := blobURL.StageBlock(ctx, azblob.BlockID{0}.ToBase64(), strings.NewReader(blockBlobDefaultData), azblob.LeaseAccessConditions{}, nil)
492	c.Assert(err, chk.IsNil)
493	_, err = blobURL.StageBlock(ctx, azblob.BlockID{1}.ToBase64(), strings.NewReader(blockBlobDefaultData), azblob.LeaseAccessConditions{}, nil)
494	c.Assert(err, chk.IsNil)
495	_, err = blobURL.CommitBlockList(ctx, []string{azblob.BlockID{1}.ToBase64(), azblob.BlockID{0}.ToBase64()}, azblob.BlobHTTPHeaders{}, nil, azblob.BlobAccessConditions{})
496	c.Assert(err, chk.IsNil)
497
498	// Put two uncommitted blocks
499	_, err = blobURL.StageBlock(ctx, azblob.BlockID{3}.ToBase64(), strings.NewReader(blockBlobDefaultData), azblob.LeaseAccessConditions{}, nil)
500	c.Assert(err, chk.IsNil)
501	_, err = blobURL.StageBlock(ctx, azblob.BlockID{2}.ToBase64(), strings.NewReader(blockBlobDefaultData), azblob.LeaseAccessConditions{}, nil)
502	c.Assert(err, chk.IsNil)
503
504	resp, err := blobURL.GetBlockList(ctx, azblob.BlockListAll, azblob.LeaseAccessConditions{})
505	c.Assert(err, chk.IsNil)
506	c.Assert(resp.CommittedBlocks[0].Name, chk.Equals, azblob.BlockID{1}.ToBase64())
507	c.Assert(resp.CommittedBlocks[1].Name, chk.Equals, azblob.BlockID{0}.ToBase64())   // Committed blocks are returned in the order they are committed (in the commit list)
508	c.Assert(resp.UncommittedBlocks[0].Name, chk.Equals, azblob.BlockID{2}.ToBase64()) // Uncommitted blocks are returned in alphabetical order
509	c.Assert(resp.UncommittedBlocks[1].Name, chk.Equals, azblob.BlockID{3}.ToBase64())
510}
511
512func (s *aztestsSuite) TestBlobGetBlockListInvalidType(c *chk.C) {
513	bsu := getBSU()
514	containerURL, _ := createNewContainer(c, bsu)
515	defer deleteContainer(c, containerURL)
516	blobURL, _ := getBlockBlobURL(c, containerURL)
517
518	_, err := blobURL.StageBlock(ctx, azblob.BlockID{0}.ToBase64(), strings.NewReader(blockBlobDefaultData), azblob.LeaseAccessConditions{}, nil)
519	c.Assert(err, chk.IsNil)
520
521	_, err = blobURL.GetBlockList(ctx, azblob.BlockListType("garbage"), azblob.LeaseAccessConditions{})
522	validateStorageError(c, err, azblob.ServiceCodeInvalidQueryParameterValue)
523}
524
525func (s *aztestsSuite) TestBlobGetBlockListSnapshot(c *chk.C) {
526	bsu := getBSU()
527	containerURL, _ := createNewContainer(c, bsu)
528	defer deleteContainer(c, containerURL)
529	blobURL, _ := getBlockBlobURL(c, containerURL)
530
531	_, err := blobURL.StageBlock(ctx, azblob.BlockID{0}.ToBase64(), strings.NewReader(blockBlobDefaultData), azblob.LeaseAccessConditions{}, nil)
532	c.Assert(err, chk.IsNil)
533	_, err = blobURL.CommitBlockList(ctx, []string{azblob.BlockID{0}.ToBase64()}, azblob.BlobHTTPHeaders{}, nil, azblob.BlobAccessConditions{})
534	c.Assert(err, chk.IsNil)
535
536	resp, err := blobURL.CreateSnapshot(ctx, nil, azblob.BlobAccessConditions{})
537	c.Assert(err, chk.IsNil)
538	snapshotURL := blobURL.WithSnapshot(resp.Snapshot())
539
540	resp2, err := snapshotURL.GetBlockList(ctx, azblob.BlockListAll, azblob.LeaseAccessConditions{})
541	c.Assert(err, chk.IsNil)
542	c.Assert(resp2.CommittedBlocks, chk.HasLen, 1)
543}
544
545func (s *aztestsSuite) TestBlobPutBlockIDInvalidCharacters(c *chk.C) {
546	bsu := getBSU()
547	containerURL, _ := createNewContainer(c, bsu)
548	defer deleteContainer(c, containerURL)
549	blobURL, _ := getBlockBlobURL(c, containerURL)
550
551	_, err := blobURL.StageBlock(ctx, "!!", strings.NewReader(blockBlobDefaultData), azblob.LeaseAccessConditions{}, nil)
552	validateStorageError(c, err, azblob.ServiceCodeInvalidQueryParameterValue)
553}
554
555func (s *aztestsSuite) TestBlobPutBlockIDInvalidLength(c *chk.C) {
556	bsu := getBSU()
557	containerURL, _ := createNewContainer(c, bsu)
558	defer deleteContainer(c, containerURL)
559	blobURL, _ := getBlockBlobURL(c, containerURL)
560
561	_, err := blobURL.StageBlock(ctx, azblob.BlockID{0}.ToBase64(), strings.NewReader(blockBlobDefaultData), azblob.LeaseAccessConditions{}, nil)
562	c.Assert(err, chk.IsNil)
563	_, err = blobURL.StageBlock(ctx, "00000000", strings.NewReader(blockBlobDefaultData), azblob.LeaseAccessConditions{}, nil)
564	validateStorageError(c, err, azblob.ServiceCodeInvalidBlobOrBlock)
565}
566
567func (s *aztestsSuite) TestBlobPutBlockEmptyBody(c *chk.C) {
568	bsu := getBSU()
569	containerURL, _ := createNewContainer(c, bsu)
570	defer deleteContainer(c, containerURL)
571	blobURL, _ := getBlockBlobURL(c, containerURL)
572
573	_, err := blobURL.StageBlock(ctx, azblob.BlockID{0}.ToBase64(), strings.NewReader(""), azblob.LeaseAccessConditions{}, nil)
574	validateStorageError(c, err, azblob.ServiceCodeInvalidHeaderValue)
575}
576
577func setupPutBlockListTest(c *chk.C) (containerURL azblob.ContainerURL, blobURL azblob.BlockBlobURL, id string) {
578	bsu := getBSU()
579	containerURL, _ = createNewContainer(c, bsu)
580	blobURL, _ = getBlockBlobURL(c, containerURL)
581	id = azblob.BlockID{0}.ToBase64()
582	_, err := blobURL.StageBlock(ctx, id, strings.NewReader(blockBlobDefaultData), azblob.LeaseAccessConditions{}, nil)
583	c.Assert(err, chk.IsNil)
584	return
585}
586
587func (s *aztestsSuite) TestBlobPutBlockListInvalidID(c *chk.C) {
588	containerURL, blobURL, id := setupPutBlockListTest(c)
589	defer deleteContainer(c, containerURL)
590
591	_, err := blobURL.CommitBlockList(ctx, []string{id[:2]}, azblob.BlobHTTPHeaders{}, nil, azblob.BlobAccessConditions{})
592	validateStorageError(c, err, azblob.ServiceCodeInvalidBlockID)
593}
594
595func (s *aztestsSuite) TestBlobPutBlockListDuplicateBlocks(c *chk.C) {
596	containerURL, blobURL, id := setupPutBlockListTest(c)
597	defer deleteContainer(c, containerURL)
598
599	_, err := blobURL.CommitBlockList(ctx, []string{id, id}, azblob.BlobHTTPHeaders{}, nil, azblob.BlobAccessConditions{})
600	c.Assert(err, chk.IsNil)
601
602	resp, err := blobURL.GetBlockList(ctx, azblob.BlockListAll, azblob.LeaseAccessConditions{})
603	c.Assert(err, chk.IsNil)
604	c.Assert(resp.CommittedBlocks, chk.HasLen, 2)
605}
606
607func (s *aztestsSuite) TestBlobPutBlockListEmptyList(c *chk.C) {
608	containerURL, blobURL, _ := setupPutBlockListTest(c)
609	defer deleteContainer(c, containerURL)
610
611	_, err := blobURL.CommitBlockList(ctx, []string{}, azblob.BlobHTTPHeaders{}, azblob.Metadata{}, azblob.BlobAccessConditions{})
612	c.Assert(err, chk.IsNil)
613
614	resp, err := blobURL.GetBlockList(ctx, azblob.BlockListAll, azblob.LeaseAccessConditions{})
615	c.Assert(err, chk.IsNil)
616	c.Assert(resp.CommittedBlocks, chk.HasLen, 0)
617}
618
619func (s *aztestsSuite) TestBlobPutBlockListMetadataEmpty(c *chk.C) {
620	containerURL, blobURL, id := setupPutBlockListTest(c)
621	defer deleteContainer(c, containerURL)
622
623	_, err := blobURL.CommitBlockList(ctx, []string{id}, azblob.BlobHTTPHeaders{}, azblob.Metadata{}, azblob.BlobAccessConditions{})
624	c.Assert(err, chk.IsNil)
625
626	resp, err := blobURL.GetProperties(ctx, azblob.BlobAccessConditions{})
627	c.Assert(err, chk.IsNil)
628	c.Assert(resp.NewMetadata(), chk.HasLen, 0)
629}
630
631func (s *aztestsSuite) TestBlobPutBlockListMetadataNonEmpty(c *chk.C) {
632	containerURL, blobURL, id := setupPutBlockListTest(c)
633	defer deleteContainer(c, containerURL)
634
635	_, err := blobURL.CommitBlockList(ctx, []string{id}, azblob.BlobHTTPHeaders{}, basicMetadata, azblob.BlobAccessConditions{})
636	c.Assert(err, chk.IsNil)
637
638	resp, err := blobURL.GetProperties(ctx, azblob.BlobAccessConditions{})
639	c.Assert(err, chk.IsNil)
640	c.Assert(resp.NewMetadata(), chk.DeepEquals, basicMetadata)
641}
642
643func (s *aztestsSuite) TestBlobPutBlockListHTTPHeaders(c *chk.C) {
644	containerURL, blobURL, id := setupPutBlockListTest(c)
645	defer deleteContainer(c, containerURL)
646
647	_, err := blobURL.CommitBlockList(ctx, []string{id}, basicHeaders, nil, azblob.BlobAccessConditions{})
648	c.Assert(err, chk.IsNil)
649
650	resp, _ := blobURL.GetProperties(ctx, azblob.BlobAccessConditions{})
651	h := resp.NewHTTPHeaders()
652	c.Assert(h, chk.DeepEquals, basicHeaders)
653}
654
655func (s *aztestsSuite) TestBlobPutBlockListHTTPHeadersEmpty(c *chk.C) {
656	containerURL, blobURL, id := setupPutBlockListTest(c)
657	defer deleteContainer(c, containerURL)
658
659	_, err := blobURL.CommitBlockList(ctx, []string{id}, azblob.BlobHTTPHeaders{ContentDisposition: "my_disposition"}, nil, azblob.BlobAccessConditions{})
660	c.Assert(err, chk.IsNil)
661
662	_, err = blobURL.CommitBlockList(ctx, []string{id}, azblob.BlobHTTPHeaders{}, nil, azblob.BlobAccessConditions{})
663	c.Assert(err, chk.IsNil)
664
665	resp, err := blobURL.GetProperties(ctx, azblob.BlobAccessConditions{})
666	c.Assert(err, chk.IsNil)
667	c.Assert(resp.ContentDisposition(), chk.Equals, "")
668}
669
670func validateBlobCommitted(c *chk.C, blobURL azblob.BlockBlobURL) {
671	resp, err := blobURL.GetBlockList(ctx, azblob.BlockListAll, azblob.LeaseAccessConditions{})
672	c.Assert(err, chk.IsNil)
673	c.Assert(resp.CommittedBlocks, chk.HasLen, 1)
674}
675
676func (s *aztestsSuite) TestBlobPutBlockListIfModifiedSinceTrue(c *chk.C) {
677	containerURL, blobURL, id := setupPutBlockListTest(c)
678	defer deleteContainer(c, containerURL)
679	_, err := blobURL.CommitBlockList(ctx, []string{id}, azblob.BlobHTTPHeaders{}, nil, azblob.BlobAccessConditions{}) // The blob must actually exist to have a modifed time
680	c.Assert(err, chk.IsNil)
681
682	currentTime := getRelativeTimeGMT(-10)
683
684	_, err = blobURL.CommitBlockList(ctx, []string{id}, azblob.BlobHTTPHeaders{}, nil,
685		azblob.BlobAccessConditions{ModifiedAccessConditions: azblob.ModifiedAccessConditions{IfModifiedSince: currentTime}})
686	c.Assert(err, chk.IsNil)
687
688	validateBlobCommitted(c, blobURL)
689}
690
691func (s *aztestsSuite) TestBlobPutBlockListIfModifiedSinceFalse(c *chk.C) {
692	containerURL, blobURL, id := setupPutBlockListTest(c)
693	defer deleteContainer(c, containerURL)
694
695	currentTime := getRelativeTimeGMT(10)
696
697	_, err := blobURL.CommitBlockList(ctx, []string{id}, azblob.BlobHTTPHeaders{}, nil,
698		azblob.BlobAccessConditions{ModifiedAccessConditions: azblob.ModifiedAccessConditions{IfModifiedSince: currentTime}})
699	validateStorageError(c, err, azblob.ServiceCodeConditionNotMet)
700}
701
702func (s *aztestsSuite) TestBlobPutBlockListIfUnmodifiedSinceTrue(c *chk.C) {
703	containerURL, blobURL, id := setupPutBlockListTest(c)
704	defer deleteContainer(c, containerURL)
705	_, err := blobURL.CommitBlockList(ctx, []string{id}, azblob.BlobHTTPHeaders{}, nil, azblob.BlobAccessConditions{}) // The blob must actually exist to have a modifed time
706	c.Assert(err, chk.IsNil)
707
708	currentTime := getRelativeTimeGMT(10)
709
710	_, err = blobURL.CommitBlockList(ctx, []string{id}, azblob.BlobHTTPHeaders{}, nil,
711		azblob.BlobAccessConditions{ModifiedAccessConditions: azblob.ModifiedAccessConditions{IfUnmodifiedSince: currentTime}})
712	c.Assert(err, chk.IsNil)
713
714	validateBlobCommitted(c, blobURL)
715}
716
717func (s *aztestsSuite) TestBlobPutBlockListIfUnmodifiedSinceFalse(c *chk.C) {
718	containerURL, blobURL, id := setupPutBlockListTest(c)
719	blobURL.CommitBlockList(ctx, []string{id}, azblob.BlobHTTPHeaders{}, nil, azblob.BlobAccessConditions{}) // The blob must actually exist to have a modifed time
720	defer deleteContainer(c, containerURL)
721
722	currentTime := getRelativeTimeGMT(-10)
723
724	_, err := blobURL.CommitBlockList(ctx, []string{id}, azblob.BlobHTTPHeaders{}, nil,
725		azblob.BlobAccessConditions{ModifiedAccessConditions: azblob.ModifiedAccessConditions{IfUnmodifiedSince: currentTime}})
726
727	validateStorageError(c, err, azblob.ServiceCodeConditionNotMet)
728}
729
730func (s *aztestsSuite) TestBlobPutBlockListIfMatchTrue(c *chk.C) {
731	containerURL, blobURL, id := setupPutBlockListTest(c)
732	defer deleteContainer(c, containerURL)
733	resp, err := blobURL.CommitBlockList(ctx, []string{id}, azblob.BlobHTTPHeaders{}, nil, azblob.BlobAccessConditions{}) // The blob must actually exist to have a modifed time
734	c.Assert(err, chk.IsNil)
735
736	_, err = blobURL.CommitBlockList(ctx, []string{id}, azblob.BlobHTTPHeaders{}, nil,
737		azblob.BlobAccessConditions{ModifiedAccessConditions: azblob.ModifiedAccessConditions{IfMatch: resp.ETag()}})
738	c.Assert(err, chk.IsNil)
739
740	validateBlobCommitted(c, blobURL)
741}
742
743func (s *aztestsSuite) TestBlobPutBlockListIfMatchFalse(c *chk.C) {
744	containerURL, blobURL, id := setupPutBlockListTest(c)
745	defer deleteContainer(c, containerURL)
746	_, err := blobURL.CommitBlockList(ctx, []string{id}, azblob.BlobHTTPHeaders{}, nil, azblob.BlobAccessConditions{}) // The blob must actually exist to have a modifed time
747	c.Assert(err, chk.IsNil)
748
749	_, err = blobURL.CommitBlockList(ctx, []string{id}, azblob.BlobHTTPHeaders{}, nil,
750		azblob.BlobAccessConditions{ModifiedAccessConditions: azblob.ModifiedAccessConditions{IfMatch: azblob.ETag("garbage")}})
751
752	validateStorageError(c, err, azblob.ServiceCodeConditionNotMet)
753}
754
755func (s *aztestsSuite) TestBlobPutBlockListIfNoneMatchTrue(c *chk.C) {
756	containerURL, blobURL, id := setupPutBlockListTest(c)
757	defer deleteContainer(c, containerURL)
758	_, err := blobURL.CommitBlockList(ctx, []string{id}, azblob.BlobHTTPHeaders{}, nil, azblob.BlobAccessConditions{}) // The blob must actually exist to have a modifed time
759	c.Assert(err, chk.IsNil)
760
761	_, err = blobURL.CommitBlockList(ctx, []string{id}, azblob.BlobHTTPHeaders{}, nil,
762		azblob.BlobAccessConditions{ModifiedAccessConditions: azblob.ModifiedAccessConditions{IfNoneMatch: azblob.ETag("garbage")}})
763	c.Assert(err, chk.IsNil)
764
765	validateBlobCommitted(c, blobURL)
766}
767
768func (s *aztestsSuite) TestBlobPutBlockListIfNoneMatchFalse(c *chk.C) {
769	containerURL, blobURL, id := setupPutBlockListTest(c)
770	defer deleteContainer(c, containerURL)
771	resp, err := blobURL.CommitBlockList(ctx, []string{id}, azblob.BlobHTTPHeaders{}, nil, azblob.BlobAccessConditions{}) // The blob must actually exist to have a modifed time
772	c.Assert(err, chk.IsNil)
773
774	_, err = blobURL.CommitBlockList(ctx, []string{id}, azblob.BlobHTTPHeaders{}, nil,
775		azblob.BlobAccessConditions{ModifiedAccessConditions: azblob.ModifiedAccessConditions{IfNoneMatch: resp.ETag()}})
776
777	validateStorageError(c, err, azblob.ServiceCodeConditionNotMet)
778}
779
780func (s *aztestsSuite) TestBlobPutBlockListValidateData(c *chk.C) {
781	containerURL, blobURL, id := setupPutBlockListTest(c)
782	defer deleteContainer(c, containerURL)
783
784	_, err := blobURL.CommitBlockList(ctx, []string{id}, azblob.BlobHTTPHeaders{}, nil, azblob.BlobAccessConditions{})
785
786	resp, err := blobURL.Download(ctx, 0, 0, azblob.BlobAccessConditions{}, false)
787	c.Assert(err, chk.IsNil)
788	data, _ := ioutil.ReadAll(resp.Response().Body)
789	c.Assert(string(data), chk.Equals, blockBlobDefaultData)
790}
791
792func (s *aztestsSuite) TestBlobPutBlockListModifyBlob(c *chk.C) {
793	containerURL, blobURL, id := setupPutBlockListTest(c)
794	defer deleteContainer(c, containerURL)
795
796	_, err := blobURL.CommitBlockList(ctx, []string{id}, azblob.BlobHTTPHeaders{}, nil, azblob.BlobAccessConditions{})
797	c.Assert(err, chk.IsNil)
798
799	_, err = blobURL.StageBlock(ctx, "0001", bytes.NewReader([]byte("new data")), azblob.LeaseAccessConditions{}, nil)
800	c.Assert(err, chk.IsNil)
801	_, err = blobURL.StageBlock(ctx, "0010", bytes.NewReader([]byte("new data")), azblob.LeaseAccessConditions{}, nil)
802	c.Assert(err, chk.IsNil)
803	_, err = blobURL.StageBlock(ctx, "0011", bytes.NewReader([]byte("new data")), azblob.LeaseAccessConditions{}, nil)
804	c.Assert(err, chk.IsNil)
805	_, err = blobURL.StageBlock(ctx, "0100", bytes.NewReader([]byte("new data")), azblob.LeaseAccessConditions{}, nil)
806	c.Assert(err, chk.IsNil)
807
808	_, err = blobURL.CommitBlockList(ctx, []string{"0001", "0011"}, azblob.BlobHTTPHeaders{}, nil, azblob.BlobAccessConditions{})
809	c.Assert(err, chk.IsNil)
810
811	resp, err := blobURL.GetBlockList(ctx, azblob.BlockListAll, azblob.LeaseAccessConditions{})
812	c.Assert(err, chk.IsNil)
813	c.Assert(resp.CommittedBlocks, chk.HasLen, 2)
814	c.Assert(resp.CommittedBlocks[0].Name, chk.Equals, "0001")
815	c.Assert(resp.CommittedBlocks[1].Name, chk.Equals, "0011")
816	c.Assert(resp.UncommittedBlocks, chk.HasLen, 0)
817}
818