punk 0.1.4 → 0.2.0

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 (143) hide show
  1. checksums.yaml +4 -4
  2. data/.editorconfig +9 -0
  3. data/.github/workflows/test.yml +26 -1
  4. data/.rdoc_options +23 -0
  5. data/.rgignore +1 -0
  6. data/.rspec +2 -0
  7. data/Gemfile +5 -6
  8. data/Gemfile.lock +16 -29
  9. data/README.md +1 -1
  10. data/VERSION +1 -1
  11. data/app/migrations/001_lets_punk.rb +3 -0
  12. data/app/routes/hello.rb +4 -0
  13. data/env/.gitignore +3 -0
  14. data/env/spec/test.sh +3 -0
  15. data/lib/punk/actions/.keep +0 -0
  16. data/lib/punk/actions/groups/list.rb +24 -0
  17. data/lib/punk/actions/sessions/clear.rb +21 -0
  18. data/lib/punk/actions/sessions/create.rb +64 -0
  19. data/lib/punk/actions/sessions/list.rb +18 -0
  20. data/lib/punk/actions/sessions/verify.rb +24 -0
  21. data/lib/punk/actions/tenants/list.rb +18 -0
  22. data/lib/punk/actions/users/list_group.rb +18 -0
  23. data/lib/punk/actions/users/list_tenant.rb +18 -0
  24. data/lib/punk/actions/users/show.rb +18 -0
  25. data/lib/punk/commands/list.rb +12 -6
  26. data/lib/punk/config/defaults.json +3 -0
  27. data/lib/punk/config/schema.json +3 -0
  28. data/lib/punk/core/app.rb +4 -6
  29. data/lib/punk/core/commander.rb +7 -4
  30. data/lib/punk/core/exec.rb +2 -0
  31. data/lib/punk/core/load.rb +0 -1
  32. data/lib/punk/framework/command.rb +5 -1
  33. data/lib/punk/framework/plugins/validation.rb +0 -14
  34. data/lib/punk/helpers/loggable.rb +1 -1
  35. data/lib/punk/migrations/001_punk.rb +103 -0
  36. data/lib/punk/models/.keep +0 -0
  37. data/lib/punk/models/group.rb +20 -0
  38. data/lib/punk/models/group_user_metadata.rb +17 -0
  39. data/lib/punk/models/identity.rb +29 -0
  40. data/lib/punk/models/session.rb +89 -0
  41. data/lib/punk/models/tenant.rb +19 -0
  42. data/lib/punk/models/tenant_user_metadata.rb +17 -0
  43. data/lib/punk/models/user.rb +31 -0
  44. data/lib/punk/routes/groups.rb +31 -0
  45. data/lib/punk/routes/plivo.rb +4 -0
  46. data/lib/punk/routes/sessions.rb +108 -0
  47. data/lib/punk/routes/swagger.rb +9 -0
  48. data/lib/punk/routes/tenants.rb +29 -0
  49. data/lib/punk/routes/users.rb +36 -0
  50. data/lib/punk/services/.keep +0 -0
  51. data/lib/punk/services/challenge_claim.rb +46 -0
  52. data/lib/punk/services/create_identities.rb +25 -0
  53. data/lib/punk/services/generate_swagger.rb +25 -0
  54. data/lib/punk/services/prove_claim.rb +29 -0
  55. data/lib/punk/services/secret.rb +9 -0
  56. data/lib/punk/templates/groups/list.jbuilder +7 -0
  57. data/lib/punk/templates/plivo.slim +16 -0
  58. data/lib/punk/templates/sessions/list.jbuilder +6 -0
  59. data/lib/punk/templates/sessions/pending.jbuilder +4 -0
  60. data/lib/punk/templates/tenants/list.jbuilder +7 -0
  61. data/lib/punk/templates/tenants/list.slim +8 -0
  62. data/lib/punk/templates/users/list.jbuilder +7 -0
  63. data/lib/punk/templates/users/list.rcsv +4 -0
  64. data/lib/punk/templates/users/show.jbuilder +5 -0
  65. data/lib/punk/views/groups/list.rb +22 -0
  66. data/lib/punk/views/plivo_store.rb +15 -0
  67. data/lib/punk/views/sessions/list.rb +22 -0
  68. data/lib/punk/views/sessions/pending.rb +28 -0
  69. data/lib/punk/views/tenants/list.rb +22 -0
  70. data/lib/punk/views/users/list.rb +22 -0
  71. data/lib/punk/views/users/show.rb +22 -0
  72. data/lib/punk/workers/.keep +0 -0
  73. data/lib/punk/workers/expire_sessions.rb +9 -0
  74. data/lib/punk/workers/geocode_session_worker.rb +48 -0
  75. data/lib/punk/workers/identify_session_worker.rb +45 -0
  76. data/lib/punk/workers/secret.rb +18 -0
  77. data/lib/punk/workers/send_email_worker.rb +51 -0
  78. data/lib/punk/workers/send_sms_worker.rb +40 -0
  79. data/punk.gemspec +140 -14
  80. data/schema.psql +345 -0
  81. data/spec/actions/groups/punk/list_groups_action_spec.rb +36 -0
  82. data/spec/actions/sessions/punk/clear_session_action_spec.rb +29 -0
  83. data/spec/actions/sessions/punk/create_session_action_spec.rb +33 -0
  84. data/spec/actions/sessions/punk/list_sessions_action_spec.rb +26 -0
  85. data/spec/actions/sessions/punk/verify_session_action_spec.rb +59 -0
  86. data/spec/actions/tenants/punk/list_tenants_action_spec.rb +25 -0
  87. data/spec/actions/users/punk/list_group_users_action_spec.rb +26 -0
  88. data/spec/actions/users/punk/list_tenant_users_action_spec.rb +26 -0
  89. data/spec/factories/group.rb +12 -0
  90. data/spec/factories/group_user_metadata.rb +10 -0
  91. data/spec/factories/identity.rb +19 -0
  92. data/spec/factories/session.rb +12 -0
  93. data/spec/factories/tenant.rb +10 -0
  94. data/spec/factories/tenant_user_metadata.rb +10 -0
  95. data/spec/factories/user.rb +12 -0
  96. data/spec/lib/commands/auth_spec.rb +11 -0
  97. data/spec/lib/commands/generate_spec.rb +7 -0
  98. data/spec/lib/commands/http_spec.rb +23 -0
  99. data/spec/lib/commands/list_spec.rb +7 -0
  100. data/spec/lib/commands/swagger_spec.rb +7 -0
  101. data/spec/lib/engine/punk_env_spec.rb +13 -0
  102. data/spec/lib/engine/punk_exec_spec.rb +9 -0
  103. data/spec/lib/engine/punk_init_spec.rb +9 -0
  104. data/spec/lib/engine/punk_store_spec.rb +10 -0
  105. data/spec/lib/punk.env +7 -0
  106. data/spec/models/punk/group_spec.rb +50 -0
  107. data/spec/models/punk/group_user_metadata_spec.rb +61 -0
  108. data/spec/models/punk/identity_spec.rb +61 -0
  109. data/spec/models/punk/session_spec.rb +156 -0
  110. data/spec/models/punk/tenant_spec.rb +51 -0
  111. data/spec/models/punk/tenant_user_metadata_spec.rb +61 -0
  112. data/spec/models/punk/user_spec.rb +115 -0
  113. data/spec/routes/groups/get_groups_spec.rb +33 -0
  114. data/spec/routes/plivo/get_plivo_spec.rb +11 -0
  115. data/spec/routes/sessions/delete_session_spec.rb +11 -0
  116. data/spec/routes/sessions/get_sessions_spec.rb +30 -0
  117. data/spec/routes/sessions/patch_session_spec.rb +11 -0
  118. data/spec/routes/sessions/post_session_spec.rb +11 -0
  119. data/spec/routes/swagger/get_swagger_spec.rb +12 -0
  120. data/spec/routes/tenants/get_tenants_spec.rb +31 -0
  121. data/spec/routes/users/get_users_spec.rb +60 -0
  122. data/spec/services/punk/challenge_claim_service_spec.rb +7 -0
  123. data/spec/services/punk/create_identities_service_spec.rb +14 -0
  124. data/spec/services/punk/generate_swagger_service_spec.rb +7 -0
  125. data/spec/services/punk/prove_claim_service_spec.rb +7 -0
  126. data/spec/services/punk/secret_service_spec.rb +7 -0
  127. data/spec/spec_helper.rb +122 -0
  128. data/spec/vcr_cassettes/PUNK_GeocodeSessionWorker/updates_the_session_data.yml +57 -0
  129. data/spec/vcr_cassettes/PUNK_IdentifySessionWorker/updates_the_session_data.yml +112 -0
  130. data/spec/views/punk/plivo_store_spec.rb +7 -0
  131. data/spec/views/sessions/punk/list_sessions_view_spec.rb +7 -0
  132. data/spec/views/sessions/punk/pending_session_view_spec.rb +7 -0
  133. data/spec/views/tenants/punk/list_tenants_view_spec.rb +7 -0
  134. data/spec/views/users/punk/list_groups_view_spec.rb +7 -0
  135. data/spec/views/users/punk/list_users_view_spec.rb +7 -0
  136. data/spec/workers/punk/expire_sessions_worker_spec.rb +31 -0
  137. data/spec/workers/punk/geocode_session_worker_spec.rb +14 -0
  138. data/spec/workers/punk/identify_session_worker_spec.rb +15 -0
  139. data/spec/workers/punk/secret_worker_spec.rb +20 -0
  140. data/spec/workers/punk/send_email_worker_spec.rb +46 -0
  141. data/spec/workers/punk/send_sms_worker_spec.rb +33 -0
  142. metadata +148 -11
  143. data/lib/punk/views/all.rb +0 -4
