test_registration.rb

  1# frozen_string_literal: true
  2
  3require "test_helper"
  4require "customer"
  5require "registration"
  6
  7def execute_command(
  8	iq=Blather::Stanza::Iq::Command.new.tap { |i| i.from = "test@example.com" },
  9	blather: BLATHER,
 10	&blk
 11)
 12	Command::Execution.new(
 13		Minitest::Mock.new,
 14		blather,
 15		:to_s.to_proc,
 16		iq
 17	).execute(&blk).sync
 18end
 19
 20class RegistrationTest < Minitest::Test
 21	def test_for_registered
 22		sgx = OpenStruct.new(
 23			registered?: EMPromise.resolve(OpenStruct.new(phone: "+15555550000"))
 24		)
 25		iq = Blather::Stanza::Iq::Command.new
 26		iq.from = "test@example.com"
 27		result = execute_command(iq) do
 28			Registration.for(
 29				Customer.new("test", sgx: sgx),
 30				Minitest::Mock.new
 31			)
 32		end
 33		assert_kind_of Registration::Registered, result
 34	end
 35	em :test_for_registered
 36
 37	def test_for_activated
 38		sgx = OpenStruct.new(registered?: EMPromise.resolve(nil))
 39		web_manager = WebRegisterManager.new
 40		web_manager["test@example.com"] = "+15555550000"
 41		iq = Blather::Stanza::Iq::Command.new
 42		iq.from = "test@example.com"
 43		result = execute_command(iq) do
 44			Registration.for(
 45				Customer.new(
 46					"test",
 47					plan_name: "test_usd",
 48					expires_at: Time.now + 999,
 49					sgx: sgx
 50				),
 51				web_manager
 52			)
 53		end
 54		assert_kind_of Registration::Finish, result
 55	end
 56	em :test_for_activated
 57
 58	def test_for_not_activated_with_customer_id
 59		sgx = OpenStruct.new(registered?: EMPromise.resolve(nil))
 60		web_manager = WebRegisterManager.new
 61		web_manager["test@example.com"] = "+15555550000"
 62		iq = Blather::Stanza::Iq::Command.new
 63		iq.from = "test@example.com"
 64		result = execute_command(iq) do
 65			Registration.for(
 66				Customer.new("test", sgx: sgx),
 67				web_manager
 68			)
 69		end
 70		assert_kind_of Registration::Activation, result
 71	end
 72	em :test_for_not_activated_with_customer_id
 73
 74	class ActivationTest < Minitest::Test
 75		Command::COMMAND_MANAGER = Minitest::Mock.new
 76		def setup
 77			@activation = Registration::Activation.new("test", "+15555550000")
 78		end
 79
 80		def test_write
 81			stub_request(
 82				:get,
 83				"https://dashboard.bandwidth.com/v1.0/tns/+15555550000"
 84			).to_return(status: 201, body: <<~RESPONSE)
 85				<TelephoneNumberResponse>
 86					<TelephoneNumber>5555550000</TelephoneNumber>
 87				</TelephoneNumberResponse>
 88			RESPONSE
 89			stub_request(
 90				:get,
 91				"https://dashboard.bandwidth.com/v1.0/tns/5555550000/ratecenter"
 92			).to_return(status: 201, body: <<~RESPONSE)
 93				<TelephoneNumberResponse>
 94					<TelephoneNumberDetails>
 95						<State>KE</State>
 96						<RateCenter>FA</RateCenter>
 97					</TelephoneNumberDetails>
 98				</TelephoneNumberResponse>
 99			RESPONSE
