Repository: serverless/serverless (JavaScript) — Bug Fix

Issue:
'serverless slstats --disable' doesn't run on v1.2
# This is a Bug Report


## Description

For bug reports:
* What went wrong? Running 'serverless slstats --disable' doesn't disable the slstats and causes serverless deployment to fail.

This change caused the issue: https://github.com/serverless/serverless/commit/45f2d82f597d034da59dffb140f5701c8f98757a#diff-a964b61d4edd03113e94955888593669
* What did you expect should have happened? stats-disabled be created in .serverless folder
* What was the config you used?
* What stacktrace or error message from your provider did you see?

Similar or dependent issues:
#2335 

 Serverless Error ---------------------------------------
 
     Enabling / Disabling of statistics failed: ENOENT: no
     such file or directory, lstat '/Users/user/.serverless/stats-enabled'
 
  Get Support --------------------------------------------
     Docs:          docs.serverless.com
     Bugs:          github.com/serverless/serverless/issues
 
  Your Environment Information -----------------------------
     OS:                 darwin (Mac)
     Node Version:       4.3.2
     Serverless Version: 1.2.1

I'll look at fixing this issue later tonight.

**Should this:**
Check if the project contains a stats-disabled file?
or
Check a variable after each serverless command?
or
Check via a variable in serverless.yml?
or
Check via a environment variable?
or
Should it handle the fse.lstatSync better? (By returning the try catch for that method?) 

I don't think any of the above solution are great. So I'd be happy to hear other ways to do this.
'serverless slstats --disable' doesn't run on v1.2
# This is a Bug Report


## Description

For bug reports:
* What went wrong? Running 'serverless slstats --disable' doesn't disable the slstats and causes serverless deployment to fail.

This change caused the issue: https://github.com/serverless/serverless/commit/45f2d82f597d034da59dffb140f5701c8f98757a#diff-a964b61d4edd03113e94955888593669
* What did you expect should have happened? stats-disabled be created in .serverless folder
* What was the config you used?
* What stacktrace or error message from your provider did you see?

Similar or dependent issues:
#2335 

 Serverless Error ---------------------------------------
 
     Enabling / Disabling of statistics failed: ENOENT: no
     such file or directory, lstat '/Users/user/.serverless/stats-enabled'
 
  Get Support --------------------------------------------
     Docs:          docs.serverless.com
     Bugs:          github.com/serverless/serverless/issues
 
  Your Environment Information -----------------------------
     OS:                 darwin (Mac)
     Node Version:       4.3.2
     Serverless Version: 1.2.1

I'll look at fixing this issue later tonight.

**Should this:**
Check if the project contains a stats-disabled file?
or
Check a variable after each serverless command?
or
Check via a variable in serverless.yml?
or
Check via a environment variable?
or
Should it handle the fse.lstatSync better? (By returning the try catch for that method?) 

I don't think any of the above solution are great. So I'd be happy to hear other ways to do this.
'serverless slstats --disable' doesn't run on v1.2
# This is a Bug Report


## Description

For bug reports:
* What went wrong? Running 'serverless slstats --disable' doesn't disable the slstats and causes serverless deployment to fail.

This change caused the issue: https://github.com/serverless/serverless/commit/45f2d82f597d034da59dffb140f5701c8f98757a#diff-a964b61d4edd03113e94955888593669
* What did you expect should have happened? stats-disabled be created in .serverless folder
* What was the config you used?
* What stacktrace or error message from your provider did you see?

Similar or dependent issues:
#2335 

 Serverless Error ---------------------------------------
 
     Enabling / Disabling of statistics failed: ENOENT: no
     such file or directory, lstat '/Users/user/.serverless/stats-enabled'
 
  Get Support --------------------------------------------
     Docs:          docs.serverless.com
     Bugs:          github.com/serverless/serverless/issues
 
  Your Environment Information -----------------------------
     OS:                 darwin (Mac)
     Node Version:       4.3.2
     Serverless Version: 1.2.1

I'll look at fixing this issue later tonight.

**Should this:**
Check if the project contains a stats-disabled file?
or
Check a variable after each serverless command?
or
Check via a variable in serverless.yml?
or
Check via a environment variable?
or
Should it handle the fse.lstatSync better? (By returning the try catch for that method?) 

I don't think any of the above solution are great. So I'd be happy to hear other ways to do this.


Discussion:

Resolving patch:
diff --git a/lib/plugins/slstats/slstats.js b/lib/plugins/slstats/slstats.js
index 5eff6ca017e..5301826d02c 100644
--- a/lib/plugins/slstats/slstats.js
+++ b/lib/plugins/slstats/slstats.js
@@ -33,22 +33,30 @@ class SlStats {
     };
   }
 