@@ -0,0 +1,115 @@
1
+ # frozen_string_literal: true
2
+
3
+ describe PUNK::User do
4
+ it "is valid with valid attributes" do
5
+ expect { create(:user) }.not_to raise_error
6
+ end
7
+
8
+ it "is assigned a uuid on save" do
9
+ user = build(:user)
10
+ expect(user.id).to be_nil
11
+ user.save
12
+ expect(valid_uuid?(user.id)).to be(true)
13
+ end
14
+
15
+ it "can be saved with a custom uuid" do
16
+ uuid = generate(:uuid)
17
+ user = create(:user, id: uuid)
18
+ expect(user.id).to eq(uuid)
19
+ end
20
+
21
+ it "is invalid without a name" do
22
+ user = build(:user, name: nil)
23
+ expect(user.valid?).to be(false)
24
+ expect(user.errors[:name].first).to eq('is not present')
25
+ end
26
+
27
+ it "validates name presence in the database" do
28
+ user = build(:user, name: nil)
29
+ expect { user.save(validate: false) }.to raise_error(Sequel::NotNullConstraintViolation)
30
+ end
31
+
32
+ it "is valid without an icon" do
33
+ user = build(:user, icon: nil)
34
+ expect(user.valid?).to be(true)
35
+ end
36
+
37
+ it "is invalid if the icon is not a URL" do
38
+ user = build(:user, icon: Faker::Alphanumeric.alpha)
39
+ expect(user.valid?).to be(false)
40
+ expect(user.errors[:icon].first).to eq('is not a URL')
41
+ end
42
+
43
+ it "is valid without an email" do
44
+ user = build(:user, email: nil)
45
+ expect(user.valid?).to be(true)
46
+ end
47
+
48
+ it "is invalid if the email is not an email address" do
49
+ user = build(:user, email: Faker::Alphanumeric.alpha)
50
+ expect(user.valid?).to be(false)
51
+ expect(user.errors[:email].first).to eq('is not an email address')
52
+ end
53
+
54
+ it "has a unique email" do
55
+ email = create(:user).email
56
+ user = build(:user, email: email)
57
+ expect(user.valid?).to be(false)
58
+ expect(user.errors[:email].first).to eq('is already taken')
59
+ end
60
+
61
+ it "validates email uniqueness in the database" do
62
+ email = create(:user).email
63
+ user = build(:user, email: email)
64
+ expect { user.save(validate: false) }.to raise_error(Sequel::UniqueConstraintViolation)
65
+ end
66
+
67
+ it "is valid without a phone" do
68
+ user = build(:user, phone: nil)
69
+ expect(user.valid?).to be(true)
70
+ end
71
+
72
+ it "is invalid if the phone is not a phone number" do
73
+ user = build(:user, phone: Faker::Alphanumeric.alpha)
74
+ expect(user.valid?).to be(false)
75
+ expect(user.errors[:phone].first).to eq('is not a phone number')
76
+ end
77
+
78
+ it "has a unique phone" do
79
+ phone = create(:user).phone
80
+ user = build(:user, phone: phone)
81
+ expect(user.valid?).to be(false)
82
+ expect(user.errors[:phone].first).to eq('is already taken')
83
+ end
84
+
85
+ it "validates phone uniqueness in the database" do
86
+ phone = create(:user).phone
87
+ user = build(:user, phone: phone)
88
+ expect { user.save(validate: false) }.to raise_error(Sequel::UniqueConstraintViolation)
89
+ end
90
+
91
+ it "is invalid without either an email or a phone" do
92
+ user = build(:user, email: nil, phone: nil)
93
+ user.valid?
94
+ expect(user.errors[:email].first).to eq('is not present')
95
+ expect(user.errors[:phone].first).to eq('is not present')
96
+ end
97
+
98
+ it "can belong to multiple tenants" do
99
+ user = create(:user)
100
+ expect(user.tenants.count).to eq(0)
101
+ 3.times { create(:tenant).add_user(user) }
102
+ expect(user.tenants.count).to eq(3)
103
+ end
104
+
105
+ it "can belong to multiple groups" do
106
+ user = create(:user)
107
+ expect(user.groups.count).to eq(0)
108
+ 3.times { create(:group).add_user(user) }
109
+ expect(user.groups.count).to eq(3)
110
+ end
111
+
112
+ it "can have many sessions"
113
+
114
+ it "has an active sessions scope"
115
+ end
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ describe PUNK, "GET /groups" do
4
+ include_context "Punk"
5
+
6
+ context 'when the user is not authenticated' do
7
+ before do
8
+ get '/groups'
9
+ end
10
+
11
+ it { is_expected.not_to be_successful }
12
+ end
13
+
14
+ context 'when the user is authenticated' do
15
+ let(:tenant) { create(:tenant) }
16
+ let(:identity) { create(:identity, claim_type: 'phone') }
17
+ let(:group) { create(:group, tenant: tenant) }
18
+
19
+ before do
20
+ identity.user.add_tenant(tenant)
21
+ identity.user.add_group(group)
22
+ login(identity.claim)
23
+ get "/groups?tenant_id=#{tenant.id}"
24
+ end
25
+
26
+ after do
27
+ logout
28
+ end
29
+
30
+ it { is_expected.to be_successful }
31
+ its(:body) { is_expected.to match(group.name) }
32
+ end
33
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ describe PUNK, "GET /plivo" do
4
+ include_context "Punk"
5
+
6
+ before do
7
+ get '/plivo.html'
8
+ end
9
+
10
+ it { is_expected.to be_successful }
11
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ describe PUNK, "DELETE /sessions" do
4
+ include_context "Punk"
5
+
6
+ before do
7
+ delete '/sessions'
8
+ end
9
+
10
+ it { is_expected.not_to be_successful }
11
+ end
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ describe PUNK, "GET /sessions" do
4
+ include_context "Punk"
5
+
6
+ context 'when the user is not authenticated' do
7
+ before do
8
+ get '/sessions'
9
+ end
10
+
11
+ it { is_expected.not_to be_successful }
12
+ end
13
+
14
+ context 'when the user is authenticated' do
15
+ let(:user) { create(:user) }
16
+ let(:identity) { create(:identity, user: user, claim_type: 'phone') }
17
+
18
+ before do
19
+ login(identity.claim)
20
+ get '/sessions'
21
+ end
22
+
23
+ after do
24
+ logout
25
+ end
26
+
27
+ it { is_expected.to be_successful }
28
+ its(:body) { is_expected.to match(user.active_sessions.first.id) }
29
+ end
30
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ describe PUNK, "PATCH /sessions" do
4
+ include_context "Punk"
5
+
6
+ before do
7
+ patch '/sessions'
8
+ end
9
+
10
+ it { is_expected.not_to be_successful }
11
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ describe PUNK, "POST /sessions" do
4
+ include_context "Punk"
5
+
6
+ before do
7
+ post '/sessions'
8
+ end
9
+
10
+ it { is_expected.not_to be_successful }
11
+ end
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ describe PUNK, "GET /swagger" do
4
+ include_context "Punk"
5
+
6
+ before do
7
+ get '/swagger'
8
+ end
9
+
10
+ it { is_expected.to be_successful }
11
+ its(:body) { is_expected.to match('"openapi": "3.0.0"') }
12
+ end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ describe PUNK, "GET /tenants" do
4
+ include_context "Punk"
5
+
6
+ context 'when the user is not authenticated' do
7
+ before do
8
+ get '/tenants'
9
+ end
10
+
11
+ it { is_expected.not_to be_successful }
12
+ end
13
+
14
+ context 'when the user is authenticated' do
15
+ let(:tenant) { create(:tenant) }
16
+ let(:identity) { create(:identity, claim_type: 'phone') }
17
+
18
+ before do
19
+ identity.user.add_tenant(tenant)
20
+ login(identity.claim)
21
+ get '/tenants'
22
+ end
23
+
24
+ after do
25
+ logout
26
+ end
27
+
28
+ it { is_expected.to be_successful }
29
+ its(:body) { is_expected.to match(tenant.name) }
30
+ end
31
+ end
@@ -0,0 +1,60 @@
1
+ # frozen_string_literal: true
2
+
3
+ describe PUNK, "GET /users" do
4
+ include_context "Punk"
5
+
6
+ context 'when the user is not authenticated' do
7
+ before do
8
+ get '/users'
9
+ end
10
+
11
+ it { is_expected.not_to be_successful }
12
+ end
13
+
14
+ context 'when the user is authenticated' do
15
+ let(:tenant) { create(:tenant) }
16
+ let(:identity) { create(:identity, claim_type: 'phone') }
17
+ let(:group) { create(:group, tenant: tenant) }
18
+ let(:tenant_user) { create(:user) }
19
+ let(:group_user) { create(:user) }
20
+
21
+ before do
22
+ identity.user.add_tenant(tenant)
23
+ identity.user.add_group(group)
24
+ tenant_user.add_tenant(tenant)
25
+ group_user.add_tenant(tenant)
26
+ group_user.add_group(group)
27
+ login(identity.claim)
28
+ end
29
+
30
+ after do
31
+ logout
32
+ end
33
+
34
+ context 'without a group' do
35
+ before do
36
+ get "/users?tenant_id=#{tenant.id}"
37
+ end
38
+
39
+ it { is_expected.to be_successful }
40
+
41
+ its(:body) do
42
+ is_expected.to match(tenant_user.name)
43
+ is_expected.to match(group_user.name)
44
+ end
45
+ end
46
+
47
+ context 'with a group' do
48
+ before do
49
+ get "/users?tenant_id=#{tenant.id}&group_id=#{group.id}"
50
+ end
51
+
52
+ it { is_expected.to be_successful }
53
+
54
+ its(:body) do
55
+ is_expected.not_to match(tenant_user.name)
56
+ is_expected.to match(group_user.name)
57
+ end
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ describe PUNK::ChallengeClaimService do
4
+ context 'with no specs written' do
5
+ it 'displays a warning'
6
+ end
7
+ end
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ describe PUNK::CreateIdentitiesService do
4
+ before do
5
+ create(:user, phone: nil)
6
+ create(:user, email: nil)
7
+ end
8
+
9
+ it 'creates identities for users that are missing them' do
10
+ expect(PUNK::Identity.count).to eq(0)
11
+ described_class.run
12
+ expect(PUNK::Identity.count).to eq(2)
13
+ end
14
+ end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ describe PUNK::GenerateSwaggerService do
4
+ context 'with no specs written' do
5
+ it 'displays a warning'
6
+ end
7
+ end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ describe PUNK::ProveClaimService do
4
+ context 'with no specs written' do
5
+ it 'displays a warning'
6
+ end
7
+ end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ describe PUNK::SecretService do
4
+ context 'with no specs written' do
5
+ it 'displays a warning'
6
+ end
7
+ end
@@ -0,0 +1,122 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'simplecov'
4
+ require 'coveralls'
5
+ SimpleCov.start do
6
+ add_filter 'spec'
7
+ add_filter 'lib/punk/commands/auth.rb'
8
+ add_filter 'lib/punk/commands/generate.rb'
9
+ add_filter 'lib/punk/commands/http.rb'
10
+ add_filter 'lib/punk/commands/list.rb'
11
+ add_filter 'lib/punk/core/commands.rb'
12
+ add_filter 'lib/punk/framework/command.rb'
13
+ add_filter 'lib/punk/plugins/cors.rb'
14
+ add_filter 'lib/punk/plugins/ssl.rb'
15
+ add_filter 'lib/punk/startup/logger.rb'
16
+ end
17
+ Coveralls.wear!
18
+
19
+ require_relative '../lib/punk'
20
+
21
+ require 'factory_bot'
22
+ require 'faker'
23
+ require 'timecop'
24
+ require 'securerandom'
25
+ require 'vcr'
26
+ require 'rack/test'
27
+
28
+ Faker::Config.locale = 'en-US'
29
+
30
+ FactoryBot.use_parent_strategy = false
31
+
32
+ VCR.configure do |c|
33
+ c.ignore_localhost = true
34
+ c.cassette_library_dir = 'spec/vcr_cassettes'
35
+ c.hook_into :webmock
36
+ c.configure_rspec_metadata!
37
+ end
38
+
39
+ PUNK.init(task: 'spec', config: { app: { name: 'Punk Test' } }).exec
40
+
41
+ Sidekiq.logger = SemanticLogger['PUNK::SKQ']
42
+
43
+ RSpec.shared_context 'Punk' do # rubocop:disable RSpec/ContextWording
44
+ include Rack::Test::Methods
45
+
46
+ subject { last_response }
47
+
48
+ def app
49
+ PUNK.app
50
+ end
51
+
52
+ def login(claim)
53
+ PUNK.cache.delete(:plivo)
54
+ response =
55
+ PUNK.app.call(
56
+ "REQUEST_METHOD" => "POST",
57
+ "PATH_INFO" => "/sessions",
58
+ "CONTENT_TYPE" => "text/json",
59
+ "SCRIPT_NAME" => "",
60
+ "rack.input" => StringIO.new({ claim: claim }.to_json)
61
+ )
62
+ response = ActiveSupport::JSON.decode(response[-1].first).deep_symbolize_keys
63
+ slug = response[:slug]
64
+ PUNK::SendSmsWorker.drain
65
+ sms = PUNK.cache.get(:plivo).first
66
+ PUNK.app.call(
67
+ "REQUEST_METHOD" => "PATCH",
68
+ "PATH_INFO" => "/sessions/#{slug}",
69
+ "CONTENT_TYPE" => "text/json",
70
+ "SCRIPT_NAME" => "",
71
+ "rack.input" => StringIO.new({ secret: sms[:body][-7..-2] }.to_json)
72
+ )
73
+ end
74
+
75
+ def logout
76
+ PUNK.app.call(
77
+ "REQUEST_METHOD" => "DELETE",
78
+ "PATH_INFO" => "/sessions",
79
+ "SCRIPT_NAME" => "",
80
+ "rack.input" => StringIO.new
81
+ )
82
+ end
83
+ end
84
+
85
+ module Helpers
86
+ def valid_uuid?(data)
87
+ data.split('-').map { |s| Integer(s, 16) }.length == 5
88
+ end
89
+ end
90
+
91
+ FactoryBot.define do
92
+ sequence :phone do
93
+ phone = "+1#{Phony.normalize(Faker::PhoneNumber.cell_phone)}" until Phony.plausible?(phone)
94
+ phone
95
+ end
96
+
97
+ sequence :uuid do
98
+ SecureRandom.uuid
99
+ end
100
+ end
101
+
102
+ RSpec.configure do |config|
103
+ config.expect_with :rspec do |expectations|
104
+ expectations.include_chain_clauses_in_custom_matcher_descriptions = true
105
+ end
106
+
107
+ config.include Helpers
108
+
109
+ config.include FactoryBot::Syntax::Methods
110
+ config.before(:suite) do
111
+ FactoryBot.find_definitions
112
+ end
113
+ config.before do
114
+ Sidekiq::Worker.clear_all
115
+ end
116
+
117
+ config.around do |example|
118
+ PUNK.db.transaction(rollback: :always, auto_savepoint: true) { example.run }
119
+ end
120
+
121
+ PUNK.commands(:spec, config)
122
+ end