adhearsion 2.0.0.beta1 → 2.0.0.rc1

Sign up to get free protection for your applications and to get access to all the features.
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