adhearsion 2.0.0.beta1 → 2.0.0.rc1

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.
Files changed (118) hide show
  1. data/.travis.yml +2 -4
  2. data/CHANGELOG.md +34 -4
  3. data/README.markdown +2 -1
  4. data/Rakefile +22 -1
  5. data/adhearsion.gemspec +1 -0
  6. data/bin/ahn +0 -2
  7. data/features/cli_daemon.feature +2 -0
  8. data/features/cli_restart.feature +19 -0
  9. data/features/cli_start.feature +4 -6
  10. data/features/cli_stop.feature +3 -0
  11. data/features/step_definitions/app_generator_steps.rb +2 -0
  12. data/features/step_definitions/cli_steps.rb +2 -0
  13. data/features/support/aruba_helper.rb +2 -0
  14. data/features/support/env.rb +8 -46
  15. data/features/support/utils.rb +2 -0
  16. data/lib/adhearsion.rb +4 -6
  17. data/lib/adhearsion/call.rb +71 -17
  18. data/lib/adhearsion/call_controller.rb +25 -14
  19. data/lib/adhearsion/call_controller/dial.rb +34 -15
  20. data/lib/adhearsion/call_controller/input.rb +186 -144
  21. data/lib/adhearsion/call_controller/output.rb +10 -6
  22. data/lib/adhearsion/call_controller/record.rb +11 -13
  23. data/lib/adhearsion/call_controller/utility.rb +2 -0
  24. data/lib/adhearsion/calls.rb +4 -2
  25. data/lib/adhearsion/cli.rb +4 -0
  26. data/lib/adhearsion/cli_commands.rb +8 -2
  27. data/lib/adhearsion/configuration.rb +7 -3
  28. data/lib/adhearsion/console.rb +17 -17
  29. data/lib/adhearsion/events.rb +10 -4
  30. data/lib/adhearsion/foundation.rb +9 -0
  31. data/lib/adhearsion/foundation/custom_daemonizer.rb +3 -1
  32. data/lib/adhearsion/foundation/exception_handler.rb +2 -0
  33. data/lib/adhearsion/foundation/libc.rb +2 -0
  34. data/lib/adhearsion/foundation/object.rb +3 -0
  35. data/lib/adhearsion/foundation/thread_safety.rb +5 -11
  36. data/lib/adhearsion/generators.rb +2 -0
  37. data/lib/adhearsion/generators/app/app_generator.rb +2 -0
  38. data/lib/adhearsion/generators/app/templates/README.md +9 -0
  39. data/lib/adhearsion/generators/app/templates/config/adhearsion.rb +38 -16
  40. data/lib/adhearsion/generators/app/templates/config/environment.rb +2 -0
  41. data/lib/adhearsion/generators/app/templates/lib/simon_game.rb +5 -3
  42. data/lib/adhearsion/generators/controller/controller_generator.rb +2 -0
  43. data/lib/adhearsion/generators/controller/templates/lib/controller.rb +2 -0
  44. data/lib/adhearsion/generators/controller/templates/spec/controller_spec.rb +2 -0
  45. data/lib/adhearsion/generators/generator.rb +3 -1
  46. data/lib/adhearsion/generators/plugin/plugin_generator.rb +2 -0
  47. data/lib/adhearsion/initializer.rb +31 -17
  48. data/lib/adhearsion/linux_proc_name.rb +2 -0
  49. data/lib/adhearsion/logging.rb +5 -3
  50. data/lib/adhearsion/menu_dsl.rb +2 -0
  51. data/lib/adhearsion/menu_dsl/calculated_match.rb +2 -0
  52. data/lib/adhearsion/menu_dsl/calculated_match_collection.rb +2 -0
  53. data/lib/adhearsion/menu_dsl/fixnum_match_calculator.rb +2 -0
  54. data/lib/adhearsion/menu_dsl/match_calculator.rb +2 -0
  55. data/lib/adhearsion/menu_dsl/menu.rb +58 -4
  56. data/lib/adhearsion/menu_dsl/menu_builder.rb +14 -1
  57. data/lib/adhearsion/menu_dsl/range_match_calculator.rb +4 -1
  58. data/lib/adhearsion/menu_dsl/string_match_calculator.rb +2 -0
  59. data/lib/adhearsion/outbound_call.rb +2 -0
  60. data/lib/adhearsion/plugin.rb +9 -7
  61. data/lib/adhearsion/plugin/collection.rb +3 -1
  62. data/lib/adhearsion/plugin/initializer.rb +3 -1
  63. data/lib/adhearsion/process.rb +8 -2
  64. data/lib/adhearsion/punchblock_plugin.rb +3 -1
  65. data/lib/adhearsion/punchblock_plugin/initializer.rb +34 -11
  66. data/lib/adhearsion/router.rb +4 -2
  67. data/lib/adhearsion/router/route.rb +2 -0
  68. data/lib/adhearsion/script_ahn_loader.rb +2 -0
  69. data/lib/adhearsion/tasks.rb +2 -0
  70. data/lib/adhearsion/tasks/configuration.rb +2 -0
  71. data/lib/adhearsion/tasks/debugging.rb +8 -0
  72. data/lib/adhearsion/tasks/environment.rb +2 -0
  73. data/lib/adhearsion/tasks/plugins.rb +2 -0
  74. data/lib/adhearsion/tasks/testing.rb +2 -0
  75. data/lib/adhearsion/version.rb +3 -1
  76. data/pre-commit +2 -0
  77. data/spec/adhearsion/call_controller/dial_spec.rb +114 -25
  78. data/spec/adhearsion/call_controller/input_spec.rb +192 -169
  79. data/spec/adhearsion/call_controller/output_spec.rb +26 -12
  80. data/spec/adhearsion/call_controller/record_spec.rb +29 -77
  81. data/spec/adhearsion/call_controller/utility_spec.rb +69 -0
  82. data/spec/adhearsion/call_controller_spec.rb +90 -15
  83. data/spec/adhearsion/call_spec.rb +92 -24
  84. data/spec/adhearsion/calls_spec.rb +9 -7
  85. data/spec/adhearsion/configuration_spec.rb +58 -56
  86. data/spec/adhearsion/console_spec.rb +4 -2
  87. data/spec/adhearsion/events_spec.rb +9 -7
  88. data/spec/adhearsion/generators_spec.rb +3 -1
  89. data/spec/adhearsion/initializer_spec.rb +16 -14
  90. data/spec/adhearsion/logging_spec.rb +11 -9
  91. data/spec/adhearsion/menu_dsl/calculated_match_collection_spec.rb +6 -4
  92. data/spec/adhearsion/menu_dsl/calculated_match_spec.rb +6 -4
  93. data/spec/adhearsion/menu_dsl/fixnum_match_calculator_spec.rb +3 -1
  94. data/spec/adhearsion/menu_dsl/match_calculator_spec.rb +2 -0
  95. data/spec/adhearsion/menu_dsl/menu_builder_spec.rb +42 -11
  96. data/spec/adhearsion/menu_dsl/menu_spec.rb +197 -36
  97. data/spec/adhearsion/menu_dsl/range_match_calculator_spec.rb +4 -2
  98. data/spec/adhearsion/menu_dsl/string_match_calculator_spec.rb +5 -3
  99. data/spec/adhearsion/outbound_call_spec.rb +7 -5
  100. data/spec/adhearsion/plugin_spec.rb +19 -15
  101. data/spec/adhearsion/process_spec.rb +12 -7
  102. data/spec/adhearsion/punchblock_plugin/initializer_spec.rb +35 -15
  103. data/spec/adhearsion/punchblock_plugin_spec.rb +4 -1
  104. data/spec/adhearsion/router/route_spec.rb +8 -6
  105. data/spec/adhearsion/router_spec.rb +12 -10
  106. data/spec/adhearsion_spec.rb +13 -2
  107. data/spec/capture_warnings.rb +33 -0
  108. data/spec/spec_helper.rb +4 -0
  109. data/spec/support/call_controller_test_helpers.rb +2 -4
  110. data/spec/support/initializer_stubs.rb +8 -5
  111. data/spec/support/logging_helpers.rb +2 -0
  112. data/spec/support/punchblock_mocks.rb +2 -0
  113. metadata +84 -71
  114. data/EVENTS +0 -11
  115. data/lib/adhearsion/call_controller/menu.rb +0 -124
  116. data/lib/adhearsion/foundation/all.rb +0 -8
  117. data/spec/adhearsion/call_controller/menu_spec.rb +0 -120
  118. data/spec/adhearsion/menu_dsl_spec.rb +0 -12
