Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore: deploy test #1223

Merged
merged 2 commits into from
Jun 20, 2024
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
9 changes: 9 additions & 0 deletions .github/workflows/terraform.yml
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,15 @@ jobs:
AWS_ECR_URI=${{secrets.DEV_AWS_ECR_URI}}
INCLUDE_DIGITAL_CREDENTIAL=true
INSTALL_SSO_CSS_GRAFANA=false

INCLUDE_BC_SERVICES_CARD=true
BCSC_INITIAL_ACCESS_TOKEN_DEV=${{secrets.BCSC_INITIAL_ACCESS_TOKEN_DEV}}
BCSC_INITIAL_ACCESS_TOKEN_TEST=${{secrets.BCSC_INITIAL_ACCESS_TOKEN_TEST}}
BCSC_INITIAL_ACCESS_TOKEN_PROD=${{secrets.BCSC_INITIAL_ACCESS_TOKEN_DPROD}
BCSC_REGISTRATION_BASE_URL_DEV=${{secrets.BCSC_REGISTRATION_BASE_URL_DEV}}
BCSC_REGISTRATION_BASE_URL_TEST=${{secrets.BCSC_REGISTRATION_BASE_URL_TEST}}
BCSC_REGISTRATION_BASE_URL_PROD=${{secrets.BCSC_REGISTRATION_BASE_URL_DPROD}

EOF

- name: Set env to production
Expand Down
8 changes: 5 additions & 3 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ services:
MAINTENANCE_MODE_ACTIVE: 'false'
SSO_REQUESTS_BACKEND_HOSTNAME: sso-requests
APP_URL: http://localhost:3000
INCLUDE_DIGITAL_CREDENTIAL: 'true'
depends_on:
- sso-requests
networks:
Expand Down Expand Up @@ -85,13 +86,14 @@ services:
GRAFANA_API_URL: https://sso-grafana-sandbox.apps.gold.devops.gov.bc.ca/api
GOLD_IP_ADDRESS: '142.34.229.4'
CYPRESS_RUNNER: 'true'
INCLUDE_DIGITAL_CREDENTIAL: 'true'
networks:
css-net:
aliases: [sso-requests-api]

dev-keycloak:
container_name: dev-keycloak
image: ghcr.io/bcgov/sso:7.6.39-build.1
image: ghcr.io/bcgov/sso:7.6.39-build.2
depends_on:
- sso-db
ports:
Expand All @@ -113,7 +115,7 @@ services:

test-keycloak:
container_name: test-keycloak
image: ghcr.io/bcgov/sso:7.6.39-build.1
image: ghcr.io/bcgov/sso:7.6.39-build.2
depends_on:
- sso-db
ports:
Expand All @@ -135,7 +137,7 @@ services:

prod-keycloak:
container_name: prod-keycloak
image: ghcr.io/bcgov/sso:7.6.39-build.1
image: ghcr.io/bcgov/sso:7.6.39-build.2
depends_on:
- sso-db
ports:
Expand Down
2 changes: 1 addition & 1 deletion lambda/__tests__/11.remove-inactive-users.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@ describe('users and teams', () => {
expect(deleteResponse.status).toEqual(200);
const user = await models.user.findOne({ where: { idir_userid: TEAM_ADMIN_IDIR_USERID_01 }, raw: true });
expect(user).toBeNull();
expect(emailList.length).toEqual(2);
expect(emailList.length).toEqual(1);
expect(emailList[0].subject).toEqual(template.subject);
expect(emailList[0].body).toEqual(template.body);
expect(emailList[0].to.length).toEqual(1);
Expand Down
25 changes: 1 addition & 24 deletions lambda/__tests__/14.verifiable-credential.test.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,11 @@
import { buildGitHubRequestData } from '@lambda-app/controllers/requests';
import { Status } from 'app/interfaces/types';
import app from './helpers/server';
import supertest from 'supertest';
import { APP_BASE_PATH } from './helpers/constants';
import { cleanUpDatabaseTables, createMockAuth, createMockSendEmail } from './helpers/utils';
import { TEAM_ADMIN_IDIR_EMAIL_01, TEAM_ADMIN_IDIR_USERID_01 } from './helpers/fixtures';
import { models } from '@lambda-shared/sequelize/models/models';
import { IntegrationData } from '@lambda-shared/interfaces';
import { DIT_EMAIL_ADDRESS } from '@lambda-shared/local';
import { updateIntegration } from './helpers/modules/integrations';
import { submitNewIntegration, updateIntegration } from './helpers/modules/integrations';
import { EMAILS } from '@lambda-shared/enums';

jest.mock('../app/src/authenticate');
Expand Down Expand Up @@ -111,26 +108,6 @@ const mockIntegration: IntegrationData = {
primaryEndUsers: [],
};

const submitNewIntegration = async (integration: IntegrationData) => {
const { projectName, projectLead, serviceType, usesTeam } = integration;
const {
body: { id },
} = await supertest(app)
.post(`${APP_BASE_PATH}/requests`)
.send({
projectName,
projectLead,
serviceType,
usesTeam,
})
.set('Accept', 'application/json');

return supertest(app)
.put(`${APP_BASE_PATH}/requests?submit=true`)
.send({ ...integration, id })
.set('Accept', 'application/json');
};

const OLD_ENV = process.env;
beforeEach(() => {
jest.resetModules();
Expand Down
26 changes: 21 additions & 5 deletions lambda/__tests__/17.run-queued-requests.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ describe('Request Queue', () => {
kcClientSpy.mockImplementation(() => Promise.resolve(true));
const emailResults = createMockSendEmail();

await createRequestQueueItem(1, formDataProd, ACTION_TYPES.CREATE as QUEUE_ACTION);
await createRequestQueueItem(1, formDataProd, ACTION_TYPES.CREATE as QUEUE_ACTION, 61);
const request = await generateRequest(formDataProd);
expect(request.status).toBe('draft');

Expand Down Expand Up @@ -72,12 +72,26 @@ describe('Request Queue', () => {
kcClientSpy.mockRestore();
});

it('Ignores queue items if created too recently', async () => {
const kcClientSpy = jest.spyOn(IntegrationModule, 'keycloakClient');
kcClientSpy.mockImplementation(() => Promise.resolve(true));
// Simulate one second old
await createRequestQueueItem(1, formDataProd, ACTION_TYPES.CREATE as QUEUE_ACTION, 1);

await handler();
expect(kcClientSpy).not.toHaveBeenCalled();

// Check the queue item is still there
const queueItems = await getQueueItems();
expect(queueItems.length).toBe(1);
});

it('Sends an update email if request was previously applied', async () => {
const kcClientSpy = jest.spyOn(IntegrationModule, 'keycloakClient');
kcClientSpy.mockImplementation(() => Promise.resolve(true));
const emailResults = createMockSendEmail();

await createRequestQueueItem(1, formDataProd, ACTION_TYPES.UPDATE as QUEUE_ACTION);
await createRequestQueueItem(1, formDataProd, ACTION_TYPES.UPDATE as QUEUE_ACTION, 61);
const request = await generateRequest(formDataProd);
// Insert a previous applied event
await createEvent({ eventCode: EVENTS.REQUEST_APPLY_SUCCESS, requestId: request.id });
Expand All @@ -94,7 +108,7 @@ describe('Request Queue', () => {
it('Keeps item in the queue if any environments fail and updates request status to failed', async () => {
const kcClientSpy = jest.spyOn(IntegrationModule, 'keycloakClient');

await createRequestQueueItem(1, formDataProd, ACTION_TYPES.CREATE as QUEUE_ACTION);
await createRequestQueueItem(1, formDataProd, ACTION_TYPES.CREATE as QUEUE_ACTION, 61);
const request = await generateRequest(formDataProd);

// Have one environment fail
Expand Down Expand Up @@ -162,7 +176,7 @@ describe('Delete and Update', () => {
find: jest.fn(() => Promise.resolve([archivedData])),
},
});
await createRequestQueueItem(1, archivedData, ACTION_TYPES.DELETE as QUEUE_ACTION);
await createRequestQueueItem(1, archivedData, ACTION_TYPES.DELETE as QUEUE_ACTION, 61);
await generateRequest(archivedData);
const emailResults = createMockSendEmail();

Expand All @@ -177,7 +191,7 @@ describe('Delete and Update', () => {
find: jest.fn(() => Promise.resolve([formDataProd])),
},
});
await createRequestQueueItem(1, formDataProd, ACTION_TYPES.CREATE as QUEUE_ACTION);
await createRequestQueueItem(1, formDataProd, ACTION_TYPES.CREATE as QUEUE_ACTION, 61);
await generateRequest(formDataProd);

await handler();
Expand All @@ -197,6 +211,7 @@ describe('Delete and Update', () => {
request.id,
{ ...formDataProd, existingClientId: existingClientId },
ACTION_TYPES.CREATE as QUEUE_ACTION,
61,
);

await handler();
Expand All @@ -215,6 +230,7 @@ describe('Delete and Update', () => {
request.id,
{ ...formDataProd, existingClientId: '' },
ACTION_TYPES.CREATE as QUEUE_ACTION,
61,
);
await handler();

Expand Down
206 changes: 206 additions & 0 deletions lambda/__tests__/20.bcsc.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,206 @@
import { cleanUpDatabaseTables, createMockAuth } from './helpers/utils';
import * as IdpModule from '@lambda-app/keycloak/idp';
import * as ClientScopeModule from '@lambda-app/keycloak/clientScopes';
import { buildGitHubRequestData, createBCSCIntegration } from '@lambda-app/controllers/requests';
import { TEAM_ADMIN_IDIR_EMAIL_01, TEAM_ADMIN_IDIR_USERID_01, formDataProd } from './helpers/fixtures';
import { bcscIdpMappers } from '@lambda-app/utils/constants';
import { submitNewIntegration } from './helpers/modules/integrations';
import { IntegrationData } from '@lambda-shared/interfaces';

jest.mock('@lambda-app/controllers/bc-services-card', () => {
return {
getPrivacyZones: jest.fn(() => Promise.resolve([{ privacy_zone_uri: 'zone', privacy_zone_name: 'zone' }])),
getAttributes: jest.fn(() => Promise.resolve([{ name: 'attr' }])),
};
});

jest.mock('../app/src/authenticate');

jest.mock('@lambda-app/keycloak/adminClient', () => {
return {
getAdminClient: jest.fn(() => Promise.resolve({})),
};
});

jest.mock('@lambda-shared/utils/ches');
jest.mock('@lambda-app/bcsc/client', () => {
const original = jest.requireActual('@lambda-app/bcsc/client');
return {
...original,
createBCSCClient: jest.fn(() =>
Promise.resolve({
data: {
client_secret: 'secret',
client_id: 'client_id',
registration_access_token: 'token',
},
}),
),
updateBCSCClient: jest.fn(() =>
Promise.resolve({
data: {
client_secret: 'secret',
client_id: 'client_id',
registration_access_token: 'token',
},
}),
),
};
});

const OLD_ENV = process.env;
beforeEach(() => {
jest.resetModules();
process.env = { ...OLD_ENV };
});

afterAll(() => {
process.env = OLD_ENV;
});

describe('BCSC', () => {
const spies = {
getIdp: null,
getIdpMappers: null,
createIdpMapper: null,
getClientScope: null,
getClientScopeMapper: null,
createClientScopeMapper: null,
createClientScope: null,
createIdp: null,
};

beforeEach(() => {
spies.getIdp = jest.spyOn(IdpModule, 'getIdp');
spies.getIdp.mockImplementation(() => Promise.resolve(null));

spies.getIdpMappers = jest.spyOn(IdpModule, 'getIdpMappers');
spies.getIdpMappers.mockImplementation(() => Promise.resolve([]));
spies.createIdpMapper = jest.spyOn(IdpModule, 'createIdpMapper');
spies.createIdpMapper.mockImplementation(() => Promise.resolve(null));
spies.getClientScope = jest.spyOn(ClientScopeModule, 'getClientScope');
spies.getClientScope.mockImplementation(() => Promise.resolve({ id: '1' }));
spies.getClientScopeMapper = jest.spyOn(ClientScopeModule, 'getClientScopeMapper');
spies.getClientScopeMapper.mockImplementation(() => Promise.resolve(null));
spies.createClientScopeMapper = jest.spyOn(ClientScopeModule, 'createClientScopeMapper');
spies.createClientScopeMapper.mockImplementation(() => Promise.resolve(null));
spies.createClientScope = jest.spyOn(ClientScopeModule, 'createClientScope');
spies.createClientScope.mockImplementation(() => Promise.resolve({ id: 1, name: 'name' }));
spies.createIdp = jest.spyOn(IdpModule, 'createIdp');
spies.createIdp.mockImplementation(() => Promise.resolve(null));
});

afterAll(async () => {
await cleanUpDatabaseTables();
});

afterEach(() => {
jest.clearAllMocks();
});

it('Only creates the idp if not found', async () => {
spies.getIdp.mockImplementation(() => Promise.resolve(null));
await createBCSCIntegration('dev', bcscProdIntegration, 1);
expect(spies.createIdp).toHaveBeenCalled();

jest.clearAllMocks();

spies.getIdp.mockImplementation(() => Promise.resolve({}));
await createBCSCIntegration('dev', bcscProdIntegration, 1);
expect(spies.createIdp).not.toHaveBeenCalled();
});

it('Only creates the idp mappers if not found', async () => {
// Return all requiredMappers
spies.getIdpMappers.mockImplementation(() => Promise.resolve(bcscIdpMappers));
await createBCSCIntegration('dev', bcscProdIntegration, 1);
expect(spies.createIdpMapper).not.toHaveBeenCalled();

jest.clearAllMocks();

spies.getIdpMappers.mockImplementation(() => Promise.resolve([]));
await createBCSCIntegration('dev', bcscProdIntegration, 1);
expect(spies.createIdpMapper).toHaveBeenCalledTimes(bcscIdpMappers.length);
});

it('Only creates the client scope if not found', async () => {
// Return all requiredMappers
spies.getClientScope.mockImplementation(() => Promise.resolve(null));
await createBCSCIntegration('dev', bcscProdIntegration, 1);
expect(spies.createClientScope).toHaveBeenCalled();

jest.clearAllMocks();

spies.getClientScope.mockImplementation(() => Promise.resolve({ id: '1', name: 'name' }));
await createBCSCIntegration('dev', bcscProdIntegration, 1);
expect(spies.createClientScope).not.toHaveBeenCalled();
});

it('Only creates the client scope mappers if not found', async () => {
// Return all requiredMappers
spies.getClientScopeMapper.mockImplementation(() => Promise.resolve(null));
await createBCSCIntegration('dev', bcscProdIntegration, 1);
expect(spies.createClientScopeMapper).toHaveBeenCalledTimes(1);

jest.clearAllMocks();

spies.getClientScopeMapper.mockImplementation(() => Promise.resolve(bcscProdIntegration.bcscAttributes));
await createBCSCIntegration('dev', bcscProdIntegration, 1);
expect(spies.createClientScopeMapper).not.toHaveBeenCalled();
});
});

const bcscProdIntegration: IntegrationData = {
...formDataProd,
devIdps: ['bcservicescard', 'idir'],
bcscPrivacyZone: 'zone',
bcscAttributes: ['attr'],
primaryEndUsers: [],
devHomePageUri: 'https://example.com',
testHomePageUri: 'https://example.com',
prodHomePageUri: 'https://example.com',
};

describe('Feature flag', () => {
beforeAll(async () => {
createMockAuth(TEAM_ADMIN_IDIR_USERID_01, TEAM_ADMIN_IDIR_EMAIL_01);
});

it('Does not allow bc services card as an IDP if feature flag is not included in env vars', async () => {
process.env.INCLUDE_BC_SERVICES_CARD = undefined;
const result = await submitNewIntegration(bcscProdIntegration);
expect(result.status).toBe(422);
});

it('Does not allow bc services card as an IDP if feature flag is set but not true', async () => {
process.env.INCLUDE_BC_SERVICES_CARD = 'false';
const result = await submitNewIntegration(bcscProdIntegration);
expect(result.status).toBe(422);
});

it('Allows bc services card as an IDP if feature flag is set to true', async () => {
process.env.INCLUDE_BC_SERVICES_CARD = 'true';
const result = await submitNewIntegration(bcscProdIntegration);
expect(result.status).toBe(200);
});
});

describe('Build Github Dispatch', () => {
it('Removes bc services card from production IDP list if not approved yet, but keeps it in dev and test', () => {
const processedIntegration = buildGitHubRequestData(bcscProdIntegration);
expect(processedIntegration.prodIdps.includes('bcservicescard')).toBe(false);

// Leaves other idp alone
expect(processedIntegration.prodIdps.includes('idir')).toBe(true);

// Keeps VC in dev and test
expect(processedIntegration.testIdps.includes('bcservicescard')).toBe(true);
expect(processedIntegration.devIdps.includes('bcservicescard')).toBe(true);
});

it('Keeps bc services card in production IDP list if approved', () => {
const approvedIntegration = { ...bcscProdIntegration, bcServicesCardApproved: true };
const processedIntegration = buildGitHubRequestData(approvedIntegration);
expect(processedIntegration.prodIdps.includes('bcservicescard')).toBe(true);
});
});
Loading
Loading