divvy-rest
Version:
A RESTful API for submitting payments and monitoring accounts on the Divvy network.
1,246 lines (1,118 loc) • 38.9 kB
JavaScript
;
var supertest = require('supertest');
var _app = require('../server/express_app');
var app = supertest(_app);
var assert = require('assert-diff');
var ws = require('ws');
var route = new (require('events').EventEmitter);
var fixtures = require('./fixtures')._payments;
var RL = require('divvy-lib');
var remote = require('../server/api').remote;
var testutils = { };
// check if obj has keys
testutils.hasKeys = function(obj, keys) {
var list = Object.keys(obj);
var hasAllKeys = true;
var missing = {};
keys.forEach(function(key) {
if (list.indexOf(key) === -1) {
hasAllKeys = false;
missing[key] = true;
}
});
return {hasAllKeys: hasAllKeys, missing: missing};
};
// used to mark the orderings and count of incoming divvyd
function OrderList(list) {
// list = [{command:<command>}, ... ]
var _list = list;
var idx = 0;
this.isMock = true;
this.create = function(__list) {
_list = __list;
idx = 0;
};
this.mark = function(command) {
if ((_list[idx]) && (_list[idx].command === command)) {
idx++;
} else {
throw new Error('out of order divvyd command', command);
}
};
this.test = function() {
if (this.isMock) {
return idx === _list.length;
}
return true;
};
this.reset = function() {
_list = [];
idx = 0;
};
}
var orderlist = new OrderList();
/**
* Payment tests
* This file, _payment-test.js describes tests in a different format from
* other other tests
*
* If you consider making changes to a test in this file, consider moving the
* tests over to payment-test.js and adopting the structure we have for the
* other tests
*
* New tests should not be added in the structure laid out here, but in the
* structure we use in the other test files.
*/
suite('payments', function() {
var divvyd;
suiteSetup(function(done) {
var self = this;
self.remote = remote;
divvyd = new ws.Server({port: 5150});
route.on('ping', fixtures.ping);
route.on('subscribe', fixtures.subscribe);
route.on('response', fixtures.response);
route.on('server_info', fixtures.server_info);
route.on('account_info', fixtures.account_info);
route.on('divvy_path_find', fixtures.divvy_path_find);
route.on('account_lines', fixtures.account_lines);
route.on('submit', fixtures.submit);
route.on('tx', fixtures.tx);
divvyd.on('connection', fixtures.connection.bind({route: route}));
self.remote.once('connect', function() {
self.remote.getServer().once('ledger_closed', function() {
// proceed to the tests, api is ready
done();
});
self.remote.getServer().emit('message', fixtures.sample_ledger);
});
self.remote._servers = [ ];
self.remote.addServer('ws://localhost:5150');
self.remote.connect();
});
suiteTeardown(function(done) {
var self = this;
self.remote.once('disconnect', function() {
divvyd.close();
done();
});
self.remote.disconnect();
});
var store = {};
test('Pathfinding:XDV', function(done) {
// genesis initially gives Alice 429 XDV
orderlist.create([{command: 'divvy_path_find'}]);
function incoming(data) {
delete data.id;
assert.deepEqual(data, {
command: 'divvy_path_find',
source_account: 'rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh',
destination_account: 'rJRLoJSErtNRFnbCyHEUYnRUKNwkVYDM7U',
destination_amount: '429000000'
});
orderlist.mark('divvy_path_find');
}
route.once('divvy_path_find', incoming);
app.get('/v1/accounts/' + fixtures.accounts.genesis.address
+ '/payments/paths/' + fixtures.accounts.alice.address + '/429+XDV')
.end(function(err, resp) {
if (err) {
throw err;
}
assert.strictEqual(resp.body.success, true);
var keyresp = testutils.hasKeys(resp.body, ['payments', 'success']);
assert.equal(keyresp.hasAllKeys, true);
store.paymentGenesisToAlice =
{
'secret': fixtures.accounts.genesis.secret,
'client_resource_id': 'foobar24',
'payment': resp.body.payments[0]
};
assert.equal(orderlist.test(), true);
orderlist.reset();
done();
});
});
test('Posting XDV from genesis to alice', function(done) {
function _subscribe(data) {
delete data.id;
assert.deepEqual(data, {
command: 'subscribe',
accounts: ['rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh']
});
orderlist.mark('subscribe');
}
function _accountinfo(data) {
delete data.id;
assert.deepEqual(data, {
command: 'account_info',
account: 'rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh'
});
orderlist.mark('account_info');
}
function _submit(data) {
delete data.id;
var so = new RL.SerializedObject(data.tx_blob).to_json();
delete so.TxnSignature; // sigs won't match ever
assert.deepEqual(so, {
TransactionType: 'Payment',
Flags: 2147483648,
Sequence: 1,
LastLedgerSequence: 8804619,
Amount: '429000000',
Fee: '12',
SigningPubKey:
'0330E7FC9D56BB25D6893BA3F317AE5BCF33B3291BD63DB32654A313222F7FD020',
Account: 'rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh',
Destination: 'rJRLoJSErtNRFnbCyHEUYnRUKNwkVYDM7U'
});
orderlist.mark('submit');
}
orderlist.create([
{command: 'subscribe'},
{command: 'account_info'},
{command: 'submit'}
]);
route.once('subscribe', _subscribe);
route.once('account_info', _accountinfo);
route.once('submit', _submit);
// actually post a XDV payment of 429 from genesis to alice
app.post('/v1/accounts/' + fixtures.accounts.genesis.address + '/payments')
.send(store.paymentGenesisToAlice)
.expect(function(resp) {
assert.strictEqual(resp.body.success, true);
var keys = Object.keys(fixtures.nominal_xdv_post_response);
assert.equal(testutils.hasKeys(resp.body, keys).hasAllKeys, true);
assert.equal(orderlist.test(), true);
orderlist.reset();
})
.end(done);
});
test('check amount alice has', function(done) {
// we check that alice has 429 XDV
orderlist.create([
{command: 'account_info'},
{command: 'account_lines'}
]);
function _account_info(data) {
delete data.id;
assert.deepEqual(data, {
command: 'account_info',
account: 'rJRLoJSErtNRFnbCyHEUYnRUKNwkVYDM7U',
ledger_index: 'validated'
});
orderlist.mark('account_info');
}
function _account_lines(data) {
delete data.id;
assert.deepEqual(data, {
command: 'account_lines',
account: 'rJRLoJSErtNRFnbCyHEUYnRUKNwkVYDM7U',
ledger_index: 'validated',
limit: 200
});
orderlist.mark('account_lines');
}
route.once('account_info', _account_info);
route.once('account_lines', _account_lines);
app.get('/v1/accounts/' + fixtures.accounts.alice.address + '/balances')
.end(function(err, resp) {
if (err) {
throw err;
}
assert.equal(resp.body.balances[0].value, 429);
assert.equal(orderlist.test(), true);
orderlist.reset();
done();
});
});
test('Pathfinding: from alice to bob XDV', function(done) {
// Now alice gives bob 1 drop XDV
orderlist.create([
{command: 'divvy_path_find'},
{command: 'account_info'}
]);
function _divvy_path_find(data) {
orderlist.mark('divvy_path_find');
delete data.id;
assert.deepEqual(data, {
command: 'divvy_path_find',
source_account: 'rJRLoJSErtNRFnbCyHEUYnRUKNwkVYDM7U',
destination_account: 'rwmityd4Ss34DBUsRy7Pacv6UA5n7yjfe5',
destination_amount: '1'
});
}
function _account_info(data) {
orderlist.mark('account_info');
delete data.id;
assert.deepEqual(data, {
command: 'account_info',
account: 'rJRLoJSErtNRFnbCyHEUYnRUKNwkVYDM7U'
});
}
route.once('divvy_path_find', _divvy_path_find);
route.once('account_info', _account_info);
app.get('/v1/accounts/' + fixtures.accounts.alice.address +
'/payments/paths/' + fixtures.accounts.bob.address + '/0.000001+XDV')
.end(function(err, resp) {
if (err) {
throw err;
}
assert.strictEqual(resp.body.success, true);
var keyresp = testutils.hasKeys(resp.body, ['payments', 'success']);
assert.equal(keyresp.hasAllKeys, true);
store.paymentAliceToBob = {
'secret': fixtures.accounts.alice.secret,
'client_resource_id': 'foobar25',
'payment': resp.body.payments[0]
};
assert.equal(orderlist.test(), true);
orderlist.reset();
done();
});
});
test('discover the reserve_base_xdv', function(done) {
function _server_info(data) {
orderlist.mark('server_info');
delete data.id;
assert.deepEqual(data, {
command: 'server_info'
});
}
route.once('server_info', _server_info);
orderlist.create([{command: 'server_info'}]);
app.get('/v1/server')
.end(function(err, resp) {
if (err) {
throw err;
}
store.reserve_base_xdv = resp.body.divvyd_server_status
.validated_ledger.reserve_base_xdv;
assert.equal(orderlist.test(), true);
orderlist.reset();
done();
});
});
test('Pathfinding: from alice to bob XDV', function(done) {
// Now alice gives bob exactly the reserve_base_xdv XDV
orderlist.create([
{command: 'divvy_path_find'},
{command: 'account_info'}
]);
function _divvy_path_find(data) {
orderlist.mark('divvy_path_find');
delete data.id;
assert.deepEqual(data, {
command: 'divvy_path_find',
source_account: 'rJRLoJSErtNRFnbCyHEUYnRUKNwkVYDM7U',
destination_account: 'rwmityd4Ss34DBUsRy7Pacv6UA5n7yjfe5',
destination_amount: (store.reserve_base_xdv * 1000000).toString()
});
}
function _account_info(data) {
orderlist.mark('account_info');
delete data.id;
assert.deepEqual(data, {
command: 'account_info',
account: 'rJRLoJSErtNRFnbCyHEUYnRUKNwkVYDM7U'
});
}
route.once('divvy_path_find', _divvy_path_find);
route.once('account_info', _account_info);
app.get('/v1/accounts/' + fixtures.accounts.alice.address
+ '/payments/paths/' + fixtures.accounts.bob.address
+ '/' + store.reserve_base_xdv + '+XDV')
.end(function(err, resp) {
if (err) {
throw err;
}
assert.strictEqual(resp.body.success, true);
var keyresp = testutils.hasKeys(resp.body, ['payments', 'success']);
assert.equal(keyresp.hasAllKeys, true);
store.paymentAliceToBob = {
'secret': fixtures.accounts.alice.secret,
'client_resource_id': 'foobar26',
'payment': resp.body.payments[0]
};
assert.equal(orderlist.test(), true);
orderlist.reset();
done();
});
});
test('send bob reserve_base_xdv XDV from Alice to bob', function(done) {
// sending bob reserve_base_xdv XDV from alice
orderlist.create([{command: 'submit'}]);
function _submit(data) {
var so = new RL.SerializedObject(data.tx_blob).to_json();
orderlist.mark('submit');
delete so.TxnSignature;
assert.deepEqual(so, {
TransactionType: 'Payment',
Flags: 2147483648,
Sequence: 1,
LastLedgerSequence: 8804619,
Amount: (store.reserve_base_xdv * 1000000).toString(),
Fee: '12',
SigningPubKey:
'022E3308DCB75B17BEF734CE342AC40FF7FDF55E3FEA3593EE8301A70C532BB5BB',
Account: 'rJRLoJSErtNRFnbCyHEUYnRUKNwkVYDM7U',
Destination: 'rwmityd4Ss34DBUsRy7Pacv6UA5n7yjfe5'
});
}
route.once('submit', _submit);
app.post('/v1/accounts/' + fixtures.accounts.alice.address + '/payments')
.send(store.paymentAliceToBob)
.expect(function(resp) {
assert.deepEqual(resp.body, {
success: true,
client_resource_id: 'foobar26',
status_url: 'http://127.0.0.1:5990/v1/accounts/'
+ fixtures.accounts.alice.address + '/payments/foobar26'
});
store.status_url = '/v1/accounts/' + fixtures.accounts.alice.address
+ '/payments/foobar26';
assert.equal(orderlist.test(), true);
orderlist.reset();
})
.end(done);
});
// confirm payment via client resource ID
test('check status url of the reserve_base_xdv transfer from alice to bob',
function(done) {
orderlist.create([{command: 'tx'}]);
function _tx(data) {
delete data.id;
delete data.transaction;
assert.deepEqual(data, {command: 'tx', binary: true});
orderlist.mark('tx');
}
route.once('tx', _tx);
app.get(store.status_url)
.expect(function(resp) {
assert.equal(resp.status, 200);
assert.equal(resp.body.success, true);
var payment = resp.body.payment;
var statusPayment = {
source_account: 'rJRLoJSErtNRFnbCyHEUYnRUKNwkVYDM7U',
source_tag: '',
source_amount: {value: '200', currency: 'XDV', issuer: ''},
source_slippage: '0',
destination_account: 'rwmityd4Ss34DBUsRy7Pacv6UA5n7yjfe5',
destination_tag: '',
destination_amount: {value: '200', currency: 'XDV', issuer: ''},
invoice_id: '',
paths: '[]',
no_direct_divvy: false,
partial_payment: false,
direction: 'outgoing',
timestamp: '',
fee: '0.000012',
source_balance_changes: [],
destination_balance_changes: []
};
store.hash = resp.body.hash;
var keys = Object.keys(statusPayment);
for (var i = 0; i < keys.length; i++) {
var key = keys[i];
assert.deepEqual(payment[key], statusPayment[key]);
}
assert.equal(resp.body.hash,
'8EA3CF4D854669007058EB45E9860611CC24FEB655895E418A5C8BC5EA901D01');
assert.equal(resp.body.ledger, 'undefined');
assert.equal(resp.body.state, 'pending');
assert.equal(orderlist.test(), true);
orderlist.reset();
})
.end(done);
});
test('confirm payment via transaction hash', function(done) {
orderlist.create([{command: 'tx'}]);
function _tx() {
orderlist.mark('tx');
}
route.once('tx', _tx);
app.get('/v1/accounts/' + fixtures.accounts.alice.address + '/payments/'
+ store.hash)
.expect(function(resp) {
assert.equal(resp.status, 200);
assert.equal(resp.body.success, true);
var payment = resp.body.payment;
var statusPayment = {
source_account: 'rJRLoJSErtNRFnbCyHEUYnRUKNwkVYDM7U',
source_tag: '',
source_amount: {value: '200', currency: 'XDV', issuer: ''},
source_slippage: '0',
destination_account: 'rwmityd4Ss34DBUsRy7Pacv6UA5n7yjfe5',
destination_tag: '',
destination_amount: {value: '200', currency: 'XDV', issuer: ''},
invoice_id: '',
paths: '[]',
no_direct_divvy: false,
partial_payment: false,
direction: 'outgoing',
timestamp: '',
fee: '0.000012',
source_balance_changes: [],
destination_balance_changes: []
};
var keys = Object.keys(statusPayment);
for (var i = 0; i < keys.length; i++) {
var key = keys[i];
assert.deepEqual(payment[key], statusPayment[key]);
}
assert.equal(resp.body.hash,
'8EA3CF4D854669007058EB45E9860611CC24FEB655895E418A5C8BC5EA901D01');
assert.equal(resp.body.ledger, 'undefined');
assert.equal(resp.body.state, 'pending');
assert.equal(orderlist.test(), true);
orderlist.reset();
})
.end(done);
});
test('check amount bob has', function(done) {
orderlist.create([
{command: 'account_info'},
{command: 'account_lines'}
]);
function _account_info(data) {
delete data.id;
assert.deepEqual(data, {
command: 'account_info',
account: 'rwmityd4Ss34DBUsRy7Pacv6UA5n7yjfe5',
ledger_index: 'validated'
});
orderlist.mark('account_info');
}
function _account_lines(data) {
delete data.id;
assert.deepEqual(data, {
command: 'account_lines',
account: 'rwmityd4Ss34DBUsRy7Pacv6UA5n7yjfe5',
ledger_index: 'validated',
limit: 200
});
orderlist.mark('account_lines');
}
route.once('account_info', _account_info);
route.once('account_lines', _account_lines);
app.get('/v1/accounts/' + fixtures.accounts.bob.address + '/balances')
.end(function(err, resp) {
if (err) {
throw err;
}
var balance = resp.body.balances[0];
// change all instances of 200 with reserve base xdv
assert.equal(balance.value, '200');
store.bob_balance = balance.value;
assert.equal(orderlist.test(), true);
orderlist.reset();
done();
});
});
// bob should try to send all money back to alice
test('try to send all of bobs money to alice below reserve', function(done) {
orderlist.create([
{command: 'divvy_path_find'},
{command: 'account_info'}
]);
function _divvy_path_find(data) {
orderlist.mark('divvy_path_find');
delete data.id;
assert.deepEqual(data, {
command: 'divvy_path_find',
source_account: 'rwmityd4Ss34DBUsRy7Pacv6UA5n7yjfe5',
destination_account: 'rJRLoJSErtNRFnbCyHEUYnRUKNwkVYDM7U',
destination_amount: '200000000'
});
}
function _account_info(data) {
orderlist.mark('account_info');
delete data.id;
assert.deepEqual(data, {
command: 'account_info',
account: 'rwmityd4Ss34DBUsRy7Pacv6UA5n7yjfe5'
});
}
route.once('divvy_path_find', _divvy_path_find);
route.once('account_info', _account_info);
var sendamount = store.bob_balance;
app.get('/v1/accounts/' + fixtures.accounts.bob.address + '/payments/paths/'
+ fixtures.accounts.alice.address + '/' + sendamount + '+XDV')
.end(function(err, resp) {
if (err) {
throw err;
}
assert.equal(404, resp.status);
assert.deepEqual(resp.body, {
success: false,
error_type: 'invalid_request',
error: 'restNOT_FOUND',
message: 'No paths found. Please ensure that the source_account has '
+ 'sufficient funds to execute the payment. If it does there may '
+ 'be insufficient liquidity in the network to execute this '
+ 'payment right now'
});
assert.equal(orderlist.test(), true);
orderlist.reset();
done();
});
});
// TODO: bob should try to send all money back to alice
test.skip('try to send 95% of bobs money to alice below reserve',
function(done) {
var sendamount = store.bob_balance * 0.95;
app.get('/v1/accounts/' + fixtures.accounts.bob.address + '/payments/paths/'
+ fixtures.accounts.alice.address + '/' + sendamount + '+XDV')
.end(function(err, resp) {
if (err) {
throw err;
}
assert.equal(404, resp.status);
assert.deepEqual(resp.body, {
success: false,
error_type: 'invalid_request',
error: 'No paths found',
message: 'Please ensure that the source_account has sufficient '
+ 'funds to execute the payment. If it does there may be '
+ 'insufficient liquidity in the network to execute this payment '
+ 'right now'
});
done();
});
});
// have alice send bob 10 USD/alice
test('alice sends bob 10USD/alice without trust', function(done) {
orderlist.create([
{command: 'divvy_path_find'}
]);
function _divvy_path_find(data) {
orderlist.mark('divvy_path_find');
delete data.id;
assert.deepEqual(data, {
command: 'divvy_path_find',
source_account: 'rJRLoJSErtNRFnbCyHEUYnRUKNwkVYDM7U',
destination_account: 'rwmityd4Ss34DBUsRy7Pacv6UA5n7yjfe5',
destination_amount: {
value: '10',
currency: 'USD',
issuer: 'rJRLoJSErtNRFnbCyHEUYnRUKNwkVYDM7U'
}
});
}
route.once('divvy_path_find', _divvy_path_find);
app.get('/v1/accounts/' + fixtures.accounts.alice.address
+ '/payments/paths/' + fixtures.accounts.bob.address + '/10+USD+'
+ fixtures.accounts.alice.address)
.expect(function(resp) {
assert.deepEqual(resp.body, {
success: false,
error_type: 'invalid_request',
error: 'restNOT_FOUND',
message: 'No paths found. The destination_account does not accept '
+ 'USD, they only accept: XDV'
});
assert.equal(orderlist.test(), true);
orderlist.reset();
})
.end(done);
});
test.skip('grant a trustline of 10 usd towards alice', function(done) {
orderlist.create([
{command: 'subscribe'},
{command: 'account_info'},
{command: 'submit'}
]);
function _subscribe(data) {
orderlist.mark('subscribe');
delete data.id;
assert.deepEqual(data, {
command: 'subscribe',
accounts: ['rwmityd4Ss34DBUsRy7Pacv6UA5n7yjfe5']
});
}
function _account_info(data) {
delete data.id;
orderlist.mark('account_info');
assert.deepEqual(data, {
command: 'account_info',
account: 'rwmityd4Ss34DBUsRy7Pacv6UA5n7yjfe5'
});
}
function _submit(data) {
orderlist.mark('submit');
var so = new RL.SerializedObject(data.tx_blob).to_json();
delete so.TxnSignature; // sigs won't match ever
assert.deepEqual(so, {
Flags: 2147483648,
TransactionType: 'TrustSet',
Account: 'rwmityd4Ss34DBUsRy7Pacv6UA5n7yjfe5',
LimitAmount: {
value: '10',
currency: 'USD',
issuer: 'rJRLoJSErtNRFnbCyHEUYnRUKNwkVYDM7U'
},
Sequence: 1,
SigningPubKey:
'03BC02F6C0F2C50EF5DB02C2C17062B7449B34FBD669A75362E41348C9FAE3DDE1',
Fee: '12',
LastLedgerSequence: 8804624
});
}
route.once('subscribe', _subscribe);
route.once('account_info', _account_info);
route.once('submit', _submit);
app.post('/v1/accounts/' + fixtures.accounts.bob.address + '/trustlines')
.send({
'secret': fixtures.accounts.bob.secret,
'trustline': {
'limit': '10',
'currency': 'USD',
'counterparty': fixtures.accounts.alice.address,
'allows_rippling': false
}
})
.expect(function(resp) {
assert.strictEqual(typeof resp.body.trustline.hash, 'string');
assert.strictEqual(typeof resp.body.trustline.hash, 'string');
delete resp.body.trustline.hash;
delete resp.body.trustline.ledger;
assert.deepEqual(resp.body, {
'success': true,
'trustline': {
'account': 'rwmityd4Ss34DBUsRy7Pacv6UA5n7yjfe5',
'limit': '10',
'currency': 'USD',
'counterparty': 'rJRLoJSErtNRFnbCyHEUYnRUKNwkVYDM7U',
'account_allows_rippling': true,
'account_trustline_frozen': false,
'state': 'pending'
}
});
assert.equal(orderlist.test(), true);
orderlist.reset();
})
.end(done);
});
// have alice send bob 10 USD/alice
test('get path for alice to bob 10USD/alice with trust', function(done) {
orderlist.create([
{command: 'divvy_path_find'}
]);
function _divvy_path_find(data) {
delete data.id;
assert.deepEqual(data, {
command: 'divvy_path_find',
source_account: 'rJRLoJSErtNRFnbCyHEUYnRUKNwkVYDM7U',
destination_account: 'rwmityd4Ss34DBUsRy7Pacv6UA5n7yjfe5',
destination_amount: {
value: '10',
currency: 'USD',
issuer: 'rJRLoJSErtNRFnbCyHEUYnRUKNwkVYDM7U'
}
});
orderlist.mark('divvy_path_find');
}
route.once('divvy_path_find', _divvy_path_find);
app.get('/v1/accounts/' + fixtures.accounts.alice.address
+ '/payments/paths/' + fixtures.accounts.bob.address + '/10+USD+'
+ fixtures.accounts.alice.address)
.expect(function(resp) {
assert.deepEqual(resp.body, {
success: true,
payments: [
{
source_account: 'rJRLoJSErtNRFnbCyHEUYnRUKNwkVYDM7U',
source_tag: '',
source_amount: {
value: '10',
currency: 'USD',
issuer: ''
},
source_slippage: '0',
destination_account: 'rwmityd4Ss34DBUsRy7Pacv6UA5n7yjfe5',
destination_tag: '',
destination_amount: {
value: '10',
currency: 'USD',
issuer: 'rJRLoJSErtNRFnbCyHEUYnRUKNwkVYDM7U'
},
invoice_id: '',
paths: '[]',
partial_payment: false,
no_direct_divvy: false
}
]
});
assert.equal(orderlist.test(), true);
orderlist.reset();
})
.end(done);
});
/**
* The tests below don't test the input for divvyd
* These should be rewritten in the payments.js class
*/
test('path find populate carol with missing sum', function(done) {
app.get('/v1/accounts/' + fixtures.accounts.genesis.address
+ '/payments/paths/' + fixtures.accounts.carol.address + '/+XDV')
.end(function(err, resp) {
if (err) {
throw err;
}
assert.equal(resp.status, 400);
assert.deepEqual(resp.body, {
success: false,
error_type: 'invalid_request',
error: 'restINVALID_PARAMETER',
message: 'Invalid parameter: destination_amount. Must be an amount '
+ 'string in the form value+currency+issuer'
});
done();
});
});
test('path find populate carol with missing currency', function(done) {
app.get('/v1/accounts/' + fixtures.accounts.genesis.address
+ '/payments/paths/' + fixtures.accounts.carol.address + '/400')
.end(function(err, resp) {
if (err) {
throw err;
}
assert.equal(resp.status, 400);
assert.deepEqual(resp.body, {
success: false,
error_type: 'invalid_request',
error: 'restINVALID_PARAMETER',
message: 'Invalid parameter: destination_amount. Must be an amount '
+ 'string in the form value+currency+issuer'
});
done();
});
});
test('path find populate carol with all missing endpoint value',
function(done) {
app.get('/v1/accounts/' + fixtures.accounts.genesis.address
+ '/payments/paths/' + fixtures.accounts.carol.address + '/')
.expect(function(resp) {
assert.equal(resp.status, 404);
})
.end(done);
});
test('path find populate carol with 400', function(done) {
app.get('/v1/accounts/' + fixtures.accounts.genesis.address
+ '/payments/paths/' + fixtures.accounts.carol.address + '/400+XDV')
.end(function(err, resp) {
if (err) {
throw err;
}
store.paymentGenesisToCarol = {
'secret': fixtures.accounts.genesis.secret,
'client_resource_id': 'asdfg',
'payment': resp.body.payments[0]
};
done();
});
});
test('Posting XDV from genesis to carol', function(done) {
app.post('/v1/accounts/' + fixtures.accounts.alice.address + '/payments')
.send(store.paymentGenesisToCarol)
.end(function(err) {
if (err) {
throw err;
}
done();
});
});
// miss the client resource id
test('path find populate dan with 600', function(done) {
app.get('/v1/accounts/' + fixtures.accounts.genesis.address
+ '/payments/paths/' + fixtures.accounts.dan.address + '/600+XDV')
.end(function(err, resp) {
if (err) {
throw err;
}
store.paymentGenesisToDan = {
'secret': fixtures.accounts.genesis.secret,
'payment': resp.body.payments[0]
};
done();
});
});
test('Posting XDV from genesis to dan with missing client resource id',
function(done) {
app.post('/v1/accounts/' + fixtures.accounts.alice.address + '/payments')
.send(store.paymentGenesisToDan)
.end(function(err, resp) {
if (err) {
throw err;
}
assert.deepEqual(resp.body, {
success: false,
error_type: 'invalid_request',
error: 'restINVALID_PARAMETER',
message: 'Parameter missing: client_resource_id'
});
done();
});
});
test('Posting XDV from genesis to dan with empty client resource id',
function(done) {
store.paymentGenesisToDan.client_resource_id = '';
app.post('/v1/accounts/' + fixtures.accounts.alice.address + '/payments')
.send(store.paymentGenesisToDan)
.end(function(err, resp) {
if (err) {
throw err;
}
assert.deepEqual(resp.body, {
success: false,
error_type: 'invalid_request',
error: 'restINVALID_PARAMETER',
message: 'Invalid parameter: client_resource_id. Must be a string '
+ 'of ASCII-printable characters. Note that 256-bit hex strings '
+ 'are disallowed because of the potential confusion with '
+ 'transaction hashes.'
});
done();
});
});
test('Posting XDV from genesis to dan with valid client resource id',
function(done) {
store.paymentGenesisToDan.client_resource_id = 'qwerty';
app.post('/v1/accounts/' + fixtures.accounts.alice.address + '/payments')
.send(store.paymentGenesisToDan)
.end(function(err, resp) {
if (err) {
throw err;
}
assert.deepEqual(resp.body, {
success: true,
client_resource_id: 'qwerty',
status_url: 'http://127.0.0.1:5990/v1/accounts/'
+ 'rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh/payments/qwerty'
});
done();
});
});
test('Double posting XDV from genesis to dan with valid client resource id',
function(done) {
store.paymentGenesisToDan.client_resource_id = 'qwerty';
app.post('/v1/accounts/' + fixtures.accounts.alice.address + '/payments')
.send(store.paymentGenesisToDan)
.expect(function(resp) {
assert.equal(resp.status, 500);
assert.deepEqual(resp.body, {
'success': false,
'error_type': 'server',
'error': 'restDUPLICATE_TRANSACTION',
'message': 'Duplicate Transaction. A record already exists in the '
+ 'database for a transaction of this type with the same '
+ 'client_resource_id. If this was not an accidental resubmission '
+ 'please submit the transaction again with a unique '
+ 'client_resource_id'
});
})
.end(done);
});
test.skip('dan grants a trustline of 10 usd towards carol but uses carols '
+ 'address in the accounts setting', function(done) {
// mocha is NOT overriding the timeout contrary to documentation
this.timeout(1000);
app.post('/v1/accounts/' + fixtures.accounts.carol.address + '/trustlines')
.send({
'secret': fixtures.accounts.dan.secret,
'trustline': {
'limit': '10',
'currency': 'USD',
'counterparty': fixtures.accounts.carol.address,
'allows_rippling': false
}
})
.expect(function(resp) {
assert.deepEqual(resp.body,
{
success: false,
error_type: 'transaction',
error: 'Invalid secret'
});
})
.end(done);
});
test.skip('dan grants a trustline of 10 usd towards carol and uses correct '
+ 'address in the accounts setting but no secret', function(done) {
app.post('/v1/accounts/' + fixtures.accounts.dan.address + '/trustlines')
.send({
'secret': '',
'trustline': {
'limit': '10',
'currency': 'USD',
'counterparty': fixtures.accounts.carol.address,
'allows_rippling': false
}
})
.expect(function(resp) {
assert.equal(resp.status, 400);
assert.deepEqual(resp.body, {
success: false,
error_type: 'invalid_request',
error: 'restINVALID_PARAMETER',
message: 'Parameter missing: secret'
});
})
.end(done);
});
test.skip('dan grants a trustline of 10 usd towards carol and uses correct '
+ 'address in the accounts setting but incorrect secret', function(done) {
app.post('/v1/accounts/' + fixtures.accounts.dan.address + '/trustlines')
.send({
'secret': fixtures.accounts.carol.secret,
'trustline': {
'limit': '10',
'currency': 'USD',
'counterparty': fixtures.accounts.carol.address,
'allows_rippling': false
}
})
.expect(function(resp) {
assert.deepEqual(resp.body,
{
success: false,
error_type: 'transaction',
error: 'Invalid secret'
});
})
.end(done);
});
test.skip('dan grants a trustline of 10 usd towards carol and uses correct '
+ 'address in the accounts setting', function(done) {
app.post('/v1/accounts/' + fixtures.accounts.dan.address + '/trustlines')
.send({
'secret': fixtures.accounts.dan.secret,
'trustline': {
'limit': '10',
'currency': 'USD',
'counterparty': fixtures.accounts.carol.address,
'allows_rippling': false
}
})
.expect(function(resp) {
assert.equal(resp.status, 201);
assert.strictEqual(typeof resp.body.hash, 'string');
assert.strictEqual(typeof resp.body.ledger, 'string');
delete resp.body.hash;
delete resp.body.ledger;
assert.deepEqual(resp.body, {
'success': true,
'trustline': {
'account': 'rsE6ZLDkXhSvfJHvSqFPhdazsoMgCEC52V',
'limit': '10',
'currency': 'USD',
'counterparty': 'r3YHFNkQRJDPc9aCkRojPLwKVwok3ihgBJ',
'account_allows_rippling': true,
'account_trustline_frozen': false
},
'state': 'pending'
});
})
.end(done);
});
test.skip('dan grants an additional trustline of 10 usd towards carol and '
+ 'uses correct address in the accounts setting', function(done) {
app.post('/v1/accounts/' + fixtures.accounts.dan.address + '/trustlines')
.send({
'secret': fixtures.accounts.dan.secret,
'trustline': {
'limit': '10',
'currency': 'USD',
'counterparty': fixtures.accounts.carol.address,
'allows_rippling': false
}
})
.expect(function(resp) {
assert.equal(resp.status, 201);
assert.strictEqual(typeof resp.body.hash, 'string');
assert.strictEqual(typeof resp.body.ledger, 'string');
delete resp.body.hash;
delete resp.body.ledger;
assert.deepEqual(resp.body, {
'success': true,
'trustline': {
'account': 'rsE6ZLDkXhSvfJHvSqFPhdazsoMgCEC52V',
'limit': '10',
'currency': 'USD',
'counterparty': 'r3YHFNkQRJDPc9aCkRojPLwKVwok3ihgBJ',
'account_allows_rippling': true,
'account_trustline_frozen': false
},
'state': 'pending'
});
})
.end(done);
});
test('get path for carol to dan 10USD/carol with trust', function(done) {
app.get('/v1/accounts/' + fixtures.accounts.carol.address
+ '/payments/paths/' + fixtures.accounts.dan.address + '/10+USD+'
+ fixtures.accounts.carol.address)
.end(function(err, resp) {
if (err) {
throw err;
}
store.paymentCarolToDan = {
secret: 'asdfkje',
client_resource_id: 'abc',
payment: resp.body.payments[0]
};
done();
});
});
test.skip('Posting 10USD from carol to dan with valid client resource id '
+ 'but incorrect secret', function(done) {
app.post('/v1/accounts/' + fixtures.accounts.alice.address + '/payments')
.send(store.paymentCarolToDan)
.expect(function(resp) {
assert.deepEqual(resp.body,
{
success: false,
error_type: 'transaction',
error: 'Invalid secret'
});
})
.end(done);
});
test('Posting 10USD from carol to dan with valid client resource id and '
+ 'correct secret but missing fields on payment object', function(done) {
store.paymentCarolToDan.secret = fixtures.accounts.carol.secret;
store.value = store.paymentCarolToDan.payment.destination_amount.value;
delete store.paymentCarolToDan.payment.destination_amount.value;
app.post('/v1/accounts/' + fixtures.accounts.alice.address + '/payments')
.send(store.paymentCarolToDan)
.expect(function(resp) {
assert.equal(resp.status, 400);
assert.deepEqual(resp.body, {
success: false,
error_type: 'invalid_request',
error: 'restINVALID_PARAMETER',
message: 'Invalid parameter: destination_amount. Must be a valid '
+ 'Amount object'
});
})
.end(done);
});
test('Posting 10USD from carol to dan with valid client resource id '
+ 'and correct secret', function(done) {
store.paymentCarolToDan.payment.destination_amount.value = store.value;
store.paymentCarolToDan.secret = fixtures.accounts.carol.secret;
store.paymentCarolToDan.client_resource_id = 'abc';
app.post('/v1/accounts/' + fixtures.accounts.alice.address + '/payments')
.send(store.paymentCarolToDan)
.expect(function(resp) {
assert.equal(resp.status, 200);
assert.deepEqual(resp.body, {
success: true,
client_resource_id: 'abc',
status_url: 'http://127.0.0.1:5990/v1/accounts/'
+ 'r3YHFNkQRJDPc9aCkRojPLwKVwok3ihgBJ/payments/abc'
});
})
.end(done);
});
});