UNPKG

21.1 kBJavaScriptView Raw
1const chai = require('chai'),
2 chaiHttp = require('chai-http'),
3 should = chai.should(),
4 expect = chai.expect,
5 BbPromise = require('bluebird'),
6 fs = BbPromise.promisifyAll(require("fs-extra")),
7 Azurite = require('./../lib/AzuriteBlob'),
8 rp = require('request-promise'),
9 path = require('path'),
10 xml2js = require('xml2js');
11
12chai.use(chaiHttp);
13
14const containerName = 'testcontainer';
15const blockBlobName = 'testblockblob';
16const appendBlobName = 'testappendblob';
17const pageBlobName = 'testpageblob';
18const url = `http://localhost:10000`;
19const urlPath = `/devstoreaccount1`;
20
21
22function createBlob(containerNamex, blobNamex, payload, blobType) {
23 // Make sure there is an existing container 'testcontainer'
24 const optionsContainer = {
25 method: 'PUT',
26 uri: `http://localhost:10000/devstoreaccount1/${containerNamex}?restype=container`,
27 body: ''
28 };
29 const optionsBlob = {
30 method: 'PUT',
31 headers: {
32 'x-ms-blob-type': blobType,
33 'Content-Type': 'application/octet-stream'
34 },
35 uri: `http://localhost:10000/devstoreaccount1/${containerNamex}/${blobNamex}`,
36 body: payload
37 }
38
39 return rp(optionsContainer)
40 .then(() => {
41 return rp(optionsBlob);
42 });
43}
44
45describe('Blob HTTP API', () => {
46 const azurite = new Azurite();
47
48 before(() => {
49 const location = path.join(process.env.AZURITE_LOCATION, 'BLOB');
50 return azurite.init({ l: location, silent: 'true', overwrite: 'true' })
51 .then(() => {
52 // Make sure there is an existing container 'testcontainer'
53 const optionsContainer = {
54 method: 'PUT',
55 uri: `http://localhost:10000/devstoreaccount1/${containerName}?restype=container`,
56 body: ''
57 };
58 const optionsBlockBlob = {
59 method: 'PUT',
60 headers: {
61 'x-ms-blob-type': 'BlockBlob',
62 'Content-Type': 'application/octet-stream'
63 },
64 uri: `http://localhost:10000/devstoreaccount1/${containerName}/${blockBlobName}`,
65 body: 'abc123'
66 }
67 const optionsAppendBlob = {
68 method: 'PUT',
69 headers: {
70 'x-ms-blob-type': 'AppendBlob',
71 'Content-Type': 'application/octet-stream'
72 },
73 uri: `http://localhost:10000/devstoreaccount1/${containerName}/${appendBlobName}`,
74 body: ''
75 }
76 const optionsPageBlob = {
77 method: 'PUT',
78 headers: {
79 'x-ms-blob-type': 'PageBlob',
80 'Content-Type': 'application/octet-stream'
81 },
82 uri: `http://localhost:10000/devstoreaccount1/${containerName}/${pageBlobName}`,
83 body: ''
84 }
85 return rp(optionsContainer)
86 .then(() => {
87 return rp(optionsBlockBlob);
88 })
89 .then(() => {
90 return rp(optionsAppendBlob);
91 })
92 .then(() => {
93 return rp(optionsPageBlob);
94 });
95 });
96 });
97
98 after(() => {
99 return azurite.close();
100 });
101
102 describe('PUT Block Blob', () => {
103 it('should fail to create a block due to missing container', () => {
104 return chai.request(url)
105 .put(`${urlPath}/DOESNOTEXISTS/blob`)
106 .set('x-ms-blob-type', 'BlockBlob')
107 .set('Content-Type', 'application/octet-stream')
108 .send('THIS IS CONTENT')
109 .catch((e) => {
110 e.should.have.status(404);
111 })
112 });
113 it('should fail to create a block due to wrong or unsupported blob type', () => {
114 return chai.request(url)
115 .put(`${urlPath}/${containerName}/blob`)
116 .set('x-ms-blob-type', 'NOTSUPPORTED')
117 .set('Content-Type', 'application/octet-stream')
118 .send('THIS IS CONTENT')
119 .catch((e) => {
120 e.should.have.status(400);
121 });
122 });
123 it('should create a simple block blob without meta headers', () => {
124 return chai.request(url)
125 .put(`${urlPath}/${containerName}/blob`)
126 .set('x-ms-blob-type', 'BlockBlob')
127 .set('Content-Type', 'application/octet-stream')
128 .send('abcdefghijklmn')
129 .then((res) => {
130 res.should.have.status(201);
131 });
132 });
133 });
134
135 describe('Put BlockList', () => {
136 const putBlockListBlobName = 'dir/putBlockListBlobName';
137 it('should create a block blob from a list of blocks', () => {
138 const optionsBlockBlob = {
139 method: 'PUT',
140 headers: {
141 'Content-Type': 'application/octet-stream',
142 'Content-Length': 6
143 },
144 qs: {
145 'comp': 'block',
146 'blockid': 'AAAAAA=='
147 },
148 uri: `http://localhost:10000/devstoreaccount1/${containerName}/${putBlockListBlobName}`,
149 body: 'AAAAAA'
150 }
151
152 return rp(optionsBlockBlob)
153 .then(() => {
154 optionsBlockBlob.body = 'BBBBBB';
155 optionsBlockBlob.qs.blockid = 'BBBBBB=='
156 return rp(optionsBlockBlob);
157 })
158 .then(() => {
159 optionsBlockBlob.body = 'CCCCCC';
160 optionsBlockBlob.qs.blockid = 'CCCCCC=='
161 return rp(optionsBlockBlob);
162 })
163 .then(() => {
164 optionsBlockBlob.body = 'DDDDDD';
165 optionsBlockBlob.qs.blockid = 'DDDDDD=='
166 return rp(optionsBlockBlob);
167 })
168 .then(() => {
169 const xmlBody =
170 `<!--?xml version="1.0" encoding="utf-8"?-->
171 <BlockList>
172 <Latest>AAAAAA==</Latest>
173 <Latest>CCCCCC==</Latest>
174 <Latest>AAAAAA==</Latest>
175 </BlockList>`
176 return chai.request(url)
177 .put(`${urlPath}/${containerName}/${putBlockListBlobName}`)
178 .query({ comp: 'blocklist' })
179 .send(xmlBody)
180 .then((res) => {
181 res.should.have.status(201);
182 });
183 });
184 });
185 });
186
187 describe('Delete Blob', () => {
188 it('should delete an existing Block Blob', () => {
189 return createBlob('deleteblobtest', 'blob', 'abc123', 'BlockBlob')
190 .then(() => {
191 return chai.request(url)
192 .delete(`${urlPath}/deleteblobtest/blob`);
193 })
194 .then((res) => {
195 res.should.have.status(202);
196 })
197 });
198 it('should fail when deleting a non-existant blob', () => {
199 return chai.request(url)
200 .delete(`${urlPath}/deleteblobtest/DOESNOTEXIST`)
201 .catch((e) => {
202 e.should.have.status(404);
203 });
204 });
205 it('should fail when deleting from a non-existant container', () => {
206 return chai.request(url)
207 .delete(`${urlPath}/DOESNOTEXIST/DOESNOTEXIST`)
208 .catch((e) => {
209 e.should.have.status(404);
210 });
211 });
212 });
213
214 describe('Append Blobs', () => {
215 it('should create an append blob', () => {
216 return chai.request(url)
217 .put(`${urlPath}/${containerName}/appendBlob`)
218 .set('x-ms-blob-type', 'AppendBlob')
219 .set('Content-Type', 'application/octet-stream')
220 .then((res) => {
221 res.should.have.status(201);
222 });
223 });
224 it('should append data to the append blob', () => {
225 return chai.request(url)
226 .put(`${urlPath}/${containerName}/appendBlob`)
227 .query({ comp: 'appendblock' })
228 .set('x-ms-blob-type', 'AppendBlob')
229 .set('Content-Type', 'application/octet-stream')
230 .send('abcdefghi')
231 .then((res) => {
232 res.should.have.status(201);
233 });
234 });
235 it('should fail to create an append blob with size > 0', () => {
236 return chai.request(url)
237 .put(`${urlPath}/${containerName}/appendBlob`)
238 .set('x-ms-blob-type', 'AppendBlob')
239 .set('Content-Type', 'application/octet-stream')
240 .send('abcdefg')
241 .catch((e) => {
242 e.should.have.status(409);
243 });
244 });
245 });
246
247 describe('Page Blobs', () => {
248 it('should get an empty page list from the page blob', () => {
249 return chai.request(url)
250 .get(`${urlPath}/${containerName}/${pageBlobName}`)
251 .query({ comp: 'pagelist' })
252 .then((res) => {
253 res.should.have.status(200);
254 xml2js.Parser().parseString(res.text, function(err, result) {
255 expect(result.PageList).to.not.have.any.keys('PageRange');
256 });
257 });
258 });
259 it('should write data to the page blob range [0-511]', () => {
260 const bodydata = Buffer.alloc(512)
261 return chai.request(url)
262 .put(`${urlPath}/${containerName}/${pageBlobName}`)
263 .query({ comp: 'page' })
264 .set('x-ms-page-write', 'update')
265 .set('x-ms-range', 'bytes=0-511')
266 .set('Content-Type', 'application/octet-stream')
267 .send(bodydata)
268 .then((res) => {
269 res.should.have.status(201);
270 });
271 });
272 it('should fail to write data to the page blob with an invalid range', () => {
273 const bodydata = Buffer.alloc(513)
274 return chai.request(url)
275 .put(`${urlPath}/${containerName}/${pageBlobName}`)
276 .query({ comp: 'page' })
277 .set('x-ms-page-write', 'update')
278 .set('x-ms-range', 'bytes=0-512')
279 .set('Content-Type', 'application/octet-stream')
280 .send(bodydata)
281 .catch((e) => {
282 e.should.have.status(416);
283 });
284 });
285 it('should fail to write data to the page blob with an invalid body length', () => {
286 const bodydata = Buffer.alloc(513)
287 return chai.request(url)
288 .put(`${urlPath}/${containerName}/${pageBlobName}`)
289 .query({ comp: 'page' })
290 .set('x-ms-page-write', 'update')
291 .set('x-ms-range', 'bytes=0-511')
292 .set('Content-Type', 'application/octet-stream')
293 .send(bodydata)
294 .catch((e) => {
295 e.should.have.status(400);
296 });
297 });
298 it('should get the page range [0-511] from the page blob', () => {
299 return chai.request(url)
300 .get(`${urlPath}/${containerName}/${pageBlobName}`)
301 .query({ comp: 'pagelist' })
302 .then((res) => {
303 res.should.have.status(200);
304 xml2js.Parser().parseString(res.text, function(err, result) {
305 expect(result.PageList.PageRange.length).to.equal(1);
306 expect(result.PageList.PageRange[0]).to.deep.equal({"Start":["0"],"End":["511"]});
307 });
308 });
309 });
310 it('should get the page range [0-511] from the page blob within range [0-1023]', () => {
311 return chai.request(url)
312 .get(`${urlPath}/${containerName}/${pageBlobName}`)
313 .query({ comp: 'pagelist' })
314 .set('x-ms-range', 'bytes=0-1023')
315 .then((res) => {
316 res.should.have.status(200);
317 xml2js.Parser().parseString(res.text, function(err, result) {
318 expect(result.PageList.PageRange.length).to.equal(1);
319 expect(result.PageList.PageRange[0]).to.deep.equal({"Start":["0"],"End":["511"]});
320 });
321 });
322 });
323 it('should fail to get the page list from the page blob within an invalid range', () => {
324 return chai.request(url)
325 .get(`${urlPath}/${containerName}/${pageBlobName}`)
326 .query({ comp: 'pagelist' })
327 .set('x-ms-range', 'bytes=0-1095')
328 .catch((e) => {
329 e.should.have.status(416);
330 });
331 });
332 it('should write data to the page blob range [1024-1535]', () => {
333 const bodydata = Buffer.alloc(512)
334 return chai.request(url)
335 .put(`${urlPath}/${containerName}/${pageBlobName}`)
336 .query({ comp: 'page' })
337 .set('x-ms-page-write', 'update')
338 .set('x-ms-range', 'bytes=1024-1535')
339 .set('Content-Type', 'application/octet-stream')
340 .send(bodydata)
341 .then((res) => {
342 res.should.have.status(201);
343 });
344 });
345 it('should get the page ranges [0-511],[1024-1535] from the page blob', () => {
346 return chai.request(url)
347 .get(`${urlPath}/${containerName}/${pageBlobName}`)
348 .query({ comp: 'pagelist' })
349 .then((res) => {
350 res.should.have.status(200);
351 xml2js.Parser().parseString(res.text, function(err, result) {
352 expect(result.PageList.PageRange.length).to.equal(2);
353 expect(result.PageList.PageRange[0]).to.deep.equal({"Start":["0"],"End":["511"]});
354 expect(result.PageList.PageRange[1]).to.deep.equal({"Start":["1024"],"End":["1535"]});
355 });
356 });
357 });
358 it('should write data to the page blob range [512-1023]', () => {
359 const bodydata = Buffer.alloc(512)
360 return chai.request(url)
361 .put(`${urlPath}/${containerName}/${pageBlobName}`)
362 .query({ comp: 'page' })
363 .set('x-ms-page-write', 'update')
364 .set('x-ms-range', 'bytes=512-1023')
365 .set('Content-Type', 'application/octet-stream')
366 .send(bodydata)
367 .then((res) => {
368 res.should.have.status(201);
369 });
370 });
371 it('should get the page range [0-1535] from the page blob', () => {
372 return chai.request(url)
373 .get(`${urlPath}/${containerName}/${pageBlobName}`)
374 .query({ comp: 'pagelist' })
375 .then((res) => {
376 res.should.have.status(200);
377 xml2js.Parser().parseString(res.text, function(err, result) {
378 expect(result.PageList.PageRange.length).to.equal(1);
379 expect(result.PageList.PageRange[0]).to.deep.equal({"Start":["0"],"End":["1535"]});
380 });
381 });
382 });
383 it('should clear data in the page blob range [512-1023]', () => {
384 return chai.request(url)
385 .put(`${urlPath}/${containerName}/${pageBlobName}`)
386 .query({ comp: 'page' })
387 .set('x-ms-page-write', 'clear')
388 .set('x-ms-range', 'bytes=512-1023')
389 .then((res) => {
390 res.should.have.status(201);
391 });
392 });
393 it('should get the page ranges [0-511],[1024-1535] from the page blob', () => {
394 return chai.request(url)
395 .get(`${urlPath}/${containerName}/${pageBlobName}`)
396 .query({ comp: 'pagelist' })
397 .then((res) => {
398 res.should.have.status(200);
399 xml2js.Parser().parseString(res.text, function(err, result) {
400 expect(result.PageList.PageRange.length).to.equal(2);
401 expect(result.PageList.PageRange[0]).to.deep.equal({"Start":["0"],"End":["511"]});
402 expect(result.PageList.PageRange[1]).to.deep.equal({"Start":["1024"],"End":["1535"]});
403 });
404 });
405 });
406 });
407
408 describe('GET Blob', () => {
409 it('should get the correct content of the Block Blob', () => {
410 const optionsBlockBlobGet = {
411 method: 'GET',
412 headers: {
413 'Content-Type': 'application/octet-stream'
414 },
415 uri: `http://localhost:10000/devstoreaccount1/${containerName}/${blockBlobName}`
416 }
417 return rp(optionsBlockBlobGet)
418 .then((res) => {
419 expect(res).to.be.equal('abc123');
420 });
421 });
422 it('should get the correct type of the append blob', () => {
423 return chai.request(url)
424 .get(`${urlPath}/${containerName}/${appendBlobName}`)
425 .then((res) => {
426 res.should.have.status(200);
427 res.should.have.header('x-ms-blob-type', 'AppendBlob');
428 });
429 });
430 });
431
432 describe('Blob Metadata', () => {
433 it('should update an existing blob with metadata.', () => {
434 return chai.request(url)
435 .put(`${urlPath}/${containerName}/${blockBlobName}`)
436 .query({ comp: 'metadata' })
437 .set('x-ms-meta-test1', 'value1')
438 .set('x-ms-meta-test2', 'value2')
439 .set('x-ms-meta-meta1', 'meta1Value')
440 .then((res) => {
441 res.should.have.status(200);
442 });
443 });
444 it('should get the correct metadata', () => {
445 return chai.request(url)
446 .get(`${urlPath}/${containerName}/${blockBlobName}`)
447 .query({ comp: 'metadata' })
448 .then((res) => {
449 res.should.have.status(200);
450 res.should.have.header('x-ms-meta-test1', 'value1');
451 res.should.have.header('x-ms-meta-test2', 'value2');
452 res.should.have.header('x-ms-meta-meta1', 'meta1Value');
453 res.should.have.header('Last-Modified');
454 res.should.have.header('ETag');
455 });
456 });
457 it('should fail to get metadata of a non-existant blob', () => {
458 return chai.request(url)
459 .get(`${urlPath}/${containerName}/BLOB_DOESNOTEXISTS`)
460 .query({ comp: 'metadata' })
461 .catch((e) => {
462 e.should.have.status(404);
463 });
464 });
465 it('should fail to get metadata of a blob in a non-existant container', () => {
466 return chai.request(url)
467 .get(`${urlPath}/CONTAINER_DOESNOTEXIST/BLOB_DOESNOTEXISTS`)
468 .query({ comp: 'metadata' })
469 .catch((e) => {
470 e.should.have.status(404);
471 });
472 });
473 });
474
475 describe('Blob Properties', () => {
476 it('should successfully set all system properties', () => {
477 return chai.request(url)
478 .put(`${urlPath}/${containerName}/${blockBlobName}`)
479 .set('x-ms-blob-cache-control', 'true')
480 .set('x-ms-blob-content-type', 'ContentType')
481 .set('x-ms-blob-content-md5', 'ContentMD5')
482 .set('x-ms-blob-content-encoding', 'ContentEncoding')
483 .set('x-ms-blob-content-language', 'ContentLanguage')
484 .query({ comp: 'properties' })
485 .then((res) => {
486 res.should.have.status(200);
487 });
488 });
489 it('should get all previously set system properties', () => {
490 return chai.request(url)
491 .head(`${urlPath}/${containerName}/${blockBlobName}`)
492 .then((res) => {
493 res.should.have.status(200);
494 res.should.have.header('ETag');
495 res.should.have.header('Last-Modified');
496 res.should.have.header('Content-Type', 'ContentType');
497 res.should.have.header('Content-Encoding', 'ContentEncoding');
498 res.should.have.header('Content-MD5', 'ContentMD5');
499 res.should.have.header('Content-Language', 'ContentLanguage');
500 res.should.have.header('Cache-Control', 'true');
501 res.should.have.header('x-ms-blob-type', 'BlockBlob');
502 });
503 });
504 });
505});