adhearsion 2.1.3 → 2.2.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 (61) hide show
  1. data/CHANGELOG.md +87 -0
  2. data/Guardfile +1 -1
  3. data/Rakefile +1 -2
  4. data/adhearsion.gemspec +1 -1
  5. data/features/cli_create.feature +1 -0
  6. data/features/step_definitions/cli_steps.rb +0 -10
  7. data/lib/adhearsion.rb +11 -0
  8. data/lib/adhearsion/call.rb +27 -12
  9. data/lib/adhearsion/call_controller.rb +1 -1
  10. data/lib/adhearsion/call_controller/dial.rb +3 -1
  11. data/lib/adhearsion/call_controller/input.rb +4 -4
  12. data/lib/adhearsion/call_controller/menu_dsl.rb +1 -0
  13. data/lib/adhearsion/call_controller/menu_dsl/array_match_calculator.rb +26 -0
  14. data/lib/adhearsion/call_controller/menu_dsl/fixnum_match_calculator.rb +2 -14
  15. data/lib/adhearsion/call_controller/menu_dsl/string_match_calculator.rb +6 -6
  16. data/lib/adhearsion/call_controller/output.rb +20 -14
  17. data/lib/adhearsion/call_controller/output/abstract_player.rb +5 -5
  18. data/lib/adhearsion/call_controller/output/formatter.rb +68 -68
  19. data/lib/adhearsion/call_controller/record.rb +91 -28
  20. data/lib/adhearsion/call_controller/utility.rb +12 -5
  21. data/lib/adhearsion/calls.rb +1 -1
  22. data/lib/adhearsion/configuration.rb +4 -0
  23. data/lib/adhearsion/events.rb +2 -1
  24. data/lib/adhearsion/generators/app/app_generator.rb +2 -2
  25. data/lib/adhearsion/generators/app/templates/Gemfile.erb +4 -0
  26. data/lib/adhearsion/generators/app/templates/Rakefile +5 -0
  27. data/lib/adhearsion/generators/app/templates/lib/simon_game.rb +3 -0
  28. data/lib/adhearsion/generators/app/templates/rspec +2 -0
  29. data/lib/adhearsion/generators/app/templates/spec/call_controllers/simon_game_spec.rb +142 -0
  30. data/lib/adhearsion/generators/controller/templates/lib/controller.rb +1 -1
  31. data/lib/adhearsion/generators/controller/templates/spec/controller_spec.rb +2 -0
  32. data/lib/adhearsion/initializer.rb +3 -2
  33. data/lib/adhearsion/outbound_call.rb +5 -2
  34. data/lib/adhearsion/router/route.rb +13 -2
  35. data/lib/adhearsion/rspec.rb +1 -1
  36. data/lib/adhearsion/statistics.rb +138 -0
  37. data/lib/adhearsion/tasks/environment.rb +1 -1
  38. data/lib/adhearsion/version.rb +1 -1
  39. data/spec/adhearsion/call_controller/dial_spec.rb +26 -0
  40. data/spec/adhearsion/call_controller/input_spec.rb +13 -1
  41. data/spec/adhearsion/call_controller/menu_dsl/array_match_calculator_spec.rb +76 -0
  42. data/spec/adhearsion/call_controller/menu_dsl/fixnum_match_calculator_spec.rb +8 -6
  43. data/spec/adhearsion/call_controller/menu_dsl/range_match_calculator_spec.rb +4 -4
  44. data/spec/adhearsion/call_controller/menu_dsl/string_match_calculator_spec.rb +6 -6
  45. data/spec/adhearsion/call_controller/output/formatter_spec.rb +3 -5
  46. data/spec/adhearsion/call_controller/output_spec.rb +59 -25
  47. data/spec/adhearsion/call_controller/record_spec.rb +123 -2
  48. data/spec/adhearsion/call_controller/utility_spec.rb +31 -11
  49. data/spec/adhearsion/call_spec.rb +77 -36
  50. data/spec/adhearsion/calls_spec.rb +13 -0
  51. data/spec/adhearsion/initializer_spec.rb +7 -0
  52. data/spec/adhearsion/outbound_call_spec.rb +14 -0
  53. data/spec/adhearsion/punchblock_plugin_spec.rb +5 -2
  54. data/spec/adhearsion/router/openended_route_spec.rb +2 -1
  55. data/spec/adhearsion/router/route_spec.rb +9 -1
  56. data/spec/adhearsion/router/unaccepting_route_spec.rb +2 -1
  57. data/spec/adhearsion/statistics/dump_spec.rb +38 -0
  58. data/spec/adhearsion/statistics_spec.rb +61 -0
  59. data/spec/adhearsion_spec.rb +21 -1
  60. data/spec/spec_helper.rb +2 -0
  61. metadata +16 -7
