snf_core 0.2.94 → 0.2.97
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/app/controllers/snf_core/auth_controller.rb +12 -44
- data/app/models/snf_core/account_transfer.rb +37 -0
- data/app/models/snf_core/item_request.rb +27 -0
- data/app/models/snf_core/quotation.rb +41 -0
- data/app/models/snf_core/user.rb +18 -18
- data/app/models/snf_core/virtual_account.rb +41 -0
- data/app/models/snf_core/virtual_account_transaction.rb +52 -0
- data/config/routes.rb +0 -3
- data/db/migrate/20250310122954_create_snf_core_virtual_accounts.rb +22 -0
- data/db/migrate/20250310123127_add_kyc_attributes_to_user.rb +17 -0
- data/db/migrate/20250310133249_create_snf_core_virtual_account_transactions.rb +18 -0
- data/db/migrate/20250310143604_create_snf_core_item_requests.rb +14 -0
- data/db/migrate/20250311000000_create_snf_core_account_transfers.rb +21 -0
- data/db/migrate/20250312043359_create_snf_core_quotations.rb +14 -0
- data/lib/snf_core/version.rb +1 -1
- data/spec/dummy/db/schema.rb +130 -27
- data/spec/dummy/log/development.log +2618 -0
- data/spec/dummy/log/test.log +110127 -0
- data/spec/dummy/tmp/storage/00/ch/00ch4m884d5bnelqgd2ld1qig6ey +0 -0
- data/spec/dummy/tmp/storage/0s/ba/0sbase6mg5sgich6or97cg1faxlw +0 -0
- data/spec/dummy/tmp/storage/0x/uy/0xuyfqa0t08zr8vfm8p123o0s34t +0 -0
- data/spec/dummy/tmp/storage/1o/ts/1otsbrqkd6n4qiht1wabl7fhjnt3 +0 -0
- data/spec/dummy/tmp/storage/1v/54/1v542i134jpb9iwbq9m1v801y0ls +0 -0
- data/spec/dummy/tmp/storage/1y/46/1y4617uv2nrk9fa58zfv9xyixmfk +0 -0
- data/spec/dummy/tmp/storage/24/kq/24kq40q48k7jry3npyah0td4uend +0 -0
- data/spec/dummy/tmp/storage/28/ye/28yeusf04wxopqkg0bxki7mqo9ph +0 -0
- data/spec/dummy/tmp/storage/2r/hv/2rhvs6uca2ozrojfuub5d4qk0ugy +0 -0
- data/spec/dummy/tmp/storage/2w/r1/2wr14buxnrx4cee19gj6sefnbby5 +0 -0
- data/spec/dummy/tmp/storage/3k/7z/3k7zlcd9fm2jwn99kinxpahrmm4u +0 -0
- data/spec/dummy/tmp/storage/3o/2a/3o2ahddfby8aqgutow91vo4asm4f +0 -0
- data/spec/dummy/tmp/storage/46/zj/46zjkljy84qmg4ebes40e2zolzp7 +0 -0
- data/spec/dummy/tmp/storage/4f/fl/4fflxsg2pissq4960wfazyv9mgp1 +0 -0
- data/spec/dummy/tmp/storage/4l/6a/4l6ap3t705ektu02kabqobaq8huf +0 -0
- data/spec/dummy/tmp/storage/4m/3n/4m3n3r5zl2jevvvw3alq0omnnowk +0 -0
- data/spec/dummy/tmp/storage/4t/qc/4tqc587gmu2g7hgntrgjnhkw0e1u +0 -0
- data/spec/dummy/tmp/storage/4w/76/4w7656628v1p4mp87fqz3okc6ix4 +0 -0
- data/spec/dummy/tmp/storage/50/ct/50ctjwt8y93spjaef1b3rfnav9gb +0 -0
- data/spec/dummy/tmp/storage/5m/0k/5m0k5zx08ouqos7rs09bpgmalspu +0 -0
- data/spec/dummy/tmp/storage/5m/4n/5m4nvbe3vqwrdpy6hujr1oq5nolv +0 -0
- data/spec/dummy/tmp/storage/5m/j8/5mj8ypl6zxx8g3h7vqmu4p2la4pk +0 -0
- data/spec/dummy/tmp/storage/5r/tv/5rtv2agppt9e52eg0oc81svs896e +0 -0
- data/spec/dummy/tmp/storage/5s/b6/5sb6kux9jtdj4du9ihu32tc82s12 +0 -0
- data/spec/dummy/tmp/storage/5t/84/5t84ncnwqnv83rndaztqz1dsae3v +0 -0
- data/spec/dummy/tmp/storage/6h/9a/6h9a6bxecnlwx7zbb4dc33rmftee +0 -0
- data/spec/dummy/tmp/storage/6h/pd/6hpdw0r7w6vpcotirwqz1fv640kk +0 -0
- data/spec/dummy/tmp/storage/6i/dm/6idmnfdfriqdmvcvgreyeho4xgra +0 -0
- data/spec/dummy/tmp/storage/6n/3b/6n3bufdfx1tvls9lzm3g625l1lyk +0 -0
- data/spec/dummy/tmp/storage/78/5u/785ub96ra353qygeubpq4vlifjas +0 -0
- data/spec/dummy/tmp/storage/7v/55/7v55c4y5xdiw6qrvbdpmd63v4toz +0 -0
- data/spec/dummy/tmp/storage/83/yi/83yiflnj2evgqahmzgiavbywle7h +0 -0
- data/spec/dummy/tmp/storage/8a/02/8a02q8tcm3lowda8cvcjh18tka7i +0 -0
- data/spec/dummy/tmp/storage/8g/4a/8g4aarquz0mos9gxzq6j7xzwli09 +0 -0
- data/spec/dummy/tmp/storage/8k/0m/8k0mv7xql5lr89d0172izqplypwx +0 -0
- data/spec/dummy/tmp/storage/8n/lz/8nlzdb800dgr9iax1ez2ko4euh02 +0 -0
- data/spec/dummy/tmp/storage/8y/y2/8yy2kqz8sa11vh7naiomyofj30ox +0 -0
- data/spec/dummy/tmp/storage/92/hz/92hz15r26fs5wymc6tced4b08bh6 +0 -0
- data/spec/dummy/tmp/storage/9k/wj/9kwjdz4yw0jyzddj32u97xciot47 +0 -0
- data/spec/dummy/tmp/storage/a0/ve/a0veqfafcpy1hl6jvg6jc4qt08kr +0 -0
- data/spec/dummy/tmp/storage/ak/g9/akg9bg7fdwisycbclvmikbzl676j +0 -0
- data/spec/dummy/tmp/storage/ap/fs/apfsy6cp4mt4jsyiyscutqhv36id +0 -0
- data/spec/dummy/tmp/storage/b2/ux/b2uxv1pu0hjqty7779grby8qxzoo +0 -0
- data/spec/dummy/tmp/storage/bh/s1/bhs1yc1jo5vhc1u8lz7zme0sowln +0 -0
- data/spec/dummy/tmp/storage/c6/29/c6299u91u1tc5bc7eb24yq8k7xgk +0 -0
- data/spec/dummy/tmp/storage/cb/dh/cbdhbsxm7irfbsrniqvxnfukotka +0 -0
- data/spec/dummy/tmp/storage/dj/zr/djzriitbyq5sebmjs7nf0wqijzl2 +0 -0
- data/spec/dummy/tmp/storage/dk/z6/dkz6yvma826vmeofy924nzacyi6a +0 -0
- data/spec/dummy/tmp/storage/dw/75/dw75t7gej4irzb3hcqssl8w5vhde +0 -0
- data/spec/dummy/tmp/storage/dz/dq/dzdq3gkuy7tdiue1yqmciu86nzfg +0 -0
- data/spec/dummy/tmp/storage/e6/5h/e65haeyr0sglopfuo5chz55thi9z +0 -0
- data/spec/dummy/tmp/storage/ez/kd/ezkdx2jgksxsm9fdwpx5qw8qd6hb +0 -0
- data/spec/dummy/tmp/storage/ft/pz/ftpzprhag9awyjvcac1xiqv9qngf +0 -0
- data/spec/dummy/tmp/storage/g1/fp/g1fprpwzsoltdd9atheqqbmt75w1 +0 -0
- data/spec/dummy/tmp/storage/g6/7r/g67rz6k8b5kmrfsk91e77d37vnfg +0 -0
- data/spec/dummy/tmp/storage/ik/r8/ikr86smhkvxy0yrqxjr0iubcuy5s +0 -0
- data/spec/dummy/tmp/storage/ip/yy/ipyyodp4f221bzp384ncz3alga7l +0 -0
- data/spec/dummy/tmp/storage/j4/ln/j4lnbjjufbp59yxsdetbqs0cyjia +0 -0
- data/spec/dummy/tmp/storage/jc/jk/jcjkdsojvg0n7abwcfpscbkrlvoj +0 -0
- data/spec/dummy/tmp/storage/jd/ik/jdik45lk030wwyyfs9xy6m4q0sv9 +0 -0
- data/spec/dummy/tmp/storage/jo/34/jo34sgi1pzpkbryh3z6bmeccbada +0 -0
- data/spec/dummy/tmp/storage/jw/gm/jwgmfnb1z5jahut7upw5lwnh6orj +0 -0
- data/spec/dummy/tmp/storage/kv/7m/kv7mdo1kvqnagfcp1dit4pzrlugm +0 -0
- data/spec/dummy/tmp/storage/kz/ao/kzao8av3jxls3bjeal7bv2qefcq2 +0 -0
- data/spec/dummy/tmp/storage/l8/1u/l81unawd0p9c61x8k6ofq0y227yt +0 -0
- data/spec/dummy/tmp/storage/lp/iz/lpizuy9o61mj9a6nsp66uhxmwifn +0 -0
- data/spec/dummy/tmp/storage/lu/ge/lugetp9a4lxzimpwttg2bkmrv1tf +0 -0
- data/spec/dummy/tmp/storage/m0/hl/m0hl9jcc1ra6ogfep0yxwon1n8e4 +0 -0
- data/spec/dummy/tmp/storage/md/94/md943ike1q6c3z19js6h89ghdqpd +0 -0
- data/spec/dummy/tmp/storage/mi/y2/miy2op7reeft1521or0hx3sa03qi +0 -0
- data/spec/dummy/tmp/storage/n4/6y/n46y5dyl1uf9ihlyp79fzdxny80f +0 -0
- data/spec/dummy/tmp/storage/ng/sy/ngsy8siky5us2x8cr54uxlzwoji5 +0 -0
- data/spec/dummy/tmp/storage/nq/9r/nq9rfa8upvcoyx92za4fhzrmob0t +0 -0
- data/spec/dummy/tmp/storage/nz/h5/nzh5f9vo9icpp0qvxvvm57ivsy85 +0 -0
- data/spec/dummy/tmp/storage/o3/3e/o33eo1zb3spyqgqbtugs43olucmj +0 -0
- data/spec/dummy/tmp/storage/oc/zy/oczyqb5chp2fvo9kdu62c3y7pl5i +0 -0
- data/spec/dummy/tmp/storage/of/zt/ofzt1knekv9ft8direbw6fiaacvc +0 -0
- data/spec/dummy/tmp/storage/oh/ls/ohlsxs36tio52r2ydrbzez1u0w26 +0 -0
- data/spec/dummy/tmp/storage/ol/u8/olu875ti78gguc82spdkt9z8tpii +0 -0
- data/spec/dummy/tmp/storage/pe/xm/pexmtjvhiiepbza9928j2j0uo6ub +0 -0
- data/spec/dummy/tmp/storage/q4/wp/q4wpnk0ajsmg3nnpj2xja15tc1az +0 -0
- data/spec/dummy/tmp/storage/q7/48/q748a4a7d3qk28vajhbkli5tttsj +0 -0
- data/spec/dummy/tmp/storage/qg/qu/qgquu27ysijim1jv06k39a3c3opr +0 -0
- data/spec/dummy/tmp/storage/qo/zx/qozxdnemk1fwxcvhe1j10ys7mxcq +0 -0
- data/spec/dummy/tmp/storage/qq/vt/qqvtf3h8ge74ce67gghth8ivch0g +0 -0
- data/spec/dummy/tmp/storage/rg/hw/rghwbui1nfqvuuja4hqvzvwofucm +0 -0
- data/spec/dummy/tmp/storage/rj/4a/rj4a0t2xebuf22tyhuitu7x0h0ct +0 -0
- data/spec/dummy/tmp/storage/ro/gg/roggtjpn82v1pf5hi9c72ta6uxt7 +0 -0
- data/spec/dummy/tmp/storage/sd/b5/sdb5dbqjlz1nng8qaejdmh94h5kh +0 -0
- data/spec/dummy/tmp/storage/t6/h0/t6h0whv9denben7ktf5maaohv9dg +0 -0
- data/spec/dummy/tmp/storage/u8/dc/u8dc1at72gpsq6d2fqb81rku7xq9 +0 -0
- data/spec/dummy/tmp/storage/v5/c9/v5c9opt3yikmy2gwg4ftmde0vyt5 +0 -0
- data/spec/dummy/tmp/storage/v8/cs/v8csqmcgvc4z177g5hfmp7twf6ay +0 -0
- data/spec/dummy/tmp/storage/vb/z1/vbz124po6xcx6fbh4nefle8r6of3 +0 -0
- data/spec/dummy/tmp/storage/vc/0v/vc0vb76xhipq8d5xrmqnnvlkbxhi +0 -0
- data/spec/dummy/tmp/storage/vc/eu/vceu8un4vczdune7r9oaihtx3l09 +0 -0
- data/spec/dummy/tmp/storage/vg/qn/vgqnfl62z1s3940ei2s9ifrjufiz +0 -0
- data/spec/dummy/tmp/storage/vz/qz/vzqz49sc3hfzbcaawqw6yuqzpocv +0 -0
- data/spec/dummy/tmp/storage/w1/9e/w19epnmenhmu0y14exloj2oskf53 +0 -0
- data/spec/dummy/tmp/storage/w4/k8/w4k8g0iv6qtdhzyw454un1d048hp +0 -0
- data/spec/dummy/tmp/storage/wv/8i/wv8iai6dxxh2wqwnj667p5rre1tn +0 -0
- data/spec/dummy/tmp/storage/xd/fp/xdfpr9gw2neskzyq6cfxyj2lsa47 +0 -0
- data/spec/dummy/tmp/storage/xh/4e/xh4e0tnk5ysrfndrhyu9u0ywueku +0 -0
- data/spec/dummy/tmp/storage/xj/d2/xjd2s558zk0eduk4f25ul4t29b2r +0 -0
- data/spec/dummy/tmp/storage/yi/r2/yir26w0pcj2268kp8biw0lcrnimz +0 -0
- data/spec/dummy/tmp/storage/yj/d7/yjd7k0ea2d8y6yjp8syfudd0qvhr +0 -0
- data/spec/dummy/tmp/storage/z0/il/z0ilfyp3hmqqn6alkocrt7y949ee +0 -0
- data/spec/dummy/tmp/storage/za/ov/zaov1yasuhakxa5olzoed94khnf8 +0 -0
- data/spec/dummy/tmp/storage/zc/d6/zcd6d4lsb12obz7m7r6240qka8xw +0 -0
- data/spec/dummy/tmp/storage/zc/i3/zci3kt86sywh9q6u7xv7yt38xznu +0 -0
- data/spec/examples.txt +199 -128
- data/spec/factories/snf_core/account_transfers.rb +27 -0
- data/spec/factories/snf_core/addresses.rb +1 -0
- data/spec/factories/snf_core/item_requests.rb +26 -0
- data/spec/factories/snf_core/quotations.rb +10 -0
- data/spec/factories/snf_core/users.rb +10 -1
- data/spec/factories/snf_core/virtual_account_transactions.rb +33 -0
- data/spec/factories/snf_core/virtual_accounts.rb +32 -0
- data/spec/models/snf_core/account_transfer_spec.rb +29 -0
- data/spec/models/snf_core/item_request_spec.rb +44 -0
- data/spec/models/snf_core/quotation_spec.rb +54 -0
- data/spec/models/snf_core/user_spec.rb +18 -22
- data/spec/models/snf_core/virtual_account_spec.rb +85 -0
- data/spec/models/snf_core/virtual_account_transaction_spec.rb +72 -0
- data/spec/requests/snf_core/auth_spec.rb +11 -85
- metadata +133 -2
@@ -0,0 +1,54 @@
|
|
1
|
+
require 'rails_helper'
|
2
|
+
|
3
|
+
module SnfCore
|
4
|
+
RSpec.describe Quotation, type: :model do
|
5
|
+
include ActiveSupport::Testing::TimeHelpers
|
6
|
+
|
7
|
+
attributes = [
|
8
|
+
{ item_request: :belong_to },
|
9
|
+
{ price: [ :presence, :numericality ] },
|
10
|
+
{ valid_until: :presence },
|
11
|
+
{ delivery_date: :presence },
|
12
|
+
{ status: :presence }
|
13
|
+
]
|
14
|
+
include_examples("model_shared_spec", :quotation, attributes)
|
15
|
+
|
16
|
+
describe 'validations' do
|
17
|
+
it 'validates price is greater than 0' do
|
18
|
+
quotation = build(:quotation, price: 0)
|
19
|
+
expect(quotation).not_to be_valid
|
20
|
+
expect(quotation.errors[:price]).to include('must be greater than 0')
|
21
|
+
end
|
22
|
+
|
23
|
+
it 'validates valid_until is not in the past for new records' do
|
24
|
+
quotation = build(:quotation, valid_until: DateTime.yesterday)
|
25
|
+
expect(quotation).not_to be_valid
|
26
|
+
expect(quotation.errors[:valid_until]).to include("can't be in the past")
|
27
|
+
end
|
28
|
+
|
29
|
+
it 'validates delivery_date is not in the past' do
|
30
|
+
quotation = build(:quotation, delivery_date: Date.yesterday)
|
31
|
+
expect(quotation).not_to be_valid
|
32
|
+
expect(quotation.errors[:delivery_date]).to include("can't be in the past")
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
describe 'status management' do
|
37
|
+
let(:quotation) { create(:quotation) }
|
38
|
+
|
39
|
+
it 'starts with pending status' do
|
40
|
+
expect(quotation.status).to eq('pending')
|
41
|
+
end
|
42
|
+
|
43
|
+
it 'allows valid status transitions' do
|
44
|
+
valid_statuses = [ :pending, :accepted, :rejected, :expired ]
|
45
|
+
valid_statuses.each do |status|
|
46
|
+
quotation.status = status
|
47
|
+
expect(quotation).to be_valid
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
|
53
|
+
end
|
54
|
+
end
|
@@ -1,22 +1,18 @@
|
|
1
|
-
require 'rails_helper'
|
2
|
-
|
3
|
-
module SnfCore
|
4
|
-
RSpec.describe User, type: :model do
|
5
|
-
attributes = [
|
6
|
-
{ first_name: :presence },
|
7
|
-
{ middle_name: :presence },
|
8
|
-
{ last_name: :presence },
|
9
|
-
{
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
end
|
20
|
-
end
|
21
|
-
end
|
22
|
-
end
|
1
|
+
require 'rails_helper'
|
2
|
+
|
3
|
+
module SnfCore
|
4
|
+
RSpec.describe User, type: :model do
|
5
|
+
attributes = [
|
6
|
+
{ first_name: :presence },
|
7
|
+
{ middle_name: :presence },
|
8
|
+
{ last_name: :presence },
|
9
|
+
{ address: :belong_to },
|
10
|
+
{ date_of_birth: :presence },
|
11
|
+
{ nationality: :presence },
|
12
|
+
{ phone_number: %i[presence uniqueness] }
|
13
|
+
]
|
14
|
+
include_examples("model_shared_spec", :user, attributes)
|
15
|
+
|
16
|
+
it { is_expected.to belong_to(:verified_by).class_name('User').optional }
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,85 @@
|
|
1
|
+
require 'rails_helper'
|
2
|
+
|
3
|
+
module SnfCore
|
4
|
+
RSpec.describe VirtualAccount, type: :model do
|
5
|
+
attributes = [
|
6
|
+
{ user: :belong_to },
|
7
|
+
{ account_number: %i[presence] },
|
8
|
+
{ cbs_account_number: %i[presence uniqueness] },
|
9
|
+
{ balance: %i[presence numericality] },
|
10
|
+
{ interest_rate: %i[presence numericality] },
|
11
|
+
{ interest_type: :presence },
|
12
|
+
{ branch_code: :presence },
|
13
|
+
{ product_scheme: :presence },
|
14
|
+
{ voucher_type: :presence },
|
15
|
+
{ active: :presence }
|
16
|
+
]
|
17
|
+
include_examples("model_shared_spec", :virtual_account, attributes)
|
18
|
+
|
19
|
+
describe 'validations' do
|
20
|
+
it 'validates interest_rate is between 0 and 1' do
|
21
|
+
virtual_account = build(:virtual_account, interest_rate: 1.5)
|
22
|
+
expect(virtual_account).not_to be_valid
|
23
|
+
expect(virtual_account.errors[:interest_rate]).to include('must be less than or equal to 1')
|
24
|
+
end
|
25
|
+
|
26
|
+
it 'validates branch_code length is 3' do
|
27
|
+
virtual_account = build(:virtual_account, branch_code: '1234')
|
28
|
+
expect(virtual_account).not_to be_valid
|
29
|
+
expect(virtual_account.errors[:branch_code]).to include('is the wrong length (should be 3 characters)')
|
30
|
+
end
|
31
|
+
|
32
|
+
it 'validates product_scheme length is 1' do
|
33
|
+
virtual_account = build(:virtual_account, product_scheme: '12')
|
34
|
+
expect(virtual_account).not_to be_valid
|
35
|
+
expect(virtual_account.errors[:product_scheme]).to include('is the wrong length (should be 1 character)')
|
36
|
+
end
|
37
|
+
|
38
|
+
it 'validates voucher_type length is 1' do
|
39
|
+
virtual_account = build(:virtual_account, voucher_type: '12')
|
40
|
+
expect(virtual_account).not_to be_valid
|
41
|
+
expect(virtual_account.errors[:voucher_type]).to include('is the wrong length (should be 1 character)')
|
42
|
+
end
|
43
|
+
|
44
|
+
it 'validates account_number uniqueness case sensitively' do
|
45
|
+
existing_account = create(:virtual_account)
|
46
|
+
new_account = build(:virtual_account, account_number: existing_account.account_number)
|
47
|
+
|
48
|
+
expect(new_account).not_to be_valid
|
49
|
+
expect(new_account.errors[:account_number]).to include('has already been taken')
|
50
|
+
end
|
51
|
+
|
52
|
+
it 'validates account_number format' do
|
53
|
+
virtual_account = build(:virtual_account, account_number: '12345678901')
|
54
|
+
expect(virtual_account.account_number).to match(/\A\d{3}\d\d\d{6}\z/)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
describe 'account_number generation' do
|
59
|
+
it 'generates account number before validation' do
|
60
|
+
virtual_account = create(:virtual_account)
|
61
|
+
expect(virtual_account.account_number).to be_present
|
62
|
+
expect(virtual_account.account_number).to match(/\A\d{3}\d\d\d{6}\z/)
|
63
|
+
end
|
64
|
+
|
65
|
+
it 'uses branch_code, product_scheme, and voucher_type in account number' do
|
66
|
+
virtual_account = create(:virtual_account,
|
67
|
+
branch_code: '002',
|
68
|
+
product_scheme: '3',
|
69
|
+
voucher_type: '5'
|
70
|
+
)
|
71
|
+
expect(virtual_account.account_number).to start_with('00235')
|
72
|
+
end
|
73
|
+
|
74
|
+
it 'increments sequence number for each new account' do
|
75
|
+
first_account = create(:virtual_account)
|
76
|
+
second_account = create(:virtual_account)
|
77
|
+
|
78
|
+
first_seq = first_account.account_number[-6..-1].to_i
|
79
|
+
second_seq = second_account.account_number[-6..-1].to_i
|
80
|
+
|
81
|
+
expect(second_seq).to be > first_seq
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
@@ -0,0 +1,72 @@
|
|
1
|
+
require 'rails_helper'
|
2
|
+
|
3
|
+
module SnfCore
|
4
|
+
RSpec.describe VirtualAccountTransaction, type: :model do
|
5
|
+
attributes = [
|
6
|
+
{ amount: %i[presence numericality] },
|
7
|
+
{ reference_number: %i[presence uniqueness] },
|
8
|
+
{ transaction_type: :presence },
|
9
|
+
{ status: :presence }
|
10
|
+
]
|
11
|
+
include_examples("model_shared_spec", :virtual_account_transaction, attributes)
|
12
|
+
|
13
|
+
describe 'validations' do
|
14
|
+
describe 'transaction type validations' do
|
15
|
+
it 'requires both accounts for transfer' do
|
16
|
+
transaction = build(:virtual_account_transaction, :transfer, from_account: nil)
|
17
|
+
expect(transaction).not_to be_valid
|
18
|
+
expect(transaction.errors[:base]).to include('Transfer requires both from and to accounts')
|
19
|
+
end
|
20
|
+
|
21
|
+
it 'requires to_account for deposit' do
|
22
|
+
transaction = build(:virtual_account_transaction, :deposit, to_account: nil)
|
23
|
+
expect(transaction).not_to be_valid
|
24
|
+
expect(transaction.errors[:base]).to include('Deposit requires to_account')
|
25
|
+
end
|
26
|
+
|
27
|
+
it 'requires from_account for withdrawal' do
|
28
|
+
transaction = build(:virtual_account_transaction, :withdrawal, from_account: nil)
|
29
|
+
expect(transaction).not_to be_valid
|
30
|
+
expect(transaction.errors[:base]).to include('Withdrawal requires from_account')
|
31
|
+
end
|
32
|
+
|
33
|
+
it 'prevents transfer to same account' do
|
34
|
+
account = create(:virtual_account)
|
35
|
+
transaction = build(:virtual_account_transaction, from_account: account, to_account: account)
|
36
|
+
expect(transaction).not_to be_valid
|
37
|
+
expect(transaction.errors[:base]).to include('Cannot transfer to same account')
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
describe 'balance validation' do
|
42
|
+
it 'validates sufficient balance for transfer' do
|
43
|
+
from_account = create(:virtual_account, balance: 100)
|
44
|
+
transaction = build(:virtual_account_transaction,
|
45
|
+
from_account: from_account,
|
46
|
+
amount: 150
|
47
|
+
)
|
48
|
+
expect(transaction).not_to be_valid
|
49
|
+
expect(transaction.errors[:amount]).to include('Insufficient balance')
|
50
|
+
end
|
51
|
+
|
52
|
+
it 'validates sufficient balance for withdrawal' do
|
53
|
+
from_account = create(:virtual_account, balance: 100)
|
54
|
+
transaction = build(:virtual_account_transaction, :withdrawal,
|
55
|
+
from_account: from_account,
|
56
|
+
amount: 150
|
57
|
+
)
|
58
|
+
expect(transaction).not_to be_valid
|
59
|
+
expect(transaction.errors[:amount]).to include('Insufficient balance')
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
describe 'transaction types' do
|
65
|
+
it { is_expected.to define_enum_for(:transaction_type).with_values(transfer: 0, deposit: 1, withdrawal: 2) }
|
66
|
+
end
|
67
|
+
|
68
|
+
describe 'status types' do
|
69
|
+
it { is_expected.to define_enum_for(:status).with_values(pending: 0, completed: 1, failed: 2, reversed: 3) }
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
@@ -17,7 +17,17 @@ module SnfCore
|
|
17
17
|
password: Faker::Internet.password(min_length: 8),
|
18
18
|
business_name: Faker::Company.name,
|
19
19
|
tin_number: Faker::Company.ein,
|
20
|
-
business_type: "retailer"
|
20
|
+
business_type: "retailer",
|
21
|
+
date_of_birth: Date.today,
|
22
|
+
gender: "male",
|
23
|
+
verified_at: DateTime.now,
|
24
|
+
verified_by_id: nil,
|
25
|
+
address_id: create(:address).id,
|
26
|
+
nationality: Faker::Address.country,
|
27
|
+
occupation: Faker::Job.title,
|
28
|
+
source_of_funds: "Salary",
|
29
|
+
kyc_status: "pending"
|
30
|
+
}
|
21
31
|
}
|
22
32
|
end
|
23
33
|
|
@@ -58,90 +68,6 @@ module SnfCore
|
|
58
68
|
end
|
59
69
|
end
|
60
70
|
|
61
|
-
describe "POST /reset_password_request" do
|
62
|
-
context 'with invalid email' do
|
63
|
-
it 'returns error for non-existent user' do
|
64
|
-
post '/snf_core/auth/reset_password_request', params: { auth: { email: 'nonexistent@email.com' } }
|
65
|
-
expect(response).to have_http_status(:unauthorized)
|
66
|
-
expect(json_response[:success]).to be false
|
67
|
-
expect(json_response[:error]).to eq("User doesn't exist")
|
68
|
-
end
|
69
|
-
end
|
70
|
-
end
|
71
|
-
|
72
|
-
describe "POST /reset_password" do
|
73
|
-
let(:reset_token) { SecureRandom.hex }
|
74
|
-
before { user.update(reset_password_token: reset_token) }
|
75
|
-
|
76
|
-
context 'with valid token' do
|
77
|
-
it 'successfully resets password' do
|
78
|
-
post '/snf_core/auth/reset_password', params: {
|
79
|
-
auth: {
|
80
|
-
reset_password_token: reset_token,
|
81
|
-
password: 'new_password123'
|
82
|
-
}
|
83
|
-
}
|
84
|
-
|
85
|
-
expect(response).to have_http_status(:success)
|
86
|
-
expect(json_response[:success]).to be true
|
87
|
-
expect(user.reload.reset_password_token).to be_nil
|
88
|
-
expect(user.password_changed).to be true
|
89
|
-
end
|
90
|
-
end
|
91
|
-
|
92
|
-
context 'with invalid token' do
|
93
|
-
it 'returns error for invalid token' do
|
94
|
-
post '/snf_core/auth/reset_password', params: {
|
95
|
-
auth: {
|
96
|
-
reset_password_token: 'invalid_token',
|
97
|
-
password: 'new_password123'
|
98
|
-
}
|
99
|
-
}
|
100
|
-
|
101
|
-
expect(response).to have_http_status(:unauthorized)
|
102
|
-
expect(json_response[:success]).to be false
|
103
|
-
end
|
104
|
-
end
|
105
|
-
end
|
106
|
-
|
107
|
-
describe "POST /change_password" do
|
108
|
-
let(:auth_token) { TokenService.new.encode({ user: user.as_json, role: role.name }) }
|
109
|
-
|
110
|
-
before do
|
111
|
-
allow_any_instance_of(ApplicationController).to receive(:current_user).and_return(user)
|
112
|
-
end
|
113
|
-
|
114
|
-
context 'with valid current password' do
|
115
|
-
it 'successfully changes password' do
|
116
|
-
post '/snf_core/auth/change_password', params: {
|
117
|
-
auth: {
|
118
|
-
password: 'password123',
|
119
|
-
new_password: 'new_password123'
|
120
|
-
}
|
121
|
-
}, headers: { 'Authorization': "Bearer #{auth_token}" }
|
122
|
-
|
123
|
-
expect(response).to have_http_status(:success)
|
124
|
-
expect(json_response[:success]).to be true
|
125
|
-
expect(user.reload.password_changed).to be true
|
126
|
-
end
|
127
|
-
end
|
128
|
-
|
129
|
-
context 'with invalid current password' do
|
130
|
-
it 'returns error for wrong current password' do
|
131
|
-
post '/snf_core/auth/change_password', params: {
|
132
|
-
auth: {
|
133
|
-
password: 'wrong_password',
|
134
|
-
new_password: 'new_password123'
|
135
|
-
}
|
136
|
-
}, headers: { 'Authorization': "Bearer #{auth_token}" }
|
137
|
-
|
138
|
-
expect(response).to have_http_status(:success)
|
139
|
-
expect(json_response[:success]).to be false
|
140
|
-
expect(json_response[:error]).to eq('Invalid password')
|
141
|
-
end
|
142
|
-
end
|
143
|
-
end
|
144
|
-
|
145
71
|
|
146
72
|
describe "POST /signup" do
|
147
73
|
context 'with valid parameters' do
|