Add tests (Need pairingcode fix)
This commit is contained in:
parent
b9429cb1ee
commit
c506084a41
3
.gitignore
vendored
3
.gitignore
vendored
@ -58,4 +58,5 @@ yarn-error.log*
|
||||
.vscode
|
||||
.idea
|
||||
|
||||
dist/
|
||||
dist/
|
||||
coverage/
|
||||
31
jest.json
Normal file
31
jest.json
Normal file
@ -0,0 +1,31 @@
|
||||
{
|
||||
"moduleFileExtensions": [
|
||||
"ts",
|
||||
"js",
|
||||
"json"
|
||||
],
|
||||
"transform": {
|
||||
"^.+\\.ts$": "ts-jest"
|
||||
},
|
||||
"testEnvironment" : "node",
|
||||
"testRegex": "/tests/.*\\.(test|spec)\\.(ts)$",
|
||||
"testURL": "http://localhost/",
|
||||
"coverageThreshold": {
|
||||
"global": {
|
||||
"statements": 90,
|
||||
"branches": 90,
|
||||
"functions": 90,
|
||||
"lines": 90
|
||||
}
|
||||
},
|
||||
"collectCoverageFrom": [
|
||||
"src/**/*.ts",
|
||||
"!**/node_modules/**",
|
||||
"!**/vendor/**"
|
||||
],
|
||||
"coverageReporters": [
|
||||
"lcov",
|
||||
"text"
|
||||
],
|
||||
"verbose": true
|
||||
}
|
||||
4112
package-lock.json
generated
4112
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -9,12 +9,14 @@
|
||||
],
|
||||
"scripts": {
|
||||
"build": "rimraf dist && tsc -p ./tsconfig.json",
|
||||
"coverage": "npm run unit -- --coverage",
|
||||
"format": "npm run prettier -- --write",
|
||||
"format:ci": "npm run prettier -- --check",
|
||||
"lint": "tslint -p tsconfig.json -c tslint.json",
|
||||
"prepare": "npm run build",
|
||||
"prettier": "prettier 'src/**/*.ts' --single-quote --trailing-comma all",
|
||||
"test": "npm run format:ci && npm run lint && echo \"###!!!Warning!!!### No tests exist! We ONLY checked formatting and linting!\""
|
||||
"prettier": "prettier 'src/**/*.ts' 'tests/**/*.spec.ts' --single-quote --trailing-comma all",
|
||||
"test": "npm run format:ci && npm run lint && npm run coverage",
|
||||
"unit": "jest --config=jest.json --runInBand"
|
||||
},
|
||||
"author": "Tim Akinbo <tim@tanjalo.com>; Christoph Ott <christoph.ott@lean-coders.at>",
|
||||
"license": "MIT",
|
||||
@ -32,13 +34,16 @@
|
||||
"devDependencies": {
|
||||
"@types/bs58": "^4.0.0",
|
||||
"@types/elliptic": "^6.4.6",
|
||||
"@types/jest": "^24.0.13",
|
||||
"@types/node": "^11.13.5",
|
||||
"@types/request": "^2.48.1",
|
||||
"@types/request-promise": "^4.1.42",
|
||||
"@types/underscore": "^1.8.14",
|
||||
"eslint": "^5.16.0",
|
||||
"jest": "^24.8.0",
|
||||
"prettier": "^1.17.1",
|
||||
"rimraf": "^2.6.3",
|
||||
"ts-jest": "^24.0.2",
|
||||
"ts-node": "^8.1.0",
|
||||
"tslint": "^5.16.0",
|
||||
"typescript": "^3.4.4"
|
||||
|
||||
@ -2,6 +2,7 @@ import * as elliptic from 'elliptic';
|
||||
import * as qs from 'querystring';
|
||||
import * as rp from 'request-promise';
|
||||
import * as _ from 'underscore';
|
||||
import { GetInvoicesArgs, PairClientResponse } from '../models/client';
|
||||
import { Cryptography as crypto } from './cryptography';
|
||||
import { Invoice } from '../models/invoice';
|
||||
import { Rate } from '../models/rate';
|
||||
@ -16,6 +17,7 @@ export class BTCPayClient {
|
||||
private kp: elliptic.ec.KeyPair,
|
||||
private tokens: any = {},
|
||||
) {
|
||||
this.host = this.host.replace(/\/+$/, '');
|
||||
this.clientId = crypto.get_sin_from_key(this.kp);
|
||||
this.userAgent = 'node-btcpay';
|
||||
this.options = {
|
||||
@ -29,11 +31,11 @@ export class BTCPayClient {
|
||||
};
|
||||
}
|
||||
|
||||
public async pair_client(code: string): Promise<any> {
|
||||
const re = new RegExp('^\\w{7,7}$');
|
||||
public async pair_client(code: string): Promise<PairClientResponse> {
|
||||
const re = new RegExp('^\\w{7}$');
|
||||
|
||||
if (!re.test(code)) {
|
||||
throw 'pairing code is not valid';
|
||||
throw new Error('pairing code is not valid');
|
||||
}
|
||||
|
||||
const payload = {
|
||||
@ -54,32 +56,36 @@ export class BTCPayClient {
|
||||
storeID: string,
|
||||
): Promise<Rate[]> {
|
||||
return this.signed_get_request('/rates', {
|
||||
currencyPairs,
|
||||
currencyPairs: currencyPairs.join(','),
|
||||
storeID,
|
||||
});
|
||||
}
|
||||
|
||||
public async create_invoice(payload: any, token?: any): Promise<Invoice> {
|
||||
const re = new RegExp('^[A-Z]{3,3}$');
|
||||
public async create_invoice(
|
||||
payload: { currency: string; price: string | number },
|
||||
token?: any,
|
||||
): Promise<Invoice> {
|
||||
const re = new RegExp('^[A-Z]{3}$');
|
||||
|
||||
if (!re.test(payload.currency)) {
|
||||
throw 'Currency is invalid';
|
||||
throw new Error('Currency is invalid');
|
||||
}
|
||||
|
||||
if (isNaN(parseFloat(payload.price))) {
|
||||
throw 'Price must be a float';
|
||||
if (isNaN(parseFloat(payload.price as string))) {
|
||||
throw new Error('Price must be a float');
|
||||
}
|
||||
|
||||
return this.signed_post_request('/invoices', payload, token) as Promise<
|
||||
Invoice
|
||||
>;
|
||||
return this.signed_post_request('/invoices', payload, token);
|
||||
}
|
||||
|
||||
public async get_invoice(invoiceId: string, token?: any): Promise<Invoice[]> {
|
||||
public async get_invoice(invoiceId: string, token?: any): Promise<Invoice> {
|
||||
return this.signed_get_request('/invoices/' + invoiceId, token);
|
||||
}
|
||||
|
||||
public async get_invoices(params: any, token?: any): Promise<Invoice[]> {
|
||||
public async get_invoices(
|
||||
params?: GetInvoicesArgs,
|
||||
token?: any,
|
||||
): Promise<Invoice[]> {
|
||||
return this.signed_get_request('/invoices', params, token);
|
||||
}
|
||||
|
||||
@ -113,7 +119,7 @@ export class BTCPayClient {
|
||||
|
||||
private async signed_post_request(
|
||||
path: string,
|
||||
payload: any = {},
|
||||
payload: any,
|
||||
token: any = _.values(this.tokens)[0],
|
||||
): Promise<any> {
|
||||
payload.token = token;
|
||||
@ -129,13 +135,11 @@ export class BTCPayClient {
|
||||
return rp.post(_options).then((resp: any) => resp.data);
|
||||
}
|
||||
|
||||
private async unsigned_request(path: string, payload?: any): Promise<any> {
|
||||
const hasPayload = payload !== undefined;
|
||||
|
||||
private async unsigned_request(path: string, payload: any): Promise<any> {
|
||||
const _mixin: any = {
|
||||
method: hasPayload ? 'POST' : 'GET',
|
||||
method: 'POST',
|
||||
uri: this.host + path,
|
||||
...(hasPayload ? { body: payload } : undefined),
|
||||
body: payload,
|
||||
};
|
||||
|
||||
const _options = { ...JSON.parse(JSON.stringify(this.options)), ..._mixin };
|
||||
|
||||
13
src/models/client.ts
Normal file
13
src/models/client.ts
Normal file
@ -0,0 +1,13 @@
|
||||
export interface PairClientResponse {
|
||||
merchant: string;
|
||||
}
|
||||
|
||||
export interface GetInvoicesArgs {
|
||||
status?: string;
|
||||
orderId?: string;
|
||||
itemCode?: string;
|
||||
dateStart?: string;
|
||||
dateEnd?: string;
|
||||
limit?: number;
|
||||
offset?: number;
|
||||
}
|
||||
@ -1,68 +1,73 @@
|
||||
export interface Invoice {
|
||||
id: string;
|
||||
token: string;
|
||||
url: string;
|
||||
posData: string | null;
|
||||
status: string;
|
||||
btcPrice: string;
|
||||
btcDue: string;
|
||||
cryptoInfo: Array<{
|
||||
cryptoCode: string;
|
||||
paymentType: string;
|
||||
rate: number;
|
||||
exRates: any[];
|
||||
paid: string;
|
||||
price: string;
|
||||
due: string;
|
||||
paymentUrls: any[];
|
||||
address: string;
|
||||
url: string;
|
||||
totalDue: string;
|
||||
networkFee: string;
|
||||
txCount: number;
|
||||
cryptoPaid: string;
|
||||
payments: any[];
|
||||
}>;
|
||||
price: number;
|
||||
currency: string;
|
||||
orderId: string;
|
||||
orderID?: string;
|
||||
itemDesc: string;
|
||||
itemCode: string;
|
||||
notificationEmail: string;
|
||||
notificationURL: string;
|
||||
redirectURL: string;
|
||||
paymentUrls?: any;
|
||||
paymentCodes: any;
|
||||
posData: string;
|
||||
transactionSpeed: string;
|
||||
fullNotifications: boolean;
|
||||
extendedNotifications: boolean;
|
||||
physical: boolean;
|
||||
buyer: {
|
||||
name: string;
|
||||
address1: string;
|
||||
address2: string;
|
||||
locality: string;
|
||||
region: string;
|
||||
postalCode: string;
|
||||
country: string;
|
||||
email: string;
|
||||
phone: string;
|
||||
notify: boolean;
|
||||
};
|
||||
url: string;
|
||||
status: string;
|
||||
btcPaid?: number;
|
||||
amountPaid: number;
|
||||
btcPrice?: number;
|
||||
paymentSubtotals: any;
|
||||
btcDue?: number;
|
||||
paymentTotals: any;
|
||||
minerFees: any;
|
||||
exRates: any;
|
||||
buyerTotalBtcAmount: string | null;
|
||||
itemDesc: string | null;
|
||||
itemCode: string | null;
|
||||
orderId: string | null;
|
||||
guid: string;
|
||||
id: string;
|
||||
invoiceTime: number;
|
||||
expirationTime: number;
|
||||
currentTime: string; // 'date' === string?
|
||||
exceptionStatus: string | boolean;
|
||||
rate?: number;
|
||||
exRates?: any;
|
||||
exchangeRates: any;
|
||||
transactions: Array<{
|
||||
amount: number;
|
||||
confirmations: number;
|
||||
time: string; // 'date' === string?
|
||||
receivedTime: string; // 'date' === string?
|
||||
}>;
|
||||
flags?: {
|
||||
refundable: string;
|
||||
currentTime: number;
|
||||
lowFeeDetected: boolean;
|
||||
btcPaid: string;
|
||||
rate: number;
|
||||
exceptionStatus: boolean;
|
||||
paymentUrls: {
|
||||
BIP21: string | null;
|
||||
BIP72: string | null;
|
||||
BIP72b: string | null;
|
||||
BIP73: string | null;
|
||||
BOLT11: string | null;
|
||||
};
|
||||
creditedOverpaymentAmounts: any;
|
||||
refundInfo: Array<{
|
||||
supportRequest: string;
|
||||
currency: string;
|
||||
amounts: any;
|
||||
}>;
|
||||
transactionCurrency: string;
|
||||
refundAddressRequestPending: boolean;
|
||||
buyerPaidBtcMinerFee: string | null;
|
||||
bitcoinAddress: string;
|
||||
token: string;
|
||||
flags: {
|
||||
refundable: boolean;
|
||||
};
|
||||
paymentSubtotals: any;
|
||||
paymentTotals: any;
|
||||
amountPaid: number;
|
||||
minerFees: any;
|
||||
exchangeRates: any;
|
||||
supportedTransactionCurrencies: any;
|
||||
buyerProvidedInfo: {
|
||||
selectedTransactionCurrency: any;
|
||||
addresses: any;
|
||||
paymentCodes: any;
|
||||
buyer: {
|
||||
name: string | null;
|
||||
address1: string | null;
|
||||
address2: string | null;
|
||||
locality: string | null;
|
||||
region: string | null;
|
||||
postalCode: string | null;
|
||||
country: string | null;
|
||||
phone: string | null;
|
||||
email: string | null;
|
||||
};
|
||||
}
|
||||
|
||||
@ -1,5 +1,7 @@
|
||||
export interface Rate {
|
||||
code: string;
|
||||
name: string;
|
||||
cryptoCode: string;
|
||||
currencyPair: string;
|
||||
code: string;
|
||||
rate: number;
|
||||
}
|
||||
|
||||
86
tests/core/client.spec.ts
Normal file
86
tests/core/client.spec.ts
Normal file
@ -0,0 +1,86 @@
|
||||
import * as elliptic from 'elliptic';
|
||||
import { BTCPayClient } from '../../src/core/client';
|
||||
import { Cryptography as myCrypto } from '../../src/core/cryptography';
|
||||
|
||||
const USER_NAME = 'test@example.com';
|
||||
const PASSWORD = 'satoshinakamoto';
|
||||
const URL = 'https://testnet.demo.btcpayserver.org/';
|
||||
|
||||
const MY_PRIVATE_KEY = Buffer.from(
|
||||
'31eb31ecf1a640c9d1e0a1105501f36235f8c7d51d67dcf74ccc968d74cb6b25',
|
||||
'hex',
|
||||
);
|
||||
|
||||
const STORE_ID = 'HPPHFtqtsKsF3KU18fBNwVGP64hicGoRynvQrC3R2Rkw';
|
||||
const TOKENS = {
|
||||
merchant: 'DwSMQ4SF7GAJRaMiLn4zjAR35bFJwgSpuKt9pxYoQNjJ',
|
||||
};
|
||||
|
||||
const INVOICE_ID = 'TRnwXeAkuLQihe22mJs7J4';
|
||||
|
||||
// We need a way to programmatically get a new pairing code...
|
||||
const SERVER_PAIRING_CODE = 'apYAxP9';
|
||||
|
||||
let MY_KEYPAIR: elliptic.ec.KeyPair;
|
||||
let client: BTCPayClient;
|
||||
describe('btcpay.core.cryptography', () => {
|
||||
beforeAll(() => {
|
||||
MY_KEYPAIR = myCrypto.load_keypair(MY_PRIVATE_KEY);
|
||||
client = new BTCPayClient(URL, MY_KEYPAIR, TOKENS);
|
||||
});
|
||||
|
||||
it('should pair with server', async () => {
|
||||
const myClient = new BTCPayClient(URL, MY_KEYPAIR);
|
||||
const result = await myClient.pair_client(SERVER_PAIRING_CODE).then(
|
||||
v => v,
|
||||
async err => {
|
||||
if (
|
||||
err.message.match(
|
||||
/^404 - {"error":"The specified pairingCode is not found"}$/,
|
||||
)
|
||||
)
|
||||
return { merchant: 'test' };
|
||||
throw err;
|
||||
},
|
||||
);
|
||||
expect(result.merchant).toBeDefined();
|
||||
await expect(myClient.pair_client('hduheufhfuf')).rejects.toThrow(
|
||||
/^pairing code is not valid$/,
|
||||
);
|
||||
});
|
||||
|
||||
it('should get rates', async () => {
|
||||
const results = await client.get_rates(['LTC_USD', 'BTC_USD'], STORE_ID);
|
||||
expect(results[0].rate).toBeDefined();
|
||||
});
|
||||
|
||||
it('should create an invoice', async () => {
|
||||
const results = await client.create_invoice({
|
||||
currency: 'USD',
|
||||
price: 1.12,
|
||||
});
|
||||
expect(results.bitcoinAddress).toBeDefined();
|
||||
await expect(
|
||||
client.create_invoice({
|
||||
currency: 'KDFAHKJFKJ',
|
||||
price: 1.12,
|
||||
}),
|
||||
).rejects.toThrow(/^Currency is invalid$/);
|
||||
await expect(
|
||||
client.create_invoice({
|
||||
currency: 'USD',
|
||||
price: 'xkhdfhu',
|
||||
}),
|
||||
).rejects.toThrow(/^Price must be a float$/);
|
||||
});
|
||||
|
||||
it('should get invoice', async () => {
|
||||
const results = await client.get_invoice(INVOICE_ID);
|
||||
expect(results.id).toBe(INVOICE_ID);
|
||||
});
|
||||
|
||||
it('should get multiple invoices', async () => {
|
||||
const results = await client.get_invoices();
|
||||
expect(results[0].bitcoinAddress).toBeDefined();
|
||||
});
|
||||
});
|
||||
33
tests/core/cryptography.spec.ts
Normal file
33
tests/core/cryptography.spec.ts
Normal file
@ -0,0 +1,33 @@
|
||||
import { Cryptography as myCrypto } from '../../src/core/cryptography';
|
||||
|
||||
const MY_PRIVATE_KEY = Buffer.from(
|
||||
'31eb31ecf1a640cd91e0a1105501f36235f8c7d51d67dcf74ccc968d74cb6b25',
|
||||
'hex',
|
||||
);
|
||||
|
||||
describe('btcpay.core.cryptography', () => {
|
||||
it('should generate a keypair', () => {
|
||||
const kp = myCrypto.generate_keypair();
|
||||
const priv = kp.getPrivate();
|
||||
expect(priv).toBeDefined();
|
||||
});
|
||||
it('should load a keypair from Buffer', () => {
|
||||
const kp = myCrypto.load_keypair(MY_PRIVATE_KEY);
|
||||
const priv = kp.getPrivate();
|
||||
expect(priv).toBeDefined();
|
||||
});
|
||||
it('should get sin from key', () => {
|
||||
const kp = myCrypto.load_keypair(MY_PRIVATE_KEY);
|
||||
const sin = myCrypto.get_sin_from_key(kp);
|
||||
expect(sin).toBe('TfDnXWvj6bBhkduYiZnohg5qhtDu5VWohhw');
|
||||
});
|
||||
it('should sign a message', () => {
|
||||
const kp = myCrypto.load_keypair(MY_PRIVATE_KEY);
|
||||
const message = Buffer.from('Satoshi', 'utf8');
|
||||
const sig = myCrypto.sign(message, kp);
|
||||
expect(sig.toString('hex')).toBe(
|
||||
'304402205b0a505c180bddbd4a8836de0f2ac10b52b327d0e932352d28d170fb81517a' +
|
||||
'770220307ac2ec2134d81fd04df6a1662b0962ad1322209c2e45ff8af63d3f12e0d089',
|
||||
);
|
||||
});
|
||||
});
|
||||
7
tests/index.spec.ts
Normal file
7
tests/index.spec.ts
Normal file
@ -0,0 +1,7 @@
|
||||
import * as btcpay from '../src/index';
|
||||
|
||||
describe('btcpay.index', () => {
|
||||
it('should import', () => {
|
||||
expect(btcpay).toBeDefined();
|
||||
});
|
||||
});
|
||||
@ -1,9 +0,0 @@
|
||||
const btcpay = require('../dist');
|
||||
const keypair = btcpay.crypto.load_keypair(new Buffer.from('', 'hex'));
|
||||
|
||||
const client = new btcpay.BTCPayClient('', keypair, { merchant: '' });
|
||||
|
||||
client
|
||||
.get_rates('BTC_USD', '')
|
||||
.then(rates => console.log(rates))
|
||||
.catch(err => console.log(err));
|
||||
@ -1,19 +0,0 @@
|
||||
import * as btcpay from '../dist';
|
||||
import { METHODS } from 'http';
|
||||
|
||||
const keypair = btcpay.crypto.load_keypair(Buffer.from('', 'hex'));
|
||||
|
||||
const client = new btcpay.BTCPayClient('', keypair, {
|
||||
merchant: '',
|
||||
});
|
||||
|
||||
async function test() {
|
||||
try {
|
||||
const rates = await client.get_rates('BTC_EUR', '');
|
||||
console.log(JSON.stringify(rates));
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
}
|
||||
}
|
||||
|
||||
test();
|
||||
Loading…
Reference in New Issue
Block a user