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.
Files changed (145) hide show
  1. checksums.yaml +4 -4
  2. data/app/controllers/snf_core/auth_controller.rb +12 -44
  3. data/app/models/snf_core/account_transfer.rb +37 -0
  4. data/app/models/snf_core/item_request.rb +27 -0
  5. data/app/models/snf_core/quotation.rb +41 -0
  6. data/app/models/snf_core/user.rb +18 -18
  7. data/app/models/snf_core/virtual_account.rb +41 -0
  8. data/app/models/snf_core/virtual_account_transaction.rb +52 -0
  9. data/config/routes.rb +0 -3
  10. data/db/migrate/20250310122954_create_snf_core_virtual_accounts.rb +22 -0
  11. data/db/migrate/20250310123127_add_kyc_attributes_to_user.rb +17 -0
  12. data/db/migrate/20250310133249_create_snf_core_virtual_account_transactions.rb +18 -0
  13. data/db/migrate/20250310143604_create_snf_core_item_requests.rb +14 -0
  14. data/db/migrate/20250311000000_create_snf_core_account_transfers.rb +21 -0
  15. data/db/migrate/20250312043359_create_snf_core_quotations.rb +14 -0
  16. data/lib/snf_core/version.rb +1 -1
  17. data/spec/dummy/db/schema.rb +130 -27
  18. data/spec/dummy/log/development.log +2618 -0
  19. data/spec/dummy/log/test.log +110127 -0
  20. data/spec/dummy/tmp/storage/00/ch/00ch4m884d5bnelqgd2ld1qig6ey +0 -0
  21. data/spec/dummy/tmp/storage/0s/ba/0sbase6mg5sgich6or97cg1faxlw +0 -0
  22. data/spec/dummy/tmp/storage/0x/uy/0xuyfqa0t08zr8vfm8p123o0s34t +0 -0
  23. data/spec/dummy/tmp/storage/1o/ts/1otsbrqkd6n4qiht1wabl7fhjnt3 +0 -0
  24. data/spec/dummy/tmp/storage/1v/54/1v542i134jpb9iwbq9m1v801y0ls +0 -0
  25. data/spec/dummy/tmp/storage/1y/46/1y4617uv2nrk9fa58zfv9xyixmfk +0 -0
  26. data/spec/dummy/tmp/storage/24/kq/24kq40q48k7jry3npyah0td4uend +0 -0
  27. data/spec/dummy/tmp/storage/28/ye/28yeusf04wxopqkg0bxki7mqo9ph +0 -0
  28. data/spec/dummy/tmp/storage/2r/hv/2rhvs6uca2ozrojfuub5d4qk0ugy +0 -0
  29. data/spec/dummy/tmp/storage/2w/r1/2wr14buxnrx4cee19gj6sefnbby5 +0 -0
  30. data/spec/dummy/tmp/storage/3k/7z/3k7zlcd9fm2jwn99kinxpahrmm4u +0 -0
  31. data/spec/dummy/tmp/storage/3o/2a/3o2ahddfby8aqgutow91vo4asm4f +0 -0
  32. data/spec/dummy/tmp/storage/46/zj/46zjkljy84qmg4ebes40e2zolzp7 +0 -0
  33. data/spec/dummy/tmp/storage/4f/fl/4fflxsg2pissq4960wfazyv9mgp1 +0 -0
  34. data/spec/dummy/tmp/storage/4l/6a/4l6ap3t705ektu02kabqobaq8huf +0 -0
  35. data/spec/dummy/tmp/storage/4m/3n/4m3n3r5zl2jevvvw3alq0omnnowk +0 -0
  36. data/spec/dummy/tmp/storage/4t/qc/4tqc587gmu2g7hgntrgjnhkw0e1u +0 -0
  37. data/spec/dummy/tmp/storage/4w/76/4w7656628v1p4mp87fqz3okc6ix4 +0 -0
  38. data/spec/dummy/tmp/storage/50/ct/50ctjwt8y93spjaef1b3rfnav9gb +0 -0
  39. data/spec/dummy/tmp/storage/5m/0k/5m0k5zx08ouqos7rs09bpgmalspu +0 -0
  40. data/spec/dummy/tmp/storage/5m/4n/5m4nvbe3vqwrdpy6hujr1oq5nolv +0 -0
  41. data/spec/dummy/tmp/storage/5m/j8/5mj8ypl6zxx8g3h7vqmu4p2la4pk +0 -0
  42. data/spec/dummy/tmp/storage/5r/tv/5rtv2agppt9e52eg0oc81svs896e +0 -0
  43. data/spec/dummy/tmp/storage/5s/b6/5sb6kux9jtdj4du9ihu32tc82s12 +0 -0
  44. data/spec/dummy/tmp/storage/5t/84/5t84ncnwqnv83rndaztqz1dsae3v +0 -0
  45. data/spec/dummy/tmp/storage/6h/9a/6h9a6bxecnlwx7zbb4dc33rmftee +0 -0
  46. data/spec/dummy/tmp/storage/6h/pd/6hpdw0r7w6vpcotirwqz1fv640kk +0 -0
  47. data/spec/dummy/tmp/storage/6i/dm/6idmnfdfriqdmvcvgreyeho4xgra +0 -0
  48. data/spec/dummy/tmp/storage/6n/3b/6n3bufdfx1tvls9lzm3g625l1lyk +0 -0
  49. data/spec/dummy/tmp/storage/78/5u/785ub96ra353qygeubpq4vlifjas +0 -0
  50. data/spec/dummy/tmp/storage/7v/55/7v55c4y5xdiw6qrvbdpmd63v4toz +0 -0
  51. data/spec/dummy/tmp/storage/83/yi/83yiflnj2evgqahmzgiavbywle7h +0 -0
  52. data/spec/dummy/tmp/storage/8a/02/8a02q8tcm3lowda8cvcjh18tka7i +0 -0
  53. data/spec/dummy/tmp/storage/8g/4a/8g4aarquz0mos9gxzq6j7xzwli09 +0 -0
  54. data/spec/dummy/tmp/storage/8k/0m/8k0mv7xql5lr89d0172izqplypwx +0 -0
  55. data/spec/dummy/tmp/storage/8n/lz/8nlzdb800dgr9iax1ez2ko4euh02 +0 -0
  56. data/spec/dummy/tmp/storage/8y/y2/8yy2kqz8sa11vh7naiomyofj30ox +0 -0
  57. data/spec/dummy/tmp/storage/92/hz/92hz15r26fs5wymc6tced4b08bh6 +0 -0
  58. data/spec/dummy/tmp/storage/9k/wj/9kwjdz4yw0jyzddj32u97xciot47 +0 -0
  59. data/spec/dummy/tmp/storage/a0/ve/a0veqfafcpy1hl6jvg6jc4qt08kr +0 -0
  60. data/spec/dummy/tmp/storage/ak/g9/akg9bg7fdwisycbclvmikbzl676j +0 -0
  61. data/spec/dummy/tmp/storage/ap/fs/apfsy6cp4mt4jsyiyscutqhv36id +0 -0
  62. data/spec/dummy/tmp/storage/b2/ux/b2uxv1pu0hjqty7779grby8qxzoo +0 -0
  63. data/spec/dummy/tmp/storage/bh/s1/bhs1yc1jo5vhc1u8lz7zme0sowln +0 -0
  64. data/spec/dummy/tmp/storage/c6/29/c6299u91u1tc5bc7eb24yq8k7xgk +0 -0
  65. data/spec/dummy/tmp/storage/cb/dh/cbdhbsxm7irfbsrniqvxnfukotka +0 -0
  66. data/spec/dummy/tmp/storage/dj/zr/djzriitbyq5sebmjs7nf0wqijzl2 +0 -0
  67. data/spec/dummy/tmp/storage/dk/z6/dkz6yvma826vmeofy924nzacyi6a +0 -0
  68. data/spec/dummy/tmp/storage/dw/75/dw75t7gej4irzb3hcqssl8w5vhde +0 -0
  69. data/spec/dummy/tmp/storage/dz/dq/dzdq3gkuy7tdiue1yqmciu86nzfg +0 -0
  70. data/spec/dummy/tmp/storage/e6/5h/e65haeyr0sglopfuo5chz55thi9z +0 -0
  71. data/spec/dummy/tmp/storage/ez/kd/ezkdx2jgksxsm9fdwpx5qw8qd6hb +0 -0
  72. data/spec/dummy/tmp/storage/ft/pz/ftpzprhag9awyjvcac1xiqv9qngf +0 -0
  73. data/spec/dummy/tmp/storage/g1/fp/g1fprpwzsoltdd9atheqqbmt75w1 +0 -0
  74. data/spec/dummy/tmp/storage/g6/7r/g67rz6k8b5kmrfsk91e77d37vnfg +0 -0
  75. data/spec/dummy/tmp/storage/ik/r8/ikr86smhkvxy0yrqxjr0iubcuy5s +0 -0
  76. data/spec/dummy/tmp/storage/ip/yy/ipyyodp4f221bzp384ncz3alga7l +0 -0
  77. data/spec/dummy/tmp/storage/j4/ln/j4lnbjjufbp59yxsdetbqs0cyjia +0 -0
  78. data/spec/dummy/tmp/storage/jc/jk/jcjkdsojvg0n7abwcfpscbkrlvoj +0 -0
  79. data/spec/dummy/tmp/storage/jd/ik/jdik45lk030wwyyfs9xy6m4q0sv9 +0 -0
  80. data/spec/dummy/tmp/storage/jo/34/jo34sgi1pzpkbryh3z6bmeccbada +0 -0
  81. data/spec/dummy/tmp/storage/jw/gm/jwgmfnb1z5jahut7upw5lwnh6orj +0 -0
  82. data/spec/dummy/tmp/storage/kv/7m/kv7mdo1kvqnagfcp1dit4pzrlugm +0 -0
  83. data/spec/dummy/tmp/storage/kz/ao/kzao8av3jxls3bjeal7bv2qefcq2 +0 -0
  84. data/spec/dummy/tmp/storage/l8/1u/l81unawd0p9c61x8k6ofq0y227yt +0 -0
  85. data/spec/dummy/tmp/storage/lp/iz/lpizuy9o61mj9a6nsp66uhxmwifn +0 -0
  86. data/spec/dummy/tmp/storage/lu/ge/lugetp9a4lxzimpwttg2bkmrv1tf +0 -0
  87. data/spec/dummy/tmp/storage/m0/hl/m0hl9jcc1ra6ogfep0yxwon1n8e4 +0 -0
  88. data/spec/dummy/tmp/storage/md/94/md943ike1q6c3z19js6h89ghdqpd +0 -0
  89. data/spec/dummy/tmp/storage/mi/y2/miy2op7reeft1521or0hx3sa03qi +0 -0
  90. data/spec/dummy/tmp/storage/n4/6y/n46y5dyl1uf9ihlyp79fzdxny80f +0 -0
  91. data/spec/dummy/tmp/storage/ng/sy/ngsy8siky5us2x8cr54uxlzwoji5 +0 -0
  92. data/spec/dummy/tmp/storage/nq/9r/nq9rfa8upvcoyx92za4fhzrmob0t +0 -0
  93. data/spec/dummy/tmp/storage/nz/h5/nzh5f9vo9icpp0qvxvvm57ivsy85 +0 -0
  94. data/spec/dummy/tmp/storage/o3/3e/o33eo1zb3spyqgqbtugs43olucmj +0 -0
  95. data/spec/dummy/tmp/storage/oc/zy/oczyqb5chp2fvo9kdu62c3y7pl5i +0 -0
  96. data/spec/dummy/tmp/storage/of/zt/ofzt1knekv9ft8direbw6fiaacvc +0 -0
  97. data/spec/dummy/tmp/storage/oh/ls/ohlsxs36tio52r2ydrbzez1u0w26 +0 -0
  98. data/spec/dummy/tmp/storage/ol/u8/olu875ti78gguc82spdkt9z8tpii +0 -0
  99. data/spec/dummy/tmp/storage/pe/xm/pexmtjvhiiepbza9928j2j0uo6ub +0 -0
  100. data/spec/dummy/tmp/storage/q4/wp/q4wpnk0ajsmg3nnpj2xja15tc1az +0 -0
  101. data/spec/dummy/tmp/storage/q7/48/q748a4a7d3qk28vajhbkli5tttsj +0 -0
  102. data/spec/dummy/tmp/storage/qg/qu/qgquu27ysijim1jv06k39a3c3opr +0 -0
  103. data/spec/dummy/tmp/storage/qo/zx/qozxdnemk1fwxcvhe1j10ys7mxcq +0 -0
  104. data/spec/dummy/tmp/storage/qq/vt/qqvtf3h8ge74ce67gghth8ivch0g +0 -0
  105. data/spec/dummy/tmp/storage/rg/hw/rghwbui1nfqvuuja4hqvzvwofucm +0 -0
  106. data/spec/dummy/tmp/storage/rj/4a/rj4a0t2xebuf22tyhuitu7x0h0ct +0 -0
  107. data/spec/dummy/tmp/storage/ro/gg/roggtjpn82v1pf5hi9c72ta6uxt7 +0 -0
  108. data/spec/dummy/tmp/storage/sd/b5/sdb5dbqjlz1nng8qaejdmh94h5kh +0 -0
  109. data/spec/dummy/tmp/storage/t6/h0/t6h0whv9denben7ktf5maaohv9dg +0 -0
  110. data/spec/dummy/tmp/storage/u8/dc/u8dc1at72gpsq6d2fqb81rku7xq9 +0 -0
  111. data/spec/dummy/tmp/storage/v5/c9/v5c9opt3yikmy2gwg4ftmde0vyt5 +0 -0
  112. data/spec/dummy/tmp/storage/v8/cs/v8csqmcgvc4z177g5hfmp7twf6ay +0 -0
  113. data/spec/dummy/tmp/storage/vb/z1/vbz124po6xcx6fbh4nefle8r6of3 +0 -0
  114. data/spec/dummy/tmp/storage/vc/0v/vc0vb76xhipq8d5xrmqnnvlkbxhi +0 -0
  115. data/spec/dummy/tmp/storage/vc/eu/vceu8un4vczdune7r9oaihtx3l09 +0 -0
  116. data/spec/dummy/tmp/storage/vg/qn/vgqnfl62z1s3940ei2s9ifrjufiz +0 -0
  117. data/spec/dummy/tmp/storage/vz/qz/vzqz49sc3hfzbcaawqw6yuqzpocv +0 -0
  118. data/spec/dummy/tmp/storage/w1/9e/w19epnmenhmu0y14exloj2oskf53 +0 -0
  119. data/spec/dummy/tmp/storage/w4/k8/w4k8g0iv6qtdhzyw454un1d048hp +0 -0
  120. data/spec/dummy/tmp/storage/wv/8i/wv8iai6dxxh2wqwnj667p5rre1tn +0 -0
  121. data/spec/dummy/tmp/storage/xd/fp/xdfpr9gw2neskzyq6cfxyj2lsa47 +0 -0
  122. data/spec/dummy/tmp/storage/xh/4e/xh4e0tnk5ysrfndrhyu9u0ywueku +0 -0
  123. data/spec/dummy/tmp/storage/xj/d2/xjd2s558zk0eduk4f25ul4t29b2r +0 -0
  124. data/spec/dummy/tmp/storage/yi/r2/yir26w0pcj2268kp8biw0lcrnimz +0 -0
  125. data/spec/dummy/tmp/storage/yj/d7/yjd7k0ea2d8y6yjp8syfudd0qvhr +0 -0
  126. data/spec/dummy/tmp/storage/z0/il/z0ilfyp3hmqqn6alkocrt7y949ee +0 -0
  127. data/spec/dummy/tmp/storage/za/ov/zaov1yasuhakxa5olzoed94khnf8 +0 -0
  128. data/spec/dummy/tmp/storage/zc/d6/zcd6d4lsb12obz7m7r6240qka8xw +0 -0
  129. data/spec/dummy/tmp/storage/zc/i3/zci3kt86sywh9q6u7xv7yt38xznu +0 -0
  130. data/spec/examples.txt +199 -128
  131. data/spec/factories/snf_core/account_transfers.rb +27 -0
  132. data/spec/factories/snf_core/addresses.rb +1 -0
  133. data/spec/factories/snf_core/item_requests.rb +26 -0
  134. data/spec/factories/snf_core/quotations.rb +10 -0
  135. data/spec/factories/snf_core/users.rb +10 -1
  136. data/spec/factories/snf_core/virtual_account_transactions.rb +33 -0
  137. data/spec/factories/snf_core/virtual_accounts.rb +32 -0
  138. data/spec/models/snf_core/account_transfer_spec.rb +29 -0
  139. data/spec/models/snf_core/item_request_spec.rb +44 -0
  140. data/spec/models/snf_core/quotation_spec.rb +54 -0
  141. data/spec/models/snf_core/user_spec.rb +18 -22
  142. data/spec/models/snf_core/virtual_account_spec.rb +85 -0
  143. data/spec/models/snf_core/virtual_account_transaction_spec.rb +72 -0
  144. data/spec/requests/snf_core/auth_spec.rb +11 -85
  145. 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
- { phone_number: %i[presence uniqueness] }
10
- ]
11
- include_examples("model_shared_spec", :user, attributes)
12
-
13
- describe 'wallet creation' do
14
- it 'creates a wallet automatically for the user' do
15
- user = create(:user)
16
- expect(user.wallet).to be_present
17
- expect(user.wallet.balance).to eq(0.0)
18
- expect(user.wallet.is_active).to be_truthy
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