punchblock 1.2.0 → 1.3.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 (38) hide show
  1. data/.travis.yml +3 -3
  2. data/CHANGELOG.md +23 -0
  3. data/lib/punchblock.rb +24 -0
  4. data/lib/punchblock/command/reject.rb +10 -2
  5. data/lib/punchblock/component/record.rb +16 -0
  6. data/lib/punchblock/core_ext/blather/stanza.rb +3 -1
  7. data/lib/punchblock/dead_actor_safety.rb +9 -0
  8. data/lib/punchblock/event/complete.rb +9 -11
  9. data/lib/punchblock/rayo_node.rb +4 -0
  10. data/lib/punchblock/translator/asterisk.rb +65 -22
  11. data/lib/punchblock/translator/asterisk/call.rb +49 -30
  12. data/lib/punchblock/translator/asterisk/component.rb +6 -8
  13. data/lib/punchblock/translator/asterisk/component/asterisk/agi_command.rb +13 -20
  14. data/lib/punchblock/translator/asterisk/component/asterisk/ami_action.rb +1 -1
  15. data/lib/punchblock/translator/asterisk/component/input.rb +3 -6
  16. data/lib/punchblock/translator/asterisk/component/output.rb +40 -45
  17. data/lib/punchblock/translator/asterisk/component/record.rb +1 -1
  18. data/lib/punchblock/translator/asterisk/component/stop_by_redirect.rb +5 -2
  19. data/lib/punchblock/version.rb +1 -1
  20. data/punchblock.gemspec +5 -5
  21. data/spec/punchblock/command/reject_spec.rb +7 -1
  22. data/spec/punchblock/command_node_spec.rb +5 -2
  23. data/spec/punchblock/component/component_node_spec.rb +4 -0
  24. data/spec/punchblock/component/output_spec.rb +1 -1
  25. data/spec/punchblock/component/record_spec.rb +30 -0
  26. data/spec/punchblock/event/complete_spec.rb +10 -0
  27. data/spec/punchblock/translator/asterisk/call_spec.rb +191 -48
  28. data/spec/punchblock/translator/asterisk/component/asterisk/agi_command_spec.rb +6 -39
  29. data/spec/punchblock/translator/asterisk/component/asterisk/ami_action_spec.rb +3 -3
  30. data/spec/punchblock/translator/asterisk/component/input_spec.rb +8 -3
  31. data/spec/punchblock/translator/asterisk/component/output_spec.rb +153 -46
  32. data/spec/punchblock/translator/asterisk/component/record_spec.rb +6 -5
  33. data/spec/punchblock/translator/asterisk/component/stop_by_redirect_spec.rb +1 -2
  34. data/spec/punchblock/translator/asterisk/component_spec.rb +1 -0
  35. data/spec/punchblock/translator/asterisk_spec.rb +147 -12
  36. data/spec/punchblock_spec.rb +34 -0
  37. data/spec/spec_helper.rb +5 -1
  38. metadata +30 -20
@@ -9,6 +9,16 @@ module Punchblock
9
9
  RayoNode.class_from_registration(:complete, 'urn:xmpp:rayo:ext:1').should be == Complete
10
10
  end
11
11
 
12
+ describe "setting a reason" do
13
+ let(:reason) { Punchblock::Component::Asterisk::AGI::Command::Complete::Success.new }
14
+
15
+ subject { described_class.new }
16
+
17
+ before { subject.reason = reason }
18
+
19
+ its(:reason) { should == reason }
20
+ end
21
+
12
22
  describe "comparing for equality" do
13
23
  subject do
14
24
  Complete.new.tap do |c|
@@ -88,7 +88,7 @@ module Punchblock
88
88
  :to => '1000',
89
89
  :from => 'Jane Smith <sip:5678>',
90
90
  :headers => sip_headers
91
- translator.expects(:handle_pb_event!).with expected_offer
91
+ translator.expects(:handle_pb_event).with expected_offer
92
92
  subject.send_offer
