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

Add unit tests #7

Merged
merged 8 commits into from
Feb 29, 2020
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
2 changes: 1 addition & 1 deletion .eslintrc
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
"import/resolver": {
"typescript": {
"directory": [
"./tsconfig.json"
"./tsconfig.json",
"./test/tsconfig.json"
]
}
Expand Down
10 changes: 8 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,13 +1,19 @@
# See https://help.github.com/ignore-files/ for more about ignoring files.

# dependencies
# Dependencies
/node_modules
/dist

# misc
# Misc
/.idea
.DS_Store
npm-debug.log*
yarn-error.log*
.DS_Store

# Use Yarn!
package-lock.json

# Test artifacts
/coverage
/.nyc_output
39 changes: 37 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,29 +15,38 @@
"start:sdk": "tsc -w -p ./config/tsconfig.sdk.json",
"start:e2e": "ts-node src/node-e2e/app.ts",
"build": "npm run clean:build && ./scripts/build.sh",
"test": "npm run clean:test-artifacts && ./scripts/test.sh",
"lint": "eslint --fix src/**/*.ts",
"clean": "npm-run-all -s clean:*",
"clean:test-artifacts": "rimraf coverage && rimraf .nyc_output",
"clean:build": "rimraf dist",
"clean_node_modules": "rimraf node_modules",
"lint": "tslint --fix ."
"clean_node_modules": "rimraf node_modules"
},
"devDependencies": {
"@ikscodes/eslint-config": "^6.2.0",
"@ikscodes/prettier-config": "^1.0.0",
"@istanbuljs/nyc-config-typescript": "^1.0.1",
"@types/eth-sig-util": "^2.1.0",
"@types/express": "^4.17.2",
"@types/node": "^13.1.2",
"@types/node-fetch": "^2.5.4",
"@types/sinon": "^7.5.2",
"@typescript-eslint/eslint-plugin": "^2.15.0",
"ava": "^3.4.0",
"eslint": "^6.7.2",
"eslint-import-resolver-typescript": "^2.0.0",
"eslint-plugin-import": "^2.18.2",
"eslint-plugin-jsx-a11y": "^6.2.3",
"eslint-plugin-prettier": "^3.1.2",
"eslint-plugin-react": "^7.15.1",
"eslint-plugin-react-hooks": "^1.7.0",
"husky": "^4.2.3",
"lint-staged": "^10.0.8",
"npm-run-all": "~4.1.5",
"nyc": "^15.0.0",
"prettier": "^1.19.1",
"rimraf": "~3.0.0",
"sinon": "^9.0.0",
"ts-node": "~8.5.2",
"tslint": "~5.20.1",
"typescript": "~3.7.2"
Expand All @@ -55,5 +64,31 @@
},
"lint-staged": {
"*.{ts,tsx}": "eslint --fix"
},
"ava": {
"require": [
"ts-node/register"
],
"files": [
"test/**/*.spec.ts"
],
"extensions": [
"ts"
],
"verbose": true
},
"nyc": {
"extends": "@istanbuljs/nyc-config-typescript",
"all": false,
"check-coverage": true,
"per-file": true,
"lines": 99,
"statements": 99,
"functions": 99,
"branches": 99,
"reporter": [
"html",
"lcov"
]
}
}
8 changes: 6 additions & 2 deletions src/admin-sdk/core/sdk.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import { MiddlewaresModule } from '../modules/middlewares';
import { TokenModule } from '../modules/token';
import { UsersModule } from '../modules/users';
import { MagicAdminSDKAdditionalConfiguration } from '../types';

