test_porting_step.rb

  1# frozen_string_literal: true
  2
  3require "date"
  4require "ostruct"
  5require "test_helper"
  6
  7require "customer_info"
  8require "form_template"
  9require "form_to_h"
 10require "porting_step_repo"
 11require "port_repo"
 12
 13MINS = 1.0 / (24 * 60)
 14
 15class BlatherNotifyMock < Minitest::Mock
 16	def initialize
 17		super
 18		@exes = []
 19	end
 20
 21	def expect_execution(server, node, *args)
 22		exe = Execution.new(args)
 23		expect(
 24			:command_execution,
 25			exe,
 26			[server, node]
 27		)
 28		@exes << exe
 29	end
 30
 31	def verify
 32		super
 33		@exes.each(&:verify)
 34	end
 35
 36	class Execution < Minitest::Mock
 37		def initialize(args)
 38			super()
 39			args.each_slice(2) do |(submission, result)|
 40				expect(
 41					:fetch_and_submit,
 42					to_promise(result),
 43					**submission
 44				)
 45			end
 46		end
 47
 48		using FormToH
 49
 50		def to_promise(result)
 51			if result.is_a?(Exception)
 52				EMPromise.reject(result)
 53			else
 54				EMPromise.resolve(to_response(result))
 55			end
 56		end
 57
 58		def to_response(form)
 59			OpenStruct.new(form.to_h)
 60		end
 61	end
 62end
 63
 64def info(tel)
 65	CustomerInfo.new(
 66		plan_info: PlanInfo::NoPlan.new,
 67		tel: tel,
 68		balance: 0.to_d,
 69		cnam: nil
 70	)
 71end
 72
 73def admin_info(customer_id, tel)
 74	AdminInfo.new(
 75		jid: Blather::JID.new("#{customer_id}@example.com"),
 76		customer_id: customer_id,
 77		fwd: nil,
 78		info: info(tel),
 79		call_info: "",
 80		trust_level: "",
 81		backend: BackendSgx.new(
 82			jid: Blather::JID.new("testroute"),
 83			from_jid: Blather::JID.new("customer_#{customer_id}@example.com"),
 84			creds: {},
 85			transcription_enabled: false,
 86			registered?: false,
 87			fwd: nil,
 88			ogm_url: nil
 89		)
 90	)
 91end
 92
 93def menu
 94	FormTemplate.render("admin_menu")
 95end
 96
 97class PortingStepTest < Minitest::Test
 98	Port = Struct.new(
 99		:id,
100		:processing_status,
101		:actual_foc_date,
102		:updated_at,
103		:customer_id,
104		:tel,
105		:backend_sgx
106	)
107
108	def test_ignore_submitted_ports
109		redis = Minitest::Mock.new
110		redis.expect(:exists, EMPromise.resolve(0), ["jmp_port_freeze-01"])
111
112		step = PortingStepRepo.new(redis: redis).find(Port.new(
113			"01",
114			"SUBMITTED",
115			nil,
116			DateTime.now - 1 * MINS,
117			"ignored",
118			"9998887777",
119			Blather::JID.new("testroute")
120		)).sync
121
122		assert_kind_of PortingStepRepo::Wait, step
123	end
124	em :test_ignore_submitted_ports
125
126	def test_ignore_recent_foc
127		redis = Minitest::Mock.new
128		redis.expect(:exists, EMPromise.resolve(0), ["jmp_port_freeze-01"])
129
130		step = PortingStepRepo.new(redis: redis).find(Port.new(
131			"01",
132			"FOC",
133			DateTime.now - 5 * MINS,
134			DateTime.now - 1 * MINS,
135			"ignored",
136			"9998887777",
137			Blather::JID.new("testroute")
138		)).sync
139
140		assert_kind_of PortingStepRepo::Wait, step
141	end
142	em :test_ignore_recent_foc
143
144	def test_warn_for_late_foc
145		redis = Minitest::Mock.new
146		redis.expect(:exists, EMPromise.resolve(0), ["jmp_port_freeze-01"])
147
148		step = PortingStepRepo.new(redis: redis).find(Port.new(
149			"01",
150			"FOC",
151			DateTime.now - 25 * MINS,
152			DateTime.now - 1 * MINS,
153			"ignored",
154			"9998887777",
155			Blather::JID.new("testroute")
156		)).sync
157
158		assert_kind_of PortingStepRepo::Alert, step
159		assert_equal :late_foc, step.key
160		assert_kind_of PortingStepRepo::Wait, step.real_step
161	end
162	em :test_warn_for_late_foc
163
164	def test_already_complete
165		redis = Minitest::Mock.new
166		redis.expect(:exists, EMPromise.resolve(0), ["jmp_port_freeze-01"])
167		redis.expect(:exists, 1, ["jmp_port_complete-01"])
168
169		step = PortingStepRepo.new(redis: redis).find(Port.new(
170			"01",
171			"COMPLETE",
172			DateTime.now - 25 * MINS,
173			DateTime.now - 1 * MINS,
174			"completed",
175			"9998887777",
176			Blather::JID.new("testroute")
177		)).sync
178
179		assert_kind_of PortingStepRepo::Done, step
180		assert_mock redis
181	end
182	em :test_already_complete
183
184	def test_change_number
185		redis = Minitest::Mock.new
186		redis.expect(:exists, EMPromise.resolve(0), ["jmp_port_freeze-01"])
187		redis.expect(:exists, "0", ["jmp_port_complete-01"])
188
189		notify = BlatherNotifyMock.new
190		notify.expect_execution(
191			"sgx", "customer info",
192			{ q: "starting" }, admin_info("starting", "+19998881111").form
193		)
194
195		step = PortingStepRepo.new(
196			redis: redis,
197			blather_notify: notify,
198			admin_server: "sgx"
199		).find(Port.new(
200			"01",
201			"COMPLETE",
202			DateTime.now - 25 * MINS,
203			DateTime.now - 1 * MINS,
204			"starting",
205			"9998887777",
206			Blather::JID.new("testroute")
207		)).sync
208
209		assert_kind_of PortingStepRepo::Complete::AdminCommand::WrongNumber, step
210		assert_equal Blather::JID.new("testroute"), step.new_backend
211		assert_mock redis
212		assert_mock notify
213	end
214	em :test_change_number
215
216	def test_change_backend_only
217		redis = Minitest::Mock.new
218		redis.expect(:exists, EMPromise.resolve(0), ["jmp_port_freeze-01"])
219		redis.expect(:exists, "0", ["jmp_port_complete-01"])
220
221		notify = BlatherNotifyMock.new
222		notify.expect_execution(
223			"sgx", "customer info",
224			{ q: "starting" }, admin_info("starting", "+19998887777").form
225		)
226		notify.expect(:command_execution, nil) do |_server, node|
227			if node == "reachability"
228				raise(
229					Minitest::Assertion,
230					"reachability must not be invoked when backend needs to change"
231				)
232			end
233			false
234		end
235
236		step = PortingStepRepo.new(
237			redis: redis,
238			blather_notify: notify,
239			admin_server: "sgx"
240		).find(Port.new(
241			"01",
242			"COMPLETE",
243			DateTime.now - 25 * MINS,
244			DateTime.now - 1 * MINS,
245			"starting",
246			"+19998887777",
247			Blather::JID.new("newroute")
248		)).sync
249
250		assert_kind_of(
251			PortingStepRepo::Complete::AdminCommand::WrongNumber, step
252		)
253		assert_equal Blather::JID.new("newroute"), step.new_backend
254	end
255	em :test_change_backend_only
256
257	def test_change_number_does_not_invoke_reachability
258		redis = Minitest::Mock.new
259		redis.expect(:exists, EMPromise.resolve(0), ["jmp_port_freeze-01"])
260		redis.expect(:exists, "0", ["jmp_port_complete-01"])
261
262		notify = BlatherNotifyMock.new
263		notify.expect_execution(
264			"sgx", "customer info",
265			{ q: "starting" }, admin_info("starting", "+19998881111").form
266		)
267		notify.expect(:command_execution, nil) do |_server, node|
268			if node == "reachability"
269				raise(
270					Minitest::Assertion,
271					"reachability must not be invoked before backend change"
272				)
273			end
274			false
275		end
276
277		step = PortingStepRepo.new(
278			redis: redis,
279			blather_notify: notify,
280			admin_server: "sgx"
281		).find(Port.new(
282			"01",
283			"COMPLETE",
284			DateTime.now - 25 * MINS,
285			DateTime.now - 1 * MINS,
286			"starting",
287			"9998887777",
288			Blather::JID.new("testroute")
289		)).sync
290
291		assert_kind_of(
292			PortingStepRepo::Complete::AdminCommand::WrongNumber, step
293		)
294	end
295	em :test_change_number_does_not_invoke_reachability
296
297	def test_unknown_customer
298		redis = Minitest::Mock.new
299		redis.expect(:exists, EMPromise.resolve(0), ["jmp_port_freeze-01"])
300		redis.expect(:exists, "0", ["jmp_port_complete-01"])
301
302		notify = BlatherNotifyMock.new
303		notify.expect_execution(
304			"sgx", "customer info",
305			{ q: "unknown_customer" }, admin_info("unknown_customer", nil).form
306		)
307
308		step = PortingStepRepo.new(
309			redis: redis,
310			blather_notify: notify,
311			admin_server: "sgx"
312		).find(Port.new(
313			"01",
314			"COMPLETE",
315			DateTime.now - 25 * MINS,
316			DateTime.now - 1 * MINS,
317			"unknown_customer",
318			"9998887777",
319			Blather::JID.new("testroute")
320		)).sync
321
322		assert_kind_of PortingStepRepo::Alert, step
323		assert_equal :port_for_unknown_customer, step.key
324		assert_kind_of PortingStepRepo::Complete::NoCustomer, step.real_step
325		assert_mock redis
326		assert_mock notify
327	end
328	em :test_unknown_customer
329
330	def test_first_reachability
331		redis = Minitest::Mock.new
332		redis.expect(:exists, EMPromise.resolve(0), ["jmp_port_freeze-01"])
333		redis.expect(:exists, "0", ["jmp_port_complete-01"])
334
335		notify = BlatherNotifyMock.new
336		notify.expect_execution(
337			"sgx", "customer info",
338			{ q: "starting" }, admin_info("starting", "+19998887777").form
339		)
340
341		notify.expect_execution(
342			"sgx", "reachability",
343			{ tel: "+19998887777", type: "voice" },
344			FormTemplate.render("reachability_result", count: 0)
345		)
346
347		step = PortingStepRepo.new(
348			redis: redis,
349			blather_notify: notify,
350			admin_server: "sgx"
351		).find(Port.new(
352			"01",
353			"COMPLETE",
354			DateTime.now - 25 * MINS,
355			DateTime.now - 1 * MINS,
356			"starting",
357			"+19998887777",
358			Blather::JID.new("testroute")
359		)).sync
360
361		assert_kind_of(
362			PortingStepRepo::Complete::AdminCommand::GoodNumber::
363				Reachability::RunTest,
364			step
365		)
366		assert_equal "voice", step.type
367		assert_mock redis
368		assert_mock notify
369	end
370	em :test_first_reachability
371
372	def test_reach_sms_reachability
373		redis = Minitest::Mock.new
374		redis.expect(:exists, EMPromise.resolve(0), ["jmp_port_freeze-01"])
375		redis.expect(:exists, "0", ["jmp_port_complete-01"])
376
377		notify = BlatherNotifyMock.new
378		notify.expect_execution(
379			"sgx", "customer info",
380			{ q: "starting" }, admin_info("starting", "+19998887777").form
381		)
382
383		notify.expect_execution(
384			"sgx", "reachability",
385			{ tel: "+19998887777", type: "voice" },
386			FormTemplate.render("reachability_result", count: 1)
387		)
388
389		notify.expect_execution(
390			"sgx", "reachability",
391			{ tel: "+19998887777", type: "sms" },
392			FormTemplate.render("reachability_result", count: 0)
393		)
394
395		step = PortingStepRepo.new(
396			redis: redis,
397			blather_notify: notify,
398			admin_server: "sgx"
399		).find(Port.new(
400			"01",
401			"COMPLETE",
402			DateTime.now - 25 * MINS,
403			DateTime.now - 1 * MINS,
404			"starting",
405			"+19998887777",
406			Blather::JID.new("testroute")
407		)).sync
408
409		assert_kind_of(
410			PortingStepRepo::Complete::AdminCommand::GoodNumber::
411				Reachability::RunTest,
412			step
413		)
414		assert_equal "sms", step.type
415		assert_mock redis
416		assert_mock notify
417	end
418	em :test_reach_sms_reachability
419
420	def test_all_reachable
421		redis = Minitest::Mock.new
422		redis.expect(:exists, EMPromise.resolve(0), ["jmp_port_freeze-01"])
423		redis.expect(:exists, "0", ["jmp_port_complete-01"])
424
425		notify = BlatherNotifyMock.new
426		notify.expect_execution(
427			"sgx", "customer info",
428			{ q: "starting" }, admin_info("starting", "+19998887777").form
429		)
430
431		notify.expect_execution(
432			"sgx", "reachability",
433			{ tel: "+19998887777", type: "voice" },
434			FormTemplate.render("reachability_result", count: 1)
435		)
436
437		notify.expect_execution(
438			"sgx", "reachability",
439			{ tel: "+19998887777", type: "sms" },
440			FormTemplate.render("reachability_result", count: 1)
441		)
442
443		step = PortingStepRepo.new(
444			redis: redis,
445			blather_notify: notify,
446			admin_server: "sgx"
447		).find(Port.new(
448			"01",
449			"COMPLETE",
450			DateTime.now - 25 * MINS,
451			DateTime.now - 1 * MINS,
452			"starting",
453			"+19998887777",
454			Blather::JID.new("testroute")
455		)).sync
456
457		assert_kind_of(
458			PortingStepRepo::Complete::AdminCommand::GoodNumber::FinishUp,
459			step
460		)
461		assert_mock redis
462		assert_mock notify
463	end
464	em :test_all_reachable
465
466	def test_not_done_in_time
467		redis = Minitest::Mock.new
468		redis.expect(:exists, EMPromise.resolve(0), ["jmp_port_freeze-01"])
469		redis.expect(:exists, "0", ["jmp_port_complete-01"])
470
471		notify = BlatherNotifyMock.new
472		notify.expect_execution(
473			"sgx", "customer info",
474			{ q: "starting" }, admin_info("starting", "+19998887777").form
475		)
476
477		notify.expect_execution(
478			"sgx", "reachability",
479			{ tel: "+19998887777", type: "voice" },
480			FormTemplate.render("reachability_result", count: 0)
481		)
482
483		step = PortingStepRepo.new(
484			redis: redis,
485			blather_notify: notify,
486			admin_server: "sgx"
487		).find(Port.new(
488			"01",
489			"COMPLETE",
490			DateTime.now - 55 * MINS,
491			DateTime.now - 50 * MINS,
492			"starting",
493			"+19998887777",
494			Blather::JID.new("testroute")
495		)).sync
496
497		assert_kind_of PortingStepRepo::Alert, step
498		assert_equal :late_finish, step.key
499		assert_kind_of(
500			PortingStepRepo::Complete::AdminCommand::GoodNumber::
501				Reachability::RunTest,
502			step.real_step
503		)
504		assert_mock redis
505		assert_mock notify
506	end
507	em :test_not_done_in_time
508
509	def test_ignore_frozen_ports
510		# This tests that we ignore ports in various states
511		[
512			Port.new(
513				"01",
514				"SUBMITTED",
515				nil,
516				DateTime.now - 1 * MINS,
517				"ignored",
518				"+19998887777",
519				Blather::JID.new("testroute")
520			),
521			Port.new(
522				"01",
523				"FOC",
524				DateTime.now - 300 * MINS,
525				DateTime.now - 300 * MINS,
526				"ignored",
527				"+19998887777",
528				Blather::JID.new("testroute")
529			),
530			Port.new(
531				"01",
532				"COMPLETED",
533				DateTime.now - 10 * MINS,
534				DateTime.now - 10 * MINS,
535				"ignored",
536				"+19998887777",
537				Blather::JID.new("testroute")
538			),
539			Port.new(
540				"01",
541				"COMPLETED",
542				DateTime.now - 300 * MINS,
543				DateTime.now - 300 * MINS,
544				"ignored",
545				"+19998887777",
546				Blather::JID.new("testroute")
547			)
548		].each do |port|
549			redis = Minitest::Mock.new
550			redis.expect(:exists, EMPromise.resolve(1), ["jmp_port_freeze-01"])
551
552			step = PortingStepRepo.new(redis: redis).find(port).sync
553			assert_kind_of PortingStepRepo::Frozen, step
554		end
555	end
556	em :test_ignore_frozen_ports
557
558	def test_reachability_error_during_find
559		redis = Minitest::Mock.new
560		redis.expect(:exists, EMPromise.resolve(0), ["jmp_port_freeze-01"])
561		redis.expect(:exists, "0", ["jmp_port_complete-01"])
562
563		notify = BlatherNotifyMock.new
564		notify.expect_execution(
565			"sgx", "customer info",
566			{ q: "starting" }, admin_info("starting", "+19998887777").form
567		)
568
569		notify.expect_execution(
570			"sgx", "reachability",
571			{ tel: "+19998887777", type: "voice" },
572			RuntimeError.new("Sender not in whitelist")
573		)
574
575		step = PortingStepRepo.new(
576			redis: redis,
577			blather_notify: notify,
578			admin_server: "sgx"
579		).find(Port.new(
580			"01",
581			"COMPLETE",
582			DateTime.now - 25 * MINS,
583			DateTime.now - 1 * MINS,
584			"starting",
585			"+19998887777",
586			Blather::JID.new("testroute")
587		)).sync
588
589		assert_kind_of PortingStepRepo::Alert, step
590		assert_equal :reachability_failure, step.key
591		assert_nil step.real_step
592		assert_mock redis
593		assert_mock notify
594	end
595	em :test_reachability_error_during_find
596
597	def test_reachability_error_during_run_test
598		redis = Minitest::Mock.new
599		redis.expect(:exists, EMPromise.resolve(0), ["jmp_port_freeze-01"])
600		redis.expect(:exists, "0", ["jmp_port_complete-01"])
601
602		notify = BlatherNotifyMock.new
603		notify.expect_execution(
604			"sgx", "customer info",
605			{ q: "starting" }, admin_info("starting", "+19998887777").form
606		)
607
608		notify.expect_execution(
609			"sgx", "reachability",
610			{ tel: "+19998887777", type: "voice" },
611			FormTemplate.render("reachability_result", count: 0)
612		)
613
614		notify.expect_execution(
615			"sgx", "reachability",
616			{ tel: "+19998887777", type: "voice", reachability_tel: "+15551234567" },
617			RuntimeError.new("Sender not in whitelist")
618		)
619
620		output_mock = Minitest::Mock.new
621		output_mock.expect(
622			:warn,
623			EMPromise.resolve(nil),
624			[
625				"01",
626				:reachability_failure,
627				Matching.new { |m| m =~ /Error checking.*reachability/ }
628			]
629		)
630		output = MockOutputs.new(output_mock)
631
632		port = Port.new(
633			"01",
634			"COMPLETE",
635			DateTime.now - 25 * MINS,
636			DateTime.now - 1 * MINS,
637			"starting",
638			"+19998887777",
639			Blather::JID.new("testroute")
640		)
641
642		step = PortingStepRepo.new(
643			redis: redis,
644			blather_notify: notify,
645			admin_server: "sgx",
646			output: output,
647			testing_tel: "+15551234567"
648		).find(port).sync
649
650		assert_kind_of(
651			PortingStepRepo::Complete::AdminCommand::GoodNumber::
652				Reachability::RunTest,
653			step
654		)
655
656		step.perform_next_step.sync
657
658		assert_mock redis
659		assert_mock notify
660		assert_mock output_mock
661	end
662	em :test_reachability_error_during_run_test
663end