Convert tests to jest, added tls cert/macaroon file support

This commit is contained in:
Djuri Baars 2022-01-18 18:56:54 +01:00
parent c4c7288d39
commit c6dfc5f8c8
11 changed files with 209 additions and 114 deletions

View File

@ -1,6 +1,8 @@
PORT=7464
ALLOWED_HOSTS=['*']
MACAROON=ABC
LND_REST_API=localhost:8080
REJECT_UNAUTHORIZED=false
LND_REST_API_WS=wss://localhost:8080
LND_REST_API=https://localhost:8080
REJECT_UNAUTHORIZED=0
TLS_CERT_BASE64=ABCD=
#MACAROON_FILE=readonly.macaroon
#TLS_CERT_FILE=tls.cert

View File

@ -35,19 +35,22 @@
"@types/node": "^17.0.8",
"@typescript-eslint/eslint-plugin": "^5.8.1",
"@typescript-eslint/parser": "^5.8.1",
"axios-mock-adapter": "^1.20.0",
"concurrently": "^7.0.0",
"eslint": "^8.5.0",
"eslint-config-prettier": "^8.3.0",
"fastify-tsconfig": "^1.0.1",
"jest": "^27.4.7",
"jest-websocket-mock": "^2.2.1",
"mock-socket": "^9.1.0",
"prettier": "^2.5.1",
"ts-jest": "^27.1.2",
"ts-node": "^10.4.0"
},
"scripts": {
"start": "ts-node src/index.ts",
"start": "ts-node src/server.ts",
"build:ts": "tsc",
"lint": "eslint . --ext .ts",
"test": "npm run build:ts && tsc -p test/tsconfig.json && tap --no-coverage-map --no-check-coverage --ts test/**/*.test.ts"
"test": "jest"
}
}

View File

@ -38,7 +38,7 @@ export default class ChannelController {
.emit('channel', chanInfo);
})
.catch(() => {
console.log('Error sending channel update');
console.log(`Error sending channel update for ${channel.chan_id}`);
});
}
});

View File