93
93
  end
94
94
 
@@ -100,20 +100,19 @@ module Punchblock
100
100
  end
101
101
  end
102
102
 
103
- describe '#answer_if_not_answered' do
104
- let(:answer_command) { Command::Answer.new.tap { |a| a.request! } }
103
+ describe '#send_progress' do
105
104
 
106
105
  context "with a call that is already answered" do
107
- it 'should not answer the call' do
106
+ it 'should not send the EXEC Progress command' do
108
107
  subject.wrapped_object.expects(:'answered?').returns true
109
- subject.wrapped_object.expects(:execute_command).never
110
- subject.answer_if_not_answered
108
+ subject.wrapped_object.expects(:send_agi_action).with("EXEC Progress").never
109
+ subject.send_progress
111
110
  end
112
111
  end
113
112
 
114
113
  context "with an unanswered call" do
115
114
  before do
116
- subject.wrapped_object.expects(:'answered?').returns false
115
+ subject.wrapped_object.expects(:'answered?').returns(false).at_least_once
117
116
  end
118
117
 
119
118
  context "with a call that is outbound" do
@@ -124,9 +123,9 @@ module Punchblock
124
123
  subject.dial dial_command
125
124
  end
126
125
 
127
- it 'should not answer the call' do
128
- subject.wrapped_object.expects(:execute_command).never
129
- subject.answer_if_not_answered
126
+ it 'should not send the EXEC Progress command' do
127
+ subject.wrapped_object.expects(:send_agi_action).with("EXEC Progress").never
128
+ subject.send_progress
130
129
  end
131
130
  end
132
131
 
@@ -135,9 +134,15 @@ module Punchblock
135
134
  subject.send_offer
136
135
  end
137
136
 
138
- it 'should answer a call that is inbound and not answered' do
139
- subject.wrapped_object.expects(:execute_command).with(answer_command)
140
- subject.answer_if_not_answered
137
+ it 'should send the EXEC Progress command to a call that is inbound and not answered' do
138
+ subject.wrapped_object.expects(:send_agi_action).with("EXEC Progress")
139
+ subject.send_progress
140
+ end
141
+
142
+ it 'should send the EXEC Progress command only once if called twice' do
143
+ subject.wrapped_object.expects(:send_agi_action).with("EXEC Progress").once
144
+ subject.send_progress
145
+ subject.send_progress
141
146
  end
142
147
  end
143
148
  end
@@ -229,14 +234,19 @@ module Punchblock
229
234
  let(:cause_txt) { 'Normal Clearing' }
230
235
 
231
236
  it "should cause the actor to be terminated" do
232
- translator.expects(:handle_pb_event!).once
237
+ translator.expects(:handle_pb_event).twice
233
238
  subject.process_ami_event ami_event
234
239
  sleep 5.5
235
240
  subject.should_not be_alive
236
241
  end
237
242
 
243
+ it "de-registers the call from the translator" do
244
+ translator.stubs :handle_pb_event
245
+ translator.expects(:deregister_call).once.with(subject)
246
+ subject.process_ami_event ami_event
247
+ end
248
+
238
249
  it "should cause all components to send complete events before sending end event" do
239
- subject.expects :answer_if_not_answered
240
250
  comp_command = Punchblock::Component::Input.new :grammar => {:value => '<grammar/>'}, :mode => :dtmf
241
251
  comp_command.request!
242
252
  component = subject.execute_command comp_command
@@ -245,8 +255,8 @@ module Punchblock
245
255
  expected_complete_event.reason = Punchblock::Event::Complete::Hangup.new
246
256
  expected_end_event = Punchblock::Event::End.new :reason => :hangup, :target_call_id => subject.id
247
257
  end_sequence = sequence 'end events'
