This repository was archived by the owner on Dec 9, 2024. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 160
test: Unit tests for deploy, login, package, delete and APIM plugins #148
Merged
Merged
Changes from all commits
Commits
Show all changes
5 commits
Select commit
Hold shift + click to select a range
069b7a6
test: Unit tests for deploy and login plugins
tbarlow12 0a0e9e8
Updates from conflicts
tbarlow12 bbd9e83
Lint fixes
tbarlow12 c90e30d
test: Remove, package and APIM tests (#150)
tbarlow12 33c5a46
Remove console log from login plugin
tbarlow12 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file was deleted.
Oops, something went wrong.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,25 @@ | ||
| import { MockFactory } from "../../test/mockFactory"; | ||
| import { invokeHook } from "../../test/utils"; | ||
| import { AzureApimFunctionPlugin } from './apimFunctionPlugin'; | ||
|
|
||
| jest.mock('../../services/apimService'); | ||
| import { ApimService } from '../../services/apimService'; | ||
|
|
||
| describe('APIM Function Plugin', () => { | ||
| it('calls deploy function', async () => { | ||
| const deployFunction = jest.fn(); | ||
|
|
||
| ApimService.prototype.deployFunction = deployFunction; | ||
|
|
||
| const sls = MockFactory.createTestServerless(); | ||
| sls.service.provider['apim'] = 'apim config' | ||
| const options = MockFactory.createTestServerlessOptions(); | ||
| const plugin = new AzureApimFunctionPlugin(sls, options); | ||
|
|
||
| await invokeHook(plugin, 'after:deploy:function:deploy'); | ||
|
|
||
| expect(sls.cli.log).toBeCalledWith('Starting APIM function deployment') | ||
| expect(deployFunction).toBeCalled(); | ||
| expect(sls.cli.log).lastCalledWith('Finished APIM function deployment') | ||
| }); | ||
| }); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,33 @@ | ||
| import { MockFactory } from "../../test/mockFactory"; | ||
| import { invokeHook } from "../../test/utils"; | ||
| import { AzureDeployPlugin } from "./azureDeployPlugin"; | ||
|
|
||
| jest.mock("../../services/functionAppService"); | ||
| import { FunctionAppService } from "../../services/functionAppService"; | ||
|
|
||
| jest.mock("../../services/resourceService"); | ||
| import { ResourceService } from "../../services/resourceService"; | ||
|
|
||
| describe('Deploy plugin', () => { | ||
|
|
||
| it('calls deploy hook', async () => { | ||
| const deployResourceGroup = jest.fn(); | ||
| const functionAppStub = "Function App Stub"; | ||
| const deploy = jest.fn(() => Promise.resolve(functionAppStub)); | ||
| const uploadFunctions = jest.fn(); | ||
|
|
||
| ResourceService.prototype.deployResourceGroup = deployResourceGroup | ||
| FunctionAppService.prototype.deploy = deploy | ||
| FunctionAppService.prototype.uploadFunctions = uploadFunctions | ||
|
|
||
| const sls = MockFactory.createTestServerless(); | ||
| const options = MockFactory.createTestServerlessOptions(); | ||
| const plugin = new AzureDeployPlugin(sls, options); | ||
|
|
||
| await invokeHook(plugin, 'deploy:deploy'); | ||
|
|
||
| expect(deployResourceGroup).toBeCalled(); | ||
| expect(deploy).toBeCalled(); | ||
| expect(uploadFunctions).toBeCalledWith(functionAppStub); | ||
| }); | ||
| }); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,123 @@ | ||
| import { MockFactory } from "../../test/mockFactory"; | ||
| import { invokeHook } from "../../test/utils"; | ||
| import { AzureLoginPlugin } from "./loginPlugin"; | ||
| import { AzureLoginService } from "../../services/loginService"; | ||
|
|
||
| describe('Login Plugin', () => { | ||
|
|
||
| const authResponse = MockFactory.createTestAuthResponse(); | ||
|
|
||
| it('returns if azure credentials are set', async () => { | ||
| const interactiveLogin = jest.fn(() => Promise.resolve(authResponse)); | ||
| const servicePrincipalLogin = jest.fn(() => Promise.resolve(authResponse)); | ||
|
|
||
| AzureLoginService.interactiveLogin = interactiveLogin; | ||
| AzureLoginService.servicePrincipalLogin = servicePrincipalLogin; | ||
|
|
||
| const sls = MockFactory.createTestServerless(); | ||
| sls.variables['azureCredentials'] = 'credentials'; | ||
| const options = MockFactory.createTestServerlessOptions(); | ||
| const plugin = new AzureLoginPlugin(sls, options); | ||
|
|
||
| await invokeHook(plugin, 'before:package:initialize'); | ||
|
|
||
| expect(interactiveLogin).not.toBeCalled(); | ||
| expect(servicePrincipalLogin).not.toBeCalled(); | ||
| }); | ||
|
|
||
| it('calls login if azure credentials are not set', async () => { | ||
| const interactiveLogin = jest.fn(() => Promise.resolve(authResponse)); | ||
| const servicePrincipalLogin = jest.fn(() => Promise.resolve(authResponse)); | ||
|
|
||
| AzureLoginService.interactiveLogin = interactiveLogin; | ||
| AzureLoginService.servicePrincipalLogin = servicePrincipalLogin; | ||
|
|
||
| const sls = MockFactory.createTestServerless(); | ||
| const options = MockFactory.createTestServerlessOptions(); | ||
| const plugin = new AzureLoginPlugin(sls, options); | ||
|
|
||
| await invokeHook(plugin, 'before:package:initialize'); | ||
|
|
||
| expect(interactiveLogin).toBeCalled(); | ||
| expect(servicePrincipalLogin).not.toBeCalled(); | ||
| }); | ||
|
|
||
| it('calls service principal login if environment variables are set', async () => { | ||
|
|
||
| process.env.azureSubId = 'azureSubId'; | ||
| process.env.azureServicePrincipalClientId = 'azureServicePrincipalClientId'; | ||
| process.env.azureServicePrincipalPassword = 'azureServicePrincipalPassword'; | ||
| process.env.azureServicePrincipalTenantId = 'azureServicePrincipalTenantId'; | ||
|
|
||
| const interactiveLogin = jest.fn(() => Promise.resolve(authResponse)); | ||
| const servicePrincipalLogin = jest.fn(() => Promise.resolve(authResponse)); | ||
|
|
||
| AzureLoginService.interactiveLogin = interactiveLogin; | ||
| AzureLoginService.servicePrincipalLogin = servicePrincipalLogin; | ||
|
|
||
| const sls = MockFactory.createTestServerless(); | ||
| const options = MockFactory.createTestServerlessOptions(); | ||
| const plugin = new AzureLoginPlugin(sls, options); | ||
| await invokeHook(plugin, 'before:package:initialize'); | ||
| expect(servicePrincipalLogin).toBeCalledWith( | ||
| 'azureServicePrincipalClientId', | ||
| 'azureServicePrincipalPassword', | ||
| 'azureServicePrincipalTenantId' | ||
| ) | ||
| expect(interactiveLogin).not.toBeCalled(); | ||
|
|
||
| expect(sls.variables['azureCredentials']).toEqual(authResponse.credentials); | ||
| expect(sls.variables['subscriptionId']).toEqual('azureSubId'); | ||
| }); | ||
|
|
||
| it('calls interactive login if environment variables are not set', async () => { | ||
| delete process.env.azureSubId; | ||
| delete process.env.azureServicePrincipalClientId; | ||
| delete process.env.azureServicePrincipalPassword; | ||
| delete process.env.azureServicePrincipalTenantId; | ||
|
|
||
| const interactiveLogin = jest.fn(() => Promise.resolve(authResponse)); | ||
| const servicePrincipalLogin = jest.fn(() => Promise.resolve(authResponse)); | ||
|
|
||
| AzureLoginService.interactiveLogin = interactiveLogin; | ||
| AzureLoginService.servicePrincipalLogin = servicePrincipalLogin; | ||
|
|
||
| const sls = MockFactory.createTestServerless(); | ||
| const options = MockFactory.createTestServerlessOptions(); | ||
| const plugin = new AzureLoginPlugin(sls, options); | ||
| await invokeHook(plugin, 'before:package:initialize'); | ||
| expect(servicePrincipalLogin).not.toBeCalled(); | ||
| expect(interactiveLogin).toBeCalled(); | ||
|
|
||
| expect(sls.variables['azureCredentials']).toEqual(authResponse.credentials); | ||
| expect(sls.variables['subscriptionId']).toEqual('azureSubId'); | ||
| }); | ||
|
|
||
| it('logs an error from authentication', async () => { | ||
| process.env.azureSubId = 'azureSubId'; | ||
| process.env.azureServicePrincipalClientId = 'azureServicePrincipalClientId'; | ||
| process.env.azureServicePrincipalPassword = 'azureServicePrincipalPassword'; | ||
| process.env.azureServicePrincipalTenantId = 'azureServicePrincipalTenantId'; | ||
|
|
||
| const interactiveLogin = jest.fn(() => Promise.resolve(authResponse)); | ||
| const errorMessage = 'This is my error message'; | ||
| const servicePrincipalLogin = jest.fn(() => { | ||
| throw new Error(errorMessage); | ||
| }); | ||
|
|
||
| AzureLoginService.interactiveLogin = interactiveLogin; | ||
| AzureLoginService.servicePrincipalLogin = servicePrincipalLogin; | ||
|
|
||
| const sls = MockFactory.createTestServerless(); | ||
| const options = MockFactory.createTestServerlessOptions(); | ||
| const plugin = new AzureLoginPlugin(sls, options); | ||
| await invokeHook(plugin, 'before:package:initialize'); | ||
| expect(servicePrincipalLogin).toBeCalledWith( | ||
| 'azureServicePrincipalClientId', | ||
| 'azureServicePrincipalPassword', | ||
| 'azureServicePrincipalTenantId' | ||
| ) | ||
| expect(interactiveLogin).not.toBeCalled(); | ||
| expect(sls.cli.log).lastCalledWith(`Error: ${errorMessage}`) | ||
| }); | ||
| }) | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,7 +1,6 @@ | ||
| import open from 'open'; | ||
| import { interactiveLoginWithAuthResponse, loginWithServicePrincipalSecretWithAuthResponse } from '@azure/ms-rest-nodeauth'; | ||
| import Serverless from 'serverless'; | ||
| import AzureProvider from '../../provider/azureProvider'; | ||
| import { AzureLoginService } from '../../services/loginService'; | ||
|
|
||
| export class AzureLoginPlugin { | ||
| private provider: AzureProvider; | ||
|
|
@@ -23,32 +22,16 @@ export class AzureLoginPlugin { | |
|
|
||
| this.serverless.cli.log('Logging into Azure'); | ||
|
|
||
| let authResult = null; | ||
|
|
||
| const subscriptionId = process.env.azureSubId; | ||
| const clientId = process.env.azureServicePrincipalClientId; | ||
| const secret = process.env.azureServicePrincipalPassword; | ||
| const tenantId = process.env.azureServicePrincipalTenantId; | ||
|
|
||
| try { | ||
| if (subscriptionId && clientId && secret && tenantId) { | ||
| authResult = await loginWithServicePrincipalSecretWithAuthResponse(clientId, secret, tenantId); | ||
| } | ||
| else { | ||
| await open('https://microsoft.com/devicelogin'); | ||
| authResult = await interactiveLoginWithAuthResponse(); | ||
| } | ||
|
|
||
| // TODO: This is temporary until the azure provider goes away | ||
| this.provider.credentials = authResult.credentials; | ||
|
|
||
| this.serverless.variables['azureAccessToken'] = authResult.credentials.tokenCache._entries[0].accessToken; | ||
| const authResult = await AzureLoginService.login(); | ||
| this.serverless.variables['azureCredentials'] = authResult.credentials; | ||
| this.serverless.variables['subscriptionId'] = authResult.subscriptionId || subscriptionId; | ||
| // Use environment variable for sub ID or use the first subscription in the list (service principal can | ||
| // have access to more than one subscription) | ||
| this.serverless.variables['subscriptionId'] = process.env.azureSubId || authResult.subscriptions[0].id; | ||
| } | ||
| catch (e) { | ||
| this.serverless.cli.log('Error logging into azure'); | ||
| this.serverless.cli.log(e); | ||
| this.serverless.cli.log(`${e}`); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. we should throw error and short circuit when login failed. |
||
| } | ||
| } | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,31 @@ | ||
| import { MockFactory } from "../../test/mockFactory"; | ||
| import { invokeHook } from "../../test/utils"; | ||
| import { AzurePackage } from "./azurePackage" | ||
|
|
||
| jest.mock('../../shared/bindings'); | ||
| import { BindingUtils } from '../../shared/bindings'; | ||
| jest.mock('../../shared/utils'); | ||
| import { Utils, FunctionMetadata } from '../../shared/utils'; | ||
|
|
||
| describe('Azure Package Plugin', () => { | ||
| it('sets up provider configuration', async () => { | ||
| const metadata = 'metadata'; | ||
| const functionName = 'function1'; | ||
|
|
||
| const getFunctionMetaDataFn = jest.fn(() => metadata as any as FunctionMetadata); | ||
| const createEventsBindingsFn = jest.fn(); | ||
|
|
||
| Utils.getFunctionMetaData = getFunctionMetaDataFn | ||
| BindingUtils.createEventsBindings = createEventsBindingsFn | ||
|
|
||
| const sls = MockFactory.createTestServerless(); | ||
| const options = MockFactory.createTestServerlessOptions(); | ||
| const plugin = new AzurePackage(sls, options); | ||
|
|
||
| await invokeHook(plugin, 'package:setupProviderConfiguration'); | ||
|
|
||
| expect(sls.cli.log).toBeCalledWith('Building Azure Events Hooks'); | ||
| expect(getFunctionMetaDataFn).toBeCalledWith(functionName, sls); | ||
| expect(createEventsBindingsFn).toBeCalledWith(sls.config.servicePath, functionName, metadata); | ||
| }); | ||
| }); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,26 @@ | ||
| import { MockFactory } from "../../test/mockFactory"; | ||
| import { invokeHook } from "../../test/utils"; | ||
| import { AzureRemove } from "./azureRemove"; | ||
|
|
||
| jest.mock("../../services/resourceService"); | ||
| import { ResourceService } from "../../services/resourceService"; | ||
|
|
||
| describe('Remove Plugin', () => { | ||
| it('calls remove hook', async () => { | ||
| const deleteDeployment = jest.fn(); | ||
| const deleteResourceGroup = jest.fn(); | ||
|
|
||
| ResourceService.prototype.deleteDeployment = deleteDeployment; | ||
| ResourceService.prototype.deleteResourceGroup = deleteResourceGroup; | ||
|
|
||
| const sls = MockFactory.createTestServerless(); | ||
| const options = MockFactory.createTestServerlessOptions(); | ||
| const plugin = new AzureRemove(sls, options); | ||
|
|
||
| await invokeHook(plugin, 'remove:remove'); | ||
|
|
||
| expect(deleteDeployment).toBeCalled(); | ||
| expect(deleteResourceGroup).toBeCalled(); | ||
| expect(sls.cli.log).toBeCalledWith('Service successfully removed'); | ||
| }); | ||
| }); |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
lots of duplicate code for setting up AzureLoginService. can we move some of them to a
beforeEach?