Skip to content

Commit

Permalink
test: adds NodeJS native test runner
Browse files Browse the repository at this point in the history
Implements Node.js native test runner alongside real-world examples. Keeps swc as the compiler as well as proxyquire for module mocking in absence of native support.
  • Loading branch information
Ricardo Garim committed Apr 12, 2024
1 parent 17934f5 commit c0e79c4
Show file tree
Hide file tree
Showing 4 changed files with 134 additions and 91 deletions.
15 changes: 8 additions & 7 deletions apps/meteor/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,8 @@
"release": "meteor npm run set-version --silent",
"storybook": "cross-env NODE_OPTIONS=--max-old-space-size=8192 start-storybook -p 6006 --no-version-updates",
"prepare": "node playwright.prepare.mjs",
"docker:start": "docker-compose up"
"docker:start": "docker-compose up",
"test:unit:native": "node -r @swc-node/register --test tests/unit/app/api/server/v1/lib/*.spec.ts"
},
"license": "MIT",
"repository": {
Expand Down Expand Up @@ -85,6 +86,7 @@
"@storybook/addons": "~6.5.16",
"@storybook/react": "~6.5.16",
"@storybook/testing-library": "0.0.13",
"@swc-node/register": "^1.9.0",
"@swc/core": "^1.3.95",
"@swc/jest": "^0.2.29",
"@tanstack/react-query-devtools": "^4.19.1",
Expand Down Expand Up @@ -128,7 +130,7 @@
"@types/mkdirp": "^1.0.2",
"@types/mocha": "github:whitecolor/mocha-types",
"@types/moment-timezone": "^0.5.30",
"@types/node": "^14.18.63",
"@types/node": "^20.12.7",
"@types/node-gcm": "^1.0.3",
"@types/node-rsa": "^1.1.3",
"@types/nodemailer": "^6.4.13",
Expand Down Expand Up @@ -228,7 +230,7 @@
"@rocket.chat/agenda": "workspace:^",
"@rocket.chat/api-client": "workspace:^",
"@rocket.chat/apps": "workspace:^",
"@rocket.chat/apps-engine": "^1.42.1",
"@rocket.chat/apps-engine": "1.42.0-alpha.619",
"@rocket.chat/base64": "workspace:^",
"@rocket.chat/cas-validate": "workspace:^",
"@rocket.chat/core-services": "workspace:^",
Expand All @@ -239,8 +241,8 @@
"@rocket.chat/favicon": "workspace:^",
"@rocket.chat/forked-matrix-appservice-bridge": "^4.0.2",
"@rocket.chat/forked-matrix-bot-sdk": "^0.6.0-beta.3",
"@rocket.chat/fuselage": "^0.53.2",
"@rocket.chat/fuselage-hooks": "^0.33.1",
"@rocket.chat/fuselage": "^0.51.1",
"@rocket.chat/fuselage-hooks": "^0.33.0",
"@rocket.chat/fuselage-polyfills": "~0.31.25",
"@rocket.chat/fuselage-toastbar": "^0.31.26",
"@rocket.chat/fuselage-tokens": "^0.33.0",
Expand All @@ -254,7 +256,7 @@
"@rocket.chat/license": "workspace:^",
"@rocket.chat/log-format": "workspace:^",
"@rocket.chat/logger": "workspace:^",
"@rocket.chat/logo": "^0.31.30",
"@rocket.chat/logo": "^0.31.29",
"@rocket.chat/memo": "~0.31.25",
"@rocket.chat/message-parser": "workspace:^",
"@rocket.chat/model-typings": "workspace:^",
Expand Down Expand Up @@ -399,7 +401,6 @@
"query-string": "^7.1.3",
"queue-fifo": "^0.2.6",
"rc-scrollbars": "^1.1.6",
"re-resizable": "^6.9.9",
"react": "~17.0.2",
"react-aria": "~3.23.1",
"react-dom": "~17.0.2",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,46 +1,60 @@
import { expect } from 'chai';
import { describe, it } from 'mocha';
import { describe, it } from 'node:test';
import * as assert from 'node:assert';

import type { PermissionsPayload } from '../../../../../../../app/api/server/api.helpers';
import { checkPermissions } from '../../../../../../../app/api/server/api.helpers';

describe('checkPermissions', () => {
it('should return false when no options.permissionsRequired key is present', () => {
const options = {};
expect(checkPermissions(options)).to.be.false;

assert.strictEqual(checkPermissions(options), false);
});

it('should return false when options.permissionsRequired is of an invalid format', () => {
const options = {
permissionsRequired: 'invalid',
};

// @ts-expect-error - for testing purposes
expect(checkPermissions(options)).to.be.false;
assert.strictEqual(checkPermissions(options), false);
});

it('should return true and modify options.permissionsRequired when permissionsRequired key is an array (of permissions)', () => {
const options = {
permissionsRequired: ['invalid', 'invalid2'],
};
expect(checkPermissions(options)).to.be.true;
expect(options.permissionsRequired).to.be.an('object');
expect(options.permissionsRequired).to.have.property('*');

assert.strictEqual(checkPermissions(options), true);
assert.strictEqual(typeof options.permissionsRequired, 'object');
assert.strictEqual(options.permissionsRequired.hasOwnProperty('*'), true);
// @ts-expect-error -for test purposes :)
assert.strictEqual(typeof options.permissionsRequired['*'], 'object');
// @ts-expect-error -for test purposes :)
expect(options.permissionsRequired['*']).to.be.an('object');
assert.strictEqual(options.permissionsRequired['*'].permissions instanceof Array, true);
// @ts-expect-error -for test purposes :)
expect(options.permissionsRequired['*'].permissions).to.be.an('array').that.includes('invalid');
assert.strictEqual(options.permissionsRequired['*'].permissions.includes('invalid'), true);
});

it('should return true and modify options.permissionsRequired when permissionsRequired key is an object (of permissions)', () => {
const options = {
permissionsRequired: {
GET: ['invalid', 'invalid2'],
},
};
expect(checkPermissions(options)).to.be.true;
expect(options.permissionsRequired).to.be.an('object');
expect(options.permissionsRequired).to.have.property('GET');
expect(options.permissionsRequired.GET).to.be.an('object');
expect(options.permissionsRequired.GET).to.have.property('operation', 'hasAll');
expect(options.permissionsRequired.GET).to.have.property('permissions').that.is.an('array').that.includes('invalid');

assert.strictEqual(checkPermissions(options), true);
assert.strictEqual(typeof options.permissionsRequired, 'object');
assert.strictEqual(options.permissionsRequired.hasOwnProperty('GET'), true);
assert.strictEqual(typeof options.permissionsRequired.GET, 'object');
// @ts-expect-error -for test purposes :)
assert.strictEqual(options.permissionsRequired.GET.operation, 'hasAll');
// @ts-expect-error -for test purposes :)
assert.strictEqual(options.permissionsRequired.GET.permissions instanceof Array, true);
// @ts-expect-error -for test purposes :)
assert.strictEqual(options.permissionsRequired.GET.permissions.includes('invalid'), true);
});

it('should return true and not modify options.permissionsRequired when its of new format', () => {
const options: { permissionsRequired: PermissionsPayload } = {
permissionsRequired: {
Expand All @@ -50,12 +64,14 @@ describe('checkPermissions', () => {
},
},
};
expect(checkPermissions(options)).to.be.true;
expect(options.permissionsRequired).to.be.an('object');
expect(options.permissionsRequired).to.have.property('GET');
expect(options.permissionsRequired.GET).to.be.an('object');
expect(options.permissionsRequired.GET).to.have.property('operation', 'hasAll');
expect(options.permissionsRequired.GET).to.have.property('permissions').that.is.an('array').that.includes('invalid');

assert.strictEqual(checkPermissions(options), true);
assert.strictEqual(typeof options.permissionsRequired, 'object');
assert.strictEqual(options.permissionsRequired.hasOwnProperty('GET'), true);
assert.strictEqual(typeof options.permissionsRequired.GET, 'object');
assert.strictEqual((options.permissionsRequired.GET as { operation: string }).operation, 'hasAll');
assert.strictEqual(Array.isArray(options.permissionsRequired.GET?.permissions), true);
assert.strictEqual(options.permissionsRequired.GET?.permissions?.includes('invalid'), true);
});

it('should persist the right operation for method', () => {
Expand All @@ -67,13 +83,16 @@ describe('checkPermissions', () => {
},
},
};
expect(checkPermissions(options)).to.be.true;
expect(options.permissionsRequired).to.be.an('object');
expect(options.permissionsRequired).to.have.property('GET');
expect(options.permissionsRequired.GET).to.be.an('object');
expect(options.permissionsRequired.GET).to.have.property('operation', 'hasAny');
expect(options.permissionsRequired.GET).to.have.property('permissions').that.is.an('array').that.includes('invalid');

assert.strictEqual(checkPermissions(options), true);
assert.strictEqual(typeof options.permissionsRequired, 'object');
assert.strictEqual(options.permissionsRequired.hasOwnProperty('GET'), true);
assert.strictEqual(typeof options.permissionsRequired.GET, 'object');
assert.strictEqual((options.permissionsRequired.GET as { operation: string }).operation, 'hasAny');
assert.strictEqual(Array.isArray(options.permissionsRequired.GET?.permissions), true);
assert.strictEqual(options.permissionsRequired.GET?.permissions?.includes('invalid'), true);
});

it('should persist the right method keys', () => {
const options: { permissionsRequired: PermissionsPayload } = {
permissionsRequired: {
Expand All @@ -87,12 +106,14 @@ describe('checkPermissions', () => {
},
},
};
expect(checkPermissions(options)).to.be.true;
expect(options.permissionsRequired).to.be.an('object');
expect(options.permissionsRequired).to.have.property('GET');
expect(options.permissionsRequired).to.have.property('POST');
expect(options.permissionsRequired.GET).to.be.an('object');
expect(options.permissionsRequired.GET).to.have.property('operation', 'hasAll');
expect(options.permissionsRequired.GET).to.have.property('permissions').that.is.an('array').that.includes('invalid');

assert.strictEqual(checkPermissions(options), true);
assert.strictEqual(typeof options.permissionsRequired, 'object');
assert.strictEqual(options.permissionsRequired.hasOwnProperty('GET'), true);
assert.strictEqual(options.permissionsRequired.hasOwnProperty('POST'), true);
assert.strictEqual(typeof options.permissionsRequired.GET, 'object');
assert.strictEqual((options.permissionsRequired.GET as { operation: string }).operation, 'hasAll');
assert.strictEqual(Array.isArray(options.permissionsRequired.GET?.permissions), true);
assert.strictEqual(options.permissionsRequired.GET?.permissions?.includes('invalid'), true);
});
});
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { expect } from 'chai';
import { describe, it } from 'mocha';
import mock from 'proxyquire';
import { describe, it, mock } from 'node:test';
import * as assert from 'node:assert';
import proxyquire from 'proxyquire';

import type { PermissionsPayload } from '../../../../../../../app/api/server/api.helpers';

Expand All @@ -22,14 +22,15 @@ const mocks = {
},
};

const { checkPermissionsForInvocation } = mock.noCallThru().load('../../../../../../../app/api/server/api.helpers', mocks);
const { checkPermissionsForInvocation } = proxyquire.noCallThru().load('../../../../../../../app/api/server/api.helpers', mocks);

describe('checkPermissionsForInvocation', () => {
const fn = mock.fn(checkPermissionsForInvocation);

it('should return false when no permissions are provided', async () => {
const options = {
permissionsRequired: {},
};
expect(await checkPermissionsForInvocation('4r3fsadfasf', options.permissionsRequired, 'GET')).to.be.false;
const options = { permissionsRequired: {} };

assert.strictEqual(await fn('4r3fsadfasf', options.permissionsRequired, 'GET'), false);
});

it('should return false when no config is provided for that specific method', async () => {
Expand All @@ -41,7 +42,8 @@ describe('checkPermissionsForInvocation', () => {
},
},
};
expect(await checkPermissionsForInvocation('4r3fsadfasf', options.permissionsRequired, 'POST')).to.be.false;

assert.strictEqual(await fn('4r3fsadfasf', options.permissionsRequired, 'POST'), false);
});

it('should return true path is configured with empty permissions array', async () => {
Expand All @@ -50,7 +52,8 @@ describe('checkPermissionsForInvocation', () => {
GET: { permissions: [], operation: 'hasAll' },
},
};
expect(await checkPermissionsForInvocation('4r3fsadfasf', options.permissionsRequired, 'GET')).to.be.true;

assert.strictEqual(await fn('4r3fsadfasf', options.permissionsRequired, 'GET'), true);
});

it('should return true when user has all permissions', async () => {
Expand All @@ -62,7 +65,8 @@ describe('checkPermissionsForInvocation', () => {
},
},
};
expect(await checkPermissionsForInvocation('4r3fsadfasf', options.permissionsRequired, 'GET')).to.be.true;

assert.strictEqual(await fn('4r3fsadfasf', options.permissionsRequired, 'GET'), true);
});

it('should read permissions config from * when request method provided doesnt have config', async () => {
Expand All @@ -78,7 +82,8 @@ describe('checkPermissionsForInvocation', () => {
},
},
};
expect(await checkPermissionsForInvocation('4r3fsadfasf', options.permissionsRequired, 'PUT')).to.be.true;

assert.strictEqual(await fn('4r3fsadfasf', options.permissionsRequired, 'PUT'), true);
});

