Skip to content
This repository has been archived by the owner on Apr 13, 2023. It is now read-only.

test: Add tests for RBAC Auth #229

Merged
merged 7 commits into from
Mar 4, 2021
Merged
Show file tree
Hide file tree
Changes from 4 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
95 changes: 42 additions & 53 deletions integration-tests/bulkExport.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import BulkExportTestHelper, { ExportStatusOutput } from './bulkExportTestHelper
import { getFhirClient } from './utils';
rsmayda marked this conversation as resolved.
Show resolved Hide resolved

const FIVE_MINUTES_IN_MS = 5 * 60 * 1000;
jest.setTimeout(FIVE_MINUTES_IN_MS);

describe('Bulk Export', () => {
let bulkExportTestHelper: BulkExportTestHelper;
Expand All @@ -16,63 +17,51 @@ describe('Bulk Export', () => {
bulkExportTestHelper = new BulkExportTestHelper(fhirUserAxios);
});

test(
'Successfully export all data added to DB after currentTime',
async () => {
// BUILD
const oldCreatedResourceBundleResponse = await bulkExportTestHelper.sendCreateResourcesRequest();
const resTypToResNotExpectedInExport = bulkExportTestHelper.getResources(oldCreatedResourceBundleResponse);
const currentTime = new Date();
const newCreatedResourceBundleResponse = await bulkExportTestHelper.sendCreateResourcesRequest();
const resTypToResExpectedInExport = bulkExportTestHelper.getResources(newCreatedResourceBundleResponse);
test('Successfully export all data added to DB after currentTime', async () => {
// BUILD
const oldCreatedResourceBundleResponse = await bulkExportTestHelper.sendCreateResourcesRequest();
const resTypToResNotExpectedInExport = bulkExportTestHelper.getResources(oldCreatedResourceBundleResponse);
const currentTime = new Date();
const newCreatedResourceBundleResponse = await bulkExportTestHelper.sendCreateResourcesRequest();
const resTypToResExpectedInExport = bulkExportTestHelper.getResources(newCreatedResourceBundleResponse);

// OPERATE
// Only export resources that were added after 'currentTime'
const statusPollUrl = await bulkExportTestHelper.startExportJob({ since: currentTime });
const responseBody = await bulkExportTestHelper.getExportStatus(statusPollUrl);
// OPERATE
// Only export resources that were added after 'currentTime'
const statusPollUrl = await bulkExportTestHelper.startExportJob({ since: currentTime });
const responseBody = await bulkExportTestHelper.getExportStatus(statusPollUrl);

// CHECK
return bulkExportTestHelper.checkResourceInExportedFiles(
responseBody.output,
resTypToResExpectedInExport,
resTypToResNotExpectedInExport,
);
},
FIVE_MINUTES_IN_MS,
);
// CHECK
return bulkExportTestHelper.checkResourceInExportedFiles(
responseBody.output,
resTypToResExpectedInExport,
resTypToResNotExpectedInExport,
);
});

test(
'Successfully export just Patient data',
async () => {
// BUILD
const createdResourceBundleResponse = await bulkExportTestHelper.sendCreateResourcesRequest();
const resTypToResExpectedInExport = bulkExportTestHelper.getResources(createdResourceBundleResponse);
const type = 'Patient';
test('Successfully export just Patient data', async () => {
// BUILD
const createdResourceBundleResponse = await bulkExportTestHelper.sendCreateResourcesRequest();
const resTypToResExpectedInExport = bulkExportTestHelper.getResources(createdResourceBundleResponse);
const type = 'Patient';

// OPERATE
const statusPollUrl = await bulkExportTestHelper.startExportJob({ type });
const responseBody = await bulkExportTestHelper.getExportStatus(statusPollUrl);
// OPERATE
const statusPollUrl = await bulkExportTestHelper.startExportJob({ type });
const responseBody = await bulkExportTestHelper.getExportStatus(statusPollUrl);

// CHECK
// Check only files specified by "type" are exported
expect(new Set((responseBody.output as ExportStatusOutput[]).map(x => x.type))).toEqual(new Set([type]));
return bulkExportTestHelper.checkResourceInExportedFiles(responseBody.output, {
Patient: resTypToResExpectedInExport.Patient,
});
},
FIVE_MINUTES_IN_MS,
);
// CHECK
// Check only files specified by "type" are exported
expect(new Set((responseBody.output as ExportStatusOutput[]).map(x => x.type))).toEqual(new Set([type]));
return bulkExportTestHelper.checkResourceInExportedFiles(responseBody.output, {
Patient: resTypToResExpectedInExport.Patient,
});
});

test(
'Successfully stop a running export job',
async () => {
// BUILD
const statusPollUrl = await bulkExportTestHelper.startExportJob({});
// OPERATE
await bulkExportTestHelper.stopExportJob(statusPollUrl);
// CHECK
return bulkExportTestHelper.getExportStatus(statusPollUrl, 'Export job has been canceled');
},
FIVE_MINUTES_IN_MS,
);
test('Successfully stop a running export job', async () => {
// BUILD
const statusPollUrl = await bulkExportTestHelper.startExportJob({});
// OPERATE
await bulkExportTestHelper.stopExportJob(statusPollUrl);
// CHECK
return bulkExportTestHelper.getExportStatus(statusPollUrl, 'Export job has been canceled');
});
});
29 changes: 29 additions & 0 deletions integration-tests/rbac-permission.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { getFhirClient, randomPatient } from './utils';

