Initial commit
This commit is contained in:
135
spec/api/v2/resource/addresses_spec.rb
Normal file
135
spec/api/v2/resource/addresses_spec.rb
Normal file
@@ -0,0 +1,135 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
describe 'Addresses API test' do
|
||||
include_context 'bearer authentication'
|
||||
let!(:create_member_permission) do
|
||||
create :permission,
|
||||
role: 'member'
|
||||
end
|
||||
let!(:image) { fixture_file_upload('/files/documents_test.jpg', 'image/jpg') }
|
||||
before do
|
||||
allow(Barong::App.config).to receive_messages(required_docs_expire: false)
|
||||
end
|
||||
|
||||
describe 'POST /api/v2/resource/addresses/' do
|
||||
let(:params) do
|
||||
{
|
||||
country: 'UA',
|
||||
address: 'Yaroslaviv Val 15',
|
||||
city: 'Kiev',
|
||||
postcode: '101010',
|
||||
upload: [
|
||||
image
|
||||
]
|
||||
}
|
||||
end
|
||||
|
||||
let(:last_document) { Document.last }
|
||||
|
||||
it 'saves 10 address documents successfully' do
|
||||
10.times do
|
||||
post '/api/v2/resource/addresses', headers: auth_header,
|
||||
params: {
|
||||
country: 'UA',
|
||||
address: 'Yaroslaviv Val 15',
|
||||
city: 'Kiev',
|
||||
postcode: '101010',
|
||||
upload: [fixture_file_upload('/files/documents_test.jpg', 'image/jpg')]
|
||||
}
|
||||
end
|
||||
|
||||
expect(response.status).to eq(201)
|
||||
end
|
||||
|
||||
it 'uploads 2 files at once' do
|
||||
post '/api/v2/resource/addresses', headers: auth_header,
|
||||
params: {
|
||||
country: 'UA',
|
||||
address: 'Yaroslaviv Val 15',
|
||||
city: 'Kiev',
|
||||
postcode: '101010',
|
||||
upload: [
|
||||
fixture_file_upload('/files/documents_test.jpg', 'image/jpg'),
|
||||
fixture_file_upload('/files/documents_test.jpg', 'image/jpg')
|
||||
]
|
||||
}
|
||||
expect(response.status).to eq(201)
|
||||
expect(test_user.documents.length).to eq(2)
|
||||
end
|
||||
|
||||
it 'uploads 3 files at once' do
|
||||
post '/api/v2/resource/addresses', headers: auth_header,
|
||||
params: {
|
||||
country: 'UA',
|
||||
address: 'Yaroslaviv Val 15',
|
||||
city: 'Kiev',
|
||||
postcode: '101010',
|
||||
upload: [
|
||||
fixture_file_upload('/files/documents_test.jpg', 'image/jpg'),
|
||||
fixture_file_upload('/files/documents_test.jpg', 'image/jpg'),
|
||||
fixture_file_upload('/files/documents_test.jpg', 'image/jpg')
|
||||
]
|
||||
}
|
||||
expect(response.status).to eq(201)
|
||||
expect(test_user.documents.length).to eq(3)
|
||||
end
|
||||
|
||||
it 'Checks if params are ok and returns success' do
|
||||
post '/api/v2/resource/addresses', headers: auth_header, params: params
|
||||
expect(response.status).to eq(201)
|
||||
end
|
||||
|
||||
it 'Creates a pending address label' do
|
||||
expect(test_user.labels.find_by(key: :address)).to eq(nil)
|
||||
post '/api/v2/resource/addresses', headers: auth_header, params: params
|
||||
expect(response.status).to eq(201)
|
||||
expect(test_user.labels.find_by(key: :address)).not_to eq(nil)
|
||||
expect(test_user.labels.find_by(key: :address).value).to eq('pending')
|
||||
end
|
||||
|
||||
it 'Update rejected or verified label to pending documents label on new doc' do
|
||||
expect(test_user.labels.find_by(key: :address)).to eq(nil)
|
||||
test_user.labels.create(key: :address, value: 'verified')
|
||||
test_user.reload
|
||||
|
||||
post '/api/v2/resource/addresses', headers: auth_header, params: params
|
||||
expect(response.status).to eq(201)
|
||||
expect(test_user.labels.find_by(key: :address)).not_to eq(nil)
|
||||
expect(test_user.labels.find_by(key: :address).value).to eq('pending')
|
||||
end
|
||||
|
||||
it 'triggers KYCService' do
|
||||
expect(KycService).to receive(:address_step)
|
||||
post '/api/v2/resource/addresses', headers: auth_header, params: params
|
||||
expect(response.status).to eq(201)
|
||||
end
|
||||
end
|
||||
|
||||
context 'event API behavior' do
|
||||
let!(:url) { '/api/v2/resource/addresses' }
|
||||
let(:request_params) do
|
||||
{
|
||||
country: 'UA',
|
||||
address: 'Yaroslaviv Val 15',
|
||||
city: 'Kiev',
|
||||
postcode: '101010',
|
||||
upload: [
|
||||
image
|
||||
]
|
||||
}
|
||||
end
|
||||
|
||||
before do
|
||||
allow(EventAPI).to receive(:notify)
|
||||
end
|
||||
|
||||
it 'receive model.document.created notify' do
|
||||
expect(EventAPI).to receive(:notify).ordered.with('model.user.created', hash_including(:record))
|
||||
expect(EventAPI).to receive(:notify).ordered.with('model.document.created', hash_including(:record))
|
||||
|
||||
post url, headers: auth_header, params: request_params
|
||||
end
|
||||
end
|
||||
end
|
||||
263
spec/api/v2/resource/api_keys_spec.rb
Normal file
263
spec/api/v2/resource/api_keys_spec.rb
Normal file
@@ -0,0 +1,263 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
describe 'Api::V2::APIKeys' do
|
||||
include_context 'bearer authentication'
|
||||
let!(:create_member_permission) do
|
||||
create :permission,
|
||||
role: 'member'
|
||||
end
|
||||
let!(:create_admin_permission) do
|
||||
create :permission,
|
||||
role: 'admin'
|
||||
end
|
||||
let!(:test_user) { create(:user, otp: otp_enabled) }
|
||||
let(:otp_enabled) { true }
|
||||
let!(:first_api_key) { create :api_key, key_holder_account: test_user }
|
||||
let!(:second_api_key) { create :api_key, key_holder_account: test_user }
|
||||
let(:valid_otp_code) { '1357' }
|
||||
let(:invalid_otp_code) { '1234' }
|
||||
let(:otp_code) { valid_otp_code }
|
||||
|
||||
before do
|
||||
allow(TOTPService).to receive(:validate?)
|
||||
.with(test_user.uid, valid_otp_code) { true }
|
||||
allow(TOTPService).to receive(:validate?)
|
||||
.with(test_user.uid, invalid_otp_code) { false }
|
||||
allow(TOTPService).to receive(:validate?)
|
||||
.with(test_user.uid, nil) { false }
|
||||
end
|
||||
|
||||
describe 'GET /api/v2/resource/api_keys/' do
|
||||
let(:get_params) do
|
||||
{
|
||||
ordering: 'asc',
|
||||
oreder_by: 'id'
|
||||
}
|
||||
end
|
||||
let(:post_params) do
|
||||
{
|
||||
scope: 'trade',
|
||||
algorithm: 'HS256'
|
||||
}
|
||||
end
|
||||
let(:do_request) do
|
||||
post '/api/v2/resource/api_keys',
|
||||
params: post_params.merge(totp_code: otp_code),
|
||||
headers: auth_header
|
||||
end
|
||||
let(:do_get_request) { get "/api/v2/resource/api_keys", params: get_params, headers: auth_header }
|
||||
let(:expected_fields) do
|
||||
{
|
||||
kid: first_api_key.kid,
|
||||
state: first_api_key.state,
|
||||
scope: %w[trade]
|
||||
}
|
||||
end
|
||||
let(:totp_code) { valid_otp_code }
|
||||
|
||||
it 'Return api key for current account in ASC order' do
|
||||
do_get_request
|
||||
|
||||
expect(json_body.count).to eq 2
|
||||
expect(response.status).to eq(200)
|
||||
expect(json_body.first).to include(expected_fields)
|
||||
end
|
||||
|
||||
it 'Return api key for current account in DESC order' do
|
||||
get_params.merge!(ordering: 'desc')
|
||||
do_get_request
|
||||
|
||||
expect(json_body.count).to eq 2
|
||||
expect(response.status).to eq(200)
|
||||
expect(json_body.second).to include(expected_fields)
|
||||
end
|
||||
|
||||
context 'when invalid ordering' do
|
||||
it 'renders an error' do
|
||||
get_params.merge!(ordering: 'resc')
|
||||
do_get_request
|
||||
|
||||
expect(response.status).to eq(422)
|
||||
expect_body.to eq(errors: ["resource.api_key.invalid_ordering"])
|
||||
end
|
||||
end
|
||||
|
||||
context 'when invalid order_by' do
|
||||
it 'renders an error' do
|
||||
get_params.merge!(order_by: 'invalid')
|
||||
do_get_request
|
||||
|
||||
expect(response.status).to eq(422)
|
||||
expect_body.to eq(errors: ["resource.api_key.invalid_attribute"])
|
||||
end
|
||||
end
|
||||
|
||||
context 'when otp is not enabled' do
|
||||
let(:otp_enabled) { false }
|
||||
|
||||
it 'renders an error' do
|
||||
do_request
|
||||
expect(response.status).to eq(400)
|
||||
expect_body.to eq(errors: ["resource.api_key.2fa_disabled"])
|
||||
end
|
||||
end
|
||||
|
||||
context 'when code is invalid' do
|
||||
let(:otp_code) { invalid_otp_code }
|
||||
|
||||
it 'renders an error' do
|
||||
do_request
|
||||
expect(response.status).to eq(422)
|
||||
expect_body.to eq(errors: ["resource.api_key.invalid_totp"])
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'POST /api/v2/resource/api_keys' do
|
||||
let(:do_request) do
|
||||
post '/api/v2/resource/api_keys',
|
||||
params: params.merge(totp_code: otp_code),
|
||||
headers: auth_header
|
||||
end
|
||||
let(:otp_code) { valid_otp_code }
|
||||
|
||||
context 'when fields are valid' do
|
||||
let(:params) do
|
||||
{
|
||||
scope: 'trade',
|
||||
algorithm: 'HS256'
|
||||
}
|
||||
end
|
||||
let(:expected_fields) do
|
||||
{
|
||||
state: 'active',
|
||||
scope: params[:scope].split(','),
|
||||
algorithm: 'HS256'
|
||||
}
|
||||
end
|
||||
|
||||
it 'Create an api key' do
|
||||
expect { do_request }.to change { APIKey.count }.by(1)
|
||||
expect(response.status).to eq(201)
|
||||
expect_body.to include(expected_fields)
|
||||
end
|
||||
|
||||
it 'does not create api key if vault is down' do
|
||||
allow(Vault::Rails).to receive(:encrypt).and_raise(Vault::VaultError)
|
||||
expect { do_request }.not_to change { APIKey.count }
|
||||
expect(response.status).to eq(422)
|
||||
expect_body.to eq(errors: ["api_key.could_not_save_secret"])
|
||||
end
|
||||
|
||||
context 'when otp is not enabled' do
|
||||
let(:otp_enabled) { false }
|
||||
|
||||
it 'renders an error' do
|
||||
do_request
|
||||
expect(response.status).to eq(400)
|
||||
expect_body.to eq(errors: ["resource.api_key.2fa_disabled"])
|
||||
end
|
||||
end
|
||||
|
||||
context 'when code is invalid' do
|
||||
let(:otp_code) { invalid_otp_code }
|
||||
|
||||
it 'renders an error' do
|
||||
do_request
|
||||
expect(response.status).to eq(422)
|
||||
expect_body.to eq(errors: ["resource.api_key.invalid_totp"])
|
||||
end
|
||||
end
|
||||
|
||||
context 'when algorithm is invalid' do
|
||||
it 'renders an error' do
|
||||
params[:algorithm] = 'kek'
|
||||
do_request
|
||||
expect_body.to eq(errors: ["kid.blank", "algorithm.inclusion"])
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'PATCH /api/v2/resource/api_keys/:kid' do
|
||||
let(:do_request) do
|
||||
patch "/api/v2/resource/api_keys/#{first_api_key.kid}", params: params.merge(totp_code: otp_code),
|
||||
headers: auth_header
|
||||
end
|
||||
let(:otp_code) { valid_otp_code }
|
||||
context 'when valid fields' do
|
||||
let(:params) do
|
||||
{
|
||||
state: 'inactive',
|
||||
scope: 'sell',
|
||||
algorithm: 'HS256'
|
||||
}
|
||||
end
|
||||
|
||||
it 'Updates a state' do
|
||||
expect { do_request }.to change { first_api_key.reload.state }
|
||||
.from('active').to('inactive')
|
||||
expect(response.status).to eq(200)
|
||||
end
|
||||
|
||||
it 'Updates a scope' do
|
||||
expect { do_request }.to change { first_api_key.reload.scope }
|
||||
.from(['trade']).to(['sell'])
|
||||
expect(response.status).to eq(200)
|
||||
end
|
||||
|
||||
context 'when otp is not enabled' do
|
||||
let(:otp_enabled) { false }
|
||||
|
||||
it 'renders an error' do
|
||||
do_request
|
||||
expect(response.status).to eq(400)
|
||||
expect_body.to eq(errors: ["resource.api_key.2fa_disabled"])
|
||||
end
|
||||
end
|
||||
|
||||
context 'when code is invalid' do
|
||||
let(:otp_code) { invalid_otp_code }
|
||||
|
||||
it 'renders an error' do
|
||||
do_request
|
||||
expect(response.status).to eq(422)
|
||||
expect_body.to eq(errors: ["resource.api_key.invalid_totp"])
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'DELETE /api/v2/resource/api_keys/:uid' do
|
||||
let(:do_request) do
|
||||
delete "/api/v2/resource/api_keys/#{first_api_key.kid}?totp_code=#{otp_code}",
|
||||
headers: auth_header
|
||||
end
|
||||
let(:otp_code) { valid_otp_code }
|
||||
|
||||
it 'Removes an api key' do
|
||||
do_request
|
||||
expect(response.status).to eq(204)
|
||||
end
|
||||
|
||||
context 'when otp is not enabled' do
|
||||
let(:otp_enabled) { false }
|
||||
|
||||
it 'renders an error' do
|
||||
do_request
|
||||
expect(response.status).to eq(400)
|
||||
expect_body.to eq(errors: ["resource.api_key.2fa_disabled"])
|
||||
end
|
||||
end
|
||||
|
||||
context 'when code is invalid' do
|
||||
let(:otp_code) { invalid_otp_code }
|
||||
|
||||
it 'renders an error' do
|
||||
do_request
|
||||
expect(response.status).to eq(422)
|
||||
expect_body.to eq(errors: ["resource.api_key.invalid_totp"])
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
58
spec/api/v2/resource/data_storage_spec.rb
Normal file
58
spec/api/v2/resource/data_storage_spec.rb
Normal file
@@ -0,0 +1,58 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
describe 'Api::V2::Resource::DataStorage' do
|
||||
include_context 'bearer authentication'
|
||||
let!(:create_member_permission) do
|
||||
create :permission,
|
||||
role: 'member'
|
||||
end
|
||||
|
||||
describe 'POST /api/v2/resource/data_storage' do
|
||||
let(:url) { '/api/v2/resource/data_storage' }
|
||||
|
||||
context 'successful creation' do
|
||||
let(:valid_params) do
|
||||
{
|
||||
title: 'personal',
|
||||
data: { wife_last_name: Faker::Name.last_name, wife_first_name: Faker::Name.first_name }.to_json
|
||||
}
|
||||
end
|
||||
|
||||
it 'creates data_storage record' do
|
||||
expect { post url, params: valid_params, headers: auth_header }.to change { DataStorage.count }.by(1)
|
||||
expect(response.status).to eq(201)
|
||||
end
|
||||
|
||||
it 'creates a label when recording data_storage' do
|
||||
expect { post url, params: valid_params, headers: auth_header }.to change { Label.count }.by(1)
|
||||
expect(response.status).to eq(201)
|
||||
expect(test_user.labels.find_by(key: valid_params[:title])).not_to be_nil
|
||||
end
|
||||
end
|
||||
|
||||
context 'errors' do
|
||||
let(:non_json_data_params) { { title: 'personal', data: 'My name is John Doe. Hello, World!' } }
|
||||
let(:non_whitelisted_params) { { title: 'family_info', data: { wife_first_name: Faker::Name.first_name }.to_json } }
|
||||
let(:blacklisted_params) { { title: 'document', data: { approved: true }.to_json } }
|
||||
|
||||
it 'doesnt accept non-json data' do
|
||||
expect { post url, params: non_json_data_params, headers: auth_header }.not_to change { DataStorage.count }
|
||||
|
||||
expect(response.status).to eq(422)
|
||||
expect(json_body).to eq({errors: ['data.invalid_format']})
|
||||
end
|
||||
|
||||
it 'doesnt accept non-whitelisted title' do
|
||||
expect { post url, params: non_whitelisted_params, headers: auth_header }.not_to change { DataStorage.count }
|
||||
expect(response.status).to eq(422)
|
||||
expect(json_body).to eq({errors: ['title.inclusion']})
|
||||
end
|
||||
|
||||
it 'doesnt accept blacklisted title' do
|
||||
expect { post url, params: blacklisted_params, headers: auth_header }.not_to change { DataStorage.count }
|
||||
expect(response.status).to eq(422)
|
||||
expect(json_body).to eq({errors: ['title.inclusion', 'title.exclusion']})
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
288
spec/api/v2/resource/documents_spec.rb
Normal file
288
spec/api/v2/resource/documents_spec.rb
Normal file
@@ -0,0 +1,288 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
describe 'Documents API test' do
|
||||
include_context 'bearer authentication'
|
||||
let!(:create_member_permission) do
|
||||
create :permission,
|
||||
role: 'member'
|
||||
end
|
||||
let!(:image) { fixture_file_upload('/files/documents_test.jpg', 'image/jpg') }
|
||||
|
||||
describe 'POST /api/v2/resource/documents/' do
|
||||
let(:params) do
|
||||
{
|
||||
doc_type: 'Passport',
|
||||
doc_expire: '3020-01-22',
|
||||
doc_number: 'AA1234BB',
|
||||
doc_category: 'front_side',
|
||||
upload: [
|
||||
image
|
||||
]
|
||||
}
|
||||
end
|
||||
|
||||
let!(:optional_params) do
|
||||
{
|
||||
metadata: { country: Faker::Address.country }.to_json
|
||||
}
|
||||
end
|
||||
|
||||
let(:last_document) { Document.last }
|
||||
|
||||
it 'saves 10 documents successfully' do
|
||||
10.times do
|
||||
post '/api/v2/resource/documents', headers: auth_header,
|
||||
params: {
|
||||
doc_type: 'Passport',
|
||||
doc_expire: '3020-01-22',
|
||||
doc_number: 'AA1234BB',
|
||||
upload: [fixture_file_upload('/files/documents_test.jpg', 'image/jpg')]
|
||||
}
|
||||
end
|
||||
|
||||
expect(response.status).to eq(201)
|
||||
end
|
||||
|
||||
it 'renders an error when max documents already reached' do
|
||||
11.times do
|
||||
post '/api/v2/resource/documents', headers: auth_header,
|
||||
params: {
|
||||
doc_type: 'Passport',
|
||||
doc_expire: '3020-01-22',
|
||||
doc_number: 'AA1234BB',
|
||||
upload: [fixture_file_upload('/files/documents_test.jpg', 'image/jpg')]
|
||||
}
|
||||
end
|
||||
|
||||
expect(response.status).to eq(400)
|
||||
expect_body.to eq(errors: ['resource.documents.limit_will_be_reached'])
|
||||
end
|
||||
|
||||
it 'uploads 2 files at once' do
|
||||
post '/api/v2/resource/documents', headers: auth_header,
|
||||
params: {
|
||||
doc_type: 'Passport',
|
||||
doc_expire: '3020-01-22',
|
||||
doc_number: 'AA1234BB',
|
||||
upload: [
|
||||
fixture_file_upload('/files/documents_test.jpg', 'image/jpg'),
|
||||
fixture_file_upload('/files/documents_test.jpg', 'image/jpg')
|
||||
]
|
||||
}
|
||||
expect(response.status).to eq(201)
|
||||
expect(test_user.documents.length).to eq(2)
|
||||
end
|
||||
|
||||
it 'uploads 3 files at once' do
|
||||
post '/api/v2/resource/documents', headers: auth_header,
|
||||
params: {
|
||||
doc_type: 'Passport',
|
||||
doc_expire: '3020-01-22',
|
||||
doc_number: 'AA1234BB',
|
||||
upload: [
|
||||
fixture_file_upload('/files/documents_test.jpg', 'image/jpg'),
|
||||
fixture_file_upload('/files/documents_test.jpg', 'image/jpg'),
|
||||
fixture_file_upload('/files/documents_test.jpg', 'image/jpg')
|
||||
]
|
||||
}
|
||||
expect(response.status).to eq(201)
|
||||
expect(test_user.documents.length).to eq(3)
|
||||
end
|
||||
|
||||
it 'doesn\'t upload more than 10 files at once' do
|
||||
post '/api/v2/resource/documents', headers: auth_header,
|
||||
params: {
|
||||
doc_type: 'Passport',
|
||||
doc_expire: '3020-01-22',
|
||||
doc_number: 'AA1234BB',
|
||||
upload: [
|
||||
fixture_file_upload('/files/documents_test.jpg', 'image/jpg'),
|
||||
fixture_file_upload('/files/documents_test.jpg', 'image/jpg'),
|
||||
fixture_file_upload('/files/documents_test.jpg', 'image/jpg'),
|
||||
fixture_file_upload('/files/documents_test.jpg', 'image/jpg'),
|
||||
fixture_file_upload('/files/documents_test.jpg', 'image/jpg'),
|
||||
fixture_file_upload('/files/documents_test.jpg', 'image/jpg'),
|
||||
fixture_file_upload('/files/documents_test.jpg', 'image/jpg'),
|
||||
fixture_file_upload('/files/documents_test.jpg', 'image/jpg'),
|
||||
fixture_file_upload('/files/documents_test.jpg', 'image/jpg'),
|
||||
fixture_file_upload('/files/documents_test.jpg', 'image/jpg'),
|
||||
fixture_file_upload('/files/documents_test.jpg', 'image/jpg')
|
||||
]
|
||||
}
|
||||
expect(response.status).to eq(400)
|
||||
end
|
||||
|
||||
it 'Checks if params are ok and returns success' do
|
||||
post '/api/v2/resource/documents', headers: auth_header, params: params
|
||||
expect(response.status).to eq(201)
|
||||
end
|
||||
|
||||
it 'Creates a pending documents label' do
|
||||
expect(test_user.labels.find_by(key: :document)).to eq(nil)
|
||||
post '/api/v2/resource/documents', headers: auth_header, params: params
|
||||
expect(response.status).to eq(201)
|
||||
expect(test_user.labels.find_by(key: :document)).to eq(nil)
|
||||
post '/api/v2/resource/documents', headers: auth_header,
|
||||
params: params.merge({ doc_category: 'selfie',
|
||||
upload: [fixture_file_upload('/files/documents_test.jpg', 'image/jpg')],
|
||||
identificator: test_user.documents.last.identificator })
|
||||
expect(test_user.labels.find_by(key: :document).value).to eq('pending')
|
||||
end
|
||||
|
||||
it 'Update rejected or verified label to pending documents label on new doc' do
|
||||
expect(test_user.labels.find_by(key: :document)).to eq(nil)
|
||||
test_user.labels.create(key: :document, value: 'verified')
|
||||
test_user.reload
|
||||
|
||||
post '/api/v2/resource/documents', headers: auth_header, params: params
|
||||
expect(response.status).to eq(201)
|
||||
expect(test_user.labels.find_by(key: :document)).not_to eq(nil)
|
||||
expect(test_user.labels.find_by(key: :document).value).to eq('verified')
|
||||
post '/api/v2/resource/documents', headers: auth_header,
|
||||
params: params.merge({ doc_category: 'selfie',
|
||||
upload: [fixture_file_upload('/files/documents_test.jpg', 'image/jpg')],
|
||||
identificator: test_user.documents.last.identificator })
|
||||
expect(test_user.labels.find_by(key: :document).value).to eq('pending')
|
||||
end
|
||||
|
||||
it 'triggers KYCService' do
|
||||
expect(KycService).to receive(:document_step)
|
||||
post '/api/v2/resource/documents', headers: auth_header, params: params
|
||||
expect(response.status).to eq(201)
|
||||
end
|
||||
|
||||
it 'Creates document with optional params and returns success' do
|
||||
post '/api/v2/resource/documents', headers: auth_header, params: params.merge(optional_params)
|
||||
expect(response.status).to eq(201)
|
||||
expect(last_document.metadata).to eq(optional_params[:metadata])
|
||||
end
|
||||
|
||||
it 'renders an error if metadata is not json' do
|
||||
post '/api/v2/resource/documents', headers: auth_header, params: params.merge({ metadata: '{ bar: baz }' })
|
||||
expect_status_to_eq 422
|
||||
expect_body.to eq(errors: ['metadata.invalid_format'])
|
||||
end
|
||||
|
||||
it 'Checks provided params and returns error, cause some of them are not valid or absent' do
|
||||
post '/api/v2/resource/documents', params: params.except(:doc_type), headers: auth_header
|
||||
expect_body.to eq(errors: ['resource.document.missing_doc_type', 'resource.document.empty_doc_type'])
|
||||
expect(response.status).to eq(422)
|
||||
|
||||
post '/api/v2/resource/documents', params: params.except(:doc_number), headers: auth_header
|
||||
expect_body.to eq(errors: ['resource.document.missing_doc_number', 'resource.document.empty_doc_number'])
|
||||
expect(response.status).to eq(422)
|
||||
|
||||
post '/api/v2/resource/documents', params: params.except(:upload), headers: auth_header
|
||||
expect_body.to eq(errors: ['resource.document.missing_upload'])
|
||||
expect(response.status).to eq(422)
|
||||
|
||||
post '/api/v2/resource/documents', params: params.except(:doc_expire).merge(doc_expire: 'blah'), headers: auth_header
|
||||
expect_body.to eq(errors: ['resource.documents.expire_not_a_date'])
|
||||
expect(response.status).to eq(422)
|
||||
|
||||
params0 = params
|
||||
params0[:upload] = [Faker::Avatar.image]
|
||||
post '/api/v2/resource/documents', params: params0, headers: auth_header
|
||||
expect_body.to eq(errors: ['upload.blank'])
|
||||
expect(response.status).to eq(422)
|
||||
end
|
||||
|
||||
it 'Does not return error when docs expire is optional' do
|
||||
allow(Barong::App.config).to receive(:required_docs_expire).and_return(false)
|
||||
post '/api/v2/resource/documents', params: params.except(:doc_expire), headers: auth_header
|
||||
expect(response.status).to eq(201)
|
||||
end
|
||||
|
||||
it 'Returns error when docs expire is not optional' do
|
||||
allow(Barong::App.config).to receive(:required_docs_expire).and_return(true)
|
||||
post '/api/v2/resource/documents', params: params.except(:doc_expire), headers: auth_header
|
||||
expect(response.status).to eq(422)
|
||||
expect_body.to eq({ errors: ['resource.documents.invalid_format'] })
|
||||
end
|
||||
|
||||
it 'Returns error when docs expire is not optional and date in past' do
|
||||
allow(Barong::App.config).to receive(:required_docs_expire).and_return(true)
|
||||
post '/api/v2/resource/documents', params: params.merge({ doc_expire: DateTime.now.to_date - 1 }), headers: auth_header
|
||||
expect(response.status).to eq(422)
|
||||
expect_body.to eq({ errors: ['resource.documents.already_expired'] })
|
||||
end
|
||||
|
||||
it 'Doesnt return error when docs expire is optional and date in past' do
|
||||
allow(Barong::App.config).to receive(:required_docs_expire).and_return(false)
|
||||
post '/api/v2/resource/documents', params: params.merge({ doc_expire: DateTime.now.to_date - 1 }), headers: auth_header
|
||||
expect(response.status).to eq(201)
|
||||
end
|
||||
|
||||
it 'Returns user all his documents' do
|
||||
post '/api/v2/resource/documents', params: params, headers: auth_header
|
||||
expect(response.status).to eq(201)
|
||||
|
||||
get '/api/v2/resource/documents', headers: auth_header
|
||||
response_arr = JSON.parse(response.body)
|
||||
|
||||
expect(response_arr.count).to eq(1)
|
||||
expect(response_arr.last['upload']).to_not be_nil
|
||||
expect(response_arr.last['doc_type']).to eq('Passport')
|
||||
expect(response_arr.last['doc_expire']).to eq('3020-01-22')
|
||||
expect(response_arr.last['doc_number']).to eq('AA****BB')
|
||||
expect(response.status).to eq(200)
|
||||
end
|
||||
|
||||
context 'without masking' do
|
||||
before do
|
||||
Barong::App.config.stub(:api_data_masking_enabled).and_return(false)
|
||||
end
|
||||
|
||||
it 'Returns user all his documents' do
|
||||
post '/api/v2/resource/documents', params: params, headers: auth_header
|
||||
expect(response.status).to eq(201)
|
||||
|
||||
get '/api/v2/resource/documents', headers: auth_header
|
||||
response_arr = JSON.parse(response.body)
|
||||
|
||||
expect(response_arr.count).to eq(1)
|
||||
expect(response_arr.last['upload']).to_not be_nil
|
||||
expect(response_arr.last['doc_type']).to eq('Passport')
|
||||
expect(response_arr.last['doc_expire']).to eq('3020-01-22')
|
||||
expect(response_arr.last['doc_number']).to eq('AA1234BB')
|
||||
expect(response.status).to eq(200)
|
||||
end
|
||||
end
|
||||
|
||||
it 'Returns error without token' do
|
||||
post '/api/v2/resource/documents', params: params
|
||||
expect(response.status).to eq(401)
|
||||
|
||||
get '/api/v2/resource/documents', params: params
|
||||
expect(response.status).to eq(401)
|
||||
end
|
||||
after(:all) { User.destroy_all }
|
||||
end
|
||||
|
||||
context 'event API behavior' do
|
||||
let!(:url) { '/api/v2/resource/documents' }
|
||||
let(:request_params) do
|
||||
{
|
||||
doc_type: 'Passport',
|
||||
doc_expire: '3020-01-22',
|
||||
doc_number: 'AA1234BB',
|
||||
upload: [
|
||||
image
|
||||
]
|
||||
}
|
||||
end
|
||||
|
||||
before do
|
||||
allow(EventAPI).to receive(:notify)
|
||||
end
|
||||
|
||||
it 'receive model.document.created notify' do
|
||||
expect(EventAPI).to receive(:notify).ordered.with('model.user.created', hash_including(:record))
|
||||
expect(EventAPI).to receive(:notify).ordered.with('model.document.created', hash_including(:record))
|
||||
|
||||
post url, headers: auth_header, params: request_params
|
||||
end
|
||||
end
|
||||
end
|
||||
88
spec/api/v2/resource/labels_spec.rb
Normal file
88
spec/api/v2/resource/labels_spec.rb
Normal file
@@ -0,0 +1,88 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
describe 'Labels API.' do
|
||||
include_context 'bearer authentication'
|
||||
let!(:create_member_permission) do
|
||||
create :permission,
|
||||
role: 'member'
|
||||
end
|
||||
let!(:create_admin_permission) do
|
||||
create :permission,
|
||||
role: 'admin'
|
||||
end
|
||||
let(:post_params) do
|
||||
{
|
||||
key: ::Faker::Internet.slug(glue: '-'),
|
||||
value: ::Faker::Internet.slug(glue: '-')
|
||||
}
|
||||
end
|
||||
|
||||
context 'Happy paths for the current user.' do
|
||||
let(:test_user) { create(:user, role: "admin") }
|
||||
let!(:label) { create :label, user: test_user }
|
||||
let!(:label2) { create :label, user: test_user }
|
||||
let!(:label3) { create :label, user: test_user }
|
||||
|
||||
it 'Return lables for current user' do
|
||||
get '/api/v2/resource/labels', headers: auth_header
|
||||
|
||||
expect(response.status).to eq(200)
|
||||
expect(json_body.size).to eq(3)
|
||||
expect(json_body.last[:key]).to eq(label3.key)
|
||||
end
|
||||
|
||||
it 'Return a label by key' do
|
||||
get "/api/v2/resource/labels/#{label.key}", headers: auth_header
|
||||
expect(response.status).to eq(200)
|
||||
expect(json_body[:key]).to eq(label.key)
|
||||
end
|
||||
|
||||
it 'Create a label' do
|
||||
post '/api/v2/resource/labels', params: post_params, headers: auth_header
|
||||
expect(response.status).to eq(201)
|
||||
persisted = Label.find_by(key: json_body[:key])
|
||||
expect(persisted.key).to eq(post_params[:key])
|
||||
expect(persisted.value).to eq(post_params[:value])
|
||||
expect(persisted.scope).to eq('public')
|
||||
expect(persisted.user_id).to eq(test_user.id)
|
||||
end
|
||||
|
||||
it 'Update a label' do
|
||||
patch "/api/v2/resource/labels/#{label.key}", params: post_params, headers: auth_header
|
||||
expect(response.status).to eq(200)
|
||||
persisted = Label.find_by(key: label.key)
|
||||
expect(persisted.value).to eq(post_params[:value])
|
||||
expect(persisted.scope).to eq('public')
|
||||
end
|
||||
|
||||
it 'Delete a label' do
|
||||
delete "/api/v2/resource/labels/#{label.key}", headers: auth_header
|
||||
expect(response.status).to eq(204)
|
||||
expect(Label.find_by(key: label.key)).to be_nil
|
||||
end
|
||||
end
|
||||
|
||||
context 'Errors.' do
|
||||
let(:test_user) { create(:user, role: "admin") }
|
||||
let!(:label) { create :label, user: test_user }
|
||||
|
||||
it 'Respond with errors on create if existing key is used for current user' do
|
||||
post '/api/v2/resource/labels', params: post_params.merge(key: label.key), headers: auth_header
|
||||
expect(response.status).to eq(422)
|
||||
expect(response.body).to be_include('key.taken')
|
||||
end
|
||||
|
||||
it 'Respond with error if attempted to update a private label' do
|
||||
label.update(scope: 'private')
|
||||
patch "/api/v2/resource/labels/#{label.key}", params: post_params, headers: auth_header
|
||||
expect(response.status).to eq(400)
|
||||
end
|
||||
|
||||
it 'Respond with error if key not found' do
|
||||
get '/api/v2/resource/labels/blah', headers: auth_header
|
||||
expect(response.status).to eq(404)
|
||||
end
|
||||
end
|
||||
end
|
||||
129
spec/api/v2/resource/otp_spec.rb
Normal file
129
spec/api/v2/resource/otp_spec.rb
Normal file
@@ -0,0 +1,129 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
describe 'Api::V2::Resource::Otp' do
|
||||
include_context 'bearer authentication'
|
||||
let!(:create_member_permission) do
|
||||
create :permission,
|
||||
role: 'member'
|
||||
end
|
||||
let(:do_request) { post '/api/v2/resource/otp/enable', headers: auth_header }
|
||||
let(:otp_code) { '111111' }
|
||||
|
||||
context 'valid request' do
|
||||
before do
|
||||
allow(TOTPService).to receive(:validate?)
|
||||
.with(test_user.uid, otp_code) { true }
|
||||
end
|
||||
|
||||
it 'user enables 2fa successfully' do
|
||||
post '/api/v2/resource/otp/enable', headers: auth_header, params: {
|
||||
code: otp_code
|
||||
}
|
||||
|
||||
expect(response.status).to eq 201
|
||||
expect(test_user.reload.otp).to eq true
|
||||
expect(test_user.reload.labels.find_by(key: :otp, scope: :private)).not_to eq nil
|
||||
end
|
||||
|
||||
it 'user disables 2fa successfully' do
|
||||
test_user.update(otp: true)
|
||||
test_user.labels.create(value: 'verified', key: :otp, scope: :private)
|
||||
post '/api/v2/resource/otp/disable', headers: auth_header, params: {
|
||||
code: otp_code
|
||||
}
|
||||
|
||||
expect(response.status).to eq 200
|
||||
expect(test_user.reload.otp).to eq false
|
||||
expect(test_user.reload.labels.find_by(key: :otp, scope: :private)).to eq nil
|
||||
end
|
||||
|
||||
it 'user verify otp Code' do
|
||||
|
||||
test_user.update(otp: true)
|
||||
test_user.labels.create(value: 'verified', key: :otp, scope: :private)
|
||||
|
||||
post '/api/v2/resource/otp/verify', headers: auth_header, params: {
|
||||
code: otp_code
|
||||
}
|
||||
|
||||
expect(response.status).to eq 201
|
||||
expect(test_user.reload.otp).to eq true
|
||||
expect(test_user.reload.labels.find_by(key: :otp, scope: :private)).not_to eq nil
|
||||
end
|
||||
end
|
||||
|
||||
context 'incomplete request' do
|
||||
it 'user receives error in enabling' do
|
||||
post '/api/v2/resource/otp/enable', headers: auth_header, params: {
|
||||
code: otp_code
|
||||
}
|
||||
|
||||
expect(response.status).to eq 422
|
||||
expect(test_user.reload.otp).to eq false
|
||||
expect(test_user.reload.labels.find_by(key: :otp, scope: :private)).to eq nil
|
||||
end
|
||||
|
||||
it 'user receives error in disabling' do
|
||||
test_user.update(otp: true)
|
||||
test_user.labels.create(key: :otp, scope: :private)
|
||||
post '/api/v2/resource/otp/disable', headers: auth_header, params: {
|
||||
code: otp_code
|
||||
}
|
||||
|
||||
expect(response.status).to eq 422
|
||||
expect(test_user.reload.otp).to eq true
|
||||
expect(test_user.reload.labels.find_by(key: :otp, scope: :private)).to eq nil
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
|
||||
context 'already done' do
|
||||
before do
|
||||
allow(TOTPService).to receive(:validate?)
|
||||
.with(test_user.uid, otp_code) { true }
|
||||
end
|
||||
|
||||
it 'enabling otp' do
|
||||
test_user.update(otp: true)
|
||||
test_user.labels.create(value: 'verified', key: :otp, scope: :private)
|
||||
post '/api/v2/resource/otp/enable', headers: auth_header, params: {
|
||||
code: otp_code
|
||||
}
|
||||
|
||||
expect(response.status).to eq 400
|
||||
expect(test_user.reload.otp).to eq true
|
||||
expect(test_user.reload.labels.find_by(key: :otp, scope: :private)).not_to eq nil
|
||||
end
|
||||
|
||||
it 'disabling otp' do
|
||||
post '/api/v2/resource/otp/disable', headers: auth_header, params: {
|
||||
code: otp_code
|
||||
}
|
||||
|
||||
expect(response.status).to eq 400
|
||||
expect(test_user.reload.otp).to eq false
|
||||
expect(test_user.reload.labels.find_by(key: :otp, scope: :private)).to eq nil
|
||||
end
|
||||
|
||||
it 'otp disabled' do
|
||||
post '/api/v2/resource/otp/verify', headers: auth_header, params: {
|
||||
code: otp_code
|
||||
}
|
||||
|
||||
expect(response.status).to eq 400
|
||||
expect(test_user.reload.otp).to eq false
|
||||
expect(test_user.reload.labels.find_by(key: :otp, scope: :private)).to eq nil
|
||||
end
|
||||
|
||||
it 'otp enabled' do
|
||||
test_user.update(otp: true)
|
||||
test_user.labels.create(value: 'verified', key: :otp, scope: :private)
|
||||
post '/api/v2/resource/otp/generate_qrcode', headers: auth_header
|
||||
|
||||
expect(response.status).to eq 400
|
||||
expect(test_user.reload.otp).to eq true
|
||||
expect(test_user.reload.labels.find_by(key: :otp, scope: :private)).not_to eq nil
|
||||
end
|
||||
end
|
||||
end
|
||||
469
spec/api/v2/resource/phones_spec.rb
Normal file
469
spec/api/v2/resource/phones_spec.rb
Normal file
@@ -0,0 +1,469 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
describe 'Api::V2::Resources::Phones' do
|
||||
include_context 'bearer authentication'
|
||||
let!(:create_member_permission) do
|
||||
create :permission,
|
||||
role: 'member'
|
||||
end
|
||||
|
||||
context 'Twilio Verify Service' do
|
||||
before do
|
||||
allow(Barong::App.config).to receive(:twilio_provider).and_return(TwilioVerifyService)
|
||||
end
|
||||
|
||||
describe 'GET /api/v2/resource/phones' do
|
||||
let!(:phone1) { create(:phone, user: test_user) }
|
||||
let!(:phone2) { create(:phone, user: test_user, number: 12345677711 ) }
|
||||
let(:do_request) do
|
||||
get '/api/v2/resource/phones', headers: auth_header
|
||||
end
|
||||
|
||||
it 'returns list of user\'s phones' do
|
||||
do_request
|
||||
|
||||
expect(json_body.length).to eq 2
|
||||
|
||||
expect(json_body.first[:number]).to eq phone1.sub_masked_number
|
||||
expect(json_body.first[:country]).to eq phone1.country
|
||||
expect(json_body.first[:validated_at]).to eq phone1.validated_at
|
||||
expect(json_body.second[:number]).to eq phone2.sub_masked_number
|
||||
expect(json_body.second[:country]).to eq phone2.country
|
||||
expect(json_body.second[:validated_at]).to eq phone2.validated_at
|
||||
end
|
||||
|
||||
context 'list of users phone without masking' do
|
||||
before do
|
||||
Barong::App.config.stub(:api_data_masking_enabled).and_return(false)
|
||||
end
|
||||
|
||||
it 'returns list of user\'s phones' do
|
||||
do_request
|
||||
|
||||
expect(json_body.length).to eq 2
|
||||
|
||||
expect(json_body.first[:number]).to eq phone1.number
|
||||
expect(json_body.first[:country]).to eq phone1.country
|
||||
expect(json_body.first[:validated_at]).to eq phone1.validated_at
|
||||
expect(json_body.second[:number]).to eq phone2.number
|
||||
expect(json_body.second[:country]).to eq phone2.country
|
||||
expect(json_body.second[:validated_at]).to eq phone2.validated_at
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'POST /api/v2/resource/phones' do
|
||||
let(:do_request) do
|
||||
post '/api/v2/resource/phones', params: params, headers: auth_header
|
||||
end
|
||||
let(:phone_number) { nil }
|
||||
|
||||
let(:params) { { phone_number: phone_number, channel: 'sms' } }
|
||||
|
||||
describe 'errors: ' do
|
||||
context 'when phone is missing' do
|
||||
let(:phone_number) { nil }
|
||||
|
||||
it 'renders an error' do
|
||||
do_request
|
||||
expect_body.to eq(errors: ["resource.phone.empty_phone_number"])
|
||||
expect_status.to eq 422
|
||||
end
|
||||
end
|
||||
|
||||
context 'when phone is invalid' do
|
||||
let(:phone_number) { '123' }
|
||||
|
||||
it 'renders an error' do
|
||||
do_request
|
||||
expect_body.to eq(errors: ["resource.phone.invalid_num"])
|
||||
expect_status.to eq 400
|
||||
end
|
||||
end
|
||||
|
||||
context 'when phone is already exists' do
|
||||
let!(:phone) do
|
||||
create(:phone, validated_at: validated_at)
|
||||
end
|
||||
let(:phone_number) { phone.number }
|
||||
|
||||
context 'when phone verified' do
|
||||
let(:validated_at) { 1.minutes.ago }
|
||||
|
||||
it 'renders an error' do
|
||||
do_request
|
||||
expect_body.to eq(errors: ["resource.phone.number_exist"])
|
||||
expect_status.to eq 400
|
||||
end
|
||||
end
|
||||
|
||||
context 'when phone is not verified' do
|
||||
let(:validated_at) { 1.minutes.ago }
|
||||
|
||||
it 'renders an error' do
|
||||
do_request
|
||||
expect_body.to eq(errors: ["resource.phone.number_exist"])
|
||||
expect_status.to eq 400
|
||||
end
|
||||
end
|
||||
|
||||
context 'when phone verified but number is not sanitized' do
|
||||
let(:validated_at) { 1.minutes.ago }
|
||||
let(:phone_number) { "++#{phone.number}" }
|
||||
|
||||
it 'renders an error' do
|
||||
do_request
|
||||
expect_body.to eq(errors: ["resource.phone.number_exist"])
|
||||
expect_status.to eq 400
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'valid story' do
|
||||
before do
|
||||
allow(Barong::App.config.twilio_provider).to receive(:send_code).and_return(false)
|
||||
end
|
||||
|
||||
context 'when phone is not verified' do
|
||||
let(:phone_number) { build(:phone).number }
|
||||
|
||||
it 'assigns a phone to account and send sms' do
|
||||
do_request
|
||||
expect_body.to eq(message: 'Code was sent successfully via sms')
|
||||
expect_status.to eq 201
|
||||
end
|
||||
end
|
||||
|
||||
context 'when phone is valid' do
|
||||
let(:phone_number) { build(:phone).number }
|
||||
|
||||
it 'creates a phone and send code via sms channel' do
|
||||
do_request
|
||||
expect_body.to eq(message: 'Code was sent successfully via sms')
|
||||
expect_status.to eq 201
|
||||
end
|
||||
|
||||
it 'creates a phone and send code via call channel' do
|
||||
params['channel'] = 'call'
|
||||
|
||||
do_request
|
||||
expect_body.to eq(message: 'Code was sent successfully via call')
|
||||
expect_status.to eq 201
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when phone is on national format with zero' do
|
||||
let(:phone_number) { '+44 07418084106' }
|
||||
let(:international_phone) { '447418084106' }
|
||||
|
||||
it 'creates a phone and send sms' do
|
||||
allow(Barong::App.config.twilio_provider).to receive(:send_code).and_return(false)
|
||||
|
||||
do_request
|
||||
expect_body.to eq(message: 'Code was sent successfully via sms')
|
||||
expect_status.to eq 201
|
||||
expect(Phone.last.number).to eq(international_phone)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when phone exists on international format' do
|
||||
let(:phone_number) { '+44 07418084106' }
|
||||
let(:international_phone) { '447418084106' }
|
||||
let!(:phone) do
|
||||
create(:phone, validated_at: 1.minute.ago, number: international_phone)
|
||||
end
|
||||
|
||||
it 'renders an error' do
|
||||
do_request
|
||||
expect_body.to eq errors: ["resource.phone.number_exist"]
|
||||
expect_status.to eq 400
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'POST /api/v2/resource/phones/verify' do
|
||||
let(:do_request) do
|
||||
post '/api/v2/resource/phones/verify', params: params, headers: auth_header
|
||||
end
|
||||
let(:params) do
|
||||
{
|
||||
phone_number: phone_number,
|
||||
verification_code: verification_code
|
||||
}
|
||||
end
|
||||
let(:verification_code) { '12345' }
|
||||
|
||||
context 'when phone is missing and code is missing' do
|
||||
let(:phone_number) { nil }
|
||||
let(:verification_code) { '' }
|
||||
|
||||
it 'renders an error' do
|
||||
do_request
|
||||
expect_body.to eq(errors: ["resource.phone.empty_phone_number", "resource.phone.empty_verification_code"])
|
||||
expect_status.to eq 422
|
||||
end
|
||||
end
|
||||
|
||||
context 'when phone is invalid' do
|
||||
let(:phone_number) { '123' }
|
||||
|
||||
it 'renders an error' do
|
||||
do_request
|
||||
expect_body.to eq(errors: ["resource.phone.invalid_num"])
|
||||
expect_status.to eq 400
|
||||
end
|
||||
end
|
||||
|
||||
context 'when phone is already exists and verified' do
|
||||
let!(:phone) do
|
||||
create(:phone, validated_at: 1.minutes.ago)
|
||||
end
|
||||
let(:phone_number) { phone.number }
|
||||
|
||||
it 'renders an error' do
|
||||
do_request
|
||||
expect_body.to eq(errors: ["resource.phone.number_exist"])
|
||||
expect_status.to eq 400
|
||||
end
|
||||
end
|
||||
|
||||
context 'when phone exists in international format' do
|
||||
let!(:phone) do
|
||||
create(:phone, validated_at: 1.minutes.ago, number: international_phone)
|
||||
end
|
||||
let(:phone_number) { '+44 07418084106' }
|
||||
let(:international_phone) { '447418084106' }
|
||||
|
||||
it 'renders an error' do
|
||||
do_request
|
||||
expect_body.to eq(errors: ["resource.phone.number_exist"])
|
||||
expect_status.to eq 400
|
||||
end
|
||||
end
|
||||
|
||||
context 'when phone is not found in test_user' do
|
||||
let!(:phone) { create(:phone) }
|
||||
let(:phone_number) { phone.number }
|
||||
|
||||
it 'rendens an error' do
|
||||
allow(Barong::App.config.twilio_provider).to receive(:verify_code?).and_return(false)
|
||||
|
||||
do_request
|
||||
expect_body.to eq(errors: ["resource.phone.doesnt_exist"])
|
||||
expect_status.to eq 404
|
||||
end
|
||||
end
|
||||
|
||||
context 'when phone is found in test_user but code is invalid' do
|
||||
let!(:phone) { create(:phone, user: test_user) }
|
||||
let(:phone_number) { phone.number }
|
||||
|
||||
it 'rendens an error' do
|
||||
allow(Barong::App.config.twilio_provider).to receive(:verify_code?).and_return(false)
|
||||
|
||||
do_request
|
||||
expect_body.to eq(errors: ["resource.phone.verification_invalid"])
|
||||
expect_status.to eq 404
|
||||
end
|
||||
end
|
||||
|
||||
context 'when phone and code is valid' do
|
||||
let(:phone) { create(:phone, user: test_user) }
|
||||
let(:phone_number) { phone.number }
|
||||
|
||||
it 'responses with success' do
|
||||
allow(Barong::App.config.twilio_provider).to receive(:verify_code?).and_return(true)
|
||||
|
||||
set_level(test_user, 1)
|
||||
do_request
|
||||
expect_status.to eq 201
|
||||
expect(phone.reload.validated_at).to be
|
||||
test_user.update_level
|
||||
expect(test_user.reload.level).to eq 2
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'With Twilio SMS Sender service' do
|
||||
let(:phone_number) { phone.number }
|
||||
let(:do_request) do
|
||||
post '/api/v2/resource/phones', params: params, headers: auth_header
|
||||
end
|
||||
let(:params) { { phone_number: phone_number, channel: 'sms' } }
|
||||
let(:verification_code) { '12345' }
|
||||
|
||||
context 'valid story sms sender service' do
|
||||
let(:mock_sms) { Barong::MockSMS }
|
||||
|
||||
before do
|
||||
allow(Barong::App.config).to receive(:twilio_provider).and_return(MockPhoneVerifyService)
|
||||
end
|
||||
|
||||
context 'when phone is not verified' do
|
||||
let(:phone_number) { build(:phone).number }
|
||||
|
||||
it 'assigns a phone to account and send sms' do
|
||||
do_request
|
||||
expect_body.to eq(message: 'Code was sent successfully via sms')
|
||||
expect_status.to eq 201
|
||||
expect(mock_sms.messages.last.to).to eq "+#{phone_number}"
|
||||
end
|
||||
|
||||
it 'sends a default sms content' do
|
||||
do_request
|
||||
expect_body.to eq(message: 'Code was sent successfully via sms')
|
||||
expect_status.to eq 201
|
||||
expect(mock_sms.messages.last.body).to start_with('Your verification code for Barong: ')
|
||||
end
|
||||
|
||||
it 'sends a custom message with content before code' do
|
||||
allow(Barong::App.config).to receive(:sms_content_template).and_return('Please confirm your phone with the following code: {{code}}')
|
||||
do_request
|
||||
expect_body.to eq(message: 'Code was sent successfully via sms')
|
||||
expect_status.to eq 201
|
||||
expect(mock_sms.messages.last.body).to start_with('Please confirm your phone with the following code: ')
|
||||
end
|
||||
|
||||
it 'sends a custom message with content after code' do
|
||||
allow(Barong::App.config).to receive(:sms_content_template).and_return('{{code}} - this is your confirmation code')
|
||||
do_request
|
||||
expect_body.to eq(message: 'Code was sent successfully via sms')
|
||||
expect_status.to eq 201
|
||||
expect(mock_sms.messages.last.body).to end_with(' - this is your confirmation code')
|
||||
end
|
||||
|
||||
it 'sends a custom message with code in the midle of the content' do
|
||||
allow(Barong::App.config).to receive(:sms_content_template).and_return('Following code: {{code}} should be used for phone confirmation')
|
||||
do_request
|
||||
expect_body.to eq(message: 'Code was sent successfully via sms')
|
||||
expect_status.to eq 201
|
||||
expect(mock_sms.messages.last.body).to start_with('Following code: ')
|
||||
expect(mock_sms.messages.last.body).to end_with(' should be used for phone confirmation')
|
||||
end
|
||||
end
|
||||
|
||||
context 'when phone is valid' do
|
||||
let(:phone_number) { build(:phone).number }
|
||||
|
||||
it 'creates a phone and send sms' do
|
||||
do_request
|
||||
expect_body.to eq(message: 'Code was sent successfully via sms')
|
||||
expect_status.to eq 201
|
||||
expect(mock_sms.messages.last.to).to eq "+#{phone_number}"
|
||||
end
|
||||
|
||||
it 'doesnt change code in DB on phone initialize' do
|
||||
do_request
|
||||
expect_body.to eq(message: 'Code was sent successfully via sms')
|
||||
expect_status.to eq 201
|
||||
code_after_create = Phone.last.code
|
||||
# Phone.last initilazes phone
|
||||
code_after_initialize = Phone.last.code
|
||||
expect(code_after_create).to eq code_after_initialize
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'POST /api/v2/resource/phones/verify' do
|
||||
let(:do_request) do
|
||||
post '/api/v2/resource/phones/verify', params: params, headers: auth_header
|
||||
end
|
||||
let(:params) do
|
||||
{
|
||||
phone_number: phone_number,
|
||||
verification_code: verification_code
|
||||
}
|
||||
end
|
||||
let(:verification_code) { '12345' }
|
||||
|
||||
context 'when phone is missing and code is missing' do
|
||||
let(:phone_number) { nil }
|
||||
let(:verification_code) { '' }
|
||||
|
||||
it 'renders an error' do
|
||||
do_request
|
||||
expect_body.to eq(errors: ['resource.phone.empty_phone_number', 'resource.phone.empty_verification_code'])
|
||||
expect_status.to eq 422
|
||||
end
|
||||
end
|
||||
|
||||
context 'when phone is invalid' do
|
||||
let(:phone_number) { '123' }
|
||||
|
||||
it 'renders an error' do
|
||||
do_request
|
||||
expect_body.to eq(errors: ['resource.phone.invalid_num'])
|
||||
expect_status.to eq 400
|
||||
end
|
||||
end
|
||||
|
||||
context 'when phone is already exists and verified' do
|
||||
let!(:phone) do
|
||||
create(:phone, validated_at: 1.minutes.ago)
|
||||
end
|
||||
let(:phone_number) { phone.number }
|
||||
|
||||
it 'renders an error' do
|
||||
do_request
|
||||
expect_body.to eq(errors: ['resource.phone.number_exist'])
|
||||
expect_status.to eq 400
|
||||
end
|
||||
end
|
||||
|
||||
context 'when phone exists in international format' do
|
||||
let!(:phone) do
|
||||
create(:phone, validated_at: 1.minutes.ago, number: international_phone)
|
||||
end
|
||||
let(:phone_number) { '+44 07418084106' }
|
||||
let(:international_phone) { '447418084106' }
|
||||
|
||||
it 'renders an error' do
|
||||
do_request
|
||||
expect_body.to eq(errors: ['resource.phone.number_exist'])
|
||||
expect_status.to eq 400
|
||||
end
|
||||
end
|
||||
|
||||
context 'when phone is not found in test_user' do
|
||||
let!(:phone) { create(:phone) }
|
||||
let(:phone_number) { phone.number }
|
||||
|
||||
it 'rendens an error' do
|
||||
do_request
|
||||
expect_body.to eq(errors: ['resource.phone.doesnt_exist'])
|
||||
expect_status.to eq 404
|
||||
end
|
||||
end
|
||||
|
||||
context 'when phone is found in test_user but code is invalid still approves because of mock provider' do
|
||||
let!(:phone) { create(:phone, user: test_user) }
|
||||
let(:phone_number) { phone.number }
|
||||
|
||||
it 'approves phone' do
|
||||
do_request
|
||||
|
||||
expect_status.to eq 201
|
||||
expect(phone.reload.validated_at).to be
|
||||
end
|
||||
end
|
||||
|
||||
context 'when phone and code is valid' do
|
||||
let(:phone) { create(:phone, user: test_user) }
|
||||
let(:phone_number) { phone.number }
|
||||
let(:verification_code) { phone.code }
|
||||
|
||||
it 'responses with success' do
|
||||
set_level(test_user, 1)
|
||||
do_request
|
||||
expect_status.to eq 201
|
||||
expect(phone.reload.validated_at).to be
|
||||
test_user.update_level
|
||||
expect(test_user.reload.level).to eq 2
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
572
spec/api/v2/resource/profiles_spec.rb
Normal file
572
spec/api/v2/resource/profiles_spec.rb
Normal file
@@ -0,0 +1,572 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
describe 'API::V2::Resource::Profiles' do
|
||||
before { allow(Barong::App.config).to receive_messages(kyc_provider: 'local') }
|
||||
|
||||
include_context 'bearer authentication'
|
||||
let!(:create_member_permission) do
|
||||
create :permission,
|
||||
role: 'member'
|
||||
end
|
||||
let!(:optional_params) do
|
||||
{
|
||||
metadata: {
|
||||
gender: Faker::Creature::Dog.gender,
|
||||
place_of_birth: Faker::Address.city
|
||||
}.to_json
|
||||
}
|
||||
end
|
||||
|
||||
describe 'POST /api/v2/resource/profiles' do
|
||||
describe 'KYC verification' do
|
||||
let!(:request_params) do
|
||||
{
|
||||
last_name: Faker::Name.last_name,
|
||||
first_name: Faker::Name.first_name,
|
||||
dob: Faker::Date.birthday,
|
||||
country: Faker::Address.country_code,
|
||||
city: Faker::Address.city,
|
||||
address: Faker::Address.state,
|
||||
postcode: Faker::Address.zip_code
|
||||
}
|
||||
end
|
||||
let!(:url) { '/api/v2/resource/profiles' }
|
||||
|
||||
it 'triggers KycService' do
|
||||
expect(KycService).to receive(:profile_step)
|
||||
|
||||
post url, params: request_params, headers: auth_header
|
||||
expect(response.status).to eq(201)
|
||||
end
|
||||
|
||||
context 'Local verification' do
|
||||
before { allow(Barong::App.config).to receive_messages(kyc_provider: 'local') }
|
||||
|
||||
it 'adds a label for first verification' do
|
||||
expect(test_user.labels).to eq([])
|
||||
post url, params: request_params, headers: auth_header
|
||||
expect(test_user.reload.labels.count).to eq(1)
|
||||
expect(test_user.reload.labels.first.key).to eq('profile')
|
||||
expect(test_user.reload.labels.first.value).to eq('drafted')
|
||||
end
|
||||
|
||||
it 'doesnt trigger KYCAID worker' do
|
||||
expect(KycService).to receive(:profile_step)
|
||||
expect(KYC::Kycaid::ApplicantWorker).not_to receive(:perform_async)
|
||||
|
||||
post url, params: request_params, headers: auth_header
|
||||
expect(response.status).to eq(201)
|
||||
end
|
||||
|
||||
it 'updates label on re-submit' do
|
||||
expect(test_user.labels).to eq([])
|
||||
post url, params: request_params, headers: auth_header
|
||||
expect(test_user.reload.labels.count).to eq(1)
|
||||
expect(test_user.reload.labels.first.key).to eq('profile')
|
||||
expect(test_user.reload.labels.first.value).to eq('drafted')
|
||||
|
||||
put url, params: request_params.merge(confirm: true), headers: auth_header
|
||||
expect(test_user.reload.labels.count).to eq(1)
|
||||
expect(test_user.reload.labels.first.key).to eq('profile')
|
||||
expect(test_user.reload.labels.first.value).to eq('submitted')
|
||||
end
|
||||
end
|
||||
|
||||
context 'KYCAID verification' do
|
||||
before { allow(Barong::App.config).to receive_messages(kyc_provider: 'kycaid') }
|
||||
|
||||
it 'adds a label for first verification' do
|
||||
expect(test_user.labels).to eq([])
|
||||
post url, params: request_params, headers: auth_header
|
||||
expect(test_user.reload.labels.count).to eq(1)
|
||||
expect(test_user.reload.labels.first.key).to eq('profile')
|
||||
expect(test_user.reload.labels.first.value).to eq('drafted')
|
||||
end
|
||||
|
||||
it 'updates label on re-submit' do
|
||||
expect(test_user.labels).to eq([])
|
||||
post url, params: request_params, headers: auth_header
|
||||
expect(test_user.reload.labels.count).to eq(1)
|
||||
expect(test_user.reload.labels.first.key).to eq('profile')
|
||||
expect(test_user.reload.labels.first.value).to eq('drafted')
|
||||
|
||||
put url, params: request_params.merge(confirm: true), headers: auth_header
|
||||
expect(test_user.reload.labels.count).to eq(1)
|
||||
expect(test_user.reload.labels.first.key).to eq('profile')
|
||||
expect(test_user.reload.labels.first.value).to eq('submitted')
|
||||
end
|
||||
|
||||
it 'triggers KYCAID worker on submitted state' do
|
||||
expect(KYC::Kycaid::ApplicantWorker).to receive(:perform_async)
|
||||
post url, params: request_params.merge(confirm: true), headers: auth_header
|
||||
expect(response.status).to eq(201)
|
||||
end
|
||||
|
||||
it 'doesnt trigger KYCAID worker on drafted state' do
|
||||
expect(KYC::Kycaid::ApplicantWorker).not_to receive(:perform_async)
|
||||
post url, params: request_params, headers: auth_header
|
||||
expect(response.status).to eq(201)
|
||||
end
|
||||
|
||||
it 'doesnt trigger KYCAID worker on rejected state' do
|
||||
post url, params: request_params.merge(confirm: true), headers: auth_header
|
||||
expect(response.status).to eq(201)
|
||||
|
||||
expect(KYC::Kycaid::ApplicantWorker).not_to receive(:perform_async)
|
||||
test_user.reload.profiles.last.update(state: 'rejected')
|
||||
end
|
||||
|
||||
it 'doesnt trigger KYCAID worker on verified state' do
|
||||
post url, params: request_params.merge(confirm: true), headers: auth_header
|
||||
expect(response.status).to eq(201)
|
||||
|
||||
expect(KYC::Kycaid::ApplicantWorker).not_to receive(:perform_async)
|
||||
test_user.reload.profiles.last.update(state: 'verified')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
let!(:url) { '/api/v2/resource/profiles' }
|
||||
let!(:user_info) { '/api/v2/resource/users/me' }
|
||||
let!(:request_params) do
|
||||
{
|
||||
last_name: Faker::Name.last_name,
|
||||
first_name: Faker::Name.first_name,
|
||||
dob: Faker::Date.birthday,
|
||||
country: Faker::Address.country_code_long,
|
||||
city: Faker::Address.city,
|
||||
address: Faker::Address.state,
|
||||
postcode: Faker::Address.zip_code
|
||||
}
|
||||
end
|
||||
|
||||
let!(:asian_params) do
|
||||
{
|
||||
last_name: "국",
|
||||
first_name: "채원",
|
||||
dob: Faker::Date.birthday,
|
||||
country: Faker::Address.country_code_long,
|
||||
city: "안산시",
|
||||
address: "사직로3길 23",
|
||||
postcode: Faker::Address.zip_code
|
||||
}
|
||||
end
|
||||
|
||||
it 'accept / , ; in address' do
|
||||
request_params[:address] = '28/2 Kevin Brook; Miami, USA'
|
||||
expect { post url, params: request_params, headers: auth_header }
|
||||
.to change { Profile.count }.by (1)
|
||||
expect(response.status).to eq(201)
|
||||
res = json_body
|
||||
expect(res[:address]).to eq request_params[:address]
|
||||
end
|
||||
|
||||
it 'accept . in address' do
|
||||
request_params[:address] = 'Larkin Fork.South, New York/AP'
|
||||
expect { post url, params: request_params, headers: auth_header }
|
||||
.to change { Profile.count }.by (1)
|
||||
expect(response.status).to eq(201)
|
||||
res = json_body
|
||||
expect(res[:address]).to eq request_params[:address]
|
||||
end
|
||||
|
||||
it 'accept # ~ \ : " & ( ) in address' do
|
||||
request_params[:address] = '28~1/2 \"Kevin & Brook": (Miami, USA)'
|
||||
expect { post url, params: request_params, headers: auth_header }
|
||||
.to change { Profile.count }.by (1)
|
||||
expect(response.status).to eq(201)
|
||||
res = json_body
|
||||
expect(res[:address]).to eq request_params[:address]
|
||||
end
|
||||
|
||||
it "accept ' in address" do
|
||||
request_params[:address] = "'Larkin Fork' South New York"
|
||||
expect { post url, params: request_params, headers: auth_header }
|
||||
.to change { Profile.count }.by (1)
|
||||
expect(response.status).to eq(201)
|
||||
res = json_body
|
||||
expect(res[:address]).to eq request_params[:address]
|
||||
end
|
||||
|
||||
it "accept – in address" do
|
||||
request_params[:address] = "'Larkin–Fork' South New York"
|
||||
expect { post url, params: request_params, headers: auth_header }
|
||||
.to change { Profile.count }.by (1)
|
||||
expect(response.status).to eq(201)
|
||||
res = json_body
|
||||
expect(res[:address]).to eq request_params[:address]
|
||||
end
|
||||
|
||||
it "doesn't accept @ in address" do
|
||||
request_params[:address] = "'Larkin–Fork' @South New York"
|
||||
expect { post url, params: request_params, headers: auth_header }
|
||||
.not_to change { Profile.count }
|
||||
expect(response.status).to eq(422)
|
||||
end
|
||||
|
||||
it 'accept . in city' do
|
||||
request_params[:city] = 'St. Petersburg'
|
||||
expect { post url, params: request_params, headers: auth_header }
|
||||
.to change { Profile.count }.by (1)
|
||||
expect(response.status).to eq(201)
|
||||
res = json_body
|
||||
expect(res[:city]).to eq request_params[:city]
|
||||
end
|
||||
|
||||
it 'accept dash in city' do
|
||||
request_params[:city] = 'Hubli–Dharwad'
|
||||
expect { post url, params: request_params, headers: auth_header }
|
||||
.to change { Profile.count }.by (1)
|
||||
expect(response.status).to eq(201)
|
||||
res = json_body
|
||||
expect(res[:city]).to eq request_params[:city]
|
||||
end
|
||||
|
||||
it 'accept hyphen in city' do
|
||||
request_params[:city] = 'Hubli-Dharwad'
|
||||
expect { post url, params: request_params, headers: auth_header }
|
||||
.to change { Profile.count }.by (1)
|
||||
expect(response.status).to eq(201)
|
||||
res = json_body
|
||||
expect(res[:city]).to eq request_params[:city]
|
||||
end
|
||||
|
||||
it 'accept \' in city' do
|
||||
request_params[:city] = 'Cava de\' Tirreni'
|
||||
expect { post url, params: request_params, headers: auth_header }
|
||||
.to change { Profile.count }.by (1)
|
||||
expect(response.status).to eq(201)
|
||||
res = json_body
|
||||
expect(res[:city]).to eq request_params[:city]
|
||||
end
|
||||
|
||||
it "doesn't accept # in city" do
|
||||
request_params[:city] = 'Cava de\' #Tirreni'
|
||||
expect { post url, params: request_params, headers: auth_header }
|
||||
.not_to change { Profile.count }
|
||||
expect(response.status).to eq(422)
|
||||
end
|
||||
|
||||
it "doesn't accept @ in city" do
|
||||
request_params[:city] = 'Cava de\' @Tirreni'
|
||||
expect { post url, params: request_params, headers: auth_header }
|
||||
.not_to change { Profile.count }
|
||||
expect(response.status).to eq(422)
|
||||
end
|
||||
|
||||
it 'creates new profile with only required fields' do
|
||||
expect { post url, params: request_params, headers: auth_header }
|
||||
.to change { Profile.count }.by (1)
|
||||
expect(response.status).to eq(201)
|
||||
|
||||
res = json_body
|
||||
expect(res[:last_name]).to eq request_params[:last_name].sub(/(?<=\A.{1})(.*)/) { |match| '*' * match.length }
|
||||
expect(res[:first_name]).to eq request_params[:first_name]
|
||||
expect(res[:dob]).to eq request_params[:dob].to_s.sub(/(?<=\A.{8})(.*)/) { |match| '*' * match.length }
|
||||
expect(res[:country]).to eq request_params[:country]
|
||||
expect(res[:city]).to eq request_params[:city]
|
||||
expect(res[:address]).to eq request_params[:address]
|
||||
expect(res[:postcode]).to eq request_params[:postcode]
|
||||
expect(res[:metadata]).to be_blank
|
||||
end
|
||||
|
||||
it 'creates new profile with only required fields without masking' do
|
||||
Barong::App.config.stub(:api_data_masking_enabled).and_return(false)
|
||||
expect { post url, params: request_params, headers: auth_header }
|
||||
.to change { Profile.count }.by (1)
|
||||
expect(response.status).to eq(201)
|
||||
|
||||
res = json_body
|
||||
expect(res[:last_name]).to eq request_params[:last_name]
|
||||
expect(res[:first_name]).to eq request_params[:first_name]
|
||||
expect(res[:dob]).to eq request_params[:dob].to_s
|
||||
expect(res[:country]).to eq request_params[:country]
|
||||
expect(res[:city]).to eq request_params[:city]
|
||||
expect(res[:address]).to eq request_params[:address]
|
||||
expect(res[:postcode]).to eq request_params[:postcode]
|
||||
expect(res[:metadata]).to be_blank
|
||||
end
|
||||
|
||||
it 'creates new profile with corean symbols fields' do
|
||||
expect { post url, params: asian_params, headers: auth_header }
|
||||
.to change { Profile.count }.by (1)
|
||||
expect(response.status).to eq(201)
|
||||
|
||||
res = json_body
|
||||
|
||||
expect(res[:last_name]).to eq asian_params[:last_name]
|
||||
expect(res[:first_name]).to eq asian_params[:first_name]
|
||||
expect(res[:dob]).to eq asian_params[:dob].to_s.sub(/(?<=\A.{8})(.*)/) { |match| '*' * match.length }
|
||||
expect(res[:country]).to eq asian_params[:country]
|
||||
expect(res[:city]).to eq asian_params[:city]
|
||||
expect(res[:address]).to eq asian_params[:address]
|
||||
expect(res[:postcode]).to eq asian_params[:postcode]
|
||||
expect(res[:metadata]).to be_blank
|
||||
end
|
||||
|
||||
it 'creates new profile with all metadata fields' do
|
||||
expect { post url, params: request_params.merge(optional_params), headers: auth_header }
|
||||
.to change { Profile.count }.by (1)
|
||||
expect(response.status).to eq(201)
|
||||
|
||||
res = json_body
|
||||
expect(res[:metadata]).to eq(optional_params[:metadata])
|
||||
end
|
||||
|
||||
it 'renders an error if metadata is not json' do
|
||||
expect { post url, params: request_params.merge({ metadata: '{ bar: baz }' }), headers: auth_header }
|
||||
.not_to change { Profile.count }
|
||||
expect_status_to_eq 422
|
||||
expect_body.to eq(errors: ["metadata.invalid_format"])
|
||||
end
|
||||
|
||||
context 'create another one profile with state drafted' do
|
||||
before do
|
||||
post url, params: request_params.merge(optional_params), headers: auth_header
|
||||
end
|
||||
|
||||
it 'doesn\'t create new profile' do
|
||||
post url, params: request_params.merge(optional_params), headers: auth_header
|
||||
|
||||
result = json_body
|
||||
expect(*result[:errors]).to eq('state.exists')
|
||||
end
|
||||
end
|
||||
|
||||
context 'create another one profile with state submitted' do
|
||||
before do
|
||||
post url, params: request_params.merge(optional_params, confirm: true), headers: auth_header
|
||||
end
|
||||
|
||||
it 'doesn\'t create new profile' do
|
||||
post url, params: request_params.merge(optional_params, confirm: true), headers: auth_header
|
||||
|
||||
result = json_body
|
||||
expect(*result[:errors]).to eq('state.exists')
|
||||
end
|
||||
end
|
||||
|
||||
context 'create another one profile with state submitted and drafted' do
|
||||
before do
|
||||
post url, params: request_params.merge(optional_params, confirm: true), headers: auth_header
|
||||
end
|
||||
|
||||
it 'doesn\'t create new profile' do
|
||||
post url, params: request_params.merge(optional_params), headers: auth_header
|
||||
|
||||
result = json_body
|
||||
expect(*result[:errors]).to eq('state.exists')
|
||||
end
|
||||
end
|
||||
|
||||
context 'partial creating profile' do
|
||||
context 'empty params' do
|
||||
|
||||
it 'returns profile' do
|
||||
expect { post url, params: {}, headers: auth_header }
|
||||
.to change { Profile.count }.by (1)
|
||||
|
||||
expect(response.status).to eq(201)
|
||||
expect(json_body[:state]).to eq('drafted')
|
||||
expect(json_body[:metadata].blank?).to be_truthy
|
||||
Profile::OPTIONAL_PARAMS.each { |p|
|
||||
expect(json_body[p].blank?).to be_truthy
|
||||
}
|
||||
Profile::OPTIONAL_PARAMS.each { |p|
|
||||
expect(json_body.with_indifferent_access[p].blank?).to be_truthy
|
||||
}
|
||||
expect(Profile.last.user.labels.find_by(key: 'profile').value).to eq('drafted')
|
||||
end
|
||||
end
|
||||
|
||||
context 'several params' do
|
||||
let(:params) { { last_name: Faker::Name.last_name, first_name: Faker::Name.first_name } }
|
||||
|
||||
it 'returns profile' do
|
||||
expect { post url, params: params, headers: auth_header }
|
||||
.to change { Profile.count }.by (1)
|
||||
|
||||
expect(response.status).to eq(201)
|
||||
expect(json_body[:first_name].nil?).to be_falsey
|
||||
expect(json_body[:last_name].nil?).to be_falsey
|
||||
expect(json_body[:state]).to eq('drafted')
|
||||
expect(json_body[:metadata].blank?).to be_truthy
|
||||
(Profile::OPTIONAL_PARAMS - params.stringify_keys.keys).each { |p|
|
||||
expect(json_body.with_indifferent_access[p].blank?).to be_truthy
|
||||
}
|
||||
expect(Profile.last.user.labels.find_by(key: :profile).value).to eq('drafted')
|
||||
end
|
||||
end
|
||||
|
||||
context 'several params with profile confirmation' do
|
||||
let(:params) { { last_name: Faker::Name.last_name, first_name: Faker::Name.first_name } }
|
||||
|
||||
it 'returns profile' do
|
||||
expect { post url, params: params.merge(confirm: true), headers: auth_header }
|
||||
.to change { Profile.count }.by (1)
|
||||
|
||||
expect(response.status).to eq(201)
|
||||
expect(json_body[:first_name].nil?).to be_falsey
|
||||
expect(json_body[:last_name].nil?).to be_falsey
|
||||
expect(json_body[:state]).to eq('submitted')
|
||||
expect(json_body[:metadata].blank?).to be_truthy
|
||||
(Profile::OPTIONAL_PARAMS - params.stringify_keys.keys).each { |p|
|
||||
expect(json_body.with_indifferent_access[p].blank?).to be_truthy
|
||||
}
|
||||
expect(Profile.last.user.labels.find_by(key: :profile).value).to eq('submitted')
|
||||
end
|
||||
end
|
||||
|
||||
context 'full profile params' do
|
||||
it 'returns profile' do
|
||||
expect { post url, params: request_params, headers: auth_header }
|
||||
.to change { Profile.count }.by (1)
|
||||
|
||||
expect(response.status).to eq(201)
|
||||
expect(json_body[:state]).to eq('drafted')
|
||||
expect(json_body[:metadata].blank?).to be_truthy
|
||||
request_params.keys.each { |p|
|
||||
expect(json_body[p].present?).to be_truthy
|
||||
}
|
||||
expect(Profile.last.user.labels.find_by(key: 'profile').value).to eq('drafted')
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'GET /api/v2/resource/profiles/me' do
|
||||
let!(:url) { '/api/v2/resource/profiles/me' }
|
||||
let!(:request_params) do
|
||||
{
|
||||
first_name: Faker::Name.first_name,
|
||||
last_name: Faker::Name.last_name,
|
||||
dob: Faker::Date.birthday,
|
||||
address: Faker::Address.state,
|
||||
postcode: Faker::Address.zip_code,
|
||||
city: Faker::Address.city,
|
||||
country: Faker::Address.country_code_long
|
||||
}
|
||||
end
|
||||
|
||||
it 'returns user profile data with metadata' do
|
||||
post '/api/v2/resource/profiles', params: request_params.merge(optional_params),
|
||||
headers: auth_header
|
||||
expect(response.status).to eq(201)
|
||||
|
||||
get url, headers: auth_header
|
||||
expect(response.status).to eq(200)
|
||||
last_name = request_params[:last_name].sub(/(?<=\A.{1})(.*)/) { |match| '*' * match.length }
|
||||
dob = request_params[:dob].to_s.sub(/(?<=\A.{8})(.*)/) { |match| '*' * match.length }
|
||||
expected_json = request_params.merge(state: 'drafted', last_name: last_name,dob: dob).merge(optional_params).to_json
|
||||
expect(JSON.parse(response.body)[0].except('created_at', 'updated_at')).to eq(JSON.parse(expected_json))
|
||||
end
|
||||
end
|
||||
|
||||
context 'event API behavior' do
|
||||
let!(:url) { '/api/v2/resource/profiles' }
|
||||
let!(:request_params) do
|
||||
{
|
||||
last_name: Faker::Name.last_name,
|
||||
first_name: Faker::Name.first_name,
|
||||
dob: Faker::Date.birthday,
|
||||
country: Faker::Address.country_code_long,
|
||||
city: Faker::Address.city,
|
||||
address: Faker::Address.state,
|
||||
postcode: Faker::Address.zip_code
|
||||
}
|
||||
end
|
||||
|
||||
before do
|
||||
allow(EventAPI).to receive(:notify)
|
||||
end
|
||||
|
||||
it 'receive model.profile.created notify' do
|
||||
expect(EventAPI).to receive(:notify).ordered.with('model.user.created', hash_including(:record))
|
||||
expect(EventAPI).to receive(:notify).ordered.with('model.profile.created', hash_including(:record))
|
||||
|
||||
post url, params: request_params, headers: auth_header
|
||||
end
|
||||
|
||||
it 'receive model.profile.created notify with user and profile params' do
|
||||
expect(EventAPI).to receive(:notify).ordered.with('model.user.created', hash_including(:record))
|
||||
expect(EventAPI).to receive(:notify).ordered.with(
|
||||
'model.profile.created',
|
||||
hash_including(
|
||||
record: {
|
||||
user: anything,
|
||||
address: anything,
|
||||
city: anything,
|
||||
country: anything,
|
||||
created_at: anything,
|
||||
updated_at: anything,
|
||||
dob: anything,
|
||||
first_name: anything,
|
||||
last_name: anything,
|
||||
postcode: anything
|
||||
}
|
||||
)
|
||||
)
|
||||
|
||||
post url, params: request_params, headers: auth_header
|
||||
end
|
||||
end
|
||||
|
||||
describe 'PUT /api/v2/resource/profiles' do
|
||||
let!(:url) { '/api/v2/resource/profiles' }
|
||||
let!(:request_params) do
|
||||
{
|
||||
last_name: Faker::Name.last_name,
|
||||
first_name: Faker::Name.first_name,
|
||||
dob: Faker::Date.birthday,
|
||||
country: Faker::Address.country_code_long,
|
||||
city: Faker::Address.city,
|
||||
address: Faker::Address.street_address,
|
||||
postcode: Faker::Address.zip_code
|
||||
}
|
||||
end
|
||||
|
||||
context 'user without profile' do
|
||||
it 'renders an error when profile doesnt exist' do
|
||||
put url, params: request_params, headers: auth_header
|
||||
|
||||
expect_status.to eq(404)
|
||||
expect_body.to eq(errors: ['resource.profile.doesnt_exist_or_not_editable'])
|
||||
end
|
||||
end
|
||||
|
||||
context 'user with profile' do
|
||||
let!(:profile) { create(:profile, user: test_user)}
|
||||
|
||||
it 'returns submitted profile' do
|
||||
expect { put url, params: request_params.merge(confirm: true), headers: auth_header }
|
||||
.not_to change { Profile.count }
|
||||
|
||||
expect(response.status).to eq(200)
|
||||
expect(json_body[:state]).to eq('submitted')
|
||||
expect(profile[:metadata]).to be_blank
|
||||
end
|
||||
end
|
||||
|
||||
context 'user with partial profile' do
|
||||
let!(:profile) { create(:profile, user: test_user, last_name: nil, first_name: nil) }
|
||||
|
||||
it 'returns partial profile' do
|
||||
expect { put url, params: request_params.except(:first_name), headers: auth_header }
|
||||
.not_to change { Profile.count }
|
||||
|
||||
expect(response.status).to eq(200)
|
||||
expect(json_body[:state]).to eq('drafted')
|
||||
expect(json_body[:metadata]).to be_blank
|
||||
end
|
||||
|
||||
it 'returns completed profile' do
|
||||
expect { put url, params: request_params, headers: auth_header }
|
||||
.not_to change { Profile.count }
|
||||
|
||||
expect(response.status).to eq(200)
|
||||
expect(json_body[:state]).to eq('drafted')
|
||||
expect(json_body[:metadata]).to be_blank
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
297
spec/api/v2/resource/service_accounts_spec.rb
Normal file
297
spec/api/v2/resource/service_accounts_spec.rb
Normal file
@@ -0,0 +1,297 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
describe 'Api::V2::Resource::ServiceAccounts' do
|
||||
include_context 'bearer authentication'
|
||||
let!(:create_member_permission) do
|
||||
create :permission,
|
||||
role: 'member'
|
||||
end
|
||||
let!(:create_service_account_permission) do
|
||||
create :permission,
|
||||
role: 'service_account'
|
||||
end
|
||||
let!(:create_admin_permission) do
|
||||
create :permission,
|
||||
role: 'admin'
|
||||
end
|
||||
let!(:test_user) { create(:user, otp: otp_enabled, role: 'member') }
|
||||
let!(:service_account) { create(:service_account, user: test_user) }
|
||||
let(:otp_enabled) { true }
|
||||
let!(:first_api_key) { create :api_key, key_holder_account: service_account }
|
||||
let!(:second_api_key) { create :api_key, key_holder_account: service_account }
|
||||
let(:valid_otp_code) { '1357' }
|
||||
let(:invalid_otp_code) { '1234' }
|
||||
let(:otp_code) { valid_otp_code }
|
||||
|
||||
before do
|
||||
allow(TOTPService).to receive(:validate?)
|
||||
.with(test_user.uid, valid_otp_code) { true }
|
||||
allow(TOTPService).to receive(:validate?)
|
||||
.with(test_user.uid, invalid_otp_code) { false }
|
||||
allow(TOTPService).to receive(:validate?)
|
||||
.with(test_user.uid, nil) { false }
|
||||
end
|
||||
|
||||
describe 'GET /api/v2/resource/service_accounts' do
|
||||
let(:do_request) do
|
||||
get '/api/v2/resource/service_accounts',
|
||||
headers: auth_header
|
||||
end
|
||||
|
||||
let(:expected_fields) do
|
||||
{
|
||||
uid: service_account.uid,
|
||||
email: service_account.email,
|
||||
owner_id: test_user.id,
|
||||
role: service_account.role,
|
||||
level: service_account.level,
|
||||
state: service_account.state,
|
||||
}
|
||||
end
|
||||
|
||||
it 'Return service accounts for current user' do
|
||||
do_request
|
||||
|
||||
expect(json_body.count).to eq 1
|
||||
expect(response.status).to eq(200)
|
||||
expect(json_body.first).to include(expected_fields)
|
||||
end
|
||||
end
|
||||
|
||||
describe 'GET /api/v2/resource/service_accounts/api_keys/' do
|
||||
let(:get_params) do
|
||||
{
|
||||
service_account_uid: service_account.uid,
|
||||
ordering: 'asc',
|
||||
order_by: 'id'
|
||||
}
|
||||
end
|
||||
let(:post_params) do
|
||||
{
|
||||
service_account_uid: service_account.uid,
|
||||
scope: 'trade',
|
||||
algorithm: 'HS256'
|
||||
}
|
||||
end
|
||||
let(:do_request) do
|
||||
post '/api/v2/resource/service_accounts/api_keys',
|
||||
params: post_params.merge(totp_code: otp_code),
|
||||
headers: auth_header
|
||||
end
|
||||
let(:do_get_request) { get "/api/v2/resource/service_accounts/api_keys", params: get_params, headers: auth_header }
|
||||
let(:expected_fields) do
|
||||
{
|
||||
kid: first_api_key.kid,
|
||||
state: first_api_key.state,
|
||||
scope: %w[trade]
|
||||
}
|
||||
end
|
||||
let(:totp_code) { valid_otp_code }
|
||||
|
||||
it 'Return api key for current account in ASC order' do
|
||||
do_get_request
|
||||
|
||||
expect(json_body.count).to eq 2
|
||||
expect(response.status).to eq(200)
|
||||
expect(json_body.first).to include(expected_fields)
|
||||
end
|
||||
|
||||
it 'Return api key for current account in DESC order' do
|
||||
get_params.merge!(ordering: 'desc')
|
||||
do_get_request
|
||||
|
||||
expect(json_body.count).to eq 2
|
||||
expect(response.status).to eq(200)
|
||||
expect(json_body.second).to include(expected_fields)
|
||||
end
|
||||
|
||||
context 'when invalid ordering' do
|
||||
it 'renders an error' do
|
||||
get_params.merge!(ordering: 'resc')
|
||||
do_get_request
|
||||
|
||||
expect(response.status).to eq(422)
|
||||
expect_body.to eq(errors: ["resource.service_accounts.invalid_ordering"])
|
||||
end
|
||||
end
|
||||
|
||||
context 'when invalid order_by' do
|
||||
it 'renders an error' do
|
||||
get_params.merge!(order_by: 'invalid')
|
||||
do_get_request
|
||||
|
||||
expect(response.status).to eq(422)
|
||||
expect_body.to eq(errors: ["resource.service_accounts.invalid_attribute"])
|
||||
end
|
||||
end
|
||||
|
||||
context 'when otp is not enabled' do
|
||||
let(:otp_enabled) { false }
|
||||
|
||||
it 'renders an error' do
|
||||
do_request
|
||||
expect(response.status).to eq(400)
|
||||
expect_body.to eq(errors: ["resource.service_accounts.2fa_disabled"])
|
||||
end
|
||||
end
|
||||
|
||||
context 'when code is invalid' do
|
||||
let(:otp_code) { invalid_otp_code }
|
||||
|
||||
it 'renders an error' do
|
||||
do_request
|
||||
expect(response.status).to eq(422)
|
||||
expect_body.to eq(errors: ["resource.service_accounts.invalid_totp"])
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'POST /api/v2/resource/api_keys' do
|
||||
let(:do_request) do
|
||||
post '/api/v2/resource/api_keys',
|
||||
params: params.merge(totp_code: otp_code),
|
||||
headers: auth_header
|
||||
end
|
||||
let(:otp_code) { valid_otp_code }
|
||||
|
||||
context 'when fields are valid' do
|
||||
let(:params) do
|
||||
{
|
||||
scope: 'trade',
|
||||
algorithm: 'HS256'
|
||||
}
|
||||
end
|
||||
let(:expected_fields) do
|
||||
{
|
||||
state: 'active',
|
||||
scope: params[:scope].split(','),
|
||||
algorithm: 'HS256'
|
||||
}
|
||||
end
|
||||
|
||||
it 'Create an api key' do
|
||||
expect { do_request }.to change { APIKey.count }.by(1)
|
||||
expect(response.status).to eq(201)
|
||||
expect_body.to include(expected_fields)
|
||||
end
|
||||
|
||||
it 'does not create api key if vault is down' do
|
||||
allow(Vault::Rails).to receive(:encrypt).and_raise(Vault::VaultError)
|
||||
expect { do_request }.not_to change { APIKey.count }
|
||||
expect(response.status).to eq(422)
|
||||
expect_body.to eq(errors: ["api_key.could_not_save_secret"])
|
||||
end
|
||||
|
||||
context 'when otp is not enabled' do
|
||||
let(:otp_enabled) { false }
|
||||
|
||||
it 'renders an error' do
|
||||
do_request
|
||||
expect(response.status).to eq(400)
|
||||
expect_body.to eq(errors: ["resource.api_key.2fa_disabled"])
|
||||
end
|
||||
end
|
||||
|
||||
context 'when code is invalid' do
|
||||
let(:otp_code) { invalid_otp_code }
|
||||
|
||||
it 'renders an error' do
|
||||
do_request
|
||||
expect(response.status).to eq(422)
|
||||
expect_body.to eq(errors: ["resource.api_key.invalid_totp"])
|
||||
end
|
||||
end
|
||||
|
||||
context 'when algorithm is invalid' do
|
||||
it 'renders an error' do
|
||||
params[:algorithm] = 'kek'
|
||||
do_request
|
||||
expect_body.to eq(errors: ["kid.blank", "algorithm.inclusion"])
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'PUT /api/v2/resource/service_accounts/api_keys/:kid' do
|
||||
let(:do_request) do
|
||||
put "/api/v2/resource/service_accounts/api_keys/#{first_api_key.kid}", params: params.merge(totp_code: otp_code),
|
||||
headers: auth_header
|
||||
end
|
||||
let(:otp_code) { valid_otp_code }
|
||||
context 'when valid fields' do
|
||||
let(:params) do
|
||||
{
|
||||
service_account_uid: service_account.uid,
|
||||
state: 'inactive',
|
||||
scope: 'sell',
|
||||
algorithm: 'HS256'
|
||||
}
|
||||
end
|
||||
|
||||
it 'Updates a state' do
|
||||
expect { do_request }.to change { first_api_key.reload.state }
|
||||
.from('active').to('inactive')
|
||||
expect(response.status).to eq(200)
|
||||
end
|
||||
|
||||
it 'Updates a scope' do
|
||||
expect { do_request }.to change { first_api_key.reload.scope }
|
||||
.from(['trade']).to(['sell'])
|
||||
expect(response.status).to eq(200)
|
||||
end
|
||||
|
||||
context 'when otp is not enabled' do
|
||||
let(:otp_enabled) { false }
|
||||
|
||||
it 'renders an error' do
|
||||
do_request
|
||||
expect(response.status).to eq(400)
|
||||
expect_body.to eq(errors: ["resource.service_accounts.2fa_disabled"])
|
||||
end
|
||||
end
|
||||
|
||||
context 'when code is invalid' do
|
||||
let(:otp_code) { invalid_otp_code }
|
||||
|
||||
it 'renders an error' do
|
||||
do_request
|
||||
expect(response.status).to eq(422)
|
||||
expect_body.to eq(errors: ["resource.service_accounts.invalid_totp"])
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'DELETE /api/v2/resource/service_accounts/api_keys/:uid' do
|
||||
let(:do_request) do
|
||||
delete "/api/v2/resource/service_accounts/api_keys/#{first_api_key.kid}?totp_code=#{otp_code}&service_account_uid=#{service_account.uid}",
|
||||
headers: auth_header
|
||||
end
|
||||
let(:otp_code) { valid_otp_code }
|
||||
|
||||
it 'Removes an api key' do
|
||||
do_request
|
||||
expect(response.status).to eq(204)
|
||||
end
|
||||
|
||||
context 'when otp is not enabled' do
|
||||
let(:otp_enabled) { false }
|
||||
|
||||
it 'renders an error' do
|
||||
do_request
|
||||
expect(response.status).to eq(400)
|
||||
expect_body.to eq(errors: ["resource.service_accounts.2fa_disabled"])
|
||||
end
|
||||
end
|
||||
|
||||
context 'when code is invalid' do
|
||||
let(:otp_code) { invalid_otp_code }
|
||||
|
||||
it 'renders an error' do
|
||||
do_request
|
||||
expect(response.status).to eq(422)
|
||||
expect_body.to eq(errors: ["resource.service_accounts.invalid_totp"])
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
270
spec/api/v2/resource/users_spec.rb
Normal file
270
spec/api/v2/resource/users_spec.rb
Normal file
@@ -0,0 +1,270 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
describe 'Api::V1::Profiles' do
|
||||
include_context 'bearer authentication'
|
||||
let!(:create_member_permission) do
|
||||
create :permission,
|
||||
role: 'member'
|
||||
end
|
||||
describe 'GET /api/v2/resource/users/me' do
|
||||
it 'should reply permissions denied' do
|
||||
get '/api/v2/resource/users/me'
|
||||
expect_body.to eq(errors: ["jwt.decode_and_verify"])
|
||||
expect(response.status).to eq(401)
|
||||
end
|
||||
|
||||
it 'should allow traffic with Authorization' do
|
||||
get '/api/v2/resource/users/me', headers: auth_header
|
||||
expect(json_body[:email]).to eq(test_user.email)
|
||||
expect(response.status).to eq(200)
|
||||
end
|
||||
end
|
||||
|
||||
let(:valid_otp_code) { '1357' }
|
||||
let(:invalid_otp_code) { '1234' }
|
||||
|
||||
before do
|
||||
allow(TOTPService).to receive(:validate?)
|
||||
.with(test_user.uid, valid_otp_code) { true }
|
||||
allow(TOTPService).to receive(:validate?)
|
||||
.with(test_user.uid, invalid_otp_code) { false }
|
||||
end
|
||||
|
||||
describe 'PUT /api/v2/resource/users/me' do
|
||||
let(:correct_data) { { bar: 'baz' }.to_json }
|
||||
let(:incorrect_data) { 'random string' }
|
||||
|
||||
it 'changes user data field' do
|
||||
put '/api/v2/resource/users/me', headers: auth_header, params: {
|
||||
data: correct_data
|
||||
}
|
||||
expect(test_user.reload.data).to eq(correct_data)
|
||||
expect(response.status).to eq 200
|
||||
end
|
||||
|
||||
it 'receives invalid data error' do
|
||||
put '/api/v2/resource/users/me', headers: auth_header, params: {
|
||||
data: incorrect_data
|
||||
}
|
||||
|
||||
expect(response.status).to eq 422
|
||||
expect_body.to eq errors: ['data.invalid_format']
|
||||
end
|
||||
end
|
||||
|
||||
describe 'DELETE /api/v2/resource/users/me' do
|
||||
it 'receives invalid password error' do
|
||||
delete '/api/v2/resource/users/me', headers: auth_header, params: {
|
||||
password: 'WrongPassword'
|
||||
}
|
||||
|
||||
expect(response.status).to eq 422
|
||||
expect(test_user.reload.state).to eq 'active'
|
||||
expect_body.to eq errors: ['resource.user.invalid_password']
|
||||
end
|
||||
|
||||
it 'receives otp missing error if 2fa is enabled and param is missing' do
|
||||
test_user.update(otp: true)
|
||||
delete '/api/v2/resource/users/me', headers: auth_header, params: {
|
||||
password: 'Tecohvi0'
|
||||
}
|
||||
|
||||
expect(response.status).to eq 422
|
||||
expect(test_user.reload.state).to eq 'active'
|
||||
expect_body.to eq errors: ['resource.user.missing_otp_code']
|
||||
end
|
||||
|
||||
it 'receives otp empty error if 2fa is enabled and param is empty' do
|
||||
test_user.update(otp: true)
|
||||
delete '/api/v2/resource/users/me', headers: auth_header, params: {
|
||||
password: 'Tecohvi0',
|
||||
otp_code: ''
|
||||
}
|
||||
|
||||
expect(response.status).to eq 422
|
||||
expect(test_user.reload.state).to eq 'active'
|
||||
expect_body.to eq errors: ['resource.user.empty_otp_code']
|
||||
end
|
||||
|
||||
it 'receives invalid otp error with wrong otp code' do
|
||||
test_user.update(otp: true)
|
||||
delete '/api/v2/resource/users/me', headers: auth_header, params: {
|
||||
password: 'Tecohvi0',
|
||||
otp_code: invalid_otp_code
|
||||
}
|
||||
|
||||
expect(response.status).to eq 422
|
||||
expect(test_user.reload.state).to eq 'active'
|
||||
expect_body.to eq errors: ['resource.user.invalid_otp']
|
||||
end
|
||||
|
||||
it 'marks user as deleted with turned on 2fa and valid otp' do
|
||||
test_user.update(otp: true)
|
||||
delete '/api/v2/resource/users/me', headers: auth_header, params: {
|
||||
password: 'Tecohvi0',
|
||||
otp_code: valid_otp_code
|
||||
}
|
||||
|
||||
expect(response.status).to eq 200
|
||||
expect(test_user.reload.state).to eq 'deleted'
|
||||
end
|
||||
|
||||
it 'marks user as deleted with turned off 2fa' do
|
||||
delete '/api/v2/resource/users/me', headers: auth_header, params: {
|
||||
password: 'Tecohvi0'
|
||||
}
|
||||
|
||||
expect(response.status).to eq 200
|
||||
expect(test_user.reload.state).to eq 'deleted'
|
||||
end
|
||||
end
|
||||
|
||||
describe 'POST /api/v2/resource/otp/disable' do
|
||||
it 'with dasbled 2fa' do
|
||||
post '/api/v2/resource/otp/disable', headers: auth_header, params: { code: valid_otp_code }
|
||||
expect_body.to eq errors: ['resource.otp.not_enabled']
|
||||
end
|
||||
|
||||
it 'with invalid code' do
|
||||
test_user.update(otp: true)
|
||||
post '/api/v2/resource/otp/disable', headers: auth_header, params: { code: invalid_otp_code }
|
||||
expect_body.to eq errors: ['resource.otp.invalid']
|
||||
end
|
||||
|
||||
it 'disables 2fa' do
|
||||
test_user.update(otp: true)
|
||||
expect {
|
||||
post '/api/v2/resource/otp/disable', headers: auth_header, params: { code: valid_otp_code }
|
||||
}.to change { test_user.reload.otp }.from(true).to(false)
|
||||
|
||||
expect(response.status).to eq 200
|
||||
end
|
||||
end
|
||||
|
||||
describe 'POST /api/v2/resource/users/activity' do
|
||||
it 'allows only [password, otp, session, all] as a topic parameter' do
|
||||
get '/api/v2/resource/users/activity/invalid', headers: auth_header
|
||||
expect(response.status).to eq(422)
|
||||
expect_body.to eq(errors: ['resource.user.wrong_topic'])
|
||||
get '/api/v2/resource/users/activity/session', headers: auth_header
|
||||
expect(response.status).to eq(422)
|
||||
expect_body.to eq(errors: ['resource.user.no_activity'])
|
||||
end
|
||||
it 'sorts user activities' do
|
||||
4.times { create(:activity, user: test_user) }
|
||||
get '/api/v2/resource/users/activity/all', headers: auth_header
|
||||
activities = JSON.parse(response.body)
|
||||
expect(activities.first['id']).to be >= activities.last['id']
|
||||
end
|
||||
it 'sorts user activities by result' do
|
||||
4.times { create(:activity, user: test_user, result: 'succeed') }
|
||||
get '/api/v2/resource/users/activity/all', params: { result: 'succeed' }, headers: auth_header
|
||||
activities = JSON.parse(response.body)
|
||||
expect(activities.length).to eq(4)
|
||||
end
|
||||
it 'sorts user activities by time range' do
|
||||
4.times { create(:activity, user: test_user, result: 'succeed') }
|
||||
get '/api/v2/resource/users/activity/all', params: { time_from: 5.hours.ago.to_i, time_to: 1.hours.ago.to_i }, headers: auth_header
|
||||
activities = JSON.parse(response.body)
|
||||
expect(activities.length).to eq(1)
|
||||
end
|
||||
|
||||
it 'sorts user activities by time range' do
|
||||
4.times { create(:activity, user: test_user, result: 'succeed') }
|
||||
4.times { create(:activity, user: test_user, result: 'succeed', created_at: 10.hours.ago) }
|
||||
|
||||
get '/api/v2/resource/users/activity/all', params: { time_from: 5.hours.ago.to_i}, headers: auth_header
|
||||
activities = JSON.parse(response.body)
|
||||
expect(activities.length).to eq(4)
|
||||
end
|
||||
end
|
||||
|
||||
describe 'POST /api/v2/resource/users/password' do
|
||||
let(:do_request) do
|
||||
put '/api/v2/resource/users/password', params: params, headers: auth_header
|
||||
end
|
||||
let(:do_confirm_request) do
|
||||
post '/api/v2/resource/users/confirm_password', params: { code: otp_code }, headers: auth_header
|
||||
end
|
||||
let(:params) do
|
||||
{
|
||||
old_password: old_password,
|
||||
new_password: new_password,
|
||||
confirm_password: confirm_password
|
||||
}
|
||||
end
|
||||
let(:old_password) { '' }
|
||||
let(:new_password) { '' }
|
||||
let(:confirm_password) { '' }
|
||||
context 'when params are blank' do
|
||||
it 'renders 400 error' do
|
||||
do_request
|
||||
expect(response.status).to eq(422)
|
||||
expect_body.to eq(errors: ["resource.user.empty_old_password", "resource.user.empty_new_password", "resource.user.empty_confirm_password"])
|
||||
end
|
||||
end
|
||||
context 'when old password is not right' do
|
||||
let(:old_password) { 'invalid' }
|
||||
let(:new_password) { 'Gol4aid2' }
|
||||
let(:confirm_password) { 'Gol4aid2' }
|
||||
it 'renders 400 error' do
|
||||
do_request
|
||||
expect(response.status).to eq(400)
|
||||
expect_body.to eq(errors: ["resource.password.prev_pass_not_correct"])
|
||||
end
|
||||
end
|
||||
context 'when new pass and confirmation are different' do
|
||||
let(:old_password) { 'Tecohvi0' } # 'Tecohvi0' - test_user password
|
||||
let(:new_password) { 'Gol4aid1' }
|
||||
let(:confirm_password) { 'Gol4aid2' }
|
||||
it 'renders 422 error' do
|
||||
do_request
|
||||
expect(response.status).to eq(422)
|
||||
expect_body.to eq(errors: ["resource.password.doesnt_match"])
|
||||
end
|
||||
end
|
||||
context 'when params are valid, passwords are weak' do
|
||||
let(:old_password) { 'Tecohvi0' } # 'Tecohvi0' - test_user password
|
||||
let(:new_password) { 'Simple' }
|
||||
let(:confirm_password) { 'Simple' }
|
||||
it 'returns weak password error' do
|
||||
do_request
|
||||
expect_status_to_eq 422
|
||||
expect_body.to eq(errors: ["temp_password.requirements"])
|
||||
end
|
||||
end
|
||||
|
||||
context 'when params are valid, confirm code not valid' do
|
||||
let(:old_password) { 'Tecohvi0' } # 'Tecohvi0' - test_user password
|
||||
let(:new_password) { 'Gol4aid1' }
|
||||
let(:confirm_password) { 'Gol4aid1' }
|
||||
let(:otp_code) { invalid_otp_code }
|
||||
let(:log_in) { post '/api/v2/identity/sessions', params: { email: test_user.email, password: new_password } }
|
||||
it 'changes a password' do
|
||||
do_request
|
||||
do_confirm_request
|
||||
expect_status_to_eq 422
|
||||
expect_body.to eq(errors: ["resource.password.confirm_code_not_match"])
|
||||
end
|
||||
end
|
||||
|
||||
context 'when params are valid' do
|
||||
let(:old_password) { 'Tecohvi0' } # 'Tecohvi0' - test_user password
|
||||
let(:new_password) { 'Gol4aid1' }
|
||||
let(:confirm_password) { 'Gol4aid1' }
|
||||
let(:otp_code) { valid_otp_code }
|
||||
let(:log_in) { post '/api/v2/identity/sessions', params: { email: test_user.email, password: new_password } }
|
||||
it 'changes a password' do
|
||||
do_request
|
||||
expect(response.status).to eq(201)
|
||||
|
||||
do_confirm_request
|
||||
expect(response.status).to eq(201)
|
||||
|
||||
log_in
|
||||
expect(response.status).to eq(200)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
Reference in New Issue
Block a user