-  toggleStats() {
-    const serverlessDirPath = path.join(os.homedir(), '.serverless');
-    const statsDisabledFilePath = path.join(serverlessDirPath, 'stats-disabled');
-    const statsEnabledFilePath = path.join(serverlessDirPath, 'stats-enabled');
+  createStatsFile(oldPath, newPath) {
+    const oldFileExists = this.serverless.utils.fileExistsSync(oldPath);
+    const newFileExists = this.serverless.utils.fileExistsSync(newPath);
+    const isFileToBeRenamed = !newFileExists && oldFileExists;
+    const isFileToBeCreated = !newFileExists;
+    if (isFileToBeRenamed) {
+      fse.renameSync(oldPath, newPath);
+    } else if (isFileToBeCreated) {
+      this.serverless.utils.writeFileSync(newPath);
+    }
+  }
 
+  toggleStats() {
     try {
-      if (this.options.enable && !this.options.disable) {
-        if (fse.lstatSync(statsDisabledFilePath).isFile()) {
-          fse.renameSync(statsDisabledFilePath, statsEnabledFilePath);
-        }
+      const serverlessDirPath = path.join(os.homedir(), '.serverless');
+      const statsDisabledFilePath = path.join(serverlessDirPath, 'stats-disabled');
+      const statsEnabledFilePath = path.join(serverlessDirPath, 'stats-enabled');
+      const isStatsEnabled = this.options.enable && !this.options.disable;
+      const isStatsDisabled = this.options.disable && !this.options.enable;
+      if (isStatsEnabled) {
+        this.createStatsFile(statsDisabledFilePath, statsEnabledFilePath);
         this.serverless.cli.log('Stats successfully enabled');
-      }
-      if (this.options.disable && !this.options.enable) {
-        if (fse.lstatSync(statsEnabledFilePath).isFile()) {
-          fse.renameSync(statsEnabledFilePath, statsDisabledFilePath);
-        }
+      } else if (isStatsDisabled) {
+        this.createStatsFile(statsEnabledFilePath, statsDisabledFilePath);
         this.serverless.cli.log('Stats successfully disabled');
       }
     } catch (error) {


Test patch:
diff --git a/lib/plugins/slstats/slstats.test.js b/lib/plugins/slstats/slstats.test.js
index 77ca1dddebc..8a7d7f30f81 100644
--- a/lib/plugins/slstats/slstats.test.js
+++ b/lib/plugins/slstats/slstats.test.js
@@ -4,6 +4,7 @@ const expect = require('chai').expect;
 const path = require('path');
 const fse = require('fs-extra');
 const os = require('os');
+const sinon = require('sinon');
 const SlStats = require('./slstats');
 const Serverless = require('../../Serverless');
 const testUtils = require('../../../tests/utils');
@@ -47,8 +48,7 @@ describe('SlStats', () => {
     it('should rename the stats file to stats-disabled if disabled', () => {
       // create a stats-enabled file
       serverless.utils.writeFileSync(
-        path.join(serverlessDirPath, 'stats-enabled'),
-        'some content'
+        path.join(serverlessDirPath, 'stats-enabled')
       );
 
       slStats.options = { disable: true };
@@ -66,8 +66,7 @@ describe('SlStats', () => {
     it('should rename the stats file to stats-enabled if enabled', () => {
       // create a stats-disabled file
       serverless.utils.writeFileSync(
-        path.join(serverlessDirPath, 'stats-disabled'),
-        'some content'
+        path.join(serverlessDirPath, 'stats-disabled')
       );
 
       slStats.options = { enable: true };
@@ -82,17 +81,53 @@ describe('SlStats', () => {
       ).to.equal(false);
     });
 
-    it('should throw an error if the stats file does not exist', () => {
+
+    it('should create the stats-enabled file if no stats-enabled file exists', () => {
+      slStats.options = { enable: true };
+
+      slStats.toggleStats();
+
+      expect(
+        serverless.utils.fileExistsSync(path.join(serverlessDirPath, 'stats-enabled'))
+      ).to.equal(true);
+      expect(
+        serverless.utils.fileExistsSync(path.join(serverlessDirPath, 'stats-disabled'))
+      ).to.equal(false);
+    });
+
+    it('should do nothing if the stats-enabled file already exists', () => {
+      // create a stats-disabled file
+      serverless.utils.writeFileSync(
+        path.join(serverlessDirPath, 'stats-enabled')
+      );
+
+      slStats.options = { enable: true };
+
+      slStats.toggleStats();
+
+      expect(
+        serverless.utils.fileExistsSync(path.join(serverlessDirPath, 'stats-enabled'))
+      ).to.equal(true);
+      expect(
+        serverless.utils.fileExistsSync(path.join(serverlessDirPath, 'stats-disabled'))
+      ).to.equal(false);
+    });
+
+    it('should throw an error if unable to create the stats file', () => {
+      const slStatsStub = sinon.stub(SlStats.prototype, 'createStatsFile').throws(
+        new Error('EACCESS: Permission denied')
+      );
       slStats.options = { enable: true };
 
       expect(() => slStats.toggleStats()).to.throw(Error,
-      /Enabling \/ Disabling of statistics failed: ENOENT: no such file or directory, lstat/);
+      /Enabling \/ Disabling of statistics failed: EACCESS: Permission denied/);
       expect(
         serverless.utils.fileExistsSync(path.join(serverlessDirPath, 'stats-enabled'))
       ).to.equal(false);
       expect(
         serverless.utils.fileExistsSync(path.join(serverlessDirPath, 'stats-disabled'))
       ).to.equal(false);
+      slStatsStub.restore();
     });
 
     afterEach(() => {