@@ -10,15 +10,15 @@ module Adhearsion
10
10
  it "matching with a Range should handle the case of two potential matches in the range" do
11
11
  digits_that_begin_with_eleven = [110..119, 1100..1111].map { |x| Array(x) }.flatten
12
12
  calculator = RangeMatchCalculator.new 11..1111, :match_payload_doesnt_matter
13
- match = calculator.match 11
14
- match.exact_matches.should be == [11]
13
+ match = calculator.match '11'
14
+ match.exact_matches.should be == ['11']
15
15
  match.potential_matches.should be == digits_that_begin_with_eleven
16
16
  end
17
17
 
18
18
  it "return values of #match should be an instance of CalculatedMatch" do
19
19
  calculator = RangeMatchCalculator.new 1..9, :match_payload_doesnt_matter
20
- calculator.match(0).should be_an_instance_of CalculatedMatch
21
- calculator.match(1000).should be_an_instance_of CalculatedMatch
20
+ calculator.match('0').should be_an_instance_of CalculatedMatch
21
+ calculator.match('1000').should be_an_instance_of CalculatedMatch
22
22
  end
23
23
 
24
24
  it "returns a failed match if the query is not numeric or coercible to numeric" do
@@ -14,13 +14,13 @@ module Adhearsion
14
14
  calculator = StringMatchCalculator.new str, match_payload
15
15
 
16
16
  match_case = calculator.match str[0,2]
17
- match_case.exact_match?.should_not be true
18
- match_case.potential_match?.should be true
17
+ match_case.should_not be_exact_match
18
+ match_case.should be_potential_match
19
19
  match_case.potential_matches.should be == [str]
20
20
 
21
21
  match_case = calculator.match str
22
- match_case.exact_match?.should be true
23
- match_case.potential_match?.should_not be true
22
+ match_case.should be_exact_match
23
+ match_case.should_not be_potential_match
24
24
  match_case.exact_matches.should be == [str]
25
25
  end
26
26
  end