248
- translator.expects(:handle_pb_event!).with(expected_complete_event).once.in_sequence(end_sequence)
249
- translator.expects(:handle_pb_event!).with(expected_end_event).once.in_sequence(end_sequence)
258
+ translator.expects(:handle_pb_event).with(expected_complete_event).once.in_sequence(end_sequence)
259
+ translator.expects(:handle_pb_event).with(expected_end_event).once.in_sequence(end_sequence)
250
260
  subject.process_ami_event ami_event
251
261
  end
252
262
 
@@ -257,7 +267,7 @@ module Punchblock
257
267
  it 'should send an end (hangup) event to the translator' do
258
268
  expected_end_event = Punchblock::Event::End.new :reason => :hangup,
259
269
  :target_call_id => subject.id
260
- translator.expects(:handle_pb_event!).with expected_end_event
270
+ translator.expects(:handle_pb_event).with expected_end_event
261
271
  subject.process_ami_event ami_event
262
272
  end
263
273
  end
@@ -269,7 +279,7 @@ module Punchblock
269
279
  it 'should send an end (hangup) event to the translator' do
270
280
  expected_end_event = Punchblock::Event::End.new :reason => :hangup,
271
281
  :target_call_id => subject.id
272
- translator.expects(:handle_pb_event!).with expected_end_event
282
+ translator.expects(:handle_pb_event).with expected_end_event
273
283
  subject.process_ami_event ami_event
274
284
  end
275
285
  end
@@ -281,7 +291,7 @@ module Punchblock
281
291
  it 'should send an end (busy) event to the translator' do
282
292
  expected_end_event = Punchblock::Event::End.new :reason => :busy,
283
293
  :target_call_id => subject.id
284
- translator.expects(:handle_pb_event!).with expected_end_event
294
+ translator.expects(:handle_pb_event).with expected_end_event
285
295
  subject.process_ami_event ami_event
286
296
  end
287
297
  end
@@ -297,7 +307,7 @@ module Punchblock
297
307
  it 'should send an end (timeout) event to the translator' do
298
308
  expected_end_event = Punchblock::Event::End.new :reason => :timeout,
299
309
  :target_call_id => subject.id
300
- translator.expects(:handle_pb_event!).with expected_end_event
310
+ translator.expects(:handle_pb_event).with expected_end_event
301
311
  subject.process_ami_event ami_event
302
312
  end
303
313
  end
@@ -315,7 +325,7 @@ module Punchblock
315
325
  it 'should send an end (reject) event to the translator' do
316
326
  expected_end_event = Punchblock::Event::End.new :reason => :reject,
317
327
  :target_call_id => subject.id
318
- translator.expects(:handle_pb_event!).with expected_end_event
328
+ translator.expects(:handle_pb_event).with expected_end_event
319
329
  subject.process_ami_event ami_event
320
330
  end
321
331
  end
@@ -367,7 +377,7 @@ module Punchblock
367
377
  it 'should send an end (error) event to the translator' do
368
378
  expected_end_event = Punchblock::Event::End.new :reason => :error,
369
379
  :target_call_id => subject.id
370
- translator.expects(:handle_pb_event!).with expected_end_event
380
+ translator.expects(:handle_pb_event).with expected_end_event
371
381
  subject.process_ami_event ami_event
372
382
  end
373
383
  end
@@ -395,7 +405,7 @@ module Punchblock
395
405
  end
396
406
 
397
407
  it 'should send the event to the component' do
398
- component.expects(:handle_ami_event!).once.with ami_event
408
+ component.expects(:handle_ami_event).once.with ami_event
399
409
  subject.process_ami_event ami_event
400
410
  end
401
411
  end
@@ -422,7 +432,7 @@ module Punchblock
422
432
  it 'should send a ringing event' do
423
433
  expected_ringing = Punchblock::Event::Ringing.new
424
434
  expected_ringing.target_call_id = subject.id
425
- translator.expects(:handle_pb_event!).with expected_ringing
435
+ translator.expects(:handle_pb_event).with expected_ringing
426
436
  subject.process_ami_event ami_event
427
437
  end
428
438
 
@@ -439,7 +449,7 @@ module Punchblock
439
449
  it 'should send a ringing event' do