@@ -1,3 +1,5 @@
1
+ # encoding: utf-8
2
+
1
3
  require 'spec_helper'
2
4
 
3
5
  module Adhearsion
@@ -12,111 +14,61 @@ module Adhearsion
12
14
  :max_duration => 5000
13
15
  }
14
16
  end
15
- let(:component) { Punchblock::Component::Record.new options }
16
- let(:response) { Punchblock::Event::Complete.new }
17
+ let(:component) { ::Punchblock::Component::Record.new options }
18
+ let(:response) { ::Punchblock::Event::Complete.new }
17
19
 
18
20
  describe "with :async => true and an :on_complete callback" do
19
- let(:callback) { lambda { |rec| @rec.push rec } }
20
-
21
21
  before do
22
- expect_component_execution_asynchronously component
22
+ component
23
+ flexmock(::Punchblock::Component::Record).should_receive(:new).once.with(options).and_return component
24
+ expect_message_waiting_for_response component
23
25
  @rec = Queue.new
24
- options.merge! :async => true, :on_complete => callback
25
- subject.record options
26
- component.request!
26
+ subject.record(options.merge(async: true)) { |rec| @rec.push rec }
27
27
  component.execute!
28
28
  end
29
29
 
30
30
  it "should execute the callback" do
31
31
  component.trigger_event_handler response