@@ -29,9 +29,9 @@ module Adhearsion
29
29
  %w[* #].each do |special_digit|
30
30
  calculator = StringMatchCalculator.new(special_digit, match_payload)
31
31
  match_case = calculator.match special_digit
32
- match_case.potential_match?.should_not be true
33
- match_case.exact_match?.should be true
34
32
  match_case.exact_matches.first.should be == special_digit
33
+ match_case.should be_exact_match
34
+ match_case.should_not be_potential_match
35
35
  end
36
36
  end
37
37
  end
@@ -6,9 +6,7 @@ module Adhearsion
6
6
  class CallController
7
7
  module Output
8
8
  describe Formatter do
9
- subject { Formatter }
10
-
11
- describe ".ssml_for" do
9
+ describe "#ssml_for" do
12
10
  let(:prompt) { "Please stand by" }
13
11
 
14
12
  let(:ssml) do
@@ -24,7 +22,7 @@ module Adhearsion
24
22
  end
25
23
  end
26
24
 
27
- describe ".ssml_for_collection" do
25
+ describe "#ssml_for_collection" do
28
26
  let(:collection) { ["/foo/bar.wav", 1, Time.now] }
29
27
  let :ssml do
30
28
  file = collection[0]
@@ -42,7 +40,7 @@ module Adhearsion
42
40
  end
43
41
  end
44
42
 
45
- describe ".detect_type" do
43
+ describe "#detect_type" do
46
44
  it "detects an HTTP path" do
47
45
  http_path = "http://adhearsion.com/sounds/hello.mp3"
48
46
  subject.detect_type(http_path).should be :audio
@@ -7,12 +7,12 @@ module Adhearsion
7
7
  describe Output do
8
8
  include CallControllerTestHelpers
9
9
 
10
- def expect_ssml_output(ssml)
11
- expect_component_execution Punchblock::Component::Output.new(:ssml => ssml)
10
+ def expect_ssml_output(ssml, options = {})
11
+ expect_component_execution Punchblock::Component::Output.new(options.merge(:ssml => ssml))
12
12
  end
13
13
 
14
- def expect_async_ssml_output(ssml)
15
- expect_message_waiting_for_response Punchblock::Component::Output.new(:ssml => ssml)
14
+ def expect_async_ssml_output(ssml, options = {})
15
+ expect_message_waiting_for_response Punchblock::Component::Output.new(options.merge(:ssml => ssml))
16
16
  end
17
17
 
18
18
  describe "#player" do
@@ -350,6 +350,25 @@ module Adhearsion
350
350
  end
351
351
  end
352
352
 
353
+ describe "with a collection of arguments" do
354
+ let(:args) { ["/foo/bar.wav", 1, Time.now] }
355
+ let :ssml do
356
+ file = args[0]
357
+ n = args[1].to_s
358
+ t = args[2].to_s
359
+ RubySpeech::SSML.draw do
360
+ audio :src => file
361
+ say_as(:interpret_as => 'cardinal') { n }
362
+ say_as(:interpret_as => 'time') { t }
363
+ end
364
+ end
365
+
366
+ it 'plays all arguments in one document' do
367
+ expect_ssml_output ssml
368
+ subject.play(args).should be true
369
+ end
370
+ end
371
+
353
372
  describe "with a number" do
354
373
  let(:argument) { 123 }
355
374
 
@@ -413,7 +432,7 @@ module Adhearsion
413
432
  end
414
433
  end
415
434
 
416
- describe "with an array containing a Date/DateTime/Time object and a hash" do
435
+ describe "with an hash containing a Date/DateTime/Time object and format options" do
417
436
  let(:date) { Date.parse '2011-01-23' }
418
437
  let(:format) { "d-m-y" }
419
438
  let(:strftime) { "%d-%m%Y" }
@@ -619,42 +638,31 @@ module Adhearsion
619
638
  :grammar => { :value => grammar.to_s }
620
639
  }
621
640
 
641
+ def expect_component_complete_event
642
+ complete_event = Punchblock::Event::Complete.new
643
+ flexmock(complete_event).should_receive(:reason => flexmock(:utterance => 'dtmf-5'))
644
+ flexmock(Punchblock::Component::Input).new_instances do |input|
645
+ input.should_receive(:complete?).and_return(false)
646
+ input.should_receive(:complete_event).and_return(complete_event)
647
+ end
648
+ end
649
+
622
650
  #test does pass and method works, but not sure if the empty method is a good idea
623
651
  it "plays the correct output" do
624
652
  def controller.write_and_await_response(input_component)
625
653
  # it is actually a no-op here
626
654
  end
627
655
 
628
- def expect_component_complete_event
629
- complete_event = Punchblock::Event::Complete.new
630
- flexmock(complete_event).should_receive(:reason => flexmock(:interpretation => 'dtmf-5', :name => :input))
631
- flexmock(Punchblock::Component::Input).new_instances do |input|
632
- input.should_receive(:complete?).and_return(false)
633
- input.should_receive(:complete_event).and_return(complete_event)
634
- end
635
- end
636
-
637
656
  expect_component_complete_event