100			Command::COMMAND_MANAGER.expect(
101				:write,
102				EMPromise.reject(:test_result),
103				[Matching.new do |iq|
104					assert_equal :form, iq.form.type
105					assert_equal(
106						"You've selected +15555550000 (FA, KE) as your JMP number",
107						iq.form.instructions
108					)
109				end]
110			)
111			assert_equal(
112				:test_result,
113				execute_command { @activation.write.catch { |e| e } }
114			)
115			assert_mock Command::COMMAND_MANAGER
116		end
117		em :test_write
118	end
119
120	class PaymentTest < Minitest::Test
121		Customer::BRAINTREE = Minitest::Mock.new
122
123		def test_for_bitcoin
124			customer = Minitest::Mock.new(Customer.new("test"))
125			customer.expect(
126				:add_btc_address,
127				EMPromise.resolve("testaddr")
128			)
129			iq = Blather::Stanza::Iq::Command.new
130			iq.form.fields = [
131				{ var: "activation_method", value: "bitcoin" },
132				{ var: "plan_name", value: "test_usd" }
133			]
134			result = Registration::Payment.for(iq, customer, "+15555550000")
135			assert_kind_of Registration::Payment::Bitcoin, result
136		end
137
138		def test_for_credit_card
139			braintree_customer = Minitest::Mock.new
140			Customer::BRAINTREE.expect(
141				:customer,
142				braintree_customer
143			)
144			braintree_customer.expect(
145				:find,
146				EMPromise.resolve(OpenStruct.new(payment_methods: [])),
147				["test"]
148			)
149			iq = Blather::Stanza::Iq::Command.new
150			iq.from = "test@example.com"
151			iq.form.fields = [
152				{ var: "activation_method", value: "credit_card" },
153				{ var: "plan_name", value: "test_usd" }
154			]
155			result = Registration::Payment.for(
156				iq,
157				Customer.new("test"),
158				"+15555550000"
159			).sync
160			assert_kind_of Registration::Payment::CreditCard, result
161		end
162		em :test_for_credit_card
163
164		def test_for_code
165			iq = Blather::Stanza::Iq::Command.new
166			iq.form.fields = [
167				{ var: "activation_method", value: "code" },
168				{ var: "plan_name", value: "test_usd" }
169			]
170			result = Registration::Payment.for(
171				iq,
172				Customer.new("test"),
173				"+15555550000"
174			)
175			assert_kind_of Registration::Payment::InviteCode, result
176		end
177
178		class BitcoinTest < Minitest::Test
179			Registration::Payment::Bitcoin::BTC_SELL_PRICES = Minitest::Mock.new
180			Customer::REDIS = Minitest::Mock.new
181
182			def setup
183				@customer = Minitest::Mock.new(
184					Customer.new("test", plan_name: "test_usd")
185				)
186				@customer.expect(
187					:add_btc_address,
188					EMPromise.resolve("testaddr")
189				)
190				@bitcoin = Registration::Payment::Bitcoin.new(
191					@customer,
192					"+15555550000"
193				)
194			end
195
196			def test_write
197				Customer::REDIS.expect(
198					:smembers,
199					EMPromise.resolve([]),
200					["jmp_customer_btc_addresses-test"]
201				)
202				reply_text = <<~NOTE
203					Activate your account by sending at least 1.000000 BTC to
204					testaddr
205
206					You will receive a notification when your payment is complete.
207				NOTE
208				blather = Minitest::Mock.new
209				blather.expect(
210					:<<,
211					nil,
212					[Matching.new do |reply|
213						assert_equal :canceled, reply.status
214						assert_equal :info, reply.note_type
215						assert_equal reply_text, reply.note.content
216						true
217					end]
218				)
219				Registration::Payment::Bitcoin::BTC_SELL_PRICES.expect(
220					:usd,
221					EMPromise.resolve(BigDecimal.new(1))
222				)
223				@bitcoin.stub(:save, EMPromise.resolve(nil)) do
224					execute_command(blather: blather) do
225						@bitcoin.write
226					end
227				end
228				assert_mock blather
229			end
230			em :test_write
231		end
232
233		class CreditCardTest < Minitest::Test
234			def setup
235				@credit_card = Registration::Payment::CreditCard.new(
236					Customer.new("test"),
237					"+15555550000"
238				)
239			end
240
241			def test_for
242				customer = Minitest::Mock.new(Customer.new("test"))
243				customer.expect(
244					:payment_methods,
245					EMPromise.resolve(OpenStruct.new(default_payment_method: :test))
246				)
247				assert_kind_of(
248					Registration::Payment::CreditCard::Activate,
249					Registration::Payment::CreditCard.for(
250						customer,
251						"+15555550000"
252					).sync
253				)
254			end
255			em :test_for
256
257			def test_write
258				result = execute_command do
259					Command::COMMAND_MANAGER.expect(
260						:write,
261						EMPromise.reject(:test_result),
262						[Matching.new do |reply|
263							assert_equal [:execute, :next], reply.allowed_actions
264							assert_equal(
265								"Add credit card, then return here to continue: " \
266								"http://creditcard.example.com",
267								reply.note.content
268							)
269						end]
270					)
271
272					@credit_card.write.catch { |e| e }
273				end
274
275				assert_equal :test_result, result
276			end
277			em :test_write
278		end
279
280		class ActivateTest < Minitest::Test
281			Registration::Payment::CreditCard::Activate::Finish =
282				Minitest::Mock.new
283			Registration::Payment::CreditCard::Activate::Transaction =
284				Minitest::Mock.new
285			Command::COMMAND_MANAGER = Minitest::Mock.new
286
287			def test_write
288				transaction = PromiseMock.new
289				transaction.expect(
290					:insert,
291					EMPromise.resolve(nil)
292				)
293				customer = Minitest::Mock.new(
294					Customer.new("test", plan_name: "test_usd")
295				)
296				Registration::Payment::CreditCard::Activate::Transaction.expect(
297					:sale,
298					transaction
299				) do |acustomer, amount:, payment_method:|
300					assert_operator customer, :===, acustomer
301					assert_equal CONFIG[:activation_amount], amount
302					assert_equal :test_default_method, payment_method
303				end
304				customer.expect(:bill_plan, nil)
305				Registration::Payment::CreditCard::Activate::Finish.expect(
306					:new,
307					OpenStruct.new(write: nil),
308					[customer, "+15555550000"]
309				)
310				execute_command do
311					Registration::Payment::CreditCard::Activate.new(
312						customer,
313						:test_default_method,
314						"+15555550000"
315					).write
316				end
317				Registration::Payment::CreditCard::Activate::Transaction.verify
318				transaction.verify
319				customer.verify
320				Registration::Payment::CreditCard::Activate::Finish.verify
321			end
322			em :test_write
323
324			def test_write_declines
325				customer = Minitest::Mock.new(
326					Customer.new("test", plan_name: "test_usd")
327				)
328				iq = Blather::Stanza::Iq::Command.new
329				iq.from = "test@example.com"
330				Command::COMMAND_MANAGER.expect(
331					:write,
332					EMPromise.reject(:test_result),
333					[Matching.new do |reply|
334						assert_equal :error, reply.note_type
335						assert_equal(
336							Registration::Payment::CreditCard::Activate::DECLINE_MESSAGE +
337							": http://creditcard.example.com",
338							reply.note.content
339						)
340					end]
341				)
342				result = execute_command do
343					Registration::Payment::CreditCard::Activate::Transaction.expect(
344						:sale,
345						EMPromise.reject("declined")
346					) do |acustomer, amount:, payment_method:|
347						assert_operator customer, :===, acustomer
348						assert_equal CONFIG[:activation_amount], amount
349						assert_equal :test_default_method, payment_method
350					end
351
352					Registration::Payment::CreditCard::Activate.new(
353						customer,
354						:test_default_method,
355						"+15555550000"
356					).write.catch { |e| e }
357				end
358				assert_equal :test_result, result
359				Registration::Payment::CreditCard::Activate::Transaction.verify
360			end
361			em :test_write_declines
362		end
363
364		class InviteCodeTest < Minitest::Test
365			Registration::Payment::InviteCode::DB =
366				Minitest::Mock.new
367			Registration::Payment::InviteCode::REDIS =
368				Minitest::Mock.new
369			Command::COMMAND_MANAGER = Minitest::Mock.new
370			Registration::Payment::InviteCode::Finish =
371				Minitest::Mock.new
372			def test_write
373				customer = Customer.new("test", plan_name: "test_usd")
374				Registration::Payment::InviteCode::DB.expect(:transaction, true, [])
375				Registration::Payment::InviteCode::Finish.expect(
376					:new,
377					OpenStruct.new(write: nil),
378					[
379						customer,
380						"+15555550000"
381					]
382				)
383				execute_command do
384					Registration::Payment::InviteCode::REDIS.expect(
385						:get,
386						EMPromise.resolve(nil),
387						["jmp_invite_tries-test"]
388					)
389					Command::COMMAND_MANAGER.expect(
390						:write,
391						EMPromise.resolve(
392							Blather::Stanza::Iq::Command.new.tap { |iq|
393								iq.form.fields = [{ var: "code", value: "abc" }]
394							}
395						),
396						[Matching.new do |reply|
397							assert_equal :form, reply.form.type
398							assert_nil reply.form.instructions
399						end]
400					)
401
402					Registration::Payment::InviteCode.new(
403						customer,
404						"+15555550000"
405					).write
406				end
407				assert_mock Command::COMMAND_MANAGER
408				assert_mock Registration::Payment::InviteCode::DB
409				assert_mock Registration::Payment::InviteCode::REDIS
410				assert_mock Registration::Payment::InviteCode::Finish
411			end
412			em :test_write
413
414			def test_write_bad_code
415				result = execute_command do
416					customer = Customer.new("test", plan_name: "test_usd")
417					Registration::Payment::InviteCode::REDIS.expect(
418						:get,
419						EMPromise.resolve(0),
420						["jmp_invite_tries-test"]
421					)
422					Registration::Payment::InviteCode::DB.expect(:transaction, []) do
423						raise Registration::Payment::InviteCode::Invalid, "wut"
424					end
425					Registration::Payment::InviteCode::REDIS.expect(
426						:incr,
427						EMPromise.resolve(nil),
428						["jmp_invite_tries-test"]
429					)
430					Registration::Payment::InviteCode::REDIS.expect(
431						:expire,
432						EMPromise.resolve(nil),
433						["jmp_invite_tries-test", 60 * 60]
434					)
435					Command::COMMAND_MANAGER.expect(
436						:write,
437						EMPromise.resolve(
438							Blather::Stanza::Iq::Command.new.tap { |iq|
439								iq.form.fields = [{ var: "code", value: "abc" }]
440							}
441						),
442						[Matching.new do |reply|
443							assert_equal :form, reply.form.type
444							assert_nil reply.form.instructions
445						end]
446					)
447					Command::COMMAND_MANAGER.expect(
448						:write,
449						EMPromise.reject(:test_result),
450						[Matching.new do |reply|
451							assert_equal :form, reply.form.type
452							assert_equal "wut", reply.form.instructions
453						end]
454					)
455
456					Registration::Payment::InviteCode.new(
457						customer,
458						"+15555550000"
459					).write.catch { |e| e }
460				end
461				assert_equal :test_result, result
462				assert_mock Command::COMMAND_MANAGER
463				assert_mock Registration::Payment::InviteCode::DB
464				assert_mock Registration::Payment::InviteCode::REDIS
465			end
466			em :test_write_bad_code
467
468			def test_write_bad_code_over_limit
469				result = execute_command do
470					customer = Customer.new("test", plan_name: "test_usd")
471					Registration::Payment::InviteCode::REDIS.expect(
472						:get,
473						EMPromise.resolve(11),
474						["jmp_invite_tries-test"]
475					)
476					Command::COMMAND_MANAGER.expect(
477						:write,
478						EMPromise.resolve(
479							Blather::Stanza::Iq::Command.new.tap { |iq|
480								iq.form.fields = [{ var: "code", value: "abc" }]
481							}
482						),
483						[Matching.new do |reply|
484							assert_equal :form, reply.form.type
485							assert_nil reply.form.instructions
486						end]
487					)
488					Registration::Payment::InviteCode::REDIS.expect(
489						:incr,
490						EMPromise.resolve(nil),
491						["jmp_invite_tries-test"]
492					)
493					Registration::Payment::InviteCode::REDIS.expect(
494						:expire,
495						EMPromise.resolve(nil),
496						["jmp_invite_tries-test", 60 * 60]
497					)
498					Command::COMMAND_MANAGER.expect(
499						:write,
500						EMPromise.reject(:test_result),
501						[Matching.new do |reply|
502							assert_equal :form, reply.form.type
503							assert_equal "Too many wrong attempts", reply.form.instructions
504						end]
505					)
506					Registration::Payment::InviteCode.new(
507						customer,
508						"+15555550000"
509					).write.catch { |e| e }
510				end
511				assert_equal :test_result, result
512				assert_mock Command::COMMAND_MANAGER
513				assert_mock Registration::Payment::InviteCode::REDIS
514			end
515			em :test_write_bad_code_over_limit
516		end
517	end
518
519	class FinishTest < Minitest::Test
520		Registration::Finish::REDIS = Minitest::Mock.new
521		BackendSgx::REDIS = Minitest::Mock.new
522
523		def setup
524			@sgx = Minitest::Mock.new(BackendSgx.new("test"))
525			iq = Blather::Stanza::Iq::Command.new
526			iq.from = "test\\40example.com@cheogram.com"
527			@finish = Registration::Finish.new(
528				Customer.new("test", sgx: @sgx),
529				"+15555550000"
530			)
531		end
532
533		def test_write
534			create_order = stub_request(
535				:post,
536				"https://dashboard.bandwidth.com/v1.0/accounts//orders"
537			).to_return(status: 201, body: <<~RESPONSE)
538				<OrderResponse>
539					<Order>
540						<id>test_order</id>
541					</Order>
542				</OrderResponse>
543			RESPONSE
544			stub_request(
545				:get,
546				"https://dashboard.bandwidth.com/v1.0/accounts//orders/test_order"
547			).to_return(status: 201, body: <<~RESPONSE)
548				<OrderResponse>
549					<OrderStatus>COMPLETE</OrderStatus>
550					<CompletedNumbers>
551						<TelephoneNumber>
552							<FullNumber>5555550000</FullNumber>
553						</TelephoneNumber>
554					</CompletedNumbers>
555				</OrderResponse>
556			RESPONSE
557			stub_request(
558				:post,
559				"https://api.catapult.inetwork.com/v1/users/catapult_user/phoneNumbers"
560			).with(
561				body: open(__dir__ + "/data/catapult_import_body.json").read.chomp,
562				headers: {
563					"Authorization" => "Basic Y2F0YXB1bHRfdG9rZW46Y2F0YXB1bHRfc2VjcmV0",
564					"Content-Type" => "application/json"
565				}
566			).to_return(status: 201)
567			Registration::Finish::REDIS.expect(
568				:set,
569				nil,
570				[
571					"catapult_fwd-+15555550000",
572					"sip:test%40example.com@sip.cheogram.com"
573				]
574			)
575			BackendSgx::REDIS.expect(
576				:set,
577				nil,
578				["catapult_fwd_timeout-customer_test@component", 25]
579			)
580			blather = Minitest::Mock.new
581			blather.expect(
582				:<<,
583				nil,
584				[Matching.new do |reply|
585					assert_equal :completed, reply.status
586					assert_equal :info, reply.note_type
587					assert_equal(
588						"Your JMP account has been activated as +15555550000",
589						reply.note.content
590					)
591				end]
592			)
593			execute_command(blather: blather) do
594				@sgx.expect(
595					:register!,
596					EMPromise.resolve(OpenStruct.new(error?: false)),
597					["+15555550000"]
598				)
599
600				@finish.write
601			end
602			assert_requested create_order
603			assert_mock @sgx
604			assert_mock Registration::Finish::REDIS
605			assert_mock BackendSgx::REDIS
606			assert_mock blather
607		end
608		em :test_write
609
610		def test_write_tn_fail
611			create_order = stub_request(
612				:post,
613				"https://dashboard.bandwidth.com/v1.0/accounts//orders"
614			).to_return(status: 201, body: <<~RESPONSE)
615				<OrderResponse>
616					<Order>
617						<id>test_order</id>
618					</Order>
619				</OrderResponse>
620			RESPONSE
621			stub_request(
622				:get,
623				"https://dashboard.bandwidth.com/v1.0/accounts//orders/test_order"
624			).to_return(status: 201, body: <<~RESPONSE)
625				<OrderResponse>
626					<OrderStatus>FAILED</OrderStatus>
627				</OrderResponse>
628			RESPONSE
629			blather = Minitest::Mock.new
630			blather.expect(
631				:<<,
632				nil,
633				[Matching.new do |reply|
634					assert_equal :completed, reply.status
635					assert_equal :error, reply.note_type
636					assert_equal(
637						"The JMP number +15555550000 is no longer available, " \
638						"please visit https://jmp.chat and choose another.",
639						reply.note.content
640					)
641				end]
642			)
643			execute_command(blather: blather) { @finish.write }
644			assert_requested create_order
645			assert_mock blather
646		end
647		em :test_write_tn_fail
648	end
649end