|
4 | 4 | let!(:org) { create(:casa_org) } |
5 | 5 | let!(:user) { create(:user, phone_number: "+12222222222", casa_org: org) } |
6 | 6 |
|
7 | | - let!(:twilio_service_double) { instance_double(TwilioService) } |
| 7 | + let!(:twillio_service_double) { instance_double(TwilioService) } |
8 | 8 | let!(:short_url_service_double) { instance_double(ShortUrlService) } |
9 | 9 |
|
10 | 10 | before do |
11 | 11 | allow(TwilioService).to( |
12 | 12 | receive(:new).with( |
13 | 13 | org |
14 | | - ).and_return(twilio_service_double) |
| 14 | + ).and_return(twillio_service_double) |
15 | 15 | ) |
16 | 16 |
|
17 | | - allow(twilio_service_double).to receive(:send_sms) |
| 17 | + allow(twillio_service_double).to receive(:send_sms) |
18 | 18 |
|
19 | 19 | allow(ShortUrlService).to receive(:new).and_return(short_url_service_double) |
20 | 20 |
|
|
37 | 37 |
|
38 | 38 | it "sends a password reset SMS to existing user" do |
39 | 39 | request |
40 | | - expect(twilio_service_double).to have_received(:send_sms).once.with( |
| 40 | + expect(twillio_service_double).to have_received(:send_sms).once.with( |
41 | 41 | {From: org.twilio_phone_number, Body: a_string_matching("reset_url"), To: user.phone_number} |
42 | 42 | ) |
43 | 43 | end |
44 | 44 |
|
45 | 45 | it "sends a password reset email to existing user" do |
46 | | - expect { request }.to change { ActionMailer::Base.deliveries.count }.by(1) |
47 | | - expect(ActionMailer::Base.deliveries.last.to).to include(user.email) |
| 46 | + expect_any_instance_of(User).to receive(:send_reset_password_instructions).once |
| 47 | + request |
48 | 48 | end |
49 | 49 |
|
50 | 50 | it { is_expected.to redirect_to(user_session_url) } |
|
60 | 60 | let(:params) { {user: {email: user.email, phone_number: ""}} } |
61 | 61 |
|
62 | 62 | it "sends a password reset email to existing user" do |
63 | | - expect { request }.to change { ActionMailer::Base.deliveries.count }.by(1) |
64 | | - expect(ActionMailer::Base.deliveries.last.to).to include(user.email) |
| 63 | + expect_any_instance_of(User).to receive(:send_reset_password_instructions).once |
| 64 | + request |
65 | 65 | end |
66 | 66 |
|
67 | 67 | it "does not send sms with reset password" do |
68 | 68 | request |
69 | | - expect(twilio_service_double).not_to have_received(:send_sms) |
| 69 | + expect(twillio_service_double).not_to have_received(:send_sms) |
70 | 70 | end |
71 | 71 | end |
72 | 72 |
|
|
75 | 75 |
|
76 | 76 | it "sends a password reset SMS to existing user" do |
77 | 77 | request |
78 | | - expect(twilio_service_double).to have_received(:send_sms).once.with( |
| 78 | + expect(twillio_service_double).to have_received(:send_sms).once.with( |
79 | 79 | {From: org.twilio_phone_number, Body: a_string_matching("reset_url"), To: user.phone_number} |
80 | 80 | ) |
81 | 81 | end |
82 | 82 |
|
83 | 83 | it "does not send email with reset password" do |
84 | | - expect { request }.not_to change { ActionMailer::Base.deliveries.count } |
| 84 | + expect_any_instance_of(User).not_to receive(:send_reset_password_instructions) |
| 85 | + request |
85 | 86 | end |
86 | 87 | end |
87 | 88 | end |
|
95 | 96 | end |
96 | 97 | end |
97 | 98 |
|
98 | | - context "with wrong parameters (non-existent user)" do |
| 99 | + context "with wrong parameters" do |
99 | 100 | let(:params) { {user: {phone_number: "13333333333"}} } |
100 | 101 |
|
101 | | - it "does not reveal if user exists (security)" do |
| 102 | + it "sets errors correctly" do |
102 | 103 | request |
103 | 104 | expect(flash[:notice]).to( |
104 | 105 | eq("If the account exists you will receive an email or SMS with instructions on how to reset your password in a few minutes.") |
105 | 106 | ) |
106 | 107 | end |
107 | 108 | end |
108 | 109 |
|
109 | | - context "with invalid phone number format" do |
110 | | - let(:params) { {user: {email: "", phone_number: "1234"}} } |
111 | | - |
112 | | - it "shows phone number validation error" do |
113 | | - request |
114 | | - expect(request.parsed_body.to_html).to include("phone_number") |
115 | | - end |
116 | | - end |
117 | | - |
118 | | - context "with non-numeric phone number" do |
119 | | - let(:params) { {user: {email: "", phone_number: "abc"}} } |
120 | | - |
121 | | - it "shows phone number validation error" do |
122 | | - request |
123 | | - expect(request.parsed_body.to_html).to include("phone_number") |
124 | | - end |
125 | | - end |
126 | | - |
127 | 110 | context "when twilio is disabled" do |
128 | 111 | let(:params) { {user: {email: user.email, phone_number: user.phone_number}} } |
129 | 112 |
|
|
132 | 115 | end |
133 | 116 |
|
134 | 117 | it "does not send an sms, only an email" do |
135 | | - expect { request }.to change { ActionMailer::Base.deliveries.count }.by(1) |
136 | | - expect(flash[:notice]).to( |
137 | | - eq("If the account exists you will receive an email or SMS with instructions on how to reset your password in a few minutes.") |
138 | | - ) |
139 | | - end |
140 | | - end |
141 | | - |
142 | | - context "when email sending times out with Net::ReadTimeout" do |
143 | | - let(:params) { {user: {email: user.email, phone_number: user.phone_number}} } |
144 | | - |
145 | | - before do |
146 | | - allow(user).to receive(:send_reset_password_instructions).and_raise(Net::ReadTimeout) |
147 | | - allow(User).to receive(:find_by).and_return(user) |
148 | | - end |
149 | | - |
150 | | - it "handles the timeout gracefully and still shows success message" do |
151 | | - expect(Rails.logger).to receive(:error).with(/Password reset email failed to send/) |
152 | | - request |
153 | | - expect(flash[:notice]).to( |
154 | | - eq("If the account exists you will receive an email or SMS with instructions on how to reset your password in a few minutes.") |
155 | | - ) |
156 | | - end |
157 | | - |
158 | | - it "does not crash the request" do |
159 | | - expect { request }.not_to raise_error |
160 | | - expect(response).to redirect_to(user_session_url) |
161 | | - end |
162 | | - |
163 | | - it "notifies Bugsnag of the error" do |
164 | | - expect(Bugsnag).to receive(:notify).with(instance_of(Net::ReadTimeout)) |
165 | | - request |
166 | | - end |
167 | | - |
168 | | - it "generates a fallback token for SMS to use" do |
169 | | - expect(user).to receive(:generate_password_reset_token).and_call_original |
170 | | - request |
171 | | - end |
172 | | - |
173 | | - it "still sends SMS with the fallback token" do |
174 | | - request |
175 | | - expect(twilio_service_double).to have_received(:send_sms).once |
176 | | - end |
177 | | - end |
178 | | - |
179 | | - context "when email sending times out with Net::OpenTimeout" do |
180 | | - let(:params) { {user: {email: user.email, phone_number: ""}} } |
181 | | - |
182 | | - before do |
183 | | - allow(user).to receive(:send_reset_password_instructions).and_raise(Net::OpenTimeout) |
184 | | - allow(User).to receive(:find_by).and_return(user) |
185 | | - end |
186 | | - |
187 | | - it "handles the timeout gracefully and still shows success message" do |
188 | | - expect(Rails.logger).to receive(:error).with(/Password reset email failed to send/) |
| 118 | + expect_any_instance_of(User).to receive(:send_reset_password_instructions).once |
189 | 119 | request |
190 | 120 | expect(flash[:notice]).to( |
191 | 121 | eq("If the account exists you will receive an email or SMS with instructions on how to reset your password in a few minutes.") |
192 | 122 | ) |
193 | 123 | end |
194 | | - |
195 | | - it "does not crash the request" do |
196 | | - expect { request }.not_to raise_error |
197 | | - expect(response).to redirect_to(user_session_url) |
198 | | - end |
199 | | - |
200 | | - it "notifies Bugsnag of the error" do |
201 | | - expect(Bugsnag).to receive(:notify).with(instance_of(Net::OpenTimeout)) |
202 | | - request |
203 | | - end |
204 | | - end |
205 | | - |
206 | | - context "when SMS sending fails" do |
207 | | - let(:params) { {user: {email: "", phone_number: user.phone_number}} } |
208 | | - |
209 | | - before do |
210 | | - allow(twilio_service_double).to receive(:send_sms).and_raise(Twilio::REST::TwilioError.new("Service unavailable")) |
211 | | - end |
212 | | - |
213 | | - it "raises the error (no rescue in controller)" do |
214 | | - expect { request }.to raise_error(Twilio::REST::TwilioError) |
215 | | - end |
216 | 124 | end |
217 | 125 | end |
218 | 126 |
|
|
233 | 141 | } |
234 | 142 | end |
235 | 143 |
|
236 | | - subject(:request) { put user_password_path, params: params } |
| 144 | + subject(:submit_reset) { put user_password_path, params: params } |
237 | 145 |
|
238 | | - context "with valid token and password" do |
239 | | - it "successfully resets the password" do |
240 | | - request |
241 | | - expect(response).to redirect_to(new_user_session_path) |
242 | | - expect(flash[:notice]).to eq("Your password has been changed successfully.") |
243 | | - end |
244 | | - |
245 | | - it "allows user to sign in with new password" do |
246 | | - request |
247 | | - user.reload |
248 | | - expect(user.valid_password?("newpassword123!")).to be true |
249 | | - end |
250 | | - end |
251 | | - |
252 | | - context "with password mismatch" do |
253 | | - let(:params) do |
254 | | - { |
255 | | - user: { |
256 | | - reset_password_token: token, |
257 | | - password: "newpassword123!", |
258 | | - password_confirmation: "differentpassword123!" |
259 | | - } |
260 | | - } |
261 | | - end |
262 | | - |
263 | | - it "does not reset the password" do |
264 | | - old_password_digest = user.encrypted_password |
265 | | - request |
266 | | - user.reload |
267 | | - expect(user.encrypted_password).to eq(old_password_digest) |
268 | | - end |
269 | | - end |
270 | | - |
271 | | - context "with password too short" do |
272 | | - let(:params) do |
273 | | - { |
274 | | - user: { |
275 | | - reset_password_token: token, |
276 | | - password: "abc", |
277 | | - password_confirmation: "abc" |
278 | | - } |
279 | | - } |
280 | | - end |
281 | | - |
282 | | - it "does not reset the password" do |
283 | | - old_password_digest = user.encrypted_password |
284 | | - request |
285 | | - user.reload |
286 | | - expect(user.encrypted_password).to eq(old_password_digest) |
287 | | - end |
| 146 | + it "successfully resets the password" do |
| 147 | + submit_reset |
| 148 | + expect(response).to redirect_to(new_user_session_path) |
| 149 | + expect(flash[:notice]).to eq("Your password has been changed successfully.") |
288 | 150 | end |
289 | 151 | end |
290 | 152 | end |
0 commit comments