ccxt-rest
Version:
Open Source Unified REST API of 100+ Crypto Exchange Sites
620 lines (538 loc) • 25.3 kB
JavaScript
const expect = require('chai').expect;
const should = require('should');
const superagent = require('superagent');
const db = require('./../../api/models');
const getUnattractiveOrderPlacement = require('./../../scripts/_common/get-unattractive-order-placement').getUnattractiveOrderPlacement
const jwtHelper = require('./../../api/helpers/jwt-helper')
const TIMEOUT_MS = process.env.TIMEOUT_MS || 10000
const BASE_URL = process.env.BASE_URL || 'http://localhost:3000'
const creds = JSON.parse('%%creds%%')
const knownCurrencyPairs = JSON.parse('%%knownCurrencyPairs%%')
let expectedStatusCodes = {
fetchTicker : parseInt("%%expectedStatusCodesFetchTicker%%"),
fetchOrders : parseInt("%%expectedStatusCodesFetchOrders%%"),
fetchMyTrades : parseInt("%%expectedStatusCodesFetchMyTrades%%"),
fetchTicker : parseInt("%%expectedStatusCodesFetchTicker%%"),
fetchTickers : parseInt("%%expectedStatusCodesFetchTickers%%"),
fetchClosedOrders : parseInt("%%expectedStatusCodesFetchClosedOrders%%")
}
describe(`> [%%exchangeName%%]`, function() {
describe(`> [%%exchangeName%%] Given no saved exchanges`, function() {
describe(`> [%%exchangeName%%] Using no Saved Instance's Exchange Management API`, function() {
before(function() {
return new Promise((resolve) => {
jwtHelper.initialize()
.then(() => {
resolve()
})
})
})
it(`> When GET:/exchange/%%exchangeName%% then return public %%exchangeName%%`, function(done) {
this.timeout(TIMEOUT_MS);
superagent
.get(`${BASE_URL}/exchange/%%exchangeName%%`)
.end((err, res) => {
expect(err).to.not.exist
expect(res.type).to.eql('application/json')
expect(res.status).to.eql(200)
res.body.private.should.eql(false);
done();
});
});
it(`> When GET:/exchange/%%exchangeName%% with invalid jwt token, then return 403`, function(done) {
this.timeout(TIMEOUT_MS)
const token = 'xxx.yyy.zzz'
superagent
.get(`${BASE_URL}/exchange/%%exchangeName%%`)
.set('Authorization', `Bearer ${token}`)
.end((err, res) => {
expect(res.type).to.eql('application/json')
expect(res.status).to.eql(403)
done();
})
});
it(`> When GET:/exchange/%%exchangeName%% with valid jwt token but referencing non-existent exchange, then return 404`, function(done) {
this.timeout(TIMEOUT_MS)
jwtHelper.sign(
`%%exchangeName%%`,
`%%exchangeName%%_dummy`,
function(err, token) {
expect(err).to.not.exist
superagent
.get(`${BASE_URL}/exchange/%%exchangeName%%`)
.set('Authorization', `Bearer ${token}`)
.end((err, res) => {
expect(res.type).to.eql('application/json')
expect(res.status).to.eql(404)
done();
})
})
});
it(`> When DELETE:/exchange/%%exchangeName%% with no jwt token then return 403`, function(done) {
this.timeout(TIMEOUT_MS);
superagent
.delete(`${BASE_URL}/exchange/%%exchangeName%%`)
.end((err, res) => {
expect(res.type).to.eql('application/json')
expect(res.status).to.eql(403)
done();
})
});
});
describe(`> [%%exchangeName%%] Using no Saved Instance\'s Public Data API`, function() {
this.timeout(TIMEOUT_MS);
it(`> GET:/exchange/%%exchangeName%%/markets then use public %%exchangeName%% and return 200`, function(done) {
superagent
.get(`${BASE_URL}/exchange/%%exchangeName%%/markets`)
.end((err, res) => {
expect(err).to.not.exist
expect(res.type).to.eql('application/json')
expect(res.status).to.eql(200)
done();
})
});
it(`> GET:/exchange/%%exchangeName%%/orderBook?symbol=%%targetCurrencyPair%% then use public %%exchangeName%% and return 200`, function(done) {
this.timeout(TIMEOUT_MS);
superagent
.get(`${BASE_URL}/exchange/%%exchangeName%%/orderBook`)
.query({ symbol: `%%targetCurrencyPair%%` })
.end((err, res) => {
expect(err).to.not.exist
expect(res.type).to.eql('application/json')
expect(res.status).to.eql(200)
done();
})
})
it(`> GET:/exchange/%%exchangeName%%/l2OrderBook?symbol=%%targetCurrencyPair%% then use public %%exchangeName%% and return 200`, function(done) {
this.timeout(TIMEOUT_MS);
superagent
.get(`${BASE_URL}/exchange/%%exchangeName%%/l2OrderBook`)
.query({ symbol: `%%targetCurrencyPair%%` })
.end((err, res) => {
expect(err).to.not.exist
expect(res.type).to.eql('application/json')
expect(res.status).to.eql(200)
done();
})
})
it(`> GET:/exchange/%%exchangeName%%/trades?symbol=%%targetCurrencyPair%% then use public %%exchangeName%% and return 200`, function(done) {
this.timeout(TIMEOUT_MS)
superagent
.get(`${BASE_URL}/exchange/%%exchangeName%%/trades`)
.query({ symbol: `%%targetCurrencyPair%%` })
.retry(3)
.end((err, res) => {
expect(err).to.not.exist
expect(res.type).to.eql('application/json')
expect(res.status).to.eql(200)
done();
})
})
it(`> GET:/exchange/%%exchangeName%%/ticker?symbol=%%targetCurrencyPair%% then use public %%exchangeName%% and return ${expectedStatusCodes['fetchTicker']}`, function(done) {
this.timeout(TIMEOUT_MS)
superagent
.get(`${BASE_URL}/exchange/%%exchangeName%%/ticker`)
.query({ symbol: `%%targetCurrencyPair%%` })
.retry(3)
.end((err, res) => {
expect(err).to.not.exist
expect(res.type).to.eql('application/json')
expect(res.status).to.eql(200)
done();
})
})
it(`> GET:/exchange/%%exchangeName%%/tickers then return use public %%exchangeName%% and return ${expectedStatusCodes['fetchTickers']}`, function(done) {
this.timeout(TIMEOUT_MS)
superagent
.get(`${BASE_URL}/exchange/%%exchangeName%%/tickers`)
.end((err, res) => {
expect(res.type).to.eql('application/json')
expect(res.status).to.eql(expectedStatusCodes['fetchTickers'])
if (expectedStatusCodes['fetchTickers'] >= 200 && expectedStatusCodes['fetchTickers'] < 300) {
expect(err).to.not.exist
}
done();
})
})
it(`> POST:/exchange/%%exchangeName%%/_/loadMarkets then use public %%exchangeName%% and return 200`, function(done) {
this.timeout(TIMEOUT_MS)
superagent
.post(`${BASE_URL}/exchange/%%exchangeName%%/_/loadMarkets`)
.send(JSON.stringify([true]))
.set('Accept', 'application/json')
.end((err, res) => {
expect(res.status).to.eql(200)
expect(res.type).to.eql('application/json')
expect(err).to.not.exist
done();
})
})
});
});
describe(`> [%%exchangeName%%] Given with saved exchange`, function() {
var token
before(function() {
this.timeout(TIMEOUT_MS)
return new Promise((resolve) => {
superagent
.post(`${BASE_URL}/exchange/%%exchangeName%%`)
.send(creds)
.set('Accept', 'application/json')
.end((err, res) => {
expect(err).to.not.exist
expect(res.type).to.eql('application/json')
expect(res.status).to.eql(200)
db.Exchange.findOne({
exchangeName:`%%exchangeName%%`,
exchangeId:`%%exchangeId%%`
}).then(exchange => {
should.exist(exchange);
should.exist(res.body.token)
token = res.body.token
resolve();
}).catch(resolve)
});
});
});
after(function() {
this.timeout(TIMEOUT_MS)
return new Promise((resolve) => {
db.Exchange.findOne({
exchangeName:`%%exchangeName%%`,
exchangeId:`%%exchangeId%%`
}).then(beforeDeleteExchange => {
superagent
.delete(`${BASE_URL}/exchange/%%exchangeName%%`)
.set('Authorization', `Bearer ${token}`)
.end((err, res) => {
expect(err).to.not.exist
expect(res.type).to.eql('application/json')
expect(res.status).to.eql(200)
expect(res.body.name).to.be.match(new RegExp(`%%exchangeName%%`, 'i'))
db.Exchange.findOne({
exchangeName:`%%exchangeName%%`,
exchangeId:`%%exchangeId%%`
}).then(exchangeAfterDeletion => {
should.not.exist(exchangeAfterDeletion)
resolve()
}).catch(resolve)
});
}).catch(resolve)
});
});
describe(`> [%%exchangeName%%] Using Saved Instance's Exchange Management APIs`, function() {
it(`> GET:/exchange/%%exchangeName%% then return id of new exchange`, function(done) {
this.timeout(TIMEOUT_MS);
superagent
.get(`${BASE_URL}/exchange/%%exchangeName%%`)
.set('Authorization', `Bearer ${token}`)
.end((err, res) => {
expect(err).to.not.exist
expect(res.type).to.eql('application/json')
expect(res.status).to.eql(200)
expect(res.body.private).to.be.true;
done();
});
});
it(`> GET:/exchange/%%exchangeName%% then get exchange`, function(done) {
this.timeout(TIMEOUT_MS);
superagent
.get(`${BASE_URL}/exchange/%%exchangeName%%`)
.set('Authorization', `Bearer ${token}`)
.end((err, res) => {
expect(err).to.not.exist
expect(res.type).to.eql('application/json')
expect(res.status).to.eql(200)
db.Exchange.findOne({
exchangeName:`%%exchangeName%%`,
exchangeId:`%%exchangeId%%`
}).then(exchange => {
should.exist(exchange);
res.body.id.should.eql(`%%exchangeId%%`);
done()
}).catch(done)
});
});
})
describe(`> [%%exchangeName%%] Using Saved Instance's Public Data APIs`, function() {
it(`> GET:/exchange/%%exchangeName%%/markets then get exchange's markets`, function(done) {
this.timeout(TIMEOUT_MS)
superagent
.get(`${BASE_URL}/exchange/%%exchangeName%%/markets`)
.set('Authorization', `Bearer ${token}`)
.end((err, res) => {
expect(err).to.not.exist
expect(res.type).to.eql('application/json')
expect(res.status).to.eql(200)
var symbols = res.body.map(entry => entry.symbol)
expect(symbols).to.include.members(knownCurrencyPairs)
done();
});
})
it(`> GET:/exchange/%%exchangeName%%/orderBook then get exchange's Order Book`, function(done) {
this.timeout(TIMEOUT_MS)
superagent
.get(`${BASE_URL}/exchange/%%exchangeName%%/orderBook`)
.query({ symbol: `%%targetCurrencyPair%%` })
.set('Authorization', `Bearer ${token}`)
.end((err, res) => {
expect(err).to.not.exist
expect(res.type).to.eql('application/json')
expect(res.status).to.eql(200)
expect(res.body.bids.length).to.at.least(50);
expect(res.body.asks.length).to.at.least(50);
done();
});
})
it(`> GET:/exchange/%%exchangeName%%/orderBook with limit then get exchange's Order Book`, function(done) {
this.timeout(TIMEOUT_MS)
superagent
.get(`${BASE_URL}/exchange/%%exchangeName%%/orderBook`)
.query({ symbol: `%%targetCurrencyPair%%`, limit: 50 })
.set('Authorization', `Bearer ${token}`)
.end((err, res) => {
expect(err).to.not.exist
expect(res.type).to.eql('application/json')
expect(res.status).to.eql(200)
expect(res.body.bids.length).to.eql(50);
expect(res.body.asks.length).to.eql(50);
done();
});
})
it(`> GET:/exchange/%%exchangeName%%/l2OrderBook then get exchange's L2 Order Book`, function(done) {
this.timeout(TIMEOUT_MS)
superagent
.get(`${BASE_URL}/exchange/%%exchangeName%%/l2OrderBook`)
.query({ symbol: `%%targetCurrencyPair%%` })
.set('Authorization', `Bearer ${token}`)
.end((err, res) => {
expect(err).to.not.exist
expect(res.type).to.eql('application/json')
expect(res.status).to.eql(200)
expect(res.body.bids.length).to.at.least(50);
expect(res.body.asks.length).to.at.least(50);
done();
});
})
it(`> GET:/exchange/%%exchangeName%%/l2OrderBook with limit then get exchange's L2 Order Book`, function(done) {
this.timeout(TIMEOUT_MS)
superagent
.get(`${BASE_URL}/exchange/%%exchangeName%%/l2OrderBook`)
.query({ symbol: `%%targetCurrencyPair%%`, limit: 50 })
.set('Authorization', `Bearer ${token}`)
.end((err, res) => {
expect(err).to.not.exist
expect(res.type).to.eql('application/json')
expect(res.status).to.eql(200)
expect(res.body.bids.length).to.eql(50);
expect(res.body.asks.length).to.eql(50);
done();
});
})
it(`> GET:/exchange/%%exchangeName%%/trades then get exchange's trades`, function(done) {
this.timeout(TIMEOUT_MS)
superagent
.get(`${BASE_URL}/exchange/%%exchangeName%%/trades`)
.query({ symbol: `%%targetCurrencyPair%%` })
.set('Authorization', `Bearer ${token}`)
.end((err, res) => {
expect(err).to.not.exist
expect(res.type).to.eql('application/json')
expect(res.status).to.eql(200)
expect(res.body.length).to.at.least(10);
done();
});
})
it(`> GET:/exchange/%%exchangeName%%/ticker then return ' + ${expectedStatusCodes['fetchTicker']}`, function(done) {
this.timeout(TIMEOUT_MS)
superagent
.get(`${BASE_URL}/exchange/%%exchangeName%%/ticker`)
.query({ symbol: `%%targetCurrencyPair%%` })
.set('Authorization', `Bearer ${token}`)
.end((err, res) => {
expect(res.type).to.eql('application/json')
expect(res.status).to.eql(expectedStatusCodes['fetchTicker'])
if (expectedStatusCodes['fetchTicker'] >= 200 && expectedStatusCodes['fetchTicker'] < 300) {
expect(err).to.not.exist
}
done()
})
})
it(`> GET:/exchange/%%exchangeName%%/tickers then ${expectedStatusCodes['fetchTickers']}`, function(done) {
this.timeout(TIMEOUT_MS)
superagent
.get(`${BASE_URL}/exchange/%%exchangeName%%/tickers`)
.set('Authorization', `Bearer ${token}`)
.end((err, res) => {
expect(res.type).to.eql('application/json')
expect(res.status).to.eql(expectedStatusCodes['fetchTickers'])
if (expectedStatusCodes['fetchTickers'] >= 200 && expectedStatusCodes['fetchTickers'] < 300) {
expect(err).to.not.exist
}
done()
})
})
it(`> POST:/exchange/%%exchangeName%%/_/loadMarkets then get exchange's direct method`, function(done) {
this.timeout(TIMEOUT_MS)
superagent
.post(`${BASE_URL}/exchange/%%exchangeName%%/_/loadMarkets`)
.type('text')
.send(JSON.stringify([true]))
.set('Accept', 'application/json')
.set('Authorization', `Bearer ${token}`)
.end((err, res) => {
expect(err).to.not.exist
expect(res.type).to.eql('application/json')
expect(res.status).to.eql(200)
done();
});
})
});
describe(`> [%%exchangeName%%] Using Saved Instance's Private Data APIs`, function() {
before(function() {
if (!creds) {
console.info(`[SKIP REASON] No credentials found for %%exchangeName%%`)
this.skip()
}
})
it(`> GET:/exchange/%%exchangeName%%/balances then get exchange's balances`, function(done) {
this.timeout(TIMEOUT_MS)
superagent
.get(`${BASE_URL}/exchange/%%exchangeName%%/balances`)
.set('Authorization', `Bearer ${token}`)
.end((err, res) => {
expect(err).to.not.exist
expect(res.type).to.eql('application/json')
expect(res.status).to.eql(200)
expect(res.body.info).to.exist;
expect(res.body.balances.length).to.be.at.least(1);
done();
});
});
for (var type of ['limit']) {
for (var side of ['buy', 'sell']) {
describe(`> [%%exchangeName%%] Given with open ${type} ${side} order`, function() {
var orderId
before(function() {
this.timeout(TIMEOUT_MS)
return new Promise(resolve => {
console.info(`Getting unattractive order placement for ${BASE_URL}, %%exchangeName%%, ${token}, %%targetCurrencyPair%%, ${side}`)
getUnattractiveOrderPlacement(BASE_URL, `%%exchangeName%%`, token, `%%targetCurrencyPair%%`, side)
.then(orderPlacement => {
console.info(`Got unattractive order placement ${JSON.stringify(orderPlacement)}`)
superagent
.post(`${BASE_URL}/exchange/%%exchangeName%%/order`)
.send(orderPlacement)
.set('Accept', 'application/json')
.set('Authorization', `Bearer ${token}`)
.end((err, res) => {
if (err) {
console.info([orderPlacement, err])
}
expect(err).to.not.exist
expect(res.type).to.eql('application/json')
expect(res.status).to.eql(200)
orderId = res.body.id
resolve();
});
}).catch(error => {
console.info(`Got error while trying to get unattractive order placement ${error}`)
expect(error).to.not.exist
resolve()
})
});
})
after(function() {
this.timeout(TIMEOUT_MS)
return new Promise((resolve) => {
if (orderId) {
superagent
.delete(`${BASE_URL}/exchange/%%exchangeName%%/order/${orderId}`)
.query({symbol : `%%targetCurrencyPair%%`})
.set('Authorization', `Bearer ${token}`)
.end((err, res) => {
expect(err).to.not.exist
expect(res.type).to.eql('application/json')
expect(res.status).to.eql(200)
resolve();
});
}
});
});
it(`> [%%exchangeName%%] Given with open ${type} ${side} order, get order`, function(done) {
this.timeout(TIMEOUT_MS)
superagent
.get(`${BASE_URL}/exchange/%%exchangeName%%/order/${orderId}`)
.query({symbol : `%%targetCurrencyPair%%`})
.set('Authorization', `Bearer ${token}`)
.end((err, res) => {
expect(err).to.not.exist
expect(res.type).to.eql('application/json')
expect(res.status).to.eql(200)
done();
});
})
it(`> [%%exchangeName%%] Given with open ${type} ${side} order, get orders, then ${expectedStatusCodes['fetchOrders']}`, function(done) {
this.timeout(TIMEOUT_MS)
superagent
.get(`${BASE_URL}/exchange/%%exchangeName%%/orders`)
.query({symbol : `%%targetCurrencyPair%%`})
.set('Authorization', `Bearer ${token}`)
.end((err, res) => {
expect(res.type).to.eql('application/json')
expect(res.status).to.eql(expectedStatusCodes['fetchOrders'])
if (expectedStatusCodes['fetchOrders'] >= 200 && expectedStatusCodes['fetchOrders'] < 300) {
expect(err).to.not.exist
}
done();
});
})
it(`> [%%exchangeName%%] Given with open ${type} ${side} order, get open orders`, function(done) {
this.timeout(TIMEOUT_MS)
superagent
.get(`${BASE_URL}/exchange/%%exchangeName%%/orders/open`)
.query({symbol : `%%targetCurrencyPair%%`})
.set('Authorization', `Bearer ${token}`)
.end((err, res) => {
expect(err).to.not.exist
expect(res.type).to.eql('application/json')
expect(res.status).to.eql(200)
done();
});
})
it(`> [%%exchangeName%%] Given with open ${type} ${side} order, get closed orders, then ${expectedStatusCodes['fetchClosedOrders']}`, function(done) {
this.timeout(TIMEOUT_MS)
superagent
.get(`${BASE_URL}/exchange/%%exchangeName%%/orders/closed`)
.query({symbol : `%%targetCurrencyPair%%`})
.set('Authorization', `Bearer ${token}`)
.end((err, res) => {
expect(res.type).to.eql('application/json')
expect(res.status).to.eql(expectedStatusCodes['fetchClosedOrders'])
if (expectedStatusCodes['fetchClosedOrders'] >= 200 && expectedStatusCodes['fetchClosedOrders'] < 300) {
expect(err).to.not.exist
}
done();
});
})
it(`> [%%exchangeName%%] Given with open ${type} ${side} order, get my trades, then ${expectedStatusCodes['fetchMyTrades']}`, function(done) {
this.timeout(TIMEOUT_MS)
superagent
.get(`${BASE_URL}/exchange/%%exchangeName%%/trades/mine`)
.query({symbol : `%%targetCurrencyPair%%`})
.set('Authorization', `Bearer ${token}`)
.end((err, res) => {
expect(res.type).to.eql('application/json')
expect(res.status).to.eql(expectedStatusCodes['fetchMyTrades'])
if (expectedStatusCodes['fetchMyTrades'] >= 200 && expectedStatusCodes['fetchMyTrades'] < 300) {
expect(err).to.not.exist
}
done();
});
})
});
} // end of side for-loop
} // end of type for-loop
});
});
});