text: 'use strict';
  
  const assert = require('assert');
  const path = require('path');
  const fs = require('fs');
  const md5 = require('md5');
  const server = require('./server');
  const fileDir = server.fileDir;
  const uploadDir = server.uploadDir;
  const {
    debugLog,
    isFunc,
    errorFunc,
    getTempFilename,
    buildOptions,
    buildFields,
    checkAndMakeDir,
    deleteFile,
    copyFile,
    saveBufferToFile,
    parseFileName,
    uriDecodeFileName
  } = require('../lib/utilities');
  
  const mockFile = 'basketball.png';
  const mockBuffer = fs.readFileSync(path.join(fileDir, mockFile));
  const mockHash = md5(mockBuffer);
  
  
  describe('Test of the utilities functions', function() {
    beforeEach(function() {
      server.clearUploadsDir();
    });
    //debugLog tests
    describe('Test debugLog function', () => {
  
      let testMessage = 'Test message';
  
      it('debugLog returns false if no options passed', () => {
        assert.equal(debugLog(null, testMessage), false);
      });
  
      it('debugLog returns false if option debug is false', () => {
        assert.equal(debugLog({debug: false}, testMessage), false);
      });
  
      it('debugLog returns true if option debug is true', () => {
        assert.equal(debugLog({debug: true}, testMessage), true);
      });
  
    });
    //isFunc tests
    describe('Test isFunc function', () => {
  
      it('isFunc returns true if function passed', () => assert.equal(isFunc(()=>{}), true));
  
      it('isFunc returns false if null passed', function() {
        assert.equal(isFunc(null), false);
      });
  
      it('isFunc returns false if undefined passed', function() {
        assert.equal(isFunc(undefined), false);
      });
  
      it('isFunc returns false if object passed', function() {
        assert.equal(isFunc({}), false);
      });
  
      it('isFunc returns false if array passed', function() {
        assert.equal(isFunc([]), false);
      });
    });
    //errorFunc tests
    describe('Test errorFunc function', () => {
  
      const resolve = () => 'success';
      const reject = () => 'error';
  
      it('errorFunc returns resolve if reject function has not been passed', () => {
        let result = errorFunc(resolve);
        assert.equal(result(), 'success');
      });
  
      it('errorFunc returns reject if reject function has been passed', () => {
        let result = errorFunc(resolve, reject);
        assert.equal(result(), 'error');
      });
  
    });
    //getTempFilename tests
    describe('Test getTempFilename function', () => {
  
      const nameRegexp = /tmp-\d{1,5}-\d{1,}/;
  
      it('getTempFilename result matches regexp /tmp-d{1,5}-d{1,}/', () => {
  
        let errCounter = 0;
        let tempName = '';
        for (var i = 0; i < 65537; i++) {
          tempName = getTempFilename();
          if (!nameRegexp.test(tempName)) errCounter ++;
        }
  
        assert.equal(errCounter, 0);
      });
  
      it('getTempFilename current and previous results are not equal', () => {
  
        let errCounter = 0;
        let tempName = '';
        let previousName = '';
        for (var i = 0; i < 65537; i++) {
          previousName = tempName;
          tempName = getTempFilename();
          if (previousName === tempName) errCounter ++;
        }
  
        assert.equal(errCounter, 0);
      });
  
    });
    //parseFileName
    describe('Test parseFileName function', () => {
  
      it('Does nothing to your filename when disabled.', () => {
        const opts = {safeFileNames: false};
        const name = 'my$Invalid#fileName.png123';
        const expected = 'my$Invalid#fileName.png123';
        let result = parseFileName(opts, name);
        assert.equal(result, expected);
      });
  
      it('Cuts of file name length if it more then 255 chars.', () => {
        const name = 'a'.repeat(300);
        const result = parseFileName({}, name);
        assert.equal(result.length, 255);
      });
      
      it(
        'Strips away all non-alphanumeric characters (excluding hyphens/underscores) when enabled.',
        () => {
          const opts = {safeFileNames: true};
          const name = 'my$Invalid#fileName.png123';
          const expected = 'myInvalidfileNamepng123';
          let result = parseFileName(opts, name);
          assert.equal(result, expected);
        });
  
      it(
        'Strips away all non-alphanumeric chars when preserveExtension: true for a name without dots',
        () => {
          const opts = {safeFileNames: true, preserveExtension: true};
          const name = 'my$Invalid#fileName';
          const expected = 'myInvalidfileName';
          let result = parseFileName(opts, name);
          assert.equal(result, expected);
        });
  
      it('Accepts a regex for stripping (decidedly) "invalid" characters from filename.', () => {
        const opts = {safeFileNames: /[$#]/g};
        const name = 'my$Invalid#fileName.png123';
        const expected = 'myInvalidfileName.png123';
        let result = parseFileName(opts, name);
        assert.equal(result, expected);
      });
  
      it(
        'Returns correct filename if name contains dots characters and preserveExtension: true.',
        () => {
          const opts = {safeFileNames: true, preserveExtension: true};
          const name = 'basket.ball.png';
          const expected = 'basketball.png';
          let result = parseFileName(opts, name);
          assert.equal(result, expected);
        });
  
      it('Returns a temporary file name if name argument is empty.', () => {
        const opts = {safeFileNames: false};
        const result = parseFileName(opts);
        assert.equal(typeof result, 'string');
      });
  
    });
    //buildOptions tests
    describe('Test buildOptions function', () => {
  
      const source = { option1: '1', option2: '2' };
      const sourceAddon = { option3: '3'};
      const expected = { option1: '1', option2: '2' };
      const expectedAddon = { option1: '1', option2: '2', option3: '3'};
  
      it('buildOptions returns and equal object to the object which was paased', () => {
        let result = buildOptions(source);
        assert.deepStrictEqual(result, source);
      });
  
      it('buildOptions doesnt add non object or null arguments to the result', () => {
        let result = buildOptions(source, 2, '3', null);
        assert.deepStrictEqual(result, expected);
      });
  
      it('buildOptions adds value to the result from the several source argumets', () => {
        let result = buildOptions(source, sourceAddon);
        assert.deepStrictEqual(result, expectedAddon);
      });    
  
    });
    //buildFields tests
    describe('Test buildOptions function', () => {
    
      it('buildFields does nothing if null value has been passed', () => {
        let fields = null;
        fields = buildFields(fields, 'test', null);
        assert.equal(fields, null);
      });
  
    });
    //checkAndMakeDir tests
    describe('Test checkAndMakeDir function', () => {
      //
      it('checkAndMakeDir returns false if upload options object was not set', () => {
        assert.equal(checkAndMakeDir(), false);
      });
      //
      it('checkAndMakeDir returns false if upload option createParentPath was not set', () => {
        assert.equal(checkAndMakeDir({}), false);
      });
      //
      it('checkAndMakeDir returns false if filePath was not set', () => {
        assert.equal(checkAndMakeDir({createParentPath: true}), false);
      });
      //
      it('checkAndMakeDir return true if path to the file already exists', ()=>{
        let dir = path.join(uploadDir, 'testfile');
        assert.equal(checkAndMakeDir({createParentPath: true}, dir), true);
      });
      //
      it('checkAndMakeDir creates a dir if path to the file not exists', ()=>{
        let dir = path.join(uploadDir, 'testfolder', 'testfile');
        assert.equal(checkAndMakeDir({createParentPath: true}, dir), true);
      });
      //
      it('checkAndMakeDir creates a dir recursively if path to the file not exists', ()=>{
        let dir = path.join(uploadDir, 'testfolder', 'testsubfolder', 'testfile');
        assert.equal(checkAndMakeDir({createParentPath: true}, dir), true);
      });
    });
    //saveBufferToFile tests
    describe('Test saveBufferToFile function', function(){
      beforeEach(function() {
        server.clearUploadsDir();
      });
  
      it('Save buffer to a file', function(done) {
        let filePath = path.join(uploadDir, mockFile);
        saveBufferToFile(mockBuffer, filePath, function(err){
          if (err) {
            return done(err);
          }
          fs.stat(filePath, done);
        });
      });
  
      it('Failed if not a buffer passed', function(done) {
        let filePath = path.join(uploadDir, mockFile);
        saveBufferToFile(undefined, filePath, function(err){
          if (err) {
            return done();
          }
        });
      });
  
      it('Failed if wrong path passed', function(done) {
        let filePath = '';
        saveBufferToFile(mockFile, filePath, function(err){
          if (err) {
            return done();
          }
        });
      });
    });
  
    describe('Test deleteFile function', function(){
      beforeEach(function() {
        server.clearUploadsDir();
      });
  
      it('Failed if nonexistent file passed', function(done){
        let filePath = path.join(uploadDir, getTempFilename());
        
        deleteFile(filePath, function(err){
          if (err) {
            return done();
          }
        });
      });
  
      it('Delete a file', function(done){
        let srcPath = path.join(fileDir, mockFile);
        let dstPath = path.join(uploadDir, getTempFilename());
  
        //copy a file
        copyFile(srcPath, dstPath, function(err){
          if (err) {
            return done(err);
          }
          fs.stat(dstPath, (err)=>{
            if (err){
              return done(err);
            }
            // delete a file
            deleteFile(dstPath, function(err){
              if (err) {
                return done(err);
              }
  
              fs.stat(dstPath, (err)=>{
                if (err){
                  return done();
                }
  
                //error if a file still exist
                done(err);
              });
            });
          });
        });      
      });
  
    });
    
    describe('Test copyFile function', function(){
      beforeEach(function() {
        server.clearUploadsDir();
      });
  
      it('Copy a file and check a hash', function(done) {
        let srcPath = path.join(fileDir, mockFile);
        let dstPath = path.join(uploadDir, mockFile);
        
        copyFile(srcPath, dstPath, function(err){
          if (err) {
            return done(err);
          }
          fs.stat(dstPath, (err)=>{
            if (err){
              return done(err);
            }
            //Match source and destination files hash.
            let fileBuffer = fs.readFileSync(dstPath);
            let fileHash = md5(fileBuffer);
            return (fileHash === mockHash) ? done() : done(err);
          });
        });
      });
  
      it('Failed if wrong source file path passed', function(done){
        let srcPath = path.join(fileDir, 'unknown');
        let dstPath = path.join(uploadDir, mockFile);
        
        copyFile(srcPath, dstPath, function(err){
          if (err) {
            return done();
          }
        });
      });
  
      it('Failed if wrong destination file path passed', function(done){
        let srcPath = path.join(fileDir, 'unknown');
        let dstPath = path.join('unknown', 'unknown');
        
        copyFile(srcPath, dstPath, function(err){
          if (err) {
            return done();
          }
        });
      });
    });
  
    describe('Test uriDecodeFileName function', function() {
      const testData = [
        { enc: 'test%22filename', dec: 'test"filename' },
        { enc: 'test%60filename', dec: 'test`filename' },
        { enc: '%3Fx%3Dtest%22filename', dec: '?x=test"filename'}
      ];
  
      // Test decoding if uriDecodeFileNames: true.
      testData.forEach((testName) => {
        const opts = { uriDecodeFileNames: true };
        it(`Return ${testName.dec} for input ${testName.enc} if uriDecodeFileNames: true`, () => {
          assert.equal(uriDecodeFileName(opts, testName.enc), testName.dec);
        });
      });
  
      // Test decoding if uriDecodeFileNames: false.
      testData.forEach((testName) => {
        const opts = { uriDecodeFileNames: false };
        it(`Return ${testName.enc} for input ${testName.enc} if uriDecodeFileNames: false`, () => {
          assert.equal(uriDecodeFileName(opts, testName.enc), testName.enc);
        });
      });
    });
  });
  