punchblock 0.9.2 → 0.10.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (113) hide show
  1. data/.gitignore +1 -0
  2. data/CHANGELOG.md +34 -21
  3. data/Rakefile +20 -0
  4. data/lib/punchblock/client/component_registry.rb +2 -0
  5. data/lib/punchblock/client.rb +2 -2
  6. data/lib/punchblock/command/accept.rb +2 -0
  7. data/lib/punchblock/command/answer.rb +2 -0
  8. data/lib/punchblock/command/dial.rb +2 -0
  9. data/lib/punchblock/command/hangup.rb +2 -0
  10. data/lib/punchblock/command/join.rb +2 -0
  11. data/lib/punchblock/command/mute.rb +2 -0
  12. data/lib/punchblock/command/redirect.rb +2 -0
  13. data/lib/punchblock/command/reject.rb +3 -1
  14. data/lib/punchblock/command/unjoin.rb +2 -0
  15. data/lib/punchblock/command/unmute.rb +2 -0
  16. data/lib/punchblock/command.rb +2 -0
  17. data/lib/punchblock/command_node.rb +2 -0
  18. data/lib/punchblock/component/asterisk/agi/command.rb +4 -2
  19. data/lib/punchblock/component/asterisk/agi.rb +2 -0
  20. data/lib/punchblock/component/asterisk/ami/action.rb +4 -2
  21. data/lib/punchblock/component/asterisk/ami.rb +2 -0
  22. data/lib/punchblock/component/asterisk.rb +2 -0
  23. data/lib/punchblock/component/component_node.rb +2 -0
  24. data/lib/punchblock/component/input.rb +2 -0
  25. data/lib/punchblock/component/output.rb +2 -0
  26. data/lib/punchblock/component/record.rb +2 -0
  27. data/lib/punchblock/component/stop.rb +2 -0
  28. data/lib/punchblock/component.rb +2 -0
  29. data/lib/punchblock/connection/asterisk.rb +4 -1
  30. data/lib/punchblock/connection/connected.rb +2 -0
  31. data/lib/punchblock/connection/generic_connection.rb +5 -0
  32. data/lib/punchblock/connection/xmpp.rb +5 -6
  33. data/lib/punchblock/connection.rb +2 -0
  34. data/lib/punchblock/core_ext/blather/stanza/presence.rb +2 -0
  35. data/lib/punchblock/core_ext/blather/stanza.rb +2 -0
  36. data/lib/punchblock/core_ext/ruby.rb +1 -12
  37. data/lib/punchblock/disconnected_error.rb +2 -0
  38. data/lib/punchblock/event/answered.rb +2 -0
  39. data/lib/punchblock/event/asterisk/ami/event.rb +3 -1
  40. data/lib/punchblock/event/asterisk/ami.rb +2 -0
  41. data/lib/punchblock/event/asterisk.rb +2 -0
  42. data/lib/punchblock/event/complete.rb +3 -1
  43. data/lib/punchblock/event/dtmf.rb +2 -0
  44. data/lib/punchblock/event/end.rb +2 -0
  45. data/lib/punchblock/event/joined.rb +2 -0
  46. data/lib/punchblock/event/offer.rb +6 -0
  47. data/lib/punchblock/event/ringing.rb +2 -0
  48. data/lib/punchblock/event/unjoined.rb +2 -0
  49. data/lib/punchblock/event.rb +2 -0
  50. data/lib/punchblock/has_headers.rb +3 -1
  51. data/lib/punchblock/header.rb +2 -0
  52. data/lib/punchblock/key_value_pair_node.rb +2 -0
  53. data/lib/punchblock/media_container.rb +2 -0
  54. data/lib/punchblock/media_node.rb +2 -0
  55. data/lib/punchblock/protocol_error.rb +2 -0
  56. data/lib/punchblock/rayo_node.rb +4 -3
  57. data/lib/punchblock/ref.rb +2 -0
  58. data/lib/punchblock/translator/asterisk/call.rb +80 -26
  59. data/lib/punchblock/translator/asterisk/component/asterisk/agi_command.rb +3 -1
  60. data/lib/punchblock/translator/asterisk/component/asterisk/ami_action.rb +3 -1
  61. data/lib/punchblock/translator/asterisk/component/asterisk.rb +2 -0
  62. data/lib/punchblock/translator/asterisk/component/input.rb +4 -2
  63. data/lib/punchblock/translator/asterisk/component/output.rb +21 -3
  64. data/lib/punchblock/translator/asterisk/component.rb +2 -0
  65. data/lib/punchblock/translator/asterisk.rb +50 -20
  66. data/lib/punchblock/translator.rb +2 -0
  67. data/lib/punchblock/version.rb +3 -1
  68. data/lib/punchblock.rb +2 -0
  69. data/punchblock.gemspec +2 -2
  70. data/spec/capture_warnings.rb +32 -0
  71. data/spec/punchblock/client/component_registry_spec.rb +2 -0
  72. data/spec/punchblock/client_spec.rb +3 -1
  73. data/spec/punchblock/command/accept_spec.rb +3 -1
  74. data/spec/punchblock/command/answer_spec.rb +3 -1
  75. data/spec/punchblock/command/dial_spec.rb +12 -10
  76. data/spec/punchblock/command/hangup_spec.rb +3 -1
  77. data/spec/punchblock/command/join_spec.rb +11 -9
  78. data/spec/punchblock/command/mute_spec.rb +3 -1
  79. data/spec/punchblock/command/redirect_spec.rb +5 -3
  80. data/spec/punchblock/command/reject_spec.rb +7 -5
  81. data/spec/punchblock/command/unjoin_spec.rb +7 -5
  82. data/spec/punchblock/command/unmute_spec.rb +3 -1
  83. data/spec/punchblock/command_node_spec.rb +9 -7
  84. data/spec/punchblock/component/asterisk/agi/command_spec.rb +21 -19
  85. data/spec/punchblock/component/asterisk/ami/action_spec.rb +19 -17
  86. data/spec/punchblock/component/component_node_spec.rb +5 -3
  87. data/spec/punchblock/component/input_spec.rb +51 -49
  88. data/spec/punchblock/component/output_spec.rb +60 -58
  89. data/spec/punchblock/component/record_spec.rb +36 -34
  90. data/spec/punchblock/connection/asterisk_spec.rb +9 -4
  91. data/spec/punchblock/connection/xmpp_spec.rb +40 -39
  92. data/spec/punchblock/event/answered_spec.rb +4 -2
  93. data/spec/punchblock/event/asterisk/ami/event_spec.rb +9 -7
  94. data/spec/punchblock/event/complete_spec.rb +12 -10
  95. data/spec/punchblock/event/dtmf_spec.rb +6 -4
  96. data/spec/punchblock/event/end_spec.rb +6 -4
  97. data/spec/punchblock/event/joined_spec.rb +8 -6
  98. data/spec/punchblock/event/offer_spec.rb +7 -5
  99. data/spec/punchblock/event/ringing_spec.rb +4 -2
  100. data/spec/punchblock/event/unjoined_spec.rb +8 -6
  101. data/spec/punchblock/header_spec.rb +13 -11
  102. data/spec/punchblock/protocol_error_spec.rb +8 -6
  103. data/spec/punchblock/ref_spec.rb +5 -3
  104. data/spec/punchblock/translator/asterisk/call_spec.rb +261 -14
  105. data/spec/punchblock/translator/asterisk/component/asterisk/agi_command_spec.rb +13 -11
  106. data/spec/punchblock/translator/asterisk/component/asterisk/ami_action_spec.rb +4 -2
  107. data/spec/punchblock/translator/asterisk/component/input_spec.rb +10 -8
  108. data/spec/punchblock/translator/asterisk/component/output_spec.rb +111 -20
  109. data/spec/punchblock/translator/asterisk/component_spec.rb +3 -1
  110. data/spec/punchblock/translator/asterisk_spec.rb +107 -10
  111. data/spec/spec_helper.rb +23 -17
  112. data/spec/support/mock_connection_with_event_handler.rb +2 -0
  113. metadata +43 -41