638
657
  expect_component_execution Punchblock::Component::Output.new(:ssml => ssml.to_s)
639
658
  subject.stream_file prompt, allowed_digits
640
659
  end
641
660
 
642
661
  it "returns a single digit amongst the allowed when pressed" do
643
- flexmock(Punchblock::Event::Complete).new_instances.should_receive(:reason => flexmock(:interpretation => 'dtmf-5', :name => :input))
644
-
645
662
  def controller.write_and_await_response(input_component)
646
663
  input_component.trigger_event_handler Punchblock::Event::Complete.new
647
664
  end
648
665
 
649
- def expect_component_complete_event
650
- complete_event = Punchblock::Event::Complete.new
651
- flexmock(complete_event).should_receive(:reason => flexmock(:interpretation => 'dtmf-5', :name => :input))
652
- flexmock(Punchblock::Component::Input).new_instances do |input|
653
- input.should_receive(:complete?).and_return(false)
654
- input.should_receive(:complete_event).and_return(complete_event)
655
- end
656
- end
657
-
658
666
  expect_component_complete_event
659
667
  flexmock(Punchblock::Component::Output).new_instances.should_receive(:stop!)
660
668
  expect_component_execution output_component
@@ -680,6 +688,19 @@ module Adhearsion
680
688
  end
681
689
  end
682
690
 
691
+ describe "with a default voice set" do
692
+ before { Adhearsion.config.punchblock.default_voice = 'foo' }
693
+
694
+ it 'sets the voice on the output component' do
695
+ str = "Hello world"
696
+ ssml = RubySpeech::SSML.draw { string str }
697
+ expect_ssml_output ssml, voice: 'foo'
698
+ subject.say(str)
699
+ end
700
+
701
+ after { Adhearsion.config.punchblock.default_voice = nil }
702
+ end
703
+
683
704
  describe "converts the argument to a string" do
684
705
  it 'calls output with a string' do
685
706
  argument = 123
@@ -714,6 +735,19 @@ module Adhearsion
714
735
  end
715
736
  end
716
737
 
738
+ describe "with a default voice set" do
739
+ before { Adhearsion.config.punchblock.default_voice = 'foo' }
740
+
741
+ it 'sets the voice on the output component' do
742
+ str = "Hello world"
743
+ ssml = RubySpeech::SSML.draw { string str }
744
+ expect_async_ssml_output ssml, voice: 'foo'
745
+ subject.say!(str)
746
+ end
747
+
748
+ after { Adhearsion.config.punchblock.default_voice = nil }
749
+ end
750
+
717
751
  describe "converts the argument to a string" do
718
752
  it 'calls output with a string' do
719
753
  argument = 123
@@ -7,8 +7,129 @@ module Adhearsion
7
7
  describe Record do
8
8
  include CallControllerTestHelpers
9
9
 
