324 lines
13 KiB
Ruby
324 lines
13 KiB
Ruby
# encoding: UTF-8
|
|
# frozen_string_literal: true
|
|
|
|
describe API::V2::Admin::Withdraws, type: :request do
|
|
let(:admin) { create(:member, :admin, :level_3, email: 'example@gmail.com', uid: 'ID73BF61C8H0') }
|
|
let(:token) { jwt_for(admin) }
|
|
let(:level_3_member) { create(:member, :level_3) }
|
|
let(:level_3_member_token) { jwt_for(level_3_member) }
|
|
before do
|
|
[admin, level_3_member].each do |member|
|
|
member.touch_accounts
|
|
member.accounts.map { |a| a.update(balance: 500) }
|
|
end
|
|
|
|
create(:usd_withdraw, amount: 10.0, sum: 10.0, member: admin)
|
|
create(:usd_withdraw, amount: 9.0, sum: 9.0, member: admin)
|
|
create(:usd_withdraw, amount: 100.0, sum: 100.0, member: level_3_member)
|
|
create(:btc_withdraw, amount: 42.0, sum: 42.0, txid: 'special_txid', member: admin)
|
|
create(:btc_withdraw, amount: 42.0, sum: 42.0, member: admin, aasm_state: :accepted)
|
|
create(:btc_withdraw, amount: 11.0, sum: 11.0, member: level_3_member, aasm_state: :skipped)
|
|
create(:btc_withdraw, amount: 12.0, sum: 12.0, member: level_3_member, aasm_state: :errored)
|
|
end
|
|
|
|
describe 'GET /api/v2/admin/withdraws' do
|
|
let(:url) { '/api/v2/admin/withdraws' }
|
|
|
|
it 'get all withdraws' do
|
|
api_get url, token: token
|
|
|
|
actual = JSON.parse(response.body)
|
|
expected = Withdraw.all
|
|
|
|
expect(actual.length).to eq expected.length
|
|
expect(actual.map { |a| a['state'] }).to match_array expected.map(&:aasm_state)
|
|
expect(actual.map { |a| a['id'] }).to match_array expected.map(&:id)
|
|
expect(actual.map { |a| a['currency'] }).to match_array expected.map(&:currency_id)
|
|
expect(actual.map { |a| a['member'] }).to match_array expected.map(&:member_id)
|
|
expect(actual.map { |a| a['type'] }).to match_array(expected.map { |d| d.currency.coin? ? 'coin' : 'fiat' })
|
|
expect(actual.map { |a| a['uid'] }).to match_array(expected.map { |d| d.member.uid })
|
|
expect(actual.map { |a| a['email'] }).to match_array(expected.map { |d| d.member.email })
|
|
end
|
|
|
|
context 'ordering' do
|
|
it 'ascending by id' do
|
|
api_get url, token: token, params: { order_by: 'id', ordering: 'asc' }
|
|
|
|
actual = JSON.parse(response.body)
|
|
expected = Withdraw.order(id: 'asc')
|
|
|
|
expect(actual.map { |a| a['id'] }).to eq expected.map(&:id)
|
|
end
|
|
|
|
it 'descending by sum' do
|
|
api_get url, token: token, params: { order_by: 'sum', ordering: 'desc' }
|
|
|
|
actual = JSON.parse(response.body)
|
|
expected = Withdraw.order(sum: 'desc')
|
|
|
|
expect(actual.map { |a| a['id'] }).to eq expected.map(&:id)
|
|
end
|
|
end
|
|
|
|
context 'filtering' do
|
|
it 'by member' do
|
|
api_get url, token: token, params: { uid: level_3_member.uid }
|
|
|
|
actual = JSON.parse(response.body)
|
|
expected = Withdraw.where(member_id: level_3_member.id)
|
|
|
|
expect(actual.length).to eq expected.length
|
|
expect(actual.map { |a| a['state'] }).to match_array expected.map(&:aasm_state)
|
|
expect(actual.map { |a| a['id'] }).to match_array expected.map(&:id)
|
|
expect(actual.map { |a| a['currency'] }).to match_array expected.map(&:currency_id)
|
|
expect(actual.map { |a| a['member'] }).to all eq level_3_member.id
|
|
expect(actual.map { |a| a['type'] }).to match_array(expected.map { |d| d.currency.coin? ? 'coin' : 'fiat' })
|
|
expect(actual.map { |a| a['uid'] }).to match_array(expected.map { |d| d.member.uid })
|
|
expect(actual.map { |a| a['email'] }).to match_array(expected.map { |d| d.member.email })
|
|
end
|
|
|
|
it 'by state' do
|
|
api_get url, token: token, params: { state: :skipped }
|
|
|
|
actual = JSON.parse(response.body)
|
|
expected = Withdraw.where(aasm_state: :skipped)
|
|
|
|
expect(actual.map { |a| a['state'] }).to all eq 'skipped'
|
|
expect(actual.length).to eq expected.count
|
|
expect(actual.map { |a| a['id'] }).to match_array expected.map(&:id)
|
|
expect(actual.map { |a| a['uid'] }).to match_array(expected.map { |d| d.member.uid })
|
|
end
|
|
|
|
it 'by multiple states' do
|
|
api_get url, token: token, params: { state: [:skipped, :accepted] }
|
|
|
|
actual = JSON.parse(response.body)
|
|
expected = Withdraw.where(aasm_state: [:skipped, :accepted])
|
|
|
|
expect(actual.map { |a| a['state'] }.uniq).to match_array %w[skipped accepted]
|
|
expect(actual.length).to eq expected.count
|
|
expect(actual.map { |a| a['id'] }).to match_array expected.map(&:id)
|
|
expect(actual.map { |a| a['uid'] }).to match_array(expected.map { |d| d.member.uid })
|
|
end
|
|
|
|
it 'by type' do
|
|
api_get url, token: token, params: { type: 'coin' }
|
|
|
|
actual = JSON.parse(response.body)
|
|
expected = Withdraw.where(type: 'Withdraws::Coin')
|
|
|
|
expect(actual.length).to eq expected.length
|
|
expect(actual.map { |a| a['state'] }).to match_array expected.map(&:aasm_state)
|
|
expect(actual.map { |a| a['id'] }).to match_array expected.map(&:id)
|
|
expect(actual.map { |a| a['currency'] }).to match_array expected.map(&:currency_id)
|
|
expect(actual.map { |a| a['member'] }).to match_array expected.map(&:member_id)
|
|
expect(actual.map { |a| a['type'] }).to all eq 'coin'
|
|
end
|
|
|
|
it 'by txid' do
|
|
api_get url, token: token, params: { txid: Withdraw.where(type: 'Withdraws::Coin').first.txid }
|
|
|
|
actual = JSON.parse(response.body)
|
|
expected = Withdraw.where(type: 'Withdraws::Coin').first
|
|
|
|
expect(actual.length).to eq 1
|
|
expect(actual.first['state']).to eq expected.aasm_state
|
|
expect(actual.first['id']).to eq expected.id
|
|
expect(actual.first['currency']).to eq expected.currency_id
|
|
expect(actual.first['member']).to eq expected.member_id
|
|
expect(actual.first['type']).to eq 'coin'
|
|
end
|
|
|
|
it 'by wallet_type' do
|
|
wallet_type = Wallet.joins(:currencies)
|
|
.find_by(currencies: { id: Withdraw.find_by(type: 'Withdraws::Coin').currency_id })
|
|
.gateway
|
|
api_get url, token: token, params: { wallet_type: wallet_type }
|
|
|
|
actual = JSON.parse(response.body)
|
|
|
|
expect(actual.length).to eq Withdraw.where(currency: Currency.joins(:wallets)
|
|
.where(wallets: { id: Wallet.where(gateway: wallet_type) })).count
|
|
end
|
|
end
|
|
end
|
|
|
|
describe 'GET /api/v2/admin/withdraws/:id' do
|
|
context 'invalid params' do
|
|
context 'non-integer id' do
|
|
it do
|
|
api_get '/api/v2/admin/withdraws/id', token: token
|
|
expect(response).to include_api_error('admin.withdraw.non_integer_id')
|
|
end
|
|
end
|
|
|
|
context 'withdraw does not exist' do
|
|
it do
|
|
api_get "/api/v2/admin/withdraws/#{Withdraw.last.id + 1}", token: token
|
|
expect(response).to include_api_error('record.not_found')
|
|
end
|
|
end
|
|
end
|
|
|
|
context 'with beneficiary' do
|
|
context 'has beneficiary' do
|
|
let!(:withdraw) { create(:usd_withdraw, :with_beneficiary, :with_deposit_liability) }
|
|
it 'includes beneficiary in withdrawal payload' do
|
|
beneficiary_json = API::V2::Entities::Beneficiary
|
|
.represent(withdraw.beneficiary)
|
|
.as_json
|
|
.deep_stringify_keys
|
|
|
|
api_get "/api/v2/admin/withdraws/#{withdraw.id}", token: token
|
|
expect(response_body['beneficiary']).to_not be_nil
|
|
expect(response_body['beneficiary']).to eq(beneficiary_json)
|
|
end
|
|
end
|
|
|
|
context 'does not have beneficiary' do
|
|
let!(:withdraw) { create(:usd_withdraw, :with_deposit_liability) }
|
|
|
|
it 'includes beneficiary in withdrawal payload' do
|
|
api_get "/api/v2/admin/withdraws/#{withdraw.id}", token: token
|
|
expect(response_body['beneficiary']).to be_nil
|
|
end
|
|
end
|
|
end
|
|
|
|
context 'with error message' do
|
|
context 'does not have error message' do
|
|
let!(:withdraw) { create(:usd_withdraw, :with_beneficiary, :with_deposit_liability, aasm_state: :succeed) }
|
|
|
|
it 'without error message in withdrawal payload' do
|
|
api_get "/api/v2/admin/withdraws/#{withdraw.id}", token: token
|
|
expect(response_body.include?('error')).to be_falsey
|
|
end
|
|
end
|
|
|
|
context 'includes error message' do
|
|
let!(:withdraw) { create(:usd_withdraw, :with_beneficiary, :with_deposit_liability, aasm_state: :skipped) }
|
|
|
|
it 'includes error message in withdrawal payload' do
|
|
api_get "/api/v2/admin/withdraws/#{withdraw.id}", token: token
|
|
expect(response_body.include?('error')).to be_truthy
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
describe 'PUT /api/v2/admin/withdraws' do
|
|
let(:url) { '/api/v2/admin/withdraws' }
|
|
let(:fiat) { Withdraw.where(type: 'Withdraws::Fiat').first }
|
|
let(:coin) { Withdraw.where(type: 'Withdraws::Coin').first }
|
|
context 'updates withdraw' do
|
|
it 'updates empty metadata' do
|
|
api_put url, token: token, params: { metadata: { info: :some }, id: coin.id }
|
|
expect(response_body['metadata']).to eq({'info' => 'some'})
|
|
end
|
|
|
|
it 'updates existing metadata' do
|
|
coin.update!(metadata: { data: :data })
|
|
api_put url, token: token, params: { metadata: { info: :some }, id: coin.id }
|
|
expect(response_body['metadata']).to eq({'data' => 'data', 'info' => 'some'})
|
|
end
|
|
|
|
it 'updates existing metadata' do
|
|
coin.update!(metadata: { info: :some })
|
|
api_put url, token: token, params: { metadata: { info: :some1 }, id: coin.id }
|
|
expect(response_body['metadata']).to eq({ 'info' => 'some1' })
|
|
end
|
|
end
|
|
end
|
|
|
|
describe 'POST /api/v2/admin/withdraws/actions' do
|
|
let(:url) { '/api/v2/admin/withdraws/actions' }
|
|
let(:fiat) { Withdraw.where(type: 'Withdraws::Fiat').first }
|
|
let(:coin) { Withdraw.where(type: 'Withdraws::Coin').first }
|
|
|
|
context 'validates params' do
|
|
it 'does not pass unsupported action' do
|
|
api_post url, token: token, params: { action: 'illegal', id: fiat.id }
|
|
|
|
expect(response.status).to eq 422
|
|
expect(response).to include_api_error('admin.withdraw.invalid_action')
|
|
end
|
|
|
|
it 'passes supported action for coin' do
|
|
api_post url, token: token, params: { action: 'process', id: coin.id }
|
|
expect(response).not_to include_api_error('admin.withdraw.invalid_action')
|
|
end
|
|
|
|
it 'passes supported action for fiat' do
|
|
api_post url, token: token, params: { action: 'reject', id: fiat.id }
|
|
expect(response).not_to include_api_error('admin.withdraw.invalid_action')
|
|
end
|
|
|
|
it 'does not pass coin action for fiat' do
|
|
api_post url, token: token, params: { action: 'load', id: fiat.id }
|
|
|
|
expect(response.status).to eq 422
|
|
expect(response).to include_api_error('admin.withdraw.cannot_load')
|
|
end
|
|
end
|
|
|
|
context 'updates withdraw' do
|
|
before { [coin, fiat].map(&:accept!) }
|
|
|
|
it 'process coin' do
|
|
api_post url, token: token, params: { action: 'process', id: coin.id }
|
|
expect(coin.reload.aasm_state).to eq('processing')
|
|
end
|
|
|
|
it 'reject fiat' do
|
|
api_post url, token: token, params: { action: 'reject', id: fiat.id }
|
|
expect(fiat.reload.aasm_state).to eq('rejected')
|
|
expect(response).to be_successful
|
|
end
|
|
|
|
it 'fail coin' do
|
|
coin.accept!
|
|
coin.process!
|
|
api_post url, token: token, params: { action: 'fail', id: coin.id }
|
|
expect(coin.reload.aasm_state).to eq('failed')
|
|
expect(response).to be_successful
|
|
end
|
|
|
|
it 'load coin with txid' do
|
|
BlockchainService.any_instance.expects(:fetch_transaction).once.returns(Peatio::Transaction.new)
|
|
coin.accept!
|
|
api_post url, token: token, params: { action: 'load', id: coin.id, txid: 'new_txid' }
|
|
expect(coin.reload.txid).to eq('new_txid')
|
|
expect(coin.aasm_state).to eq('confirming')
|
|
expect(response).to be_successful
|
|
end
|
|
|
|
it 'load fiat with txid' do
|
|
fiat.accept!
|
|
expect {
|
|
api_post url, token: token, params: { action: 'load', id: fiat.id, txid: 'new_txid' }
|
|
}.not_to change { fiat }
|
|
expect(response).to include_api_error('admin.withdraw.redundant_txid')
|
|
end
|
|
|
|
it 'load coin without txid with txid as param' do
|
|
BlockchainService.any_instance.expects(:fetch_transaction).once.returns(Peatio::Transaction.new)
|
|
coin.update(txid: nil)
|
|
coin.accept!
|
|
api_post url, token: token, params: { action: 'load', id: coin.id, txid: 'new_txid' }
|
|
expect(coin.reload.txid).to eq('new_txid')
|
|
expect(coin.aasm_state).to eq('confirming')
|
|
expect(response).to be_successful
|
|
end
|
|
|
|
it 'load coin without txid' do
|
|
coin.update(txid: nil)
|
|
coin.accept!
|
|
expect {
|
|
api_post url, token: token, params: { action: 'load', id: coin.id }
|
|
}.not_to change { coin }
|
|
expect(response).to include_api_error('admin.withdraw.cannot_load')
|
|
end
|
|
end
|
|
end
|
|
end
|