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_number_does_not_invoke_reachability
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", "+19998881111").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 before backend 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			"9998887777",
247			Blather::JID.new("testroute")
248		)).sync
249
250		assert_kind_of(
251			PortingStepRepo::Complete::AdminCommand::WrongNumber, step
252		)
253	end
254	em :test_change_number_does_not_invoke_reachability
255
256	def test_unknown_customer
257		redis = Minitest::Mock.new
258		redis.expect(:exists, EMPromise.resolve(0), ["jmp_port_freeze-01"])
259		redis.expect(:exists, "0", ["jmp_port_complete-01"])
260
261		notify = BlatherNotifyMock.new
262		notify.expect_execution(
263			"sgx", "customer info",
264			{ q: "unknown_customer" }, admin_info("unknown_customer", nil).form
265		)
266
267		step = PortingStepRepo.new(
268			redis: redis,
269			blather_notify: notify,
270			admin_server: "sgx"
271		).find(Port.new(
272			"01",
273			"COMPLETE",
274			DateTime.now - 25 * MINS,
275			DateTime.now - 1 * MINS,
276			"unknown_customer",
277			"9998887777",
278			Blather::JID.new("testroute")
279		)).sync
280
281		assert_kind_of PortingStepRepo::Alert, step
282		assert_equal :port_for_unknown_customer, step.key
283		assert_kind_of PortingStepRepo::Complete::NoCustomer, step.real_step
284		assert_mock redis
285		assert_mock notify
286	end
287	em :test_unknown_customer
288
289	def test_first_reachability
290		redis = Minitest::Mock.new
291		redis.expect(:exists, EMPromise.resolve(0), ["jmp_port_freeze-01"])
292		redis.expect(:exists, "0", ["jmp_port_complete-01"])
293
294		notify = BlatherNotifyMock.new
295		notify.expect_execution(
296			"sgx", "customer info",
297			{ q: "starting" }, admin_info("starting", "+19998887777").form
298		)
299
300		notify.expect_execution(
301			"sgx", "reachability",
302			{ tel: "+19998887777", type: "voice" },
303			FormTemplate.render("reachability_result", count: 0)
304		)
305
306		step = PortingStepRepo.new(
307			redis: redis,
308			blather_notify: notify,
309			admin_server: "sgx"
310		).find(Port.new(
311			"01",
312			"COMPLETE",
313			DateTime.now - 25 * MINS,
314			DateTime.now - 1 * MINS,
315			"starting",
316			"+19998887777",
317			Blather::JID.new("testroute")
318		)).sync
319
320		assert_kind_of(
321			PortingStepRepo::Complete::AdminCommand::GoodNumber::
322				Reachability::RunTest,
323			step
324		)
325		assert_equal "voice", step.type
326		assert_mock redis
327		assert_mock notify
328	end
329	em :test_first_reachability
330
331	def test_reach_sms_reachability
332		redis = Minitest::Mock.new
333		redis.expect(:exists, EMPromise.resolve(0), ["jmp_port_freeze-01"])
334		redis.expect(:exists, "0", ["jmp_port_complete-01"])
335
336		notify = BlatherNotifyMock.new
337		notify.expect_execution(
338			"sgx", "customer info",
339			{ q: "starting" }, admin_info("starting", "+19998887777").form
340		)
341
342		notify.expect_execution(
343			"sgx", "reachability",
344			{ tel: "+19998887777", type: "voice" },
345			FormTemplate.render("reachability_result", count: 1)
346		)
347
348		notify.expect_execution(
349			"sgx", "reachability",
350			{ tel: "+19998887777", type: "sms" },
351			FormTemplate.render("reachability_result", count: 0)
352		)
353
354		step = PortingStepRepo.new(
355			redis: redis,
356			blather_notify: notify,
357			admin_server: "sgx"
358		).find(Port.new(
359			"01",
360			"COMPLETE",
361			DateTime.now - 25 * MINS,
362			DateTime.now - 1 * MINS,
363			"starting",
364			"+19998887777",
365			Blather::JID.new("testroute")
366		)).sync
367
368		assert_kind_of(
369			PortingStepRepo::Complete::AdminCommand::GoodNumber::
370				Reachability::RunTest,
371			step
372		)
373		assert_equal "sms", step.type
374		assert_mock redis
375		assert_mock notify
376	end
377	em :test_reach_sms_reachability
378
379	def test_all_reachable
380		redis = Minitest::Mock.new
381		redis.expect(:exists, EMPromise.resolve(0), ["jmp_port_freeze-01"])
382		redis.expect(:exists, "0", ["jmp_port_complete-01"])
383
384		notify = BlatherNotifyMock.new
385		notify.expect_execution(
386			"sgx", "customer info",
387			{ q: "starting" }, admin_info("starting", "+19998887777").form
388		)
389
390		notify.expect_execution(
391			"sgx", "reachability",
392			{ tel: "+19998887777", type: "voice" },
393			FormTemplate.render("reachability_result", count: 1)
394		)
395
396		notify.expect_execution(
397			"sgx", "reachability",
398			{ tel: "+19998887777", type: "sms" },
399			FormTemplate.render("reachability_result", count: 1)
400		)
401
402		step = PortingStepRepo.new(
403			redis: redis,
404			blather_notify: notify,
405			admin_server: "sgx"
406		).find(Port.new(
407			"01",
408			"COMPLETE",
409			DateTime.now - 25 * MINS,
410			DateTime.now - 1 * MINS,
411			"starting",
412			"+19998887777",
413			Blather::JID.new("testroute")
414		)).sync
415
416		assert_kind_of(
417			PortingStepRepo::Complete::AdminCommand::GoodNumber::FinishUp,
418			step
419		)
420		assert_mock redis
421		assert_mock notify
422	end
423	em :test_all_reachable
424
425	def test_not_done_in_time
426		redis = Minitest::Mock.new
427		redis.expect(:exists, EMPromise.resolve(0), ["jmp_port_freeze-01"])
428		redis.expect(:exists, "0", ["jmp_port_complete-01"])
429
430		notify = BlatherNotifyMock.new
431		notify.expect_execution(
432			"sgx", "customer info",
433			{ q: "starting" }, admin_info("starting", "+19998887777").form
434		)
435
436		notify.expect_execution(
437			"sgx", "reachability",
438			{ tel: "+19998887777", type: "voice" },
439			FormTemplate.render("reachability_result", count: 0)
440		)
441
442		step = PortingStepRepo.new(
443			redis: redis,
444			blather_notify: notify,
445			admin_server: "sgx"
446		).find(Port.new(
447			"01",
448			"COMPLETE",
449			DateTime.now - 55 * MINS,
450			DateTime.now - 50 * MINS,
451			"starting",
452			"+19998887777",
453			Blather::JID.new("testroute")
454		)).sync
455
456		assert_kind_of PortingStepRepo::Alert, step
457		assert_equal :late_finish, step.key
458		assert_kind_of(
459			PortingStepRepo::Complete::AdminCommand::GoodNumber::
460				Reachability::RunTest,
461			step.real_step
462		)
463		assert_mock redis
464		assert_mock notify
465	end
466	em :test_not_done_in_time
467
468	def test_ignore_frozen_ports
469		# This tests that we ignore ports in various states
470		[
471			Port.new(
472				"01",
473				"SUBMITTED",
474				nil,
475				DateTime.now - 1 * MINS,
476				"ignored",
477				"+19998887777",
478				Blather::JID.new("testroute")
479			),
480			Port.new(
481				"01",
482				"FOC",
483				DateTime.now - 300 * MINS,
484				DateTime.now - 300 * MINS,
485				"ignored",
486				"+19998887777",
487				Blather::JID.new("testroute")
488			),
489			Port.new(
490				"01",
491				"COMPLETED",
492				DateTime.now - 10 * MINS,
493				DateTime.now - 10 * MINS,
494				"ignored",
495				"+19998887777",
496				Blather::JID.new("testroute")
497			),
498			Port.new(
499				"01",
500				"COMPLETED",
501				DateTime.now - 300 * MINS,
502				DateTime.now - 300 * MINS,
503				"ignored",
504				"+19998887777",
505				Blather::JID.new("testroute")
506			)
507		].each do |port|
508			redis = Minitest::Mock.new
509			redis.expect(:exists, EMPromise.resolve(1), ["jmp_port_freeze-01"])
510
511			step = PortingStepRepo.new(redis: redis).find(port).sync
512			assert_kind_of PortingStepRepo::Frozen, step
513		end
514	end
515	em :test_ignore_frozen_ports
516
517	def test_reachability_error_during_find
518		redis = Minitest::Mock.new
519		redis.expect(:exists, EMPromise.resolve(0), ["jmp_port_freeze-01"])
520		redis.expect(:exists, "0", ["jmp_port_complete-01"])
521
522		notify = BlatherNotifyMock.new
523		notify.expect_execution(
524			"sgx", "customer info",
525			{ q: "starting" }, admin_info("starting", "+19998887777").form
526		)
527
528		notify.expect_execution(
529			"sgx", "reachability",
530			{ tel: "+19998887777", type: "voice" },
531			RuntimeError.new("Sender not in whitelist")
532		)
533
534		step = PortingStepRepo.new(
535			redis: redis,
536			blather_notify: notify,
537			admin_server: "sgx"
538		).find(Port.new(
539			"01",
540			"COMPLETE",
541			DateTime.now - 25 * MINS,
542			DateTime.now - 1 * MINS,
543			"starting",
544			"+19998887777",
545			Blather::JID.new("testroute")
546		)).sync
547
548		assert_kind_of PortingStepRepo::Alert, step
549		assert_equal :reachability_failure, step.key
550		assert_nil step.real_step
551		assert_mock redis
552		assert_mock notify
553	end
554	em :test_reachability_error_during_find
555
556	def test_reachability_error_during_run_test
557		redis = Minitest::Mock.new
558		redis.expect(:exists, EMPromise.resolve(0), ["jmp_port_freeze-01"])
559		redis.expect(:exists, "0", ["jmp_port_complete-01"])
560
561		notify = BlatherNotifyMock.new
562		notify.expect_execution(
563			"sgx", "customer info",
564			{ q: "starting" }, admin_info("starting", "+19998887777").form
565		)
566
567		notify.expect_execution(
568			"sgx", "reachability",
569			{ tel: "+19998887777", type: "voice" },
570			FormTemplate.render("reachability_result", count: 0)
571		)
572
573		notify.expect_execution(
574			"sgx", "reachability",
575			{ tel: "+19998887777", type: "voice", reachability_tel: "+15551234567" },
576			RuntimeError.new("Sender not in whitelist")
577		)
578
579		output_mock = Minitest::Mock.new
580		output_mock.expect(
581			:warn,
582			EMPromise.resolve(nil),
583			[
584				"01",
585				:reachability_failure,
586				Matching.new { |m| m =~ /Error checking.*reachability/ }
587			]
588		)
589		output = MockOutputs.new(output_mock)
590
591		port = Port.new(
592			"01",
593			"COMPLETE",
594			DateTime.now - 25 * MINS,
595			DateTime.now - 1 * MINS,
596			"starting",
597			"+19998887777",
598			Blather::JID.new("testroute")
599		)
600
601		step = PortingStepRepo.new(
602			redis: redis,
603			blather_notify: notify,
604			admin_server: "sgx",
605			output: output,
606			testing_tel: "+15551234567"
607		).find(port).sync
608
609		assert_kind_of(
610			PortingStepRepo::Complete::AdminCommand::GoodNumber::
611				Reachability::RunTest,
612			step
613		)
614
615		step.perform_next_step.sync
616
617		assert_mock redis
618		assert_mock notify
619		assert_mock output_mock
620	end
621	em :test_reachability_error_during_run_test
622end