From 77a47c40ff760f4ade64397540b5bd2c6f57da02 Mon Sep 17 00:00:00 2001 From: Tanner Barlow Date: Wed, 29 May 2019 09:08:00 -0700 Subject: [PATCH 01/10] Login Service tests --- src/services/loginService.test.ts | 42 +++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100644 src/services/loginService.test.ts diff --git a/src/services/loginService.test.ts b/src/services/loginService.test.ts new file mode 100644 index 00000000..76147c50 --- /dev/null +++ b/src/services/loginService.test.ts @@ -0,0 +1,42 @@ +import { AzureLoginService } from "./loginService"; + +describe('Login Service', () => { + beforeAll(() => { + // Because the functions we use for authentication are exported + // as functions from their respective modules + // (open, interactiveLoginWithAuthResponse and + // loginWithServicePrincipalSecretWithAuthResponse), + // it is extremely difficult to mock their functionality. + // As a workaround, they have been placed in the thinnest possible + // functions within the Azure Login service, which we will + // use to make assertions on the functionality of the login service itself + AzureLoginService.interactiveLogin = jest.fn(); + AzureLoginService.servicePrincipalLogin = jest.fn(); + }); + + 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(AzureLoginService.interactiveLogin).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(AzureLoginService.servicePrincipalLogin).toBeCalledWith( + 'azureServicePrincipalClientId', + 'azureServicePrincipalPassword', + 'azureServicePrincipalTenantId' + ); + }); +}); \ No newline at end of file From 191285212e7e687b7a715c996e230f8a70dbe6fb Mon Sep 17 00:00:00 2001 From: Tanner Barlow Date: Wed, 29 May 2019 09:41:13 -0700 Subject: [PATCH 02/10] Resource service tests --- src/services/resourceService.test.ts | 73 ++++++++++++++++++++++++++++ 1 file changed, 73 insertions(+) create mode 100644 src/services/resourceService.test.ts diff --git a/src/services/resourceService.test.ts b/src/services/resourceService.test.ts new file mode 100644 index 00000000..93273e3b --- /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(); + 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(); + sls.variables['azureCredentials'] = 'fake credentials' + 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 From 6a53c066f945b33a69955ccbebb9192cc882939a Mon Sep 17 00:00:00 2001 From: Tanner Barlow Date: Wed, 29 May 2019 10:36:25 -0700 Subject: [PATCH 03/10] Starting to test Function app service --- package-lock.json | 6 ++ package.json | 1 + src/services/functionAppService.test.ts | 76 +++++++++++++++++++++++++ src/services/functionAppService.ts | 2 +- src/test/mockFactory.ts | 24 +++++--- 5 files changed, 100 insertions(+), 9 deletions(-) create mode 100644 src/services/functionAppService.test.ts diff --git a/package-lock.json b/package-lock.json index 04d35d85..08b1f0a9 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 30eb685d..ed382127 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/functionAppService.test.ts b/src/services/functionAppService.test.ts new file mode 100644 index 00000000..df8a129f --- /dev/null +++ b/src/services/functionAppService.test.ts @@ -0,0 +1,76 @@ +import mockfs from "mock-fs" +import { MockFactory } from "../test/mockFactory"; +import Serverless from "serverless"; +import { FunctionAppService } from "./functionAppService"; + +jest.mock("@azure/arm-resources") +import { ResourceManagementClient } from "@azure/arm-resources"; + +jest.mock("@azure/arm-appservice") +import { WebSiteManagementClient } from "@azure/arm-appservice"; + +jest.mock("axios") +import axios from "axios"; + +describe("Function App Service", () => { + + const defaultFunctionApp = MockFactory.createTestFunctionApp(); + const subId = "mySubId"; + const credentials = "myCredentials"; + const resourceGroup = "myResourceGroup"; + const serviceName = "myServiceName"; + const deploymentName = "myDeploymentName"; + const artifact = "app.zip"; + + beforeAll(() => { + WebSiteManagementClient.prototype.webApps = { + get: jest.fn(() => defaultFunctionApp) + } as any; + axios.prototype = jest.fn(); + }); + + afterAll(() => { + + }); + + + function createSls() { + const sls = MockFactory.createTestServerless(); + sls.variables["azureCredentials"] = credentials + sls.variables["subscriptionId"] = subId; + sls.service["service"] = serviceName; + sls.service.provider["resourceGroup"] = resourceGroup; + sls.service.provider["deploymentName"] = deploymentName; + sls.service["artifact"] = artifact; + return sls; + } + + function createOptions() { + const options = MockFactory.createTestServerlessOptions(); + return options; + } + + function createService(sls?: Serverless, options?: Serverless.Options) { + + return new FunctionAppService( + sls || createSls(), + options || createOptions() + ) + } + + it("get returns function app", async () => { + const service = createService(); + const result = await service.get(); + expect(WebSiteManagementClient.prototype.webApps.get) + .toBeCalledWith(resourceGroup, serviceName); + expect(result).toEqual(defaultFunctionApp) + }); + + it("gets master key", () => { + const service = createService(); + const key = service.getMasterKey(); + const calls = axios.prototype.mock.calls; + const a = 4; + fail(); + }); +}); \ No newline at end of file diff --git a/src/services/functionAppService.ts b/src/services/functionAppService.ts index f40e6204..c2fe5810 100644 --- a/src/services/functionAppService.ts +++ b/src/services/functionAppService.ts @@ -29,7 +29,7 @@ export class FunctionAppService extends BaseService { return response; } - public async getMasterKey(functionApp) { + public async getMasterKey(functionApp?) { functionApp = functionApp || await this.get(); const adminToken = await this.getAuthKey(functionApp); const keyUrl = `https://${functionApp.defaultHostName}/admin/host/systemkeys/_master`; diff --git a/src/test/mockFactory.ts b/src/test/mockFactory.ts index da96e62f..379cbf8b 100644 --- a/src/test/mockFactory.ts +++ b/src/test/mockFactory.ts @@ -1,8 +1,8 @@ -import { AuthResponse, LinkedSubscription, TokenCredentialsBase } from '@azure/ms-rest-nodeauth'; -import Serverless from 'serverless'; -import Service from 'serverless/classes/Service'; -import Utils = require('serverless/classes/Utils'); -import PluginManager = require('serverless/classes/PluginManager'); +import { AuthResponse, LinkedSubscription, TokenCredentialsBase } from "@azure/ms-rest-nodeauth"; +import Serverless from "serverless"; +import Service from "serverless/classes/Service"; +import Utils = require("serverless/classes/Utils"); +import PluginManager = require("serverless/classes/PluginManager"); export class MockFactory { public static createTestServerless(config?: any): Serverless { @@ -28,18 +28,26 @@ export class MockFactory { public static createTestAuthResponse(): AuthResponse { return { - credentials: 'credentials' as any as TokenCredentialsBase, + credentials: "credentials" as any as TokenCredentialsBase, subscriptions: [ { - id: 'azureSubId', + id: "azureSubId", } ] as any as LinkedSubscription[] } } + public static createTestFunctionApp() { + return { + id: "App Id", + name: "App Name", + defaultHostName: "My Host Name" + } + } + private static createTestService(): Service { return { - getAllFunctions: jest.fn(() => ['function1']), + getAllFunctions: jest.fn(() => ["function1"]), getFunction: jest.fn(), getAllEventsInFunction: jest.fn(), getAllFunctionsNames: jest.fn(), From 925d2e99a4c6f05e2c42956a357e2d2108b6846d Mon Sep 17 00:00:00 2001 From: Tanner Barlow Date: Wed, 29 May 2019 11:08:21 -0700 Subject: [PATCH 04/10] Delete function app service test from this branch --- src/services/functionAppService.test.ts | 76 ------------------------- 1 file changed, 76 deletions(-) delete mode 100644 src/services/functionAppService.test.ts diff --git a/src/services/functionAppService.test.ts b/src/services/functionAppService.test.ts deleted file mode 100644 index df8a129f..00000000 --- a/src/services/functionAppService.test.ts +++ /dev/null @@ -1,76 +0,0 @@ -import mockfs from "mock-fs" -import { MockFactory } from "../test/mockFactory"; -import Serverless from "serverless"; -import { FunctionAppService } from "./functionAppService"; - -jest.mock("@azure/arm-resources") -import { ResourceManagementClient } from "@azure/arm-resources"; - -jest.mock("@azure/arm-appservice") -import { WebSiteManagementClient } from "@azure/arm-appservice"; - -jest.mock("axios") -import axios from "axios"; - -describe("Function App Service", () => { - - const defaultFunctionApp = MockFactory.createTestFunctionApp(); - const subId = "mySubId"; - const credentials = "myCredentials"; - const resourceGroup = "myResourceGroup"; - const serviceName = "myServiceName"; - const deploymentName = "myDeploymentName"; - const artifact = "app.zip"; - - beforeAll(() => { - WebSiteManagementClient.prototype.webApps = { - get: jest.fn(() => defaultFunctionApp) - } as any; - axios.prototype = jest.fn(); - }); - - afterAll(() => { - - }); - - - function createSls() { - const sls = MockFactory.createTestServerless(); - sls.variables["azureCredentials"] = credentials - sls.variables["subscriptionId"] = subId; - sls.service["service"] = serviceName; - sls.service.provider["resourceGroup"] = resourceGroup; - sls.service.provider["deploymentName"] = deploymentName; - sls.service["artifact"] = artifact; - return sls; - } - - function createOptions() { - const options = MockFactory.createTestServerlessOptions(); - return options; - } - - function createService(sls?: Serverless, options?: Serverless.Options) { - - return new FunctionAppService( - sls || createSls(), - options || createOptions() - ) - } - - it("get returns function app", async () => { - const service = createService(); - const result = await service.get(); - expect(WebSiteManagementClient.prototype.webApps.get) - .toBeCalledWith(resourceGroup, serviceName); - expect(result).toEqual(defaultFunctionApp) - }); - - it("gets master key", () => { - const service = createService(); - const key = service.getMasterKey(); - const calls = axios.prototype.mock.calls; - const a = 4; - fail(); - }); -}); \ No newline at end of file From 658411aa85e2fe55c390cd34cad8ed0921ccdf0f Mon Sep 17 00:00:00 2001 From: Tanner Barlow Date: Wed, 29 May 2019 11:10:49 -0700 Subject: [PATCH 05/10] Remove function app references --- src/services/functionAppService.ts | 2 +- src/test/mockFactory.ts | 8 -------- 2 files changed, 1 insertion(+), 9 deletions(-) diff --git a/src/services/functionAppService.ts b/src/services/functionAppService.ts index c2fe5810..f40e6204 100644 --- a/src/services/functionAppService.ts +++ b/src/services/functionAppService.ts @@ -29,7 +29,7 @@ export class FunctionAppService extends BaseService { return response; } - public async getMasterKey(functionApp?) { + public async getMasterKey(functionApp) { functionApp = functionApp || await this.get(); const adminToken = await this.getAuthKey(functionApp); const keyUrl = `https://${functionApp.defaultHostName}/admin/host/systemkeys/_master`; diff --git a/src/test/mockFactory.ts b/src/test/mockFactory.ts index 379cbf8b..c415baf1 100644 --- a/src/test/mockFactory.ts +++ b/src/test/mockFactory.ts @@ -37,14 +37,6 @@ export class MockFactory { } } - public static createTestFunctionApp() { - return { - id: "App Id", - name: "App Name", - defaultHostName: "My Host Name" - } - } - private static createTestService(): Service { return { getAllFunctions: jest.fn(() => ["function1"]), From 2f16329f2e2c36438d4dc64d8e632d911bdf5cc7 Mon Sep 17 00:00:00 2001 From: Tanner Barlow Date: Wed, 29 May 2019 13:34:34 -0700 Subject: [PATCH 06/10] Refactored mock factory to include more out of the box --- src/plugins/login/loginPlugin.test.ts | 4 ++ src/services/functionAppService.test.ts | 52 +++++++------------------ src/services/resourceService.test.ts | 2 +- src/test/mockFactory.ts | 32 +++++++++++---- 4 files changed, 44 insertions(+), 46 deletions(-) diff --git a/src/plugins/login/loginPlugin.test.ts b/src/plugins/login/loginPlugin.test.ts index 86f9eea4..5a5cd231 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/functionAppService.test.ts b/src/services/functionAppService.test.ts index df8a129f..fecaaa67 100644 --- a/src/services/functionAppService.test.ts +++ b/src/services/functionAppService.test.ts @@ -14,17 +14,14 @@ import axios from "axios"; describe("Function App Service", () => { - const defaultFunctionApp = MockFactory.createTestFunctionApp(); - const subId = "mySubId"; - const credentials = "myCredentials"; - const resourceGroup = "myResourceGroup"; - const serviceName = "myServiceName"; - const deploymentName = "myDeploymentName"; - const artifact = "app.zip"; + const defaultApp = MockFactory.createTestFunctionApp(); + const slsService = MockFactory.createTestService(); + const variables = MockFactory.createTestVariables(); + const provider = MockFactory.createTestAzureServiceProvider(); beforeAll(() => { WebSiteManagementClient.prototype.webApps = { - get: jest.fn(() => defaultFunctionApp) + get: jest.fn(() => defaultApp) } as any; axios.prototype = jest.fn(); }); @@ -33,44 +30,25 @@ describe("Function App Service", () => { }); - - function createSls() { - const sls = MockFactory.createTestServerless(); - sls.variables["azureCredentials"] = credentials - sls.variables["subscriptionId"] = subId; - sls.service["service"] = serviceName; - sls.service.provider["resourceGroup"] = resourceGroup; - sls.service.provider["deploymentName"] = deploymentName; - sls.service["artifact"] = artifact; - return sls; - } - - function createOptions() { - const options = MockFactory.createTestServerlessOptions(); - return options; - } - function createService(sls?: Serverless, options?: Serverless.Options) { - return new FunctionAppService( - sls || createSls(), - options || createOptions() + sls || MockFactory.createTestServerless({ + service: slsService, + variables: variables, + }), + options || MockFactory.createTestServerlessOptions() ) } it("get returns function app", async () => { - const service = createService(); - const result = await service.get(); + const functionAppService = createService(); + const result = await functionAppService.get(); expect(WebSiteManagementClient.prototype.webApps.get) - .toBeCalledWith(resourceGroup, serviceName); - expect(result).toEqual(defaultFunctionApp) + .toBeCalledWith(provider.resourceGroup, slsService["service"]); + expect(result).toEqual(defaultApp) }); it("gets master key", () => { - const service = createService(); - const key = service.getMasterKey(); - const calls = axios.prototype.mock.calls; - const a = 4; - fail(); + }); }); \ No newline at end of file diff --git a/src/services/resourceService.test.ts b/src/services/resourceService.test.ts index 93273e3b..fc8777a4 100644 --- a/src/services/resourceService.test.ts +++ b/src/services/resourceService.test.ts @@ -20,13 +20,13 @@ describe('Resource Service', () => { 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(); - sls.variables['azureCredentials'] = 'fake credentials' const options = MockFactory.createTestServerlessOptions(); expect(() => new ResourceService(sls, options)).not.toThrowError(); }); diff --git a/src/test/mockFactory.ts b/src/test/mockFactory.ts index 379cbf8b..12c5bedc 100644 --- a/src/test/mockFactory.ts +++ b/src/test/mockFactory.ts @@ -7,11 +7,11 @@ import PluginManager = require("serverless/classes/PluginManager"); 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 = config && config.service || MockFactory.createTestService(); + sls.utils = config && config.utils || MockFactory.createTestUtils(); + sls.cli = config && config.cli || MockFactory.createTestCli(); + sls.pluginManager = config && config.pluginManager || MockFactory.createTestPluginManager(); + sls.variables = config && config.variables || MockFactory.createTestVariables(); return sls; } @@ -45,7 +45,7 @@ export class MockFactory { } } - private static createTestService(): Service { + public static createTestService(): Service { return { getAllFunctions: jest.fn(() => ["function1"]), getFunction: jest.fn(), @@ -59,8 +59,24 @@ 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 static createTestUtils(): Utils { From b77d34285f4e5f364183cee27d771a740520a274 Mon Sep 17 00:00:00 2001 From: Tanner Barlow Date: Wed, 29 May 2019 14:15:09 -0700 Subject: [PATCH 07/10] WIP --- src/services/baseService.ts | 2 +- src/services/functionAppService.test.ts | 58 ++++++++++++++++++++++--- 2 files changed, 52 insertions(+), 8 deletions(-) diff --git a/src/services/baseService.ts b/src/services/baseService.ts index cb7d9761..145590d5 100644 --- a/src/services/baseService.ts +++ b/src/services/baseService.ts @@ -24,7 +24,7 @@ export abstract class BaseService { } } - async sendApiRequest(method: string, relativeUrl: string, options: any = {}) { + protected async sendApiRequest(method: string, relativeUrl: string, options: any = {}) { const defaultHeaders = { 'Authorization': `Bearer ${this.credentials.tokenCache._entries[0].accessToken}` }; diff --git a/src/services/functionAppService.test.ts b/src/services/functionAppService.test.ts index fecaaa67..86e275c8 100644 --- a/src/services/functionAppService.test.ts +++ b/src/services/functionAppService.test.ts @@ -11,19 +11,28 @@ import { WebSiteManagementClient } from "@azure/arm-appservice"; jest.mock("axios") import axios from "axios"; +import { BaseService } from "./baseService"; describe("Function App Service", () => { - const defaultApp = MockFactory.createTestFunctionApp(); + const app = MockFactory.createTestFunctionApp(); const slsService = MockFactory.createTestService(); const variables = MockFactory.createTestVariables(); const provider = MockFactory.createTestAzureServiceProvider(); + const apiRequest = jest.fn((method, url) => { + return { + + }[method][url] + }); + const sendFile = jest.fn(); beforeAll(() => { WebSiteManagementClient.prototype.webApps = { - get: jest.fn(() => defaultApp) + get: jest.fn(() => app), + deleteFunction: jest.fn(), } as any; axios.prototype = jest.fn(); + (FunctionAppService.prototype as any).sendApiRequest = apiRequest; }); afterAll(() => { @@ -41,14 +50,49 @@ describe("Function App Service", () => { } it("get returns function app", async () => { - const functionAppService = createService(); - const result = await functionAppService.get(); + const service = createService(); + const result = await service.get(); expect(WebSiteManagementClient.prototype.webApps.get) .toBeCalledWith(provider.resourceGroup, slsService["service"]); - expect(result).toEqual(defaultApp) + expect(result).toEqual(app) + }); + + it("gets master key", async () => { + const service = createService(); + const masterKey = await service.getMasterKey(); + }); + + it("deletes function", async () => { + const service = createService(); + await service.deleteFunction(app.name); + expect(WebSiteManagementClient.prototype.webApps.deleteFunction).toBeCalledWith( + provider.resourceGroup, + slsService["service"], + app.name + ); + }); + + it("syncs triggers", async () => { + const service = createService(); + await service.syncTriggers(app); + expect(apiRequest).toBeCalledWith( + 'POST', + `https://management.azure.com${app.id}/syncfunctiontriggers?api-version=2016-08-01` + ) + }); + + it("cleans up", async () => { + const service = createService(); + await service.cleanUp(app); + }); + + it("lists functions", async () => { + const service = createService(); + service.listFunctions(app); }); - it("gets master key", () => { - + it("uploads functions", async () => { + const service = createService(); + await service.uploadFunctions(app); }); }); \ No newline at end of file From 577e7426c47c6fd7aec69337c6a82e1c9fbabe1d Mon Sep 17 00:00:00 2001 From: Tanner Barlow Date: Wed, 29 May 2019 15:01:55 -0700 Subject: [PATCH 08/10] Update serverless factory instantiation --- src/test/mockFactory.ts | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/src/test/mockFactory.ts b/src/test/mockFactory.ts index 12c5bedc..ae3e18cf 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 = config && config.service || MockFactory.createTestService(); - sls.utils = config && config.utils || MockFactory.createTestUtils(); - sls.cli = config && config.cli || MockFactory.createTestCli(); - sls.pluginManager = config && config.pluginManager || MockFactory.createTestPluginManager(); - sls.variables = config && config.variables || MockFactory.createTestVariables(); + 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; } @@ -79,6 +86,10 @@ export class MockFactory { } } + private getConfig(config: any, prop: string, defaultValue: any) { + + } + private static createTestUtils(): Utils { return { appendFileSync: jest.fn(), From 9da9e88815726ae354ff20492c949718c1c4ca49 Mon Sep 17 00:00:00 2001 From: Tanner Barlow Date: Thu, 30 May 2019 07:39:01 -0700 Subject: [PATCH 09/10] Update login service tests --- src/services/loginService.test.ts | 24 +++++++++--------------- src/services/loginService.ts | 1 + 2 files changed, 10 insertions(+), 15 deletions(-) diff --git a/src/services/loginService.test.ts b/src/services/loginService.test.ts index 76147c50..2e7d8c56 100644 --- a/src/services/loginService.test.ts +++ b/src/services/loginService.test.ts @@ -1,19 +1,12 @@ import { AzureLoginService } from "./loginService"; +jest.mock("open"); +import open from "open"; -describe('Login Service', () => { - beforeAll(() => { - // Because the functions we use for authentication are exported - // as functions from their respective modules - // (open, interactiveLoginWithAuthResponse and - // loginWithServicePrincipalSecretWithAuthResponse), - // it is extremely difficult to mock their functionality. - // As a workaround, they have been placed in the thinnest possible - // functions within the Azure Login service, which we will - // use to make assertions on the functionality of the login service itself - AzureLoginService.interactiveLogin = jest.fn(); - AzureLoginService.servicePrincipalLogin = jest.fn(); - }); +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; @@ -22,7 +15,8 @@ describe('Login Service', () => { delete process.env.azureServicePrincipalTenantId; await AzureLoginService.login(); - expect(AzureLoginService.interactiveLogin).toBeCalled(); + expect(open).toBeCalledWith("https://microsoft.com/devicelogin") + expect(interactiveLoginWithAuthResponse).toBeCalled(); }); it('logs in with a service principal', async () => { @@ -33,7 +27,7 @@ describe('Login Service', () => { process.env.azureServicePrincipalTenantId = 'azureServicePrincipalTenantId'; await AzureLoginService.login(); - expect(AzureLoginService.servicePrincipalLogin).toBeCalledWith( + expect(loginWithServicePrincipalSecretWithAuthResponse).toBeCalledWith( 'azureServicePrincipalClientId', 'azureServicePrincipalPassword', 'azureServicePrincipalTenantId' diff --git a/src/services/loginService.ts b/src/services/loginService.ts index 05abd722..6eda1e9f 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 { From cf5fa7c91bbd5ba92422ad32c451ee96fa87d859 Mon Sep 17 00:00:00 2001 From: Tanner Barlow Date: Thu, 30 May 2019 10:08:27 -0700 Subject: [PATCH 10/10] Remove node 6 --- .travis.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index bb3eba91..b7979fe3 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,7 +4,6 @@ os: linux matrix: include: - - node_js: '6' - node_js: '8' - node_js: '10'