32
32
  Timeout::timeout 5 do
33
- @rec.pop.should == response
33
+ @rec.pop.should be response
34
34
  end
35
35
  end
36
+ end
36
37
 
37
- describe "when the callback raises an exception" do
38
- before { TestException = Class.new StandardError }
39
- let(:callback) { lambda { |rec| raise TestException } }
38
+ describe "when the callback raises an exception" do
39
+ before do
40
+ TestException = Class.new StandardError
41
+ component
42
+ flexmock(::Punchblock::Component::Record).should_receive(:new).once.with({}).and_return component
43
+ end
40
44
 
41
- it "should pass the exception to the events system" do
42
- flexmock(Events).should_receive(:trigger).once.with(:exception, TestException)
43
- component.trigger_event_handler response
44
- end
45
+ it "should pass the exception to the events system" do
46
+ flexmock(Events).should_receive(:trigger).once.with(:exception, TestException)
47
+ expect_component_execution component
48
+ subject.record { |rec| raise TestException }
49
+ component.request!
50
+ component.execute!
51
+ component.trigger_event_handler response
45
52
  end
46
53
  end
47
54
 
48
55
  describe "with :async => false" do
49
56
  before do
57
+ component
58
+ flexmock(::Punchblock::Component::Record).should_receive(:new).once.with(options).and_return component
50
59
  expect_component_execution component
51
- component.request!
52
- component.execute!
53
- component.add_event response
54
60
  @rec = Queue.new
55
61
  subject.record(options.merge(:async => false)) { |rec| @rec.push rec }
62
+ component.request!
63
+ component.execute!
56
64
  end
57
65
 
58
66
  it 'should execute a passed block' do
67
+ component.trigger_event_handler response
59
68
  Timeout::timeout 5 do
60
- @rec.pop.should == response
61
- end
62
- end
63
- end
64
- end
65
-
66
- describe "#record with default options" do
67
- let(:options) {{
68
- :start_beep => true,
69
- :format => 'mp3',
70
- :start_paused => false,
71
- :stop_beep => true,
72
- :max_duration => 500000,
73
- :initial_timeout => 10000,
74
- :final_timeout => 30000
75
- }}
76
-
77
- let(:component) { Punchblock::Component::Record.new(options) }
78
- let(:response) { Punchblock::Event::Complete.new }
79
-
80
- before do
81
- expect_message_waiting_for_response component
82
- component.execute!
83
- component.complete_event = response
84
- end
85
-
86
- it 'executes a #record with the correct options' do
87
- subject.execute_component_and_await_completion component
88
- end
89
-
90
- it 'takes a block which is executed after acknowledgement but before waiting on completion' do
91
- @comp = nil
92
- subject.execute_component_and_await_completion(component) { |comp| @comp = comp }.should == component
93
- @comp.should == component
94
- end
95
-
96
- describe "with a successful completion" do
97
- it 'returns the executed component' do
98
- subject.execute_component_and_await_completion(component).should be component
99
- end
100
- end
101
-
102
- describe 'with an error response' do
103
- let(:response) do
104
- Punchblock::Event::Complete.new.tap do |complete|
105
- complete << error
69
+ @rec.pop.should be == response
106
70
  end
