punchblock 0.9.2 → 0.10.0

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 (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'