punk 0.0.3 → 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 (150) hide show
  1. checksums.yaml +4 -4
  2. data/.editorconfig +9 -0
  3. data/.github/workflows/ship.yml +28 -0
  4. data/.github/workflows/test.yml +45 -0
  5. data/.rdoc_options +23 -0
  6. data/.rgignore +1 -0
  7. data/.rspec +2 -0
  8. data/.rubocop.yml +243 -0
  9. data/Gemfile +6 -6
  10. data/Gemfile.lock +18 -30
  11. data/README.md +8 -0
  12. data/Rakefile +7 -9
  13. data/VERSION +1 -1
  14. data/app/migrations/001_lets_punk.rb +3 -0
  15. data/app/routes/hello.rb +4 -0
  16. data/bin/punk +0 -1
  17. data/env/.gitignore +3 -0
  18. data/env/spec/test.sh +3 -0
  19. data/env/test.sh +5 -0
  20. data/lib/punk/actions/.keep +0 -0
  21. data/lib/punk/actions/groups/list.rb +24 -0
  22. data/lib/punk/actions/sessions/clear.rb +21 -0
  23. data/lib/punk/actions/sessions/create.rb +64 -0
  24. data/lib/punk/actions/sessions/list.rb +18 -0
  25. data/lib/punk/actions/sessions/verify.rb +24 -0
  26. data/lib/punk/actions/tenants/list.rb +18 -0
  27. data/lib/punk/actions/users/list_group.rb +18 -0
  28. data/lib/punk/actions/users/list_tenant.rb +18 -0
  29. data/lib/punk/actions/users/show.rb +18 -0
  30. data/lib/punk/commands/http.rb +3 -3
  31. data/lib/punk/commands/list.rb +12 -6
  32. data/lib/punk/config/defaults.json +3 -0
  33. data/lib/punk/config/schema.json +3 -0
  34. data/lib/punk/core/app.rb +6 -8
  35. data/lib/punk/core/commander.rb +9 -6
  36. data/lib/punk/core/exec.rb +2 -0
  37. data/lib/punk/core/load.rb +0 -1
  38. data/lib/punk/framework/command.rb +5 -1
  39. data/lib/punk/framework/plugins/validation.rb +0 -14
  40. data/lib/punk/framework/runnable.rb +1 -1
  41. data/lib/punk/helpers/loggable.rb +1 -1
  42. data/lib/punk/migrations/001_punk.rb +103 -0
  43. data/lib/punk/models/.keep +0 -0
  44. data/lib/punk/models/group.rb +20 -0
  45. data/lib/punk/models/group_user_metadata.rb +17 -0
  46. data/lib/punk/models/identity.rb +29 -0
  47. data/lib/punk/models/session.rb +89 -0
  48. data/lib/punk/models/tenant.rb +19 -0
  49. data/lib/punk/models/tenant_user_metadata.rb +17 -0
  50. data/lib/punk/models/user.rb +31 -0
  51. data/lib/punk/routes/groups.rb +31 -0
  52. data/lib/punk/routes/plivo.rb +4 -0
  53. data/lib/punk/routes/sessions.rb +108 -0
  54. data/lib/punk/routes/swagger.rb +9 -0
  55. data/lib/punk/routes/tenants.rb +29 -0
  56. data/lib/punk/routes/users.rb +36 -0
  57. data/lib/punk/services/.keep +0 -0
  58. data/lib/punk/services/challenge_claim.rb +46 -0
  59. data/lib/punk/services/create_identities.rb +25 -0
  60. data/lib/punk/services/generate_swagger.rb +25 -0
  61. data/lib/punk/services/prove_claim.rb +29 -0
  62. data/lib/punk/services/secret.rb +9 -0
  63. data/lib/punk/templates/groups/list.jbuilder +7 -0
  64. data/lib/punk/templates/plivo.slim +16 -0
  65. data/lib/punk/templates/sessions/list.jbuilder +6 -0
  66. data/lib/punk/templates/sessions/pending.jbuilder +4 -0
  67. data/lib/punk/templates/tenants/list.jbuilder +7 -0
  68. data/lib/punk/templates/tenants/list.slim +8 -0
  69. data/lib/punk/templates/users/list.jbuilder +7 -0
  70. data/lib/punk/templates/users/list.rcsv +4 -0
  71. data/lib/punk/templates/users/show.jbuilder +5 -0
  72. data/lib/punk/views/groups/list.rb +22 -0
  73. data/lib/punk/views/plivo_store.rb +15 -0
  74. data/lib/punk/views/sessions/list.rb +22 -0
  75. data/lib/punk/views/sessions/pending.rb +28 -0
  76. data/lib/punk/views/tenants/list.rb +22 -0
  77. data/lib/punk/views/users/list.rb +22 -0
  78. data/lib/punk/views/users/show.rb +22 -0
  79. data/lib/punk/workers/.keep +0 -0
  80. data/lib/punk/workers/expire_sessions.rb +9 -0
  81. data/lib/punk/workers/geocode_session_worker.rb +48 -0
  82. data/lib/punk/workers/identify_session_worker.rb +45 -0
  83. data/lib/punk/workers/secret.rb +18 -0
  84. data/lib/punk/workers/send_email_worker.rb +51 -0
  85. data/lib/punk/workers/send_sms_worker.rb +40 -0
  86. data/punk.gemspec +149 -16
  87. data/schema.psql +345 -0
  88. data/spec/actions/groups/punk/list_groups_action_spec.rb +36 -0
  89. data/spec/actions/sessions/punk/clear_session_action_spec.rb +29 -0
  90. data/spec/actions/sessions/punk/create_session_action_spec.rb +33 -0
  91. data/spec/actions/sessions/punk/list_sessions_action_spec.rb +26 -0
  92. data/spec/actions/sessions/punk/verify_session_action_spec.rb +59 -0
  93. data/spec/actions/tenants/punk/list_tenants_action_spec.rb +25 -0
  94. data/spec/actions/users/punk/list_group_users_action_spec.rb +26 -0
  95. data/spec/actions/users/punk/list_tenant_users_action_spec.rb +26 -0
  96. data/spec/factories/group.rb +12 -0
  97. data/spec/factories/group_user_metadata.rb +10 -0
  98. data/spec/factories/identity.rb +19 -0
  99. data/spec/factories/session.rb +12 -0
  100. data/spec/factories/tenant.rb +10 -0
  101. data/spec/factories/tenant_user_metadata.rb +10 -0
  102. data/spec/factories/user.rb +12 -0
  103. data/spec/lib/commands/auth_spec.rb +11 -0
  104. data/spec/lib/commands/generate_spec.rb +7 -0
  105. data/spec/lib/commands/http_spec.rb +23 -0
  106. data/spec/lib/commands/list_spec.rb +7 -0
  107. data/spec/lib/commands/swagger_spec.rb +7 -0
  108. data/spec/lib/engine/punk_env_spec.rb +13 -0
  109. data/spec/lib/engine/punk_exec_spec.rb +9 -0
  110. data/spec/lib/engine/punk_init_spec.rb +9 -0
  111. data/spec/lib/engine/punk_store_spec.rb +10 -0
  112. data/spec/lib/punk.env +7 -0
  113. data/spec/models/punk/group_spec.rb +50 -0
  114. data/spec/models/punk/group_user_metadata_spec.rb +61 -0
  115. data/spec/models/punk/identity_spec.rb +61 -0
  116. data/spec/models/punk/session_spec.rb +156 -0
  117. data/spec/models/punk/tenant_spec.rb +51 -0
  118. data/spec/models/punk/tenant_user_metadata_spec.rb +61 -0
  119. data/spec/models/punk/user_spec.rb +115 -0
  120. data/spec/routes/groups/get_groups_spec.rb +33 -0
  121. data/spec/routes/plivo/get_plivo_spec.rb +11 -0
  122. data/spec/routes/sessions/delete_session_spec.rb +11 -0
  123. data/spec/routes/sessions/get_sessions_spec.rb +30 -0
  124. data/spec/routes/sessions/patch_session_spec.rb +11 -0
  125. data/spec/routes/sessions/post_session_spec.rb +11 -0
  126. data/spec/routes/swagger/get_swagger_spec.rb +12 -0
  127. data/spec/routes/tenants/get_tenants_spec.rb +31 -0
  128. data/spec/routes/users/get_users_spec.rb +60 -0
  129. data/spec/services/punk/challenge_claim_service_spec.rb +7 -0
  130. data/spec/services/punk/create_identities_service_spec.rb +14 -0
  131. data/spec/services/punk/generate_swagger_service_spec.rb +7 -0
  132. data/spec/services/punk/prove_claim_service_spec.rb +7 -0
  133. data/spec/services/punk/secret_service_spec.rb +7 -0
  134. data/spec/spec_helper.rb +122 -0
  135. data/spec/vcr_cassettes/PUNK_GeocodeSessionWorker/updates_the_session_data.yml +57 -0
  136. data/spec/vcr_cassettes/PUNK_IdentifySessionWorker/updates_the_session_data.yml +112 -0
  137. data/spec/views/punk/plivo_store_spec.rb +7 -0
  138. data/spec/views/sessions/punk/list_sessions_view_spec.rb +7 -0
  139. data/spec/views/sessions/punk/pending_session_view_spec.rb +7 -0
  140. data/spec/views/tenants/punk/list_tenants_view_spec.rb +7 -0
  141. data/spec/views/users/punk/list_groups_view_spec.rb +7 -0
  142. data/spec/views/users/punk/list_users_view_spec.rb +7 -0
  143. data/spec/workers/punk/expire_sessions_worker_spec.rb +31 -0
  144. data/spec/workers/punk/geocode_session_worker_spec.rb +14 -0
  145. data/spec/workers/punk/identify_session_worker_spec.rb +15 -0
  146. data/spec/workers/punk/secret_worker_spec.rb +20 -0
  147. data/spec/workers/punk/send_email_worker_spec.rb +46 -0
  148. data/spec/workers/punk/send_sms_worker_spec.rb +33 -0
  149. metadata +169 -13
  150. data/lib/punk/views/all.rb +0 -4