10
+ describe Recorder do
11
+ let(:interruptible) { false }
12
+ let(:async) { false }
13
+ let(:component_options) { { :start_beep => true } }
14
+ let :options do
15
+ component_options.merge :interruptible => interruptible,
16
+ :async => async
17
+ end
18
+
19
+ let(:component) { Punchblock::Component::Record.new component_options }
20
+ let :stopper_grammar do
21
+ RubySpeech::GRXML.draw :mode => 'dtmf', :root => 'inputdigits' do
22
+ rule id: 'inputdigits', scope: 'public' do
23
+ one_of do
24
+ item { '0' }
25
+ item { '1' }
26
+ item { '2' }
27
+ item { '3' }
28
+ item { '4' }
29
+ item { '5' }
30
+ item { '6' }
31
+ item { '7' }
32
+ item { '8' }
33
+ item { '9' }
34
+ item { '#' }
35
+ item { '*' }
36
+ end
37
+ end
38
+ end
39
+ end
40
+ let(:input_component) { Punchblock::Component::Input.new mode: :dtmf, grammar: { :value => stopper_grammar } }
41
+
42
+ subject { Recorder.new controller, options }
43
+
44
+ its(:record_component) { should == component }
45
+
46
+ context "when passing time related options" do
47
+ let :component_options do
48
+ { :max_duration => 5.5, :initial_timeout => 6.5, :final_timeout => 3.2 }
49
+ end
50
+
51
+ let :component do
52
+ Punchblock::Component::Record.new :max_duration => 5500,
53
+ :initial_timeout => 6500,
54
+ :final_timeout => 3200
55
+ end
56
+
57
+ it "takes seconds but sets milliseconds on the command" do
58
+ subject.record_component.should == component
59
+ end
60
+ end
61
+
62
+ describe "#run" do
63
+ context "with :async => false" do
64
+ it "executes the component synchronously" do
65
+ expect_component_execution subject.record_component
66
+ subject.run
67
+ end
68
+ end
69
+
70
+ context "with :async => true" do
71
+ let(:async) { true }
72
+
73
+ it "executes the component asynchronously" do
74
+ expect_message_waiting_for_response subject.record_component
75
+ subject.run
76
+ end
77
+ end
78
+
79
+ context "with :interruptible => false" do
80
+ its(:stopper_component) { should be_nil }
81
+
82
+ it "does not use an Input component" do
83
+ controller.should_receive(:execute_component_and_await_completion).once.with(component)
84
+ controller.should_receive(:write_and_await_response).never.with(input_component)
85
+ subject.run
86
+ end
87
+ end
88
+
89
+ context "with :interruptible => true" do
90
+ let(:interruptible) { true }
91
+
92
+ its(:stopper_component) { should == input_component }
93
+
94
+ describe "when the input component completes" do
95
+ let(:complete_event) { Punchblock::Event::Complete.new }
96
+
97
+ before do
98
+ subject.stopper_component.request!
99
+ subject.stopper_component.execute!
100
+ end
101
+
102
+ it "stops the recording" do
103
+ flexmock(subject.record_component).should_receive(:stop!).once
104
+ subject.stopper_component.trigger_event_handler complete_event
105
+ end
106
+ end
107
+
108
+ describe "when the recording completes" do
109
+ it "stops the input component" do
110
+ controller.should_receive(:execute_component_and_await_completion).once.with(component)
111
+ controller.should_receive(:write_and_await_response).once.with(input_component)
112
+ flexmock(subject.stopper_component).should_receive(:stop!).once
113
+ subject.run
114
+ end
115
+ end
116
+ end
117
+ end
118
+
119
+ describe "setting completion handlers" do
120
+ let(:complete_event) { Punchblock::Event::Complete.new }
121
+
122
+ it "should execute those handlers when recording completes" do
123
+ foo = flexmock 'foo'
124
+ foo.should_receive(:call).once.with Punchblock::Event::Complete
125
+ subject.handle_record_completion { |e| foo.call e }
126
+ subject.record_component.trigger_event_handler complete_event
127
+ end
128
+ end
129
+ end
130
+
10
131
  describe "#record" do
11
- let(:max_duration) { 5 }
132
+ let(:max_duration) { 5.5 }
12
133
  let(:options) do
