punchblock 0.4.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 (94) hide show
  1. data/.document +5 -0
  2. data/.gitignore +12 -0
  3. data/.rspec +3 -0
  4. data/Gemfile +2 -0
  5. data/LICENSE.txt +20 -0
  6. data/README.markdown +31 -0
  7. data/Rakefile +23 -0
  8. data/assets/ozone/ask-1.0.xsd +56 -0
  9. data/assets/ozone/conference-1.0.xsd +17 -0
  10. data/assets/ozone/ozone-1.0.xsd +127 -0
  11. data/assets/ozone/say-1.0.xsd +24 -0
  12. data/assets/ozone/transfer-1.0.xsd +32 -0
  13. data/bin/punchblock-console +125 -0
  14. data/lib/punchblock/command/accept.rb +30 -0
  15. data/lib/punchblock/command/answer.rb +30 -0
  16. data/lib/punchblock/command/dial.rb +88 -0
  17. data/lib/punchblock/command/hangup.rb +25 -0
  18. data/lib/punchblock/command/join.rb +81 -0
  19. data/lib/punchblock/command/mute.rb +7 -0
  20. data/lib/punchblock/command/redirect.rb +49 -0
  21. data/lib/punchblock/command/reject.rb +61 -0
  22. data/lib/punchblock/command/unjoin.rb +50 -0
  23. data/lib/punchblock/command/unmute.rb +7 -0
  24. data/lib/punchblock/command.rb +16 -0
  25. data/lib/punchblock/command_node.rb +46 -0
  26. data/lib/punchblock/component/input.rb +320 -0
  27. data/lib/punchblock/component/output.rb +449 -0
  28. data/lib/punchblock/component/record.rb +216 -0
  29. data/lib/punchblock/component/tropo/ask.rb +197 -0
  30. data/lib/punchblock/component/tropo/conference.rb +328 -0
  31. data/lib/punchblock/component/tropo/say.rb +113 -0
  32. data/lib/punchblock/component/tropo/transfer.rb +178 -0
  33. data/lib/punchblock/component/tropo.rb +12 -0
  34. data/lib/punchblock/component.rb +73 -0
  35. data/lib/punchblock/connection.rb +209 -0
  36. data/lib/punchblock/core_ext/blather/stanza/presence.rb +11 -0
  37. data/lib/punchblock/core_ext/blather/stanza.rb +26 -0
  38. data/lib/punchblock/dsl.rb +46 -0
  39. data/lib/punchblock/event/answered.rb +7 -0
  40. data/lib/punchblock/event/complete.rb +65 -0
  41. data/lib/punchblock/event/dtmf.rb +19 -0
  42. data/lib/punchblock/event/end.rb +15 -0
  43. data/lib/punchblock/event/info.rb +15 -0
  44. data/lib/punchblock/event/joined.rb +50 -0
  45. data/lib/punchblock/event/offer.rb +29 -0
  46. data/lib/punchblock/event/ringing.rb +7 -0
  47. data/lib/punchblock/event/unjoined.rb +50 -0
  48. data/lib/punchblock/event.rb +16 -0
  49. data/lib/punchblock/generic_connection.rb +18 -0
  50. data/lib/punchblock/has_headers.rb +34 -0
  51. data/lib/punchblock/header.rb +47 -0
  52. data/lib/punchblock/media_container.rb +39 -0
  53. data/lib/punchblock/media_node.rb +17 -0
  54. data/lib/punchblock/protocol_error.rb +16 -0
  55. data/lib/punchblock/rayo_node.rb +88 -0
  56. data/lib/punchblock/ref.rb +26 -0
  57. data/lib/punchblock/version.rb +3 -0
  58. data/lib/punchblock.rb +42 -0
  59. data/log/.gitkeep +0 -0
  60. data/punchblock.gemspec +42 -0
  61. data/spec/punchblock/command/accept_spec.rb +13 -0
  62. data/spec/punchblock/command/answer_spec.rb +13 -0
  63. data/spec/punchblock/command/dial_spec.rb +54 -0
  64. data/spec/punchblock/command/hangup_spec.rb +13 -0
  65. data/spec/punchblock/command/join_spec.rb +21 -0
  66. data/spec/punchblock/command/mute_spec.rb +11 -0
  67. data/spec/punchblock/command/redirect_spec.rb +19 -0
  68. data/spec/punchblock/command/reject_spec.rb +43 -0
  69. data/spec/punchblock/command/unjoin_spec.rb +19 -0
  70. data/spec/punchblock/command/unmute_spec.rb +11 -0
  71. data/spec/punchblock/command_node_spec.rb +80 -0
  72. data/spec/punchblock/component/input_spec.rb +188 -0
  73. data/spec/punchblock/component/output_spec.rb +531 -0
  74. data/spec/punchblock/component/record_spec.rb +235 -0
  75. data/spec/punchblock/component/tropo/ask_spec.rb +183 -0
  76. data/spec/punchblock/component/tropo/conference_spec.rb +360 -0
  77. data/spec/punchblock/component/tropo/say_spec.rb +171 -0
  78. data/spec/punchblock/component/tropo/transfer_spec.rb +153 -0
  79. data/spec/punchblock/component_spec.rb +126 -0
  80. data/spec/punchblock/connection_spec.rb +194 -0
  81. data/spec/punchblock/event/answered_spec.rb +23 -0
  82. data/spec/punchblock/event/complete_spec.rb +80 -0
  83. data/spec/punchblock/event/dtmf_spec.rb +24 -0
  84. data/spec/punchblock/event/end_spec.rb +30 -0
  85. data/spec/punchblock/event/info_spec.rb +30 -0
  86. data/spec/punchblock/event/joined_spec.rb +32 -0
  87. data/spec/punchblock/event/offer_spec.rb +35 -0
  88. data/spec/punchblock/event/ringing_spec.rb +23 -0
  89. data/spec/punchblock/event/unjoined_spec.rb +32 -0
  90. data/spec/punchblock/header_spec.rb +44 -0
  91. data/spec/punchblock/protocol_error_spec.rb +9 -0
  92. data/spec/punchblock/ref_spec.rb +21 -0
  93. data/spec/spec_helper.rb +43 -0
  94. metadata +353 -0
