Initial commit
This commit is contained in:
468
spec/api/v2/management/withdraws_spec.rb
Normal file
468
spec/api/v2/management/withdraws_spec.rb
Normal file
@@ -0,0 +1,468 @@
|
||||
# encoding: UTF-8
|
||||
# frozen_string_literal: true
|
||||
|
||||
describe API::V2::Management::Withdraws, type: :request do
|
||||
before do
|
||||
defaults_for_management_api_v1_security_configuration!
|
||||
management_api_v1_security_configuration.merge! \
|
||||
scopes: {
|
||||
read_withdraws: { permitted_signers: %i[alex jeff], mandatory_signers: %i[alex] },
|
||||
write_withdraws: { permitted_signers: %i[alex jeff james], mandatory_signers: %i[alex jeff] }
|
||||
}
|
||||
end
|
||||
|
||||
describe 'list withdraws' do
|
||||
def request
|
||||
post_json '/api/v2/management/withdraws', multisig_jwt_management_api_v1({ data: data }, *signers)
|
||||
end
|
||||
|
||||
let(:data) { {} }
|
||||
let(:signers) { %i[alex jeff] }
|
||||
let(:members) { create_list(:member, 2, :barong) }
|
||||
|
||||
before do
|
||||
Withdraw::STATES.tap do |states|
|
||||
(states.count * 2).times do
|
||||
create(:btc_withdraw, :with_deposit_liability, sum: 1, member: members.sample, aasm_state: states.sample, rid: Faker::Blockchain::Bitcoin.address)
|
||||
create(:usd_withdraw, :with_deposit_liability, sum: 1, member: members.sample, aasm_state: states.sample, rid: Faker::Bank.iban)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
it 'returns withdraws' do
|
||||
request
|
||||
expect(response).to have_http_status(200)
|
||||
expect(JSON.parse(response.body).map { |x| x.fetch('tid') }).to eq Withdraw.order(id: :desc).pluck(:tid)
|
||||
end
|
||||
|
||||
it 'filters by member' do
|
||||
member = members.last
|
||||
data.merge!(uid: member.uid)
|
||||
request
|
||||
expect(response).to have_http_status(200)
|
||||
expect(JSON.parse(response.body).count).to eq member.withdraws.count
|
||||
end
|
||||
|
||||
it 'filters by currency' do
|
||||
data.merge!(currency: :usd)
|
||||
request
|
||||
expect(response).to have_http_status(200)
|
||||
expect(JSON.parse(response.body).count).to eq Withdraw.where(currency_id: :usd).count
|
||||
end
|
||||
|
||||
it 'filters by state' do
|
||||
Withdraw::STATES.each do |state|
|
||||
data.merge!(state: state)
|
||||
request
|
||||
expect(response).to have_http_status(200)
|
||||
expect(JSON.parse(response.body).count).to eq Withdraw.where(aasm_state: state).count
|
||||
end
|
||||
end
|
||||
|
||||
it 'paginates' do
|
||||
ids = Withdraw.order(id: :desc).pluck(:tid)
|
||||
data.merge!(page: 1, limit: 4)
|
||||
request
|
||||
expect(response).to have_http_status(200)
|
||||
expect(JSON.parse(response.body).map { |x| x.fetch('tid') }).to eq ids[0...4]
|
||||
data.merge!(page: 3, limit: 4)
|
||||
request
|
||||
expect(response).to have_http_status(200)
|
||||
expect(JSON.parse(response.body).map { |x| x.fetch('tid') }).to eq ids[8...12]
|
||||
end
|
||||
end
|
||||
|
||||
describe 'create withdraw' do
|
||||
def request
|
||||
post_json '/api/v2/management/withdraws/new', multisig_jwt_management_api_v1({ data: data }, *signers)
|
||||
end
|
||||
|
||||
let(:member) { create(:member, :barong) }
|
||||
let(:currency) { Currency.find(:btc) }
|
||||
let(:amount) { 0.1575 }
|
||||
let(:signers) { %i[alex jeff] }
|
||||
let :data do
|
||||
{ uid: member.uid,
|
||||
currency: currency.code,
|
||||
amount: amount.to_s,
|
||||
rid: Faker::Blockchain::Bitcoin.address }
|
||||
end
|
||||
let(:account) { member.get_account(currency) }
|
||||
let(:balance) { 1.2 }
|
||||
before { account.plus_funds(balance) }
|
||||
|
||||
context 'crypto withdraw' do
|
||||
it 'creates new withdraw and immediately submits it' do
|
||||
request
|
||||
expect(response).to have_http_status(201)
|
||||
record = Withdraw.find_by_tid!(JSON.parse(response.body).fetch('tid'))
|
||||
expect(record.sum).to eq 0.1575
|
||||
expect(record.aasm_state).to eq 'accepted'
|
||||
expect(record.rid).to eq data[:rid]
|
||||
expect(record.account).to eq account
|
||||
expect(record.account.balance).to eq (1.2 - amount)
|
||||
expect(record.account.locked).to eq amount
|
||||
expect(response_body['transfer_type']).to eq 'crypto'
|
||||
end
|
||||
|
||||
context 'disabled currency' do
|
||||
before do
|
||||
currency.update(withdrawal_enabled: false)
|
||||
end
|
||||
|
||||
it 'returns error for disabled withdrawal' do
|
||||
request
|
||||
expect(response).to have_http_status(422)
|
||||
expect(response).to include_api_error('management.currency.withdrawal_disabled')
|
||||
end
|
||||
end
|
||||
|
||||
context 'withdrawal with beneficiary' do
|
||||
let(:beneficiary) { create(:beneficiary, state: :active, currency: currency) }
|
||||
let(:data) do
|
||||
{ uid: member.uid,
|
||||
currency: currency.code,
|
||||
amount: amount.to_s,
|
||||
beneficiary_id: beneficiary.id }
|
||||
end
|
||||
|
||||
it 'creates new withdraw and immediately submits it' do
|
||||
request
|
||||
expect(response).to have_http_status(201)
|
||||
record = Withdraw.find_by_tid!(JSON.parse(response.body).fetch('tid'))
|
||||
expect(record.sum).to eq 0.1575
|
||||
expect(record.aasm_state).to eq 'accepted'
|
||||
expect(record.rid).to eq beneficiary.rid
|
||||
expect(record.account).to eq account
|
||||
expect(record.account.balance).to eq (1.2 - amount)
|
||||
expect(record.account.locked).to eq amount
|
||||
end
|
||||
|
||||
context 'pending beneficiary' do
|
||||
before do
|
||||
beneficiary.update(state: :pending)
|
||||
end
|
||||
|
||||
it 'returns error for pending beneficiary' do
|
||||
request
|
||||
expect(response).to have_http_status(422)
|
||||
expect(response).to include_api_error('management.beneficiary.invalid_state_for_withdrawal')
|
||||
end
|
||||
end
|
||||
|
||||
context 'withdrawal with note' do
|
||||
let(:member) { create(:member, :barong) }
|
||||
let(:currency) { Currency.find(:btc) }
|
||||
let(:amount) { 0.1575 }
|
||||
let(:signers) { %i[alex jeff] }
|
||||
let :data do
|
||||
{ uid: member.uid,
|
||||
currency: currency.code,
|
||||
amount: amount.to_s,
|
||||
rid: Faker::Blockchain::Bitcoin.address,
|
||||
note: 'Withdraw money'
|
||||
}
|
||||
end
|
||||
|
||||
it 'returns new withdraw with correct note' do
|
||||
request
|
||||
expect(JSON.parse(response.body)['note']).to eq 'Withdraw money'
|
||||
end
|
||||
end
|
||||
|
||||
context 'withdrawal with transfer_type' do
|
||||
let(:member) { create(:member, :barong) }
|
||||
let(:currency) { Currency.find(:btc) }
|
||||
let(:amount) { 0.1575 }
|
||||
let(:signers) { %i[alex jeff] }
|
||||
let :data do
|
||||
{ uid: member.uid,
|
||||
currency: currency.code,
|
||||
amount: amount.to_s,
|
||||
rid: Faker::Blockchain::Bitcoin.address,
|
||||
transfer_type: 'card'
|
||||
}
|
||||
end
|
||||
|
||||
it 'returns new withdraw with correct transfer_type' do
|
||||
request
|
||||
expect(JSON.parse(response.body)['transfer_type']).to eq 'card'
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'action: :process' do
|
||||
it 'creates new withdraw and immediately submits it' do
|
||||
data.merge!(action: 'process')
|
||||
request
|
||||
expect(response).to have_http_status(201)
|
||||
record = Withdraw.find_by_tid!(JSON.parse(response.body).fetch('tid'))
|
||||
expect(record.sum).to eq 0.1575
|
||||
expect(record.aasm_state).to eq 'accepted'
|
||||
expect(record.rid).to eq data[:rid]
|
||||
expect(record.account).to eq account
|
||||
expect(record.account.balance).to eq (1.2 - amount)
|
||||
expect(record.account.locked).to eq amount
|
||||
end
|
||||
end
|
||||
|
||||
context 'invalid withdraw' do
|
||||
before do
|
||||
data[:action] = :process
|
||||
data[:amount] = '1000'
|
||||
end
|
||||
it 'validates enough balance' do
|
||||
request
|
||||
expect(response).to have_http_status(422)
|
||||
expect(JSON.parse(response.body)).to eq("errors"=>["Account balance is insufficient"])
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'extremely precise values' do
|
||||
before { Currency.any_instance.stubs(:withdraw_fee).returns(BigDecimal(0)) }
|
||||
before { Currency.any_instance.stubs(:precision).returns(16) }
|
||||
it 'keeps precision for amount' do
|
||||
currency.update!(precision: 16)
|
||||
data.merge!(amount: '0.0000000123456789')
|
||||
request
|
||||
expect(response).to have_http_status(201)
|
||||
expect(Withdraw.last.sum.to_s).to eq data[:amount]
|
||||
end
|
||||
end
|
||||
|
||||
context 'fiat withdraw' do
|
||||
let(:currency) { Currency.find(:usd) }
|
||||
let(:amount) { 5 }
|
||||
let(:balance) { 20 }
|
||||
|
||||
it 'creates new withdraw with state set to «submitted»' do
|
||||
request
|
||||
expect(response).to have_http_status(201)
|
||||
expect(account.reload.balance).to eq(15)
|
||||
expect(account.reload.locked).to eq 5
|
||||
expect(Withdraw.last.aasm_state).to eq 'accepted'
|
||||
end
|
||||
|
||||
context 'action: :process' do
|
||||
it 'creates new withdraw with state set to «submitted»' do
|
||||
data.merge!(action: :process)
|
||||
request
|
||||
expect(response).to have_http_status(201)
|
||||
expect(account.reload.balance).to eq(15)
|
||||
expect(account.reload.locked).to eq 0
|
||||
expect(Withdraw.last.aasm_state).to eq 'succeed'
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'get withdraw' do
|
||||
def request
|
||||
post_json '/api/v2/management/withdraws/get', multisig_jwt_management_api_v1({ data: data }, *signers)
|
||||
end
|
||||
|
||||
let(:signers) { %i[alex jeff] }
|
||||
let(:data) { { tid: record.tid } }
|
||||
let(:record) { create(:btc_withdraw, :with_deposit_liability, member: member) }
|
||||
let(:member) { create(:member, :barong) }
|
||||
|
||||
it 'returns withdraw by TID' do
|
||||
request
|
||||
expect(JSON.parse(response.body).fetch('tid')).to eq record.tid
|
||||
end
|
||||
end
|
||||
|
||||
describe 'update withdraw' do
|
||||
def request
|
||||
put_json '/api/v2/management/withdraws/action', multisig_jwt_management_api_v1({ data: data }, *signers)
|
||||
end
|
||||
|
||||
let(:currency) { Currency.find(:usd) }
|
||||
let(:member) { create(:member, :barong) }
|
||||
let(:amount) { 160.79 }
|
||||
let(:signers) { %i[alex jeff] }
|
||||
let(:data) { { tid: record.tid } }
|
||||
let(:account) { member.get_account(currency) }
|
||||
let(:record) { "Withdraws::#{currency.type.camelize}".constantize.create!(member: member, sum: amount, rid: Faker::Bank.iban, currency: currency) }
|
||||
let(:balance) { 800.77 }
|
||||
before { account.plus_funds(balance) }
|
||||
|
||||
context 'crypto withdraws' do
|
||||
let(:currency) { Currency.find(:btc) }
|
||||
|
||||
context 'action: :process' do
|
||||
before { data[:action] = :process }
|
||||
|
||||
it 'processes prepared withdraws' do
|
||||
expect(record.aasm_state).to eq 'prepared'
|
||||
expect(account.reload.balance).to eq balance
|
||||
expect(account.reload.locked).to eq 0
|
||||
request
|
||||
expect(response).to have_http_status(200)
|
||||
record = Withdraw.find_by_tid!(JSON.parse(response.body).fetch('tid'))
|
||||
expect(record.aasm_state).to eq 'accepted'
|
||||
expect(record.account.balance).to eq (balance - amount)
|
||||
expect(record.account.locked).to eq amount
|
||||
end
|
||||
|
||||
it 'processes submitted withdraws' do
|
||||
record.accept!
|
||||
expect(record.aasm_state).to eq 'accepted'
|
||||
expect(account.reload.balance).to eq (balance - amount)
|
||||
expect(account.reload.locked).to eq amount
|
||||
request
|
||||
expect(response).to have_http_status(200)
|
||||
record = Withdraw.find_by_tid!(JSON.parse(response.body).fetch('tid'))
|
||||
expect(record.aasm_state).to eq 'accepted'
|
||||
expect(record.account.balance).to eq (balance - amount)
|
||||
expect(record.account.locked).to eq amount
|
||||
end
|
||||
|
||||
it 'processes accepted withdraws' do
|
||||
record.accept!
|
||||
record.accept!
|
||||
expect(record.aasm_state).to eq 'accepted'
|
||||
expect(account.reload.balance).to eq (balance - amount)
|
||||
expect(account.reload.locked).to eq amount
|
||||
request
|
||||
expect(response).to have_http_status(200)
|
||||
record = Withdraw.find_by_tid!(JSON.parse(response.body).fetch('tid'))
|
||||
expect(record.aasm_state).to eq 'accepted'
|
||||
expect(record.account.balance).to eq (balance - amount)
|
||||
expect(record.account.locked).to eq amount
|
||||
end
|
||||
end
|
||||
|
||||
context 'action: :cancel' do
|
||||
before { data[:action] = :cancel }
|
||||
|
||||
it 'cancels prepared withdraws' do
|
||||
expect(record.aasm_state).to eq 'prepared'
|
||||
expect(account.reload.balance).to eq balance
|
||||
expect(account.reload.locked).to eq 0
|
||||
request
|
||||
expect(response).to have_http_status(200)
|
||||
record = Withdraw.find_by_tid!(JSON.parse(response.body).fetch('tid'))
|
||||
expect(record.aasm_state).to eq 'canceled'
|
||||
expect(record.account.balance).to eq balance
|
||||
expect(record.account.locked).to eq 0
|
||||
end
|
||||
|
||||
it 'cancels submitted withdraws' do
|
||||
record.accept!
|
||||
expect(record.aasm_state).to eq 'accepted'
|
||||
expect(account.reload.balance).to eq (balance - amount)
|
||||
expect(account.reload.locked).to eq amount
|
||||
request
|
||||
expect(response).to have_http_status(200)
|
||||
record = Withdraw.find_by_tid!(JSON.parse(response.body).fetch('tid'))
|
||||
expect(record.aasm_state).to eq 'canceled'
|
||||
expect(record.account.balance).to eq balance
|
||||
expect(record.account.locked).to eq 0
|
||||
end
|
||||
|
||||
it 'cancels accepted withdraws' do
|
||||
record.accept!
|
||||
record.accept!
|
||||
expect(record.aasm_state).to eq 'accepted'
|
||||
expect(account.reload.balance).to eq (balance - amount)
|
||||
expect(account.reload.locked).to eq amount
|
||||
request
|
||||
expect(response).to have_http_status(200)
|
||||
record = Withdraw.find_by_tid!(JSON.parse(response.body).fetch('tid'))
|
||||
expect(record.aasm_state).to eq 'canceled'
|
||||
expect(record.account.balance).to eq balance
|
||||
expect(record.account.locked).to eq 0
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'fiat withdraws' do
|
||||
context 'action: :process' do
|
||||
before { data[:action] = :process }
|
||||
|
||||
it 'processes prepared withdraws' do
|
||||
expect(record.aasm_state).to eq 'prepared'
|
||||
expect(account.reload.balance).to eq balance
|
||||
expect(account.reload.locked).to eq 0
|
||||
request
|
||||
expect(response).to have_http_status(200)
|
||||
record = Withdraw.find_by_tid!(JSON.parse(response.body).fetch('tid'))
|
||||
expect(record.aasm_state).to eq 'succeed'
|
||||
expect(record.account.balance).to eq (balance - amount)
|
||||
expect(record.account.locked).to eq 0
|
||||
end
|
||||
|
||||
it 'processes accepted withdraws' do
|
||||
record.accept!
|
||||
expect(record.aasm_state).to eq 'accepted'
|
||||
expect(account.reload.balance).to eq (balance - amount)
|
||||
expect(account.reload.locked).to eq amount
|
||||
request
|
||||
expect(response).to have_http_status(200)
|
||||
record = Withdraw.find_by_tid!(JSON.parse(response.body).fetch('tid'))
|
||||
expect(record.aasm_state).to eq 'succeed'
|
||||
expect(record.account.balance).to eq (balance - amount)
|
||||
expect(record.account.locked).to eq 0
|
||||
end
|
||||
|
||||
it 'processes accepted withdraws' do
|
||||
record.accept!
|
||||
expect(record.aasm_state).to eq 'accepted'
|
||||
expect(account.reload.balance).to eq (balance - amount)
|
||||
expect(account.reload.locked).to eq amount
|
||||
request
|
||||
expect(response).to have_http_status(200)
|
||||
record = Withdraw.find_by_tid!(JSON.parse(response.body).fetch('tid'))
|
||||
expect(record.aasm_state).to eq 'succeed'
|
||||
expect(record.account.balance).to eq (balance - amount)
|
||||
expect(record.account.locked).to eq 0
|
||||
end
|
||||
end
|
||||
|
||||
context 'action: :cancel' do
|
||||
before { data[:action] = :cancel }
|
||||
|
||||
it 'cancels prepared withdraws' do
|
||||
expect(record.aasm_state).to eq 'prepared'
|
||||
expect(account.reload.balance).to eq balance
|
||||
expect(account.reload.locked).to eq 0
|
||||
request
|
||||
expect(response).to have_http_status(200)
|
||||
record = Withdraw.find_by_tid!(JSON.parse(response.body).fetch('tid'))
|
||||
expect(record.aasm_state).to eq 'canceled'
|
||||
expect(record.account.balance).to eq balance
|
||||
expect(record.account.locked).to eq 0
|
||||
end
|
||||
|
||||
it 'cancels accepted withdraws' do
|
||||
record.accept!
|
||||
expect(record.aasm_state).to eq 'accepted'
|
||||
expect(account.reload.balance).to eq (balance - amount)
|
||||
expect(account.reload.locked).to eq amount
|
||||
request
|
||||
expect(response).to have_http_status(200)
|
||||
record = Withdraw.find_by_tid!(JSON.parse(response.body).fetch('tid'))
|
||||
expect(record.aasm_state).to eq 'canceled'
|
||||
expect(record.account.balance).to eq balance
|
||||
expect(record.account.locked).to eq 0
|
||||
end
|
||||
|
||||
it 'cancels accepted withdraws' do
|
||||
record.accept!
|
||||
record.accept!
|
||||
expect(record.aasm_state).to eq 'accepted'
|
||||
expect(account.reload.balance).to eq (balance - amount)
|
||||
expect(account.reload.locked).to eq amount
|
||||
request
|
||||
expect(response).to have_http_status(200)
|
||||
record = Withdraw.find_by_tid!(JSON.parse(response.body).fetch('tid'))
|
||||
expect(record.aasm_state).to eq 'canceled'
|
||||
expect(record.account.balance).to eq balance
|
||||
expect(record.account.locked).to eq 0
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
Reference in New Issue
Block a user