440
450
  expected_answered = Punchblock::Event::Answered.new
441
451
  expected_answered.target_call_id = subject.id
442
- translator.expects(:handle_pb_event!).with expected_answered
452
+ translator.expects(:handle_pb_event).with expected_answered
443
453
  subject.process_ami_event ami_event
444
454
  end
445
455
 
@@ -450,6 +460,55 @@ module Punchblock
450
460
  end
451
461
  end
452
462
 
463
+ context 'with an OriginateResponse event' do
464
+ let :ami_event do
465
+ RubyAMI::Event.new('OriginateResponse').tap do |e|
466
+ e['Privilege'] = 'call,all'
467
+ e['ActionID'] = '9d0c1aa4-5e3b-4cae-8aef-76a6119e2909'
468
+ e['Response'] = response
469
+ e['Channel'] = 'SIP/15557654321'
470
+ e['Context'] = ''
471
+ e['Exten'] = ''
472
+ e['Reason'] = '0'
473
+ e['Uniqueid'] = uniqueid
474
+ e['CallerIDNum'] = 'sip:5551234567'
475
+ e['CallerIDName'] = 'Bryan 100'
476
+ end
477
+ end
478
+
479
+ context 'sucessful' do
480
+ let(:response) { 'Success' }
481
+ let(:uniqueid) { '<null>' }
482
+
483
+ it 'should not send an end event' do
484
+ translator.expects(:handle_pb_event).once.with is_a(Punchblock::Event::Asterisk::AMI::Event)
485
+ subject.process_ami_event ami_event
486
+ end
487
+ end
488
+
489
+ context 'failed after being connected' do
490
+ let(:response) { 'Failure' }
491
+ let(:uniqueid) { '1235' }
492
+
493
+ it 'should not send an end event' do
494
+ translator.expects(:handle_pb_event).once.with is_a(Punchblock::Event::Asterisk::AMI::Event)
495
+ subject.process_ami_event ami_event
496
+ end
497
+ end
498
+
499
+ context 'failed without ever having connected' do
500
+ let(:response) { 'Failure' }
501
+ let(:uniqueid) { '<null>' }
502
+
503
+ it 'should send an error end event' do
504
+ expected_end_event = Punchblock::Event::End.new :reason => :error,
505
+ :target_call_id => subject.id
506
+ translator.expects(:handle_pb_event).with expected_end_event
507
+ subject.process_ami_event ami_event
508
+ end
509
+ end
510
+ end
511
+
453
512
  context 'with a handler registered for a matching event' do
454
513
  let :ami_event do
455
514
  RubyAMI::Event.new('DTMF').tap do |e|
@@ -551,12 +610,12 @@ module Punchblock
551
610
  end
552
611
 
553
612
  it 'sends the Joined event when the call is the first channel' do
554
- translator.expects(:handle_pb_event!).with expected_joined
613
+ translator.expects(:handle_pb_event).with expected_joined
555
614
  subject.process_ami_event ami_event
556
615
  end
557
616
 
558
617
  it 'sends the Joined event when the call is the second channel' do
559
- translator.expects(:handle_pb_event!).with expected_joined
618
+ translator.expects(:handle_pb_event).with expected_joined
560
619
  subject.process_ami_event switched_ami_event
561
620
  end
562
621
  end
@@ -572,12 +631,12 @@ module Punchblock
572
631
  end
573
632
 
574
633
  it 'sends the Unjoined event when the call is the first channel' do
575
- translator.expects(:handle_pb_event!).with expected_unjoined
634
+ translator.expects(:handle_pb_event).with expected_unjoined
576
635
  subject.process_ami_event ami_event
577
636
  end
578
637
 
579
638
  it 'sends the Unjoined event when the call is the second channel' do
580
- translator.expects(:handle_pb_event!).with expected_unjoined
639
+ translator.expects(:handle_pb_event).with expected_unjoined
581
640
  subject.process_ami_event switched_ami_event