13
134
  {
14
135
  :start_beep => true,
@@ -104,7 +225,7 @@ module Adhearsion
104
225
  complete_event = Punchblock::Event::Complete.new
105
226
  flexmock(complete_event).should_receive(:reason => flexmock(:name => :input))
106
227
  flexmock(Punchblock::Component::Input).new_instances do |input|
107
- input.should_receive(:complete?).and_return(false)
228
+ input.should_receive(:complete?).and_return(true)
108
229
  input.should_receive(:complete_event).and_return(complete_event)
109
230
  end
110
231
  flexmock(Punchblock::Component::Record).new_instances.should_receive(:stop!)
@@ -47,21 +47,41 @@ module Adhearsion
47
47
  end
48
48
  end # grammar_accept
49
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
50
+ describe "#parse_dtmf" do
51
+ context "with a single digit" do
52
+ it "correctly returns the parsed input" do
53
+ subject.parse_dtmf("dtmf-3").should be == '3'
54
+ end
54
55
 
55
- it "correctly returns star as *" do
56
- subject.parse_single_dtmf("dtmf-star").should be == '*'
57
- end
56
+ it "correctly returns star as *" do
57
+ subject.parse_dtmf("dtmf-star").should be == '*'
58
+ end
59
+
60
+ it "correctly returns * as *" do
61
+ subject.parse_dtmf("*").should be == '*'
62
+ end
63
+
64
+ it "correctly returns pound as #" do
65
+ subject.parse_dtmf("dtmf-pound").should be == '#'
66
+ end
67
+
68
+ it "correctly returns # as #" do
69
+ subject.parse_dtmf("#").should be == '#'
70
+ end
58
71
 
59
- it "correctly returns pound as #" do
60
- subject.parse_single_dtmf("dtmf-pound").should be == '#'
72
+ it "correctly parses digits without the dtmf- prefix" do
73
+ subject.parse_dtmf('1').should be == '1'
74
+ end
75
+
76
+ it "correctly returns nil when input is nil" do
77
+ subject.parse_dtmf(nil).should be == nil
78
+ end
61
79
  end
62
80
 
63
- it "correctly returns nil when input is nil" do
64
- subject.parse_single_dtmf(nil).should be == nil
81
+ context "with multiple digits separated by spaces" do
82
+ it "returns the digits without space separation" do
83
+ subject.parse_dtmf('1 dtmf-5 dtmf-star # 2').should be == '15*#2'
84
+ end
65
85
  end
66
86
  end # describe #grammar_accept
67
87
  end
@@ -43,47 +43,45 @@ module Adhearsion
43
43
  its(:id) { should be == call_id }
44
44
  its(:to) { should be == to }
45
45
  its(:from) { should be == from }
46
- its(:offer) { should be offer }
47
- its(:client) { should be mock_client }
48
-
49
- its(:after_end_hold_time) { should be == 30 }
50
46
 
51
47
  describe "its variables" do
52
- context "with an offer with headers" do
53
- let(:headers) { {:x_foo => 'bar'} }
54
- its(:variables) { should be == headers }
48
+ context "with an offer" do
49
+ context "with headers" do
50
+ let(:headers) { {:x_foo => 'bar'} }
51
+ its(:variables) { should be == headers }
55
52
 
56
- it "should be made available via []" do
57
- subject[:x_foo].should be == 'bar'
58
- end
53
+ it "should be made available via []" do
54
+ subject[:x_foo].should be == 'bar'
55
+ end
59
56
 
60
- it "should be alterable using []=" do
61
- subject[:x_foo] = 'baz'
62
- subject[:x_foo].should be == 'baz'
63
- end
57
+ it "should be alterable using []=" do
58
+ subject[:x_foo] = 'baz'
59
+ subject[:x_foo].should be == 'baz'
60
+ end
64
61
 
65
- context "when receiving an event with headers" do
66
- let(:event) { Punchblock::Event::End.new :headers => {:x_bar => 'foo'} }
62
+ context "when receiving an event with headers" do
63
+ let(:event) { Punchblock::Event::End.new :headers => {:x_bar => 'foo'} }
67
64
 
68
- it "should merge later headers" do
69
- subject << event
70
- subject.variables.should be == {:x_foo => 'bar', :x_bar => 'foo'}
65
+ it "should merge later headers" do
66
+ subject << event
67
+ subject.variables.should be == {:x_foo => 'bar', :x_bar => 'foo'}
68
+ end
71
69
  end
72
- end
73
70
 
74
- context "when sending a command with headers" do
75
- let(:command) { Punchblock::Command::Accept.new :headers => {:x_bar => 'foo'} }
71
+ context "when sending a command with headers" do
72
+ let(:command) { Punchblock::Command::Accept.new :headers => {:x_bar => 'foo'} }
76
73
 
77
- it "should merge later headers" do
78
- subject.write_command command
79
- subject.variables.should be == {:x_foo => 'bar', :x_bar => 'foo'}
74
+ it "should merge later headers" do
75
+ subject.write_command command
76
+ subject.variables.should be == {:x_foo => 'bar', :x_bar => 'foo'}
77
+ end
80
78
  end
81
79
  end
82
- end
83
80
 
84
- context "with an offer without headers" do
85
- let(:headers) { nil }
86
- its(:variables) { should be == {} }
81
+ context "without headers" do
82
+ let(:headers) { nil }
83
+ its(:variables) { should be == {} }
84
+ end
87
85
  end
88
86
 
89
87
  context "without an offer" do
@@ -273,6 +271,49 @@ module Adhearsion
273
271
  end
274
272
  end
275
273
 
274
+ context "peer registry" do
275
+ let(:other_call_id) { 'foobar' }
276
+ let(:other_call) { flexmock Call.new, :id => other_call_id }
277
+
278
+ let :joined_event do
279
+ Punchblock::Event::Joined.new :call_id => other_call_id
280
+ end
281
+
282
+ let :unjoined_event do
283
+ Punchblock::Event::Unjoined.new :call_id => other_call_id
284
+ end
285
+
286
+ context "when we know about the joined call" do
287
+ before { Adhearsion.active_calls << other_call }
288
+
289
+ it "should add the peer to its registry" do
290
+ subject << joined_event
291
+ subject.peers.should == {'foobar' => other_call}
292
+ end
293
+ end
294
+
295
+ context "when we don't know about the joined call" do
296
+ it "should add a nil entry to its registry" do
297
+ subject << joined_event
298
+ subject.peers.should == {'foobar' => nil}
299
+ end
300
+ end
301
+
302
+ it "should not return the same registry every call" do
303
+ subject.peers.should_not be subject.peers
304
+ end
305
+
306
+ context "when being unjoined from a previously joined call" do
307
+ before { subject << joined_event }
308
+
309
+ it "should remove the peer from its registry" do
310
+ subject.peers.should_not eql({})
311
+ subject << unjoined_event
312
+ subject.peers.should eql({})
313
+ end
314
+ end
315
+ end
316
+
276
317
  describe "#<<" do
277
318
  describe "with a Punchblock End" do
278
319
  let :end_event do
@@ -307,7 +348,7 @@ module Adhearsion
307
348
  end
308
349
 
309
350
  it "shuts down the actor" do
310
- flexmock subject.wrapped_object, :after_end_hold_time => 2
351
+ Adhearsion.config.platform.after_hangup_lifetime = 2
311
352
  subject << end_event
312
353
  sleep 2.1
313
354
  subject.should_not be_alive
@@ -539,6 +580,12 @@ module Adhearsion
539
580
  subject.reject nil, headers
540
581
  end
541
582
  end
583
+
584
+ it "should immediately fire the :call_rejected event giving the call and the reason" do
585
+ expect_message_waiting_for_response Punchblock::Command::Reject
586
+ flexmock(Adhearsion::Events).should_receive(:trigger_immediately).once.with(:call_rejected, :call => subject, :reason => :decline)
587
+ subject.reject :decline
588
+ end
542
589
  end
543
590
 
544
591
  describe "#hangup" do
@@ -760,12 +807,6 @@ module Adhearsion
760
807
  lambda { subject.execute_controller(mock_controller) { foo } }.should raise_error(ArgumentError)
761
808
  end
762
809
 
763
- it "should add the controller thread to the important threads" do
764
- flexmock(CallController).should_receive(:exec)
765
- controller_thread = subject.execute_controller mock_controller, lambda { |call| latch.countdown! }
766
- Adhearsion::Process.important_threads.should include controller_thread
767
- end
768
-
769
810
  it "should pass the exception to the events system" do
770
811
  latch = CountDownLatch.new 1
771
812
  Adhearsion::Events.exception do |e, l|