@@ -0,0 +1,126 @@
1
+ require 'spec_helper'
2
+
3
+ %w{
4
+ blather/client/dsl
5
+ punchblock/core_ext/blather/stanza
6
+ punchblock/core_ext/blather/stanza/presence
7
+ }.each { |f| require f }
8
+
9
+ module Punchblock
10
+ module Component
11
+ describe ComponentNode do
12
+ its(:event_queue) { should be_empty }
13
+
14
+ it "should not initially have a complete event set" do
15
+ subject.complete_event.set_yet?.should == false
16
+ end
17
+
18
+ describe "#add_event" do
19
+ let(:event) { Event::Complete.new }
20
+
21
+ before do
22
+ subject.request!
23
+ subject.execute!
24
+ end
25
+
26
+ let(:add_event) { subject.add_event event }
27
+
28
+ it "should add the event to the component's event queue" do
29
+ add_event
30
+ subject.event_queue.pop(false).should == event
31
+ end
32
+
33
+ it "should set the original event on the command" do
34
+ add_event
35
+ event.original_component.should == subject
36
+ end
37
+
38
+ it "should trigger state transition" do
39
+ subject.expects(:transition_state!).once.with event
40
+ add_event
41
+ end
42
+
43
+ describe "with a complete event" do
44
+ it "should set the complete event resource" do
45
+ add_event
46
+ subject.complete_event.set_yet?.should == true
47
+ subject.complete_event.resource.should == event
48
+ end
49
+ end
50
+
51
+ describe "with another event" do
52
+ let(:event) { Event::Answered.new }
53
+
54
+ it "should not set the complete event resource" do
55
+ add_event
56
+ subject.complete_event.set_yet?.should == false
57
+ end
58
+ end
59
+
60
+ describe "with an event callback set" do
61
+ let(:event_callback) { lambda { |event| @foo = :bar } }
62
+
63
+ before do
64
+ @foo = nil
65
+ subject.event_callback = event_callback
66
+ add_event
67
+ end
68
+
69
+ it "should trigger the callback" do
70
+ @foo.should == :bar
71
+ end
72
+
73
+ it "should not write the event to the event queue" do
74
+ subject.event_queue.should be_empty
75
+ end
76
+
77
+ describe "which returns a falsy value" do
78
+ let(:event_callback) do
79
+ lambda { |event| false }
80
+ end
81
+
82
+ it "should add the event to the event queue" do
83
+ subject.event_queue.should_not be_empty
84
+ end
85
+ end
86
+ end
87
+ end # #add_event
88
+
89
+ describe "#transition_state!" do
90
+ describe "with a complete" do
91
+ it "should call #complete!" do
92
+ subject.expects(:complete!).once
93
+ subject.transition_state! Event::Complete.new
94
+ end
95
+ end
96
+ end # #transition_state!
97
+
98
+ describe "#response=" do
99
+ before do
100
+ subject.request!
101
+ subject.connection = mock(:record_command_id_for_iq_id => true)
102
+ end
103
+
104
+ let(:component_id) { 'abc123' }
105
+
106
+ let :ref do
107
+ Ref.new.tap do |ref|
108
+ ref.id = component_id
109
+ end
110
+ end
111
+
112
+ let :iq do
113
+ Blather::Stanza::Iq.new(:result, 'blah').tap do |iq|
114
+ iq.from = "12345@call.rayo.net"
115
+ iq << ref
116
+ end
117
+ end
118
+
119
+ it "should set the component ID from the ref" do
120
+ subject.response = iq
121
+ subject.component_id.should == component_id
122
+ end
123
+ end
124
+ end # ComponentNode
125
+ end # Component
126
+ end # Punchblock
@@ -0,0 +1,194 @@
1
+ require 'spec_helper'
2
+
3
+ module Punchblock
4
+ describe Connection do
5
+ let(:connection) { Connection.new :username => 1, :password => 1 }
6
+
7
+ subject { connection }
8
+
9
+ it 'should require a username and password to be passed in the options' do
10
+ expect { Connection.new :password => 1 }.to raise_error ArgumentError
11
+ expect { Connection.new :username => 1 }.to raise_error ArgumentError
12
+ end
13
+
14
+ it 'should properly set the Blather logger' do
15
+ Connection.new :wire_logger => :foo, :username => 1, :password => 1
16
+ Blather.logger.should be :foo
17
+ end
18
+
19
+ its(:event_queue) { should be_a Queue }
20
+
21
+ it "looking up original command by command ID" do
22
+ offer = Event::Offer.new
23
+ offer.call_id = '9f00061'
24
+ offer.to = 'sip:whatever@127.0.0.1'
25
+ say = <<-MSG
26
+ <say xmlns='urn:xmpp:tropo:say:1' voice='allison'>
27
+ <audio url='http://acme.com/greeting.mp3'>
28
+ Thanks for calling ACME company
29
+ </audio>
30
+ <audio url='http://acme.com/package-shipped.mp3'>
31
+ Your package was shipped on
32
+ </audio>
33
+ <say-as interpret-as='date'>12/01/2011</say-as>
34
+ </say>
35
+ MSG
36
+ Component::Tropo::Say
37
+ say = RayoNode.import parse_stanza(say).root
38
+ connection.event_queue = []
39
+ connection.expects(:write_to_stream).once.returns true
40
+ iq = Blather::Stanza::Iq.new :set, '9f00061@call.rayo.net'
41
+ connection.expects(:create_iq).returns iq
42
+
43
+ write_thread = Thread.new do
44
+ connection.write offer.call_id, say
45
+ end
46
+
47
+ result = import_stanza <<-MSG
48
+ <iq type='result' from='16577@app.rayo.net/1' to='9f00061@call.rayo.net/1' id='#{iq.id}'>
49
+ <ref id='fgh4590' xmlns='urn:xmpp:rayo:1' />
50
+ </iq>
51
+ MSG
52
+
53
+ sleep 0.5 # Block so there's enough time for the write thread to get to the point where it's waiting on an IQ
54
+
55
+ connection.__send__ :handle_iq_result, result
56
+
57
+ write_thread.join
58
+
59
+ say.state_name.should == :executing
60
+
61
+ connection.original_component_from_id('fgh4590').should == say
62
+
63
+ example_complete = import_stanza <<-MSG
64
+ <presence to='16577@app.rayo.net/1' from='9f00061@call.rayo.net/fgh4590'>
65
+ <complete xmlns='urn:xmpp:rayo:ext:1'>
66
+ <success xmlns='urn:xmpp:tropo:say:complete:1' />
67
+ </complete>
68
+ </presence>
69
+ MSG
70
+
71
+ connection.__send__ :handle_presence, example_complete
72
+ say.event_queue.pop(false).source.should == say
73
+
74
+ say.component_id.should == 'fgh4590'
75
+ end
76
+
77
+ describe '#handle_presence' do
78
+ let :offer_xml do
79
+ <<-MSG
80
+ <presence to='16577@app.rayo.net/1' from='9f00061@call.rayo.net'>
81
+ <offer xmlns="urn:xmpp:rayo:1" to="sip:whatever@127.0.0.1" from="sip:ylcaomxb@192.168.1.9">
82
+ <header name="Max-Forwards" value="70"/>
83
+ <header name="Content-Length" value="367"/>
84
+ </offer>
85
+ </presence>
86
+ MSG
87
+ end
88
+
89
+ let(:example_offer) { import_stanza offer_xml }
90
+
91
+ it { example_offer.should be_a Blather::Stanza::Presence }
92
+
93
+ let :complete_xml do
94
+ <<-MSG
95
+ <presence to='16577@app.rayo.net/1' from='9f00061@call.rayo.net/fgh4590'>
96
+ <complete xmlns='urn:xmpp:rayo:ext:1'>
97
+ <success xmlns='urn:xmpp:tropo:say:complete:1' />
98
+ </complete>
99
+ </presence>
100
+ MSG
101
+ end
102
+
103
+ let(:example_complete) { import_stanza complete_xml }
104
+
105
+ it { example_complete.should be_a Blather::Stanza::Presence }
106
+
107
+ describe "event placed on the event queue" do
108
+ before do
109
+ connection.event_queue = []
110
+ end
111
+
112
+ describe "from an offer" do
113
+ before do
114
+ connection.__send__ :handle_presence, example_offer
115
+ end
116
+
117
+ subject { connection.event_queue.first }
118
+
119
+ it { should be_instance_of Event::Offer }
120
+ its(:call_id) { should == '9f00061' }
121
+
122
+ it "should populate the call map with the domain for the call ID" do
123
+ callmap = connection.instance_variable_get(:'@callmap')
124
+ callmap['9f00061'].should == 'call.rayo.net'
125
+ end
126
+ end
127
+
128
+ describe "from a complete" do
129
+ before do
130
+ connection.__send__ :handle_presence, example_complete
131
+ end
132
+
133
+ subject { connection.event_queue.first }
134
+
135
+ it { should be_instance_of Event::Complete }
136
+ its(:call_id) { should == '9f00061' }
137
+ its(:connection) { should == connection }
138
+ end
139
+
140
+ describe "from something that's not a real event" do
141
+ let :irrelevant_xml do
142
+ <<-MSG
143
+ <presence to='16577@app.rayo.net/1' from='9f00061@call.rayo.net/fgh4590'>
144
+ <foo/>
145
+ </presence>
146
+ MSG
147
+ end
148
+
149
+ let(:example_irrelevant_event) { import_stanza irrelevant_xml }
150
+
151
+ before do
152
+ lambda { connection.__send__ :handle_presence, example_irrelevant_event }.should throw_symbol(:pass)
153
+ end
154
+
155
+ subject { connection.event_queue }
156
+
157
+ it { should be_empty }
158
+ end
159
+ end
160
+ end
161
+
162
+ describe "#handle_error" do
163
+ let(:call_id) { "f6d437f4-1e18-457b-99f8-b5d853f50347" }
164
+ let(:component_id) { 'abc123' }
165
+ let :error_xml do
166
+ <<-MSG
167
+ <iq type="error" id="blather000e" from="f6d437f4-1e18-457b-99f8-b5d853f50347@10.0.1.11/abc123" to="usera@10.0.1.11/voxeo">
168
+ <output xmlns="urn:xmpp:rayo:output:1"/>
169
+ <error type="cancel">
170
+ <item-not-found xmlns="urn:ietf:params:xml:ns:xmpp-stanzas"/>
171
+ <text xmlns="urn:ietf:params:xml:ns:xmpp-stanzas" lang="en">Could not find call [id=f6d437f4-1e18-457b-99f8-b5d853f50347]</text>
172
+ </error>
173
+ </iq>
174
+ MSG
175
+ end
176
+
177
+ let(:example_error) { import_stanza error_xml }
178
+ let(:cmd) { Component::Output.new }
179
+
180
+ before(:all) do
181
+ cmd.request!
182
+ connection.instance_variable_get(:'@iq_id_to_command')['blather000e'] = cmd
183
+ connection.__send__ :handle_error, example_error
184
+ end
185
+
186
+ subject { cmd.response }
187
+
188
+ its(:call_id) { should == call_id }
189
+ its(:component_id) { should == component_id }
190
+ its(:name) { should == :item_not_found }
191
+ its(:text) { should == 'Could not find call [id=f6d437f4-1e18-457b-99f8-b5d853f50347]' }
192
+ end
193
+ end # describe Connection
194
+ end # Punchblock
@@ -0,0 +1,23 @@
1
+ require 'spec_helper'
2
+
3
+ module Punchblock
4
+ class Event
5
+ describe Answered do
6
+ it 'registers itself' do
7
+ RayoNode.class_from_registration(:answered, 'urn:xmpp:rayo:1').should == Answered
8
+ end
9
+
10
+ describe "from a stanza" do
11
+ let(:stanza) { '<answered xmlns="urn:xmpp:rayo:1"/>' }
12
+
13
+ subject { RayoNode.import parse_stanza(stanza).root, '9f00061', '1' }
14
+
15
+ it { should be_instance_of Answered }
16
+
17
+ it_should_behave_like 'event'
18
+
19
+ its(:xmlns) { should == 'urn:xmpp:rayo:1' }
20
+ end
21
+ end
22
+ end
23
+ end # Punchblock
@@ -0,0 +1,80 @@
1
+ require 'spec_helper'
2
+
3
+ module Punchblock
4
+ class Event
5
+ describe Complete do
6
+ it 'registers itself' do
7
+ RayoNode.class_from_registration(:complete, 'urn:xmpp:rayo:ext:1').should == Complete
8
+ end
9
+
10
+ describe "from a stanza" do
11
+ let :stanza do
12
+ <<-MESSAGE
13
+ <complete xmlns='urn:xmpp:rayo:ext:1'>
14
+ <success xmlns='urn:xmpp:tropo:say:complete:1' />
15
+ </complete>
16
+ MESSAGE
17
+ end
18
+
19
+ subject { RayoNode.import parse_stanza(stanza).root, '9f00061', '1' }
20
+
21
+ it { should be_instance_of Complete }
22
+
23
+ it_should_behave_like 'event'
24
+
25
+ its(:reason) { should be_instance_of Component::Tropo::Say::Complete::Success }
26
+ end
27
+ end
28
+
29
+ describe Complete::Stop do
30
+ let :stanza do
31
+ <<-MESSAGE
32
+ <complete xmlns='urn:xmpp:rayo:ext:1'>
33
+ <stop xmlns='urn:xmpp:rayo:ext:complete:1' />
34
+ </complete>
35
+ MESSAGE
36
+ end
37
+
38
+ subject { RayoNode.import(parse_stanza(stanza).root).reason }
39
+
40
+ it { should be_instance_of Complete::Stop }
41
+
42
+ its(:name) { should == :stop }
43
+ end
44
+
45
+ describe Complete::Hangup do
46
+ let :stanza do
47
+ <<-MESSAGE
48
+ <complete xmlns='urn:xmpp:rayo:ext:1'>
49
+ <hangup xmlns='urn:xmpp:rayo:ext:complete:1' />
50
+ </complete>
51
+ MESSAGE
52
+ end
53
+
54
+ subject { RayoNode.import(parse_stanza(stanza).root).reason }
55
+
56
+ it { should be_instance_of Complete::Hangup }
57
+
58
+ its(:name) { should == :hangup }
59
+ end
60
+
61
+ describe Complete::Error do
62
+ let :stanza do
63
+ <<-MESSAGE
64
+ <complete xmlns='urn:xmpp:rayo:ext:1'>
65
+ <error xmlns='urn:xmpp:rayo:ext:complete:1'>
66
+ Something really bad happened
67
+ </error>
68
+ </complete>
69
+ MESSAGE
70
+ end
71
+
72
+ subject { RayoNode.import(parse_stanza(stanza).root).reason }
73
+
74
+ it { should be_instance_of Complete::Error }
75
+
76
+ its(:name) { should == :error }
77
+ its(:details) { should == "Something really bad happened" }
78
+ end
79
+ end
80
+ end # Punchblock
@@ -0,0 +1,24 @@
1
+ require 'spec_helper'
2
+
3
+ module Punchblock
4
+ class Event
5
+ describe DTMF do
6
+ it 'registers itself' do
7
+ RayoNode.class_from_registration(:dtmf, 'urn:xmpp:rayo:1').should == DTMF
8
+ end
9
+
10
+ describe "from a stanza" do
11
+ let(:stanza) { "<dtmf xmlns='urn:xmpp:rayo:1' signal='#' />" }
12
+
13
+ subject { RayoNode.import parse_stanza(stanza).root, '9f00061', '1' }
14
+
15
+ it { should be_instance_of DTMF }
16
+
17
+ it_should_behave_like 'event'
18
+
19
+ its(:signal) { should == '#' }
20
+ its(:xmlns) { should == 'urn:xmpp:rayo:1' }
21
+ end
22
+ end
23
+ end
24
+ end # Punchblock
@@ -0,0 +1,30 @@
1
+ require 'spec_helper'
2
+
3
+ module Punchblock
4
+ class Event
5
+ describe End do
6
+ it 'registers itself' do
7
+ RayoNode.class_from_registration(:end, 'urn:xmpp:rayo:1').should == End
8
+ end
9
+
10
+ describe "from a stanza" do
11
+ let :stanza do
12
+ <<-MESSAGE
13
+ <end xmlns="urn:xmpp:rayo:1">
14
+ <timeout />
15
+ </end>
16
+ MESSAGE
17
+ end
18
+
19
+ subject { RayoNode.import parse_stanza(stanza).root, '9f00061', '1' }
20
+
21
+ it { should be_instance_of End }
22
+
23
+ it_should_behave_like 'event'
24
+
25
+ its(:reason) { should == :timeout }
26
+ its(:xmlns) { should == 'urn:xmpp:rayo:1' }
27
+ end
28
+ end
29
+ end
30
+ end # Punchblock
@@ -0,0 +1,30 @@
1
+ require 'spec_helper'
2
+
3
+ module Punchblock
4
+ class Event
5
+ describe Info do
6
+ it 'registers itself' do
7
+ RayoNode.class_from_registration(:info, 'urn:xmpp:rayo:1').should == Info
8
+ end
9
+
10
+ describe "from a stanza" do
11
+ let :stanza do
12
+ <<-MESSAGE
13
+ <info xmlns='urn:xmpp:rayo:1'>
14
+ <something/>
15
+ </info>
16
+ MESSAGE
17
+ end
18
+
19
+ subject { RayoNode.import parse_stanza(stanza).root, '9f00061', '1' }
20
+
21
+ it { should be_instance_of Info }
22
+
23
+ it_should_behave_like 'event'
24
+
25
+ its(:event_name) { should == :something }
26
+ its(:xmlns) { should == 'urn:xmpp:rayo:1' }
27
+ end
28
+ end
29
+ end
30
+ end # Punchblock
@@ -0,0 +1,32 @@
1
+ require 'spec_helper'
2
+
3
+ module Punchblock
4
+ class Event
5
+ describe Joined do
6
+ it 'registers itself' do
7
+ RayoNode.class_from_registration(:joined, 'urn:xmpp:rayo:1').should == Joined
8
+ end
9
+
10
+ describe "from a stanza" do
11
+ let(:stanza) { '<joined xmlns="urn:xmpp:rayo:1" call-id="b" mixer-id="m" />' }
12
+
13
+ subject { RayoNode.import parse_stanza(stanza).root, '9f00061', '1' }
14
+
15
+ it { should be_instance_of Joined }
16
+
17
+ it_should_behave_like 'event'
18
+
19
+ its(:other_call_id) { should == 'b' }
20
+ its(:mixer_id) { should == 'm' }
21
+ its(:xmlns) { should == 'urn:xmpp:rayo:1' }
22
+ end
23
+
24
+ describe "when setting options in initializer" do
25
+ subject { Joined.new :other_call_id => 'abc123', :mixer_id => 'blah' }
26
+
27
+ its(:other_call_id) { should == 'abc123' }
28
+ its(:mixer_id) { should == 'blah' }
29
+ end
30
+ end
31
+ end
32
+ end # Punchblock
@@ -0,0 +1,35 @@
1
+ require 'spec_helper'
2
+
3
+ module Punchblock
4
+ class Event
5
+ describe Offer do
6
+ it 'registers itself' do
7
+ RayoNode.class_from_registration(:offer, 'urn:xmpp:rayo:1').should == Offer
8
+ end
9
+
10
+ describe "from a stanza" do
11
+ let :stanza do
12
+ <<-MESSAGE
13
+ <offer xmlns='urn:xmpp:rayo:1'
14
+ to='tel:+18003211212'
15
+ from='tel:+13058881212'>
16
+ <!-- Signaling (e.g. SIP) Headers -->
17
+ <header name="x-skill" value="agent" />
18
+ <header name="x-customer-id" value="8877" />
19
+ </offer>
20
+ MESSAGE
21
+ end
22
+
23
+ subject { RayoNode.import parse_stanza(stanza).root, '9f00061', '1' }
24
+
25
+ it { should be_instance_of Offer }
26
+
27
+ it_should_behave_like 'event'
28
+ it_should_behave_like 'event_headers'
29
+
30
+ its(:to) { should == 'tel:+18003211212' }
31
+ its(:from) { should == 'tel:+13058881212' }
32
+ end
33
+ end
34
+ end
35
+ end # Punchblock
@@ -0,0 +1,23 @@
1
+ require 'spec_helper'
2
+
3
+ module Punchblock
4
+ class Event
5
+ describe Ringing do
6
+ it 'registers itself' do
7
+ RayoNode.class_from_registration(:ringing, 'urn:xmpp:rayo:1').should == Ringing
8
+ end
9
+
10
+ describe "from a stanza" do
11
+ let(:stanza) { '<ringing xmlns="urn:xmpp:rayo:1"/>' }
12
+
13
+ subject { RayoNode.import parse_stanza(stanza).root, '9f00061', '1' }
14
+
15
+ it { should be_instance_of Ringing }
16
+
17
+ it_should_behave_like 'event'
18
+
19
+ its(:xmlns) { should == 'urn:xmpp:rayo:1' }
20
+ end
21
+ end
22
+ end
23
+ end # Punchblock
@@ -0,0 +1,32 @@
1
+ require 'spec_helper'
2
+
3
+ module Punchblock
4
+ class Event
5
+ describe Unjoined do
6
+ it 'registers itself' do
7
+ RayoNode.class_from_registration(:unjoined, 'urn:xmpp:rayo:1').should == Unjoined
8
+ end
9
+
10
+ describe "from a stanza" do
11
+ let(:stanza) { '<unjoined xmlns="urn:xmpp:rayo:1" call-id="b" mixer-id="m" />' }
12
+
13
+ subject { RayoNode.import parse_stanza(stanza).root, '9f00061', '1' }
14
+
15
+ it { should be_instance_of Unjoined }
16
+
17
+ it_should_behave_like 'event'
18
+
19
+ its(:other_call_id) { should == 'b' }
20
+ its(:mixer_id) { should == 'm' }
21
+ its(:xmlns) { should == 'urn:xmpp:rayo:1' }
22
+ end
23
+
24
+ describe "when setting options in initializer" do
25
+ subject { Unjoined.new :other_call_id => 'abc123', :mixer_id => 'blah' }
26
+
27
+ its(:other_call_id) { should == 'abc123' }
28
+ its(:mixer_id) { should == 'blah' }
29
+ end
30
+ end
31
+ end
32
+ end # Punchblock
@@ -0,0 +1,44 @@
1
+ require 'spec_helper'
2
+
3
+ module Punchblock
4
+ describe Header do
5
+ it 'will auto-inherit nodes' do
6
+ n = parse_stanza "<header name='boo' value='bah' />"
7
+ h = Header.new n.root
8
+ h.name.should == :boo
9
+ h.value.should == 'bah'
10
+ end
11
+
12
+ it 'has a name attribute' do
13
+ n = Header.new :boo, 'bah'
14
+ n.name.should == :boo
15
+ n.name = :foo
16
+ n.name.should == :foo
17
+ end
18
+
19
+ it "substitutes - for _ on the name attribute when reading" do
20
+ n = parse_stanza "<header name='boo-bah' value='foo' />"
21
+ h = Header.new n.root
22
+ h.name.should == :boo_bah
23
+ end
24
+
25
+ it "substitutes _ for - on the name attribute when writing" do
26
+ h = Header.new :boo_bah, 'foo'
27
+ h.to_xml.should == '<header name="boo-bah" value="foo"/>'
28
+ end
29
+
30
+ it 'has a value attribute' do
31
+ n = Header.new :boo, 'en'
32
+ n.value.should == 'en'
33
+ n.value = 'de'
34
+ n.value.should == 'de'
35
+ end
36
+
37
+ it 'can determine equality' do
38
+ a = Header.new :boo, 'bah'
39
+ a.should == Header.new(:boo, 'bah')
40
+ a.should_not == Header.new(:bah, 'bah')
41
+ a.should_not == Header.new(:boo, 'boo')
42
+ end
43
+ end
44
+ end # Punchblock
@@ -0,0 +1,9 @@
1
+ require 'spec_helper'
2
+
3
+ module Punchblock
4
+ describe ProtocolError do
5
+ subject { ProtocolError.new :item_not_found, 'Could not find call [id=f6d437f4-1e18-457b-99f8-b5d853f50347]', 'f6d437f4-1e18-457b-99f8-b5d853f50347', 'abc123' }
6
+
7
+ its(:inspect) { should == '#<Punchblock::ProtocolError: name=:item_not_found text="Could not find call [id=f6d437f4-1e18-457b-99f8-b5d853f50347]" call_id="f6d437f4-1e18-457b-99f8-b5d853f50347" component_id="abc123">' }
8
+ end
9
+ end