multipartUploads.spec.js 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451
  1. 'use strict';
  2. const fs = require('fs');
  3. const md5 = require('md5');
  4. const path = require('path');
  5. const request = require('supertest');
  6. const server = require('./server');
  7. const fileDir = server.fileDir;
  8. const tempDir = server.tempDir;
  9. const uploadDir = server.uploadDir;
  10. const clearTempDir = server.clearTempDir;
  11. const clearUploadsDir = server.clearUploadsDir;
  12. const mockFiles = ['car.png', 'tree.png', 'basketball.png', 'emptyfile.txt'];
  13. const mockUser = {
  14. firstName: 'Joe',
  15. lastName: 'Schmo',
  16. email: 'joe@mailinator.com'
  17. };
  18. // Reset response body.uploadDir/uploadPath for testing.
  19. const resetBodyUploadData = (res) => {
  20. res.body.uploadDir = '';
  21. res.body.uploadPath = '';
  22. };
  23. const genUploadResult = (fileName, filePath) => {
  24. const fileStat = fs.statSync(filePath);
  25. const fileBuffer = fs.readFileSync(filePath);
  26. return {
  27. name: fileName,
  28. md5: md5(fileBuffer),
  29. size: fileStat.size,
  30. uploadDir: '',
  31. uploadPath: ''
  32. };
  33. };
  34. describe('Test Directory Cleaning Method', function() {
  35. it('emptied "uploads" directory', function(done) {
  36. clearUploadsDir();
  37. const filesFound = fs.readdirSync(uploadDir).length;
  38. done(filesFound ? `Directory not empty. Found ${filesFound} files.` : null);
  39. });
  40. });
  41. describe('Test Single File Upload', function() {
  42. const app = server.setup();
  43. mockFiles.forEach((fileName) => {
  44. const filePath = path.join(fileDir, fileName);
  45. const uploadedFilePath = path.join(uploadDir, fileName);
  46. const result = genUploadResult(fileName, filePath);
  47. it(`upload ${fileName} with POST`, function(done) {
  48. clearUploadsDir();
  49. request(app)
  50. .post('/upload/single')
  51. .attach('testFile', filePath)
  52. .expect(resetBodyUploadData)
  53. .expect(200, result, err => (err ? done(err) : fs.stat(uploadedFilePath, done)));
  54. });
  55. it(`upload ${fileName} with PUT`, function(done) {
  56. clearUploadsDir();
  57. request(app)
  58. .post('/upload/single')
  59. .attach('testFile', filePath)
  60. .expect(resetBodyUploadData)
  61. .expect(200, result, err => (err ? done(err) : fs.stat(uploadedFilePath, done)));
  62. });
  63. });
  64. it('fail when no files were attached', function(done) {
  65. request(app)
  66. .post('/upload/single')
  67. .expect(400)
  68. .end(done);
  69. });
  70. it('fail when using GET', function(done) {
  71. request(app)
  72. .get('/upload/single')
  73. .attach('testFile', path.join(fileDir, mockFiles[0]))
  74. .expect(400)
  75. .end(done);
  76. });
  77. it('fail when using HEAD', function(done) {
  78. request(app)
  79. .head('/upload/single')
  80. .attach('testFile', path.join(fileDir, mockFiles[0]))
  81. .expect(400)
  82. .end(done);
  83. });
  84. });
  85. describe('Test Single File Upload w/ .mv()', function() {
  86. const app = server.setup();
  87. mockFiles.forEach((fileName) => {
  88. const filePath = path.join(fileDir, fileName);
  89. const uploadedFilePath = path.join(uploadDir, fileName);
  90. const result = genUploadResult(fileName, filePath);
  91. it(`upload ${fileName} with POST w/ .mv()`, function(done) {
  92. clearUploadsDir();
  93. request(app)
  94. .post('/upload/single')
  95. .attach('testFile', filePath)
  96. .expect(resetBodyUploadData)
  97. .expect(200, result, err => (err ? done(err) : fs.stat(uploadedFilePath, done)));
  98. });
  99. it(`upload ${fileName} with PUT w/ .mv()`, function(done) {
  100. clearUploadsDir();
  101. request(app)
  102. .post('/upload/single')
  103. .attach('testFile', filePath)
  104. .expect(resetBodyUploadData)
  105. .expect(200, result, err => (err ? done(err) : fs.stat(uploadedFilePath, done)));
  106. });
  107. });
  108. });
  109. describe('Test Single File Upload with useTempFiles option.', function() {
  110. const app = server.setup({ useTempFiles: true, tempFileDir: tempDir });
  111. mockFiles.forEach((fileName) => {
  112. const filePath = path.join(fileDir, fileName);
  113. const uploadedFilePath = path.join(uploadDir, fileName);
  114. const result = genUploadResult(fileName, filePath);
  115. it(`upload ${fileName} with POST`, function(done) {
  116. clearUploadsDir();
  117. request(app)
  118. .post('/upload/single')
  119. .attach('testFile', filePath)
  120. .expect(resetBodyUploadData)
  121. .expect(200, result, err => (err ? done(err) : fs.stat(uploadedFilePath, done)));
  122. });
  123. it(`upload ${fileName} with PUT`, function(done) {
  124. clearUploadsDir();
  125. request(app)
  126. .post('/upload/single')
  127. .attach('testFile', filePath)
  128. .expect(resetBodyUploadData)
  129. .expect(200, result, err => (err ? done(err) : fs.stat(uploadedFilePath, done)));
  130. });
  131. });
  132. it('fail when no files were attached', function(done) {
  133. request(app)
  134. .post('/upload/single')
  135. .expect(400)
  136. .end(done);
  137. });
  138. it('fail when using GET', function(done) {
  139. request(app)
  140. .get('/upload/single')
  141. .attach('testFile', path.join(fileDir, mockFiles[0]))
  142. .expect(400)
  143. .end(done);
  144. });
  145. it('fail when using HEAD', function(done) {
  146. request(app)
  147. .head('/upload/single')
  148. .attach('testFile', path.join(fileDir, mockFiles[0]))
  149. .expect(400)
  150. .end(done);
  151. });
  152. });
  153. describe('Test Single File Upload with useTempFiles option and empty tempFileDir.', function() {
  154. const app = server.setup({ useTempFiles: true, tempFileDir: '' });
  155. mockFiles.forEach((fileName) => {
  156. const filePath = path.join(fileDir, fileName);
  157. const uploadedFilePath = path.join(uploadDir, fileName);
  158. const result = genUploadResult(fileName, filePath);
  159. it(`upload ${fileName} with POST`, function(done) {
  160. clearUploadsDir();
  161. request(app)
  162. .post('/upload/single')
  163. .attach('testFile', filePath)
  164. .expect(resetBodyUploadData)
  165. .expect(200, result, err => (err ? done(err) : fs.stat(uploadedFilePath, done)));
  166. });
  167. });
  168. });
  169. describe('Test Single File Upload w/ .mv() Promise', function() {
  170. const app = server.setup();
  171. mockFiles.forEach((fileName) => {
  172. const filePath = path.join(fileDir, fileName);
  173. const uploadedFilePath = path.join(uploadDir, fileName);
  174. const result = genUploadResult(fileName, filePath);
  175. it(`upload ${fileName} with POST w/ .mv() Promise`, function(done) {
  176. clearUploadsDir();
  177. request(app)
  178. .post('/upload/single/promise')
  179. .attach('testFile', filePath)
  180. .expect(resetBodyUploadData)
  181. .expect(200, result, err => (err ? done(err) : fs.stat(uploadedFilePath, done)));
  182. });
  183. it(`upload ${fileName} with PUT w/ .mv() Promise`, function(done) {
  184. clearUploadsDir();
  185. request(app)
  186. .post('/upload/single/promise')
  187. .attach('testFile', filePath)
  188. .expect(resetBodyUploadData)
  189. .expect(200, result, err => (err ? done(err) : fs.stat(uploadedFilePath, done)));
  190. });
  191. });
  192. it('fail when no files were attached', function(done) {
  193. request(app)
  194. .post('/upload/single')
  195. .expect(400)
  196. .end(done);
  197. });
  198. it('fail when using GET', function(done) {
  199. request(app)
  200. .get('/upload/single')
  201. .attach('testFile', path.join(fileDir, mockFiles[0]))
  202. .expect(400)
  203. .end(done);
  204. });
  205. it('fail when using HEAD', function(done) {
  206. request(app)
  207. .head('/upload/single')
  208. .attach('testFile', path.join(fileDir, mockFiles[0]))
  209. .expect(400)
  210. .end(done);
  211. });
  212. });
  213. describe('Test Single File Upload w/ .mv() Promise and useTempFiles set to true', function() {
  214. const app = server.setup({ useTempFiles: true, tempFileDir: tempDir });
  215. mockFiles.forEach((fileName) => {
  216. const filePath = path.join(fileDir, fileName);
  217. const uploadedFilePath = path.join(uploadDir, fileName);
  218. const result = genUploadResult(fileName, filePath);
  219. it(`upload ${fileName} with POST w/ .mv() Promise`, function(done) {
  220. clearUploadsDir();
  221. request(app)
  222. .post('/upload/single/promise')
  223. .attach('testFile', filePath)
  224. .expect(resetBodyUploadData)
  225. .expect(200, result, err => (err ? done(err) : fs.stat(uploadedFilePath, done)));
  226. });
  227. it(`upload ${fileName} with PUT w/ .mv() Promise`, function(done) {
  228. clearUploadsDir();
  229. request(app)
  230. .post('/upload/single/promise')
  231. .attach('testFile', filePath)
  232. .expect(resetBodyUploadData)
  233. .expect(200, result, err => (err ? done(err) : fs.stat(uploadedFilePath, done)));
  234. });
  235. });
  236. it('fail when no files were attached', (done) => {
  237. request(app)
  238. .post('/upload/single')
  239. .expect(400)
  240. .end(done);
  241. });
  242. it('fail when using GET', (done) => {
  243. request(app)
  244. .get('/upload/single')
  245. .attach('testFile', path.join(fileDir, mockFiles[0]))
  246. .expect(400)
  247. .end(done);
  248. });
  249. it('fail when using HEAD', (done) => {
  250. request(app)
  251. .head('/upload/single')
  252. .attach('testFile', path.join(fileDir, mockFiles[0]))
  253. .expect(400)
  254. .end(done);
  255. });
  256. });
  257. describe('Test Multi-File Upload', function() {
  258. const app = server.setup();
  259. it('upload multiple files with POST', (done) => {
  260. clearUploadsDir();
  261. const req = request(app).post('/upload/multiple');
  262. const expectedResult = [];
  263. const expectedResultSorted = [];
  264. const uploadedFilesPath = [];
  265. mockFiles.forEach((fileName, index) => {
  266. const filePath = path.join(fileDir, fileName);
  267. req.attach(`testFile${index + 1}`, filePath);
  268. uploadedFilesPath.push(path.join(uploadDir, fileName));
  269. expectedResult.push(genUploadResult(fileName, filePath));
  270. });
  271. req
  272. .expect((res) => {
  273. res.body.forEach((fileInfo) => {
  274. fileInfo.uploadDir = '';
  275. fileInfo.uploadPath = '';
  276. const index = mockFiles.indexOf(fileInfo.name);
  277. expectedResultSorted.push(expectedResult[index]);
  278. });
  279. })
  280. .expect(200, expectedResultSorted)
  281. .end((err) => {
  282. if (err) return done(err);
  283. fs.stat(uploadedFilesPath[0], (err) => {
  284. if (err) return done(err);
  285. fs.stat(uploadedFilesPath[1], (err) => {
  286. if (err) return done(err);
  287. fs.stat(uploadedFilesPath[2], done);
  288. });
  289. });
  290. });
  291. });
  292. });
  293. describe('Test File Array Upload', function() {
  294. const app = server.setup();
  295. it('upload array of files with POST', (done) => {
  296. clearUploadsDir();
  297. const req = request(app).post('/upload/array');
  298. const expectedResult = [];
  299. const expectedResultSorted = [];
  300. const uploadedFilesPath = [];
  301. mockFiles.forEach((fileName) => {
  302. const filePath = path.join(fileDir, fileName);
  303. uploadedFilesPath.push(path.join(uploadDir, fileName));
  304. expectedResult.push(genUploadResult(fileName, filePath));
  305. req.attach('testFiles', filePath);
  306. });
  307. req
  308. .expect((res)=>{
  309. res.body.forEach((fileInfo) => {
  310. fileInfo.uploadDir = '';
  311. fileInfo.uploadPath = '';
  312. const index = mockFiles.indexOf(fileInfo.name);
  313. expectedResultSorted.push(expectedResult[index]);
  314. });
  315. })
  316. .expect(200, expectedResultSorted)
  317. .end((err) => {
  318. if (err) return done(err);
  319. uploadedFilesPath.forEach((uploadedFilePath) => {
  320. fs.statSync(uploadedFilePath);
  321. });
  322. done();
  323. });
  324. });
  325. });
  326. describe('Test Upload With Fields', function() {
  327. const app = server.setup();
  328. mockFiles.forEach((fileName) => {
  329. const filePath = path.join(fileDir, fileName);
  330. const uploadedFilePath = path.join(uploadDir, fileName);
  331. // Expected results
  332. const result = genUploadResult(fileName, filePath);
  333. result.firstName = mockUser.firstName;
  334. result.lastName = mockUser.lastName;
  335. result.email = mockUser.email;
  336. it(`upload ${fileName} and submit fields at the same time with POST`, function(done) {
  337. clearUploadsDir();
  338. request(app)
  339. .post('/upload/single/withfields')
  340. .attach('testFile', filePath)
  341. .field('firstName', mockUser.firstName)
  342. .field('lastName', mockUser.lastName)
  343. .field('email', mockUser.email)
  344. .expect(resetBodyUploadData)
  345. .expect(200, result, err => (err ? done(err) : fs.stat(uploadedFilePath, done)));
  346. });
  347. it(`upload ${fileName} and submit fields at the same time with PUT`, function(done) {
  348. clearUploadsDir();
  349. request(app)
  350. .put('/upload/single/withfields')
  351. .attach('testFile', filePath)
  352. .field('firstName', mockUser.firstName)
  353. .field('lastName', mockUser.lastName)
  354. .field('email', mockUser.email)
  355. .expect(resetBodyUploadData)
  356. .expect(200, result, err => (err ? done(err) : fs.stat(uploadedFilePath, done)));
  357. });
  358. });
  359. });
  360. describe('Test Aborting/Canceling during upload', function() {
  361. this.timeout(4000); // Set timeout for async tests.
  362. const uploadTimeout = 1000;
  363. const app = server.setup({
  364. useTempFiles: true,
  365. tempFileDir: tempDir,
  366. debug: true,
  367. uploadTimeout
  368. });
  369. clearTempDir();
  370. clearUploadsDir();
  371. mockFiles.forEach((fileName) => {
  372. const filePath = path.join(fileDir, fileName);
  373. it(`Delete temp file if ${fileName} upload was aborted`, (done) => {
  374. const req = request(app)
  375. .post('/upload/single')
  376. .attach('testFile', filePath)
  377. .on('progress', (e) => {
  378. const progress = (e.loaded * 100) / e.total;
  379. // Aborting request, use req.req since it is original superagent request.
  380. if (progress > 50) req.req.abort();
  381. })
  382. .end((err) => {
  383. if (!err) return done(`Connection hasn't been aborted!`);
  384. if (err.code !== 'ECONNRESET') return done(err);
  385. // err.code === 'ECONNRESET' that means upload has been aborted.
  386. // Checking temp directory after upload timeout.
  387. setTimeout(() => {
  388. fs.readdir(tempDir, (err, files) => {
  389. if (err) return done(err);
  390. return files.length ? done(`Temporary directory contains files!`) : done();
  391. });
  392. }, uploadTimeout * 2);
  393. });
  394. });
  395. });
  396. });