107
71
  end
108
-
109
- let(:error) do |error|
110
- Punchblock::Event::Complete::Error.new.tap do |error|
111
- error << details
112
- end
113
- end
114
-
115
- let(:details) { "Something came up" }
116
-
117
- it 'raises the error' do
118
- lambda { subject.execute_component_and_await_completion component }.should raise_error(StandardError, details)
119
- end
120
72
  end
121
73
  end
122
74
 
@@ -0,0 +1,69 @@
1
+ # encoding: utf-8
2
+
3
+ require 'spec_helper'
4
+
5
+ module Adhearsion
6
+ class CallController
7
+ describe Utility do
8
+ include CallControllerTestHelpers
9
+
10
+ describe "#grammar_digits" do
11
+ let(:grxml) {
12
+ RubySpeech::GRXML.draw :mode => 'dtmf', :root => 'inputdigits' do
13
+ rule id: 'inputdigits', scope: 'public' do
14
+ item repeat: '2' do
15
+ one_of do
16
+ 0.upto(9) { |d| item { d.to_s } }
17
+ end
18
+ end
19
+ end
20
+ end
21
+ }
22
+
23
+ it 'generates the correct GRXML grammar' do
24
+ subject.grammar_digits(2).to_s.should be == grxml.to_s
25
+ end
26
+
27
+ end # describe #grammar_digits
28
+
29
+ describe "#grammar_accept" do
30
+ let(:grxml) {
31
+ RubySpeech::GRXML.draw :mode => 'dtmf', :root => 'inputdigits' do
32
+ rule id: 'inputdigits', scope: 'public' do
33
+ one_of do
34
+ item { '3' }
35
+ item { '5' }
36
+ end
37
+ end
38
+ end
39
+ }
40
+
41
+ it 'generates the correct GRXML grammar' do
42
+ subject.grammar_accept('35').to_s.should be == grxml.to_s
43
+ end
44
+
45
+ it 'filters meaningless characters out' do
46
+ subject.grammar_accept('3+5').to_s.should be == grxml.to_s
47
+ end
48
+ end # grammar_accept
49
+
50
+ describe "#parse_single_dtmf" do
51
+ it "correctly returns the parsed input" do
52
+ subject.parse_single_dtmf("dtmf-3").should be == '3'
53
+ end
54
+
55
+ it "correctly returns star as *" do
56
+ subject.parse_single_dtmf("dtmf-star").should be == '*'
57
+ end
58
+
59
+ it "correctly returns pound as #" do
60
+ subject.parse_single_dtmf("dtmf-pound").should be == '#'
61
+ end
62
+
63
+ it "correctly returns nil when input is nil" do
64
+ subject.parse_single_dtmf(nil).should be == nil
65
+ end
66
+ end # describe #grammar_accept
67
+ end
68
+ end
69
+ end
@@ -1,3 +1,5 @@
1
+ # encoding: utf-8
2
+
1
3
  require 'spec_helper'
2
4
 
3
5
  class FinancialWizard < Adhearsion::CallController
@@ -10,16 +12,16 @@ module Adhearsion
10
12
  let(:call) { Adhearsion::Call.new mock_offer(nil, :x_foo => 'bar') }
11
13
 
12
14
  its(:call) { should be call }
13
- its(:metadata) { should == {:doo => :dah} }
15
+ its(:metadata) { should be == {:doo => :dah} }
14
16
 
15
17
  describe "setting meta-data" do
16
18
  it "should preserve data correctly" do
17
19
  subject[:foo].should be nil
18
20
  subject[:foo] = 7
19
- subject[:foo].should == 7
21
+ subject[:foo].should be == 7
20
22
  subject[:bar] = 10
21
- subject[:bar].should == 10
22
- subject[:foo].should == 7
23
+ subject[:bar].should be == 10
24
+ subject[:foo].should be == 7
23
25
  end
24
26
  end
25
27
 
@@ -33,15 +35,23 @@ module Adhearsion
33
35
  end
34
36
 
35
37
  it "catches Hangup exceptions and logs the hangup" do