582
641
  end
583
642
  end
@@ -628,15 +687,43 @@ module Punchblock
628
687
  end
629
688
 
630
689
  it 'sends the Unjoined event when the call is the first channel' do
631
- translator.expects(:handle_pb_event!).with expected_unjoined
690
+ translator.expects(:handle_pb_event).with expected_unjoined
632
691
  subject.process_ami_event ami_event
633
692
  end
634
693
 
635
694
  it 'sends the Unjoined event when the call is the second channel' do
636
- translator.expects(:handle_pb_event!).with expected_unjoined
695
+ translator.expects(:handle_pb_event).with expected_unjoined
637
696
  subject.process_ami_event switched_ami_event
638
697
  end
639
698
  end
699
+
700
+ let :ami_event do
701
+ RubyAMI::Event.new('Foo').tap do |e|
702
+ e['Uniqueid'] = "1320842458.8"
703
+ e['Calleridnum'] = "5678"
704
+ e['Calleridname'] = "Jane Smith"
705
+ e['Cause'] = "0"
706
+ e['Cause-txt'] = "Unknown"
707
+ e['Channel'] = channel
708
+ end
709
+ end
710
+
711
+ let :expected_pb_event do
712
+ Event::Asterisk::AMI::Event.new :name => 'Foo',
713
+ :attributes => { :channel => channel,
714
+ :uniqueid => "1320842458.8",
715
+ :calleridnum => "5678",
716
+ :calleridname => "Jane Smith",
717
+ :cause => "0",
718
+ :'cause-txt' => "Unknown"},
719
+ :target_call_id => subject.id
720
+ end
721
+
722
+ it 'sends the AMI event to the connection as a PB event' do
723
+ translator.expects(:handle_pb_event).with expected_pb_event
724
+ subject.process_ami_event ami_event
725
+ end
726
+
640
727
  end
641
728
 
642
729
  describe '#execute_command' do
@@ -660,7 +747,6 @@ module Punchblock
660
747
  component.internal.should be_true
661
748
  agi_command = subject.wrapped_object.instance_variable_get(:'@current_agi_command')
662
749
  agi_command.name.should be == "EXEC RINGING"
663
- agi_command.execute!
664
750
  agi_command.add_event expected_agi_complete_event
665
751
  command.response(0.5).should be true
666
752
  end
@@ -675,7 +761,6 @@ module Punchblock
675
761
  component.internal.should be_true
676
762
  agi_command = subject.wrapped_object.instance_variable_get(:'@current_agi_command')
677
763
  agi_command.name.should be == "EXEC Busy"
678
- agi_command.execute!
679
764
  agi_command.add_event expected_agi_complete_event
680
765
  command.response(0.5).should be true
681
766
  end
@@ -686,7 +771,6 @@ module Punchblock
686
771
  component.internal.should be_true
687
772
  agi_command = subject.wrapped_object.instance_variable_get(:'@current_agi_command')
688
773
  agi_command.name.should be == "EXEC Busy"
689
- agi_command.execute!
690
774
  agi_command.add_event expected_agi_complete_event
691
775
  command.response(0.5).should be true
692
776
  end
@@ -697,7 +781,6 @@ module Punchblock
697
781
  component.internal.should be_true
698
782
  agi_command = subject.wrapped_object.instance_variable_get(:'@current_agi_command')
699
783
  agi_command.name.should be == "EXEC Congestion"
700
- agi_command.execute!
701
784
  agi_command.add_event expected_agi_complete_event
702
785
  command.response(0.5).should be true
703
786
  end
@@ -706,12 +789,11 @@ module Punchblock
706
789
  context 'with an answer command' do
707
790
  let(:command) { Command::Answer.new }
708
791
 
709
- it "should send an EXEC ANSWER AGI command and set the command's response" do
792
+ it "should send an ANSWER AGI command and set the command's response" do
710
793
  component = subject.execute_command command
711
794
  component.internal.should be_true