it('should return false when user has no permissions', async () => {
Expand All @@ -90,7 +95,8 @@ describe('checkPermissionsForInvocation', () => {
},
},
};
expect(await checkPermissionsForInvocation('4r3fsadfasf4', options.permissionsRequired, 'GET')).to.be.false;

assert.strictEqual(await fn('4r3fsadfasf4', options.permissionsRequired, 'GET'), false);
});

it('should return false when operation is invalid', async () => {
Expand All @@ -103,10 +109,11 @@ describe('checkPermissionsForInvocation', () => {
},
},
};
expect(await checkPermissionsForInvocation('4r3fsadfasf', options.permissionsRequired, 'GET')).to.be.false;

assert.strictEqual(await fn('4r3fsadfasf', options.permissionsRequired, 'GET'), false);
});

it('should return true when operation is hasAny and user has at least one listed permission', async () => {
it('should return true when operation is hasAny and user has at least one listed permission', async (t) => {
const options: { permissionsRequired: PermissionsPayload } = {
permissionsRequired: {
GET: {
Expand All @@ -115,6 +122,7 @@ describe('checkPermissionsForInvocation', () => {
},
},
};
expect(await checkPermissionsForInvocation('4r3fsadfasf', options.permissionsRequired, 'GET')).to.be.true;

assert.strictEqual(await fn('4r3fsadfasf', options.permissionsRequired, 'GET'), true);
});
});

0 comments on commit c0e79c4

Please sign in to comment.