36
- subject.should_receive(:run).once.and_raise(Hangup).ordered
38
+ subject.should_receive(:run).once.and_raise(Call::Hangup).ordered
37
39
  flexmock(subject.logger).should_receive(:info).once.with(/Call was hung up/).ordered
38
40
  subject.execute!
39
41
  end
40
42
 
41
43
  it "catches standard errors, triggering an exception event" do
42
44
  subject.should_receive(:run).once.and_raise(StandardError).ordered
43
- flexmock(Events).should_receive(:trigger).once.with(:exception, StandardError).ordered
45
+ latch = CountDownLatch.new 1
46
+ ex = lo = nil
47
+ Events.exception do |e, l|
48
+ ex, lo = e, l
49
+ latch.countdown!
50
+ end
44
51
  subject.execute!
52
+ latch.wait(1).should be true
53
+ ex.should be_a StandardError
54
+ lo.should be subject.logger
45
55
  end
46
56
 
47
57
  context "when a block is specified" do
@@ -76,7 +86,7 @@ module Adhearsion
76
86
  end
77
87
 
78
88
  def simulate_remote_hangup
79
- raise Hangup
89
+ raise Call::Hangup
80
90
  end
81
91
  end
82
92
 
@@ -193,8 +203,7 @@ module Adhearsion
193
203
  :reject,
194
204
  :hangup,
195
205
  :mute,
196
- :unmute,
197
- :join].each do |method_name|
206
+ :unmute].each do |method_name|
198
207
  describe "##{method_name}" do
199
208
  it "delegates to the call, blocking first until it is allowed to execute" do
200
209
  flexmock(subject).should_receive(:block_until_resumed).once.ordered
@@ -204,6 +213,70 @@ module Adhearsion
204
213
  end
205
214
  end
206
215
 
216
+ describe "#join" do
217
+ it "delegates to the call, blocking first until it is allowed to execute, and unblocking when an unjoined event is received" do
218
+ flexmock(subject).should_receive(:block_until_resumed).once.ordered
219
+ flexmock(subject.call).should_receive(:join).once.with('call1', :foo => :bar).ordered.and_return Punchblock::Command::Join.new(:other_call_id => 'call1')
220
+ latch = CountDownLatch.new 1
221
+ Thread.new do
222
+ subject.join 'call1', :foo => :bar
223
+ latch.countdown!
224
+ end
225
+ latch.wait(1).should be false
226
+ subject.call << Punchblock::Event::Joined.new(:other_call_id => 'call1')
227
+ latch.wait(1).should be false
228
+ subject.call << Punchblock::Event::Unjoined.new(:other_call_id => 'call1')
229
+ latch.wait(1).should be true
230
+ end
231
+
232
+ context "with a mixer" do
233
+ it "delegates to the call, blocking first until it is allowed to execute, and unblocking when an unjoined event is received" do
234
+ flexmock(subject).should_receive(:block_until_resumed).once.ordered
235
+ flexmock(subject.call).should_receive(:join).once.with({:mixer_name => 'foobar', :foo => :bar}, {}).ordered.and_return Punchblock::Command::Join.new(:mixer_name => 'foobar')
236
+ latch = CountDownLatch.new 1
237
+ Thread.new do
238
+ subject.join :mixer_name => 'foobar', :foo => :bar
239
+ latch.countdown!
240
+ end
241
+ latch.wait(1).should be false
242
+ subject.call << Punchblock::Event::Joined.new(:mixer_name => 'foobar')
243
+ latch.wait(1).should be false
244
+ subject.call << Punchblock::Event::Unjoined.new(:mixer_name => 'foobar')
245
+ latch.wait(1).should be true
246
+ end
247
+ end
248
+
249
+ context "with :async => true" do
250
+ it "delegates to the call, blocking first until it is allowed to execute, and unblocking when the joined event is received" do
251
+ flexmock(subject).should_receive(:block_until_resumed).once.ordered
252
+ flexmock(subject.call).should_receive(:join).once.with('call1', :foo => :bar).ordered.and_return Punchblock::Command::Join.new(:other_call_id => 'call1')
253
+ latch = CountDownLatch.new 1
254
+ Thread.new do
255
+ subject.join 'call1', :foo => :bar, :async => true
256
+ latch.countdown!
257
+ end
258
+ latch.wait(1).should be false
259
+ subject.call << Punchblock::Event::Joined.new(:other_call_id => 'call1')
260
+ latch.wait(1).should be true
261
+ end
262
+
263
+ context "with a mixer" do
264
+ it "delegates to the call, blocking first until it is allowed to execute, and unblocking when the joined event is received" do
265
+ flexmock(subject).should_receive(:block_until_resumed).once.ordered
266
+ flexmock(subject.call).should_receive(:join).once.with({:mixer_name => 'foobar', :foo => :bar}, {}).ordered.and_return Punchblock::Command::Join.new(:mixer_name => 'foobar')
267
+ latch = CountDownLatch.new 1
268
+ Thread.new do
269
+ subject.join :mixer_name => 'foobar', :foo => :bar, :async => true
270
+ latch.countdown!
271
+ end
272
+ latch.wait(1).should be false
273
+ subject.call << Punchblock::Event::Joined.new(:mixer_name => 'foobar')
274
+ latch.wait(1).should be true
275
+ end
276
+ end
277
+ end
278
+ end
279
+
207
280
  describe "#block_until_resumed" do