712
795
  agi_command = subject.wrapped_object.instance_variable_get(:'@current_agi_command')
713
- agi_command.name.should be == "EXEC ANSWER"
714
- agi_command.execute!
796
+ agi_command.name.should be == "ANSWER"
715
797
  agi_command.add_event expected_agi_complete_event
716
798
  command.response(0.5).should be true
717
799
  end
@@ -738,7 +820,7 @@ module Punchblock
738
820
 
739
821
  it 'should create an AGI command component actor and execute it asynchronously' do
740
822
  mock_action.expects(:internal=).never
741
- Component::Asterisk::AGICommand.expects(:new).once.with(command, subject).returns mock_action
823
+ Component::Asterisk::AGICommand.expects(:new_link).once.with(command, subject).returns mock_action
742
824
  mock_action.expects(:execute!).once
743
825
  subject.execute_command command
744
826
  end
@@ -752,7 +834,7 @@ module Punchblock
752
834
  let(:mock_action) { mock 'Component::Asterisk::Output', :id => 'foo' }
753
835
 
754
836
  it 'should create an Output component and execute it asynchronously' do
755
- Component::Output.expects(:new).once.with(command, subject).returns mock_action
837
+ Component::Output.expects(:new_link).once.with(command, subject).returns mock_action
756
838
  mock_action.expects(:internal=).never
757
839
  mock_action.expects(:execute!).once
758
840
  subject.execute_command command
@@ -767,7 +849,7 @@ module Punchblock
767
849
  let(:mock_action) { mock 'Component::Asterisk::Input', :id => 'foo' }
768
850
 
769
851
  it 'should create an Input component and execute it asynchronously' do
770
- Component::Input.expects(:new).once.with(command, subject).returns mock_action
852
+ Component::Input.expects(:new_link).once.with(command, subject).returns mock_action
771
853
  mock_action.expects(:internal=).never
772
854
  mock_action.expects(:execute!).once
773
855
  subject.execute_command command
@@ -782,7 +864,7 @@ module Punchblock
782
864
  let(:mock_action) { mock 'Component::Asterisk::Record', :id => 'foo' }
783
865
 
784
866
  it 'should create a Record component and execute it asynchronously' do
785
- Component::Record.expects(:new).once.with(command, subject).returns mock_action
867
+ Component::Record.expects(:new_link).once.with(command, subject).returns mock_action
786
868
  mock_action.expects(:internal=).never
787
869
  mock_action.expects(:execute!).once
788
870
  subject.execute_command command
@@ -804,15 +886,57 @@ module Punchblock
804
886
  before { subject.register_component mock_component }
805
887
 
806
888
  it 'should send the command to the component for execution' do
807
- mock_component.expects(:execute_command!).once
889
+ mock_component.expects(:execute_command).once
808
890
  subject.execute_command command
809
891
  end
810
892
  end
811
893
 
894
+ context "for a component which began executing but crashed" do
895
+ let :component_command do
896
+ Punchblock::Component::Asterisk::AGI::Command.new :name => 'Wait'
897
+ end
898
+
899
+ let(:comp_id) { component_command.response.id }
900
+
901
+ let(:subsequent_command) { Punchblock::Component::Stop.new :component_id => comp_id }
902
+
903
+ let :expected_event do
904
+ Punchblock::Event::Complete.new.tap do |e|
905
+ e.target_call_id = subject.id
906
+ e.component_id = comp_id
907
+ e.reason = Punchblock::Event::Complete::Error.new
908
+ end
909
+ end
910
+
911
+ before do
912
+ component_command.request!
913
+ subject.execute_command component_command
914
+ end
915
+
916
+ it 'sends an error in response to the command' do
917
+ component = subject.component_with_id comp_id
918
+
919
+ component.wrapped_object.define_singleton_method(:oops) do
920
+ raise 'Woops, I died'
921
+ end
922
+
923
+ translator.expects(:handle_pb_event).once.with expected_event
924
+
925
+ lambda { component.oops }.should raise_error(/Woops, I died/)
926
+ sleep 0.1
927
+ component.should_not be_alive
928
+ subject.component_with_id(comp_id).should be_nil
929
+
930
+ subsequent_command.request!
931
+ subject.execute_command subsequent_command
932
+ subsequent_command.response.should be == ProtocolError.new.setup(:item_not_found, "Could not find a component with ID #{comp_id} for call #{subject.id}", subject.id, comp_id)
933
+ end
934
+ end
935
+
812
936
  context "for an unknown component ID" do
