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,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