jest.setTimeout(60 * 1000);

test('practitioner role can create new patient', async () => {
const client = await getFhirClient(false);
const patientRecord: any = randomPatient();
delete patientRecord.id;
await expect(client.post('Patient', patientRecord)).resolves.toMatchObject({
status: 201,
data: patientRecord,
});
});

describe('Negative tests', () => {
test('invalid token', async () => {
const client = await getFhirClient(false, 'Invalid token');
await expect(client.post('Patient', randomPatient())).rejects.toMatchObject({
response: { status: 401 },
});
});

test('auditor role cannot create new patient record', async () => {
const client = await getFhirClient(true);
await expect(client.post('Patient', randomPatient())).rejects.toMatchObject({
response: { status: 403 },
});
});
});
45 changes: 31 additions & 14 deletions integration-tests/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,19 @@ import * as AWS from 'aws-sdk';
import axios from 'axios';
import { Chance } from 'chance';

export const getFhirClient = async () => {
const { API_URL, API_KEY, API_AWS_REGION, COGNITO_USERNAME, COGNITO_PASSWORD, COGNITO_CLIENT_ID } = process.env;
export const getFhirClient = async (
isAuditor: boolean = false,
providedAccessToken: string | undefined = undefined,
Copy link
Contributor

Choose a reason for hiding this comment

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

Can we use strings for the role instead? Otherwise it's hard to understand the meaning of something like getFhirClient(false)
Also, providedAccessToken is effectively an optional parameter

Suggested change
isAuditor: boolean = false,
providedAccessToken: string | undefined = undefined,
role: 'auditor'|'practitioner' = 'auditor',
providedAccessToken: string?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

updated

) => {
const {
API_URL,
API_KEY,
API_AWS_REGION,
COGNITO_USERNAME_PRACTITIONER,
COGNITO_USERNAME_AUDITOR,
COGNITO_PASSWORD,
COGNITO_CLIENT_ID,
} = process.env;
if (API_URL === undefined) {
throw new Error('API_URL environment variable is not defined');
}
Expand All @@ -21,8 +32,11 @@ export const getFhirClient = async () => {
if (COGNITO_CLIENT_ID === undefined) {
throw new Error('COGNITO_CLIENT_ID environment variable is not defined');
}
if (COGNITO_USERNAME === undefined) {
throw new Error('COGNITO_USERNAME environment variable is not defined');
if (COGNITO_USERNAME_PRACTITIONER === undefined) {
throw new Error('COGNITO_USERNAME_PRACTITIONER environment variable is not defined');
}
if (COGNITO_USERNAME_AUDITOR === undefined) {
throw new Error('COGNITO_USERNAME_AUDITOR environment variable is not defined');
}
if (COGNITO_PASSWORD === undefined) {
throw new Error('COGNITO_PASSWORD environment variable is not defined');
Expand All @@ -31,19 +45,22 @@ export const getFhirClient = async () => {
AWS.config.update({ region: API_AWS_REGION });
const Cognito = new AWS.CognitoIdentityServiceProvider();

const authResponse = await Cognito.initiateAuth({
ClientId: COGNITO_CLIENT_ID,
AuthFlow: 'USER_PASSWORD_AUTH',
AuthParameters: {
USERNAME: COGNITO_USERNAME,
PASSWORD: COGNITO_PASSWORD,
},
}).promise();

const accessToken =
providedAccessToken ??
(
await Cognito.initiateAuth({
ClientId: COGNITO_CLIENT_ID,
AuthFlow: 'USER_PASSWORD_AUTH',
AuthParameters: {
USERNAME: isAuditor ? COGNITO_USERNAME_AUDITOR : COGNITO_USERNAME_PRACTITIONER,
PASSWORD: COGNITO_PASSWORD,
},
}).promise()
).AuthenticationResult!.AccessToken;
return axios.create({
headers: {
'x-api-key': API_KEY,
Authorization: `Bearer ${authResponse.AuthenticationResult!.AccessToken}`,
Authorization: `Bearer ${accessToken}`,
},
baseURL: API_URL,
});
Expand Down