Skip to content

Commit

Permalink
Merge pull request #228 from bcgov/tests/sync-unit-tests
Browse files Browse the repository at this point in the history
Add tests for certain sync features
  • Loading branch information
jujaga committed Nov 24, 2023
2 parents b06c49e + 479b849 commit a07a0c8
Show file tree
Hide file tree
Showing 9 changed files with 361 additions and 79 deletions.
4 changes: 2 additions & 2 deletions app/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -92,9 +92,9 @@ apiRouter.get('/', (_req, res) => {
authMode: state.authMode,
gitRev: state.gitRev,
name: appName,
nodeVersion: appVersion,
nodeVersion: process.version,
privacyMask: config.has('server.privacyMask'),
version: process.env.npm_package_version
version: appVersion
},
endpoints: ['/api/v1'],
versions: [1]
Expand Down
51 changes: 46 additions & 5 deletions app/tests/unit/components/utils.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@ const Problem = require('api-problem');
// Mock config library - @see {@link https://stackoverflow.com/a/64819698}
jest.mock('config');

const DEFAULTREGION = 'us-east-1'; // Need to specify valid AWS region or it'll explode ('us-east-1' is default, 'ca-central-1' for Canada)
// Need to specify valid AWS region or it'll explode ('us-east-1' is default, 'ca-central-1' for Canada)
const DEFAULTREGION = 'us-east-1';

beforeEach(() => {
jest.resetAllMocks();
Expand Down Expand Up @@ -246,7 +247,9 @@ describe('getCurrentSubject', () => {
expect(getCurrentTokenClaimSpy).toHaveBeenCalledWith(currentUser, 'sub', undefined);
});

it.each([undefined, null, '', [], {}])('should call getCurrentTokenClaim correctly given %j and defaultValue \'default\'', (currentUser) => {
it.each(
[undefined, null, '', [], {}]
)('should call getCurrentTokenClaim correctly given %j and defaultValue \'default\'', (currentUser) => {
const defaultValue = 'default';
utils.getCurrentSubject(currentUser, defaultValue);

Expand Down Expand Up @@ -472,15 +475,21 @@ describe('mixedQueryToArray', () => {
});

it('should return an array with the appropriate set when there are multiples', () => {
expect(utils.mixedQueryToArray('there,are,duplicates,here,yes,here,there,is,here')).toEqual(['there', 'are', 'duplicates', 'here', 'yes', 'is']);
expect(utils.mixedQueryToArray('there,are,duplicates,here,yes,here,there,is,here')).toEqual(
['there', 'are', 'duplicates', 'here', 'yes', 'is']
);
});

it('should return an array with the appropriate set when there are multiples and spaces', () => {
expect(utils.mixedQueryToArray('there, are, duplicates, here ,yes ,here ,there,is,here ')).toEqual(['there', 'are', 'duplicates', 'here', 'yes', 'is']);
expect(utils.mixedQueryToArray('there, are, duplicates, here ,yes ,here ,there,is,here ')).toEqual(
['there', 'are', 'duplicates', 'here', 'yes', 'is']
);
});

it('should return an array with the appropriate set when there are multiples and spaces', () => {
expect(utils.mixedQueryToArray(['there', ' are', ' duplicates', ' here ', 'yes ', 'here ', 'there', 'is', 'here '])).toEqual(['there', 'are', 'duplicates', 'here', 'yes', 'is']);
expect(utils.mixedQueryToArray(
['there', ' are', ' duplicates', ' here ', 'yes ', 'here ', 'there', 'is', 'here ']
)).toEqual(['there', 'are', 'duplicates', 'here', 'yes', 'is']);
});
});

Expand Down Expand Up @@ -575,3 +584,35 @@ describe('toLowerKeys', () => {
expect(utils.toLowerKeys(value)).toEqual(expected);
});
});

describe('getUniqueObjects', () => {
const testObj1 = {key1: 'test1', val1: 'val11', val2: 'val21'};
const testObj2 = {key1: 'test2', val1: 'val12', val2: 'val22'};
const testObj3 = {key1: 'test3', val1: 'val13', val2: 'val23'};
const testObj4 = {key1: 'test4', val1: 'val14', val2: 'val24'};
const testObj5 = {key1: 'test4', val1: 'val15', val2: 'val25'};

it('return all input objects', () => {
expect(utils.getUniqueObjects([
testObj1, testObj2, testObj3, testObj4
], 'key1')).toMatchObject([
testObj1, testObj2, testObj3, testObj4
]);
});

it('filter for unique keys', () => {
expect(utils.getUniqueObjects([
testObj2, testObj3, testObj4, testObj5
], 'key1')).toMatchObject([
testObj2, testObj3, testObj5
]);
});

it('should return the last object entered with duplicate key', () => {
expect(utils.getUniqueObjects([
testObj5, testObj4,
], 'key1')).toMatchObject([
testObj4
]);
});
});
9 changes: 8 additions & 1 deletion app/tests/unit/controllers/object.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,14 @@ const { MAXCOPYOBJECTLENGTH, MetadataDirective, TaggingDirective } = require('..
const utils = require('../../../src/db/models/utils');

const controller = require('../../../src/controllers/object');
const { storageService, objectService, metadataService, tagService, versionService, userService } = require('../../../src/services');
const {
storageService,
objectService,
metadataService,
tagService,
versionService,
userService
} = require('../../../src/services');

const mockResponse = () => {
const res = {};
Expand Down
13 changes: 11 additions & 2 deletions app/tests/unit/controllers/objectPermission.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,12 @@ describe('searchPermissions', () => {
const res = mockResponse();
await controller.searchPermissions(req, res, next);
expect(searchPermissionsSpy).toHaveBeenCalledTimes(1);
expect(searchPermissionsSpy).toHaveBeenCalledWith({ bucketId: [req.query.bucketId], objId: [req.query.objectId], userId: [req.query.userId], permCode: [req.query.permCode] });
expect(searchPermissionsSpy).toHaveBeenCalledWith({
bucketId: [req.query.bucketId],
objId: [req.query.objectId],
userId: [req.query.userId],
permCode: [req.query.permCode]
});
expect(res.status).toHaveBeenCalledWith(200);
expect(res.json).toHaveBeenCalledWith([]);
expect(next).toHaveBeenCalledTimes(0);
Expand Down Expand Up @@ -70,7 +75,11 @@ describe('listPermissions', () => {
const res = mockResponse();
await controller.listPermissions(req, res, next);
expect(searchPermissionsSpy).toHaveBeenCalledTimes(1);
expect(searchPermissionsSpy).toHaveBeenCalledWith({ objId: req.params.objectId, userId: [req.query.userId], permCode: [req.query.permCode] });
expect(searchPermissionsSpy).toHaveBeenCalledWith({
objId: req.params.objectId,
userId: [req.query.userId],
permCode: [req.query.permCode]
});
expect(res.status).toHaveBeenCalledWith(200);
expect(res.json).toHaveBeenCalledWith({ res: 123 });
expect(next).toHaveBeenCalledTimes(0);
Expand Down
7 changes: 6 additions & 1 deletion app/tests/unit/middleware/authorization.spec.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
const { NIL: SYSTEM_USER } = require('uuid');

const mw = require('../../../src/middleware/authorization');
const { bucketPermissionService, objectService, objectPermissionService, userService } = require('../../../src/services');
const {
bucketPermissionService,
objectService,
objectPermissionService,
userService
} = require('../../../src/services');
const { AuthMode, AuthType, Permissions } = require('../../../src/components/constants');
const utils = require('../../../src/components/utils');

Expand Down
158 changes: 103 additions & 55 deletions app/tests/unit/services/storage.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,9 @@ const service = require('../../../src/services/storage');
const utils = require('../../../src/components/utils');
const { MetadataDirective, TaggingDirective } = require('../../../src/components/constants');

const DEFAULTREGION = 'us-east-1'; // Need to specify valid AWS region or it'll explode ('us-east-1' is default, 'ca-central-1' for Canada)
// Need to specify valid AWS region or it'll explode ('us-east-1' is default, 'ca-central-1' for Canada)
const DEFAULTREGION = 'us-east-1';

const bucket = 'bucket';
const key = 'filePath';
const defaultTempExpiresIn = parseInt(config.get('server.defaultTempExpiresIn'), 10);
Expand Down Expand Up @@ -456,8 +458,14 @@ describe('listAllObjects', () => {

it('should call listObjectsV2 multiple times and return an array of precise path objects', async () => {
const continueToken = 'token';
listObjectsV2Mock.mockResolvedValueOnce({ Contents: [{ Key: 'filePath/foo' }], IsTruncated: true, NextContinuationToken: continueToken });
listObjectsV2Mock.mockResolvedValueOnce({ Contents: [{ Key: 'filePath/bar' }], IsTruncated: false });
listObjectsV2Mock.mockResolvedValueOnce({
Contents: [{ Key: 'filePath/foo' }],
IsTruncated: true,
NextContinuationToken: continueToken });
listObjectsV2Mock.mockResolvedValueOnce({
Contents: [{ Key: 'filePath/bar' }],
IsTruncated: false
});

const result = await service.listAllObjects();

Expand All @@ -482,8 +490,14 @@ describe('listAllObjects', () => {

it('should call listObjectsV2 multiple times and return an array of all path objects', async () => {
const continueToken = 'token';
listObjectsV2Mock.mockResolvedValueOnce({ Contents: [{ Key: 'filePath/test/foo' }], IsTruncated: true, NextContinuationToken: continueToken });
listObjectsV2Mock.mockResolvedValueOnce({ Contents: [{ Key: 'filePath/test/bar' }], IsTruncated: false });
listObjectsV2Mock.mockResolvedValueOnce({
Contents: [{ Key: 'filePath/test/foo' }],
IsTruncated: true,
NextContinuationToken: continueToken });
listObjectsV2Mock.mockResolvedValueOnce({
Contents: [{ Key: 'filePath/test/bar' }],
IsTruncated: false
});

const result = await service.listAllObjects({ precisePath: false });

Expand All @@ -506,34 +520,43 @@ describe('listAllObjects', () => {
}));
});

it('should call listObjectsV2 multiple times with the right bucketId and filePath, returning an array of objects', async () => {
const continueToken = 'token';
const customPath = 'filePath/test';
listObjectsV2Mock.mockResolvedValueOnce({ Contents: [{ Key: 'filePath/test/foo' }], IsTruncated: true, NextContinuationToken: continueToken });
listObjectsV2Mock.mockResolvedValueOnce({ Contents: [{ Key: 'filePath/test/bar' }], IsTruncated: false });

const result = await service.listAllObjects({ filePath: customPath, bucketId: bucket });

expect(result).toBeTruthy();
expect(Array.isArray(result)).toBeTruthy();
expect(result).toHaveLength(2);
expect(result).toEqual(expect.arrayContaining([
{ Key: 'filePath/test/foo' },
{ Key: 'filePath/test/bar' }
]));
expect(utils.getBucket).toHaveBeenCalledTimes(0);
expect(utils.isAtPath).toHaveBeenCalledTimes(2);
expect(listObjectsV2Mock).toHaveBeenCalledTimes(2);
expect(listObjectsV2Mock).toHaveBeenNthCalledWith(1, expect.objectContaining({
filePath: customPath,
bucketId: bucket
}));
expect(listObjectsV2Mock).toHaveBeenNthCalledWith(2, expect.objectContaining({
filePath: customPath,
continuationToken: continueToken,
bucketId: bucket
}));
});
it(
'should call listObjectsV2 multiple times with the right bucketId and filePath, returning an array of objects',
async () => {
const continueToken = 'token';
const customPath = 'filePath/test';
listObjectsV2Mock.mockResolvedValueOnce({
Contents: [{ Key: 'filePath/test/foo' }],
IsTruncated: true,
NextContinuationToken: continueToken });
listObjectsV2Mock.mockResolvedValueOnce({
Contents: [{ Key: 'filePath/test/bar' }],
IsTruncated: false
});

const result = await service.listAllObjects({ filePath: customPath, bucketId: bucket });

expect(result).toBeTruthy();
expect(Array.isArray(result)).toBeTruthy();
expect(result).toHaveLength(2);
expect(result).toEqual(expect.arrayContaining([
{ Key: 'filePath/test/foo' },
{ Key: 'filePath/test/bar' }
]));
expect(utils.getBucket).toHaveBeenCalledTimes(0);
expect(utils.isAtPath).toHaveBeenCalledTimes(2);
expect(listObjectsV2Mock).toHaveBeenCalledTimes(2);
expect(listObjectsV2Mock).toHaveBeenNthCalledWith(1, expect.objectContaining({
filePath: customPath,
bucketId: bucket
}));
expect(listObjectsV2Mock).toHaveBeenNthCalledWith(2, expect.objectContaining({
filePath: customPath,
continuationToken: continueToken,
bucketId: bucket
}));
}
);
});

describe('listAllObjectVersions', () => {
Expand Down Expand Up @@ -589,8 +612,14 @@ describe('listAllObjectVersions', () => {

it('should call listObjectVersion multiple times and return precise path objects', async () => {
const nextKeyMarker = 'token';
listObjectVersionMock.mockResolvedValueOnce({ DeleteMarkers: [{ Key: 'filePath/foo' }], IsTruncated: true, NextKeyMarker: nextKeyMarker });
listObjectVersionMock.mockResolvedValueOnce({ Versions: [{ Key: 'filePath/bar' }], IsTruncated: false });
listObjectVersionMock.mockResolvedValueOnce({
DeleteMarkers: [{ Key: 'filePath/foo' }],
IsTruncated: true,
NextKeyMarker: nextKeyMarker });
listObjectVersionMock.mockResolvedValueOnce({
Versions: [{ Key: 'filePath/bar' }],
IsTruncated: false
});

const result = await service.listAllObjectVersions({ filePath: 'filePath' });

Expand Down Expand Up @@ -619,8 +648,14 @@ describe('listAllObjectVersions', () => {

it('should call listObjectVersion multiple times and return all path objects', async () => {
const nextKeyMarker = 'token';
listObjectVersionMock.mockResolvedValueOnce({ DeleteMarkers: [{ Key: 'filePath/test/foo' }], IsTruncated: true, NextKeyMarker: nextKeyMarker });
listObjectVersionMock.mockResolvedValueOnce({ Versions: [{ Key: 'filePath/test/bar' }], IsTruncated: false });
listObjectVersionMock.mockResolvedValueOnce({
DeleteMarkers: [{ Key: 'filePath/test/foo' }],
IsTruncated: true,
NextKeyMarker: nextKeyMarker });
listObjectVersionMock.mockResolvedValueOnce({
Versions: [{ Key: 'filePath/test/bar' }],
IsTruncated: false
});

const result = await service.listAllObjectVersions({ filePath: 'filePath', precisePath: false });

Expand Down Expand Up @@ -649,10 +684,20 @@ describe('listAllObjectVersions', () => {

it('should call listObjectVersion multiple times and return all latest path objects', async () => {
const nextKeyMarker = 'token';
listObjectVersionMock.mockResolvedValueOnce({ DeleteMarkers: [{ Key: 'filePath/test/foo', IsLatest: true }], IsTruncated: true, NextKeyMarker: nextKeyMarker });
listObjectVersionMock.mockResolvedValueOnce({ Versions: [{ Key: 'filePath/test/bar', IsLatest: false }], IsTruncated: false });
listObjectVersionMock.mockResolvedValueOnce({
DeleteMarkers: [{ Key: 'filePath/test/foo', IsLatest: true }],
IsTruncated: true,
NextKeyMarker: nextKeyMarker });
listObjectVersionMock.mockResolvedValueOnce({
Versions: [{ Key: 'filePath/test/bar', IsLatest: false }],
IsTruncated: false
});

const result = await service.listAllObjectVersions({ filePath: 'filePath', precisePath: false, filterLatest: true });
const result = await service.listAllObjectVersions({
filePath: 'filePath',
precisePath: false,
filterLatest: true
});

expect(result).toBeTruthy();
expect(Array.isArray(result.DeleteMarkers)).toBeTruthy();
Expand Down Expand Up @@ -941,21 +986,24 @@ describe('readSignedUrl', () => {
presignUrlMock.mockRestore();
});

it('should call presignUrl with a get object command for the latest object and default expiration and bucketId', async () => {
const filePath = 'filePath';
const bucketId = 'abc';
const result = await service.readSignedUrl({ filePath, bucketId: bucketId });

expect(result).toBeTruthy();
expect(utils.getBucket).toHaveBeenCalledTimes(1);
expect(presignUrlMock).toHaveBeenCalledTimes(1);
expect(presignUrlMock).toHaveBeenCalledWith(expect.objectContaining({
input: {
Bucket: bucket,
Key: filePath
}
}), defaultTempExpiresIn, bucketId);
});
it(
'should call presignUrl with a get object command for the latest object and default expiration and bucketId',
async () => {
const filePath = 'filePath';
const bucketId = 'abc';
const result = await service.readSignedUrl({ filePath, bucketId: bucketId });

expect(result).toBeTruthy();
expect(utils.getBucket).toHaveBeenCalledTimes(1);
expect(presignUrlMock).toHaveBeenCalledTimes(1);
expect(presignUrlMock).toHaveBeenCalledWith(expect.objectContaining({
input: {
Bucket: bucket,
Key: filePath
}
}), defaultTempExpiresIn, bucketId);
}
);

it('should call presignUrl with a get object command for a specific version and default expiration', async () => {
const filePath = 'filePath';
Expand Down
Loading

0 comments on commit a07a0c8

Please sign in to comment.