Initial commit
This commit is contained in:
76
spec/workers/deposit_coin_address_spec.rb
Normal file
76
spec/workers/deposit_coin_address_spec.rb
Normal file
@@ -0,0 +1,76 @@
|
||||
# encoding: UTF-8
|
||||
# frozen_string_literal: true
|
||||
|
||||
describe Workers::AMQP::DepositCoinAddress do
|
||||
let(:member) { create(:member, :barong) }
|
||||
let(:address) { Faker::Blockchain::Bitcoin.address }
|
||||
let(:secret) { PasswordGenerator.generate(64) }
|
||||
let(:wallet) { Wallet.deposit.find_by(blockchain_key: 'btc-testnet') }
|
||||
let(:payment_address) { member.payment_address(wallet.id) }
|
||||
let(:create_address_result) do
|
||||
{ address: address,
|
||||
secret: secret,
|
||||
details: { label: 'new-label' } }
|
||||
end
|
||||
|
||||
subject { member.payment_address(wallet.id).address }
|
||||
|
||||
it 'raise error on databse connection error' do
|
||||
Member.stubs(:find_by_id).raises(Mysql2::Error::ConnectionError.new(''))
|
||||
expect {
|
||||
Workers::AMQP::DepositCoinAddress.new.process(member_id: member.id, wallet_id: wallet.id)
|
||||
}.to raise_error Mysql2::Error::ConnectionError
|
||||
end
|
||||
|
||||
context 'wallet service' do
|
||||
before do
|
||||
WalletService.any_instance
|
||||
.expects(:create_address!)
|
||||
.returns(create_address_result)
|
||||
end
|
||||
|
||||
it 'is passed to wallet service' do
|
||||
Workers::AMQP::DepositCoinAddress.new.process(member_id: member.id, wallet_id: wallet.id)
|
||||
expect(subject).to eq address
|
||||
payment_address.reload
|
||||
expect(payment_address.as_json
|
||||
.deep_symbolize_keys
|
||||
.slice(:address, :secret, :details)).to eq(create_address_result)
|
||||
end
|
||||
|
||||
context 'empty address details' do
|
||||
let(:create_address_result) do
|
||||
{ address: address,
|
||||
secret: secret }
|
||||
end
|
||||
|
||||
it 'is passed to wallet service' do
|
||||
Workers::AMQP::DepositCoinAddress.new.process(member_id: member.id, wallet_id: wallet.id)
|
||||
expect(subject).to eq address
|
||||
payment_address.reload
|
||||
expect(payment_address.as_json
|
||||
.deep_symbolize_keys
|
||||
.slice(:address, :secret, :details)).to eq(create_address_result.merge(details: {}))
|
||||
end
|
||||
end
|
||||
|
||||
context 'should skip address with details' do
|
||||
let(:create_address_result) do
|
||||
{ address: nil,
|
||||
secret: secret,
|
||||
details: {
|
||||
address_id: 'address_id'
|
||||
} }
|
||||
end
|
||||
|
||||
it 'shouldnt create address' do
|
||||
Workers::AMQP::DepositCoinAddress.new.process(member_id: member.id, wallet_id: wallet.id)
|
||||
expect(subject).to eq nil
|
||||
payment_address.reload
|
||||
expect(payment_address.as_json
|
||||
.deep_symbolize_keys
|
||||
.slice(:address, :secret, :details)).to eq(create_address_result.merge(details: {:address_id=>"address_id"}))
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
155
spec/workers/deposit_spec.rb
Normal file
155
spec/workers/deposit_spec.rb
Normal file
@@ -0,0 +1,155 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
describe Workers::Daemons::Deposit do
|
||||
let(:btc_deposit) { create(:deposit, :deposit_btc) }
|
||||
let(:eth_deposit) { create(:deposit, :deposit_eth) }
|
||||
let(:trst_deposit) { create(:deposit, :deposit_trst) }
|
||||
|
||||
let(:collected_spread) do
|
||||
spread.each_with_index.map do |s, i|
|
||||
s.merge(hash: "hash-#{i}")
|
||||
end
|
||||
end
|
||||
|
||||
subject { Workers::Daemons::Deposit.new }
|
||||
|
||||
context 'collect btc deposit' do
|
||||
before do
|
||||
btc_deposit.accept!
|
||||
btc_deposit.process!
|
||||
btc_deposit.update!(updated_at: Time.now - 20.minutes)
|
||||
transactions = collected_spread.map { |s| Peatio::Transaction.new(s) }
|
||||
WalletService.any_instance
|
||||
.expects(:collect_deposit!)
|
||||
.with(instance_of(Deposits::Coin), anything)
|
||||
.returns(transactions)
|
||||
end
|
||||
|
||||
let(:spread) do
|
||||
[{ to_address: 'to-address', amount: 0.1, status: 'pending' }]
|
||||
end
|
||||
|
||||
it 'process one btc deposit' do
|
||||
subject.process
|
||||
expect(btc_deposit.reload.collected?).to be_truthy
|
||||
end
|
||||
end
|
||||
|
||||
context 'collect eth deposit' do
|
||||
before do
|
||||
eth_deposit.accept!
|
||||
eth_deposit.process!
|
||||
eth_deposit.update!(updated_at: Time.now - 20.minutes)
|
||||
end
|
||||
|
||||
before do
|
||||
transactions = collected_spread.map { |s| Peatio::Transaction.new(s) }
|
||||
WalletService.any_instance
|
||||
.expects(:collect_deposit!)
|
||||
.with(instance_of(Deposits::Coin), anything)
|
||||
.returns(transactions)
|
||||
end
|
||||
|
||||
let(:spread) do
|
||||
[{ to_address: 'to-address', amount: 0.1, status: 'pending' }]
|
||||
end
|
||||
|
||||
it 'process one eth deposit' do
|
||||
subject.process
|
||||
expect(eth_deposit.reload.collected?).to be_truthy
|
||||
end
|
||||
end
|
||||
|
||||
context 'collect fee for trst deposit' do
|
||||
before do
|
||||
trst_deposit.accept!
|
||||
trst_deposit.process!
|
||||
trst_deposit.update!(updated_at: Time.now - 20.minutes)
|
||||
end
|
||||
|
||||
before do
|
||||
transactions = collected_spread.map { |s| Peatio::Transaction.new(s) }
|
||||
WalletService.any_instance
|
||||
.expects(:deposit_collection_fees!)
|
||||
.with(instance_of(Deposits::Coin), anything)
|
||||
.returns(transactions)
|
||||
end
|
||||
|
||||
let(:spread) do
|
||||
[{ to_address: 'to-address', amount: 0.1, status: 'pending' }]
|
||||
end
|
||||
|
||||
it 'process one trst deposit' do
|
||||
subject.process
|
||||
expect(trst_deposit.reload.fee_processing?).to be_truthy
|
||||
end
|
||||
end
|
||||
|
||||
context 'collect trst deposit' do
|
||||
before do
|
||||
trst_deposit.accept!
|
||||
trst_deposit.process!
|
||||
trst_deposit.fee_process!
|
||||
trst_deposit.update!(updated_at: Time.now - 20.minutes)
|
||||
end
|
||||
|
||||
before do
|
||||
transactions = collected_spread.map { |s| Peatio::Transaction.new(s) }
|
||||
WalletService.any_instance
|
||||
.expects(:collect_deposit!)
|
||||
.with(instance_of(Deposits::Coin), anything)
|
||||
.returns(transactions)
|
||||
end
|
||||
|
||||
let(:spread) do
|
||||
[{ to_address: 'to-address', amount: 0.1, status: 'pending' }]
|
||||
end
|
||||
|
||||
it 'process one trst deposit' do
|
||||
subject.process
|
||||
expect(trst_deposit.reload.collected?).to be_truthy
|
||||
end
|
||||
end
|
||||
|
||||
context 'error raised on collection step' do
|
||||
before do
|
||||
btc_deposit.accept!
|
||||
btc_deposit.process!
|
||||
btc_deposit.update!(updated_at: Time.now - 20.minutes)
|
||||
WalletService.any_instance
|
||||
.expects(:collect_deposit!)
|
||||
.with(instance_of(Deposits::Coin), anything)
|
||||
.raises(StandardError.new)
|
||||
end
|
||||
|
||||
let(:spread) do
|
||||
[{ to_address: 'to-address', amount: 0.1, status: 'pending' }]
|
||||
end
|
||||
|
||||
it 'process one btc deposit' do
|
||||
subject.process
|
||||
expect(btc_deposit.reload.processing?).to be_truthy
|
||||
end
|
||||
end
|
||||
|
||||
context 'error raised on collection fee step' do
|
||||
before do
|
||||
eth_deposit.accept!
|
||||
eth_deposit.process!
|
||||
eth_deposit.update!(updated_at: Time.now - 20.minutes)
|
||||
WalletService.any_instance
|
||||
.expects(:deposit_collection_fees!)
|
||||
.with(instance_of(Deposits::Coin), anything)
|
||||
.raises(StandardError.new)
|
||||
end
|
||||
|
||||
let(:spread) do
|
||||
[{ to_address: 'to-address', amount: 0.1, status: 'pending' }]
|
||||
end
|
||||
|
||||
it 'process one eth deposit' do
|
||||
subject.process
|
||||
expect(eth_deposit.reload.processing?).to be_truthy
|
||||
end
|
||||
end
|
||||
end
|
||||
128
spec/workers/matching_spec.rb
Normal file
128
spec/workers/matching_spec.rb
Normal file
@@ -0,0 +1,128 @@
|
||||
# encoding: UTF-8
|
||||
# frozen_string_literal: true
|
||||
|
||||
describe Workers::AMQP::Matching do
|
||||
let(:alice) { who_is_billionaire }
|
||||
let(:bob) { who_is_billionaire }
|
||||
let(:market) { Market.find(:btcusd) }
|
||||
|
||||
subject { Workers::AMQP::Matching.new }
|
||||
|
||||
context 'engines' do
|
||||
it 'should get all engines' do
|
||||
expect(subject.engines.keys.sort).to eq Market.pluck(:id).sort
|
||||
end
|
||||
|
||||
it 'should started all engines' do
|
||||
expect(subject.engines.values.map(&:mode)).to eq Array.new(Market.count, :run)
|
||||
end
|
||||
end
|
||||
|
||||
context 'partial match' do
|
||||
let(:existing) { create(:order_ask, :btcusd, price: '4001', volume: '10.0', member: alice) }
|
||||
|
||||
before do
|
||||
subject.process({ action: 'submit', order: existing.to_matching_attributes }, {}, {})
|
||||
end
|
||||
|
||||
it 'should started engine' do
|
||||
expect(subject.engines['btcusd'].mode).to eq :run
|
||||
end
|
||||
|
||||
it 'should match part of existing order' do
|
||||
order = create(:order_bid, :btcusd, price: '4001', volume: '8.0', member: bob)
|
||||
|
||||
AMQP::Queue.expects(:enqueue)
|
||||
.with(:trade_executor, { action: 'execute', trade: { market_id: market.id, maker_order_id: existing.id, taker_order_id: order.id, strike_price: '4001'.to_d, amount: '8.0'.to_d, total: '32008'.to_d } }, anything)
|
||||
subject.process({ action: 'submit', order: order.to_matching_attributes }, {}, {})
|
||||
end
|
||||
|
||||
it 'should match part of new order' do
|
||||
order = create(:order_bid, :btcusd, price: '4001', volume: '12.0', member: bob)
|
||||
|
||||
AMQP::Queue.expects(:enqueue)
|
||||
.with(:trade_executor, { action: 'execute', trade: { market_id: market.id, maker_order_id: existing.id, taker_order_id: order.id, strike_price: '4001'.to_d, amount: '10.0'.to_d, total: '40010'.to_d } }, anything)
|
||||
subject.process({ action: 'submit', order: order.to_matching_attributes }, {}, {})
|
||||
end
|
||||
end
|
||||
|
||||
context 'complex partial match' do
|
||||
# submit | ask price/volume | bid price/volume |
|
||||
# -----------------------------------------------
|
||||
# ask1 | 4003/3 | |
|
||||
# -----------------------------------------------
|
||||
# ask2 | 4002/3, 4003/3 | |
|
||||
# -----------------------------------------------
|
||||
# bid3 | | 4003/2 |
|
||||
# -----------------------------------------------
|
||||
# ask4 | 4002/3 | |
|
||||
# -----------------------------------------------
|
||||
# bid5 | | |
|
||||
# -----------------------------------------------
|
||||
# bid6 | | 4001/5 |
|
||||
# -----------------------------------------------
|
||||
let!(:ask1) { create(:order_ask, :btcusd, price: '4003', volume: '3.0', member: alice) }
|
||||
let!(:ask2) { create(:order_ask, :btcusd, price: '4002', volume: '3.0', member: alice) }
|
||||
let!(:bid3) { create(:order_bid, :btcusd, price: '4003', volume: '8.0', member: bob) }
|
||||
let!(:ask4) { create(:order_ask, :btcusd, price: '4002', volume: '5.0', member: alice) }
|
||||
let!(:bid5) { create(:order_bid, :btcusd, price: '4003', volume: '3.0', member: bob) }
|
||||
let!(:bid6) { create(:order_bid, :btcusd, price: '4001', volume: '5.0', member: bob) }
|
||||
|
||||
let!(:orderbook) { Matching::OrderBookManager.new('btcusd', broadcast: false) }
|
||||
let!(:engine) { Matching::Engine.new(market, mode: :run) }
|
||||
|
||||
before do
|
||||
engine.stubs(:orderbook).returns(orderbook)
|
||||
::Matching::Engine.stubs(:new).returns(engine)
|
||||
end
|
||||
|
||||
it 'should create many trades' do
|
||||
AMQP::Queue.expects(:enqueue)
|
||||
.with(:trade_executor, { action: 'execute', trade: { market_id: market.id, maker_order_id: ask1.id, taker_order_id: bid3.id, strike_price: ask1.price, amount: ask1.volume, total: '12009'.to_d } }, anything).once
|
||||
AMQP::Queue.expects(:enqueue)
|
||||
.with(:trade_executor, { action: 'execute', trade: { market_id: market.id, maker_order_id: ask2.id, taker_order_id: bid3.id, strike_price: ask2.price, amount: ask2.volume, total: '12006'.to_d } }, anything).once
|
||||
AMQP::Queue.expects(:enqueue)
|
||||
.with(:trade_executor, { action: 'execute', trade: { market_id: market.id, taker_order_id: ask4.id, maker_order_id: bid3.id, strike_price: bid3.price, amount: '2.0'.to_d, total: '8006'.to_d } }, anything).once
|
||||
AMQP::Queue.expects(:enqueue)
|
||||
.with(:trade_executor, { action: 'execute', trade: { market_id: market.id, maker_order_id: ask4.id, taker_order_id: bid5.id, strike_price: ask4.price, amount: bid5.volume, total: '12006'.to_d } }, anything).once
|
||||
|
||||
subject
|
||||
end
|
||||
end
|
||||
|
||||
context 'cancel order' do
|
||||
let(:existing) { create(:order_ask, :btcusd, price: '4001', volume: '10.0', member: alice) }
|
||||
|
||||
before do
|
||||
subject.process({ action: 'submit', order: existing.to_matching_attributes }, {}, {})
|
||||
end
|
||||
|
||||
it 'should cancel existing order' do
|
||||
subject.process({ action: 'cancel', order: existing.to_matching_attributes }, {}, {})
|
||||
expect(subject.engines[market.id].ask_orders.limit_orders).to be_empty
|
||||
end
|
||||
end
|
||||
|
||||
context 'dryrun' do
|
||||
let!(:bid) { create(:order_bid, :btcusd, price: '4001', volume: '8.0', member: bob) }
|
||||
|
||||
subject { Workers::AMQP::Matching.new(mode: :dryrun) }
|
||||
|
||||
context 'very old orders matched' do
|
||||
let!(:ask) { create(:order_ask, :btcusd, price: '4000', volume: '3.0', member: alice, created_at: 1.day.ago) }
|
||||
|
||||
it 'should not start engine' do
|
||||
expect(subject.engines['btcusd'].mode).to eq :dryrun
|
||||
expect(subject.engines['btcusd'].queue.size).to eq 1
|
||||
end
|
||||
end
|
||||
|
||||
context 'buffered orders matched' do
|
||||
let!(:ask) { create(:order_ask, :btcusd, price: '4000', volume: '3.0', member: alice) }
|
||||
|
||||
it 'should start engine' do
|
||||
expect(subject.engines['btcusd'].mode).to eq :run
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
126
spec/workers/withdraw_coin_spec.rb
Normal file
126
spec/workers/withdraw_coin_spec.rb
Normal file
@@ -0,0 +1,126 @@
|
||||
# encoding: UTF-8
|
||||
# frozen_string_literal: true
|
||||
|
||||
describe Workers::AMQP::WithdrawCoin do
|
||||
let(:member) { create(:member, :barong) }
|
||||
let(:withdrawal) { create(:btc_withdraw, :with_deposit_liability) }
|
||||
let(:processing_withdrawal) do
|
||||
create(:btc_withdraw, :with_deposit_liability)
|
||||
.tap(&:accept!)
|
||||
.tap(&:process!)
|
||||
end
|
||||
|
||||
context 'withdrawal does not exist' do
|
||||
before { Withdraw.expects(:find_by_id).returns(nil) }
|
||||
|
||||
it 'returns nil' do
|
||||
expect(Workers::AMQP::WithdrawCoin.new.process(withdrawal.as_json)).to be(nil)
|
||||
end
|
||||
end
|
||||
|
||||
context 'withdrawal is not in processing state' do
|
||||
it 'returns nil' do
|
||||
expect(Workers::AMQP::WithdrawCoin.new.process(withdrawal.as_json)).to be(nil)
|
||||
end
|
||||
end
|
||||
|
||||
context 'withdrawal with empty rid' do
|
||||
before do
|
||||
# withdrawal.submit!
|
||||
# withdrawal.accept!
|
||||
# withdrawal.process!
|
||||
#
|
||||
# Withdraws::Coin.any_instance
|
||||
# .expects(:rid)
|
||||
# .with(anything)
|
||||
# .twice
|
||||
# .returns('')
|
||||
|
||||
end
|
||||
|
||||
# TODO: Finalize me.
|
||||
it 'returns nil and fail withdrawal' do
|
||||
# expect(Workers::AMQP::WithdrawCoin.new.process(processing_withdrawal.as_json)).to be(nil)
|
||||
# expect(processing_withdrawal.reload.failed?).to be_truthy
|
||||
end
|
||||
end
|
||||
|
||||
context 'hot wallet does not exist' do
|
||||
before do
|
||||
Wallet.expects(:active)
|
||||
.returns(Wallet.none)
|
||||
end
|
||||
|
||||
it 'returns nil and skip withdrawal' do
|
||||
expect(Workers::AMQP::WithdrawCoin.new.process(processing_withdrawal.as_json)).to be(nil)
|
||||
expect(processing_withdrawal.reload.skipped?).to be_truthy
|
||||
end
|
||||
end
|
||||
|
||||
context 'WalletService2 raises error' do
|
||||
before do
|
||||
WalletService.any_instance.expects(:load_balance!)
|
||||
.returns(100)
|
||||
WalletService.any_instance.expects(:build_withdrawal!)
|
||||
.raises(Peatio::Wallet::Registry::NotRegisteredAdapterError)
|
||||
end
|
||||
|
||||
it 'returns true and marks withdrawal as errored' do
|
||||
expect(Workers::AMQP::WithdrawCoin.new.process(processing_withdrawal.as_json)).to be_truthy
|
||||
expect(processing_withdrawal.reload.errored?).to be_truthy
|
||||
end
|
||||
end
|
||||
|
||||
context 'wallet balance is not sufficient' do
|
||||
before do
|
||||
WalletService.any_instance
|
||||
.expects(:load_balance!)
|
||||
.returns(withdrawal.amount * 0.9)
|
||||
end
|
||||
|
||||
it 'returns nil and skip withdrawal' do
|
||||
expect(Workers::AMQP::WithdrawCoin.new.process(processing_withdrawal.as_json)).to be(true)
|
||||
expect(processing_withdrawal.reload.skipped?).to be_truthy
|
||||
end
|
||||
end
|
||||
|
||||
context 'wallet balance is sufficient but build_withdrawal! raises error' do
|
||||
before do
|
||||
WalletService.any_instance
|
||||
.expects(:load_balance!)
|
||||
.returns(withdrawal.amount)
|
||||
|
||||
WalletService.any_instance
|
||||
.expects(:build_withdrawal!)
|
||||
.with(instance_of(Withdraws::Coin))
|
||||
.raises(Peatio::Blockchain::ClientError)
|
||||
end
|
||||
|
||||
it 'returns true and marks withdrawal as errored' do
|
||||
expect(Workers::AMQP::WithdrawCoin.new.process(processing_withdrawal.as_json)).to be_truthy
|
||||
expect(processing_withdrawal.reload.errored?).to be_truthy
|
||||
end
|
||||
end
|
||||
|
||||
context 'wallet balance is sufficient and build_withdrawal! returns transaction' do
|
||||
before do
|
||||
WalletService.any_instance
|
||||
.expects(:load_balance!)
|
||||
.returns(withdrawal.amount)
|
||||
|
||||
transaction = Peatio::Transaction.new(amount: withdrawal.amount,
|
||||
to_address: withdrawal.rid,
|
||||
hash: 'hash-1')
|
||||
WalletService.any_instance
|
||||
.expects(:build_withdrawal!)
|
||||
.with(instance_of(Withdraws::Coin))
|
||||
.returns(transaction)
|
||||
end
|
||||
|
||||
it 'returns true and dispatch withdrawal' do
|
||||
expect(Workers::AMQP::WithdrawCoin.new.process(processing_withdrawal.as_json)).to be_truthy
|
||||
expect(processing_withdrawal.reload.confirming?).to be_truthy
|
||||
expect(processing_withdrawal.txid).to eq('hash-1')
|
||||
end
|
||||
end
|
||||
end
|
||||
Reference in New Issue
Block a user