Repository: serverless/serverless-azure-functions
Pull request #152 (2019-05-29 18:16:02)

test: Unit tests for ResourceService and LoginService
Resolves [AB#18738] and [AB#18740]


Patch:
diff --git a/.travis.yml b/.travis.yml
index bb3eba9..b7979fe 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -4,7 +4,6 @@ os: linux
 
 matrix:
   include:   
-    - node_js: '6'
     - node_js: '8'
     - node_js: '10'
 
diff --git a/package-lock.json b/package-lock.json
index 04d35d8..08b1f0a 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -6776,6 +6776,12 @@
         }
       }
     },
+    "mock-fs": {
+      "version": "4.10.0",
+      "resolved": "https://registry.npmjs.org/mock-fs/-/mock-fs-4.10.0.tgz",
+      "integrity": "sha512-eBpLEjI6tK4RKK44BbUBQu89lrNh+5WeX3wf2U6Uwo6RtRGAQ77qvKeuuQh3lVXHF1aPndVww9VcjqmLThIdtA==",
+      "dev": true
+    },
     "moment": {
       "version": "2.24.0",
       "resolved": "https://registry.npmjs.org/moment/-/moment-2.24.0.tgz",
diff --git a/package.json b/package.json
index 30eb685..ed38212 100644
--- a/package.json
+++ b/package.json
@@ -50,6 +50,7 @@
     "eslint": "^5.16.0",
     "jest": "^24.8.0",
     "jest-cli": "^24.8.0",
+    "mock-fs": "^4.10.0",
     "serverless": "^1.43.0",
     "ts-jest": "^24.0.2",
     "typescript": "^3.4.5"
diff --git a/src/services/loginService.ts b/src/services/loginService.ts
index 05abd72..6eda1e9 100644
--- a/src/services/loginService.ts
+++ b/src/services/loginService.ts
@@ -1,4 +1,5 @@
 import { interactiveLoginWithAuthResponse, loginWithServicePrincipalSecretWithAuthResponse } from "@azure/ms-rest-nodeauth";
+import open from "open";
 
 export class AzureLoginService {
 


Test patch:
diff --git a/src/plugins/login/loginPlugin.test.ts b/src/plugins/login/loginPlugin.test.ts
index 2b31cc6..1444551 100644
--- a/src/plugins/login/loginPlugin.test.ts
+++ b/src/plugins/login/loginPlugin.test.ts
@@ -33,6 +33,7 @@ describe("Login Plugin", () => {
     AzureLoginService.servicePrincipalLogin = servicePrincipalLogin;
 
     const sls = MockFactory.createTestServerless();
+    delete sls.variables['azureCredentials']
     const options = MockFactory.createTestServerlessOptions();
     const plugin = new AzureLoginPlugin(sls, options);
 
@@ -56,6 +57,7 @@ describe("Login Plugin", () => {
     AzureLoginService.servicePrincipalLogin = servicePrincipalLogin;
     
     const sls = MockFactory.createTestServerless();
+    delete sls.variables['azureCredentials']
     const options = MockFactory.createTestServerlessOptions();
     const plugin = new AzureLoginPlugin(sls, options);
     await invokeHook(plugin, "before:package:initialize");
@@ -83,6 +85,7 @@ describe("Login Plugin", () => {
     AzureLoginService.servicePrincipalLogin = servicePrincipalLogin;
 
     const sls = MockFactory.createTestServerless();
+    delete sls.variables['azureCredentials']
     const options = MockFactory.createTestServerlessOptions();
     const plugin = new AzureLoginPlugin(sls, options);
     await invokeHook(plugin, "before:package:initialize");
@@ -109,6 +112,7 @@ describe("Login Plugin", () => {
     AzureLoginService.servicePrincipalLogin = servicePrincipalLogin;
     
     const sls = MockFactory.createTestServerless();
+    delete sls.variables['azureCredentials']
     const options = MockFactory.createTestServerlessOptions();
     const plugin = new AzureLoginPlugin(sls, options);
     await invokeHook(plugin, "before:package:initialize");
diff --git a/src/services/loginService.test.ts b/src/services/loginService.test.ts
new file mode 100644
index 0000000..2e7d8c5
--- /dev/null
+++ b/src/services/loginService.test.ts
@@ -0,0 +1,36 @@
+import { AzureLoginService } from "./loginService";
+jest.mock("open");
+import open from "open";
+
+jest.mock("@azure/ms-rest-nodeauth")
+import { interactiveLoginWithAuthResponse, loginWithServicePrincipalSecretWithAuthResponse } from "@azure/ms-rest-nodeauth";
+
+describe('Login Service', () => {
+  
+  it('logs in interactively', async () => {
+    // Ensure env variables are not set
+    delete process.env.azureSubId;
+    delete process.env.azureServicePrincipalClientId;
+    delete process.env.azureServicePrincipalPassword;
+    delete process.env.azureServicePrincipalTenantId;
+
+    await AzureLoginService.login();
+    expect(open).toBeCalledWith("https://microsoft.com/devicelogin")
+    expect(interactiveLoginWithAuthResponse).toBeCalled();
+  });
+
+  it('logs in with a service principal', async () => {
+    // Set environment variables
+    process.env.azureSubId = 'azureSubId';
+    process.env.azureServicePrincipalClientId = 'azureServicePrincipalClientId';
+    process.env.azureServicePrincipalPassword = 'azureServicePrincipalPassword';
+    process.env.azureServicePrincipalTenantId = 'azureServicePrincipalTenantId';
+
+    await AzureLoginService.login();
+    expect(loginWithServicePrincipalSecretWithAuthResponse).toBeCalledWith(
+      'azureServicePrincipalClientId',
+      'azureServicePrincipalPassword',
+      'azureServicePrincipalTenantId'
+    );
+  });
+});
\ No newline at end of file
diff --git a/src/services/resourceService.test.ts b/src/services/resourceService.test.ts
new file mode 100644
index 0000000..fc8777a
--- /dev/null
+++ b/src/services/resourceService.test.ts
@@ -0,0 +1,73 @@
+import { MockFactory } from '../test/mockFactory';
+import { ResourceService } from './resourceService';
+
+
+jest.mock('@azure/arm-resources')
+import { ResourceManagementClient } from '@azure/arm-resources';
+
+describe('Resource Service', () => {
+
+  beforeAll(() => {
+    ResourceManagementClient.prototype.resourceGroups = {
+      createOrUpdate: jest.fn(),
+      deleteMethod: jest.fn(),
+    } as any;
+
+    ResourceManagementClient.prototype.deployments = {
+      deleteMethod: jest.fn()
+    } as any;
+  });
+
+  it('throws error with empty credentials', () => {
+    const sls = MockFactory.createTestServerless();
+    delete sls.variables['azureCredentials']
+    const options = MockFactory.createTestServerlessOptions();
+    expect(() => new ResourceService(sls, options)).toThrowError('Azure Credentials has not been set in ResourceService')
+  });
+
+  it('initializes a resource service', () => {
+    const sls = MockFactory.createTestServerless();
+    const options = MockFactory.createTestServerlessOptions();
+    expect(() => new ResourceService(sls, options)).not.toThrowError();
+  });
+
+  it('deploys a resource group', () => {
+    const sls = MockFactory.createTestServerless();
+    const resourceGroup = 'myResourceGroup'
+    const location = 'West Us';
+    sls.service.provider['resourceGroup'] = resourceGroup
+    sls.service.provider['location'] = location;
+    sls.variables['azureCredentials'] = 'fake credentials'
+    const options = MockFactory.createTestServerlessOptions();
+    const service = new ResourceService(sls, options);
+    service.deployResourceGroup();
+    expect(ResourceManagementClient.prototype.resourceGroups.createOrUpdate)
+      .toBeCalledWith(resourceGroup, { location });
+  });
+
+  it('deletes a deployment', () => {
+    const sls = MockFactory.createTestServerless();
+    const resourceGroup = 'myResourceGroup';
+    const deploymentName = 'myDeployment';
+    sls.service.provider['resourceGroup'] = resourceGroup
+    sls.service.provider['deploymentName'] = deploymentName;
+    sls.variables['azureCredentials'] = 'fake credentials'
+    const options = MockFactory.createTestServerlessOptions();
+    const service = new ResourceService(sls, options);
+    service.deleteDeployment();
+    expect(ResourceManagementClient.prototype.deployments.deleteMethod)
+      .toBeCalledWith(resourceGroup, deploymentName);
+  });
+
+  it('deletes a resource group', () => {
+    const sls = MockFactory.createTestServerless();
+    const resourceGroup = 'myResourceGroup';
+    sls.service.provider['resourceGroup'] = resourceGroup
+    sls.variables['azureCredentials'] = 'fake credentials'
+    const options = MockFactory.createTestServerlessOptions();
+    const service = new ResourceService(sls, options);
+    service.deleteResourceGroup();
+    expect(ResourceManagementClient.prototype.resourceGroups.deleteMethod)
+      .toBeCalledWith(resourceGroup);
+  });
+});
\ No newline at end of file
diff --git a/src/test/mockFactory.ts b/src/test/mockFactory.ts
index c415baf..ae3e18c 100644
--- a/src/test/mockFactory.ts
+++ b/src/test/mockFactory.ts
@@ -4,14 +4,21 @@ import Service from "serverless/classes/Service";
 import Utils = require("serverless/classes/Utils");
 import PluginManager = require("serverless/classes/PluginManager");
 
+function getAttribute(object: any, prop: string, defaultValue: any): any {
+  if (object && object[prop]) {
+    return object[prop];
+  }
+  return defaultValue;
+}
+
 export class MockFactory {
   public static createTestServerless(config?: any): Serverless {
     const sls = new Serverless(config);
-    sls.service = MockFactory.createTestService();
-    sls.utils = MockFactory.createTestUtils();
-    sls.cli = MockFactory.createTestCli();
-    sls.pluginManager = MockFactory.createTestPluginManager();
-    sls.variables = {};
+    sls.service = getAttribute(config, "service", MockFactory.createTestService());
+    sls.utils = getAttribute(config, "utils", MockFactory.createTestUtils());
+    sls.cli = getAttribute(config, "cli", MockFactory.createTestCli());
+    sls.pluginManager = getAttribute(config, "pluginManager", MockFactory.createTestPluginManager());
+    sls.variables = getAttribute(config, "variables", MockFactory.createTestVariables());
     return sls;
   }
 
@@ -37,7 +44,15 @@ export class MockFactory {
     }
   }
 
-  private static createTestService(): Service {
+  public static createTestFunctionApp() {
+    return {
+      id: "App Id",
+      name: "App Name",
+      defaultHostName: "My Host Name"
+    }
+  }
+
+  public static createTestService(): Service {
     return {
       getAllFunctions: jest.fn(() => ["function1"]),
       getFunction: jest.fn(),
@@ -51,8 +66,28 @@ export class MockFactory {
       update: jest.fn(),
       validate: jest.fn(),
       custom: null,
-      provider: {} as any,
-    };
+      provider: MockFactory.createTestAzureServiceProvider(),
+      service: "serviceName",
+      artifact: "app.zip",
+    } as any as Service;
+  }
+
+  public static createTestAzureServiceProvider() {
+    return {
+      resourceGroup: "myResourceGroup",
+      deploymentName: "myDeploymentName",
+    }
+  }
+
+  public static createTestVariables() {
+    return {
+      azureCredentials: "credentials",
+      subscriptionId: "subId",
+    }
+  }
+
+  private getConfig(config: any, prop: string, defaultValue: any) {
+
   }
 
   private static createTestUtils(): Utils {