208
281
  context "when the controller has not been paused" do
209
282
  it "should not block" do
@@ -221,7 +294,7 @@ module Adhearsion
221
294
  it "should unblock when the controller is unpaused" do
222
295
  t1 = t2 = nil
223
296
  latch = CountDownLatch.new 1
224
- t = Thread.new do
297
+ Thread.new do
225
298
  t1 = Time.now
226
299
  subject.block_until_resumed
227
300
  t2 = Time.now
@@ -255,8 +328,8 @@ module Adhearsion
255
328
 
256
329
  it "takes a block which is executed after acknowledgement but before waiting on completion" do
257
330
  @comp = nil
258
- subject.execute_component_and_await_completion(component) { |comp| @comp = comp }.should == component
259
- @comp.should == component
331
+ subject.execute_component_and_await_completion(component) { |comp| @comp = comp }.should be == component
332
+ @comp.should be == component
260
333
  end
261
334
 
262
335
  describe "with a successful completion" do
@@ -273,20 +346,22 @@ module Adhearsion
273
346
  end
274
347
 
275
348
  let(:error) do |error|
276
- Punchblock::Event::Complete::Error.new.tap do |error|
277
- error << details
349
+ Punchblock::Event::Complete::Error.new.tap do |e|
350
+ e << details
278
351
  end
279
352
  end
280
353
 
281
354
  let(:details) { "Oh noes, it's all borked" }
282
355
 
283
356
  it "raises the error" do
284
- lambda { subject.execute_component_and_await_completion component }.should raise_error(StandardError, details)
357
+ lambda { subject.execute_component_and_await_completion component }.should raise_error(StandardError, "#{details}: #{component}")
285
358
  end
286
359
  end
287
360
 
288
361
  it "blocks until the component receives a complete event" do
289
362
  slow_component = Punchblock::Component::Output.new
363
+ slow_component.request!
364
+ slow_component.execute!
290
365
  Thread.new do
291
366
  sleep 0.5
292
367
  slow_component.complete_event = response
@@ -1,8 +1,10 @@
1
+ # encoding: utf-8
2
+
1
3
  require 'spec_helper'
2
4
 
3
5
  module Adhearsion
4
6
  describe Call do
5
- let(:mock_client) { flexmock('Client').tap &:should_ignore_missing }
7
+ let(:mock_client) { flexmock('Client').tap(&:should_ignore_missing) }
6
8
 
7
9
  let(:call_id) { rand }
8
10
  let(:headers) { nil }
@@ -27,32 +29,31 @@ module Adhearsion
27
29
 
28
30
  it { should respond_to :<< }
29
31
 
30
- its(:end_reason) { should == nil }
32
+ its(:end_reason) { should be == nil }
31
33
  it { should be_active }
32
34
 
33
- its(:commands) { should be_a Call::CommandRegistry }
34
35
  its(:commands) { should be_empty }
35
36
 
36
- its(:id) { should == call_id }
37
- its(:to) { should == to }
38
- its(:from) { should == from }
37
+ its(:id) { should be == call_id }
38
+ its(:to) { should be == to }
39
+ its(:from) { should be == from }
39
40
  its(:offer) { should be offer }
40
41
  its(:client) { should be mock_client }
41
42
 
42
- its(:after_end_hold_time) { should == 30 }
43
+ its(:after_end_hold_time) { should be == 30 }
43
44
 