@@ -0,0 +1,61 @@
1
+ # frozen_string_literal: true
2
+
3
+ describe PUNK::Identity do
4
+ it 'is valid with valid attributes' do
5
+ expect { create(:identity) }.not_to raise_error
6
+ end
7
+
8
+ it 'must claim an email or a phone' do
9
+ identity = build(:identity, claim_type: 'zork', claim: 'xyzzy')
10
+ expect(identity.valid?).to be(false)
11
+ expect(identity.errors[:claim_type].first).to eq('is not in range or set: [:email, :phone]')
12
+ end
13
+
14
+ it 'is assigned a uuid on save' do
15
+ identity = build(:identity)
16
+ expect(identity.id).to be_nil
17
+ identity.save
18
+ expect(valid_uuid?(identity.id)).to be(true)
19
+ end
20
+
21
+ it 'can be saved with a custom uuid' do
22
+ uuid = generate(:uuid)
23
+ identity = create(:identity, id: uuid)
24
+ expect(identity.id).to eq(uuid)
25
+ end
26
+
27
+ it 'may belong to a user' do
28
+ identity_with_user = create(:identity)
29
+ expect(identity_with_user.user).to exist
30
+ identity_without_user = create(:identity, user: nil)
31
+ expect(identity_without_user.user).to be_nil
32
+ end
33
+
34
+ it 'has an email? accessor' do
35
+ identity_with_email = build(:identity, claim_type: 'email')
36
+ expect(identity_with_email.email?).to be(true)
37
+ identity_without_email = build(:identity, claim_type: 'phone')
38
+ expect(identity_without_email.email?).to be(false)
39
+ end
40
+
41
+ it 'has a phone? accessor' do
42
+ identity_with_phone = create(:identity, claim_type: 'phone')
43
+ expect(identity_with_phone.phone?).to be(true)
44
+ identity_without_phone = create(:identity, claim_type: 'email')
45
+ expect(identity_without_phone.phone?).to be(false)
46
+ end
47
+
48
+ it 'has a unique claim' do
49
+ identity = create(:identity)
50
+ duplicate_identity = build(:identity, claim_type: identity.claim_type, claim: identity.claim)
51
+ expect(duplicate_identity.valid?).to be(false)
52
+ expect(duplicate_identity.errors[:claim].first).to eq('is already taken')
53
+ end
54
+
55
+ it 'can have multiple sessions' do
56
+ identity = create(:identity)
57
+ expect(identity.sessions.count).to eq(0)
58
+ create_list(:session, 3, identity: identity)
59
+ expect(identity.sessions.count).to eq(3)
60
+ end
61
+ end
@@ -0,0 +1,156 @@
1
+ # frozen_string_literal: true
2
+
3
+ describe PUNK::Session do
4
+ it "is valid with valid attributes" do
5
+ expect { create(:session) }.not_to raise_error
6
+ end
7
+
8
+ it "is assigned a uuid on save" do
9
+ session = build(:session)
10
+ expect(session.id).to be_nil
11
+ session.save
12
+ expect(valid_uuid?(session.id)).to be(true)
13
+ end
14
+
15
+ it "can be saved with a custom uuid" do
16
+ uuid = generate(:uuid)
17
+ session = create(:session, id: uuid)
18
+ expect(session.id).to eq(uuid)
19
+ end
20
+
21
+ it "must belong to an identity" do
22
+ session = build(:session, identity: nil)
23
+ expect(session.valid?).to be(false)
24
+ expect(session.errors[:identity].first).to eq('is not present')
25
+ end
26
+
27
+ it "may have a user" do
28
+ session = build(:session)
29
+ expect(session.user).to exist
30
+ session = build(:session, identity: create(:identity, user: nil))
31
+ expect(session.user).to be_nil
32
+ end
33
+
34
+ it "can contain client data" do
35
+ identity = create(:session, data: { foo: 'bar' })
36
+ expect(identity.data[:foo]).to eq('bar')
37
+ end
38
+
39
+ it "permits only three validation attempts" do
40
+ session = create(:session, attempt_count: 3)
41
+ expect(session.valid?).to be(true)
42
+ expect { session.increment_attempts }.to raise_error(Sequel::ValidationFailed, "attempt_count is not in range or set: [0, 1, 2, 3]")
43
+ end
44
+
45
+ it "starts in the created state" do
46
+ session = create(:session)
47
+ expect(session.created?).to be(true)
48
+ end
49
+
50
+ it "can be challenged" do
51
+ session = create(:session)
52
+ expect(session.may_challenge?).to be(true)
53
+ session.challenge!
54
+ expect(session.pending?).to be(true)
55
+ end
56
+
57
+ it "can be verified" do
58
+ session = create(:session)
59
+ session.challenge!
60
+ expect(session.may_verify?).to be(true)
61
+ session.verify!
62
+ expect(session.active?).to be(true)
63
+ end
64
+
65
+ it "will not timeout when first created" do
66
+ session = create(:session)
67
+ session.timeout?
68
+ expect(session.created?).to be(true)
69
+ end
70
+
71
+ it "will timeout after 5 minutes if not active" do
72
+ session = create(:session)
73
+ Timecop.travel(6.minutes.from_now)
74
+ expect(described_class.expiring.count).to eq(1)
75
+ session.timeout?
76
+ expect(session.expired?).to be(true)
77
+ end
78
+
79
+ it "will not time out after 5 minutes if active" do
80
+ session = create(:session, state: :active)
81
+ Timecop.travel(1.week.from_now)
82
+ expect(described_class.expiring.count).to eq(0)
83
+ session.timeout?
84
+ expect(session.active?).to be(true)
85
+ end
86
+
87
+ it "will timeout after 1 month if active but unused" do
88
+ session = create(:session, state: :active)
89
+ Timecop.travel((1.month + 1.day).from_now)
90
+ expect(described_class.expiring.count).to eq(1)
91
+ session.timeout?
92
+ expect(session.expired?).to be(true)
93
+ end
94
+
95
+ it "will not timeout after 1 month if active and used" do
96
+ session = create(:session, state: :active)
97
+ Timecop.travel(6.months.from_now)
98
+ session.touch
99
+ session.timeout?
100
+ expect(session.active?).to be(true)
101
+ end
102
+
103
+ it "will timeout after 1 year if active and used" do
104
+ session = create(:session, state: :active)
105
+ Timecop.travel((1.year + 1.day).from_now)
106
+ session.touch
107
+ session.timeout?
108
+ expect(session.expired?).to be(true)
109
+ end
110
+
111
+ it "can be cleared" do
112
+ session = create(:session, state: :active)
113
+ expect(session.may_clear?).to be(true)
114
+ session.clear!
115
+ expect(session.deleted?).to be(true)
116
+ end
117
+
118
+ context "when many sessions exist" do
119
+ before do
120
+ create(:session, state: :created)
121
+ create(:session, state: :pending)
122
+ create(:session, state: :active)
123
+ create(:session, state: :expired)
124
+ create(:session, state: :deleted)
125
+ end
126
+
127
+ it "can be scoped to created sessions" do
128
+ expect(described_class.count).to eq(5)
129
+ expect(described_class.created.count).to eq(1)
130
+ end
131
+
132
+ it "can be scoped to pending sessions" do
133
+ expect(described_class.count).to eq(5)
134
+ expect(described_class.pending.count).to eq(1)
135
+ end
136
+
137
+ it "can be scoped to active sessions" do
138
+ expect(described_class.count).to eq(5)
139
+ expect(described_class.active.count).to eq(1)
140
+ end
141
+
142
+ it "can be scoped to expired sessions" do
143
+ expect(described_class.count).to eq(5)
144
+ expect(described_class.expired.count).to eq(1)
145
+ end
146
+
147
+ it "can be scoped to deleted sessions" do
148
+ expect(described_class.count).to eq(5)
149
+ expect(described_class.deleted.count).to eq(1)
150
+ end
151
+
152
+ it "can be accessed at random" do
153
+ expect { described_class.sample }.not_to raise_error
154
+ end
155
+ end
156
+ end
@@ -0,0 +1,51 @@
1
+ # frozen_string_literal: true
2
+
3
+ describe PUNK::Tenant do
4
+ it "is valid with valid attributes" do
5
+ expect { create(:tenant) }.not_to raise_error
6
+ end
7
+
8
+ it "is assigned a uuid on save" do
9
+ tenant = build(:tenant)
10
+ expect(tenant.id).to be_nil
11
+ tenant.save
12
+ expect(valid_uuid?(tenant.id)).to be(true)
13
+ end
14
+
15
+ it "can be saved with a custom uuid" do
16
+ uuid = generate(:uuid)
17
+ tenant = create(:tenant, id: uuid)
18
+ expect(tenant.id).to eq(uuid)
19
+ end
20
+
21
+ it "is invalid without a name" do
22
+ tenant = build(:tenant, name: nil)
23
+ expect(tenant.valid?).to be(false)
24
+ expect(tenant.errors[:name].first).to eq('is not present')
25
+ end
26
+
27
+ it "is valid without an icon" do
28
+ tenant = build(:tenant, icon: nil)
29
+ expect(tenant.valid?).to be(true)
30
+ end
31
+
32
+ it "is invalid if the icon is not a URL" do
33
+ tenant = build(:tenant, icon: Faker::Alphanumeric.alpha)
34
+ expect(tenant.valid?).to be(false)
35
+ expect(tenant.errors[:icon].first).to eq('is not a URL')
36
+ end
37
+
38
+ it "can have multiple users" do
39
+ tenant = create(:tenant)
40
+ expect(tenant.users.count).to eq(0)
41
+ 3.times { create(:user).add_tenant(tenant) }
42
+ expect(tenant.users.count).to eq(3)
43
+ end
44
+
45
+ it "can have multiple groups" do
46
+ tenant = create(:tenant)
47
+ expect(tenant.groups.count).to eq(0)
48
+ create_list(:group, 3, tenant: tenant)
49
+ expect(tenant.groups.count).to eq(3)
50
+ end
51
+ end
@@ -0,0 +1,61 @@
1
+ # frozen_string_literal: true
2
+
3
+ describe PUNK::TenantUserMetadata do
4
+ it "is valid with valid attributes" do
5
+ expect { create(:tenant_user_metadata) }.not_to raise_error
6
+ end
7
+
8
+ it "is invalid without a tenant" do
9
+ tenant_user_metadata = build(:tenant_user_metadata, tenant: nil)
10
+ expect(tenant_user_metadata.valid?).to be(false)
11
+ expect(tenant_user_metadata.errors[:tenant].first).to eq('is not present')
12
+ end
13
+
14
+ it "is invalid without a user" do
15
+ tenant_user_metadata = build(:tenant_user_metadata, user: nil)
16
+ expect(tenant_user_metadata.valid?).to be(false)
17
+ expect(tenant_user_metadata.errors[:user].first).to eq('is not present')
18
+ end
19
+
20
+ it "displays as the two IDs concatenated" do
21
+ tenant_user_metadata = create(:tenant_user_metadata)
22
+ expect(tenant_user_metadata.to_s).to include(tenant_user_metadata.tenant.id)
23
+ expect(tenant_user_metadata.to_s).to include(tenant_user_metadata.user.id)
24
+ end
25
+
26
+ context "when a user and a tenant exist" do
27
+ let(:user) { create(:user) }
28
+ let(:tenant) { create(:tenant) }
29
+
30
+ context "when a user is added to a tenant" do
31
+ let(:tenant_user_metadata) do
32
+ described_class[tenant: tenant, user: user]
33
+ end
34
+
35
+ before do
36
+ tenant.add_user(user)
37
+ end
38
+
39
+ it "is created automatically" do
40
+ expect(tenant_user_metadata).not_to be_nil
41
+ expect(tenant.users).to include(user)
42
+ end
43
+
44
+ it "destroying it will remove the user from the tenant" do
45
+ expect(tenant.users).to include(user)
46
+ tenant_user_metadata.destroy
47
+ tenant.reload
48
+ expect(tenant.users).not_to include(user)
49
+ end
50
+ end
51
+
52
+ context "when it is created" do
53
+ it "adds a user to a tenant" do
54
+ expect(tenant.users).not_to include(user)
55
+ create(:tenant_user_metadata, tenant: tenant, user: user)
56
+ tenant.reload
57
+ expect(tenant.users).to include(user)
58
+ end
59
+ end
60
+ end
61
+ end
@@ -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