export class MagicAdminSDK {
public readonly apiBaseUrl = 'https://api.fortmatic.com';
public readonly apiBaseUrl: string;

/**
* Contains token validation middlewares for various NodeJS server frameworks.
Expand All @@ -22,7 +23,10 @@ export class MagicAdminSDK {
*/
public readonly users: UsersModule;

constructor(public readonly secretApiKey: string) {
constructor(public readonly secretApiKey: string, options?: MagicAdminSDKAdditionalConfiguration) {
const endpoint = options?.endpoint ?? 'https://api.fortmatic.com';
this.apiBaseUrl = endpoint.replace(/\/+$/, '');

// Assign API Modules
this.middlewares = new MiddlewaresModule(this);
this.token = new TokenModule(this);
Expand Down
2 changes: 1 addition & 1 deletion src/admin-sdk/modules/base-module.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { MagicAdminSDK } from '../core/sdk';

export class BaseModule {
export abstract class BaseModule {
constructor(protected readonly sdk: MagicAdminSDK) {}
}
13 changes: 10 additions & 3 deletions src/admin-sdk/modules/middlewares/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,21 @@ export class MiddlewaresModule extends BaseModule {
public express = async (req: Request, res: Response, next: NextFunction) => {
const authorizationHeader = req?.headers?.authorization;
if (!authorizationHeader || !authorizationHeader.toLowerCase().startsWith('bearer ')) {
res.status(401).send(createMissingAuthHeaderError().message);
res
.status(401)
.send(createMissingAuthHeaderError().message)
.end();
return;
}
try {
const DIDToken = authorizationHeader!.substring(7); // Strips Bearer
const DIDToken = authorizationHeader.substring(7); // Strips Bearer
await this.sdk.token.validate(DIDToken);
next();
} catch (err) {
res.status(401).send(err.message);
res
.status(401)
.send(err.message)
.end();
}
};
}
2 changes: 1 addition & 1 deletion src/admin-sdk/modules/token/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ export class TokenModule extends BaseModule {
public getPublicAddress(DIDToken: string): string {
const decodedDIDToken = this.decode(DIDToken);
const claimJSONObject = JSON.parse(decodedDIDToken[1]) as Claim;
const claimedIssuer = claimJSONObject?.iss.split(':')[2];
const claimedIssuer = claimJSONObject.iss.split(':')[2];
return claimedIssuer;
}
}
9 changes: 5 additions & 4 deletions src/admin-sdk/modules/users/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,10 @@ import { createApiKeyMissingError } from '../../core/sdk-exceptions';

export class UsersModule extends BaseModule {
public async logoutByPublicAddress(publicAddress: string) {
if (!this.sdk.secretApiKey) {
throw createApiKeyMissingError();
}
if (!this.sdk.secretApiKey) throw createApiKeyMissingError();

const body = JSON.stringify({ publicaddress: publicAddress });

return fetch(`${this.sdk.apiBaseUrl}/v1/admin/auth/user/logout`, {
method: 'POST',
headers: { 'X-Fortmatic-Secret-key': this.sdk.secretApiKey },
Expand All @@ -17,9 +16,11 @@ export class UsersModule extends BaseModule {
}

public async logoutByToken(DIDToken: string) {
const publicAddress = this.sdk.token.getPublicAddress(DIDToken);
if (!this.sdk.secretApiKey) throw createApiKeyMissingError();

const publicAddress = this.sdk.token.getPublicAddress(DIDToken);
const body = JSON.stringify({ publicaddress: publicAddress });

return fetch(`${this.sdk.apiBaseUrl}/v1/admin/auth/user/logout`, {
method: 'POST',
headers: { 'X-Fortmatic-Secret-key': this.sdk.secretApiKey },
Expand Down
1 change: 1 addition & 0 deletions src/admin-sdk/types/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
export * from './didt-types';
export * from './exception-types';
export * from './sdk-types';
3 changes: 3 additions & 0 deletions src/admin-sdk/types/sdk-types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export interface MagicAdminSDKAdditionalConfiguration {
endpoint?: string;
}
Empty file removed test/lib/.gitkeep
Empty file.
26 changes: 26 additions & 0 deletions test/lib/constants.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
export const API_FULL_URL = 'https://api.fortmatic.com';
export const API_KEY = 'sk_test_123';

export const VALID_DIDT =
'WyIweDA3ODk1MGI2OTk3ZjhiMjhmZTgzMzMwMzZlMzE5ZTMyOTA5NzhlOWIwNTY5NWZjMTVhM2ZmYWRiOGJkNTFkZGExNzljYTkzMzFlODhkNDhiYzY4ZWY5YTc5NjY0MGI0MWQ2Yzg0ZDU0MWE5NzIxNTYxZDMxYzg0NzQ3ZTAyNmY3MWMiLCJ7XCJpYXRcIjoxNTgyOTQxNzE4LFwiZXh0XCI6MTg5ODM0MTcxOCxcImlzc1wiOlwiZGlkOmV0aHI6MHhlOWJlMTc3Y2E0ZDE1ZjUxY2MyODU3RUYxMzNBNWVCOUNmNTAwMjJBXCIsXCJzdWJcIjpcIjZ0RlhUZlJ4eWt3TUtPT2pTTWJkUHJFTXJwVWwzbTNqOERReWNGcU8ydHc9XCIsXCJhdWRcIjpcImRpZDptYWdpYzo5NzFiOGY5My04YjAyLTRkZTAtOTdjMy0wMGI1ODFjMzJkZDNcIixcIm5iZlwiOjE1ODI5NDE3MTgsXCJ0aWRcIjpcImVmOTA2YWExLThkYTUtNDVmMS1hMmU3LTc5MTgzMmQ3NDg1NlwifSJd';

export const VALID_DIDT_DECODED = [
'0x078950b6997f8b28fe8333036e319e3290978e9b05695fc15a3ffadb8bd51dda179ca9331e88d48bc68ef9a796640b41d6c84d541a9721561d31c84747e026f71c',
'{"iat":1582941718,"ext":1898341718,"iss":"did:ethr:0xe9be177ca4d15f51cc2857EF133A5eB9Cf50022A","sub":"6tFXTfRxykwMKOOjSMbdPrEMrpUl3m3j8DQycFqO2tw=","aud":"did:magic:971b8f93-8b02-4de0-97c3-00b581c32dd3","nbf":1582941718,"tid":"ef906aa1-8da5-45f1-a2e7-791832d74856"}',
];

export const VALID_DIDT_PARSED_CLAIMS = {
iat: 1582941718,
ext: 1898341718,
iss: 'did:ethr:0xe9be177ca4d15f51cc2857EF133A5eB9Cf50022A',
sub: '6tFXTfRxykwMKOOjSMbdPrEMrpUl3m3j8DQycFqO2tw=',
aud: 'did:magic:971b8f93-8b02-4de0-97c3-00b581c32dd3',
nbf: 1582941718,
tid: 'ef906aa1-8da5-45f1-a2e7-791832d74856',
};

export const INVALID_SIGNER_DIDT =
'WyIweGM5NTFiNGFmZDQ3MzcxYTNjYjk4ZDZiMThmMzJlM2IyMzk2YThhMmI5ZDhiY2ZhNmUzMGQwYmM5ZGY5ZDdlNjk2OWYwOTM0NDEzMzc4Yzk2ZmE1Zjc1NzYyNDg0NzY0OGI0OGI1MGE4YTdmZWY3YjA4MWMxMmRiYjdiMjI3N2I0MWMiLCJ7XCJpYXRcIjoxNTgyOTQyMjM3LFwiZXh0XCI6MTg5ODM0MjIzNyxcImlzc1wiOlwiZGlkOmV0aHI6MHgwXCIsXCJzdWJcIjpcIjZ0RlhUZlJ4eWt3TUtPT2pTTWJkUHJFTXJwVWwzbTNqOERReWNGcU8ydHc9XCIsXCJhdWRcIjpcImRpZDptYWdpYzoxOTViY2IzZi05NTM4LTQ0ODMtODc4ZC0yNDE1MDIxN2IwNzBcIixcIm5iZlwiOjE1ODI5NDIyMzcsXCJ0aWRcIjpcImIxMGEzYjdhLTY1MDktNDhlNi1hMjdhLTM4ZDM4Y2RiNjVhZVwifSJd';

export const EXPIRED_DIDT =
'WyIweGY2YTQ0YmU0MTlkMTNmZTZkYzU1MGQxMzIzOThjMjc3MTZlNzRmNzJhMWNlNGI2ZTZjNjgwYjVhYzQzMDNlNzQzYzE5ZmEzODMyOTdiMDcyODZiN2M5OTA0ZjdlNTEwOWIwZmYyZWM2NTNmYzZjM2NiMzdhYTNhYmJjYWM4MTY2MWMiLCJ7XCJpYXRcIjoxNTgyOTQzMzU3LFwiZXh0XCI6MTU4Mjk0MzM1OCxcImlzc1wiOlwiZGlkOmV0aHI6MHhlOWJlMTc3Y2E0ZDE1ZjUxY2MyODU3RUYxMzNBNWVCOUNmNTAwMjJBXCIsXCJzdWJcIjpcIjZ0RlhUZlJ4eWt3TUtPT2pTTWJkUHJFTXJwVWwzbTNqOERReWNGcU8ydHc9XCIsXCJhdWRcIjpcImRpZDptYWdpYzo5YTcwMDNjNS03NTQwLTRmOGItYTBmMC1mMDgyZGYyZGI0YWNcIixcIm5iZlwiOjE1ODI5NDMzNTcsXCJ0aWRcIjpcIjE0ODEwZTc3LTNlNTItNGUxOS05MjE5LWMzYWE4MzQ4MzEyMVwifSJd';
6 changes: 6 additions & 0 deletions test/lib/factories.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { API_FULL_URL, API_KEY } from './constants';
import { MagicAdminSDK } from '../../src/admin-sdk/core/sdk';

export function createMagicAdminSDK(endpoint = API_FULL_URL) {
return new MagicAdminSDK(API_KEY, { endpoint });
}
Empty file removed test/setup.ts
Empty file.
Empty file removed test/spec/.gitkeep
Empty file.
62 changes: 62 additions & 0 deletions test/spec/core/sdk-exceptions/error-factories.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
/* eslint-disable no-underscore-dangle */

import test, { ExecutionContext } from 'ava';
import {
MagicAdminSDKError,
createMissingAuthHeaderError,
createTokenExpiredError,
createIncorrectSignerAddressError,
createFailedRecoveringProofError,
createApiKeyMissingError,
} from '../../../../src/admin-sdk/core/sdk-exceptions';

function errorAssertions<T extends ExecutionContext<any>>(
t: T,
error: MagicAdminSDKError,
expectedCode: string,
expectedMessage: string,
) {
t.true(error instanceof MagicAdminSDKError);
t.is(error.code, expectedCode);
t.is(error.message, `Magic Admin SDK Error: [${expectedCode}] ${expectedMessage}`);
}

test('#01: Creates `ERROR_MISSING_AUTH_HEADER` error', async t => {
const error = createMissingAuthHeaderError();
errorAssertions(
t,
error,
'ERROR_MISSING_AUTH_HEADER',
'Missing authorization header. Request failed authentication.',
);
});

test('#02: Creates `ERROR_DIDT_EXPIRED` error', async t => {
const error = createTokenExpiredError();
errorAssertions(t, error, 'ERROR_DIDT_EXPIRED', 'DID Token has expired. Request failed authentication.');
});

test('#03: Creates `ERROR_INCORRECT_SIGNER_ADDR` error', async t => {
const error = createIncorrectSignerAddressError();
errorAssertions(
t,
error,
'ERROR_INCORRECT_SIGNER_ADDR',
'Incorrect signer address for DID Token. Request failed authentication.',
);
});

test('#04: Creates `ERROR_FAILED_RECOVERING_PROOF` error', async t => {
const error = createFailedRecoveringProofError();
errorAssertions(t, error, 'ERROR_FAILED_RECOVERING_PROOF', 'Failed to recover proof. Request failed authentication.');
});

test('#05: Creates `ERROR_SECRET_API_KEY_MISSING` error', async t => {
const error = createApiKeyMissingError();
errorAssertions(
t,
error,
'ERROR_SECRET_API_KEY_MISSING',
'Please provide a secret Fortmatic API key that you acquired from the developer dashboard.',
);
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import test from 'ava';
import { MagicAdminSDKError } from '../../../../../src/admin-sdk/core/sdk-exceptions';

test('#01: Instantiates `MagicAdminSDKError`', t => {
const error = new MagicAdminSDKError('TEST_CODE' as any, 'test message');
t.true(error instanceof MagicAdminSDKError);
t.is(error.message, 'Magic Admin SDK Error: [TEST_CODE] test message');
t.is(error.code, 'TEST_CODE');
});
38 changes: 38 additions & 0 deletions test/spec/core/sdk/constructor.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
/* eslint-disable no-new */

import test from 'ava';
import { MagicAdminSDK } from '../../../../src/admin-sdk/core/sdk';
import { API_FULL_URL, API_KEY } from '../../../lib/constants';
import { MiddlewaresModule } from '../../../../src/admin-sdk/modules/middlewares';
import { TokenModule } from '../../../../src/admin-sdk/modules/token';
import { UsersModule } from '../../../../src/admin-sdk/modules/users';

test.serial('#01: Initialize `MagicAdminSDK`', t => {
const magic = new MagicAdminSDK(API_KEY);

t.is(magic.secretApiKey, API_KEY);
t.is(magic.apiBaseUrl, API_FULL_URL);
t.true(magic.middlewares instanceof MiddlewaresModule);
t.true(magic.token instanceof TokenModule);
t.true(magic.users instanceof UsersModule);
});

test.serial('#02: Initialize `MagicAdminSDK` with custom endpoint', t => {
const magic = new MagicAdminSDK(API_KEY, { endpoint: 'https://example.com' });

t.is(magic.secretApiKey, API_KEY);
t.is(magic.apiBaseUrl, 'https://example.com');
t.true(magic.middlewares instanceof MiddlewaresModule);
t.true(magic.token instanceof TokenModule);
t.true(magic.users instanceof UsersModule);
});

test.serial('#03: Strips trailing slash(es) from custom endpoint argument', t => {
const magicA = new MagicAdminSDK(API_KEY, { endpoint: 'https://example.com/' });
const magicB = new MagicAdminSDK(API_KEY, { endpoint: 'https://example.com//' });
const magicC = new MagicAdminSDK(API_KEY, { endpoint: 'https://example.com///' });

t.is(magicA.apiBaseUrl, 'https://example.com');
t.is(magicB.apiBaseUrl, 'https://example.com');
t.is(magicC.apiBaseUrl, 'https://example.com');
});
13 changes: 13 additions & 0 deletions test/spec/modules/base-module/constructor.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import test from 'ava';
import { MagicAdminSDK } from '../../../../src/admin-sdk/core/sdk';
import { BaseModule } from '../../../../src/admin-sdk/modules/base-module';
import { createMagicAdminSDK } from '../../../lib/factories';

test.serial('#01: Initializes `BaseModule`', t => {
const sdk = createMagicAdminSDK();

const baseModule: any = new (BaseModule as any)(sdk);

t.true(baseModule instanceof BaseModule);
t.true(baseModule.sdk instanceof MagicAdminSDK);
});
Loading