44
45
  describe "its variables" do
45
46
  context "with an offer with headers" do
46
47
  let(:headers) { {:x_foo => 'bar'} }
47
- its(:variables) { should == headers }
48
+ its(:variables) { should be == headers }
48
49
 
49
50
  it "should be made available via []" do
50
- subject[:x_foo].should == 'bar'
51
+ subject[:x_foo].should be == 'bar'
51
52
  end
52
53
 
53
54
  it "should be alterable using []=" do
54
55
  subject[:x_foo] = 'baz'
55
- subject[:x_foo].should == 'baz'
56
+ subject[:x_foo].should be == 'baz'
56
57
  end
57
58
 
58
59
  context "when receiving an event with headers" do
@@ -60,7 +61,7 @@ module Adhearsion
60
61
 
61
62
  it "should merge later headers" do
62
63
  subject << event
63
- subject.variables.should == {:x_foo => 'bar', :x_bar => 'foo'}
64
+ subject.variables.should be == {:x_foo => 'bar', :x_bar => 'foo'}
64
65
  end
65
66
  end
66
67
 
@@ -69,19 +70,19 @@ module Adhearsion
69
70
 
70
71
  it "should merge later headers" do
71
72
  subject.write_command command
72
- subject.variables.should == {:x_foo => 'bar', :x_bar => 'foo'}
73
+ subject.variables.should be == {:x_foo => 'bar', :x_bar => 'foo'}
73
74
  end
74
75
  end
75
76
  end
76
77
 
77
78
  context "with an offer without headers" do
78
79
  let(:headers) { nil }
79
- its(:variables) { should == {} }
80
+ its(:variables) { should be == {} }
80
81
  end
81
82
 
82
83
  context "without an offer" do
83
84
  let(:offer) { nil }
84
- its(:variables) { should == {} }
85
+ its(:variables) { should be == {} }
85
86
  end
86
87
  end
87
88
 
@@ -133,7 +134,7 @@ module Adhearsion
133
134
 
134
135
  it "should set the end reason" do
135
136
  subject << end_event
136
- subject.end_reason.should == :hangup
137
+ subject.end_reason.should be == :hangup
137
138
  end
138
139
 
139
140
  it "should instruct the command registry to terminate" do
@@ -145,10 +146,10 @@ module Adhearsion
145
146
  size_before = Adhearsion.active_calls.size
146
147
 
147
148
  Adhearsion.active_calls << subject
148
- Adhearsion.active_calls.size.should > size_before
149
+ Adhearsion.active_calls.size.should be > size_before
149
150
 
150
151
  subject << end_event
151
- Adhearsion.active_calls.size.should == size_before
152
+ Adhearsion.active_calls.size.should be == size_before
152
153
  end
153
154
 
154
155
  it "shuts down the actor" do
@@ -190,7 +191,7 @@ module Adhearsion
190
191
  subject.tag :female
191
192
  subject.remove_tag :female
192
193
  subject.tag :male
193
- subject.tags.should == [:moderator, :male]
194
+ subject.tags.should be == [:moderator, :male]
194
195
  end
195
196
 
196
197
  describe "#tagged_with?" do
@@ -214,7 +215,7 @@ module Adhearsion
214
215
  it "should asynchronously write the command to the Punchblock connection" do
215
216
  mock_client = flexmock('Client')
216
217
  flexmock(subject.wrapped_object).should_receive(:client).once.and_return mock_client
217
- mock_client.should_receive(:execute_command).once.with(mock_command, :call_id => subject.id).and_return true
218
+ mock_client.should_receive(:execute_command).once.with(mock_command, :call_id => subject.id, :async => true).and_return true
218
219
  subject.write_command mock_command
219
220
  end
220
221
 
@@ -224,7 +225,7 @@ module Adhearsion
224
225
  end
225
226
 
226
227
  it "should raise a Hangup exception" do
227
- lambda { subject.write_command mock_command }.should raise_error(Hangup)
228
+ lambda { subject.write_command mock_command }.should raise_error(Call::Hangup)
228
229
  end
229
230
 
230
231
  describe "if the command is a Hangup" do
@@ -258,6 +259,7 @@ module Adhearsion
258
259
 
259
260
  it "blocks until a response is received" do
260
261
  slow_command = Punchblock::Command::Dial.new