@ -1,4 +1,5 @@
import * as https from 'https';
import {existsSync, readFileSync} from 'fs';
import axios from 'axios';
import {Subject} from 'rxjs';
@ -8,14 +9,39 @@ import dotenv from 'dotenv';
dotenv.config();
const decodedTlsCert = Buffer.from(
let tlsCertData: string;
let macaroonData: string;
let rejectUnauthorized = true;
// Check if files exists, otherwise try to read base64 data
if (existsSync(process.env.MACAROON_FILE)) {
macaroonData = readFileSync(process.env.MACAROON_FILE).toString('hex');
} else {
macaroonData = process.env.MACAROON || '';
}
if (existsSync(process.env.TLS_CERT_FILE)) {
tlsCertData = readFileSync(process.env.TLS_CERT_FILE).toString('ascii');
} else {
const tlsCertData = Buffer.from(
process.env.TLS_CERT_BASE64 || '',
'base64',
).toString('ascii');
}
if (tlsCertData === '' || macaroonData === '') {
throw new Error(
'TLS Certificate or Macaroon could not be loaded, this is required to run ringtools-ts-server',
);
}
if (process.env.REJECT_UNAUTHORIZED) {
rejectUnauthorized = Boolean(Number(process.env.REJECT_UNAUTHORIZED));
}
const httpsAgent = new https.Agent({
rejectUnauthorized: Boolean(process.env.REJECT_UNAUTHORIZED) || true,
ca: decodedTlsCert,
rejectUnauthorized,
ca: tlsCertData,
});
axios.defaults.httpsAgent = httpsAgent;
@ -40,20 +66,24 @@ export class LndService {
channelUpdateSubject: Subject<any> = new Subject();
nodeUpdateSubject: Subject<any> = new Subject();
closedChannelSubject: Subject<any> = new Subject();
lndRestApiWsUrl!: string;
lndRestApiUrl!: string;
macaroon!: string;
constructor() {}
@Initializer()
init() {
this.lndRestApiUrl = process.env.LND_REST_API || 'localhost:8080';
this.macaroon = process.env.MACAROON || '';
this.lndRestApiWsUrl = process.env.LND_REST_API_WS || 'ws://localhost:8080';
this.lndRestApiUrl = process.env.LND_REST_API || 'http://localhost:8080';
this.macaroon = macaroonData;
this.ws = new WebSocket(
`wss://${this.lndRestApiUrl}/v1/graph/subscribe?method=GET`,
`${this.lndRestApiWsUrl}/v1/graph/subscribe?method=GET`,
{
rejectUnauthorized: Boolean(process.env.REJECT_UNAUTHORIZED) || true,
ca: decodedTlsCert,
rejectUnauthorized,
ca: tlsCertData,
headers: {
'Grpc-Metadata-Macaroon': this.macaroon,
},
@ -99,7 +129,7 @@ export class LndService {
}
private doLndGet(endPoint: string) {
return axios.get(`https://${this.lndRestApiUrl}/${endPoint}`, {
return axios.get(`${this.lndRestApiUrl}/${endPoint}`, {
responseType: 'json',
headers: {

View File

@ -1,13 +1,14 @@
import tap from 'tap';
import {testBuild} from './helper';
tap.test('requests the "/" route', async (t: any) => {
const app = await testBuild(t);
const response = await app.inject({
describe('app tests', () => {
const app = testBuild();
test('requests the "/" route', async () => {
const res = await app.inject({
method: 'GET',
url: '/',
});
t.equal(response.statusCode, 200, 'returns a status code of 200');
expect(res.statusCode).toBe(404);
});
});

View File

@ -1,18 +1,14 @@
import tap from 'tap';
import {testBuild} from '../helper';
import {testBuild} from '../../test/helper';
tap.test('Non-numeric channels should return 404', async (t: any) => {
t.plan(1);
const app = await testBuild(t);
describe('channel controller tests', () => {
const app = testBuild();
test('Non-numeric channels should return 404', async () => {
const response = await app.inject({
url: '/channel/abcdefg',
method: 'GET',
});
t.equal(response.statusCode, 404, 'returns a status code of 404');
t.teardown(() => app.close());
expect(response.statusCode).toEqual(404);
});
});

View File

@ -1,18 +1,14 @@
import tap from 'tap';
import {testBuild} from '../helper';
import {testBuild} from '../../test/helper';
tap.test('Main controller should do nothing', async (t: any) => {
t.plan(1);
const app = await testBuild(t);
describe('main controller tests', () => {
const app = testBuild();
test('Main controller should do nothing', async () => {
const response = await app.inject({
url: '/',
method: 'GET',
});
t.equal(response.statusCode, 404, 'returns a status code of 404');
t.teardown(() => app.close());
expect(response.statusCode).toBe(404);
});
});

View File

@ -1,42 +1,73 @@
import tap from 'tap';
import {mock, testBuild} from '../helper';
import {testBuild} from '../helper';
describe('node controller tests', () => {
const app = testBuild();
tap.test('Too short pubkeys should return 404', async (t: any) => {
t.plan(1);
const app = await testBuild(t);
const response = await app.inject({
test('Too short pubkeys should return 404', (done) => {
app
.inject({
url: '/node/abcdefg',
method: 'GET',
})
.then((response) => {
expect(response.statusCode).toBe(404);
done();
});
});
t.equal(response.statusCode, 404, 'returns a status code of 404');
t.teardown(() => app.close());
});
tap.test('Too long pubkeys should return 404', async (t: any) => {
t.plan(1);
const app = await testBuild(t);
const response = await app.inject({
test('Too long pubkeys should return 404', (done) => {
app
.inject({
url: '/node/0380b3dbdf090cacee19eb4dc7a82630bd3de8b12608dd7bee971fb3cd2a5ae2fcd',
method: 'GET',
})
.then((response) => {
expect(response.statusCode).toBe(404);
done();
});
});
t.equal(response.statusCode, 404, 'returns a status code of 404');
t.teardown(() => app.close());
});
tap.test('Node URI should return 404', async (t: any) => {
t.plan(1);
const app = await testBuild(t);
const response = await app.inject({
test('Node URI should return 404', (done) => {
app
.inject({
url: '/node/0380b3dbdf090cacee19eb4dc7a82630bd3de8b12608dd7bee971fb3cd2a5ae2fc@[2a04:52c0:103:c1e3::1]:9735',
method: 'GET',
})
.then((response) => {
expect(response.statusCode).toBe(404);
done();
});
});
t.equal(response.statusCode, 404, 'returns a status code of 404');
t.teardown(() => app.close());
test('Node URI should return 404', (done) => {
app
.inject({
url: '/node/0380b3dbdf090cacee19eb4dc7a82630bd3de8b12608dd7bee971fb3cd2a5ae2fc@[2a04:52c0:103:c1e3::1]:9735',
method: 'GET',
})
.then((response) => {
expect(response.statusCode).toBe(404);
done();
});
});
// FIXME: Fix WebSocket mock
// test('Valid Node URI should give nodeInfo', (done) => {
// mock.onGet(/^\/node\/\d+/).reply((config) => {
// console.log("Mock!", config.url);
// return [200, {}]
// });
// app
// .inject({
// url: '/node/0205a19356bbb7482057356aef070285a2ce6141d2448545210e9d575b57eddd37',
// method: 'GET',
// })
// .then((response) => {
// expect(response.statusCode).toBe(200);
// done();
// });
// });
});

View File

@ -2,37 +2,42 @@
import Fastify from 'fastify'
import fp from 'fastify-plugin'
import { build } from '../src/app'
import * as tap from 'tap';
import MockAdapter from "axios-mock-adapter";
import axios from "axios";
export type Test = typeof tap['Test']['prototype'];
process.env['REJECT_UNAUTHORIZED'] = '0';
process.env['NODE_TLS_REJECT_UNAUTHORIZED'] = '0';
process.env['LND_REST_API_WS'] = 'ws://localhost:8080';
const edgeMockData = {"channel_id":"760383758935523329","chan_point":"0000000000000000000000000000000000000000000000000000000000000000:0","last_update":1642475331,"node1_pub":"0205a19356bbb7482057356aef070285a2ce6141d2448545210e9d575b57eddd37","node2_pub":"03d1b19ebc6cdce5685bad391f266d077f0cacd2e5c1f46824e1427ce7dba2f630","capacity":"0","node1_policy":{"time_lock_delta":42,"min_htlc":"1000","fee_base_msat":"1","fee_rate_milli_msat":"100","disabled":false,"max_htlc_msat":"990000000","last_update":1642475331},"node2_policy":{"time_lock_delta":40,"min_htlc":"1000","fee_base_msat":"0","fee_rate_milli_msat":"100","disabled":false,"max_htlc_msat":"990000000","last_update":1642460307}};
// Fill in this config with all the configurations
// needed for testing the application
async function config () {
return {}
}
// Automatically build and tear down our instance
async function testBuild (t: Test) {
const app = Fastify()
let mock;
// fastify-plugin ensures that all decorators
// are exposed for testing purposes, this is
// different from the production setup
void app.register(fp(build), await config())
export function testBuild() {
const app = Fastify();
beforeAll(async () => {
void app.register(fp(build), await config());
await app.ready();
});
// Tear down our app after we are done
t.teardown(async() => {
if (app)
await app.close()
})
beforeEach(() => {
mock = new MockAdapter(axios);
mock.onAny(/^\/v1\/graph\/edge\/\d+/, edgeMockData);
});
return app
afterEach(() => {
if (mock)
mock.reset();
});
afterAll(() => app.close());
return app;
}
export {
config,
testBuild
}
export { mock };

View File

@ -1,9 +1,9 @@
import tap from 'tap';
import { LndService } from '../../src/services/lnd-service';
tap.test('LndService should instantiate', async (t: any) => {
t.plan(1)
describe('LndService tests', () => {
test('LndService should instantiate', async () => {
const lndService = new LndService();
t.type(lndService, LndService);
expect(lndService).toBeInstanceOf(LndService);
});
});

View File

@ -1443,6 +1443,15 @@ axe-core@^4.3.5:
resolved "https://registry.yarnpkg.com/axe-core/-/axe-core-4.3.5.tgz#78d6911ba317a8262bfee292aeafcc1e04b49cc5"
integrity sha512-WKTW1+xAzhMS5dJsxWkliixlO/PqC4VhmO9T4juNYcaTg9jzWiJsou6m5pxWYGfigWbwzJWeFY6z47a+4neRXA==
axios-mock-adapter@^1.20.0:
version "1.20.0"
resolved "https://registry.yarnpkg.com/axios-mock-adapter/-/axios-mock-adapter-1.20.0.tgz#21f5b4b625306f43e8c05673616719da86e20dcb"
integrity sha512-shZRhTjLP0WWdcvHKf3rH3iW9deb3UdKbdnKUoHmmsnBhVXN3sjPJM6ZvQ2r/ywgvBVQrMnjrSyQab60G1sr2w==
dependencies:
fast-deep-equal "^3.1.3"
is-blob "^2.1.0"
is-buffer "^2.0.5"
axios@^0.24.0:
version "0.24.0"
resolved "https://registry.yarnpkg.com/axios/-/axios-0.24.0.tgz#804e6fa1e4b9c5288501dd9dff56a7a0940d20d6"
@ -3484,6 +3493,11 @@ is-binary-path@~2.1.0:
dependencies:
binary-extensions "^2.0.0"
is-blob@^2.1.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/is-blob/-/is-blob-2.1.0.tgz#e36cd82c90653f1e1b930f11baf9c64216a05385"
integrity sha512-SZ/fTft5eUhQM6oF/ZaASFDEdbFVe89Imltn9uZr03wdKMcWNVYSMjQPFtg05QuNkt5l5c135ElvXEQG0rk4tw==
is-boolean-object@^1.1.0:
version "1.1.2"
resolved "https://registry.yarnpkg.com/is-boolean-object/-/is-boolean-object-1.1.2.tgz#5c6dc200246dd9321ae4b885a114bb1f75f63719"
@ -3492,6 +3506,11 @@ is-boolean-object@^1.1.0:
call-bind "^1.0.2"
has-tostringtag "^1.0.0"
is-buffer@^2.0.5:
version "2.0.5"
resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-2.0.5.tgz#ebc252e400d22ff8d77fa09888821a24a658c191"
integrity sha512-i2R6zNFDwgEHJyQUtJEk0XFi1i0dPFn/oqjK3/vPCcDeJvW5NQ83V8QbicfF1SupOaB0h8ntgBC2YiE7dfyctQ==
is-callable@^1.1.4, is-callable@^1.2.4:
version "1.2.4"
resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.4.tgz#47301d58dd0259407865547853df6d61fe471945"
@ -3802,7 +3821,7 @@ jest-config@^27.4.7:
pretty-format "^27.4.6"
slash "^3.0.0"
jest-diff@^27.0.0, jest-diff@^27.4.6:
jest-diff@^27.0.0, jest-diff@^27.0.2, jest-diff@^27.4.6:
version "27.4.6"
resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-27.4.6.tgz#93815774d2012a2cbb6cf23f84d48c7a2618f98d"
integrity sha512-zjaB0sh0Lb13VyPsd92V7HkqF6yKRH9vm33rwBt7rPYrpQvS1nCvlIy2pICbKta+ZjWngYLNn4cCK4nyZkjS/w==
@ -4108,6 +4127,13 @@ jest-watcher@^27.4.6:
jest-util "^27.4.2"
string-length "^4.0.1"
jest-websocket-mock@^2.2.1:
version "2.2.1"
resolved "https://registry.yarnpkg.com/jest-websocket-mock/-/jest-websocket-mock-2.2.1.tgz#db4fc63733c9fc549c1fd0f79e6db8b3f947af1d"
integrity sha512-fhsGLXrPfs06PhHoxqOSA9yZ6Rb4qYrf4Wcm7/nfRzjlrf1gIeuhYUkzMRjjE0TMQ37SwkmeLanwrZY4ZaNp8g==
dependencies:
jest-diff "^27.0.2"
jest-worker@^27.4.6:
version "27.4.6"
resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-27.4.6.tgz#5d2d93db419566cb680752ca0792780e71b3273e"
@ -4515,6 +4541,11 @@ mkdirp@^1.0.4:
resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e"
integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==
mock-socket@^9.1.0:
version "9.1.0"
resolved "https://registry.yarnpkg.com/mock-socket/-/mock-socket-9.1.0.tgz#583f5984aa5759909c1b0f43676c669060722596"
integrity sha512-zNsH8h0D7buVMDZ2X1GyFYso9A1X1Co/TDfFs0AIKhSLkJeh381HYESNl/mL6BzmQpNOxZVnNhEDS1OWBrS+cQ==
mri@1.1.4:
version "1.1.4"
resolved "https://registry.yarnpkg.com/mri/-/mri-1.1.4.tgz#7cb1dd1b9b40905f1fac053abe25b6720f44744a"