@@ -1,3 +1,5 @@
1
+ # encoding: utf-8
2
+
1
3
  require 'spec_helper'
2
4
 
3
5
  module Punchblock
@@ -40,6 +42,97 @@ module Punchblock
40
42
 
41
43
  before { mock_call.stubs :answer_if_not_answered }
42
44
 
45
+ context 'with a media engine of :swift' do
46
+ let(:media_engine) { :swift }
47
+
48
+ let(:audio_filename) { 'http://foo.com/bar.mp3' }
49
+
50
+ let :ssml_doc do
51
+ RubySpeech::SSML.draw do
52
+ audio :src => audio_filename
53
+ say_as(:interpret_as => :cardinal) { 'FOO' }
54
+ end
55
+ end
56
+
57
+ let(:command_opts) { {} }
58
+
59
+ let :command_options do
60
+ { :ssml => ssml_doc }.merge(command_opts)
61
+ end
62
+
63
+ def ssml_with_options(prefix = '', postfix = '')
64
+ base_doc = ssml_doc.to_s.squish.gsub(/["\\]/) { |m| "\\#{m}" }
65
+ prefix + base_doc + postfix
66
+ end
67
+
68
+ it "should execute Swift" do
69
+ mock_call.expects(:send_agi_action!).once.with 'EXEC Swift', ssml_with_options
70
+ subject.execute
71
+ end
72
+
73
+ it 'should send a complete event when Swift completes' do
74
+ def mock_call.send_agi_action!(*args, &block)
75
+ block.call Punchblock::Component::Asterisk::AGI::Command::Complete::Success.new(:code => 200, :result => 1)
76
+ end
77
+ subject.execute
78
+ command.complete_event(0.1).reason.should be_a Punchblock::Component::Output::Complete::Success
79
+ end
80
+
81
+ describe 'interrupt_on' do
82
+ context "set to nil" do
83
+ let(:command_opts) { { :interrupt_on => nil } }
84
+ it "should not add interrupt arguments" do
85
+ mock_call.expects(:send_agi_action!).once.with 'EXEC Swift', ssml_with_options
86
+ subject.execute
87
+ end
88
+ end
89
+
90
+ context "set to :any" do
91
+ let(:command_opts) { { :interrupt_on => :any } }
92
+ it "should add the interrupt options to the argument" do
93
+ mock_call.expects(:send_agi_action!).once.with 'EXEC Swift', ssml_with_options('', '|1|1')
94
+ subject.execute
95
+ end
96
+ end
97
+
98
+ context "set to :dtmf" do
99
+ let(:command_opts) { { :interrupt_on => :dtmf } }
100
+ it "should add the interrupt options to the argument" do
101
+ mock_call.expects(:send_agi_action!).once.with 'EXEC Swift', ssml_with_options('', '|1|1')
102
+ subject.execute
103
+ end
104
+ end
105
+
106
+ context "set to :speech" do
107
+ let(:command_opts) { { :interrupt_on => :speech } }
108
+ it "should return an error and not execute any actions" do
109
+ subject.execute
110
+ error = ProtocolError.new 'option error', 'An interrupt-on value of speech is unsupported.'
111
+ command.response(0.1).should be == error
112
+ end
113
+ end
114
+ end
115
+
116
+ describe 'voice' do
117
+ context "set to nil" do
118
+ let(:command_opts) { { :voice => nil } }
119
+ it "should not add a voice at the beginning of the argument" do
120
+ mock_call.expects(:send_agi_action!).once.with 'EXEC Swift', ssml_with_options
121
+ subject.execute
122
+ end
123
+ end
124
+
125
+ context "set to Leonard" do
126
+ let(:command_opts) { { :voice => "Leonard" } }
127
+ it "should add a voice at the beginning of the argument" do
128
+ mock_call.expects(:send_agi_action!).once.with 'EXEC Swift', ssml_with_options('Leonard^', '')
129
+ subject.execute
130
+ end
131
+ end
132
+
133
+ end
134
+ end
135
+
43
136
  context 'with a media engine of :unimrcp' do
44
137
  let(:media_engine) { :unimrcp }
45
138
 
@@ -60,7 +153,7 @@ module Punchblock
60
153
 
61
154
  def expect_mrcpsynth_with_options(options)
62
155
  mock_call.expects(:send_agi_action!).once.with do |*args|
63
- args[0].should == 'EXEC MRCPSynth'
156
+ args[0].should be == 'EXEC MRCPSynth'
64
157
  args[2].should match options
65
158
  end
66
159
  end
@@ -84,7 +177,7 @@ module Punchblock
84
177
  it "should return an error and not execute any actions" do
85
178
  subject.execute
86
179
  error = ProtocolError.new 'option error', 'An SSML document is required.'
87
- command.response(0.1).should == error
180
+ command.response(0.1).should be == error
88
181
  end
89
182
  end
90
183
  end
@@ -103,7 +196,7 @@ module Punchblock
103
196
  it "should return an error and not execute any actions" do
104
197
  subject.execute
105
198
  error = ProtocolError.new 'option error', 'A start_offset value is unsupported on Asterisk.'
106
- command.response(0.1).should == error
199
+ command.response(0.1).should be == error
107
200
  end
108
201
  end
109
202
  end
@@ -122,7 +215,7 @@ module Punchblock
122
215
  it "should return an error and not execute any actions" do
123
216
  subject.execute
124
217
  error = ProtocolError.new 'option error', 'A start_paused value is unsupported on Asterisk.'
125
- command.response(0.1).should == error
218
+ command.response(0.1).should be == error
126
219
  end
127
220
  end
128
221
  end
@@ -141,7 +234,7 @@ module Punchblock
141
234
  it "should return an error and not execute any actions" do
142
235
  subject.execute
143
236
  error = ProtocolError.new 'option error', 'A repeat_interval value is unsupported on Asterisk.'
144
- command.response(0.1).should == error
237
+ command.response(0.1).should be == error
145
238
  end
146
239
  end
147
240
  end
@@ -160,7 +253,7 @@ module Punchblock
160
253
  it "should return an error and not execute any actions" do
161
254
  subject.execute
162
255
  error = ProtocolError.new 'option error', 'A repeat_times value is unsupported on Asterisk.'
163
- command.response(0.1).should == error
256
+ command.response(0.1).should be == error
164
257
  end
165
258
  end
166
259
  end
@@ -179,7 +272,7 @@ module Punchblock
179
272
  it "should return an error and not execute any actions" do
180
273
  subject.execute
181
274
  error = ProtocolError.new 'option error', 'A max_time value is unsupported on Asterisk.'
182
- command.response(0.1).should == error
275
+ command.response(0.1).should be == error
183
276
  end
184
277
  end
185
278
  end
@@ -232,7 +325,7 @@ module Punchblock
232
325
  it "should return an error and not execute any actions" do
233
326
  subject.execute
234
327
  error = ProtocolError.new 'option error', 'An interrupt-on value of speech is unsupported.'
235
- command.response(0.1).should == error
328
+ command.response(0.1).should be == error
236
329
  end
237
330
  end
238
331
  end
@@ -243,7 +336,7 @@ module Punchblock
243
336
 
244
337
  def expect_stream_file_with_options(options = nil)
245
338
  mock_call.expects(:send_agi_action!).once.with 'STREAM FILE', audio_filename, options do |*args|
246
- args[2].should == options
339
+ args[2].should be == options
247
340
  subject.continue!
248
341
  true
249
342
  end
@@ -273,7 +366,7 @@ module Punchblock
273
366
  it "should return an error and not execute any actions" do
274
367
  subject.execute
275
368
  error = ProtocolError.new 'option error', 'An SSML document is required.'
276
- command.response(0.1).should == error
369
+ command.response(0.1).should be == error
277
370
  end
278
371
  end
279
372
 
@@ -315,12 +408,10 @@ module Punchblock
315
408
  latch = CountDownLatch.new 2
316
409
  mock_call.expects(:send_agi_action!).once.with 'STREAM FILE', audio_filename1, nil do
317
410
  subject.continue
318
- true
319
411
  latch.countdown!
320
412
  end
321
413
  mock_call.expects(:send_agi_action!).once.with 'STREAM FILE', audio_filename2, nil do
322
414
  subject.continue
323
- true
324
415
  latch.countdown!
325
416
  end
326
417
  subject.execute
@@ -351,7 +442,7 @@ module Punchblock
351
442
  it "should return an unrenderable document error" do
352
443
  subject.execute
353
444
  error = ProtocolError.new 'unrenderable document error', 'The provided document could not be rendered.'
354
- command.response(0.1).should == error
445
+ command.response(0.1).should be == error
355
446
  end
356
447
  end
357
448
  end
@@ -370,7 +461,7 @@ module Punchblock
370
461
  it "should return an error and not execute any actions" do
371
462
  subject.execute
372
463
  error = ProtocolError.new 'option error', 'A start_offset value is unsupported on Asterisk.'
373
- command.response(0.1).should == error
464
+ command.response(0.1).should be == error
374
465
  end
375
466
  end
376
467
  end
@@ -389,7 +480,7 @@ module Punchblock
389
480
  it "should return an error and not execute any actions" do
390
481
  subject.execute
391
482
  error = ProtocolError.new 'option error', 'A start_paused value is unsupported on Asterisk.'
392
- command.response(0.1).should == error
483
+ command.response(0.1).should be == error
393
484
  end
394
485
  end
395
486
  end
@@ -408,7 +499,7 @@ module Punchblock
408
499
  it "should return an error and not execute any actions" do
409
500
  subject.execute
410
501
  error = ProtocolError.new 'option error', 'A repeat_interval value is unsupported on Asterisk.'
411
- command.response(0.1).should == error
502
+ command.response(0.1).should be == error
412
503
  end
413
504
  end
414
505
  end
@@ -427,7 +518,7 @@ module Punchblock
427
518
  it "should return an error and not execute any actions" do
428
519
  subject.execute
429
520
  error = ProtocolError.new 'option error', 'A repeat_times value is unsupported on Asterisk.'
430
- command.response(0.1).should == error
521
+ command.response(0.1).should be == error
431
522
  end
432
523
  end
433
524
  end
@@ -446,7 +537,7 @@ module Punchblock
446
537
  it "should return an error and not execute any actions" do
447
538
  subject.execute
448
539
  error = ProtocolError.new 'option error', 'A max_time value is unsupported on Asterisk.'
449
- command.response(0.1).should == error
540
+ command.response(0.1).should be == error
450
541
  end
451
542
  end
452
543
  end
@@ -465,7 +556,7 @@ module Punchblock
465
556
  it "should return an error and not execute any actions" do
466
557
  subject.execute
467
558
  error = ProtocolError.new 'option error', 'A voice value is unsupported on Asterisk.'
468
- command.response(0.1).should == error
559
+ command.response(0.1).should be == error
469
560
  end
470
561
  end
471
562
  end
@@ -500,7 +591,7 @@ module Punchblock
500
591
  it "should return an error and not execute any actions" do
501
592
  subject.execute
502
593
  error = ProtocolError.new 'option error', 'An interrupt-on value of speech is unsupported.'
503
- command.response(0.1).should == error
594
+ command.response(0.1).should be == error
504
595
  end
505
596
  end
506
597
  end
@@ -1,3 +1,5 @@
1
+ # encoding: utf-8
2
+
1
3
  require 'spec_helper'
2
4
 
3
5
  module Punchblock
@@ -81,7 +83,7 @@ module Punchblock
81
83
 
82
84
  it 'sends an error in response to the command' do
83
85
  subject.execute_command component_command
84
- component_command.response.should == ProtocolError.new('command-not-acceptable', "Did not understand command for component #{subject.id}", call.id, subject.id)
86
+ component_command.response.should be == ProtocolError.new('command-not-acceptable', "Did not understand command for component #{subject.id}", call.id, subject.id)
85
87
  end
86
88
  end
87
89
  end
@@ -1,3 +1,5 @@
1
+ # encoding: utf-8
2
+
1
3
  require 'spec_helper'
2
4
 
3
5
  module Punchblock
@@ -18,12 +20,12 @@ module Punchblock
18
20
 
19
21
  context 'with a configured media engine of :asterisk' do
20
22
  let(:media_engine) { :asterisk }
21
- its(:media_engine) { should == :asterisk }
23
+ its(:media_engine) { should be == :asterisk }
22
24
  end
23
25
 
24
26
  context 'with a configured media engine of :unimrcp' do
25
27
  let(:media_engine) { :unimrcp }
26
- its(:media_engine) { should == :unimrcp }
28
+ its(:media_engine) { should be == :unimrcp }
27
29
  end
28
30
 
29
31
  describe '#shutdown' do
@@ -48,7 +50,7 @@ module Punchblock
48
50
  it 'executes the call command' do
49
51
  subject.wrapped_object.expects(:execute_call_command).with do |c|
50
52
  c.should be command
51
- c.call_id.should == call_id
53
+ c.call_id.should be == call_id
52
54
  end
53
55
  subject.execute_command command, :call_id => call_id
54
56
  end
@@ -61,7 +63,7 @@ module Punchblock
61
63
  it 'executes the component command' do
62
64
  subject.wrapped_object.expects(:execute_component_command).with do |c|
63
65
  c.should be command
64
- c.component_id.should == component_id
66
+ c.component_id.should be == component_id
65
67
  end
66
68
  subject.execute_command command, :component_id => component_id
67
69
  end
@@ -130,7 +132,7 @@ module Punchblock
130
132
  context "with an unknown call ID" do
131
133
  it 'sends an error in response to the command' do
132
134
  subject.execute_call_command command
133
- command.response.should == ProtocolError.new('call-not-found', "Could not find a call with ID #{call_id}", call_id, nil)
135
+ command.response.should be == ProtocolError.new('call-not-found', "Could not find a call with ID #{call_id}", call_id, nil)
134
136
  end
135
137
  end
136
138
  end
@@ -159,7 +161,7 @@ module Punchblock
159
161
  context "with an unknown component ID" do
160
162
  it 'sends an error in response to the command' do
161
163
  subject.execute_component_command command
162
- command.response.should == ProtocolError.new('component-not-found', "Could not find a component with ID #{component_id}", nil, component_id)
164
+ command.response.should be == ProtocolError.new('component-not-found', "Could not find a component with ID #{component_id}", nil, component_id)
163
165
  end
164
166
  end
165
167
  end
@@ -216,7 +218,7 @@ module Punchblock
216
218
 
217
219
  it 'sends an error in response to the command' do
218
220
  subject.execute_command command
219
- command.response.should == ProtocolError.new('command-not-acceptable', "Did not understand command")
221
+ command.response.should be == ProtocolError.new('command-not-acceptable', "Did not understand command")
220
222
  end
221
223
  end
222
224
  end
@@ -272,6 +274,7 @@ module Punchblock
272
274
  context 'twice' do
273
275
  it 'sends a connected event to the event handler' do
274
276
  subject.connection.expects(:handle_event).once.with Connection::Connected.new
277
+ subject.wrapped_object.expects(:run_at_fully_booted).once
275
278
  subject.handle_ami_event ami_event
276
279
  subject.handle_ami_event ami_event
277
280
  end
@@ -294,7 +297,28 @@ module Punchblock
294
297
  call_actor = subject.call_for_channel('SIP/1234-00000000')
295
298
  call_actor.wrapped_object.should be_a Asterisk::Call
296
299
  call_actor.agi_env.should be_a Hash
297
- call_actor.agi_env[:agi_request].should == 'async'
300
+ call_actor.agi_env.should be == {
301
+ :agi_request => 'async',
302
+ :agi_channel => 'SIP/1234-00000000',
303
+ :agi_language => 'en',
304
+ :agi_type => 'SIP',
305
+ :agi_uniqueid => '1320835995.0',
306
+ :agi_version => '1.8.4.1',
307
+ :agi_callerid => '5678',
308
+ :agi_calleridname => 'Jane Smith',
309
+ :agi_callingpres => '0',
310
+ :agi_callingani2 => '0',
311
+ :agi_callington => '0',
312
+ :agi_callingtns => '0',
313
+ :agi_dnid => '1000',
314
+ :agi_rdnis => 'unknown',
315
+ :agi_context => 'default',
316
+ :agi_extension => '1000',
317
+ :agi_priority => '1',
318
+ :agi_enhanced => '0.0',
319
+ :agi_accountcode => '',
320
+ :agi_threadid => '4366221312'
321
+ }
298
322
  end
299
323
 
300
324
  it 'should instruct the call to send an offer' do
@@ -316,6 +340,46 @@ module Punchblock
316
340
  subject.handle_ami_event ami_event
317
341
  end
318
342
  end
343
+
344
+ context "for a 'h' extension" do
345
+ let :ami_event do
346
+ RubyAMI::Event.new('AsyncAGI').tap do |e|
347
+ e['SubEvent'] = "Start"
348
+ e['Channel'] = "SIP/1234-00000000"
349
+ e['Env'] = "agi_extension%3A%20h%0A%0A"
350
+ end
351
+ end
352
+
353
+ it "should not create a new call" do
354
+ Asterisk::Call.expects(:new).never
355
+ subject.handle_ami_event ami_event
356
+ end
357
+
358
+ it 'should not be able to look up the call by channel ID' do
359
+ subject.handle_ami_event ami_event
360
+ subject.call_for_channel('SIP/1234-00000000').should be nil
361
+ end
362
+ end
363
+
364
+ context "for a 'Kill' type" do
365
+ let :ami_event do
366
+ RubyAMI::Event.new('AsyncAGI').tap do |e|
367
+ e['SubEvent'] = "Start"
368
+ e['Channel'] = "SIP/1234-00000000"
369
+ e['Env'] = "agi_type%3A%20Kill%0A%0A"
370
+ end
371
+ end
372
+
373
+ it "should not create a new call" do
374
+ Asterisk::Call.expects(:new).never
375
+ subject.handle_ami_event ami_event
376
+ end
377
+
378
+ it 'should not be able to look up the call by channel ID' do
379
+ subject.handle_ami_event ami_event
380
+ subject.call_for_channel('SIP/1234-00000000').should be nil
381
+ end
382
+ end
319
383
  end
320
384
 
321
385
  describe 'with a VarSet event including a punchblock_call_id' do
@@ -348,7 +412,7 @@ module Punchblock
348
412
 
349
413
  it "should set the correct channel on the call" do
350
414
  subject.handle_ami_event ami_event
351
- call.channel.should == 'SIP/1234-00000000'
415
+ call.channel.should be == 'SIP/1234-00000000'
352
416
  end
353
417
 
354
418
  it "should make it possible to look up the call by the full channel name" do
@@ -397,8 +461,34 @@ module Punchblock
397
461
  call.expects(:process_ami_event!).once.with ami_event
398
462
  subject.handle_ami_event ami_event
399
463
  end
464
+
465
+ context 'with a Channel1 and Channel2 specified on the event' do
466
+ let :ami_event do
467
+ RubyAMI::Event.new('BridgeAction').tap do |e|
468
+ e['Privilege'] = "call,all"
469
+ e['Response'] = "Success"
470
+ e['Channel1'] = "SIP/1234-00000000"
471
+ e['Channel2'] = "SIP/5678-00000000"
472
+ end
473
+ end
474
+
475
+ context 'with calls for those channels' do
476
+ let(:call2) do
477
+ Asterisk::Call.new "SIP/5678-00000000", subject, "agi_request%3A%20async%0Aagi_channel%3A%20SIP%2F1234-00000000%0Aagi_language%3A%20en%0Aagi_type%3A%20SIP%0Aagi_uniqueid%3A%201320835995.0%0Aagi_version%3A%201.8.4.1%0Aagi_callerid%3A%205678%0Aagi_calleridname%3A%20Jane%20Smith%0Aagi_callingpres%3A%200%0Aagi_callingani2%3A%200%0Aagi_callington%3A%200%0Aagi_callingtns%3A%200%0Aagi_dnid%3A%201000%0Aagi_rdnis%3A%20unknown%0Aagi_context%3A%20default%0Aagi_extension%3A%201000%0Aagi_priority%3A%201%0Aagi_enhanced%3A%200.0%0Aagi_accountcode%3A%20%0Aagi_threadid%3A%204366221312%0A%0A"
478
+ end
479
+
480
+ before { subject.register_call call2 }
481
+
482
+ it 'should send the event to both calls and to the connection once as a PB event' do
483
+ subject.wrapped_object.expects(:handle_pb_event).once
484
+ call.expects(:process_ami_event!).once.with ami_event
485
+ call2.expects(:process_ami_event!).once.with ami_event
486
+ subject.handle_ami_event ami_event
487
+ end
488
+ end
489
+ end
400
490
  end
401
- end
491
+ end#handle_ami_event
402
492
 
403
493
  describe '#send_ami_action' do
404
494
  it 'should send the action to the AMI client' do
@@ -406,6 +496,13 @@ module Punchblock
406
496
  subject.send_ami_action 'foo', :foo => :bar
407
497
  end
408
498
  end
499
+
500
+ describe '#run_at_fully_booted' do
501
+ it 'should send the redirect extension Command to the AMI client' do
502
+ ami_client.expects(:send_action).once.with 'Command', 'Command' => "dialplan add extension #{Asterisk::REDIRECT_EXTENSION},#{Asterisk::REDIRECT_PRIORITY},AGI,agi:async into #{Asterisk::REDIRECT_CONTEXT}"
503
+ subject.run_at_fully_booted
504
+ end
505
+ end
409
506
  end
410
507
  end
411
508
  end
data/spec/spec_helper.rb CHANGED
@@ -1,5 +1,5 @@
1
- $LOAD_PATH.unshift File.join(File.dirname(__FILE__), '..', 'lib')
2
- $LOAD_PATH.unshift File.dirname(__FILE__)
1
+ # encoding: utf-8
2
+
3
3
  require 'punchblock'
4
4
  require 'mocha'
5
5
  require 'countdownlatch'
@@ -7,6 +7,8 @@ require 'logger'
7
7
 
8
8
  Dir[File.dirname(__FILE__) + "/support/**/*.rb"].each {|f| require f}
9
9
 
10
+ Thread.abort_on_exception = true
11
+
10
12
  RSpec.configure do |config|
11
13
  config.mock_with :mocha
12
14
  config.filter_run :focus => true
@@ -15,6 +17,10 @@ RSpec.configure do |config|
15
17
  config.before :suite do |variable|
16
18
  Punchblock.logger = Logger.new(STDOUT)
17
19
  end
20
+
21
+ config.after :each do
22
+ Object.const_defined?(:Celluloid) && Celluloid.shutdown
23
+ end
18
24
  end
19
25
 
20
26
  def parse_stanza(xml)
@@ -27,8 +33,8 @@ end
27
33
 
28
34
  # FIXME: change this to rayo_event? It can be ambigous
29
35
  shared_examples_for 'event' do
30
- its(:call_id) { should == '9f00061' }
31
- its(:component_id) { should == '1' }
36
+ its(:call_id) { should be == '9f00061' }
37
+ its(:component_id) { should be == '1' }
32
38
  end
33
39
 
34
40
  shared_examples_for 'command_headers' do
@@ -44,47 +50,47 @@ shared_examples_for 'command_headers' do
44
50
  end
45
51
 
46
52
  shared_examples_for 'event_headers' do
47
- its(:headers) { should == [Punchblock::Header.new(:x_skill, 'agent'), Punchblock::Header.new(:x_customer_id, '8877')]}
48
- its(:headers_hash) { should == {:x_skill => 'agent', :x_customer_id => '8877'} }
53
+ its(:headers) { should be == [Punchblock::Header.new(:x_skill, 'agent'), Punchblock::Header.new(:x_customer_id, '8877')]}
54
+ its(:headers_hash) { should be == {:x_skill => 'agent', :x_customer_id => '8877'} }
49
55
  end
50
56
 
51
57
  shared_examples_for 'key_value_pairs' do
52
58
  it 'will auto-inherit nodes' do
53
59
  n = parse_stanza "<#{element_name} name='boo' value='bah' />"
54
60
  h = class_name.new n.root
55
- h.name.should == :boo
56
- h.value.should == 'bah'
61
+ h.name.should be == :boo
62
+ h.value.should be == 'bah'
57
63
  end
58
64
 
59
65
  it 'has a name attribute' do
60
66
  n = class_name.new :boo, 'bah'
61
- n.name.should == :boo
67
+ n.name.should be == :boo
62
68
  n.name = :foo
63
- n.name.should == :foo
69
+ n.name.should be == :foo
64
70
  end
65
71
 
66
72
  it "substitutes - for _ on the name attribute when reading" do
67
73
  n = parse_stanza "<#{element_name} name='boo-bah' value='foo' />"
68
74
  h = class_name.new n.root
69
- h.name.should == :boo_bah
75
+ h.name.should be == :boo_bah
70
76
  end
71
77
 
72
78
  it "substitutes _ for - on the name attribute when writing" do
73
79
  h = class_name.new :boo_bah, 'foo'
74
- h.to_xml.should == "<#{element_name} name=\"boo-bah\" value=\"foo\"/>"
80
+ h.to_xml.should be == "<#{element_name} name=\"boo-bah\" value=\"foo\"/>"
75
81
  end
76
82
 
77
83
  it 'has a value param' do
78
84
  n = class_name.new :boo, 'en'
79
- n.value.should == 'en'
85
+ n.value.should be == 'en'
80
86
  n.value = 'de'
81
- n.value.should == 'de'
87
+ n.value.should be == 'de'
82
88
  end
83
89
 
84
90
  it 'can determine equality' do
85
91
  a = class_name.new :boo, 'bah'
86
- a.should == class_name.new(:boo, 'bah')
87
- a.should_not == class_name.new(:bah, 'bah')
88
- a.should_not == class_name.new(:boo, 'boo')
92
+ a.should be == class_name.new(:boo, 'bah')
93
+ a.should_not be == class_name.new(:bah, 'bah')
94
+ a.should_not be == class_name.new(:boo, 'boo')
89
95
  end
90
96
  end
@@ -1,3 +1,5 @@
1
+ # encoding: utf-8
2
+
1
3
  # This is a nasty hack due to the fact that Mocha does not support expectations returning a value calculated by executing a block with the parameters passed.
2
4
  # If it did, we could do this in our component tests:
3
5
  # mc = mock 'Connection'