adhearsion 2.6.4 → 3.0.0.beta1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.travis.yml +6 -9
- data/CHANGELOG.md +22 -7
- data/Gemfile +2 -0
- data/README.markdown +4 -5
- data/Rakefile +1 -2
- data/adhearsion.gemspec +19 -8
- data/features/cli_create.feature +19 -3
- data/features/step_definitions/cli_steps.rb +0 -11
- data/features/support/env.rb +3 -4
- data/lib/adhearsion.rb +48 -27
- data/lib/adhearsion/call.rb +34 -50
- data/lib/adhearsion/call_controller.rb +6 -12
- data/lib/adhearsion/call_controller/dial.rb +15 -53
- data/lib/adhearsion/call_controller/input.rb +39 -162
- data/lib/adhearsion/call_controller/input/ask_grammar_builder.rb +44 -0
- data/lib/adhearsion/call_controller/input/menu_builder.rb +136 -0
- data/lib/adhearsion/call_controller/input/prompt_builder.rb +78 -0
- data/lib/adhearsion/call_controller/input/result.rb +46 -0
- data/lib/adhearsion/call_controller/output.rb +48 -67
- data/lib/adhearsion/call_controller/output/abstract_player.rb +3 -3
- data/lib/adhearsion/call_controller/output/async_player.rb +3 -3
- data/lib/adhearsion/call_controller/output/player.rb +1 -1
- data/lib/adhearsion/call_controller/record.rb +23 -8
- data/lib/adhearsion/calls.rb +1 -1
- data/lib/adhearsion/cli_commands/ahn_command.rb +2 -65
- data/lib/adhearsion/cli_commands/thor_errors.rb +0 -6
- data/lib/adhearsion/configuration.rb +91 -39
- data/lib/adhearsion/core_ext/blather/stanza.rb +41 -0
- data/lib/adhearsion/core_ext/blather/stanza/presence.rb +13 -0
- data/lib/adhearsion/error.rb +5 -0
- data/lib/adhearsion/event.rb +21 -0
- data/lib/adhearsion/event/active_speaker.rb +11 -0
- data/lib/adhearsion/event/answered.rb +11 -0
- data/lib/adhearsion/event/asterisk.rb +10 -0
- data/lib/adhearsion/event/asterisk/ami.rb +34 -0
- data/lib/adhearsion/event/complete.rb +75 -0
- data/lib/adhearsion/event/dtmf.rb +11 -0
- data/lib/adhearsion/event/end.rb +22 -0
- data/lib/adhearsion/event/input_timers_started.rb +9 -0
- data/lib/adhearsion/event/joined.rb +17 -0
- data/lib/adhearsion/event/offer.rb +14 -0
- data/lib/adhearsion/event/ringing.rb +11 -0
- data/lib/adhearsion/event/started_speaking.rb +13 -0
- data/lib/adhearsion/event/stopped_speaking.rb +13 -0
- data/lib/adhearsion/event/unjoined.rb +17 -0
- data/lib/adhearsion/events.rb +47 -66
- data/lib/adhearsion/foundation.rb +0 -1
- data/lib/adhearsion/foundation/object.rb +0 -5
- data/lib/adhearsion/generators/app/app_generator.rb +4 -1
- data/lib/adhearsion/generators/app/templates/Gemfile.erb +2 -10
- data/lib/adhearsion/generators/app/templates/adhearsion.erb +9 -9
- data/lib/adhearsion/generators/app/templates/config.ru +7 -0
- data/lib/adhearsion/generators/app/templates/en.yml +4 -0
- data/lib/adhearsion/generators/app/templates/events.erb +2 -2
- data/lib/adhearsion/generators/app/templates/hello_world.wav +0 -0
- data/lib/adhearsion/generators/app/templates/simon_game.rb +2 -1
- data/lib/adhearsion/generators/app/templates/simon_game_spec.rb +2 -2
- data/lib/adhearsion/has_headers.rb +34 -0
- data/lib/adhearsion/http_server.rb +37 -0
- data/lib/adhearsion/initializer.rb +19 -153
- data/lib/adhearsion/logging.rb +6 -25
- data/lib/adhearsion/outbound_call.rb +5 -5
- data/lib/adhearsion/plugin.rb +1 -0
- data/lib/adhearsion/protocol_error.rb +26 -0
- data/lib/adhearsion/rayo.rb +30 -0
- data/lib/adhearsion/rayo/client.rb +62 -0
- data/lib/adhearsion/rayo/client/component_registry.rb +33 -0
- data/lib/adhearsion/rayo/command.rb +21 -0
- data/lib/adhearsion/rayo/command/accept.rb +16 -0
- data/lib/adhearsion/rayo/command/answer.rb +16 -0
- data/lib/adhearsion/rayo/command/dial.rb +57 -0
- data/lib/adhearsion/rayo/command/hangup.rb +16 -0
- data/lib/adhearsion/rayo/command/join.rb +43 -0
- data/lib/adhearsion/rayo/command/mute.rb +13 -0
- data/lib/adhearsion/rayo/command/redirect.rb +23 -0
- data/lib/adhearsion/rayo/command/reject.rb +40 -0
- data/lib/adhearsion/rayo/command/unjoin.rb +24 -0
- data/lib/adhearsion/rayo/command/unmute.rb +13 -0
- data/lib/adhearsion/rayo/command_node.rb +47 -0
- data/lib/adhearsion/rayo/component.rb +21 -0
- data/lib/adhearsion/rayo/component/asterisk.rb +13 -0
- data/lib/adhearsion/rayo/component/asterisk/agi.rb +14 -0
- data/lib/adhearsion/rayo/component/asterisk/agi/command.rb +46 -0
- data/lib/adhearsion/rayo/component/asterisk/ami.rb +14 -0
- data/lib/adhearsion/rayo/component/asterisk/ami/action.rb +61 -0
- data/lib/adhearsion/rayo/component/component_node.rb +90 -0
- data/lib/adhearsion/rayo/component/input.rb +186 -0
- data/lib/adhearsion/rayo/component/output.rb +471 -0
- data/lib/adhearsion/rayo/component/prompt.rb +53 -0
- data/lib/adhearsion/rayo/component/receive_fax.rb +26 -0
- data/lib/adhearsion/rayo/component/record.rb +165 -0
- data/lib/adhearsion/rayo/component/send_fax.rb +64 -0
- data/lib/adhearsion/rayo/component/stop.rb +11 -0
- data/lib/adhearsion/rayo/connection.rb +12 -0
- data/lib/adhearsion/rayo/connection/asterisk.rb +74 -0
- data/lib/adhearsion/rayo/connection/connected.rb +22 -0
- data/lib/adhearsion/rayo/connection/generic_connection.rb +22 -0
- data/lib/adhearsion/rayo/connection/xmpp.rb +198 -0
- data/lib/adhearsion/rayo/disconnected_error.rb +22 -0
- data/lib/adhearsion/{punchblock_plugin → rayo}/initializer.rb +19 -19
- data/lib/adhearsion/rayo/rayo_node.rb +127 -0
- data/lib/adhearsion/rayo/ref.rb +57 -0
- data/lib/adhearsion/statistics.rb +1 -1
- data/lib/adhearsion/tasks.rb +1 -2
- data/lib/adhearsion/tasks/configuration.rb +1 -1
- data/lib/adhearsion/tasks/environment.rb +0 -2
- data/lib/adhearsion/tasks/i18n.rb +49 -0
- data/lib/adhearsion/translator.rb +11 -0
- data/lib/adhearsion/translator/asterisk.rb +234 -0
- data/lib/adhearsion/translator/asterisk/agi_app.rb +17 -0
- data/lib/adhearsion/translator/asterisk/agi_command.rb +45 -0
- data/lib/adhearsion/translator/asterisk/ami_error_converter.rb +20 -0
- data/lib/adhearsion/translator/asterisk/call.rb +416 -0
- data/lib/adhearsion/translator/asterisk/channel.rb +43 -0
- data/lib/adhearsion/translator/asterisk/component.rb +88 -0
- data/lib/adhearsion/translator/asterisk/component/asterisk.rb +15 -0
- data/lib/adhearsion/translator/asterisk/component/asterisk/agi_command.rb +42 -0
- data/lib/adhearsion/translator/asterisk/component/asterisk/ami_action.rb +68 -0
- data/lib/adhearsion/translator/asterisk/component/composed_prompt.rb +76 -0
- data/lib/adhearsion/translator/asterisk/component/dtmf_recognizer.rb +137 -0
- data/lib/adhearsion/translator/asterisk/component/input.rb +34 -0
- data/lib/adhearsion/translator/asterisk/component/input_component.rb +90 -0
- data/lib/adhearsion/translator/asterisk/component/mrcp_native_prompt.rb +71 -0
- data/lib/adhearsion/translator/asterisk/component/mrcp_prompt.rb +55 -0
- data/lib/adhearsion/translator/asterisk/component/mrcp_recog_prompt.rb +165 -0
- data/lib/adhearsion/translator/asterisk/component/output.rb +233 -0
- data/lib/adhearsion/translator/asterisk/component/record.rb +101 -0
- data/lib/adhearsion/translator/asterisk/component/stop_by_redirect.rb +30 -0
- data/lib/adhearsion/translator/asterisk/unimrcp_app.rb +28 -0
- data/lib/adhearsion/uri_list.rb +21 -0
- data/lib/adhearsion/version.rb +1 -1
- data/spec/adhearsion/call_controller/dial_spec.rb +79 -1420
- data/spec/adhearsion/call_controller/input_spec.rb +1141 -237
- data/spec/adhearsion/call_controller/output/async_player_spec.rb +10 -10
- data/spec/adhearsion/call_controller/output/player_spec.rb +8 -8
- data/spec/adhearsion/call_controller/output_spec.rb +162 -215
- data/spec/adhearsion/call_controller/record_spec.rb +15 -16
- data/spec/adhearsion/call_controller_spec.rb +23 -40
- data/spec/adhearsion/call_spec.rb +123 -129
- data/spec/adhearsion/calls_spec.rb +3 -3
- data/spec/adhearsion/configuration_spec.rb +94 -108
- data/spec/adhearsion/event/answered_spec.rb +50 -0
- data/spec/adhearsion/event/asterisk/Find Results +402 -0
- data/spec/adhearsion/event/asterisk/ami_spec.rb +81 -0
- data/spec/adhearsion/event/complete_spec.rb +176 -0
- data/spec/adhearsion/event/dtmf_spec.rb +35 -0
- data/spec/adhearsion/event/end_spec.rb +85 -0
- data/spec/adhearsion/event/input_timers_started_spec.rb +19 -0
- data/spec/adhearsion/event/joined_spec.rb +53 -0
- data/spec/adhearsion/event/offer_spec.rb +106 -0
- data/spec/adhearsion/event/ringing_spec.rb +50 -0
- data/spec/adhearsion/event/started_speaking_spec.rb +37 -0
- data/spec/adhearsion/event/stopped_speaking_spec.rb +37 -0
- data/spec/adhearsion/event/unjoined_spec.rb +48 -0
- data/spec/adhearsion/event/untitled +0 -0
- data/spec/adhearsion/events_spec.rb +19 -45
- data/spec/adhearsion/initializer_spec.rb +12 -184
- data/spec/adhearsion/logging_spec.rb +5 -20
- data/spec/adhearsion/outbound_call_spec.rb +13 -13
- data/spec/adhearsion/plugin_spec.rb +3 -4
- data/spec/adhearsion/protocol_error_spec.rb +91 -0
- data/spec/adhearsion/rayo/client/component_registry_spec.rb +26 -0
- data/spec/adhearsion/rayo/client_spec.rb +134 -0
- data/spec/adhearsion/rayo/command/accept_spec.rb +63 -0
- data/spec/adhearsion/rayo/command/answer_spec.rb +73 -0
- data/spec/adhearsion/rayo/command/dial_spec.rb +156 -0
- data/spec/adhearsion/rayo/command/hangup_spec.rb +63 -0
- data/spec/adhearsion/rayo/command/join_spec.rb +158 -0
- data/spec/adhearsion/rayo/command/mute_spec.rb +32 -0
- data/spec/adhearsion/rayo/command/redirect_spec.rb +89 -0
- data/spec/adhearsion/rayo/command/reject_spec.rb +117 -0
- data/spec/adhearsion/rayo/command/unjoin_spec.rb +82 -0
- data/spec/adhearsion/rayo/command/unmute_spec.rb +32 -0
- data/spec/adhearsion/rayo/command_node_spec.rb +101 -0
- data/spec/adhearsion/rayo/component/asterisk/agi/command_spec.rb +111 -0
- data/spec/adhearsion/rayo/component/asterisk/ami/action_spec.rb +173 -0
- data/spec/adhearsion/rayo/component/component_node_spec.rb +110 -0
- data/spec/adhearsion/rayo/component/input_spec.rb +715 -0
- data/spec/adhearsion/rayo/component/output_spec.rb +1030 -0
- data/spec/adhearsion/rayo/component/prompt_spec.rb +171 -0
- data/spec/adhearsion/rayo/component/receive_fax_spec.rb +136 -0
- data/spec/adhearsion/rayo/component/record_spec.rb +497 -0
- data/spec/adhearsion/rayo/component/send_fax_spec.rb +144 -0
- data/spec/adhearsion/rayo/connection/asterisk_spec.rb +118 -0
- data/spec/adhearsion/rayo/connection/xmpp_spec.rb +449 -0
- data/spec/adhearsion/rayo/initializer_spec.rb +353 -0
- data/spec/adhearsion/rayo/ref_spec.rb +168 -0
- data/spec/adhearsion/rayo_spec.rb +7 -0
- data/spec/adhearsion/router/route_spec.rb +1 -1
- data/spec/adhearsion/statistics_spec.rb +2 -5
- data/spec/adhearsion/translator/asterisk/call_spec.rb +2047 -0
- data/spec/adhearsion/translator/asterisk/component/asterisk/agi_command_spec.rb +256 -0
- data/spec/adhearsion/translator/asterisk/component/asterisk/ami_action_spec.rb +151 -0
- data/spec/adhearsion/translator/asterisk/component/composed_prompt_spec.rb +257 -0
- data/spec/adhearsion/translator/asterisk/component/input_spec.rb +571 -0
- data/spec/adhearsion/translator/asterisk/component/mrcp_native_prompt_spec.rb +774 -0
- data/spec/adhearsion/translator/asterisk/component/mrcp_prompt_spec.rb +1244 -0
- data/spec/adhearsion/translator/asterisk/component/output_spec.rb +1850 -0
- data/spec/adhearsion/translator/asterisk/component/record_spec.rb +426 -0
- data/spec/adhearsion/translator/asterisk/component/stop_by_redirect_spec.rb +62 -0
- data/spec/adhearsion/translator/asterisk/component_spec.rb +83 -0
- data/spec/adhearsion/translator/asterisk_spec.rb +685 -0
- data/spec/adhearsion/uri_list_spec.rb +88 -0
- data/spec/adhearsion_spec.rb +89 -14
- data/spec/fixtures/locale/en.yml +13 -0
- data/spec/fixtures/locale/it.yml +13 -0
- data/spec/spec_helper.rb +18 -5
- data/spec/support/call_controller_test_helpers.rb +3 -2
- data/spec/support/initializer_stubs.rb +3 -1
- data/spec/support/punchblock_examples.rb +65 -0
- data/spec/support/punchblock_mocks.rb +12 -0
- metadata +412 -70
- data/features/cli_daemon.feature +0 -20
- data/features/cli_restart.feature +0 -52
- data/features/cli_stop.feature +0 -50
- data/lib/adhearsion/call_controller/menu_dsl.rb +0 -21
- data/lib/adhearsion/call_controller/menu_dsl/array_match_calculator.rb +0 -26
- data/lib/adhearsion/call_controller/menu_dsl/calculated_match.rb +0 -43
- data/lib/adhearsion/call_controller/menu_dsl/calculated_match_collection.rb +0 -45
- data/lib/adhearsion/call_controller/menu_dsl/fixnum_match_calculator.rb +0 -11
- data/lib/adhearsion/call_controller/menu_dsl/match_calculator.rb +0 -40
- data/lib/adhearsion/call_controller/menu_dsl/menu.rb +0 -207
- data/lib/adhearsion/call_controller/menu_dsl/menu_builder.rb +0 -92
- data/lib/adhearsion/call_controller/menu_dsl/range_match_calculator.rb +0 -60
- data/lib/adhearsion/call_controller/menu_dsl/string_match_calculator.rb +0 -25
- data/lib/adhearsion/call_controller/utility.rb +0 -77
- data/lib/adhearsion/foundation/custom_daemonizer.rb +0 -52
- data/lib/adhearsion/punchblock_plugin.rb +0 -63
- data/scripts/cloc-1.64.pl +0 -10483
- data/spec/adhearsion/call_controller/menu_dsl/array_match_calculator_spec.rb +0 -76
- data/spec/adhearsion/call_controller/menu_dsl/calculated_match_collection_spec.rb +0 -60
- data/spec/adhearsion/call_controller/menu_dsl/calculated_match_spec.rb +0 -61
- data/spec/adhearsion/call_controller/menu_dsl/fixnum_match_calculator_spec.rb +0 -39
- data/spec/adhearsion/call_controller/menu_dsl/match_calculator_spec.rb +0 -17
- data/spec/adhearsion/call_controller/menu_dsl/menu_builder_spec.rb +0 -165
- data/spec/adhearsion/call_controller/menu_dsl/menu_spec.rb +0 -420
- data/spec/adhearsion/call_controller/menu_dsl/range_match_calculator_spec.rb +0 -32
- data/spec/adhearsion/call_controller/menu_dsl/string_match_calculator_spec.rb +0 -40
- data/spec/adhearsion/call_controller/utility_spec.rb +0 -90
- data/spec/adhearsion/punchblock_plugin/initializer_spec.rb +0 -356
- data/spec/adhearsion/punchblock_plugin_spec.rb +0 -59
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
|
|
3
|
+
module Adhearsion
|
|
4
|
+
module Translator
|
|
5
|
+
class Asterisk
|
|
6
|
+
module Component
|
|
7
|
+
class Record < Component
|
|
8
|
+
RECORDING_BASE_PATH = '/var/adhearsion/record'
|
|
9
|
+
|
|
10
|
+
def setup
|
|
11
|
+
@complete_reason = nil
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def execute
|
|
15
|
+
max_duration = @component_node.max_duration || -1
|
|
16
|
+
|
|
17
|
+
raise OptionError, 'Record cannot be used on a call that is not answered.' unless @call.answered?
|
|
18
|
+
raise OptionError, 'A start-paused value of true is unsupported.' if @component_node.start_paused
|
|
19
|
+
raise OptionError, 'An initial-timeout value is unsupported.' if @component_node.initial_timeout && @component_node.initial_timeout != -1
|
|
20
|
+
raise OptionError, 'A final-timeout value is unsupported.' if @component_node.final_timeout && @component_node.final_timeout != -1
|
|
21
|
+
raise OptionError, 'A max-duration value that is negative (and not -1) is invalid.' unless max_duration >= -1
|
|
22
|
+
|
|
23
|
+
@format = @component_node.format || 'wav'
|
|
24
|
+
|
|
25
|
+
call.register_tmp_handler :ami, :name => 'MonitorStop' do |event|
|
|
26
|
+
finished
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
if @component_node.start_beep
|
|
30
|
+
@call.execute_agi_command 'STREAM FILE', 'beep', '""'
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
ami_client.send_action 'Monitor', 'Channel' => call.channel, 'File' => filename, 'Format' => @format, 'Mix' => true
|
|
34
|
+
unless max_duration == -1
|
|
35
|
+
call.after max_duration/1000 do
|
|
36
|
+
stop
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
send_ref
|
|
41
|
+
rescue ChannelGoneError
|
|
42
|
+
set_node_response Adhearsion::ProtocolError.new.setup(:item_not_found, "Could not find a call with ID #{call_id}", call_id)
|
|
43
|
+
rescue RubyAMI::Error => e
|
|
44
|
+
with_error :platform_error, "Terminated due to AMI error '#{e.message}'"
|
|
45
|
+
rescue OptionError => e
|
|
46
|
+
with_error 'option error', e.message
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def execute_command(command)
|
|
50
|
+
case command
|
|
51
|
+
when Adhearsion::Rayo::Component::Stop
|
|
52
|
+
command.response = true
|
|
53
|
+
ami_client.send_action 'StopMonitor', 'Channel' => call.channel
|
|
54
|
+
@complete_reason = stop_reason
|
|
55
|
+
when Adhearsion::Rayo::Component::Record::Pause
|
|
56
|
+
ami_client.send_action 'PauseMonitor', 'Channel' => call.channel
|
|
57
|
+
command.response = true
|
|
58
|
+
when Adhearsion::Rayo::Component::Record::Resume
|
|
59
|
+
ami_client.send_action 'ResumeMonitor', 'Channel' => call.channel
|
|
60
|
+
command.response = true
|
|
61
|
+
else
|
|
62
|
+
super
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def finished
|
|
67
|
+
send_complete_event(@complete_reason || max_duration_reason)
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
private
|
|
71
|
+
|
|
72
|
+
def stop
|
|
73
|
+
AMIErrorConverter.convert(nil) do
|
|
74
|
+
ami_client.send_action 'StopMonitor', 'Channel' => call.channel
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def filename
|
|
79
|
+
File.join RECORDING_BASE_PATH, id
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def recording
|
|
83
|
+
Adhearsion::Rayo::Component::Record::Recording.new :uri => "file://#{filename}.#{@format}"
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def stop_reason
|
|
87
|
+
Adhearsion::Event::Complete::Stop.new
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
def max_duration_reason
|
|
91
|
+
Adhearsion::Rayo::Component::Record::Complete::MaxDuration.new
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
def send_complete_event(reason)
|
|
95
|
+
super reason, recording
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
end
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
|
|
3
|
+
require 'active_support/core_ext/string/filters'
|
|
4
|
+
|
|
5
|
+
module Adhearsion
|
|
6
|
+
module Translator
|
|
7
|
+
class Asterisk
|
|
8
|
+
module Component
|
|
9
|
+
module StopByRedirect
|
|
10
|
+
def execute_command(command)
|
|
11
|
+
return super unless command.is_a?(Adhearsion::Rayo::Component::Stop)
|
|
12
|
+
if @complete
|
|
13
|
+
command.response = Adhearsion::ProtocolError.new.setup 'component-already-stopped', "Component #{id} is already stopped", call_id, id
|
|
14
|
+
else
|
|
15
|
+
stop_by_redirect Adhearsion::Event::Complete::Stop.new
|
|
16
|
+
command.response = true
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def stop_by_redirect(complete_reason)
|
|
21
|
+
call.register_handler :ami, [{name: 'AsyncAGI', [:[], 'SubEvent'] => 'Start'}, {name: 'AsyncAGIExec'}] do |event|
|
|
22
|
+
send_complete_event complete_reason
|
|
23
|
+
end
|
|
24
|
+
call.redirect_back
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
|
|
3
|
+
require 'adhearsion/translator/asterisk/agi_app'
|
|
4
|
+
require 'active_support/core_ext/string/filters'
|
|
5
|
+
|
|
6
|
+
module Adhearsion
|
|
7
|
+
module Translator
|
|
8
|
+
class Asterisk
|
|
9
|
+
class UniMRCPApp
|
|
10
|
+
def initialize(app, *args, options)
|
|
11
|
+
args.map! { |o| "\"#{o.to_s.squish.gsub('"', '\"')}\"" }
|
|
12
|
+
args << prepare_options(options)
|
|
13
|
+
@agi_app = AGIApp.new(app, *args)
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def execute(call)
|
|
17
|
+
@agi_app.execute call
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
private
|
|
21
|
+
|
|
22
|
+
def prepare_options(options)
|
|
23
|
+
options.map { |o| o.join '=' }.join '&'
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
|
|
3
|
+
module Adhearsion
|
|
4
|
+
class URIList < SimpleDelegator
|
|
5
|
+
def self.import(string)
|
|
6
|
+
new string.strip.split("\n").map(&:strip)
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
def initialize(*list)
|
|
10
|
+
super list.flatten
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def to_s
|
|
14
|
+
join("\n")
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def ==(other)
|
|
18
|
+
self.__getobj__ == other.to_ary
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
data/lib/adhearsion/version.rb
CHANGED
|
@@ -15,7 +15,7 @@ module Adhearsion
|
|
|
15
15
|
let(:second_other_call_id) { new_uuid }
|
|
16
16
|
let(:second_other_mock_call) { OutboundCall.new }
|
|
17
17
|
|
|
18
|
-
let(:mock_answered) {
|
|
18
|
+
let(:mock_answered) { Adhearsion::Event::Answered.new }
|
|
19
19
|
|
|
20
20
|
let(:latch) { CountDownLatch.new 1 }
|
|
21
21
|
|
|
@@ -27,7 +27,7 @@ module Adhearsion
|
|
|
27
27
|
end
|
|
28
28
|
|
|
29
29
|
def mock_end(reason = :hangup_command)
|
|
30
|
-
|
|
30
|
+
Adhearsion::Event::End.new.tap { |event| allow(event).to receive_messages reason: reason }
|
|
31
31
|
end
|
|
32
32
|
|
|
33
33
|
describe "#dial" do
|
|
@@ -150,7 +150,7 @@ module Adhearsion
|
|
|
150
150
|
end
|
|
151
151
|
|
|
152
152
|
context "with ringback specified" do
|
|
153
|
-
let(:component) {
|
|
153
|
+
let(:component) { Adhearsion::Rayo::Component::Output.new }
|
|
154
154
|
let(:options) { { ringback: ['file://tt-monkeys'] } }
|
|
155
155
|
|
|
156
156
|
before do
|
|
@@ -212,9 +212,9 @@ module Adhearsion
|
|
|
212
212
|
end
|
|
213
213
|
|
|
214
214
|
it "hangs up the new call when the root call ends" do
|
|
215
|
+
expect(other_mock_call).to receive(:hangup).once
|
|
215
216
|
expect(call).to receive(:answer).once
|
|
216
217
|
expect(other_mock_call).to receive(:join).once.with(call, {})
|
|
217
|
-
expect(other_mock_call).to receive(:hangup).once
|
|
218
218
|
|
|
219
219
|
dial_in_thread
|
|
220
220
|
|
|
@@ -266,8 +266,8 @@ module Adhearsion
|
|
|
266
266
|
it "has an overall dial status of :answer" do
|
|
267
267
|
expect(call).to receive(:answer).once
|
|
268
268
|
expect(other_mock_call).to receive(:join).once.with(call, {}) do
|
|
269
|
-
call <<
|
|
270
|
-
other_mock_call <<
|
|
269
|
+
call << Adhearsion::Event::Joined.new(call_uri: other_mock_call.id)
|
|
270
|
+
other_mock_call << Adhearsion::Event::Joined.new(call_uri: call.id)
|
|
271
271
|
end
|
|
272
272
|
|
|
273
273
|
t = dial_in_thread
|
|
@@ -290,8 +290,8 @@ module Adhearsion
|
|
|
290
290
|
it "records the duration of the join" do
|
|
291
291
|
expect(call).to receive(:answer).once
|
|
292
292
|
expect(other_mock_call).to receive(:join).once.with(call, {}) do
|
|
293
|
-
call <<
|
|
294
|
-
other_mock_call <<
|
|
293
|
+
call << Adhearsion::Event::Joined.new(call_uri: other_mock_call.id)
|
|
294
|
+
other_mock_call << Adhearsion::Event::Joined.new(call_uri: call.id)
|
|
295
295
|
end
|
|
296
296
|
allow(other_mock_call).to receive_messages hangup: true
|
|
297
297
|
|
|
@@ -306,7 +306,7 @@ module Adhearsion
|
|
|
306
306
|
|
|
307
307
|
base_time = Time.local(2008, 9, 1, 12, 0, 37)
|
|
308
308
|
Timecop.freeze base_time
|
|
309
|
-
other_mock_call <<
|
|
309
|
+
other_mock_call << Adhearsion::Event::Unjoined.new(call_uri: call.id)
|
|
310
310
|
other_mock_call << mock_end
|
|
311
311
|
|
|
312
312
|
expect(latch.wait(2)).to be_truthy
|
|
@@ -323,7 +323,10 @@ module Adhearsion
|
|
|
323
323
|
|
|
324
324
|
it "joins the calls with those options" do
|
|
325
325
|
expect(call).to receive(:answer).once
|
|
326
|
-
expect(other_mock_call).to receive(:join).once.with(call, media: :direct)
|
|
326
|
+
expect(other_mock_call).to receive(:join).once.with(call, media: :direct) do
|
|
327
|
+
call << Adhearsion::Event::Joined.new(call_uri: other_mock_call.id)
|
|
328
|
+
other_mock_call << Adhearsion::Event::Joined.new(call_uri: call.id)
|
|
329
|
+
end
|
|
327
330
|
allow(other_mock_call).to receive_messages hangup: true
|
|
328
331
|
|
|
329
332
|
t = dial_in_thread
|
|
@@ -332,7 +335,7 @@ module Adhearsion
|
|
|
332
335
|
|
|
333
336
|
other_mock_call << mock_answered
|
|
334
337
|
|
|
335
|
-
other_mock_call <<
|
|
338
|
+
other_mock_call << Adhearsion::Event::Unjoined.new(call_uri: call.id)
|
|
336
339
|
other_mock_call << mock_end
|
|
337
340
|
|
|
338
341
|
expect(latch.wait(2)).to be_truthy
|
|
@@ -348,19 +351,19 @@ module Adhearsion
|
|
|
348
351
|
before do
|
|
349
352
|
expect(call).to receive(:answer).once
|
|
350
353
|
expect(other_mock_call).to receive(:join).once.with(join_target, join_options) do
|
|
351
|
-
call <<
|
|
352
|
-
other_mock_call <<
|
|
354
|
+
call << Adhearsion::Event::Joined.new(call_uri: other_mock_call.id)
|
|
355
|
+
other_mock_call << Adhearsion::Event::Joined.new(call_uri: call.id)
|
|
353
356
|
end
|
|
354
357
|
allow(other_mock_call).to receive(:unjoin) do
|
|
355
|
-
call <<
|
|
356
|
-
other_mock_call <<
|
|
358
|
+
call << Adhearsion::Event::Unjoined.new(call_uri: other_mock_call.id)
|
|
359
|
+
other_mock_call << Adhearsion::Event::Unjoined.new(call_uri: call.id)
|
|
357
360
|
end
|
|
358
361
|
end
|
|
359
362
|
|
|
360
363
|
it "should unjoin the calls" do
|
|
361
364
|
expect(other_mock_call).to receive(:unjoin).once.ordered.with(call) do
|
|
362
|
-
call <<
|
|
363
|
-
other_mock_call <<
|
|
365
|
+
call << Adhearsion::Event::Unjoined.new(call_uri: other_mock_call.id)
|
|
366
|
+
other_mock_call << Adhearsion::Event::Unjoined.new(call_uri: call.id)
|
|
364
367
|
end
|
|
365
368
|
|
|
366
369
|
dial = Dial::Dial.new to, options, call
|
|
@@ -500,8 +503,8 @@ module Adhearsion
|
|
|
500
503
|
context "when rejoining" do
|
|
501
504
|
it "should rejoin the calls" do
|
|
502
505
|
expect(other_mock_call).to receive(:unjoin).once.ordered.with(call) do
|
|
503
|
-
call <<
|
|
504
|
-
other_mock_call <<
|
|
506
|
+
call << Adhearsion::Event::Unjoined.new(call_uri: other_mock_call.id)
|
|
507
|
+
other_mock_call << Adhearsion::Event::Unjoined.new(call_uri: call.id)
|
|
505
508
|
end
|
|
506
509
|
|
|
507
510
|
dial = Dial::Dial.new to, options, call
|
|
@@ -518,7 +521,10 @@ module Adhearsion
|
|
|
518
521
|
|
|
519
522
|
dial.split
|
|
520
523
|
|
|
521
|
-
expect(other_mock_call).to receive(:join).once.ordered.with(call, {})
|
|
524
|
+
expect(other_mock_call).to receive(:join).once.ordered.with(call, {}) do
|
|
525
|
+
call << Adhearsion::Event::Joined.new(call_uri: other_mock_call.id)
|
|
526
|
+
other_mock_call << Adhearsion::Event::Joined.new(call_uri: call.id)
|
|
527
|
+
end
|
|
522
528
|
dial.rejoin
|
|
523
529
|
|
|
524
530
|
other_mock_call << mock_end
|
|
@@ -542,7 +548,10 @@ module Adhearsion
|
|
|
542
548
|
|
|
543
549
|
dial.split
|
|
544
550
|
|
|
545
|
-
expect(other_mock_call).to receive(:join).once.ordered.with(call, media: :direct)
|
|
551
|
+
expect(other_mock_call).to receive(:join).once.ordered.with(call, media: :direct) do
|
|
552
|
+
call << Adhearsion::Event::Joined.new(call_uri: other_mock_call.id)
|
|
553
|
+
other_mock_call << Adhearsion::Event::Joined.new(call_uri: call.id)
|
|
554
|
+
end
|
|
546
555
|
dial.rejoin
|
|
547
556
|
end
|
|
548
557
|
end
|
|
@@ -558,7 +567,10 @@ module Adhearsion
|
|
|
558
567
|
|
|
559
568
|
dial.split
|
|
560
569
|
|
|
561
|
-
expect(other_mock_call).to receive(:join).once.ordered.with(call, media: :direct)
|
|
570
|
+
expect(other_mock_call).to receive(:join).once.ordered.with(call, media: :direct) do
|
|
571
|
+
call << Adhearsion::Event::Joined.new(call_uri: other_mock_call.id)
|
|
572
|
+
other_mock_call << Adhearsion::Event::Joined.new(call_uri: call.id)
|
|
573
|
+
end
|
|
562
574
|
dial.rejoin nil, media: :direct
|
|
563
575
|
end
|
|
564
576
|
end
|
|
@@ -571,8 +583,8 @@ module Adhearsion
|
|
|
571
583
|
expect(call).to receive(:join).once.with(join_target, {})
|
|
572
584
|
expect(other_mock_call).to receive(:unjoin).once.ordered.with(join_target)
|
|
573
585
|
expect(call).to receive(:unjoin).once.ordered.with(join_target) do
|
|
574
|
-
call <<
|
|
575
|
-
other_mock_call <<
|
|
586
|
+
call << Adhearsion::Event::Unjoined.new(join_target)
|
|
587
|
+
other_mock_call << Adhearsion::Event::Unjoined.new(join_target)
|
|
576
588
|
end
|
|
577
589
|
|
|
578
590
|
dial = Dial::Dial.new to, options, call
|
|
@@ -607,8 +619,8 @@ module Adhearsion
|
|
|
607
619
|
|
|
608
620
|
it "should join all calls to the mixer" do
|
|
609
621
|
expect(other_mock_call).to receive(:unjoin).once.ordered.with(call) do
|
|
610
|
-
call <<
|
|
611
|
-
other_mock_call <<
|
|
622
|
+
call << Adhearsion::Event::Unjoined.new(call_uri: other_mock_call.id)
|
|
623
|
+
other_mock_call << Adhearsion::Event::Unjoined.new(call_uri: call.id)
|
|
612
624
|
end
|
|
613
625
|
|
|
614
626
|
dial = Dial::Dial.new to, options, call
|
|
@@ -639,8 +651,8 @@ module Adhearsion
|
|
|
639
651
|
|
|
640
652
|
it "#split should then unjoin calls from the mixer" do
|
|
641
653
|
expect(other_mock_call).to receive(:unjoin).once.ordered.with(call) do
|
|
642
|
-
call <<
|
|
643
|
-
other_mock_call <<
|
|
654
|
+
call << Adhearsion::Event::Unjoined.new(call_uri: other_mock_call.id)
|
|
655
|
+
other_mock_call << Adhearsion::Event::Unjoined.new(call_uri: call.id)
|
|
644
656
|
end
|
|
645
657
|
|
|
646
658
|
dial = Dial::Dial.new to, options, call
|
|
@@ -662,16 +674,16 @@ module Adhearsion
|
|
|
662
674
|
dial.rejoin mixer_name: mixer
|
|
663
675
|
|
|
664
676
|
expect(other_mock_call).to receive(:unjoin).once.ordered.with(mixer_name: mixer) do
|
|
665
|
-
other_mock_call <<
|
|
677
|
+
other_mock_call << Adhearsion::Event::Unjoined.new(mixer_name: mixer)
|
|
666
678
|
end
|
|
667
679
|
expect(call).to receive(:unjoin).once.ordered.with(mixer_name: mixer) do
|
|
668
|
-
call <<
|
|
680
|
+
call << Adhearsion::Event::Unjoined.new(mixer_name: mixer)
|
|
669
681
|
end
|
|
670
682
|
dial.split
|
|
671
683
|
|
|
672
684
|
other_mock_call << mock_end
|
|
673
685
|
|
|
674
|
-
expect(latch.wait(
|
|
686
|
+
expect(latch.wait(2)).to be_truthy
|
|
675
687
|
|
|
676
688
|
waiter_thread.join
|
|
677
689
|
expect(dial.status.result).to eq(:answer)
|
|
@@ -705,12 +717,12 @@ module Adhearsion
|
|
|
705
717
|
|
|
706
718
|
it "should split calls, rejoin to a mixer, and rejoin other calls to mixer" do
|
|
707
719
|
expect(other_mock_call).to receive(:unjoin).once.ordered.with(call) do
|
|
708
|
-
call <<
|
|
709
|
-
other_mock_call <<
|
|
720
|
+
call << Adhearsion::Event::Unjoined.new(call_uri: other_mock_call.id)
|
|
721
|
+
other_mock_call << Adhearsion::Event::Unjoined.new(call_uri: call.id)
|
|
710
722
|
end
|
|
711
723
|
expect(second_other_mock_call).to receive(:unjoin).once.ordered.with(second_root_call) do
|
|
712
|
-
second_root_call <<
|
|
713
|
-
second_other_mock_call <<
|
|
724
|
+
second_root_call << Adhearsion::Event::Unjoined.new(call_uri: second_other_mock_call.id)
|
|
725
|
+
second_other_mock_call << Adhearsion::Event::Unjoined.new(call_uri: second_root_call.id)
|
|
714
726
|
end
|
|
715
727
|
|
|
716
728
|
expect(call).to receive(:join).once.ordered.with({mixer_name: mixer}, {})
|
|
@@ -844,15 +856,15 @@ module Adhearsion
|
|
|
844
856
|
|
|
845
857
|
[call, second_root_call, second_other_mock_call].each do |call|
|
|
846
858
|
expect(call).to receive(:unjoin).once.with(mixer_name: mixer) do
|
|
847
|
-
call <<
|
|
859
|
+
call << Adhearsion::Event::Unjoined.new(mixer_name: mixer)
|
|
848
860
|
end
|
|
849
861
|
end
|
|
850
862
|
|
|
851
863
|
dial.split
|
|
852
864
|
|
|
853
|
-
[call, second_root_call, second_other_mock_call].each do |call|
|
|
865
|
+
[call, other_mock_call, second_root_call, second_other_mock_call].each do |call|
|
|
854
866
|
expect(call).to receive(:join).once.with({mixer_name: mixer}, {}) do
|
|
855
|
-
call <<
|
|
867
|
+
call << Adhearsion::Event::Joined.new(mixer_name: mixer)
|
|
856
868
|
end
|
|
857
869
|
end
|
|
858
870
|
|
|
@@ -952,7 +964,7 @@ module Adhearsion
|
|
|
952
964
|
|
|
953
965
|
[call, second_root_call, second_other_mock_call].each do |call|
|
|
954
966
|
expect(call).to receive(:unjoin).once.with(mixer_name: mixer) do
|
|
955
|
-
call <<
|
|
967
|
+
call << Adhearsion::Event::Unjoined.new(mixer_name: mixer)
|
|
956
968
|
end
|
|
957
969
|
end
|
|
958
970
|
|
|
@@ -965,7 +977,7 @@ module Adhearsion
|
|
|
965
977
|
|
|
966
978
|
[call, second_root_call, second_other_mock_call].each do |call|
|
|
967
979
|
expect(call).to receive(:join).once.with({mixer_name: mixer}, {}) do
|
|
968
|
-
call <<
|
|
980
|
+
call << Adhearsion::Event::Joined.new(mixer_name: mixer)
|
|
969
981
|
end
|
|
970
982
|
end
|
|
971
983
|
|
|
@@ -977,8 +989,8 @@ module Adhearsion
|
|
|
977
989
|
|
|
978
990
|
context "if the calls were not joined" do
|
|
979
991
|
it "should still join to mixer" do
|
|
980
|
-
expect(other_mock_call).to receive(:unjoin).once.ordered.with(call).and_raise
|
|
981
|
-
expect(second_other_mock_call).to receive(:unjoin).once.ordered.with(second_root_call).and_raise
|
|
992
|
+
expect(other_mock_call).to receive(:unjoin).once.ordered.with(call).and_raise Adhearsion::ProtocolError.new.setup(:service_unavailable)
|
|
993
|
+
expect(second_other_mock_call).to receive(:unjoin).once.ordered.with(second_root_call).and_raise Adhearsion::ProtocolError.new.setup(:service_unavailable)
|
|
982
994
|
|
|
983
995
|
expect(call).to receive(:join).once.ordered.with({mixer_name: mixer}, {})
|
|
984
996
|
expect(other_mock_call).to receive(:join).once.ordered.with({mixer_name: mixer}, {})
|
|
@@ -1020,7 +1032,7 @@ module Adhearsion
|
|
|
1020
1032
|
|
|
1021
1033
|
it "should not make any outbound calls" do
|
|
1022
1034
|
expect(OutboundCall).to receive(:new).never
|
|
1023
|
-
expect { subject.dial to, options }.to raise_error
|
|
1035
|
+
expect { subject.dial to, options }.to raise_error(Call::Hangup)
|
|
1024
1036
|
end
|
|
1025
1037
|
end
|
|
1026
1038
|
|
|
@@ -1082,7 +1094,7 @@ module Adhearsion
|
|
|
1082
1094
|
expect(latch.wait(2)).to be_falsey
|
|
1083
1095
|
|
|
1084
1096
|
other_mock_call << mock_answered
|
|
1085
|
-
other_mock_call <<
|
|
1097
|
+
other_mock_call << Adhearsion::Event::Unjoined.new(call_uri: call.id)
|
|
1086
1098
|
|
|
1087
1099
|
expect(latch.wait(2)).to be_truthy
|
|
1088
1100
|
|
|
@@ -1273,6 +1285,9 @@ module Adhearsion
|
|
|
1273
1285
|
call[key] = value
|
|
1274
1286
|
end
|
|
1275
1287
|
@@confirmation_latch.countdown!
|
|
1288
|
+
if delay = call['confirmation_delay']
|
|
1289
|
+
sleep delay
|
|
1290
|
+
end
|
|
1276
1291
|
call['confirm'] || hangup
|
|
1277
1292
|
end
|
|
1278
1293
|
end
|
|
@@ -1332,14 +1347,14 @@ module Adhearsion
|
|
|
1332
1347
|
end
|
|
1333
1348
|
|
|
1334
1349
|
it "should join the calls if the call is still active after execution of the call controller" do
|
|
1350
|
+
expect(other_mock_call).to receive(:hangup).once do
|
|
1351
|
+
other_mock_call << mock_end
|
|
1352
|
+
end
|
|
1335
1353
|
other_mock_call['confirm'] = true
|
|
1336
1354
|
expect(call).to receive(:answer).once
|
|
1337
1355
|
expect(other_mock_call).to receive(:join).once.with(call, {}) do
|
|
1338
|
-
call <<
|
|
1339
|
-
other_mock_call <<
|
|
1340
|
-
end
|
|
1341
|
-
expect(other_mock_call).to receive(:hangup).once do
|
|
1342
|
-
other_mock_call << mock_end
|
|
1356
|
+
call << Adhearsion::Event::Joined.new(call_uri: other_mock_call.id)
|
|
1357
|
+
other_mock_call << Adhearsion::Event::Joined.new(call_uri: call.id)
|
|
1343
1358
|
end
|
|
1344
1359
|
|
|
1345
1360
|
t = dial_in_thread
|
|
@@ -1353,9 +1368,9 @@ module Adhearsion
|
|
|
1353
1368
|
|
|
1354
1369
|
base_time = Time.local(2008, 9, 1, 12, 0, 42)
|
|
1355
1370
|
Timecop.freeze base_time
|
|
1356
|
-
other_mock_call <<
|
|
1371
|
+
other_mock_call << Adhearsion::Event::Unjoined.new(call_uri: call.id)
|
|
1357
1372
|
|
|
1358
|
-
expect(latch.wait(
|
|
1373
|
+
expect(latch.wait(2)).to be_truthy
|
|
1359
1374
|
|
|
1360
1375
|
t.join
|
|
1361
1376
|
status = t.value
|
|
@@ -1393,1381 +1408,25 @@ module Adhearsion
|
|
|
1393
1408
|
end
|
|
1394
1409
|
|
|
1395
1410
|
context "when multiple calls are made" do
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
latch.countdown!
|
|
1404
|
-
status
|
|
1405
|
-
end
|
|
1406
|
-
end
|
|
1407
|
-
|
|
1408
|
-
context "when one answers" do
|
|
1409
|
-
it "should only execute the confirmation controller on the first call to answer, immediately hanging up all others" do
|
|
1410
|
-
other_mock_call['confirm'] = true
|
|
1411
|
-
expect(call).to receive(:answer).once
|
|
1412
|
-
|
|
1413
|
-
expect(other_mock_call).to receive(:dial).once.with(to, from: nil)
|
|
1414
|
-
expect(other_mock_call).to receive(:join).once.with(call, {}) do
|
|
1415
|
-
call << Punchblock::Event::Joined.new(call_uri: other_mock_call.id)
|
|
1416
|
-
other_mock_call << Punchblock::Event::Joined.new(call_uri: call.id)
|
|
1417
|
-
end
|
|
1418
|
-
expect(other_mock_call).to receive(:hangup).once do
|
|
1419
|
-
other_mock_call << mock_end
|
|
1420
|
-
end
|
|
1421
|
-
|
|
1422
|
-
expect(second_other_mock_call).to receive(:dial).once.with(second_to, from: nil)
|
|
1423
|
-
expect(second_other_mock_call).to receive(:join).never
|
|
1424
|
-
expect(second_other_mock_call).to receive(:execute_controller).never
|
|
1425
|
-
expect(second_other_mock_call).to receive(:hangup).once do
|
|
1426
|
-
second_other_mock_call << mock_end(:foo)
|
|
1411
|
+
let(:confirmation_latch) { CountDownLatch.new 2 }
|
|
1412
|
+
let(:apology_controller) do
|
|
1413
|
+
Class.new(Adhearsion::CallController) do
|
|
1414
|
+
def run
|
|
1415
|
+
logger.info "Apologising..."
|
|
1416
|
+
call['apology_metadata'] = metadata
|
|
1417
|
+
call['apology_done'] = true
|
|
1427
1418
|
end
|
|
1428
|
-
|
|
1429
|
-
t = dial_in_thread
|
|
1430
|
-
|
|
1431
|
-
expect(latch.wait(2)).to be_falsey
|
|
1432
|
-
|
|
1433
|
-
other_mock_call << mock_answered
|
|
1434
|
-
expect(confirmation_latch.wait(2)).to be_truthy
|
|
1435
|
-
|
|
1436
|
-
other_mock_call << Punchblock::Event::Unjoined.new(call_uri: call.id)
|
|
1437
|
-
|
|
1438
|
-
expect(latch.wait(2)).to be_truthy
|
|
1439
|
-
|
|
1440
|
-
t.join
|
|
1441
|
-
status = t.value
|
|
1442
|
-
expect(status).to be_a Dial::DialStatus
|
|
1443
|
-
expect(status.calls.size).to eq(2)
|
|
1444
|
-
status.calls.each { |c| expect(c).to be_a OutboundCall }
|
|
1445
|
-
expect(status.result).to eq(:answer)
|
|
1446
1419
|
end
|
|
1447
1420
|
end
|
|
1448
|
-
|
|
1449
|
-
end
|
|
1450
|
-
end
|
|
1451
|
-
|
|
1452
|
-
describe "#dial_and_confirm" do
|
|
1453
|
-
it "should dial the call to the correct endpoint and return a dial status object" do
|
|
1454
|
-
expect(OutboundCall).to receive(:new).and_return other_mock_call
|
|
1455
|
-
expect(other_mock_call).to receive(:dial).with(to, :from => 'foo').once
|
|
1456
|
-
dial_thread = Thread.new do
|
|
1457
|
-
status = subject.dial_and_confirm(to, :from => 'foo')
|
|
1458
|
-
|
|
1459
|
-
expect(status).to be_a Dial::DialStatus
|
|
1460
|
-
joined_status = status.joins[status.calls.first]
|
|
1461
|
-
expect(joined_status.duration).to eq(0.0)
|
|
1462
|
-
expect(joined_status.result).to eq(:no_answer)
|
|
1463
|
-
end
|
|
1464
|
-
sleep 0.1
|
|
1465
|
-
other_mock_call << mock_end
|
|
1466
|
-
expect(dial_thread.join).to be_truthy
|
|
1467
|
-
end
|
|
1468
|
-
|
|
1469
|
-
it "should default the caller ID to that of the original call" do
|
|
1470
|
-
allow(call).to receive_messages :from => 'sip:foo@bar.com'
|
|
1471
|
-
expect(OutboundCall).to receive(:new).and_return other_mock_call
|
|
1472
|
-
expect(other_mock_call).to receive(:dial).with(to, :from => 'sip:foo@bar.com').once
|
|
1473
|
-
dial_thread = Thread.new do
|
|
1474
|
-
subject.dial_and_confirm to
|
|
1475
|
-
end
|
|
1476
|
-
sleep 0.1
|
|
1477
|
-
other_mock_call << mock_end
|
|
1478
|
-
expect(dial_thread.join).to be_truthy
|
|
1479
|
-
end
|
|
1480
|
-
|
|
1481
|
-
let(:options) { { :foo => :bar } }
|
|
1482
|
-
|
|
1483
|
-
def dial_in_thread
|
|
1484
|
-
Thread.new do
|
|
1485
|
-
status = subject.dial_and_confirm to, options
|
|
1486
|
-
latch.countdown!
|
|
1487
|
-
status
|
|
1488
|
-
end
|
|
1489
|
-
end
|
|
1490
|
-
|
|
1491
|
-
describe "without a block" do
|
|
1492
|
-
before do
|
|
1493
|
-
expect(other_mock_call).to receive(:dial).once.with(to, options)
|
|
1494
|
-
expect(OutboundCall).to receive(:new).and_return other_mock_call
|
|
1495
|
-
end
|
|
1496
|
-
|
|
1497
|
-
it "blocks the original controller until the new call ends" do
|
|
1498
|
-
dial_in_thread
|
|
1499
|
-
|
|
1500
|
-
expect(latch.wait(2)).to be_falsey
|
|
1501
|
-
|
|
1502
|
-
other_mock_call << mock_end
|
|
1503
|
-
|
|
1504
|
-
expect(latch.wait(2)).to be_truthy
|
|
1505
|
-
end
|
|
1506
|
-
|
|
1507
|
-
it "unblocks the original controller if the original call ends" do
|
|
1508
|
-
expect(other_mock_call).to receive(:hangup).once
|
|
1509
|
-
dial_in_thread
|
|
1510
|
-
|
|
1511
|
-
expect(latch.wait(2)).to be_falsey
|
|
1512
|
-
|
|
1513
|
-
call << mock_end
|
|
1514
|
-
|
|
1515
|
-
expect(latch.wait(2)).to be_truthy
|
|
1516
|
-
end
|
|
1517
|
-
|
|
1518
|
-
it "joins the new call to the existing one on answer" do
|
|
1519
|
-
expect(call).to receive(:answer).once
|
|
1520
|
-
expect(other_mock_call).to receive(:join).once.with(call, {})
|
|
1521
|
-
|
|
1522
|
-
dial_in_thread
|
|
1523
|
-
|
|
1524
|
-
expect(latch.wait(2)).to be_falsey
|
|
1525
|
-
|
|
1526
|
-
other_mock_call << mock_answered
|
|
1527
|
-
other_mock_call << mock_end
|
|
1528
|
-
|
|
1529
|
-
expect(latch.wait(2)).to be_truthy
|
|
1530
|
-
end
|
|
1531
|
-
|
|
1532
|
-
context "with a join target specified" do
|
|
1533
|
-
let(:options) { { join_target: {mixer_name: 'foobar'} } }
|
|
1534
|
-
|
|
1535
|
-
it "joins the calls to the specified target on answer" do
|
|
1536
|
-
expect(call).to receive(:answer).once
|
|
1537
|
-
expect(call).to receive(:join).once.with({mixer_name: 'foobar'}, {})
|
|
1538
|
-
expect(other_mock_call).to receive(:join).once.with({mixer_name: 'foobar'}, {})
|
|
1539
|
-
|
|
1540
|
-
dial_in_thread
|
|
1541
|
-
|
|
1542
|
-
expect(latch.wait(2)).to be_falsey
|
|
1543
|
-
|
|
1544
|
-
other_mock_call << mock_answered
|
|
1545
|
-
other_mock_call << mock_end
|
|
1546
|
-
|
|
1547
|
-
expect(latch.wait(2)).to be_truthy
|
|
1548
|
-
end
|
|
1549
|
-
end
|
|
1550
|
-
|
|
1551
|
-
context "with a pre-join callback specified" do
|
|
1552
|
-
let(:foo) { double }
|
|
1553
|
-
let(:options) { { pre_join: ->(call) { foo.bar call } } }
|
|
1554
|
-
|
|
1555
|
-
it "executes the callback prior to joining" do
|
|
1556
|
-
expect(foo).to receive(:bar).once.with(other_mock_call).ordered
|
|
1557
|
-
expect(call).to receive(:answer).once.ordered
|
|
1558
|
-
expect(other_mock_call).to receive(:join).once.with(call, {}).ordered
|
|
1559
|
-
|
|
1560
|
-
dial_in_thread
|
|
1561
|
-
|
|
1562
|
-
expect(latch.wait(2)).to be_falsey
|
|
1563
|
-
|
|
1564
|
-
other_mock_call << mock_answered
|
|
1565
|
-
other_mock_call << mock_end
|
|
1566
|
-
|
|
1567
|
-
expect(latch.wait(2)).to be_truthy
|
|
1568
|
-
end
|
|
1569
|
-
end
|
|
1570
|
-
|
|
1571
|
-
context "with ringback specified" do
|
|
1572
|
-
let(:component) { Punchblock::Component::Output.new }
|
|
1573
|
-
let(:options) { { ringback: ['file://tt-monkeys'] } }
|
|
1421
|
+
let(:options) { {confirm: confirmation_controller, confirm_metadata: {'foo' => 'bar'}, apology: apology_controller} }
|
|
1574
1422
|
|
|
1575
1423
|
before do
|
|
1576
|
-
|
|
1577
|
-
component.execute!
|
|
1578
|
-
end
|
|
1579
|
-
|
|
1580
|
-
it "plays the ringback asynchronously, terminating prior to joining" do
|
|
1581
|
-
expect(subject).to receive(:play!).once.with(['file://tt-monkeys'], repeat_times: 0).and_return(component)
|
|
1582
|
-
expect(component).to receive(:stop!).twice
|
|
1583
|
-
expect(call).to receive(:answer).once.ordered
|
|
1584
|
-
expect(other_mock_call).to receive(:join).once.with(call, {}).ordered
|
|
1585
|
-
|
|
1586
|
-
dial_in_thread
|
|
1587
|
-
|
|
1588
|
-
expect(latch.wait(2)).to be_falsey
|
|
1589
|
-
|
|
1590
|
-
other_mock_call << mock_answered
|
|
1591
|
-
other_mock_call << mock_end
|
|
1592
|
-
|
|
1593
|
-
expect(latch.wait(2)).to be_truthy
|
|
1594
|
-
end
|
|
1595
|
-
|
|
1596
|
-
context "as a callback" do
|
|
1597
|
-
let(:foo) { double }
|
|
1598
|
-
let(:options) { { ringback: -> { foo.bar; component } } }
|
|
1599
|
-
|
|
1600
|
-
it "calls the callback to start, and uses the return value of the callback to stop the ringback" do
|
|
1601
|
-
expect(foo).to receive(:bar).once.ordered
|
|
1602
|
-
expect(component).to receive(:stop!).twice
|
|
1603
|
-
expect(call).to receive(:answer).once.ordered
|
|
1604
|
-
expect(other_mock_call).to receive(:join).once.with(call, {}).ordered
|
|
1605
|
-
|
|
1606
|
-
dial_in_thread
|
|
1607
|
-
|
|
1608
|
-
expect(latch.wait(2)).to be_falsey
|
|
1609
|
-
|
|
1610
|
-
other_mock_call << mock_answered
|
|
1611
|
-
other_mock_call << mock_end
|
|
1612
|
-
|
|
1613
|
-
expect(latch.wait(2)).to be_truthy
|
|
1614
|
-
end
|
|
1615
|
-
end
|
|
1616
|
-
|
|
1617
|
-
context "when the call is rejected" do
|
|
1618
|
-
it "terminates the ringback before returning" do
|
|
1619
|
-
expect(subject).to receive(:play!).once.with(['file://tt-monkeys'], repeat_times: 0).and_return(component)
|
|
1620
|
-
expect(component).to receive(:stop!).once
|
|
1621
|
-
|
|
1622
|
-
t = dial_in_thread
|
|
1623
|
-
|
|
1624
|
-
expect(latch.wait(2)).to be_falsey
|
|
1625
|
-
|
|
1626
|
-
other_mock_call << mock_end(:reject)
|
|
1627
|
-
|
|
1628
|
-
expect(latch.wait(2)).to be_truthy
|
|
1629
|
-
end
|
|
1630
|
-
end
|
|
1631
|
-
end
|
|
1632
|
-
|
|
1633
|
-
it "hangs up the new call when the root call ends" do
|
|
1634
|
-
expect(other_mock_call).to receive(:hangup).once
|
|
1635
|
-
expect(call).to receive(:answer).once
|
|
1636
|
-
expect(other_mock_call).to receive(:join).once.with(call, {})
|
|
1637
|
-
|
|
1638
|
-
dial_in_thread
|
|
1639
|
-
|
|
1640
|
-
expect(latch.wait(2)).to be_falsey
|
|
1641
|
-
|
|
1642
|
-
other_mock_call << mock_answered
|
|
1643
|
-
call << mock_end
|
|
1644
|
-
|
|
1645
|
-
expect(latch.wait(2)).to be_truthy
|
|
1646
|
-
end
|
|
1647
|
-
|
|
1648
|
-
context "when the call is rejected" do
|
|
1649
|
-
it "has an overall dial status of :no_answer" do
|
|
1650
|
-
t = dial_in_thread
|
|
1651
|
-
|
|
1652
|
-
sleep 0.5
|
|
1653
|
-
|
|
1654
|
-
other_mock_call << mock_end(:reject)
|
|
1655
|
-
|
|
1656
|
-
expect(latch.wait(2)).to be_truthy
|
|
1657
|
-
|
|
1658
|
-
t.join
|
|
1659
|
-
status = t.value
|
|
1660
|
-
expect(status.result).to eq(:no_answer)
|
|
1661
|
-
end
|
|
1662
|
-
end
|
|
1663
|
-
|
|
1664
|
-
context "when the call ends with an error" do
|
|
1665
|
-
it "has an overall dial status of :error" do
|
|
1666
|
-
t = dial_in_thread
|
|
1667
|
-
|
|
1668
|
-
sleep 0.5
|
|
1669
|
-
|
|
1670
|
-
other_mock_call << mock_end(:error)
|
|
1671
|
-
|
|
1672
|
-
expect(latch.wait(2)).to be_truthy
|
|
1673
|
-
|
|
1674
|
-
t.join
|
|
1675
|
-
status = t.value
|
|
1676
|
-
expect(status.result).to eq(:error)
|
|
1677
|
-
|
|
1678
|
-
joined_status = status.joins[status.calls.first]
|
|
1679
|
-
expect(joined_status.duration).to eq(0.0)
|
|
1680
|
-
expect(joined_status.result).to eq(:error)
|
|
1681
|
-
end
|
|
1682
|
-
end
|
|
1683
|
-
|
|
1684
|
-
context "when the call is answered and joined" do
|
|
1685
|
-
it "has an overall dial status of :answer" do
|
|
1686
|
-
expect(call).to receive(:answer).once
|
|
1687
|
-
expect(other_mock_call).to receive(:join).once.with(call, {}) do
|
|
1688
|
-
call << Punchblock::Event::Joined.new(call_uri: other_mock_call.id)
|
|
1689
|
-
other_mock_call << Punchblock::Event::Joined.new(call_uri: call.id)
|
|
1690
|
-
end
|
|
1691
|
-
|
|
1692
|
-
t = dial_in_thread
|
|
1693
|
-
|
|
1694
|
-
sleep 0.5
|
|
1695
|
-
|
|
1696
|
-
other_mock_call << mock_answered
|
|
1697
|
-
other_mock_call << mock_end
|
|
1698
|
-
|
|
1699
|
-
expect(latch.wait(2)).to be_truthy
|
|
1700
|
-
|
|
1701
|
-
t.join
|
|
1702
|
-
status = t.value
|
|
1703
|
-
expect(status.result).to eq(:answer)
|
|
1704
|
-
|
|
1705
|
-
joined_status = status.joins[status.calls.first]
|
|
1706
|
-
expect(joined_status.result).to eq(:joined)
|
|
1707
|
-
end
|
|
1708
|
-
|
|
1709
|
-
it "records the duration of the join" do
|
|
1710
|
-
expect(call).to receive(:answer).once
|
|
1711
|
-
expect(other_mock_call).to receive(:join).once.with(call, {}) do
|
|
1712
|
-
call << Punchblock::Event::Joined.new(call_uri: other_mock_call.id)
|
|
1713
|
-
other_mock_call << Punchblock::Event::Joined.new(call_uri: call.id)
|
|
1714
|
-
end
|
|
1715
|
-
allow(other_mock_call).to receive_messages hangup: true
|
|
1716
|
-
|
|
1717
|
-
t = dial_in_thread
|
|
1718
|
-
|
|
1719
|
-
sleep 0.5
|
|
1720
|
-
|
|
1721
|
-
base_time = Time.local(2008, 9, 1, 12, 0, 0)
|
|
1722
|
-
Timecop.freeze base_time
|
|
1723
|
-
|
|
1724
|
-
other_mock_call << mock_answered
|
|
1725
|
-
|
|
1726
|
-
base_time = Time.local(2008, 9, 1, 12, 0, 37)
|
|
1727
|
-
Timecop.freeze base_time
|
|
1728
|
-
other_mock_call << Punchblock::Event::Unjoined.new(call_uri: call.id)
|
|
1729
|
-
other_mock_call << mock_end
|
|
1730
|
-
|
|
1731
|
-
expect(latch.wait(2)).to be_truthy
|
|
1732
|
-
|
|
1733
|
-
t.join
|
|
1734
|
-
status = t.value
|
|
1735
|
-
expect(status.result).to eq(:answer)
|
|
1736
|
-
joined_status = status.joins[status.calls.first]
|
|
1737
|
-
expect(joined_status.duration).to eq(37.0)
|
|
1738
|
-
end
|
|
1739
|
-
|
|
1740
|
-
context "when join options are specified" do
|
|
1741
|
-
let(:options) { { join_options: {media: :direct} } }
|
|
1742
|
-
|
|
1743
|
-
it "joins the calls with those options" do
|
|
1744
|
-
expect(call).to receive(:answer).once
|
|
1745
|
-
expect(other_mock_call).to receive(:join).once.with(call, media: :direct) do
|
|
1746
|
-
call << Punchblock::Event::Joined.new(call_uri: other_mock_call.id)
|
|
1747
|
-
other_mock_call << Punchblock::Event::Joined.new(call_uri: call.id)
|
|
1748
|
-
end
|
|
1749
|
-
allow(other_mock_call).to receive_messages hangup: true
|
|
1750
|
-
|
|
1751
|
-
t = dial_in_thread
|
|
1752
|
-
|
|
1753
|
-
sleep 0.5
|
|
1754
|
-
|
|
1755
|
-
other_mock_call << mock_answered
|
|
1756
|
-
|
|
1757
|
-
other_mock_call << Punchblock::Event::Unjoined.new(call_uri: call.id)
|
|
1758
|
-
other_mock_call << mock_end
|
|
1759
|
-
|
|
1760
|
-
expect(latch.wait(2)).to be_truthy
|
|
1761
|
-
|
|
1762
|
-
t.join
|
|
1763
|
-
end
|
|
1764
|
-
end
|
|
1765
|
-
end
|
|
1766
|
-
|
|
1767
|
-
context "when a dial is split" do
|
|
1768
|
-
let(:join_target) { call }
|
|
1769
|
-
|
|
1770
|
-
before do
|
|
1771
|
-
expect(call).to receive(:answer).once
|
|
1772
|
-
expect(other_mock_call).to receive(:join).once.with(join_target, join_options) do
|
|
1773
|
-
call << Punchblock::Event::Joined.new(call_uri: other_mock_call.id)
|
|
1774
|
-
other_mock_call << Punchblock::Event::Joined.new(call_uri: call.id)
|
|
1775
|
-
end
|
|
1776
|
-
allow(other_mock_call).to receive(:unjoin) do
|
|
1777
|
-
call << Punchblock::Event::Unjoined.new(call_uri: other_mock_call.id)
|
|
1778
|
-
other_mock_call << Punchblock::Event::Unjoined.new(call_uri: call.id)
|
|
1779
|
-
end
|
|
1780
|
-
end
|
|
1781
|
-
|
|
1782
|
-
it "should unjoin the calls" do
|
|
1783
|
-
expect(other_mock_call).to receive(:unjoin).once.ordered.with(call) do
|
|
1784
|
-
call << Punchblock::Event::Unjoined.new(call_uri: other_mock_call.id)
|
|
1785
|
-
other_mock_call << Punchblock::Event::Unjoined.new(call_uri: call.id)
|
|
1786
|
-
end
|
|
1787
|
-
|
|
1788
|
-
dial = Dial::ParallelConfirmationDial.new to, options, call
|
|
1789
|
-
dial.run subject
|
|
1790
|
-
|
|
1791
|
-
waiter_thread = Thread.new do
|
|
1792
|
-
dial.await_completion
|
|
1793
|
-
latch.countdown!
|
|
1794
|
-
end
|
|
1795
|
-
|
|
1796
|
-
sleep 0.5
|
|
1797
|
-
|
|
1798
|
-
other_mock_call << mock_answered
|
|
1799
|
-
|
|
1800
|
-
dial.split
|
|
1801
|
-
other_mock_call << mock_end
|
|
1802
|
-
|
|
1803
|
-
expect(latch.wait(2)).to be_truthy
|
|
1804
|
-
|
|
1805
|
-
waiter_thread.join
|
|
1806
|
-
expect(dial.status.result).to eq(:answer)
|
|
1807
|
-
end
|
|
1808
|
-
|
|
1809
|
-
it "should not unblock immediately" do
|
|
1810
|
-
dial = Dial::ParallelConfirmationDial.new to, options, call
|
|
1811
|
-
dial.run subject
|
|
1812
|
-
|
|
1813
|
-
waiter_thread = Thread.new do
|
|
1814
|
-
dial.await_completion
|
|
1815
|
-
latch.countdown!
|
|
1816
|
-
end
|
|
1817
|
-
|
|
1818
|
-
sleep 0.5
|
|
1819
|
-
|
|
1820
|
-
other_mock_call << mock_answered
|
|
1821
|
-
|
|
1822
|
-
dial.split
|
|
1823
|
-
|
|
1824
|
-
expect(latch.wait(2)).to be_falsey
|
|
1825
|
-
|
|
1826
|
-
other_mock_call << mock_end
|
|
1827
|
-
|
|
1828
|
-
expect(latch.wait(2)).to be_truthy
|
|
1829
|
-
|
|
1830
|
-
waiter_thread.join
|
|
1831
|
-
expect(dial.status.result).to eq(:answer)
|
|
1832
|
-
end
|
|
1833
|
-
|
|
1834
|
-
it "should set end time" do
|
|
1835
|
-
dial = Dial::ParallelConfirmationDial.new to, options, call
|
|
1836
|
-
dial.run subject
|
|
1837
|
-
|
|
1838
|
-
waiter_thread = Thread.new do
|
|
1839
|
-
dial.await_completion
|
|
1840
|
-
latch.countdown!
|
|
1841
|
-
end
|
|
1842
|
-
|
|
1843
|
-
sleep 0.5
|
|
1844
|
-
|
|
1845
|
-
base_time = Time.local(2008, 9, 1, 12, 0, 0)
|
|
1846
|
-
Timecop.freeze base_time
|
|
1847
|
-
|
|
1848
|
-
other_mock_call << mock_answered
|
|
1849
|
-
|
|
1850
|
-
base_time = Time.local(2008, 9, 1, 12, 0, 37)
|
|
1851
|
-
Timecop.freeze base_time
|
|
1852
|
-
dial.split
|
|
1853
|
-
|
|
1854
|
-
base_time = Time.local(2008, 9, 1, 12, 0, 54)
|
|
1855
|
-
Timecop.freeze base_time
|
|
1856
|
-
other_mock_call << mock_end
|
|
1857
|
-
|
|
1858
|
-
expect(latch.wait(2)).to be_truthy
|
|
1859
|
-
|
|
1860
|
-
waiter_thread.join
|
|
1861
|
-
status = dial.status
|
|
1862
|
-
expect(status.result).to eq(:answer)
|
|
1863
|
-
joined_status = status.joins[status.calls.first]
|
|
1864
|
-
expect(joined_status.duration).to eq(37.0)
|
|
1865
|
-
end
|
|
1866
|
-
|
|
1867
|
-
context "with new controllers specified" do
|
|
1868
|
-
let(:split_latch) { CountDownLatch.new 2 }
|
|
1869
|
-
|
|
1870
|
-
let(:split_controller) do
|
|
1871
|
-
latch = split_latch
|
|
1872
|
-
Class.new(Adhearsion::CallController) do
|
|
1873
|
-
@@split_latch = latch
|
|
1874
|
-
|
|
1875
|
-
def run
|
|
1876
|
-
call['hit_split_controller'] = self.class
|
|
1877
|
-
call['split_controller_metadata'] = metadata
|
|
1878
|
-
@@split_latch.countdown!
|
|
1879
|
-
end
|
|
1880
|
-
end
|
|
1881
|
-
end
|
|
1882
|
-
|
|
1883
|
-
let(:main_split_controller) { Class.new(split_controller) }
|
|
1884
|
-
let(:others_split_controller) { Class.new(split_controller) }
|
|
1885
|
-
|
|
1886
|
-
it "should execute the :main controller on the originating call and :others on the outbound calls" do
|
|
1887
|
-
dial = Dial::ParallelConfirmationDial.new to, options, call
|
|
1888
|
-
dial.run subject
|
|
1889
|
-
|
|
1890
|
-
waiter_thread = Thread.new do
|
|
1891
|
-
dial.await_completion
|
|
1892
|
-
latch.countdown!
|
|
1893
|
-
end
|
|
1894
|
-
|
|
1895
|
-
sleep 0.5
|
|
1896
|
-
|
|
1897
|
-
other_mock_call << mock_answered
|
|
1898
|
-
|
|
1899
|
-
expect(self).to receive(:callback).once.with(call)
|
|
1900
|
-
expect(self).to receive(:callback).once.with(other_mock_call)
|
|
1901
|
-
|
|
1902
|
-
dial.split main: main_split_controller, others: others_split_controller, main_callback: ->(call) { self.callback(call) }, others_callback: ->(call) { self.callback(call) }
|
|
1903
|
-
|
|
1904
|
-
expect(latch.wait(2)).to be_falsey
|
|
1905
|
-
expect(split_latch.wait(2)).to be_truthy
|
|
1906
|
-
|
|
1907
|
-
expect(call['hit_split_controller']).to eq(main_split_controller)
|
|
1908
|
-
expect(call['split_controller_metadata']['current_dial']).to be dial
|
|
1909
|
-
|
|
1910
|
-
expect(other_mock_call['hit_split_controller']).to eq(others_split_controller)
|
|
1911
|
-
expect(other_mock_call['split_controller_metadata']['current_dial']).to be dial
|
|
1912
|
-
|
|
1913
|
-
other_mock_call << mock_end
|
|
1914
|
-
|
|
1915
|
-
expect(latch.wait(2)).to be_truthy
|
|
1916
|
-
|
|
1917
|
-
waiter_thread.join
|
|
1918
|
-
expect(dial.status.result).to eq(:answer)
|
|
1919
|
-
end
|
|
1920
|
-
end
|
|
1921
|
-
|
|
1922
|
-
context "when rejoining" do
|
|
1923
|
-
it "should rejoin the calls" do
|
|
1924
|
-
expect(other_mock_call).to receive(:unjoin).once.ordered.with(call) do
|
|
1925
|
-
call << Punchblock::Event::Unjoined.new(call_uri: other_mock_call.id)
|
|
1926
|
-
other_mock_call << Punchblock::Event::Unjoined.new(call_uri: call.id)
|
|
1927
|
-
end
|
|
1928
|
-
|
|
1929
|
-
dial = Dial::ParallelConfirmationDial.new to, options, call
|
|
1930
|
-
dial.run subject
|
|
1931
|
-
|
|
1932
|
-
waiter_thread = Thread.new do
|
|
1933
|
-
dial.await_completion
|
|
1934
|
-
latch.countdown!
|
|
1935
|
-
end
|
|
1936
|
-
|
|
1937
|
-
sleep 0.5
|
|
1938
|
-
|
|
1939
|
-
other_mock_call << mock_answered
|
|
1940
|
-
|
|
1941
|
-
dial.split
|
|
1942
|
-
|
|
1943
|
-
expect(other_mock_call).to receive(:join).once.ordered.with(call, {}) do
|
|
1944
|
-
call << Punchblock::Event::Joined.new(call_uri: other_mock_call.id)
|
|
1945
|
-
other_mock_call << Punchblock::Event::Joined.new(call_uri: call.id)
|
|
1946
|
-
end
|
|
1947
|
-
dial.rejoin
|
|
1948
|
-
|
|
1949
|
-
other_mock_call << mock_end
|
|
1950
|
-
|
|
1951
|
-
expect(latch.wait(2)).to be_truthy
|
|
1952
|
-
|
|
1953
|
-
waiter_thread.join
|
|
1954
|
-
expect(dial.status.result).to eq(:answer)
|
|
1955
|
-
end
|
|
1956
|
-
|
|
1957
|
-
context "when join options were set originally" do
|
|
1958
|
-
let(:options) { { join_options: {media: :direct} } }
|
|
1959
|
-
|
|
1960
|
-
it "should rejoin with the same parameters" do
|
|
1961
|
-
allow(other_mock_call).to receive(:unjoin)
|
|
1962
|
-
|
|
1963
|
-
dial = Dial::ParallelConfirmationDial.new to, options, call
|
|
1964
|
-
dial.run subject
|
|
1965
|
-
|
|
1966
|
-
other_mock_call << mock_answered
|
|
1967
|
-
|
|
1968
|
-
dial.split
|
|
1969
|
-
|
|
1970
|
-
expect(other_mock_call).to receive(:join).once.ordered.with(call, media: :direct) do
|
|
1971
|
-
call << Punchblock::Event::Joined.new(call_uri: other_mock_call.id)
|
|
1972
|
-
other_mock_call << Punchblock::Event::Joined.new(call_uri: call.id)
|
|
1973
|
-
end
|
|
1974
|
-
dial.rejoin
|
|
1975
|
-
end
|
|
1976
|
-
end
|
|
1977
|
-
|
|
1978
|
-
context "when join options are passed to rejoin" do
|
|
1979
|
-
it "should rejoin with those parameters" do
|
|
1980
|
-
allow(other_mock_call).to receive(:unjoin)
|
|
1981
|
-
|
|
1982
|
-
dial = Dial::ParallelConfirmationDial.new to, options, call
|
|
1983
|
-
dial.run subject
|
|
1984
|
-
|
|
1985
|
-
other_mock_call << mock_answered
|
|
1986
|
-
|
|
1987
|
-
dial.split
|
|
1988
|
-
|
|
1989
|
-
expect(other_mock_call).to receive(:join).once.ordered.with(call, media: :direct) do
|
|
1990
|
-
call << Punchblock::Event::Joined.new(call_uri: other_mock_call.id)
|
|
1991
|
-
other_mock_call << Punchblock::Event::Joined.new(call_uri: call.id)
|
|
1992
|
-
end
|
|
1993
|
-
dial.rejoin nil, media: :direct
|
|
1994
|
-
end
|
|
1995
|
-
end
|
|
1996
|
-
|
|
1997
|
-
context "when a join target was originally specified" do
|
|
1998
|
-
let(:join_target) { {mixer_name: 'foobar'} }
|
|
1999
|
-
let(:options) { { join_target: join_target } }
|
|
2000
|
-
|
|
2001
|
-
it "joins the calls to the specified target on answer" do
|
|
2002
|
-
expect(call).to receive(:join).once.with(join_target, {})
|
|
2003
|
-
expect(other_mock_call).to receive(:unjoin).once.ordered.with(join_target)
|
|
2004
|
-
expect(call).to receive(:unjoin).once.ordered.with(join_target) do
|
|
2005
|
-
call << Punchblock::Event::Unjoined.new(join_target)
|
|
2006
|
-
other_mock_call << Punchblock::Event::Unjoined.new(join_target)
|
|
2007
|
-
end
|
|
2008
|
-
|
|
2009
|
-
dial = Dial::ParallelConfirmationDial.new to, options, call
|
|
2010
|
-
dial.run subject
|
|
2011
|
-
|
|
2012
|
-
waiter_thread = Thread.new do
|
|
2013
|
-
dial.await_completion
|
|
2014
|
-
latch.countdown!
|
|
2015
|
-
end
|
|
2016
|
-
|
|
2017
|
-
sleep 0.5
|
|
2018
|
-
|
|
2019
|
-
other_mock_call << mock_answered
|
|
2020
|
-
|
|
2021
|
-
dial.split
|
|
2022
|
-
|
|
2023
|
-
expect(call).to receive(:join).once.ordered.with({mixer_name: 'foobar'}, {})
|
|
2024
|
-
expect(other_mock_call).to receive(:join).once.ordered.with({mixer_name: 'foobar'}, {})
|
|
2025
|
-
dial.rejoin
|
|
2026
|
-
|
|
2027
|
-
other_mock_call << mock_end
|
|
2028
|
-
|
|
2029
|
-
expect(latch.wait(2)).to be_truthy
|
|
2030
|
-
|
|
2031
|
-
waiter_thread.join
|
|
2032
|
-
expect(dial.status.result).to eq(:answer)
|
|
2033
|
-
end
|
|
2034
|
-
end
|
|
2035
|
-
|
|
2036
|
-
context "to a specified mixer" do
|
|
2037
|
-
let(:mixer) { SecureRandom.uuid }
|
|
2038
|
-
|
|
2039
|
-
it "should join all calls to the mixer" do
|
|
2040
|
-
expect(other_mock_call).to receive(:unjoin).once.ordered.with(call) do
|
|
2041
|
-
call << Punchblock::Event::Unjoined.new(call_uri: other_mock_call.id)
|
|
2042
|
-
other_mock_call << Punchblock::Event::Unjoined.new(call_uri: call.id)
|
|
2043
|
-
end
|
|
2044
|
-
|
|
2045
|
-
dial = Dial::ParallelConfirmationDial.new to, options, call
|
|
2046
|
-
dial.run subject
|
|
2047
|
-
|
|
2048
|
-
waiter_thread = Thread.new do
|
|
2049
|
-
dial.await_completion
|
|
2050
|
-
latch.countdown!
|
|
2051
|
-
end
|
|
2052
|
-
|
|
2053
|
-
sleep 0.5
|
|
2054
|
-
|
|
2055
|
-
other_mock_call << mock_answered
|
|
2056
|
-
|
|
2057
|
-
dial.split
|
|
2058
|
-
|
|
2059
|
-
expect(call).to receive(:join).once.ordered.with({mixer_name: mixer}, {})
|
|
2060
|
-
expect(other_mock_call).to receive(:join).once.ordered.with({mixer_name: mixer}, {})
|
|
2061
|
-
dial.rejoin mixer_name: mixer
|
|
2062
|
-
|
|
2063
|
-
other_mock_call << mock_end
|
|
2064
|
-
|
|
2065
|
-
expect(latch.wait(2)).to be_truthy
|
|
2066
|
-
|
|
2067
|
-
waiter_thread.join
|
|
2068
|
-
expect(dial.status.result).to eq(:answer)
|
|
2069
|
-
end
|
|
2070
|
-
|
|
2071
|
-
it "#split should then unjoin calls from the mixer" do
|
|
2072
|
-
expect(other_mock_call).to receive(:unjoin).once.ordered.with(call) do
|
|
2073
|
-
call << Punchblock::Event::Unjoined.new(call_uri: other_mock_call.id)
|
|
2074
|
-
other_mock_call << Punchblock::Event::Unjoined.new(call_uri: call.id)
|
|
2075
|
-
end
|
|
2076
|
-
|
|
2077
|
-
dial = Dial::ParallelConfirmationDial.new to, options, call
|
|
2078
|
-
dial.run subject
|
|
2079
|
-
|
|
2080
|
-
waiter_thread = Thread.new do
|
|
2081
|
-
dial.await_completion
|
|
2082
|
-
latch.countdown!
|
|
2083
|
-
end
|
|
2084
|
-
|
|
2085
|
-
sleep 0.5
|
|
2086
|
-
|
|
2087
|
-
other_mock_call << mock_answered
|
|
2088
|
-
|
|
2089
|
-
dial.split
|
|
2090
|
-
|
|
2091
|
-
expect(call).to receive(:join).once.ordered.with({mixer_name: mixer}, {})
|
|
2092
|
-
expect(other_mock_call).to receive(:join).once.ordered.with({mixer_name: mixer}, {})
|
|
2093
|
-
dial.rejoin mixer_name: mixer
|
|
2094
|
-
|
|
2095
|
-
expect(other_mock_call).to receive(:unjoin).once.ordered.with(mixer_name: mixer) do
|
|
2096
|
-
other_mock_call << Punchblock::Event::Unjoined.new(mixer_name: mixer)
|
|
2097
|
-
end
|
|
2098
|
-
expect(call).to receive(:unjoin).once.ordered.with(mixer_name: mixer) do
|
|
2099
|
-
call << Punchblock::Event::Unjoined.new(mixer_name: mixer)
|
|
2100
|
-
end
|
|
2101
|
-
dial.split
|
|
2102
|
-
|
|
2103
|
-
other_mock_call << mock_end
|
|
2104
|
-
|
|
2105
|
-
expect(latch.wait(2)).to be_truthy
|
|
2106
|
-
|
|
2107
|
-
waiter_thread.join
|
|
2108
|
-
expect(dial.status.result).to eq(:answer)
|
|
2109
|
-
end
|
|
2110
|
-
end
|
|
2111
|
-
end
|
|
2112
|
-
|
|
2113
|
-
context "when another dial is merged in" do
|
|
2114
|
-
let(:second_root_call_id) { new_uuid }
|
|
2115
|
-
let(:second_root_call) { Adhearsion::Call.new }
|
|
2116
|
-
let(:mixer) { SecureRandom.uuid }
|
|
2117
|
-
|
|
2118
|
-
let(:dial) { Dial::ParallelConfirmationDial.new to, options, call }
|
|
2119
|
-
let(:other_dial) { Dial::ParallelConfirmationDial.new second_to, options, second_root_call }
|
|
2120
|
-
|
|
2121
|
-
before do
|
|
2122
|
-
allow(second_root_call).to receive_messages write_command: true, id: second_root_call_id
|
|
2123
|
-
expect(OutboundCall).to receive(:new).and_return second_other_mock_call
|
|
2124
|
-
expect(second_other_mock_call).to receive(:join).once.with(second_root_call, {})
|
|
2125
|
-
expect(second_other_mock_call).to receive(:dial).once.with(second_to, options)
|
|
2126
|
-
expect(second_root_call).to receive(:answer).once
|
|
2127
|
-
|
|
2128
|
-
allow(SecureRandom).to receive_messages uuid: mixer
|
|
2129
|
-
|
|
2130
|
-
dial.run subject
|
|
2131
|
-
other_dial.run subject
|
|
2132
|
-
|
|
2133
|
-
other_mock_call << mock_answered
|
|
2134
|
-
second_other_mock_call << mock_answered
|
|
2135
|
-
end
|
|
2136
|
-
|
|
2137
|
-
it "should split calls, rejoin to a mixer, and rejoin other calls to mixer" do
|
|
2138
|
-
expect(other_mock_call).to receive(:unjoin).once.ordered.with(call) do
|
|
2139
|
-
call << Punchblock::Event::Unjoined.new(call_uri: other_mock_call.id)
|
|
2140
|
-
other_mock_call << Punchblock::Event::Unjoined.new(call_uri: call.id)
|
|
2141
|
-
end
|
|
2142
|
-
expect(second_other_mock_call).to receive(:unjoin).once.ordered.with(second_root_call) do
|
|
2143
|
-
second_root_call << Punchblock::Event::Unjoined.new(call_uri: second_other_mock_call.id)
|
|
2144
|
-
second_other_mock_call << Punchblock::Event::Unjoined.new(call_uri: second_root_call.id)
|
|
2145
|
-
end
|
|
2146
|
-
|
|
2147
|
-
expect(call).to receive(:join).once.ordered.with({mixer_name: mixer}, {})
|
|
2148
|
-
expect(other_mock_call).to receive(:join).once.ordered.with({mixer_name: mixer}, {})
|
|
2149
|
-
|
|
2150
|
-
expect(second_root_call).to receive(:join).once.ordered.with({mixer_name: mixer}, {})
|
|
2151
|
-
expect(second_other_mock_call).to receive(:join).once.ordered.with({mixer_name: mixer}, {})
|
|
2152
|
-
|
|
2153
|
-
dial.merge other_dial
|
|
2154
|
-
|
|
2155
|
-
waiter_thread = Thread.new do
|
|
2156
|
-
dial.await_completion
|
|
2157
|
-
latch.countdown!
|
|
2158
|
-
end
|
|
2159
|
-
|
|
2160
|
-
sleep 0.5
|
|
2161
|
-
|
|
2162
|
-
other_mock_call.async << mock_end
|
|
2163
|
-
second_root_call.async << mock_end
|
|
2164
|
-
second_other_mock_call.async << mock_end
|
|
2165
|
-
|
|
2166
|
-
expect(latch.wait(2)).to be_truthy
|
|
2167
|
-
|
|
2168
|
-
waiter_thread.join
|
|
2169
|
-
expect(dial.status.result).to eq(:answer)
|
|
2170
|
-
end
|
|
2171
|
-
|
|
2172
|
-
context "when join options were specified originally" do
|
|
2173
|
-
let(:options) { { join_options: {media: :direct} } }
|
|
2174
|
-
|
|
2175
|
-
it "should rejoin with default options" do
|
|
2176
|
-
allow(other_mock_call).to receive(:unjoin)
|
|
2177
|
-
allow(second_other_mock_call).to receive(:unjoin)
|
|
2178
|
-
|
|
2179
|
-
expect(call).to receive(:join).once.ordered.with({mixer_name: mixer}, {})
|
|
2180
|
-
expect(other_mock_call).to receive(:join).once.ordered.with({mixer_name: mixer}, {})
|
|
2181
|
-
|
|
2182
|
-
expect(second_root_call).to receive(:join).once.ordered.with({mixer_name: mixer}, {})
|
|
2183
|
-
expect(second_other_mock_call).to receive(:join).once.ordered.with({mixer_name: mixer}, {})
|
|
2184
|
-
|
|
2185
|
-
dial.merge other_dial
|
|
2186
|
-
end
|
|
2187
|
-
end
|
|
2188
|
-
|
|
2189
|
-
it "should add the merged calls to the returned status" do
|
|
2190
|
-
[call, other_mock_call, second_root_call, second_other_mock_call].each { |c| allow(c).to receive_messages join: true, unjoin: true }
|
|
2191
|
-
dial.merge other_dial
|
|
2192
|
-
|
|
2193
|
-
waiter_thread = Thread.new do
|
|
2194
|
-
dial.await_completion
|
|
2195
|
-
latch.countdown!
|
|
2196
|
-
end
|
|
2197
|
-
|
|
2198
|
-
sleep 0.5
|
|
2199
|
-
|
|
2200
|
-
other_mock_call.async << mock_end
|
|
2201
|
-
second_root_call.async << mock_end
|
|
2202
|
-
second_other_mock_call.async << mock_end
|
|
2203
|
-
|
|
2204
|
-
expect(latch.wait(2)).to be_truthy
|
|
2205
|
-
|
|
2206
|
-
waiter_thread.join
|
|
2207
|
-
expect(dial.status.result).to eq(:answer)
|
|
2208
|
-
expect(dial.status.calls).to include(second_root_call, second_other_mock_call)
|
|
2209
|
-
end
|
|
2210
|
-
|
|
2211
|
-
it "should not unblock until all joined calls end" do
|
|
2212
|
-
[call, other_mock_call, second_root_call, second_other_mock_call].each { |c| allow(c).to receive_messages join: true, unjoin: true }
|
|
2213
|
-
|
|
2214
|
-
dial.merge other_dial
|
|
2215
|
-
|
|
2216
|
-
waiter_thread = Thread.new do
|
|
2217
|
-
dial.await_completion
|
|
2218
|
-
latch.countdown!
|
|
2219
|
-
end
|
|
2220
|
-
|
|
2221
|
-
sleep 0.5
|
|
2222
|
-
|
|
2223
|
-
other_mock_call << mock_end
|
|
2224
|
-
expect(latch.wait(2)).to be_falsey
|
|
2225
|
-
|
|
2226
|
-
second_other_mock_call << mock_end
|
|
2227
|
-
expect(latch.wait(2)).to be_falsey
|
|
2228
|
-
|
|
2229
|
-
second_root_call << mock_end
|
|
2230
|
-
expect(latch.wait(2)).to be_truthy
|
|
2231
|
-
|
|
2232
|
-
waiter_thread.join
|
|
2233
|
-
expect(dial.status.result).to eq(:answer)
|
|
2234
|
-
end
|
|
2235
|
-
|
|
2236
|
-
it "should cleanup merged calls when the root call ends" do
|
|
2237
|
-
[call, other_mock_call, second_root_call, second_other_mock_call].each do |c|
|
|
2238
|
-
allow(c).to receive_messages join: true, unjoin: true
|
|
2239
|
-
end
|
|
2240
|
-
[other_mock_call, second_root_call, second_other_mock_call].each do |c|
|
|
2241
|
-
expect(c).to receive(:hangup).once
|
|
2242
|
-
end
|
|
2243
|
-
|
|
2244
|
-
dial.merge other_dial
|
|
2245
|
-
|
|
2246
|
-
waiter_thread = Thread.new do
|
|
2247
|
-
dial.await_completion
|
|
2248
|
-
dial.cleanup_calls
|
|
2249
|
-
latch.countdown!
|
|
2250
|
-
end
|
|
2251
|
-
|
|
2252
|
-
sleep 0.5
|
|
2253
|
-
|
|
2254
|
-
call << mock_end
|
|
2255
|
-
expect(latch.wait(2)).to be_truthy
|
|
2256
|
-
|
|
2257
|
-
waiter_thread.join
|
|
2258
|
-
expect(dial.status.result).to eq(:answer)
|
|
2259
|
-
end
|
|
2260
|
-
|
|
2261
|
-
it "should subsequently rejoin to a mixer" do
|
|
2262
|
-
[call, other_mock_call, second_root_call, second_other_mock_call].each { |c| allow(c).to receive_messages join: true, unjoin: true }
|
|
2263
|
-
|
|
2264
|
-
dial.merge other_dial
|
|
2265
|
-
|
|
2266
|
-
waiter_thread = Thread.new do
|
|
2267
|
-
dial.await_completion
|
|
2268
|
-
latch.countdown!
|
|
2269
|
-
end
|
|
2270
|
-
|
|
2271
|
-
sleep 0.5
|
|
2272
|
-
|
|
2273
|
-
other_mock_call << mock_end
|
|
2274
|
-
expect(latch.wait(2)).to be_falsey
|
|
2275
|
-
|
|
2276
|
-
[call, second_root_call, second_other_mock_call].each do |call|
|
|
2277
|
-
expect(call).to receive(:unjoin).once.with(mixer_name: mixer) do
|
|
2278
|
-
call << Punchblock::Event::Unjoined.new(mixer_name: mixer)
|
|
2279
|
-
end
|
|
2280
|
-
end
|
|
2281
|
-
|
|
2282
|
-
dial.split
|
|
2283
|
-
|
|
2284
|
-
[call, other_mock_call, second_root_call, second_other_mock_call].each do |call|
|
|
2285
|
-
expect(call).to receive(:join).once.with({mixer_name: mixer}, {}) do
|
|
2286
|
-
call << Punchblock::Event::Joined.new(mixer_name: mixer)
|
|
2287
|
-
end
|
|
2288
|
-
end
|
|
2289
|
-
|
|
2290
|
-
dial.rejoin
|
|
2291
|
-
end
|
|
2292
|
-
|
|
2293
|
-
context "if a call hangs up" do
|
|
2294
|
-
it "should still allow splitting and rejoining" do
|
|
2295
|
-
[call, other_mock_call, second_root_call, second_other_mock_call].each { |c| allow(c).to receive_messages join: true, unjoin: true }
|
|
2296
|
-
|
|
2297
|
-
dial.merge other_dial
|
|
2298
|
-
|
|
2299
|
-
waiter_thread = Thread.new do
|
|
2300
|
-
dial.await_completion
|
|
2301
|
-
latch.countdown!
|
|
2302
|
-
end
|
|
2303
|
-
|
|
2304
|
-
sleep 0.5
|
|
2305
|
-
|
|
2306
|
-
[call, second_root_call, second_other_mock_call].each do |call|
|
|
2307
|
-
expect(call).to receive(:unjoin).once.with(mixer_name: mixer) do
|
|
2308
|
-
call << Punchblock::Event::Unjoined.new(mixer_name: mixer)
|
|
2309
|
-
end
|
|
2310
|
-
end
|
|
2311
|
-
|
|
2312
|
-
expect(other_mock_call).to receive(:unjoin).and_raise Adhearsion::Call::Hangup
|
|
2313
|
-
|
|
2314
|
-
dial.split
|
|
2315
|
-
|
|
2316
|
-
other_mock_call << mock_end
|
|
2317
|
-
expect(latch.wait(2)).to be_falsey
|
|
2318
|
-
|
|
2319
|
-
[call, second_root_call, second_other_mock_call].each do |call|
|
|
2320
|
-
expect(call).to receive(:join).once.with({mixer_name: mixer}, {}) do
|
|
2321
|
-
call << Punchblock::Event::Joined.new(mixer_name: mixer)
|
|
2322
|
-
end
|
|
2323
|
-
end
|
|
2324
|
-
|
|
2325
|
-
expect(other_mock_call).to receive(:join).and_raise Adhearsion::Call::ExpiredError
|
|
2326
|
-
|
|
2327
|
-
dial.rejoin
|
|
2328
|
-
end
|
|
2329
|
-
end
|
|
2330
|
-
|
|
2331
|
-
context "if the calls were not joined" do
|
|
2332
|
-
it "should still join to mixer" do
|
|
2333
|
-
expect(other_mock_call).to receive(:unjoin).once.ordered.with(call).and_raise Punchblock::ProtocolError.new.setup(:service_unavailable)
|
|
2334
|
-
expect(second_other_mock_call).to receive(:unjoin).once.ordered.with(second_root_call).and_raise Punchblock::ProtocolError.new.setup(:service_unavailable)
|
|
2335
|
-
|
|
2336
|
-
expect(call).to receive(:join).once.ordered.with({mixer_name: mixer}, {})
|
|
2337
|
-
expect(other_mock_call).to receive(:join).once.ordered.with({mixer_name: mixer}, {})
|
|
2338
|
-
|
|
2339
|
-
expect(second_root_call).to receive(:join).once.ordered.with({mixer_name: mixer}, {})
|
|
2340
|
-
expect(second_other_mock_call).to receive(:join).once.ordered.with({mixer_name: mixer}, {})
|
|
2341
|
-
|
|
2342
|
-
dial.merge other_dial
|
|
2343
|
-
|
|
2344
|
-
waiter_thread = Thread.new do
|
|
2345
|
-
dial.await_completion
|
|
2346
|
-
latch.countdown!
|
|
2347
|
-
end
|
|
2348
|
-
|
|
2349
|
-
sleep 0.5
|
|
2350
|
-
|
|
2351
|
-
other_mock_call.async << mock_end
|
|
2352
|
-
second_root_call.async << mock_end
|
|
2353
|
-
second_other_mock_call.async << mock_end
|
|
2354
|
-
|
|
2355
|
-
expect(latch.wait(2)).to be_truthy
|
|
2356
|
-
|
|
2357
|
-
waiter_thread.join
|
|
2358
|
-
expect(dial.status.result).to eq(:answer)
|
|
2359
|
-
end
|
|
2360
|
-
end
|
|
2361
|
-
end
|
|
2362
|
-
end
|
|
2363
|
-
end
|
|
2364
|
-
|
|
2365
|
-
describe "when the caller has already hung up" do
|
|
2366
|
-
before do
|
|
2367
|
-
call << mock_end
|
|
2368
|
-
end
|
|
2369
|
-
|
|
2370
|
-
it "should raise Call::Hangup" do
|
|
2371
|
-
expect { subject.dial_and_confirm to, options }.to raise_error(Call::Hangup)
|
|
2372
|
-
end
|
|
2373
|
-
|
|
2374
|
-
it "should not make any outbound calls" do
|
|
2375
|
-
expect(OutboundCall).to receive(:new).never
|
|
2376
|
-
expect { subject.dial_and_confirm to, options }.to raise_error
|
|
2377
|
-
end
|
|
2378
|
-
end
|
|
2379
|
-
|
|
2380
|
-
describe "with multiple third parties specified" do
|
|
2381
|
-
let(:options) { {} }
|
|
2382
|
-
let(:other_options) { options }
|
|
2383
|
-
let(:second_other_options) { options }
|
|
2384
|
-
|
|
2385
|
-
before do
|
|
2386
|
-
expect(OutboundCall).to receive(:new).and_return other_mock_call, second_other_mock_call
|
|
2387
|
-
|
|
2388
|
-
expect(other_mock_call).to receive(:dial).once.with(to, other_options)
|
|
2389
|
-
|
|
2390
|
-
expect(second_other_mock_call).to receive(:dial).once.with(second_to, second_other_options)
|
|
2391
|
-
expect(second_other_mock_call).to receive(:join).never
|
|
2392
|
-
end
|
|
2393
|
-
|
|
2394
|
-
def dial_in_thread
|
|
2395
|
-
Thread.new do
|
|
2396
|
-
status = subject.dial_and_confirm [to, second_to], options
|
|
2397
|
-
latch.countdown!
|
|
2398
|
-
status
|
|
2399
|
-
end
|
|
2400
|
-
end
|
|
2401
|
-
|
|
2402
|
-
it "dials all parties and joins the first one to answer, hanging up the rest" do
|
|
2403
|
-
expect(call).to receive(:answer).once
|
|
2404
|
-
expect(other_mock_call).to receive(:join).once.with(call, {})
|
|
2405
|
-
expect(second_other_mock_call).to receive(:hangup).once do
|
|
2406
|
-
second_other_mock_call << mock_end
|
|
2407
|
-
end
|
|
2408
|
-
|
|
2409
|
-
t = dial_in_thread
|
|
2410
|
-
|
|
2411
|
-
expect(latch.wait(2)).to be_falsey
|
|
2412
|
-
|
|
2413
|
-
other_mock_call << mock_answered
|
|
2414
|
-
other_mock_call << mock_end
|
|
2415
|
-
|
|
2416
|
-
expect(latch.wait(2)).to be_truthy
|
|
2417
|
-
|
|
2418
|
-
t.join
|
|
2419
|
-
status = t.value
|
|
2420
|
-
expect(status).to be_a Dial::DialStatus
|
|
2421
|
-
expect(status.calls.size).to eq(2)
|
|
2422
|
-
status.calls.each { |c| expect(c).to be_a OutboundCall }
|
|
2423
|
-
end
|
|
2424
|
-
|
|
2425
|
-
it "unblocks when the joined call unjoins, allowing it to proceed further" do
|
|
2426
|
-
expect(call).to receive(:answer).once
|
|
2427
|
-
expect(other_mock_call).to receive(:join).once.with(call, {})
|
|
2428
|
-
expect(other_mock_call).to receive(:hangup).once
|
|
2429
|
-
expect(second_other_mock_call).to receive(:hangup).once do
|
|
2430
|
-
second_other_mock_call << mock_end
|
|
2431
|
-
end
|
|
2432
|
-
|
|
2433
|
-
t = dial_in_thread
|
|
2434
|
-
|
|
2435
|
-
expect(latch.wait(2)).to be_falsey
|
|
2436
|
-
|
|
2437
|
-
other_mock_call << mock_answered
|
|
2438
|
-
other_mock_call << Punchblock::Event::Unjoined.new(call_uri: call.id)
|
|
2439
|
-
|
|
2440
|
-
expect(latch.wait(2)).to be_truthy
|
|
2441
|
-
|
|
2442
|
-
t.join
|
|
2443
|
-
status = t.value
|
|
2444
|
-
expect(status).to be_a Dial::DialStatus
|
|
2445
|
-
expect(status.calls.size).to eq(2)
|
|
2446
|
-
status.calls.each { |c| expect(c).to be_a OutboundCall }
|
|
2447
|
-
end
|
|
2448
|
-
|
|
2449
|
-
describe "with options overrides" do
|
|
2450
|
-
let(:options) do
|
|
2451
|
-
{
|
|
2452
|
-
:from => 'foo',
|
|
2453
|
-
:timeout => 3000,
|
|
2454
|
-
:headers => {
|
|
2455
|
-
:x_foo => 'bar'
|
|
2456
|
-
}
|
|
2457
|
-
}
|
|
2458
|
-
end
|
|
2459
|
-
|
|
2460
|
-
let(:dial_other_options) do
|
|
2461
|
-
{
|
|
2462
|
-
:foo => 'bar',
|
|
2463
|
-
:headers => {
|
|
2464
|
-
:x_foo => 'buzz'
|
|
2465
|
-
}
|
|
2466
|
-
}
|
|
2467
|
-
end
|
|
2468
|
-
|
|
2469
|
-
let(:other_options) do
|
|
2470
|
-
{
|
|
2471
|
-
:from => 'foo',
|
|
2472
|
-
:timeout => 3000,
|
|
2473
|
-
:foo => 'bar',
|
|
2474
|
-
:headers => {
|
|
2475
|
-
:x_foo => 'buzz'
|
|
2476
|
-
}
|
|
2477
|
-
|
|
2478
|
-
}
|
|
2479
|
-
end
|
|
2480
|
-
|
|
2481
|
-
let(:dial_second_other_options) do
|
|
2482
|
-
{
|
|
2483
|
-
:timeout => 5000,
|
|
2484
|
-
:headers => {
|
|
2485
|
-
:x_bar => 'barbuzz'
|
|
2486
|
-
}
|
|
2487
|
-
}
|
|
2488
|
-
end
|
|
2489
|
-
|
|
2490
|
-
let(:second_other_options) do
|
|
2491
|
-
{
|
|
2492
|
-
:from => 'foo',
|
|
2493
|
-
:timeout => 5000,
|
|
2494
|
-
:headers => {
|
|
2495
|
-
:x_foo => 'bar',
|
|
2496
|
-
:x_bar => 'barbuzz'
|
|
2497
|
-
}
|
|
2498
|
-
}
|
|
2499
|
-
end
|
|
2500
|
-
|
|
2501
|
-
it "with multiple destinations as an hash, with overrides for each, and an options hash, it dials each call with specified options" do
|
|
2502
|
-
t = Thread.new do
|
|
2503
|
-
subject.dial_and_confirm({
|
|
2504
|
-
to => dial_other_options,
|
|
2505
|
-
second_to => dial_second_other_options
|
|
2506
|
-
}, options)
|
|
2507
|
-
latch.countdown!
|
|
2508
|
-
end
|
|
2509
|
-
|
|
2510
|
-
expect(latch.wait(2)).to be_falsey
|
|
2511
|
-
other_mock_call << mock_end
|
|
2512
|
-
expect(latch.wait(2)).to be_falsey
|
|
2513
|
-
second_other_mock_call << mock_end
|
|
2514
|
-
expect(latch.wait(2)).to be_truthy
|
|
2515
|
-
t.join
|
|
2516
|
-
end
|
|
2517
|
-
end
|
|
2518
|
-
|
|
2519
|
-
context "when all calls are rejected" do
|
|
2520
|
-
it "has an overall dial status of :no_answer" do
|
|
2521
|
-
t = dial_in_thread
|
|
2522
|
-
|
|
2523
|
-
sleep 0.5
|
|
2524
|
-
|
|
2525
|
-
other_mock_call << mock_end(:reject)
|
|
2526
|
-
second_other_mock_call << mock_end(:reject)
|
|
2527
|
-
|
|
2528
|
-
expect(latch.wait(2)).to be_truthy
|
|
2529
|
-
|
|
2530
|
-
t.join
|
|
2531
|
-
status = t.value
|
|
2532
|
-
expect(status.result).to eq(:no_answer)
|
|
2533
|
-
end
|
|
2534
|
-
end
|
|
2535
|
-
|
|
2536
|
-
context "when a call is answered and joined, and the other ends with an error" do
|
|
2537
|
-
it "has an overall dial status of :answer" do
|
|
2538
|
-
expect(call).to receive(:answer).once
|
|
2539
|
-
expect(other_mock_call).to receive(:join).once.with(call, {})
|
|
2540
|
-
expect(second_other_mock_call).to receive(:hangup).once do
|
|
2541
|
-
second_other_mock_call << mock_end(:error)
|
|
2542
|
-
end
|
|
2543
|
-
|
|
2544
|
-
t = dial_in_thread
|
|
2545
|
-
|
|
2546
|
-
sleep 0.5
|
|
2547
|
-
|
|
2548
|
-
other_mock_call << mock_answered
|
|
2549
|
-
other_mock_call << mock_end
|
|
2550
|
-
|
|
2551
|
-
expect(latch.wait(2)).to be_truthy
|
|
2552
|
-
|
|
2553
|
-
t.join
|
|
2554
|
-
status = t.value
|
|
2555
|
-
expect(status.result).to eq(:answer)
|
|
2556
|
-
end
|
|
2557
|
-
end
|
|
2558
|
-
end
|
|
2559
|
-
|
|
2560
|
-
describe "with a timeout specified" do
|
|
2561
|
-
let(:timeout) { 3 }
|
|
2562
|
-
|
|
2563
|
-
it "should abort the dial after the specified timeout" do
|
|
2564
|
-
expect(other_mock_call).to receive(:dial).once
|
|
2565
|
-
expect(other_mock_call).to receive(:hangup).once
|
|
2566
|
-
expect(OutboundCall).to receive(:new).and_return other_mock_call
|
|
2567
|
-
|
|
2568
|
-
time = Time.now
|
|
2569
|
-
|
|
2570
|
-
t = Thread.new do
|
|
2571
|
-
status = subject.dial_and_confirm to, :timeout => timeout
|
|
2572
|
-
latch.countdown!
|
|
2573
|
-
status
|
|
2574
|
-
end
|
|
2575
|
-
|
|
2576
|
-
latch.wait
|
|
2577
|
-
time = Time.now - time
|
|
2578
|
-
expect(time.round).to eq(timeout)
|
|
2579
|
-
t.join
|
|
2580
|
-
status = t.value
|
|
2581
|
-
expect(status.result).to eq(:timeout)
|
|
2582
|
-
end
|
|
2583
|
-
|
|
2584
|
-
describe "if someone answers before the timeout elapses" do
|
|
2585
|
-
it "should not abort until the far end hangs up" do
|
|
2586
|
-
expect(other_mock_call).to receive(:dial).once.with(to, hash_including(:timeout => timeout))
|
|
2587
|
-
expect(call).to receive(:answer).once
|
|
2588
|
-
expect(other_mock_call).to receive(:join).once.with(call, {})
|
|
2589
|
-
expect(OutboundCall).to receive(:new).and_return other_mock_call
|
|
2590
|
-
|
|
2591
|
-
time = Time.now
|
|
2592
|
-
|
|
2593
|
-
t = Thread.new do
|
|
2594
|
-
status = subject.dial_and_confirm to, :timeout => timeout
|
|
2595
|
-
latch.countdown!
|
|
2596
|
-
status
|
|
2597
|
-
end
|
|
2598
|
-
|
|
2599
|
-
expect(latch.wait(2)).to be_falsey
|
|
2600
|
-
|
|
2601
|
-
other_mock_call << mock_answered
|
|
2602
|
-
|
|
2603
|
-
expect(latch.wait(2)).to be_falsey
|
|
2604
|
-
|
|
2605
|
-
other_mock_call << mock_end
|
|
2606
|
-
|
|
2607
|
-
expect(latch.wait(0.1)).to be_truthy
|
|
2608
|
-
time = Time.now - time
|
|
2609
|
-
expect(time.to_i).to be > timeout
|
|
2610
|
-
t.join
|
|
2611
|
-
status = t.value
|
|
2612
|
-
expect(status.result).to eq(:answer)
|
|
2613
|
-
end
|
|
2614
|
-
end
|
|
2615
|
-
end
|
|
2616
|
-
|
|
2617
|
-
describe "with a confirmation controller" do
|
|
2618
|
-
let(:confirmation_controller) do
|
|
2619
|
-
latch = confirmation_latch
|
|
2620
|
-
Class.new(Adhearsion::CallController) do
|
|
2621
|
-
@@confirmation_latch = latch
|
|
2622
|
-
|
|
2623
|
-
def run
|
|
2624
|
-
# Copy metadata onto call variables so we can assert it later. Ugly hack
|
|
2625
|
-
metadata.each_pair do |key, value|
|
|
2626
|
-
call[key] = value
|
|
2627
|
-
end
|
|
2628
|
-
@@confirmation_latch.countdown!
|
|
2629
|
-
if delay = call['confirmation_delay']
|
|
2630
|
-
sleep delay
|
|
2631
|
-
end
|
|
2632
|
-
call['confirm'] || hangup
|
|
2633
|
-
end
|
|
2634
|
-
end
|
|
2635
|
-
end
|
|
2636
|
-
|
|
2637
|
-
let(:confirmation_latch) { CountDownLatch.new 1 }
|
|
2638
|
-
|
|
2639
|
-
let(:options) { {:confirm => confirmation_controller} }
|
|
2640
|
-
|
|
2641
|
-
context "with confirmation controller metadata specified" do
|
|
2642
|
-
let(:options) { {:confirm => confirmation_controller, :confirm_metadata => {:foo => 'bar'}} }
|
|
2643
|
-
|
|
2644
|
-
before do
|
|
2645
|
-
expect(other_mock_call).to receive(:dial).once
|
|
2646
|
-
expect(OutboundCall).to receive(:new).and_return other_mock_call
|
|
2647
|
-
end
|
|
2648
|
-
|
|
2649
|
-
it "should set the metadata on the controller" do
|
|
2650
|
-
expect(other_mock_call).to receive(:hangup).once do
|
|
2651
|
-
other_mock_call << mock_end
|
|
2652
|
-
end
|
|
2653
|
-
other_mock_call['confirm'] = false
|
|
2654
|
-
|
|
2655
|
-
dial_in_thread
|
|
2656
|
-
|
|
2657
|
-
expect(latch.wait(0.1)).to be_falsey
|
|
2658
|
-
|
|
2659
|
-
other_mock_call << mock_answered
|
|
2660
|
-
|
|
2661
|
-
expect(confirmation_latch.wait(2)).to be_truthy
|
|
2662
|
-
expect(latch.wait(2)).to be_truthy
|
|
2663
|
-
|
|
2664
|
-
expect(other_mock_call[:foo]).to eq('bar')
|
|
2665
|
-
end
|
|
2666
|
-
end
|
|
2667
|
-
|
|
2668
|
-
context "when an outbound call is answered" do
|
|
2669
|
-
before do
|
|
2670
|
-
expect(other_mock_call).to receive(:dial).once
|
|
2671
|
-
expect(OutboundCall).to receive(:new).and_return other_mock_call
|
|
2672
|
-
end
|
|
2673
|
-
|
|
2674
|
-
it "should execute the specified confirmation controller" do
|
|
2675
|
-
expect(other_mock_call).to receive(:hangup).once do
|
|
2676
|
-
other_mock_call << mock_end
|
|
2677
|
-
end
|
|
2678
|
-
other_mock_call['confirm'] = false
|
|
2679
|
-
|
|
2680
|
-
dial_in_thread
|
|
2681
|
-
|
|
2682
|
-
expect(latch.wait(0.1)).to be_falsey
|
|
2683
|
-
|
|
2684
|
-
other_mock_call << mock_answered
|
|
2685
|
-
|
|
2686
|
-
expect(confirmation_latch.wait(2)).to be_truthy
|
|
2687
|
-
expect(latch.wait(2)).to be_truthy
|
|
2688
|
-
end
|
|
2689
|
-
|
|
2690
|
-
it "should join the calls if the call is still active after execution of the call controller" do
|
|
2691
|
-
expect(other_mock_call).to receive(:hangup).once do
|
|
2692
|
-
other_mock_call << mock_end
|
|
2693
|
-
end
|
|
2694
|
-
other_mock_call['confirm'] = true
|
|
2695
|
-
expect(call).to receive(:answer).once
|
|
2696
|
-
expect(other_mock_call).to receive(:join).once.with(call, {}) do
|
|
2697
|
-
call << Punchblock::Event::Joined.new(call_uri: other_mock_call.id)
|
|
2698
|
-
other_mock_call << Punchblock::Event::Joined.new(call_uri: call.id)
|
|
2699
|
-
end
|
|
2700
|
-
|
|
2701
|
-
t = dial_in_thread
|
|
2702
|
-
|
|
2703
|
-
expect(latch.wait(2)).to be_falsey
|
|
2704
|
-
|
|
2705
|
-
base_time = Time.local(2008, 9, 1, 12, 0, 0)
|
|
2706
|
-
Timecop.freeze base_time
|
|
2707
|
-
|
|
2708
|
-
other_mock_call << mock_answered
|
|
2709
|
-
|
|
2710
|
-
base_time = Time.local(2008, 9, 1, 12, 0, 42)
|
|
2711
|
-
Timecop.freeze base_time
|
|
2712
|
-
other_mock_call << Punchblock::Event::Unjoined.new(call_uri: call.id)
|
|
2713
|
-
|
|
2714
|
-
expect(latch.wait(2)).to be_truthy
|
|
2715
|
-
|
|
2716
|
-
t.join
|
|
2717
|
-
status = t.value
|
|
2718
|
-
expect(status.result).to eq(:answer)
|
|
2719
|
-
|
|
2720
|
-
joined_status = status.joins[status.calls.first]
|
|
2721
|
-
expect(joined_status.duration).to eq(42.0)
|
|
2722
|
-
expect(joined_status.result).to eq(:joined)
|
|
2723
|
-
end
|
|
2724
|
-
|
|
2725
|
-
it "should not join the calls if the call is not active after execution of the call controller" do
|
|
2726
|
-
expect(other_mock_call).to receive(:hangup).once do
|
|
2727
|
-
other_mock_call << mock_end
|
|
2728
|
-
end
|
|
2729
|
-
other_mock_call['confirm'] = false
|
|
2730
|
-
expect(call).to receive(:answer).never
|
|
2731
|
-
expect(other_mock_call).to receive(:join).never.with(call)
|
|
2732
|
-
|
|
2733
|
-
t = dial_in_thread
|
|
2734
|
-
|
|
2735
|
-
expect(latch.wait(2)).to be_falsey
|
|
2736
|
-
|
|
2737
|
-
other_mock_call << mock_answered
|
|
2738
|
-
|
|
2739
|
-
expect(latch.wait(2)).to be_truthy
|
|
2740
|
-
|
|
2741
|
-
t.join
|
|
2742
|
-
status = t.value
|
|
2743
|
-
expect(status.result).to eq(:unconfirmed)
|
|
2744
|
-
|
|
2745
|
-
joined_status = status.joins[status.calls.first]
|
|
2746
|
-
expect(joined_status.duration).to eq(0.0)
|
|
2747
|
-
expect(joined_status.result).to eq(:unconfirmed)
|
|
2748
|
-
end
|
|
2749
|
-
end
|
|
2750
|
-
|
|
2751
|
-
context "when multiple calls are made" do
|
|
2752
|
-
let(:confirmation_latch) { CountDownLatch.new 2 }
|
|
2753
|
-
let(:apology_controller) do
|
|
2754
|
-
Class.new(Adhearsion::CallController) do
|
|
2755
|
-
def run
|
|
2756
|
-
logger.info "Apologising..."
|
|
2757
|
-
call['apology_metadata'] = metadata
|
|
2758
|
-
call['apology_done'] = true
|
|
2759
|
-
end
|
|
2760
|
-
end
|
|
2761
|
-
end
|
|
2762
|
-
let(:options) { {confirm: confirmation_controller, confirm_metadata: {'foo' => 'bar'}, apology: apology_controller} }
|
|
2763
|
-
|
|
2764
|
-
before do
|
|
2765
|
-
expect(OutboundCall).to receive(:new).and_return other_mock_call, second_other_mock_call
|
|
1424
|
+
expect(OutboundCall).to receive(:new).and_return other_mock_call, second_other_mock_call
|
|
2766
1425
|
end
|
|
2767
1426
|
|
|
2768
1427
|
def dial_in_thread
|
|
2769
1428
|
Thread.new do
|
|
2770
|
-
status = subject.
|
|
1429
|
+
status = subject.dial [to, second_to], options
|
|
2771
1430
|
latch.countdown!
|
|
2772
1431
|
status
|
|
2773
1432
|
end
|
|
@@ -2784,8 +1443,8 @@ module Adhearsion
|
|
|
2784
1443
|
|
|
2785
1444
|
expect(other_mock_call).to receive(:dial).once.with(to, from: nil)
|
|
2786
1445
|
expect(other_mock_call).to receive(:join).once.with(call, {}) do
|
|
2787
|
-
call <<
|
|
2788
|
-
other_mock_call <<
|
|
1446
|
+
call << Adhearsion::Event::Joined.new(call_uri: other_mock_call.id)
|
|
1447
|
+
other_mock_call << Adhearsion::Event::Joined.new(call_uri: call.id)
|
|
2789
1448
|
end
|
|
2790
1449
|
expect(other_mock_call).to receive(:hangup).once do
|
|
2791
1450
|
other_mock_call.async.deliver_message mock_end
|
|
@@ -2807,7 +1466,7 @@ module Adhearsion
|
|
|
2807
1466
|
|
|
2808
1467
|
sleep 2
|
|
2809
1468
|
|
|
2810
|
-
other_mock_call.async.deliver_message
|
|
1469
|
+
other_mock_call.async.deliver_message Adhearsion::Event::Unjoined.new(call_uri: call.id)
|
|
2811
1470
|
|
|
2812
1471
|
expect(latch.wait(2)).to be_truthy
|
|
2813
1472
|
|