813
937
  it 'sends an error in response to the command' do
814
938
  subject.execute_command command
815
- command.response.should be == ProtocolError.new.setup('component-not-found', "Could not find a component with ID #{component_id} for call #{subject.id}", subject.id, component_id)
939
+ command.response.should be == ProtocolError.new.setup(:item_not_found, "Could not find a component with ID #{component_id} for call #{subject.id}", subject.id, component_id)
816
940
  end
817
941
  end
818
942
  end
@@ -877,6 +1001,9 @@ module Punchblock
877
1001
  ami_action.headers['Exten'].should be == Punchblock::Translator::Asterisk::REDIRECT_EXTENSION
878
1002
  ami_action.headers['Priority'].should be == Punchblock::Translator::Asterisk::REDIRECT_PRIORITY
879
1003
  ami_action.headers['Context'].should be == Punchblock::Translator::Asterisk::REDIRECT_CONTEXT
1004
+
1005
+ ami_action << RubyAMI::Response.new
1006
+ command.response(1).should be_true
880
1007
  end
881
1008
 
882
1009
  it "executes the unjoin through redirection, on the subject call and the other call" do
@@ -894,6 +1021,22 @@ module Punchblock
894
1021
  ami_action.headers['ExtraPriority'].should be == Punchblock::Translator::Asterisk::REDIRECT_PRIORITY
895
1022
  ami_action.headers['ExtraContext'].should be == Punchblock::Translator::Asterisk::REDIRECT_CONTEXT
896
1023
  end
1024
+
1025
+ it "handles redirect errors" do
1026
+ translator.expects(:call_with_id).with(other_call_id).returns(nil)
1027
+ subject.execute_command command
1028
+ ami_action = subject.wrapped_object.instance_variable_get(:'@current_ami_action')
1029
+ ami_action.name.should be == "redirect"
1030
+ ami_action.headers['Channel'].should be == channel
1031
+ ami_action.headers['Exten'].should be == Punchblock::Translator::Asterisk::REDIRECT_EXTENSION
1032
+ ami_action.headers['Priority'].should be == Punchblock::Translator::Asterisk::REDIRECT_PRIORITY
1033
+ ami_action.headers['Context'].should be == Punchblock::Translator::Asterisk::REDIRECT_CONTEXT
1034
+
1035
+ ami_action << RubyAMI::Error.new.tap { |e| e.message = 'FooBar' }
1036
+ response = command.response(1)
1037
+ response.should be_a ProtocolError
1038
+ response.text.should == 'FooBar'
1039
+ end
897
1040
  end
898
1041
  end#execute_command
899
1042
 
@@ -906,12 +1049,12 @@ module Punchblock
906
1049
  end
907
1050
 
908
1051
  describe '#send_ami_action' do
909
- let(:component_id) { UUIDTools::UUID.random_create }
910
- before { UUIDTools::UUID.stubs :random_create => component_id }
1052
+ let(:component_id) { Punchblock.new_uuid }
1053
+ before { stub_uuids component_id }
911
1054
 
912
1055
  it 'should send the action to the AMI client' do
913
1056
  action = RubyAMI::Action.new 'foo', :foo => :bar
914
- translator.expects(:send_ami_action!).once.with action
1057
+ translator.expects(:send_ami_action).once.with action
915
1058
  subject.send_ami_action 'foo', :foo => :bar
916
1059
  end
917
1060
  end