262
+ slow_command.request!
261
263
  Thread.new do
262
264
  sleep 0.5
263
265
  slow_command.response = response
@@ -274,13 +276,22 @@ module Adhearsion
274
276
  end
275
277
 
276
278
  describe "with an error response" do
277
- let(:new_exception) { Class.new Exception }
279
+ let(:new_exception) { Punchblock::ProtocolError }
278
280
  let(:response) { new_exception.new }
279
281
 
280
282
  it "raises the error" do
281
283
  flexmock(Events).should_receive(:trigger).never
282
284
  lambda { subject.write_and_await_response message }.should raise_error new_exception
283
285
  end
286
+
287
+ context "where the name is :item_not_found" do
288
+ let(:response) { new_exception.new :item_not_found }
289
+
290
+ it "should raise a Hangup exception" do
291
+ flexmock(Events).should_receive(:trigger).never
292
+ lambda { subject.write_and_await_response message }.should raise_error Call::Hangup
293
+ end
294
+ end
284
295
  end
285
296
 
286
297
  describe "when the response times out" do
@@ -289,7 +300,7 @@ module Adhearsion
289
300
  end
290
301
 
291
302
  it "should raise the error in the caller but not crash the actor" do
292
- lambda { subject.write_and_await_response message }.should raise_error Timeout::Error
303
+ lambda { subject.write_and_await_response message }.should raise_error Call::CommandTimeout, message.to_s
293
304
  sleep 0.5
294
305
  subject.should be_alive
295
306
  end
@@ -497,6 +508,63 @@ module Adhearsion
497
508
  end
498
509
  end
499
510
 
511
+ describe "#unjoin" do
512
+ def expect_unjoin_with_options(options = {})
513
+ Punchblock::Command::Unjoin.new(options).tap do |unjoin|
514
+ expect_message_waiting_for_response unjoin
515
+ end
516
+ end
517
+
518
+ context "with a call" do
519
+ let(:call_id) { rand.to_s }
520
+ let(:target) { flexmock Call.new, :id => call_id }
521
+
522
+ it "should send an unjoin command unjoining from the provided call ID" do
523
+ expect_unjoin_with_options :other_call_id => call_id
524
+ subject.unjoin target
525
+ end
526
+ end
527
+
528
+ context "with a call ID" do
529
+ let(:target) { rand.to_s }
530
+
531
+ it "should send an unjoin command unjoining from the provided call ID" do
532
+ expect_unjoin_with_options :other_call_id => target
533
+ subject.unjoin target
534
+ end
535
+ end
536
+
537
+ context "with a call ID as a hash key" do
538
+ let(:call_id) { rand.to_s }
539
+ let(:target) { { :call_id => call_id } }
540
+
541
+ it "should send an unjoin command unjoining from the provided call ID" do
542
+ expect_unjoin_with_options :other_call_id => call_id
543
+ subject.unjoin target
544
+ end
545
+ end
546
+
547
+ context "with a mixer name as a hash key" do
548
+ let(:mixer_name) { rand.to_s }
549
+ let(:target) { { :mixer_name => mixer_name } }
550
+
551
+ it "should send an unjoin command unjoining from the provided call ID" do
552
+ expect_unjoin_with_options :mixer_name => mixer_name
553
+ subject.unjoin target
554
+ end
555
+ end
556
+
557
+ context "with a call ID and a mixer name as hash keys" do
558
+ let(:call_id) { rand.to_s }
559
+ let(:mixer_name) { rand.to_s }
560
+ let(:target) { { :call_id => call_id, :mixer_name => mixer_name } }
561
+
562
+ it "should raise an ArgumentError" do
563
+ lambda { subject.unjoin target }.should raise_error ArgumentError, /call ID and mixer name/
564
+ end
565
+ end
566
+ end
567
+
500
568
  describe "#mute" do
501
569
  it 'should send a Mute message' do
502
570
  expect_message_waiting_for_response Punchblock::Command::Mute.new
@@ -612,9 +680,9 @@ module Adhearsion
612
680
  end
613
681
  subject.terminate
614
682
  commands.each do |command|
615
- command.response.should be_a Hangup
683
+ command.response.should be_a Call::Hangup
616
684
  end
617
- finished_command.response.should == :foo
685
+ finished_command.response.should be == :foo
618
686
  end
619
687
  end
620
688
  end