punchblock 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
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