Skip to content
This repository was archived by the owner on Dec 9, 2024. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 0 additions & 6 deletions src/index.test.ts

This file was deleted.

25 changes: 25 additions & 0 deletions src/plugins/apim/apimFunctionPlugin.test.ts
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')
});
});
43 changes: 43 additions & 0 deletions src/plugins/apim/apimServicePlugin.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
import Serverless from 'serverless';
import { MockFactory } from "../../test/mockFactory";
import { invokeHook } from "../../test/utils";
import { AzureApimServicePlugin } from './apimServicePlugin';

jest.mock('../../services/apimService');
import { ApimService } from '../../services/apimService';

describe('APIM Service Plugin', () => {
it('is defined', () => {
expect(AzureApimServicePlugin).toBeDefined();
Expand All @@ -16,4 +21,42 @@ describe('APIM Service Plugin', () => {

expect(plugin).not.toBeNull();
});

it('calls deploy API and deploy functions', async () => {
const deployApi = jest.fn();
const deployFunctions = jest.fn();

ApimService.prototype.deployApi = deployApi;
ApimService.prototype.deployFunctions = deployFunctions;

const sls = MockFactory.createTestServerless();
sls.service.provider['apim'] = 'apim config'
const options = MockFactory.createTestServerlessOptions();
const plugin = new AzureApimServicePlugin(sls, options);

await invokeHook(plugin, 'after:deploy:deploy');

expect(sls.cli.log).toBeCalledWith('Starting APIM service deployment')
expect(deployApi).toBeCalled();
expect(deployFunctions).toBeCalled();
expect(sls.cli.log).lastCalledWith('Finished APIM service deployment')
});

it('does not call deploy API or deploy functions when "apim" not included in config', async () => {
const deployApi = jest.fn();
const deployFunctions = jest.fn();

ApimService.prototype.deployApi = deployApi;
ApimService.prototype.deployFunctions = deployFunctions;

const sls = MockFactory.createTestServerless();
const options = MockFactory.createTestServerlessOptions();
const plugin = new AzureApimServicePlugin(sls, options);

await invokeHook(plugin, 'after:deploy:deploy');

expect(sls.cli.log).not.toBeCalled()
expect(deployApi).not.toBeCalled();
expect(deployFunctions).not.toBeCalled();
});
});
33 changes: 33 additions & 0 deletions src/plugins/deploy/azureDeployPlugin.test.ts
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);
});
});
123 changes: 123 additions & 0 deletions src/plugins/login/loginPlugin.test.ts
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();
Copy link
Contributor

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?


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}`)
});
})
29 changes: 6 additions & 23 deletions src/plugins/login/loginPlugin.ts
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;
Expand All @@ -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}`);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we should throw error and short circuit when login failed.

}
}
}
31 changes: 31 additions & 0 deletions src/plugins/package/azurePackage.test.ts
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);
});
});
8 changes: 4 additions & 4 deletions src/plugins/package/azurePackage.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@

import Serverless from 'serverless';
import AzureProvider from '../../provider/azureProvider';
import { createEventsBindings } from '../../shared/bindings';
import { getFunctionMetaData } from '../../shared/utils';
import { BindingUtils } from '../../shared/bindings';
import { Utils } from '../../shared/utils';

export class AzurePackage {
provider: AzureProvider
Expand All @@ -19,9 +19,9 @@ export class AzurePackage {

const createEventsPromises = this.serverless.service.getAllFunctions()
.map((functionName) => {
const metaData = getFunctionMetaData(functionName, this.serverless);
const metaData = Utils.getFunctionMetaData(functionName, this.serverless);

return createEventsBindings(this.serverless.config.servicePath, functionName, metaData);
return BindingUtils.createEventsBindings(this.serverless.config.servicePath, functionName, metaData);
});

return Promise.all(createEventsPromises);
Expand Down
26 changes: 26 additions & 0 deletions src/plugins/remove/azureRemove.test.ts
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');
});
});
Loading