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

CAMS-442 test coverage #1015

Merged
merged 7 commits into from
Nov 8, 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
47 changes: 47 additions & 0 deletions backend/functions/azure/functions.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import { azureToCamsHttpRequest, toAzureSuccess } from './functions';
import { HttpRequest, HttpResponseInit } from '@azure/functions';
import { CamsHttpRequest } from '../lib/adapters/types/http';
import { CamsHttpResponseInit } from '../lib/adapters/utils/http-response';

describe('functions test', () => {
test('should return properly formatted CamsHttpRequest from malformed headers and query', async () => {
const request = {
method: 'GET',
url: '/test',
query: 'bar',
params: { arg1: 'hello' },
} as unknown as HttpRequest;

const expected: CamsHttpRequest = {
method: 'GET',
url: '/test',
headers: {},
query: {},
params: { arg1: 'hello' },
body: undefined,
};

const response = await azureToCamsHttpRequest(request);
expect(response).toEqual(expected);
});

test('should return empty Azure response init', () => {
const responseInit = toAzureSuccess();
expect(responseInit).toEqual({});
});

test('should return Azure response init', () => {
const camsResponseInit: CamsHttpResponseInit<{ prop1: boolean }> = {
headers: { foo: 'bar' },
statusCode: 200,
body: { data: { prop1: true } },
};
const expected: HttpResponseInit = {
headers: { foo: 'bar' },
status: 200,
jsonBody: { data: { prop1: true } },
};
const responseInit = toAzureSuccess(camsResponseInit);
expect(responseInit).toEqual(expected);
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -57,13 +57,12 @@ describe('offices repo', () => {
...session.user,
ttl,
});

const insertOneSpy = jest
.spyOn(MongoCollectionAdapter.prototype, 'insertOne')
const replaceOneSpy = jest
.spyOn(MongoCollectionAdapter.prototype, 'replaceOne')
.mockResolvedValue('inserted-id');

await repo.putOfficeStaff(officeCode, session.user);
expect(insertOneSpy).toHaveBeenCalledWith({ ...staff, updatedOn: expect.anything() });
expect(replaceOneSpy).toHaveBeenCalledWith(expect.anything(), staff, true);
});

describe('error handling', () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,11 @@ export class OfficesMongoRepository extends BaseMongoRepository implements Offic
...user,
ttl,
});
const query = QueryBuilder.build(
and(equals<string>('id', staff.id), equals<string>('officeCode', officeCode)),
);
try {
await this.getAdapter<OfficeStaff>().insertOne(staff);
await this.getAdapter<OfficeStaff>().replaceOne(query, staff, true);
} catch (originalError) {
throw getCamsError(originalError, MODULE_NAME);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import {
TransferOrderAction,
} from '../../../../../../common/src/cams/orders';
import { ApplicationContext } from '../../types/basic';
import { NotFoundError } from '../../../common-errors/not-found-error';
import { OrdersRepository } from '../../../use-cases/gateways.types';
import QueryBuilder, { ConditionOrConjunction } from '../../../query/query-builder';
import { getCamsError } from '../../../common-errors/error-utilities';
Expand Down Expand Up @@ -51,9 +50,6 @@ export class OrdersMongoRepository extends BaseMongoRepository implements Orders
const adapter = this.getAdapter<TransferOrder>();
const { docketSuggestedCaseNumber: _docketSuggestedCaseNumber, ...existingOrder } =
(await adapter.findOne(query)) as TransferOrder;
if (!existingOrder) {
throw new NotFoundError(MODULE_NAME, { message: `Order not found with id ${data.id}` });
}
const { id: _id, orderType: _orderType, caseId: _caseId, ...mutableProperties } = data;
const updatedOrder = {
...existingOrder,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { CamsError } from '../../../../common-errors/cams-error';
import { NotFoundError } from '../../../../common-errors/not-found-error';
import { UnknownError } from '../../../../common-errors/unknown-error';
import { CollectionHumble } from '../../../../humble-objects/mongo-humble';
import { CollectionHumble, DocumentClient } from '../../../../humble-objects/mongo-humble';
import QueryBuilder from '../../../../query/query-builder';
import { MongoCollectionAdapter } from './mongo-adapter';
import { MongoCollectionAdapter, removeIds } from './mongo-adapter';

const { and, orderBy } = QueryBuilder;

Expand All @@ -29,7 +29,11 @@ const spies = {
countDocuments,
};

type TestType = object;
type TestType = {
_id?: unknown;
id?: string;
foo?: string;
};

describe('Mongo adapter', () => {
const testQuery = QueryBuilder.build(and());
Expand All @@ -40,13 +44,37 @@ describe('Mongo adapter', () => {
jest.resetAllMocks();
});

test('should return an instance of the adapter from newAdapter', () => {
const mockClient: DocumentClient = {
database: () => {
return {
collection: <_t>() => {},
};
},
} as unknown as DocumentClient;
const adapter = MongoCollectionAdapter.newAdapter<TestType>(
'module',
'collection',
'database',
mockClient,
);
expect(adapter).toBeInstanceOf(MongoCollectionAdapter);
});

test('should return items from a getAll', async () => {
find.mockResolvedValue([{}, {}, {}]);
const item = await adapter.getAll();
expect(item).toEqual([{}, {}, {}]);
expect(find).toHaveBeenCalled();
});

// TODO: maybe remove this?
test('should remove Ids from an item', async () => {
const expectedItem = { arbitraryValue: 'arbitrary-value' };
const item = { _id: 'some_id', id: 'someId', ...expectedItem };
expect(removeIds(item)).toEqual(expectedItem);
});

test('should return a sorted list of items from a getAll', async () => {
function* generator() {
yield Promise.resolve({});
Expand Down Expand Up @@ -101,24 +129,77 @@ describe('Mongo adapter', () => {
);
});

test('should return a single Id from a replaceOne', async () => {
const id = '123456';
replaceOne.mockResolvedValue({ acknowdledged: true, upsertedId: id });
const result = await adapter.replaceOne(testQuery, {});
expect(result).toEqual(id);
test('should return a single Id from replaceOne', async () => {
const testObject: TestType = { id: '12345', foo: 'bar' };
const _id = 'mongoGeneratedId';
replaceOne.mockResolvedValue({
acknowledged: true,
matchedCount: 1,
modifiedCount: 1,
upsertedId: _id,
});
const result = await adapter.replaceOne(testQuery, testObject);
expect(result).not.toEqual(_id);
expect(result).toEqual(testObject.id);
});

test('should throw an error calling replaceOne for a nonexistant record and upsert=false', async () => {
const testObject: TestType = { id: '12345', foo: 'bar' };
replaceOne.mockResolvedValue({
acknowledged: false,
matchedCount: 0,
modifiedCount: 0,
upsertedId: null,
});
await expect(adapter.replaceOne(testQuery, testObject)).rejects.toThrow(
'No matching item found.',
);
});

test('should return a single Id from replaceOne when upsert = true', async () => {
const testObject: TestType = { id: '12345', foo: 'bar' };
const _id = 'mongoGeneratedId';

replaceOne.mockResolvedValue({
acknowledged: true,
matchedCount: 0,
modifiedCount: 1,
upsertedId: _id,
});
const result = await adapter.replaceOne(testQuery, testObject, true);
expect(result).toEqual(testObject.id);
expect(result).not.toEqual(_id);
});

test('should throw an error if replaceOne does not match.', async () => {
const testObject: TestType = { id: '12345', foo: 'bar' };
replaceOne.mockResolvedValue({
acknowledged: false,
matchedCount: 0,
modifiedCount: 0,
upsertedId: null,
});
await expect(adapter.replaceOne(testQuery, testObject, true)).rejects.toThrow(
'Failed to insert document into database.',
);
});

test('should return a single Id from insertOne', async () => {
const id = '123456';
insertOne.mockResolvedValue({ acknowdledged: true, insertedId: id });
insertOne.mockResolvedValue({ acknowledged: true, insertedId: id });
const result = await adapter.insertOne({});
expect(result.split('-').length).toEqual(5);
});

test('should throw an error if insertOne does not insert.', async () => {
insertOne.mockResolvedValue({ acknowledged: false });
await expect(adapter.insertOne({})).rejects.toThrow('Failed to insert document into database.');
});

test('should return a list of Ids from insertMany', async () => {
const ids = ['0', '1', '2', '3', '4'];
insertMany.mockResolvedValue({
acknowdledged: true,
acknowledged: true,
insertedIds: ids,
insertedCount: ids.length,
});
Expand All @@ -127,7 +208,7 @@ describe('Mongo adapter', () => {
});

test('should return a count of 1 for 1 item deleted', async () => {
deleteOne.mockResolvedValue({ acknowdledged: true, deletedCount: 1 });
deleteOne.mockResolvedValue({ acknowledged: true, deletedCount: 1 });
const result = await adapter.deleteOne(testQuery);
expect(result).toEqual(1);
});
Expand All @@ -140,7 +221,7 @@ describe('Mongo adapter', () => {
});

test('should return a count of 5 for 5 items deleted', async () => {
deleteMany.mockResolvedValue({ acknowdledged: true, deletedCount: 5 });
deleteMany.mockResolvedValue({ acknowledged: true, deletedCount: 5 });
const result = await adapter.deleteMany(testQuery);
expect(result).toEqual(5);
});
Expand All @@ -158,8 +239,15 @@ describe('Mongo adapter', () => {
expect(result).toEqual(5);
});

test('should return a count of 6 when countAllDocuments is called and there are 6 documents', async () => {
countDocuments.mockResolvedValue(6);
const result = await adapter.countAllDocuments();
expect(result).toEqual(6);
});

test('should throw CamsError when some but not all items are inserted', async () => {
insertMany.mockResolvedValue({
acknowledged: true,
insertedIds: {
one: 'one',
two: 'two',
Expand All @@ -176,22 +264,6 @@ describe('Mongo adapter', () => {
expect(async () => await adapter.insertMany([{}, {}, {}, {}])).rejects.toThrow(error);
});

test('should handle acknowledged == false', async () => {
const response = { acknowledged: false };
const error = new UnknownError(MODULE_NAME, {
message: 'Operation returned Not Acknowledged.',
});
Object.values(spies).forEach((spy) => {
spy.mockResolvedValue(response);
});

await expect(adapter.replaceOne(testQuery, {})).rejects.toThrow(error);
await expect(adapter.insertOne({})).rejects.toThrow(error);
await expect(adapter.insertMany([{}])).rejects.toThrow(error);
await expect(adapter.deleteOne(testQuery)).rejects.toThrow(error);
await expect(adapter.deleteMany(testQuery)).rejects.toThrow(error);
});

test('should handle errors', async () => {
const originalError = new Error('Test Exception');
const expectedError = new UnknownError(MODULE_NAME, { originalError });
Expand All @@ -206,6 +278,8 @@ describe('Mongo adapter', () => {
await expect(adapter.deleteMany(testQuery)).rejects.toThrow(expectedError);
await expect(adapter.find(testQuery)).rejects.toThrow(expectedError);
await expect(adapter.countDocuments(testQuery)).rejects.toThrow(expectedError);
await expect(adapter.countAllDocuments()).rejects.toThrow(expectedError);
await expect(adapter.findOne(testQuery)).rejects.toThrow(expectedError);
await expect(adapter.getAll()).rejects.toThrow(expectedError);
});
});
Loading
Loading