1# frozen_string_literal: true
2
3require "test_helper"
4require "tel_selections"
5
6class TelSelectionsTest < Minitest::Test
7 def setup
8 @manager = TelSelections.new(
9 redis: FakeRedis.new,
10 db: FakeDB.new,
11 memcache: FakeMemcache.new
12 )
13 end
14
15 def test_set_get
16 cust = OpenStruct.new(jid: "jid@example.com")
17 assert_kind_of TelSelections::ChooseTel, @manager[cust].sync
18 @manager.set(
19 "jid@example.com",
20 TelSelections::ChooseTel::Tn.for_pending_value("+15555550000")
21 ).sync
22 assert_kind_of TelSelections::HaveTel, @manager[cust].sync
23 end
24 em :test_set_get
25
26 def test_choose_tel_have_tel
27 jid = "jid@example.com"
28 @manager.set(
29 jid,
30 TelSelections::ChooseTel::Tn.for_pending_value("+15555550000")
31 ).sync
32 assert_equal(
33 "+15555550000",
34 @manager[OpenStruct.new(jid: jid)].then(&:choose_tel_or_data).sync.tel
35 )
36 end
37 em :test_choose_tel_have_tel
38
39 class AvailableNumberTest < Minitest::Test
40 def test_for_no_rsm
41 form = Blather::Stanza::X.new
42 form.fields = [{ var: "q", value: "226" }]
43 iris_query = TelSelections::ChooseTel::AvailableNumber
44 .for(
45 form,
46 redis: FakeRedis.new, db: FakeDB.new, memcache: FakeMemcache.new
47 ).sync.instance_variable_get(:@iris_query)
48 assert_equal(
49 { areaCode: "226", enableTNDetail: true, LCA: false, quantity: 10 },
50 iris_query
51 )
52 end
53 em :test_for_no_rsm
54
55 def test_for_rsm
56 form = Blather::Stanza::X.new
57 form.fields = [{ var: "q", value: "226" }]
58 Nokogiri::XML::Builder.with(form) do
59 set(xmlns: "http://jabber.org/protocol/rsm") do
60 max 500
61 end
62 end
63 iris_query = TelSelections::ChooseTel::AvailableNumber
64 .for(
65 form,
66 redis: FakeRedis.new, db: FakeDB.new, memcache: FakeMemcache.new
67 ).sync.instance_variable_get(:@iris_query)
68 # quantity should be 500 due to max inside tel selections
69 assert_equal(
70 { areaCode: "226", enableTNDetail: true, LCA: false, quantity: 500 },
71 iris_query
72 )
73 end
74 em :test_for_rsm
75
76 def test_for_feelinglucky
77 form = Blather::Stanza::X.new
78 form.fields = [
79 { var: "q", value: "" },
80 {
81 var: "http://jabber.org/protocol/commands#actions",
82 value: "feelinglucky"
83 }
84 ]
85 iris_query = TelSelections::ChooseTel::AvailableNumber
86 .for(
87 form,
88 redis: FakeRedis.new, db: FakeDB.new, memcache: FakeMemcache.new
89 ).sync.instance_variable_get(:@iris_query)
90 assert_equal(
91 { areaCode: "810", enableTNDetail: true, LCA: false, quantity: 10 },
92 iris_query
93 )
94 end
95 em :test_for_feelinglucky
96
97 def test_fallback
98 stub_request(
99 :get,
100 "https://dashboard.bandwidth.com/v1.0/accounts//availableNumbers" \
101 "?city=Kitchener-Waterloo&enableTNDetail=true&lCA=false&" \
102 "quantity=10&state=ON"
103 ).to_return(status: 200, body: "")
104
105 stub_request(
106 :get,
107 "https://geocoder.ca/?json=1&locate=Kitchener-Waterloo,%20ON"
108 ).to_return(status: 200, body: {
109 postal: "N2H", longt: 0, latt: 0
110 }.to_json)
111
112 stub_request(
113 :get,
114 "https://dashboard.bandwidth.com/v1.0/accounts//availableNumbers" \
115 "?areaCode=226&enableTNDetail=true&quantity=10"
116 ).to_return(status: 200, body: <<~XML)
117 <SearchResult>
118 <TelephoneNumberList>
119 <TelephoneNumber>
120 <FullNumber>22655512345</FullNumber>
121 <City>Somewhere</City>
122 <State>ON</State>
123 </TelephoneNumber>
124 </TelephoneNumberList>
125 </SearchResult>
126 XML
127
128 db = FakeDB.new(
129 ["CA", "POINT(0.0000000000 0.0000000000)", 3] =>
130 [{ "area_code" => "226" }]
131 )
132 form = Blather::Stanza::X.new
133 form.fields = [{ var: "q", value: "Kitchener, ON" }]
134 tns = execute_command do
135 TelSelections::ChooseTel::AvailableNumber
136 .for(
137 form,
138 redis: FakeRedis.new, db: db, memcache: FakeMemcache.new
139 ).sync.tns
140 end
141 assert_equal(
142 ["(226) 555-12345 (Somewhere, ON)"],
143 tns.map(&:to_s)
144 )
145 end
146 em :test_fallback
147
148 def test_local_inventory
149 stub_request(
150 :get,
151 "https://dashboard.bandwidth.com/v1.0/accounts//availableNumbers" \
152 "?city=Kitchener-Waterloo&enableTNDetail=true&lCA=false&" \
153 "quantity=10&state=ON"
154 ).to_return(status: 200, body: "")
155
156 db = FakeDB.new(
157 ["ON", "Kitchener-Waterloo"] => [{
158 "tel" => "+122655512345",
159 "region" => "ON",
160 "locality" => "Kitchener-Waterloo"
161 }]
162 )
163 form = Blather::Stanza::X.new
164 form.fields = [{ var: "q", value: "Kitchener, ON" }]
165 tns = execute_command do
166 TelSelections::ChooseTel::AvailableNumber
167 .for(
168 form,
169 redis: FakeRedis.new, db: db, memcache: FakeMemcache.new
170 ).sync.tns
171 end
172 assert_equal(
173 ["(226) 555-12345 (Kitchener-Waterloo, ON)"],
174 tns.map(&:to_s)
175 )
176 end
177 em :test_local_inventory
178 end
179
180 class TnOptionTest < Minitest::Test
181 def setup
182 @tn = TelSelections::ChooseTel::Tn::Option.new(
183 full_number: "5551234567",
184 city: "Toronto",
185 state: "ON",
186 garbage: "stuff"
187 )
188 @local_tel = TelSelections::ChooseTel::Tn::LocalInventory.new(
189 @tn,
190 "test_account",
191 price: 10.0
192 )
193 end
194
195 def test_to_s
196 assert_equal "(555) 123-4567 (Toronto, ON)", @tn.to_s
197 end
198
199 def test_tel
200 assert_equal "+15551234567", @tn.tel
201 end
202
203 def test_option
204 assert_equal(
205 Blather::Stanza::X::Field::Option.new(
206 label: "(555) 123-4567 (Toronto, ON)",
207 value: "+15551234567"
208 ),
209 @tn.option
210 )
211 end
212
213 def test_option_positive_price
214 assert_equal(
215 Blather::Stanza::X::Field::Option.new(
216 label: "(555) 123-4567 (Toronto, ON) +$10.00",
217 value: "+15551234567"
218 ),
219 @local_tel.option
220 )
221 end
222
223 def test_option_reference
224 ref = @tn.option.find("ns:reference", ns: "urn:xmpp:reference:0").first
225 assert_equal(
226 @tn.formatted_tel,
227 @tn.option.label[ref["begin"].to_i..ref["end"].to_i]
228 )
229 assert_equal "tel:+15551234567", ref["uri"]
230 end
231 end
232
233 class QTest < Minitest::Test
234 def test_for_area_code
235 q = TelSelections::ChooseTel::Q.for("226").sync
236 assert_equal({ areaCode: "226" }, q.iris_query)
237 end
238 em :test_for_area_code
239
240 def test_for_area_code_sql
241 q = TelSelections::ChooseTel::Q.for("226").sync
242 assert_equal(
243 [
244 "SELECT * FROM tel_inventory " \
245 "WHERE available_after < LOCALTIMESTAMP AND tel LIKE $1 " \
246 "AND source NOT LIKE 'xmpp:%'",
247 "+1226%"
248 ],
249 q.sql_query
250 )
251 end
252 em :test_for_area_code_sql
253
254 def test_for_npanxx
255 q = TelSelections::ChooseTel::Q.for("226666").sync
256 assert_equal({ npaNxx: "226666" }, q.iris_query)
257 end
258 em :test_for_npanxx
259
260 def test_for_npanxx_sql
261 q = TelSelections::ChooseTel::Q.for("226666").sync
262 assert_equal(
263 [
264 "SELECT * FROM tel_inventory " \
265 "WHERE available_after < LOCALTIMESTAMP AND tel LIKE $1 " \
266 "AND source NOT LIKE 'xmpp:%'",
267 "+1226666%"
268 ],
269 q.sql_query
270 )
271 end
272 em :test_for_npanxx_sql
273
274 def test_for_npanxxx
275 q = TelSelections::ChooseTel::Q.for("2266667").sync
276 assert_equal({ npaNxxx: "2266667" }, q.iris_query)
277 end
278 em :test_for_npanxxx
279
280 def test_for_npanxxx_sql
281 q = TelSelections::ChooseTel::Q.for("2266667").sync
282 assert_equal(
283 [
284 "SELECT * FROM tel_inventory " \
285 "WHERE available_after < LOCALTIMESTAMP AND tel LIKE $1 "\
286 "AND source NOT LIKE 'xmpp:%'",
287 "+12266667%"
288 ],
289 q.sql_query
290 )
291 end
292 em :test_for_npanxxx_sql
293
294 def test_for_zip
295 q = TelSelections::ChooseTel::Q.for("90210").sync
296 assert_equal({ zip: "90210" }, q.iris_query)
297 end
298 em :test_for_zip
299
300 def test_for_zip_sql
301 q = TelSelections::ChooseTel::Q.for("90210").sync
302 refute q.sql_query
303 end
304 em :test_for_zip_sql
305
306 def test_for_localvanity
307 q = TelSelections::ChooseTel::Q.for("~mboa").sync
308 assert_equal({ localVanity: "mboa" }, q.iris_query)
309 end
310 em :test_for_localvanity
311
312 def test_for_localvanity_sql
313 q = TelSelections::ChooseTel::Q.for("~mboa").sync
314 assert_equal(
315 [
316 "SELECT * FROM tel_inventory " \
317 "WHERE available_after < LOCALTIMESTAMP AND tel LIKE $1 " \
318 "AND source NOT LIKE 'xmpp:%'",
319 "%6262%"
320 ],
321 q.sql_query
322 )
323 end
324 em :test_for_localvanity_sql
325
326 def test_for_state
327 q = TelSelections::ChooseTel::Q.for("ON").sync
328 assert_equal({ state: "ON" }, q.iris_query)
329 end
330 em :test_for_state
331
332 def test_for_state_sql
333 q = TelSelections::ChooseTel::Q.for("ON").sync
334 assert_equal(
335 [
336 "SELECT * FROM tel_inventory " \
337 "WHERE available_after < LOCALTIMESTAMP AND region = $1 " \
338 "AND source NOT LIKE 'xmpp:%'",
339 "ON"
340 ],
341 q.sql_query
342 )
343 end
344 em :test_for_state_sql
345
346 def test_for_state_name
347 q = TelSelections::ChooseTel::Q.for("ontario").sync
348 assert_equal({ state: "ON" }, q.iris_query)
349 end
350 em :test_for_state_name
351
352 def test_for_state_name_sql
353 q = TelSelections::ChooseTel::Q.for("ontario").sync
354 assert_equal(
355 [
356 "SELECT * FROM tel_inventory " \
357 "WHERE available_after < LOCALTIMESTAMP AND region = $1 " \
358 "AND source NOT LIKE 'xmpp:%'",
359 "ON"
360 ],
361 q.sql_query
362 )
363 end
364 em :test_for_state_name_sql
365
366 def test_for_new_york
367 q = TelSelections::ChooseTel::Q.for("New York").sync
368 assert_equal({ state: "NY" }, q.iris_query)
369 end
370 em :test_for_new_york
371
372 def test_for_new_york_sql
373 q = TelSelections::ChooseTel::Q.for("New York").sync
374 assert_equal(
375 [
376 "SELECT * FROM tel_inventory " \
377 "WHERE available_after < LOCALTIMESTAMP AND region = $1 " \
378 "AND source NOT LIKE 'xmpp:%'",
379 "NY"
380 ],
381 q.sql_query
382 )
383 end
384 em :test_for_new_york_sql
385
386 def test_for_new_york_ny
387 q = TelSelections::ChooseTel::Q.for(
388 "New York, NY",
389 redis: FakeRedis.new, db: FakeDB.new,
390 memcache: FakeMemcache.new
391 ).sync
392 assert_equal({ city: "New York City", state: "NY" }, q.iris_query)
393 end
394 em :test_for_new_york_ny
395
396 def test_for_new_york_ny_sql
397 q = TelSelections::ChooseTel::Q.for(
398 "New York, NY",
399 redis: FakeRedis.new, db: FakeDB.new,
400 memcache: FakeMemcache.new
401 ).sync
402 assert_equal(
403 [
404 "SELECT * FROM tel_inventory " \
405 "WHERE available_after < LOCALTIMESTAMP " \
406 "AND region = $1 AND locality = $2 " \
407 "AND source NOT LIKE 'xmpp:%'",
408 "NY", "New York City"
409 ],
410 q.sql_query
411 )
412 end
413 em :test_for_new_york_ny_sql
414
415 def test_for_new_york_new_york
416 q = TelSelections::ChooseTel::Q.for(
417 "New York, New York",
418 redis: FakeRedis.new, db: FakeDB.new,
419 memcache: FakeMemcache.new
420 ).sync
421 assert_equal({ city: "New York City", state: "NY" }, q.iris_query)
422 end
423 em :test_for_new_york_new_york
424
425 def test_for_new_york_new_york_sql
426 q = TelSelections::ChooseTel::Q.for(
427 "New York, New York",
428 redis: FakeRedis.new, db: FakeDB.new,
429 memcache: FakeMemcache.new
430 ).sync
431 assert_equal(
432 [
433 "SELECT * FROM tel_inventory " \
434 "WHERE available_after < LOCALTIMESTAMP " \
435 "AND region = $1 AND locality = $2 " \
436 "AND source NOT LIKE 'xmpp:%'",
437 "NY", "New York City"
438 ],
439 q.sql_query
440 )
441 end
442 em :test_for_new_york_new_york_sql
443
444 def test_for_citystate
445 q = TelSelections::ChooseTel::Q.for(
446 "Toronto, ON",
447 redis: FakeRedis.new, db: FakeDB.new,
448 memcache: FakeMemcache.new
449 ).sync
450 assert_equal({ city: "Toronto", state: "ON" }, q.iris_query)
451 end
452 em :test_for_citystate
453
454 def test_for_citystate_sql
455 q = TelSelections::ChooseTel::Q.for(
456 "Toronto, ON",
457 redis: FakeRedis.new, db: FakeDB.new,
458 memcache: FakeMemcache.new
459 ).sync
460 assert_equal(
461 [
462 "SELECT * FROM tel_inventory " \
463 "WHERE available_after < LOCALTIMESTAMP " \
464 "AND region = $1 AND locality = $2 " \
465 "AND source NOT LIKE 'xmpp:%'",
466 "ON", "Toronto"
467 ],
468 q.sql_query
469 )
470 end
471 em :test_for_citystate_sql
472
473 def test_for_citystate_name
474 q = TelSelections::ChooseTel::Q.for(
475 "Toronto, Ontario",
476 redis: FakeRedis.new, db: FakeDB.new,
477 memcache: FakeMemcache.new
478 ).sync
479 assert_equal({ city: "Toronto", state: "ON" }, q.iris_query)
480 end
481 em :test_for_citystate_name
482
483 def test_for_citystate_name_sql
484 q = TelSelections::ChooseTel::Q.for(
485 "Toronto, Ontario",
486 redis: FakeRedis.new, db: FakeDB.new,
487 memcache: FakeMemcache.new
488 ).sync
489 assert_equal(
490 [
491 "SELECT * FROM tel_inventory " \
492 "WHERE available_after < LOCALTIMESTAMP " \
493 "AND region = $1 AND locality = $2 " \
494 "AND source NOT LIKE 'xmpp:%'",
495 "ON", "Toronto"
496 ],
497 q.sql_query
498 )
499 end
500 em :test_for_citystate_name_sql
501
502 def test_for_garbage
503 assert_raises { TelSelections::ChooseTel::Q.for("garbage").sync }
504 end
505 em :test_for_garbage
506
507 def test_offer_code
508 CustomerPlan::DB.expect(
509 :exec,
510 OpenStruct.new(cmd_tuples: 1),
511 [String, ["test", "USD", nil]]
512 )
513 CustomerPlan::DB.expect(
514 :exec,
515 OpenStruct.new(cmd_tuples: 0),
516 [String, ["test"]]
517 )
518 db = FakeDB.new(
519 ["test", "DEADBEEF"] => [{ "creator_id" => "pplus" }]
520 )
521 q = TelSelections::ChooseTel::Q.for(
522 "DEADBEEF",
523 customer: customer,
524 redis: FakeRedis.new, db: db, memcache: FakeMemcache.new
525 ).sync
526 assert_equal(
527 [
528 "SELECT * FROM tel_inventory " \
529 "WHERE available_after < LOCALTIMESTAMP AND source=$1",
530 "xmpp:pplus"
531 ],
532 q.sql_query
533 )
534 assert_mock CustomerPlan::DB
535 end
536 em :test_offer_code
537
538 def test_offer_code_invalid
539 db = FakeDB.new
540 assert_raises do
541 TelSelections::ChooseTel::Q.for(
542 "DEADBEEF",
543 customer: customer,
544 redis: FakeRedis.new, db: db, memcache: FakeMemcache.new
545 ).sync
546 end
547 assert_mock CustomerPlan::DB
548 end
549 em :test_offer_code_invalid
550
551 def test_offer_code_just_invite
552 CustomerPlan::DB.expect(
553 :exec,
554 OpenStruct.new(cmd_tuples: 1),
555 [String, ["test", "USD", nil]]
556 )
557 CustomerPlan::DB.expect(
558 :exec,
559 OpenStruct.new(cmd_tuples: 0),
560 [String, ["test"]]
561 )
562 db = FakeDB.new(
563 ["test", "DEADBEEF"] => [{ "creator_id" => "notpplus" }]
564 )
565 assert_raises do
566 TelSelections::ChooseTel::Q.for(
567 "DEADBEEF",
568 customer: customer,
569 redis: FakeRedis.new, db: db, memcache: FakeMemcache.new
570 ).sync
571 end
572 assert_mock CustomerPlan::DB
573 end
574 em :test_offer_code_just_invite
575 end
576end