1# frozen_string_literal: true
2
3require "test_helper"
4require "admin_command"
5require "admin_action"
6
7BackendSgx::IQ_MANAGER = Minitest::Mock.new
8BackendSgx::REDIS = Minitest::Mock.new
9AdminAction::LaunchSnikket::IQ_MANAGER = Minitest::Mock.new
10Customer::BLATHER = Minitest::Mock.new
11AdminActionRepo::REDIS = Minitest::Mock.new
12TrivialBackendSgxRepo::REDIS = Minitest::Mock.new
13BandwidthTnRepo::DB = Minitest::Mock.new
14AdminAction::NumberChange::REDIS = Minitest::Mock.new
15
16class AdminCommandTest < Minitest::Test
17 def assert_undoable_form(iq, note_type=nil, note_text=nil)
18 assert_equal :form, iq.form.type
19 [:execute, :next, :complete].each do |action|
20 assert iq.allowed_actions.include?(action)
21 end
22 assert_equal iq.note_type, note_type if note_type
23
24 assert iq.note_text = note_text if note_text
25 end
26
27 def admin_command(tel="+15556667777", registered: OpenStruct.new(phone: tel))
28 sgx = Minitest::Mock.new(OpenStruct.new(
29 registered?: registered
30 ))
31 [
32 sgx,
33 AdminCommand.new(
34 customer(sgx: sgx),
35 CustomerRepo.new(db: FakeDB.new),
36 AdminActionRepo.new,
37 Snikket::Repo.new(db: FakeDB.new)
38 )
39 ]
40 end
41
42 def test_no_user
43 q_form = Blather::Stanza::Iq::Command.new
44 q_form.action = :complete
45 q_form.form.fields = [
46 { var: "q", value: "testuser" }
47 ]
48
49 customer_repo = Minitest::Mock.new
50
51 result = execute_command {
52 customer_repo.expect(
53 :find_by_format,
54 EMPromise.resolve(OpenStruct.new(
55 customer_id: "testuser",
56 billing_customer_id: "testuser",
57 balance: 0.to_d,
58 jid: Blather::JID.new("test@example.com"),
59 tndetails: {}
60 )),
61 ["testuser"]
62 )
63
64 customer_repo.expect(
65 :find_by_format,
66 EMPromise.resolve(nil),
67 [Blather::JID]
68 )
69
70 customer_repo.expect(
71 :find_by_format,
72 EMPromise.resolve(nil),
73 [ProxiedJID]
74 )
75
76 TrivialBackendSgxRepo::REDIS.expect(
77 :get,
78 EMPromise.resolve(nil),
79 ["jmp_customer_backend_sgx-testuser"]
80 )
81
82 TrustLevelRepo::REDIS.expect(
83 :get,
84 EMPromise.resolve("Customer"),
85 ["jmp_customer_trust_level-testuser"]
86 )
87
88 TrustLevelRepo::DB.expect(
89 :query_one,
90 EMPromise.resolve({ settled_amount: 0 }),
91 [String, "testuser"], default: {}
92 )
93
94 Subaccount::DB.expect(
95 :query_defer,
96 EMPromise.resolve([]),
97 [String, ["testuser"]]
98 )
99
100 Command::COMMAND_MANAGER.expect(
101 :write,
102 EMPromise.resolve(q_form),
103 [Matching.new do |iq|
104 assert_equal :form, iq.form.type
105 assert iq.form.field("q")
106 end]
107 )
108
109 AdminCommand.for(nil, customer_repo).start.catch { |e| e }
110 }
111
112 assert result.stanza.completed?
113 assert_mock customer_repo
114 assert_mock Command::COMMAND_MANAGER
115 assert_mock TrustLevelRepo::REDIS
116 assert_mock Subaccount::DB
117 assert_mock TrivialBackendSgxRepo::REDIS
118 end
119 em :test_no_user
120
121 def test_action_launch_snikket
122 sgx, admin = admin_command
123 domain_form = Blather::Stanza::Iq::Command.new
124 domain_form.form.fields = [
125 { var: "domain", value: "test.snikket.chat" }
126 ]
127
128 launched = Snikket::Launched.new
129 launched << Niceogiri::XML::Node.new(
130 :launched, launched.document, "xmpp:snikket.org/hosting/v1"
131 ).tap { |inner|
132 inner << Niceogiri::XML::Node.new(
133 :'instance-id', launched.document, "xmpp:snikket.org/hosting/v1"
134 ).tap { |id|
135 id.content = "si-1234"
136 }
137 inner << Niceogiri::XML::Node.new(
138 :bootstrap, launched.document, "xmpp:snikket.org/hosting/v1"
139 ).tap { |bootstrap|
140 bootstrap << Niceogiri::XML::Node.new(
141 :token, launched.document, "xmpp:snikket.org/hosting/v1"
142 ).tap { |token|
143 token.content = "TOKEN"
144 }
145 }
146 }
147
148 result = execute_command {
149 Command::COMMAND_MANAGER.expect(
150 :write,
151 EMPromise.resolve(domain_form),
152 [Matching.new do |iq|
153 assert_equal :form, iq.form.type
154 assert iq.form.field("domain")
155 end]
156 )
157 Command::COMMAND_MANAGER.expect(
158 :write,
159 EMPromise.reject(:test_result),
160 [Matching.new do |iq|
161 assert :result, iq.type
162 assert(
163 "https://test.snikket.chat/invites_bootstrap?token=TOKEN",
164 iq.form.field("bootstrap-uri").value
165 )
166 end]
167 )
168
169 AdminAction::LaunchSnikket::IQ_MANAGER.expect(
170 :write,
171 EMPromise.resolve(launched),
172 [Matching.new do |iq|
173 assert_equal :set, iq.type
174 assert_equal CONFIG[:snikket_hosting_api], iq.to.to_s
175 assert_equal(
176 "test.snikket.chat",
177 iq.xpath(
178 "./ns:launch/ns:domain",
179 ns: "xmpp:snikket.org/hosting/v1"
180 ).text
181 )
182 end]
183 )
184
185 admin.action_launch_snikket.catch { |e| e }
186 }
187
188 assert_equal :test_result, result
189 assert_mock sgx
190 assert_mock AdminAction::LaunchSnikket::IQ_MANAGER
191 assert_mock Customer::BLATHER
192 end
193 em :test_action_launch_snikket
194
195 def test_action_cancel_account
196 req = stub_request(:post, "https://api.churnbuster.io/v1/cancellations")
197 .with(
198 body: {
199 customer: {
200 source: "braintree",
201 source_id: "test",
202 email: "test@smtp.cheogram.com",
203 properties: {}
204 },
205 subscription: {
206 source: "braintree",
207 source_id: "test"
208 }
209 }.to_json
210 )
211 .to_return(status: 200, body: "", headers: {})
212
213 sgx, admin = admin_command
214
215 Customer::BLATHER.expect(
216 :<<,
217 EMPromise.resolve(nil),
218 [
219 Matching.new do |m|
220 assert_equal "Your JMP account has been cancelled.", m.body
221 assert_equal "test@example.net", m.to.to_s
222 assert_equal "notify_from@component", m.from.to_s
223 end
224 ]
225 )
226
227 Customer::BLATHER.expect(
228 :<<,
229 EMPromise.resolve(nil),
230 [
231 Matching.new do |iq|
232 assert iq.remove?
233 assert_equal "test@example.net", iq.to.to_s
234 assert_equal "component", iq.from.to_s
235 end
236 ]
237 )
238
239 sgx.expect(:deregister!, EMPromise.resolve(nil))
240
241 stub_request(
242 :post,
243 "https://dashboard.bandwidth.com/v1.0/accounts//disconnects"
244 ).with(
245 body: {
246 name: "test",
247 DisconnectTelephoneNumberOrderType: {
248 TelephoneNumberList: {
249 TelephoneNumber: "5556667777"
250 }
251 }
252 }.to_xml(indent: 0, root: "DisconnectTelephoneNumberOrder")
253 ).to_return(status: 200, body: "")
254
255 admin.action_cancel_account.sync
256
257 assert_mock sgx
258 assert_mock BackendSgx::IQ_MANAGER
259 assert_mock Customer::BLATHER
260 assert_requested req
261 end
262 em :test_action_cancel_account
263
264 def test_action_cancel_account_keep_number
265 rate_center_response = {
266 "TelephoneNumberDetails": {
267 State: "NY",
268 RateCenter: "NWYRCYZN01"
269 }
270 }.to_xml(indent: 0, root: "TelephoneNumberResponse")
271
272 bandwidth_req = stub_request(
273 :get,
274 "https://dashboard.bandwidth.com/v1.0/tns/5566667777/ratecenter"
275 )
276 .with(
277 headers: {
278 "Accept" => "application/xml",
279 "Accept-Encoding" => "gzip, compressed",
280 "Authorization" => "Basic dGVzdF9id191c2VyOnRlc3RfYndfcGFzc3dvcmQ=",
281 "User-Agent" => "Ruby-Bandwidth-Iris"
282 },
283 body: ""
284 )
285 .to_return(status: 200, body: rate_center_response, headers: {})
286
287 churnbuster_req = stub_request(
288 :post,
289 "https://api.churnbuster.io/v1/cancellations"
290 )
291 .with(
292 body: {
293 customer: {
294 source: "braintree",
295 source_id: "test",
296 email: "test@smtp.cheogram.com",
297 properties: {}
298 },
299 subscription: {
300 source: "braintree",
301 source_id: "test"
302 }
303 }.to_json
304 )
305 .to_return(status: 200, body: "", headers: {})
306
307 sgx, admin = admin_command("+15566667777")
308
309 sql_params = ["+15566667777", "NY", "NWYRCYZN01", "test_bw_account"]
310
311 BandwidthTnRepo::DB.expect(
312 :exec,
313 EMPromise.resolve(nil),
314 [String, sql_params]
315 )
316
317 Customer::BLATHER.expect(
318 :<<,
319 EMPromise.resolve(nil),
320 [
321 Matching.new do |m|
322 assert_equal "Your JMP account has been cancelled.", m.body
323 assert_equal "test@example.net", m.to.to_s
324 assert_equal "notify_from@component", m.from.to_s
325 end
326 ]
327 )
328
329 Customer::BLATHER.expect(
330 :<<,
331 EMPromise.resolve(nil),
332 [
333 Matching.new do |iq|
334 assert iq.remove?
335 assert_equal "test@example.net", iq.to.to_s
336 assert_equal "component", iq.from.to_s
337 end
338 ]
339 )
340
341 sgx.expect(:deregister!, EMPromise.resolve(nil))
342
343 admin.action_cancel_account.sync
344
345 assert_mock sgx
346 assert_mock BackendSgx::IQ_MANAGER
347 assert_mock Customer::BLATHER
348 assert_mock BandwidthTnRepo::DB
349
350 assert_requested churnbuster_req
351 assert_requested bandwidth_req
352 end
353 em :test_action_cancel_account_keep_number
354
355 def test_change_num_default_sgx
356 # Same as default given by `sgx.registered?` returned by `admin_command`
357 old_tel = "+15556667777"
358 new_tel = "+12222222222"
359
360 admin_number_change_form = Blather::Stanza::Iq::Command.new
361 admin_number_change_form.form.fields = [
362 { var: "new_tel", value: new_tel },
363 { var: "should_delete", value: false }
364 ]
365 admin_number_change_form.from = "test@example.com"
366
367 execute_command { |exe|
368 sgx, admin = admin_command
369
370 exe.customer_repo.expect(
371 :find_by_jid,
372 EMPromise.resolve(customer(sgx: sgx)),
373 [Matching.new do |jid|
374 assert jid.is_a? Blather::JID
375 assert_equal jid.stripped.to_s, "test@example.com"
376 end]
377 )
378
379 AdminAction::NumberChange::REDIS.expect(
380 :exists,
381 EMPromise.resolve(1),
382 ["catapult_jid-#{old_tel}"]
383 )
384
385 AdminAction::NumberChange::REDIS.expect(
386 :get,
387 EMPromise.resolve(nil), # Default SGX creds
388 ["jmp_customer_backend_sgx-test"]
389 )
390
391 AdminAction::NumberChange::REDIS.expect(
392 :rename,
393 EMPromise.resolve(nil), # Default SGX creds
394 ["catapult_fwd-#{old_tel}", "catapult_fwd-#{new_tel}"]
395 )
396
397 BackendSgx::REDIS.expect(
398 :set,
399 EMPromise.resolve(nil), # Default SGX creds
400 ["catapult_jid-#{new_tel}", "customer_test@component"]
401 )
402
403 expected_xadd_args = {
404 customer_id: "test",
405 old_tel: old_tel,
406 new_tel: new_tel,
407 should_delete: nil,
408 actor_id: "test",
409 class: "NumberChange",
410 direction: :forward
411 }
412
413 AdminActionRepo::REDIS.expect(
414 :xadd,
415 EMPromise.resolve(nil)
416 ) do |admin_actions, star, **xadd_args|
417 assert_equal admin_actions, "admin_actions"
418 assert_equal star, "*"
419
420 xadd_args.each do |k, v|
421 assert_equal v, expected_xadd_args[k]
422 end
423 end
424
425 # Make sure the IBR record was sent off for
426 # the new number.
427 BackendSgx::IQ_MANAGER.expect(
428 :write,
429 EMPromise.resolve(nil),
430 [Matching.new do |iq|
431 assert_equal iq.nick, "test_bw_account"
432 assert_equal iq.username, "test_bw_user"
433 assert_equal iq.password, "test_bw_password"
434 assert_equal iq.phone, new_tel
435 end]
436 )
437
438 Command::COMMAND_MANAGER.expect(
439 :write,
440 EMPromise.resolve(admin_number_change_form),
441 [Matching.new do |iq|
442 assert_undoable_form(iq)
443 assert iq.form.field("new_tel")
444 assert iq.form.field("should_delete")
445 end]
446 )
447
448 admin.action_number_change.sync
449
450 assert_mock Command::COMMAND_MANAGER
451 assert_mock exe.customer_repo
452 assert_mock AdminAction::NumberChange::REDIS
453 assert_mock AdminActionRepo::REDIS
454 assert_mock BackendSgx::IQ_MANAGER
455 }
456 end
457 em :test_change_num_default_sgx
458end