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,360 @@
1
+ require 'spec_helper'
2
+
3
+ module Punchblock
4
+ module Component
5
+ module Tropo
6
+ describe Conference do
7
+ it 'registers itself' do
8
+ RayoNode.class_from_registration(:conference, 'urn:xmpp:tropo:conference:1').should == Conference
9
+ end
10
+
11
+ describe "when setting options in initializer" do
12
+ subject do
13
+ Conference.new :name => '1234',
14
+ :terminator => '#',
15
+ :moderator => true,
16
+ :tone_passthrough => true,
17
+ :mute => false,
18
+ :announcement => {:text => "Welcome to Rayo", :voice => 'shelly'},
19
+ :music => {:text => "The moderator how not yet joined.. Listen to this awesome music while you wait.", :voice => 'frank'}
20
+ end
21
+
22
+ its(:name) { should == '1234' }
23
+ its(:mute) { should == false }
24
+ its(:terminator) { should == '#' }
25
+ its(:tone_passthrough) { should == true }
26
+ its(:moderator) { should == true }
27
+ its(:announcement) { should == Conference::Announcement.new(:text => "Welcome to Rayo", :voice => 'shelly') }
28
+ its(:music) { should == Conference::Music.new(:text => "The moderator how not yet joined.. Listen to this awesome music while you wait.", :voice => 'frank') }
29
+ end
30
+
31
+ its(:mute_status_name) { should == :unknown_mute }
32
+ its(:hold_status_name) { should == :unknown_hold }
33
+
34
+ describe "#==" do
35
+ subject { Conference.new :name => 'the-conference' }
36
+ let(:conference2) { Conference.new :name => 'the-conference' }
37
+ let(:conference3) { Conference.new :name => 'other-conference' }
38
+
39
+ it { should == conference2 }
40
+ it { should_not == conference3 }
41
+ end
42
+
43
+ describe "#transition_state!" do
44
+ describe "with an on-hold" do
45
+ it "should call #onhold!" do
46
+ subject.expects(:onhold!).once
47
+ subject.transition_state! Conference::OnHold.new
48
+ end
49
+ end
50
+
51
+ describe "with an off-hold" do
52
+ it "should call #offhold!" do
53
+ subject.expects(:offhold!).once
54
+ subject.transition_state! Conference::OffHold.new
55
+ end
56
+ end
57
+ end # #transition_state!
58
+
59
+ describe "#requested" do
60
+ context "when requesting to be muted" do
61
+ subject { Conference.new :mute => true }
62
+ before { subject.request! }
63
+ its(:mute_status_name) { should == :muted }
64
+ end
65
+
66
+ context "when requesting not to be muted" do
67
+ subject { Conference.new :mute => false }
68
+ before { subject.request! }
69
+ its(:mute_status_name) { should == :unmuted }
70
+ end
71
+ end
72
+
73
+ describe "#onhold!" do
74
+ before do
75
+ subject.onhold!
76
+ end
77
+
78
+ its(:hold_status_name) { should == :onhold }
79
+
80
+ it "should raise a StateMachine::InvalidTransition when received a second time" do
81
+ lambda { subject.onhold! }.should raise_error(StateMachine::InvalidTransition)
82
+ end
83
+ end
84
+
85
+ describe "#offhold!" do
86
+ before do
87
+ subject.onhold!
88
+ subject.offhold!
89
+ end
90
+
91
+ its(:hold_status_name) { should == :offhold }
92
+
93
+ it "should raise a StateMachine::InvalidTransition when received a second time" do
94
+ lambda { subject.offhold! }.should raise_error(StateMachine::InvalidTransition)
95
+ end
96
+ end
97
+
98
+ describe "actions" do
99
+ let(:conference) { Conference.new :name => '1234' }
100
+
101
+ before do
102
+ conference.component_id = 'abc123'
103
+ conference.call_id = '123abc'
104
+ conference.connection = Connection.new :username => '123', :password => '123'
105
+ end
106
+
107
+ describe '#mute_action' do
108
+ subject { conference.mute_action }
109
+
110
+ it { should be_a Command::Mute }
111
+ its(:component_id) { should == 'abc123' }
112
+ its(:call_id) { should == '123abc' }
113
+ end
114
+
115
+ describe '#mute!' do
116
+ describe "when unmuted" do
117
+ before do
118
+ conference.request!
119
+ conference.execute!
120
+ end
121
+
122
+ it "should send its command properly" do
123
+ Connection.any_instance.expects(:write).with('123abc', conference.mute_action, 'abc123').returns true
124
+ conference.expects :muted!
125
+ conference.mute!
126
+ end
127
+ end
128
+
129
+ describe "when muted" do
130
+ before { conference.muted! }
131
+
132
+ it "should raise an error" do
133
+ lambda { conference.mute! }.should raise_error(InvalidActionError, "Cannot mute a Conference that is already muted")
134
+ end
135
+ end
136
+ end
137
+
138
+ describe "#muted!" do
139
+ before do
140
+ subject.request!
141
+ subject.execute!
142
+ subject.muted!
143
+ end
144
+
145
+ its(:mute_status_name) { should == :muted }
146
+
147
+ it "should raise a StateMachine::InvalidTransition when received a second time" do
148
+ lambda { subject.muted! }.should raise_error(StateMachine::InvalidTransition)
149
+ end
150
+ end
151
+
152
+ describe '#unmute_action' do
153
+ subject { conference.unmute_action }
154
+
155
+ it { should be_a Command::Unmute }
156
+ its(:component_id) { should == 'abc123' }
157
+ its(:call_id) { should == '123abc' }
158
+ end
159
+
160
+ describe '#unmute!' do
161
+ before do
162
+ conference.request!
163
+ conference.execute!
164
+ end
165
+
166
+ describe "when muted" do
167
+ before do
168
+ conference.muted!
169
+ end
170
+
171
+ it "should send its command properly" do
172
+ Connection.any_instance.expects(:write).with('123abc', conference.unmute_action, 'abc123').returns true
173
+ conference.expects :unmuted!
174
+ conference.unmute!
175
+ end
176
+ end
177
+
178
+ describe "when unmuted" do
179
+ it "should raise an error" do
180
+ lambda { conference.unmute! }.should raise_error(InvalidActionError, "Cannot unmute a Conference that is not muted")
181
+ end
182
+ end
183
+ end
184
+
185
+ describe "#unmuted!" do
186
+ before do
187
+ subject.request!
188
+ subject.execute!
189
+ subject.muted!
190
+ subject.unmuted!
191
+ end
192
+
193
+ its(:mute_status_name) { should == :unmuted }
194
+
195
+ it "should raise a StateMachine::InvalidTransition when received a second time" do
196
+ lambda { subject.unmuted! }.should raise_error(StateMachine::InvalidTransition)
197
+ end
198
+ end
199
+
200
+ describe '#stop_action' do
201
+ subject { conference.stop_action }
202
+
203
+ its(:to_xml) { should == '<stop xmlns="urn:xmpp:rayo:1"/>' }
204
+ its(:component_id) { should == 'abc123' }
205
+ its(:call_id) { should == '123abc' }
206
+ end
207
+
208
+ describe '#stop!' do
209
+ describe "when the command is executing" do
210
+ before do
211
+ conference.request!
212
+ conference.execute!
213
+ end
214
+
215
+ it "should send its command properly" do
216
+ Connection.any_instance.expects(:write).with('123abc', conference.stop_action, 'abc123')
217
+ conference.stop!
218
+ end
219
+ end
220
+
221
+ describe "when the command is not executing" do
222
+ it "should raise an error" do
223
+ lambda { conference.stop! }.should raise_error(InvalidActionError, "Cannot stop a Conference that is not executing")
224
+ end
225
+ end
226
+ end # describe #stop!
227
+
228
+ describe '#kick_action' do
229
+ subject { conference.kick_action :message => 'bye!' }
230
+
231
+ its(:to_xml) { should == '<kick xmlns="urn:xmpp:tropo:conference:1">bye!</kick>' }
232
+ its(:component_id) { should == 'abc123' }
233
+ its(:call_id) { should == '123abc' }
234
+ end
235
+
236
+ describe '#kick!' do
237
+ describe "when the command is executing" do
238
+ before do
239
+ conference.request!
240
+ conference.execute!
241
+ end
242
+
243
+ it "should send its command properly" do
244
+ Connection.any_instance.expects(:write).with('123abc', conference.kick_action(:message => 'bye!'), 'abc123')
245
+ conference.kick! :message => 'bye!'
246
+ end
247
+ end
248
+
249
+ describe "when the command is not executing" do
250
+ it "should raise an error" do
251
+ lambda { conference.kick! }.should raise_error(InvalidActionError, "Cannot kick a Conference that is not executing")
252
+ end
253
+ end
254
+ end # describe #kick!
255
+ end
256
+
257
+ describe Conference::OnHold do
258
+ it 'registers itself' do
259
+ RayoNode.class_from_registration(:'on-hold', 'urn:xmpp:tropo:conference:1').should == Conference::OnHold
260
+ end
261
+
262
+ describe "from a stanza" do
263
+ let(:stanza) { "<on-hold xmlns='urn:xmpp:tropo:conference:1'/>" }
264
+
265
+ subject { RayoNode.import parse_stanza(stanza).root, '9f00061', '1' }
266
+
267
+ it { should be_instance_of Conference::OnHold }
268
+
269
+ it_should_behave_like 'event'
270
+ end
271
+ end
272
+
273
+ describe Conference::OffHold do
274
+ it 'registers itself' do
275
+ RayoNode.class_from_registration(:'off-hold', 'urn:xmpp:tropo:conference:1').should == Conference::OffHold
276
+ end
277
+
278
+ describe "from a stanza" do
279
+ let(:stanza) { "<off-hold xmlns='urn:xmpp:tropo:conference:1'/>" }
280
+
281
+ subject { RayoNode.import parse_stanza(stanza).root, '9f00061', '1' }
282
+
283
+ it { should be_instance_of Conference::OffHold }
284
+
285
+ it_should_behave_like 'event'
286
+ end
287
+ end
288
+
289
+ describe Conference::Speaking do
290
+ it 'registers itself' do
291
+ RayoNode.class_from_registration(:speaking, 'urn:xmpp:tropo:conference:1').should == Conference::Speaking
292
+ end
293
+
294
+ describe "from a stanza" do
295
+ let(:stanza) { "<speaking xmlns='urn:xmpp:tropo:conference:1' call-id='abc123'/>" }
296
+
297
+ subject { RayoNode.import parse_stanza(stanza).root, '9f00061', '1' }
298
+
299
+ it { should be_instance_of Conference::Speaking }
300
+
301
+ it_should_behave_like 'event'
302
+
303
+ its(:speaking_call_id) { should == 'abc123' }
304
+ end
305
+ end
306
+
307
+ describe Conference::FinishedSpeaking do
308
+ it 'registers itself' do
309
+ RayoNode.class_from_registration(:'finished-speaking', 'urn:xmpp:tropo:conference:1').should == Conference::FinishedSpeaking
310
+ end
311
+
312
+ describe "from a stanza" do
313
+ let(:stanza) { "<finished-speaking xmlns='urn:xmpp:tropo:conference:1' call-id='abc123'/>" }
314
+
315
+ subject { RayoNode.import parse_stanza(stanza).root, '9f00061', '1' }
316
+
317
+ it { should be_instance_of Conference::FinishedSpeaking }
318
+
319
+ it_should_behave_like 'event'
320
+
321
+ its(:speaking_call_id) { should == 'abc123' }
322
+ end
323
+ end
324
+
325
+ describe Conference::Complete::Kick do
326
+ let :stanza do
327
+ <<-MESSAGE
328
+ <complete xmlns='urn:xmpp:rayo:ext:1'>
329
+ <kick xmlns='urn:xmpp:tropo:conference:complete:1'>wouldn't stop talking</kick>
330
+ </complete>
331
+ MESSAGE
332
+ end
333
+
334
+ subject { RayoNode.import(parse_stanza(stanza).root).reason }
335
+
336
+ it { should be_instance_of Conference::Complete::Kick }
337
+
338
+ its(:name) { should == :kick }
339
+ its(:details) { should == "wouldn't stop talking" }
340
+ end
341
+
342
+ describe Conference::Complete::Terminator do
343
+ let :stanza do
344
+ <<-MESSAGE
345
+ <complete xmlns='urn:xmpp:rayo:ext:1'>
346
+ <terminator xmlns='urn:xmpp:tropo:conference:complete:1' />
347
+ </complete>
348
+ MESSAGE
349
+ end
350
+
351
+ subject { RayoNode.import(parse_stanza(stanza).root).reason }
352
+
353
+ it { should be_instance_of Conference::Complete::Terminator }
354
+
355
+ its(:name) { should == :terminator }
356
+ end
357
+ end
358
+ end
359
+ end
360
+ end # Punchblock
@@ -0,0 +1,171 @@
1
+ require 'spec_helper'
2
+
3
+ module Punchblock
4
+ module Component
5
+ module Tropo
6
+ describe Say do
7
+ it 'registers itself' do
8
+ RayoNode.class_from_registration(:say, 'urn:xmpp:tropo:say:1').should == Say
9
+ end
10
+
11
+ describe "for text" do
12
+ subject { Say.new :text => 'Once upon a time there was a message...', :voice => 'kate' }
13
+
14
+ its(:voice) { should == 'kate' }
15
+ its(:text) { should == 'Once upon a time there was a message...' }
16
+ end
17
+
18
+ describe "for SSML" do
19
+ subject { Say.new :ssml => '<say-as interpret-as="ordinal">100</say-as>', :voice => 'kate' }
20
+
21
+ its(:voice) { should == 'kate' }
22
+ it "should have the correct content" do
23
+ subject.child.to_s.should == '<say-as interpret-as="ordinal">100</say-as>'
24
+ end
25
+ end
26
+
27
+ describe "actions" do
28
+ let(:command) { Say.new :text => 'Once upon a time there was a message...', :voice => 'kate' }
29
+
30
+ before do
31
+ command.component_id = 'abc123'
32
+ command.call_id = '123abc'
33
+ command.connection = Connection.new :username => '123', :password => '123'
34
+ end
35
+
36
+ describe '#pause_action' do
37
+ subject { command.pause_action }
38
+
39
+ its(:to_xml) { should == '<pause xmlns="urn:xmpp:tropo:say:1"/>' }
40
+ its(:component_id) { should == 'abc123' }
41
+ its(:call_id) { should == '123abc' }
42
+ end
43
+
44
+ describe '#pause!' do
45
+ describe "when the command is executing" do
46
+ before do
47
+ command.request!
48
+ command.execute!
49
+ end
50
+
51
+ it "should send its command properly" do
52
+ Connection.any_instance.expects(:write).with('123abc', command.pause_action, 'abc123').returns true
53
+ command.expects :paused!
54
+ command.pause!
55
+ end
56
+ end
57
+
58
+ describe "when the command is not executing" do
59
+ it "should raise an error" do
60
+ lambda { command.pause! }.should raise_error(InvalidActionError, "Cannot pause a Say that is not executing")
61
+ end
62
+ end
63
+ end
64
+
65
+ describe "#paused!" do
66
+ before do
67
+ subject.request!
68
+ subject.execute!
69
+ subject.paused!
70
+ end
71
+
72
+ its(:state_name) { should == :paused }
73
+
74
+ it "should raise a StateMachine::InvalidTransition when received a second time" do
75
+ lambda { subject.paused! }.should raise_error(StateMachine::InvalidTransition)
76
+ end
77
+ end
78
+
79
+ describe '#resume_action' do
80
+ subject { command.resume_action }
81
+
82
+ its(:to_xml) { should == '<resume xmlns="urn:xmpp:tropo:say:1"/>' }
83
+ its(:component_id) { should == 'abc123' }
84
+ its(:call_id) { should == '123abc' }
85
+ end
86
+
87
+ describe '#resume!' do
88
+ describe "when the command is paused" do
89
+ before do
90
+ command.request!
91
+ command.execute!
92
+ command.paused!
93
+ end
94
+
95
+ it "should send its command properly" do
96
+ Connection.any_instance.expects(:write).with('123abc', command.resume_action, 'abc123').returns true
97
+ command.expects :resumed!
98
+ command.resume!
99
+ end
100
+ end
101
+
102
+ describe "when the command is not paused" do
103
+ it "should raise an error" do
104
+ lambda { command.resume! }.should raise_error(InvalidActionError, "Cannot resume a Say that is not paused.")
105
+ end
106
+ end
107
+ end
108
+
109
+ describe "#resumed!" do
110
+ before do
111
+ subject.request!
112
+ subject.execute!
113
+ subject.paused!
114
+ subject.resumed!
115
+ end
116
+
117
+ its(:state_name) { should == :executing }
118
+
119
+ it "should raise a StateMachine::InvalidTransition when received a second time" do
120
+ lambda { subject.resumed! }.should raise_error(StateMachine::InvalidTransition)
121
+ end
122
+ end
123
+
124
+ describe '#stop_action' do
125
+ subject { command.stop_action }
126
+
127
+ its(:to_xml) { should == '<stop xmlns="urn:xmpp:rayo:1"/>' }
128
+ its(:component_id) { should == 'abc123' }
129
+ its(:call_id) { should == '123abc' }
130
+ end
131
+
132
+ describe '#stop!' do
133
+ describe "when the command is executing" do
134
+ before do
135
+ command.request!
136
+ command.execute!
137
+ end
138
+
139
+ it "should send its command properly" do
140
+ Connection.any_instance.expects(:write).with('123abc', command.stop_action, 'abc123')
141
+ command.stop!
142
+ end
143
+ end
144
+
145
+ describe "when the command is not executing" do
146
+ it "should raise an error" do
147
+ lambda { command.stop! }.should raise_error(InvalidActionError, "Cannot stop a Say that is not executing")
148
+ end
149
+ end
150
+ end # #stop!
151
+ end
152
+ end
153
+
154
+ describe Say::Complete::Success do
155
+ let :stanza do
156
+ <<-MESSAGE
157
+ <complete xmlns='urn:xmpp:rayo:ext:1'>
158
+ <success xmlns='urn:xmpp:tropo:say:complete:1' />
159
+ </complete>
160
+ MESSAGE
161
+ end
162
+
163
+ subject { RayoNode.import(parse_stanza(stanza).root).reason }
164
+
165
+ it { should be_instance_of Say::Complete::Success }
166
+
167
+ its(:name) { should == :success }
168
+ end
169
+ end
170
+ end
171
+ end # Punchblock
@@ -0,0 +1,153 @@
1
+ require 'spec_helper'
2
+
3
+ module Punchblock
4
+ module Component
5
+ module Tropo
6
+ describe Transfer do
7
+ it 'registers itself' do
8
+ RayoNode.class_from_registration(:transfer, 'urn:xmpp:tropo:transfer:1').should == Transfer
9
+ end
10
+
11
+ describe 'when setting options in initializer' do
12
+ subject do
13
+ Transfer.new :to => 'tel:+14045551212',
14
+ :from => 'tel:+14155551212',
15
+ :terminator => '*',
16
+ :timeout => 120000,
17
+ :answer_on_media => true,
18
+ :media => :direct,
19
+ :ring => {:voice => 'allison', :text => "Welcome to Rayo", :url => "http://it.doesnt.matter.does.it/?"}
20
+ end
21
+
22
+ its(:to) { should == %w{tel:+14045551212} }
23
+ its(:from) { should == 'tel:+14155551212' }
24
+ its(:terminator) { should == '*' }
25
+ its(:timeout) { should == 120000 }
26
+ its(:answer_on_media) { should == true }
27
+ its(:media) { should == :direct }
28
+ its(:ring) { should == Transfer::Ring.new(:voice => 'allison', :text => "Welcome to Rayo", :url => "http://it.doesnt.matter.does.it/?") }
29
+ end
30
+
31
+ it_should_behave_like 'command_headers'
32
+ end
33
+
34
+ describe "actions" do
35
+ let(:command) { Transfer.new :to => 'tel:+14045551212', :from => 'tel:+14155551212' }
36
+
37
+ before do
38
+ command.component_id = 'abc123'
39
+ command.call_id = '123abc'
40
+ command.connection = Connection.new :username => '123', :password => '123'
41
+ end
42
+
43
+ describe '#stop_action' do
44
+ subject { command.stop_action }
45
+
46
+ its(:to_xml) { should == '<stop xmlns="urn:xmpp:rayo:1"/>' }
47
+ its(:component_id) { should == 'abc123' }
48
+ its(:call_id) { should == '123abc' }
49
+ end
50
+
51
+ describe '#stop!' do
52
+ describe "when the command is executing" do
53
+ before do
54
+ command.request!
55
+ command.execute!
56
+ end
57
+
58
+ it "should send its command properly" do
59
+ Connection.any_instance.expects(:write).with('123abc', command.stop_action, 'abc123')
60
+ command.stop!
61
+ end
62
+ end
63
+
64
+ describe "when the command is not executing" do
65
+ it "should raise an error" do
66
+ lambda { command.stop! }.should raise_error(InvalidActionError, "Cannot stop a Transfer that is not executing")
67
+ end
68
+ end
69
+ end
70
+ end
71
+
72
+ describe Transfer::Complete::Success do
73
+ let :stanza do
74
+ <<-MESSAGE
75
+ <complete xmlns='urn:xmpp:rayo:ext:1'>
76
+ <success xmlns='urn:xmpp:tropo:transfer:complete:1' />
77
+ </complete>
78
+ MESSAGE
79
+ end
80
+
81
+ subject { RayoNode.import(parse_stanza(stanza).root).reason }
82
+
83
+ it { should be_instance_of Transfer::Complete::Success }
84
+
85
+ its(:name) { should == :success }
86
+ end
87
+
88
+ describe Transfer::Complete::Timeout do
89
+ let :stanza do
90
+ <<-MESSAGE
91
+ <complete xmlns='urn:xmpp:rayo:ext:1'>
92
+ <timeout xmlns='urn:xmpp:tropo:transfer:complete:1' />
93
+ </complete>
94
+ MESSAGE
95
+ end
96
+
97
+ subject { RayoNode.import(parse_stanza(stanza).root).reason }
98
+
99
+ it { should be_instance_of Transfer::Complete::Timeout }
100
+
101
+ its(:name) { should == :timeout }
102
+ end
103
+
104
+ describe Transfer::Complete::Terminator do
105
+ let :stanza do
106
+ <<-MESSAGE
107
+ <complete xmlns='urn:xmpp:rayo:ext:1'>
108
+ <terminator xmlns='urn:xmpp:tropo:transfer:complete:1' />
109
+ </complete>
110
+ MESSAGE
111
+ end
112
+
113
+ subject { RayoNode.import(parse_stanza(stanza).root).reason }
114
+
115
+ it { should be_instance_of Transfer::Complete::Terminator }
116
+
117
+ its(:name) { should == :terminator }
118
+ end
119
+
120
+ describe Transfer::Complete::Busy do
121
+ let :stanza do
122
+ <<-MESSAGE
123
+ <complete xmlns='urn:xmpp:rayo:ext:1'>
124
+ <busy xmlns='urn:xmpp:tropo:transfer:complete:1' />
125
+ </complete>
126
+ MESSAGE
127
+ end
128
+
129
+ subject { RayoNode.import(parse_stanza(stanza).root).reason }
130
+
131
+ it { should be_instance_of Transfer::Complete::Busy }
132
+
133
+ its(:name) { should == :busy }
134
+ end
135
+
136
+ describe Transfer::Complete::Reject do
137
+ let :stanza do
138
+ <<-MESSAGE
139
+ <complete xmlns='urn:xmpp:rayo:ext:1'>
140
+ <reject xmlns='urn:xmpp:tropo:transfer:complete:1' />
141
+ </complete>
142
+ MESSAGE
143
+ end
144
+
145
+ subject { RayoNode.import(parse_stanza(stanza).root).reason }
146
+
147
+ it { should be_instance_of Transfer::Complete::Reject }
148
+
149
+ its(:name) { should == :reject }
150
+ end
151
+ end
152
+ end
153
+ end # Punchblock