1 /**
  2  * @fileOverview Live Blob tests.
  3  */
  4 
  5 /**
  6  * @name test.live.blob
  7  */
  8 (function () {
  9   var assert = require('assert'),
 10     crypto = require('crypto'),
 11     fs = require('fs'),
 12     async = require('async'),
 13     utils = require("./utils"),
 14     Tests;
 15 
 16   /**
 17    * @exports Tests as test.live.blob.Tests
 18    * @namespace
 19    */
 20   Tests = {};
 21 
 22   Tests["Blob (w/ Blobs)"] = utils.createTestSetup([
 23     "blob001.txt"
 24   ], {
 25     "GET single blob (without data).": function (test, opts) {
 26       var self = this,
 27         blobName = "blob001.txt",
 28         stream = self.container.getBlob(blobName);
 29 
 30       test.expect(5);
 31       test.deepEqual(stream.readable, true);
 32       stream.on('error', utils.errHandle(test));
 33       stream.on('end', function (results) {
 34         var blob = results.blob;
 35 
 36         test.ok(blob, "Blob should not be empty.");
 37         test.deepEqual(self.container, blob.container);
 38         test.deepEqual(blob.name, blobName);
 39         test.deepEqual(stream.readable, false);
 40         test.done();
 41       });
 42       stream.end();
 43     },
 44     "GET single blob (with data).": function (test, opts) {
 45       var self = this,
 46         blobName = "blob001.txt",
 47         buf = [],
 48         stream = self.container.getBlob(blobName, {
 49           encoding: 'utf8'
 50         });
 51 
 52       // Should only have one '``data``' event.
 53       test.expect(7);
 54       test.deepEqual(stream.readable, true);
 55       stream.on('error', utils.errHandle(test));
 56       stream.on('data', function (chunk) {
 57         test.deepEqual(stream.readable, true);
 58         buf.push(chunk);
 59       });
 60       stream.on('end', function (results, meta) {
 61         var blob = results.blob;
 62 
 63         // Check call and object.
 64         test.ok(blob, "Blob should not be empty.");
 65         test.deepEqual(self.container, blob.container);
 66         test.deepEqual(blob.name, blobName);
 67 
 68         // Check data returned to (equal to blob name).
 69         test.deepEqual(buf.join(''), blobName);
 70         test.deepEqual(stream.readable, false);
 71         test.done();
 72       });
 73       stream.end();
 74     },
 75     "GET blob to file (WriteStream) with pipe().": function (test, opts) {
 76       var self = this,
 77         blobName = "blob001.txt",
 78         tmpFilePath = "/tmp/sunnyjs-livetest-" + utils.getUuid() + ".txt";
 79 
 80       test.expect(8);
 81       async.series([
 82         // GET blob into a /tmp/ file with stream.
 83         function (callback) {
 84           var writeStream,
 85             getStream;
 86 
 87           // Set up streams.
 88           writeStream = fs.createWriteStream(tmpFilePath, { encoding: "utf8" });
 89           getStream = self.container.getBlob(blobName, { encoding: "utf8" });
 90 
 91           // Handlers.
 92           getStream.on('error', utils.errHandle(test));
 93           getStream.on('end', function (results, meta) {
 94             var blob = results.blob;
 95 
 96             // Check blob object.
 97             test.ok(blob, "Blob should not be empty.");
 98             test.deepEqual(blob.name, blobName);
 99             test.deepEqual(self.container, blob.container);
100           });
101 
102           writeStream.on('error', utils.errHandle(test));
103           writeStream.on('pipe', function () {
104             test.ok(true, "Should get pipe event.");
105           });
106           writeStream.on('close', function () {
107             test.ok(true, "Should get close event.");
108 
109             callback(null);
110           });
111 
112           // Pipe.
113           getStream.pipe(writeStream);
114           getStream.end();
115         },
116         // Check temp file data.
117         function (callback) {
118           fs.readFile(tmpFilePath, "utf8", function (err, data) {
119             test.ok(!err, "Should not have error.");
120 
121             // Blob name **is** the file string as well.
122             test.deepEqual(blobName, data);
123 
124             callback(null);
125           });
126         }
127       ], function (err) {
128         test.ok(!err, "Should not have an error.");
129         test.done();
130       });
131     },
132     "GET blob to new PUT, then GET that.": function (test, opts) {
133       var self = this,
134         cont = self.container,
135         blobNameSrc = "blob001.txt",
136         blobNameDest = "blob001-copied.txt";
137 
138       test.expect(12);
139       async.series([
140         // GET blob into a /tmp/ file with stream.
141         function (callback) {
142           var putStream,
143             getStream;
144 
145           // Set up streams.
146           getStream = cont.getBlob(blobNameSrc, { encoding: "utf8" });
147           putStream = cont.putBlob(blobNameDest, { encoding: "utf8" });
148 
149           // GET Handlers.
150           getStream.on('error', utils.errHandle(test));
151           getStream.on('end', function (results, meta) {
152             var blob = results.blob;
153 
154             // Check blob object.
155             test.ok(blob, "Blob should not be empty.");
156             test.deepEqual(blob.name, blobNameSrc);
157             test.deepEqual(self.container, blob.container);
158 
159             // Not done: wait for putStream:end event.
160           });
161 
162           putStream.on('error', utils.errHandle(test));
163           putStream.on('pipe', function () {
164             test.ok(true, "Should get pipe event.");
165           });
166           putStream.on('end', function (results, meta) {
167             var blob = results.blob;
168 
169             // Check blob object.
170             test.ok(blob, "Blob should not be empty.");
171             test.deepEqual(blob.name, blobNameDest);
172             test.deepEqual(self.container, blob.container);
173 
174             // The PUT finishing is our completion event.
175             callback(null);
176           });
177 
178           // Pipe.
179           getStream.pipe(putStream);
180           getStream.end();
181         },
182         // GET new blob with data.
183         function (callback) {
184           var buf = [],
185             stream = self.container.getBlob(blobNameDest, {
186               encoding: 'utf8'
187             });
188 
189           stream.on('error', utils.errHandle(test));
190           stream.on('data', function (chunk) {
191             buf.push(chunk);
192           });
193           stream.on('end', function (results, meta) {
194             var blob = results.blob,
195               token;
196 
197             // Check blob object.
198             test.ok(blob, "Blob should not be empty.");
199             test.deepEqual(blob.name, blobNameDest);
200             test.deepEqual(self.container, blob.container);
201 
202             // Check data: The source blob's name.
203             test.deepEqual(blobNameSrc, buf.join(''));
204 
205             callback(null);
206           });
207           stream.end();
208         }
209       ], function (err) {
210         test.ok(!err, "Should not have an error.");
211         test.done();
212       });
213     },
214     "GET to temp file with getToFile.": function (test, opts) {
215       var self = this,
216         blobName = "blob001.txt",
217         tmpFilePath = "/tmp/sunnyjs-livetest-" + utils.getUuid() + ".txt";
218 
219       test.expect(6);
220       async.series([
221         // GET blob into a /tmp/ file.
222         function (callback) {
223           var request = self.container.getBlobToFile(blobName, tmpFilePath);
224           request.on('error', utils.errHandle(test));
225           request.on('end', function (results, meta) {
226             var blob = results.blob;
227 
228             // Check blob object.
229             test.ok(blob, "Blob should not be empty.");
230             test.deepEqual(blob.name, blobName);
231             test.deepEqual(self.container, blob.container);
232 
233             callback(null);
234           });
235           request.end();
236         },
237         // Check temp file data.
238         function (callback) {
239           fs.readFile(tmpFilePath, "utf8", function (err, data) {
240             test.ok(!err, "Should not have error.");
241 
242             // Blob name **is** the file string as well.
243             test.deepEqual(blobName, data);
244 
245             callback(null);
246           });
247         }
248       ], function (err) {
249         test.ok(!err, "Should not have an error.");
250         test.done();
251       });
252     },
253     "GET non-existent file with getToFile.": function (test, opts) {
254       var self = this,
255         blobName = "blob001.txt",
256         filePath = "/this/path/really/shouldnt/exist.txt",
257         request;
258 
259       request = self.container.getBlobToFile(blobName, filePath);
260 
261       test.expect(2);
262       request.on('error', function (err) {
263         // Local file error.
264         test.ok(err, "Should have error.");
265         test.deepEqual(err.code, "ENOENT");
266         test.done();
267       });
268       request.on('end', function (results) {
269         test.ok(false, "Should not have completion. Got: " + results);
270         test.done();
271       });
272       request.end();
273     }
274   });
275 
276   Tests["Blob (w/ Cont)"] = utils.createTestSetup(null, {
277     // FUTURE: Invalid-named blobs.
278     // > The name for a key is a sequence of Unicode characters whose UTF-8
279     // > encoding is at most 1024 bytes long.
280     //"TODO: GET invalid-named blob.": function (test, opts) {
281     //},
282     //"TODO: DELETE invalid-named blob.": function (test, opts) {
283     //},
284 
285     "GET non-existent blob.": function (test, opts) {
286       var self = this,
287         blobName = "this/blob/doesnt/exist",
288         stream = self.container.getBlob(blobName);
289 
290       test.expect(2);
291       stream.on('error', function (err) {
292         test.deepEqual(err.statusCode, 404);
293         test.deepEqual(err.isNotFound(), true);
294         test.done();
295       });
296       stream.on('data', function () {
297         test.ok(false, "Should not have data event.");
298         test.done();
299       });
300       stream.on('end', function (results) {
301         test.ok(false, "Should not have completion. Got: " + results);
302         test.done();
303       });
304       stream.end();
305     },
306     // Note: Duplicate near-in-time operations on same non-existent
307     // blob could lead to: OperationAborted...
308     "DELETE non-existent blob.": function (test, opts) {
309       var self = this,
310         blobName = "this/blob/doesnt/exist/either",
311         request = self.container.delBlob(blobName);
312 
313       test.expect(3);
314       request.on('error', utils.errHandle(test));
315       request.on('end', function (results) {
316         var blob = results.blob,
317           notFound = results.notFound;
318 
319         test.ok(blob, "Blob should not be empty.");
320         test.deepEqual(blob.name, blobName);
321 
322         // AWS doesn't not error on return error.
323         if (opts.config.isAws()) {
324           test.ok(true, "Place holder for expect() count.");
325         } else if (opts.config.isGoogle()) {
326           test.deepEqual(notFound, true);
327         }
328 
329         test.done();
330       });
331       request.end();
332     },
333     "PUT blob with utf8 (Google)": function (test, opts) {
334       // Only test on Google.
335       if (!opts.config.isGoogle()) {
336         return test.done();
337       }
338 
339       var self = this,
340         blobName = "my_test_blob/with_unicode/aquí.txt",
341         request = self.container.putBlob(blobName);
342 
343       test.expect(1);
344       request.on('error', function (err) {
345         test.ok(err.message.indexOf("Google requires ascii path:") > -1);
346         test.done();
347       });
348       request.on('end', function (results) {
349         test.ok(false, "Should not have completion. Got: " + results);
350         test.done();
351       });
352       request.end();
353     },
354     "PUT blob with utf8, md5 and metadata, and GET": function (test, opts) {
355       var self = this,
356         blobName = "my_test_blob/with_delim/file_md5_meta",
357         blobData = "This is my data string.",
358         blobMd5 = crypto.createHash('md5').update(blobData).digest("base64"),
359         metadata = {
360           'baz': "My baz metadata.",
361           'bar': 44
362         };
363 
364       // Only add utf8 on non-Google.
365       if (!opts.config.isGoogle) {
366         blobName += "_aquí.txt";
367       } else {
368         blobName += ".txt";
369       }
370 
371       test.expect(33);
372       async.series([
373         // PUT blob with md5.
374         function (callback) {
375           var stream = self.container.putBlob(blobName, {
376             encoding: "utf8",
377             metadata: metadata,
378             headers: {
379               'content-md5': blobMd5
380             }
381           });
382 
383           test.deepEqual(stream.writable, true);
384           stream.on('error', utils.errHandle(test));
385           stream.on('end', function (results, meta) {
386             var blob = results.blob;
387 
388             test.ok(blob, "Blob should not be empty.");
389             test.deepEqual(blob.name, blobName);
390             test.deepEqual(self.container, blob.container);
391             test.deepEqual(stream.writable, false);
392 
393             // Check meta.
394             test.ok(meta.headers);
395             test.ok(meta.headers['date']);
396             test.ok(meta.headers['server']);
397             test.ok(meta.headers['etag']);
398             test.ok(meta.cloudHeaders);
399             test.ok(meta.metadata);
400             if (opts.config.isAws()) {
401               test.deepEqual(meta.headers['server'], "AmazonS3");
402               test.ok(meta.cloudHeaders['request-id']);
403             } else if (opts.config.isGoogle()) {
404               test.ok(meta.headers['server']);
405               test.ok(meta.headers['expires']);
406             }
407 
408             callback(null);
409           });
410 
411           stream.end(blobData);
412           test.deepEqual(stream.writable, false);
413         },
414         // GET blob with data and metadata.
415         function (callback) {
416           var buf = [],
417             stream = self.container.getBlob(blobName, {
418               encoding: 'utf8'
419             });
420 
421           test.deepEqual(stream.readable, true);
422           stream.on('error', utils.errHandle(test));
423           stream.on('data', function (chunk) {
424             test.deepEqual(stream.readable, true);
425             buf.push(chunk);
426           });
427           stream.on('end', function (results, meta) {
428             var blob = results.blob;
429 
430             // Check blob object.
431             test.ok(blob, "Blob should not be empty.");
432             test.deepEqual(blob.name, blobName);
433             test.deepEqual(self.container, blob.container);
434 
435             // Check data.
436             test.deepEqual(buf.join(''), blobData);
437             test.deepEqual(stream.readable, false);
438 
439             // Check meta.
440             test.ok(meta.headers);
441             test.ok(meta.headers['date']);
442             test.ok(meta.headers['last-modified']);
443             test.ok(meta.headers['server']);
444             test.ok(meta.headers['etag']);
445             test.ok(meta.cloudHeaders);
446             test.ok(meta.metadata);
447             if (opts.config.isAws()) {
448               test.deepEqual(meta.headers['server'], "AmazonS3");
449               test.ok(meta.cloudHeaders['request-id']);
450             } else if (opts.config.isGoogle()) {
451               test.ok(meta.headers['server']);
452               test.ok(meta.headers['expires']);
453             }
454 
455             // Check custom metadata.
456             // Note: Everything is a string (hence, bar conversion).
457             test.deepEqual(metadata['foo'], meta.metadata['foo']);
458             test.deepEqual(metadata['bar'].toString(), meta.metadata['bar']);
459 
460             callback(null);
461           });
462           stream.end();
463         }
464       ], function (err) {
465         test.ok(!err, "Should not have an error.");
466         test.done();
467       });
468     },
469     "PUT blob (multi-data, meta), GET, and HEAD.": function (test, opts) {
470       var self = this,
471         blobName = "my_test_blob/with_delim/file.txt",
472         blobData = ["Hi ", "There", "!"],
473         metadata = {
474           'foo': "My foo metadata.",
475           'bar': 42
476         };
477 
478       test.expect(47);
479       async.series([
480         // PUT blob with multi-write data.
481         function (callback) {
482           var stream = self.container.putBlob(blobName, {
483             metadata: metadata
484           });
485 
486           test.deepEqual(stream.writable, true);
487           stream.on('error', utils.errHandle(test));
488           stream.on('end', function (results, meta) {
489             var blob = results.blob;
490 
491             test.ok(blob, "Blob should not be empty.");
492             test.deepEqual(blob.name, blobName);
493             test.deepEqual(self.container, blob.container);
494             test.deepEqual(stream.writable, false);
495 
496             // Check meta.
497             test.ok(meta.headers);
498             test.ok(meta.headers['date']);
499             test.ok(meta.headers['server']);
500             test.ok(meta.headers['etag']);
501             test.ok(meta.cloudHeaders);
502             test.ok(meta.metadata);
503             if (opts.config.isAws()) {
504               test.deepEqual(meta.headers['server'], "AmazonS3");
505               test.ok(meta.cloudHeaders['request-id']);
506             } else if (opts.config.isGoogle()) {
507               test.ok(meta.headers['server']);
508               test.ok(meta.headers['expires']);
509             }
510 
511             callback(null);
512           });
513 
514           // Write with write(), then write(), then end().
515           stream.write(blobData[0]);
516           stream.write(blobData[1], "utf8");
517           stream.end(blobData[2]);
518           test.deepEqual(stream.writable, false);
519         },
520         // GET blob with data and metadata.
521         function (callback) {
522           var buf = [],
523             stream = self.container.getBlob(blobName, {
524               encoding: 'utf8'
525             });
526 
527           test.deepEqual(stream.readable, true);
528           stream.on('error', utils.errHandle(test));
529           stream.on('data', function (chunk) {
530             test.deepEqual(stream.readable, true);
531             buf.push(chunk);
532           });
533           stream.on('end', function (results, meta) {
534             var blob = results.blob;
535 
536             // Check blob object.
537             test.ok(blob, "Blob should not be empty.");
538             test.deepEqual(blob.name, blobName);
539             test.deepEqual(self.container, blob.container);
540 
541             // Check data.
542             test.deepEqual(buf.join(''), blobData.join(''));
543             test.deepEqual(stream.readable, false);
544 
545             // Check meta.
546             test.ok(meta.headers);
547             test.ok(meta.headers['date']);
548             test.ok(meta.headers['last-modified']);
549             test.ok(meta.headers['server']);
550             test.ok(meta.headers['etag']);
551             test.ok(meta.cloudHeaders);
552             test.ok(meta.metadata);
553             if (opts.config.isAws()) {
554               test.deepEqual(meta.headers['server'], "AmazonS3");
555               test.ok(meta.cloudHeaders['request-id']);
556             } else if (opts.config.isGoogle()) {
557               test.ok(meta.headers['server']);
558               test.ok(meta.headers['expires']);
559             }
560 
561             // Check custom metadata.
562             // Note: Everything is a string (hence, bar conversion).
563             test.deepEqual(metadata['foo'], meta.metadata['foo']);
564             test.deepEqual(metadata['bar'].toString(), meta.metadata['bar']);
565 
566             callback(null);
567           });
568           stream.end();
569         },
570         // HEAD blob metadata.
571         function (callback) {
572           var buf = [],
573             request = self.container.headBlob(blobName);
574 
575           request.on('error', utils.errHandle(test));
576           request.on('end', function (results, meta) {
577             var blob = results.blob;
578 
579             // Check blob object.
580             test.ok(blob, "Blob should not be empty.");
581             test.deepEqual(blob.name, blobName);
582             test.deepEqual(self.container, blob.container);
583 
584             // Check meta.
585             test.ok(meta.headers);
586             test.ok(meta.headers['date']);
587             test.ok(meta.headers['last-modified']);
588             test.ok(meta.headers['server']);
589             test.ok(meta.headers['etag']);
590             test.ok(meta.cloudHeaders);
591             test.ok(meta.metadata);
592             if (opts.config.isAws()) {
593               test.deepEqual(meta.headers['server'], "AmazonS3");
594               test.ok(meta.cloudHeaders['request-id']);
595             } else if (opts.config.isGoogle()) {
596               test.ok(meta.headers['server']);
597               test.ok(meta.headers['expires']);
598             }
599 
600             // Check custom metadata.
601             test.deepEqual(metadata['foo'], meta.metadata['foo']);
602             test.deepEqual(metadata['bar'].toString(), meta.metadata['bar']);
603 
604             callback(null);
605           });
606           request.end();
607         }
608       ], function (err) {
609         test.ok(!err, "Should not have an error.");
610         test.done();
611       });
612     },
613     "PUT blob from file (ReadStream) with pipe().": function (test, opts) {
614       var self = this,
615         blobName = "test/live/pipe/blob.js",
616         filePath = __filename;
617 
618       test.expect(9);
619       async.series([
620         // PUT blob from file.
621         function (callback) {
622           var readStream,
623             putStream;
624 
625           // Set up streams.
626           readStream = fs.createReadStream(filePath, { encoding: "utf8" });
627           putStream = self.container.putBlob(blobName, { encoding: "utf8" });
628 
629           // Handlers.
630           putStream.on('error', utils.errHandle(test));
631           putStream.on('end', function (results, meta) {
632             var blob = results.blob;
633 
634             // Check blob object.
635             test.ok(blob, "Blob should not be empty.");
636             test.deepEqual(blob.name, blobName);
637             test.deepEqual(self.container, blob.container);
638 
639             callback(null);
640           });
641           putStream.on('pipe', function () {
642             test.ok(true, "Should get pipe event.");
643           });
644 
645           // Pipe.
646           readStream.pipe(putStream);
647         },
648         // GET blob with data.
649         function (callback) {
650           var buf = [],
651             stream = self.container.getBlob(blobName, {
652               encoding: 'utf8'
653             });
654 
655           stream.on('error', utils.errHandle(test));
656           stream.on('data', function (chunk) {
657             buf.push(chunk);
658           });
659           stream.on('end', function (results, meta) {
660             var blob = results.blob,
661               token;
662 
663             // Check blob object.
664             test.ok(blob, "Blob should not be empty.");
665             test.deepEqual(blob.name, blobName);
666             test.deepEqual(self.container, blob.container);
667 
668             // Here's our token to grep. It should now be in the returned
669             // string as we've got this entire source file. How meta...
670             token = "READ_STREAM_TOKEN_IN_THE_FILE_DATA_01";
671             test.ok(buf.join('').indexOf(token) > -1, "Should find token.");
672 
673             callback(null);
674           });
675           stream.end();
676         }
677       ], function (err) {
678         test.ok(!err, "Should not have an error.");
679         test.done();
680       });
681     },
682     "PUT with putFromFile and metadata.": function (test, opts) {
683       var self = this,
684         blobName = "test/live/putFromFile/blob.js",
685         filePath = __filename,
686         metadata = {
687           'foo': "My foo metadata.",
688           'bar': 42
689         };
690 
691       test.expect(12);
692       async.series([
693         // PUT blob from file.
694         function (callback) {
695           var request;
696 
697           request = self.container.putBlobFromFile(blobName, filePath, {
698             encoding: "utf8",
699             metadata: metadata
700           });
701 
702           request.on('error', utils.errHandle(test));
703           request.on('end', function (results, meta) {
704             var blob = results.blob;
705 
706             // Check blob object.
707             test.ok(blob, "Blob should not be empty.");
708             test.deepEqual(blob.name, blobName);
709             test.deepEqual(self.container, blob.container);
710 
711             callback(null);
712           });
713           request.end();
714         },
715         // GET blob with data.
716         function (callback) {
717           var buf = [],
718             stream = self.container.getBlob(blobName, {
719               encoding: 'utf8'
720             });
721 
722           stream.on('error', utils.errHandle(test));
723           stream.on('data', function (chunk) {
724             buf.push(chunk);
725           });
726           stream.on('end', function (results, meta) {
727             var blob = results.blob,
728               token;
729 
730             // Check blob object.
731             test.ok(blob, "Blob should not be empty.");
732             test.deepEqual(blob.name, blobName);
733             test.deepEqual(self.container, blob.container);
734 
735             // Check custom metadata.
736             test.ok(meta);
737             test.ok(meta.metadata);
738             test.deepEqual(metadata['foo'], meta.metadata['foo']);
739             test.deepEqual(metadata['bar'].toString(), meta.metadata['bar']);
740 
741             // Here's our token to grep. It should now be in the returned
742             // string as we've got this entire source file. How meta...
743             token = "READ_STREAM_TOKEN_IN_THE_FILE_DATA_02";
744             test.ok(buf.join('').indexOf(token) > -1, "Should find token.");
745 
746             callback(null);
747           });
748           stream.end();
749         }
750       ], function (err) {
751         test.ok(!err, "Should not have an error.");
752         test.done();
753       });
754     },
755     "PUT non-existent local file with putFromFile.": function (test, opts) {
756       var self = this,
757         blobName = "shouldnt/actually/create/this.txt",
758         filePath = "/this/path/really/shouldnt/exist.txt",
759         request;
760 
761       request = self.container.putBlobFromFile(blobName, filePath);
762 
763       test.expect(2);
764       request.on('error', function (err) {
765         // Expect local file not found error.
766         test.ok(err, "Should have error.");
767         test.deepEqual(err.code, "ENOENT");
768         test.done();
769       });
770       request.on('end', function (results) {
771         test.ok(false, "Should not have completion. Got: " + results);
772         test.done();
773       });
774       request.end();
775     },
776     "GET non-existent blob AND file with getToFile.": function (test, opts) {
777       var self = this,
778         blobName = "shouldnt/exist/getToFile/file.txt",
779         filePath = "/this/path/really/shouldnt/exist.txt",
780         request;
781 
782       request = self.container.getBlobToFile(blobName, filePath);
783 
784       test.expect(2);
785       request.on('error', function (err) {
786         // Local file error hits first.
787         test.ok(err, "Should have error.");
788         test.deepEqual(err.code, "ENOENT");
789         test.done();
790       });
791       request.on('end', function (results) {
792         test.ok(false, "Should not have completion. Got: " + results);
793         test.done();
794       });
795       request.end();
796     },
797     "PUT blob with bad header.": function (test, opts) {
798       var self = this,
799         blobName = "blob/shouldnt/make/it.txt",
800         request = self.container.putBlob(blobName, {
801           headers: {
802             'transfer-encoding': "This should cause cloud error in AWS/GSFD."
803           }
804         });
805 
806       test.expect(2);
807       request.on('error', function (err) {
808         // We don't handle these errors explicitly with isFoo() methods.
809         test.ok(err, "Should have error.");
810         test.ok(err.statusCode, "Should have a status code.");
811         test.done();
812       });
813       request.on('end', function (results) {
814         test.ok(false, "Should not have completion. Got: " + results);
815         test.done();
816       });
817       request.end();
818     },
819     "PUT non-existent local file with header error.": function (test, opts) {
820       var self = this,
821         blobName = "shouldnt/actually/create/this.txt",
822         filePath = "/this/path/really/shouldnt/exist.txt",
823         request;
824 
825       request = self.container.putBlobFromFile(blobName, filePath, {
826         headers: {
827           'invalid-header': "This should cause cloud error."
828         }
829       });
830 
831       test.expect(2);
832       request.on('error', function (err) {
833         // Expect local file not found error.
834         test.ok(err, "Should have error.");
835         test.deepEqual(err.code, "ENOENT");
836         test.done();
837       });
838       request.on('end', function (results) {
839         test.ok(false, "Should not have completion. Got: " + results);
840         test.done();
841       });
842       request.end();
843     }
844   });
845 
846   // // Limit tests.
847   // _group = 'Blob (w/ Cont)'
848   // _tests = [
849   //   'PUT blob with utf8, md5 and metadata, and GET',
850   //   'PUT blob with utf8 (Google)'
851   // ]
852   // _Tests = Tests
853   // Tests = {}
854   // Tests[_group] = {
855   //   setUp: _Tests[_group].setUp,
856   //   tearDown: _Tests[_group].tearDown
857   // }
858   // _tests.map(function (test) {
859   //   Tests[_group][test] = _Tests[_group][test]
860   // });
861 
862   module.exports.Tests = Tests;
863 }());
864