textmagic 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.document +5 -0
- data/.gitignore +5 -0
- data/LICENSE +20 -0
- data/README.rdoc +40 -0
- data/Rakefile +57 -0
- data/VERSION.yml +4 -0
- data/lib/api.rb +179 -0
- data/lib/charset.rb +23 -0
- data/lib/error.rb +27 -0
- data/lib/executor.rb +34 -0
- data/lib/response.rb +141 -0
- data/lib/textmagic.rb +12 -0
- data/lib/validation.rb +34 -0
- data/test/test_api.rb +246 -0
- data/test/test_charset.rb +28 -0
- data/test/test_error.rb +24 -0
- data/test/test_executor.rb +70 -0
- data/test/test_helper.rb +38 -0
- data/test/test_response.rb +176 -0
- data/test/test_validation.rb +99 -0
- data/textmagic.gemspec +64 -0
- metadata +80 -0
data/lib/textmagic.rb
ADDED
data/lib/validation.rb
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
module TextMagic
|
2
|
+
|
3
|
+
class API
|
4
|
+
|
5
|
+
module Validation
|
6
|
+
|
7
|
+
MAX_LENGTH_GSM = [160, 306, 459]
|
8
|
+
MAX_LENGTH_UNICODE = [70, 134, 201]
|
9
|
+
|
10
|
+
# Validates message text length. Returns +true+ if the text length is
|
11
|
+
# within the limits for the unicode/parts combination, otherwise it
|
12
|
+
# returns +false+.
|
13
|
+
#
|
14
|
+
# Note: <em>Maximum lengths for 1, 2 and 3-part GSM 03.38 messages are
|
15
|
+
# 160, 306 and 459 respectively.
|
16
|
+
# Maximum lengths for 1, 2 and 3-part unicode messages are
|
17
|
+
# 70, 134 and 201 respectively.</em>
|
18
|
+
def validate_text_length(text, unicode, parts = 3)
|
19
|
+
max_text_length = (unicode ? MAX_LENGTH_UNICODE : MAX_LENGTH_GSM)[parts - 1]
|
20
|
+
text.size <= max_text_length
|
21
|
+
end
|
22
|
+
|
23
|
+
# Validates a list of phone numbers. Returns +true+ if the list is not empty
|
24
|
+
# and all phone numbers are digit-only strings of maximum length of 15,
|
25
|
+
# otherwise it returns +false+.
|
26
|
+
def validate_phones(*phones)
|
27
|
+
phones.flatten!
|
28
|
+
return false if phones.empty?
|
29
|
+
phones.each { |phone| return false unless phone =~ /^\d{1,15}$/ }
|
30
|
+
true
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
data/test/test_api.rb
ADDED
@@ -0,0 +1,246 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
class APITest < Test::Unit::TestCase
|
4
|
+
|
5
|
+
context 'Initialization' do
|
6
|
+
|
7
|
+
should 'require username and password' do
|
8
|
+
lambda { TextMagic::API.new }.should raise_error(ArgumentError)
|
9
|
+
TextMagic::API.new(random_string, random_string)
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
context 'Account command' do
|
14
|
+
|
15
|
+
setup do
|
16
|
+
@balance = 0.01 * rand(1e4)
|
17
|
+
@username, @password = random_string, random_string
|
18
|
+
@api = TextMagic::API.new(@username, @password)
|
19
|
+
end
|
20
|
+
|
21
|
+
should 'call Executor execute with correct arguments' do
|
22
|
+
TextMagic::API::Executor.expects(:execute).with('account', @username, @password).returns({})
|
23
|
+
@api.account
|
24
|
+
end
|
25
|
+
|
26
|
+
should 'return a hash extended with TextMagic::API::Response::Account' do
|
27
|
+
TextMagic::API::Executor.expects(:execute).returns({ 'balance' => @balance.to_s })
|
28
|
+
response = @api.account
|
29
|
+
response.class.should == Hash
|
30
|
+
response.is_a?(TextMagic::API::Response::Account).should == true
|
31
|
+
end
|
32
|
+
|
33
|
+
should 'return a hash with balance value' do
|
34
|
+
TextMagic::API::Executor.expects(:execute).returns({ 'balance' => @balance.to_s })
|
35
|
+
response = @api.account
|
36
|
+
response['balance'].should be_close(@balance, 1e-10)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
context 'Send command' do
|
41
|
+
|
42
|
+
setup do
|
43
|
+
@username, @password = random_string, random_string
|
44
|
+
@text, @phone = random_string, random_phone
|
45
|
+
@api = TextMagic::API.new(@username, @password)
|
46
|
+
TextMagic::API::Executor.stubs(:execute)
|
47
|
+
end
|
48
|
+
|
49
|
+
should 'call Executor execute with correct arguments' do
|
50
|
+
TextMagic::API::Executor.expects(:execute).with('send', @username, @password, :text => @text, :phone => @phone, :unicode => 0)
|
51
|
+
@api.send(@text, @phone)
|
52
|
+
end
|
53
|
+
|
54
|
+
should 'join multiple phone numbers supplied as an array' do
|
55
|
+
phones = Array.new(3) { random_phone }
|
56
|
+
TextMagic::API::Executor.expects(:execute).with('send', @username, @password, :text => @text, :phone => phones.join(','), :unicode => 0)
|
57
|
+
@api.send(@text, phones)
|
58
|
+
end
|
59
|
+
|
60
|
+
should 'join multiple phone numbers supplied as arguments' do
|
61
|
+
phones = Array.new(3) { random_phone }
|
62
|
+
TextMagic::API::Executor.expects(:execute).with('send', @username, @password, :text => @text, :phone => phones.join(','), :unicode => 0)
|
63
|
+
@api.send(@text, *phones)
|
64
|
+
end
|
65
|
+
|
66
|
+
should 'replace true with 1 for unicode' do
|
67
|
+
TextMagic::API::Executor.expects(:execute).with('send', @username, @password, :text => @text, :phone => @phone, :unicode => 1)
|
68
|
+
@api.send(@text, @phone, :unicode => true)
|
69
|
+
end
|
70
|
+
|
71
|
+
should 'set unicode value to 0 if it is not set to by user and text contains only characters from GSM 03.38 charset' do
|
72
|
+
TextMagic::API::Executor.expects(:execute).with('send', @username, @password, :text => @text, :phone => @phone, :unicode => 0).times(2)
|
73
|
+
@api.send(@text, @phone)
|
74
|
+
@api.send(@text, @phone, :unicode => false)
|
75
|
+
end
|
76
|
+
|
77
|
+
should 'raise an error if unicode is set to 0 and text contains characters outside of GSM 03.38 charset' do
|
78
|
+
text = 'Вильма Привет'
|
79
|
+
lambda { @api.send(text, @phone, :unicode => false) }.should raise_error(TextMagic::API::Error)
|
80
|
+
end
|
81
|
+
|
82
|
+
should 'raise an error if unicode value is not valid' do
|
83
|
+
lambda { @api.send(@text, @phone, :unicode => 2 + rand(10)) }.should raise_error(TextMagic::API::Error)
|
84
|
+
lambda { @api.send(@text, @phone, :unicode => random_string) }.should raise_error(TextMagic::API::Error)
|
85
|
+
end
|
86
|
+
|
87
|
+
should 'raise an error if no phone numbers are specified' do
|
88
|
+
lambda { @api.send(@text) }.should raise_error(TextMagic::API::Error)
|
89
|
+
lambda { @api.send(@text, []) }.should raise_error(TextMagic::API::Error)
|
90
|
+
end
|
91
|
+
|
92
|
+
should 'raise an error if format of any of the specified phone numbers is invalid' do
|
93
|
+
TextMagic::API.expects(:validate_phones).returns(false)
|
94
|
+
lambda { @api.send(@text, random_string) }.should raise_error(TextMagic::API::Error)
|
95
|
+
end
|
96
|
+
|
97
|
+
should 'raise an error if text is empty' do
|
98
|
+
lambda { @api.send('', @phone) }.should raise_error(TextMagic::API::Error)
|
99
|
+
end
|
100
|
+
|
101
|
+
should 'raise an error if text is too long' do
|
102
|
+
TextMagic::API.expects(:validate_text_length).returns(false)
|
103
|
+
lambda { @api.send(@text, @phone) }.should raise_error(TextMagic::API::Error)
|
104
|
+
end
|
105
|
+
|
106
|
+
should 'return a hash extended with TextMagic::API::Response::Send' do
|
107
|
+
message_id = random_string
|
108
|
+
TextMagic::API::Executor.expects(:execute).returns({ 'message_id' => { message_id => @phone }, 'sent_text' => @text, 'parts_count' => 1 })
|
109
|
+
response = @api.send(@text, @phone)
|
110
|
+
response.class.should == Hash
|
111
|
+
response.is_a?(TextMagic::API::Response::Send).should == true
|
112
|
+
end
|
113
|
+
|
114
|
+
should 'return a hash with message_id_hash, message_ids, sent_text and parts_count values' do
|
115
|
+
message_id = random_string
|
116
|
+
TextMagic::API::Executor.expects(:execute).returns({ 'message_id' => { message_id => @phone }, 'sent_text' => @text, 'parts_count' => 1 })
|
117
|
+
response = @api.send(@text, @phone)
|
118
|
+
response['message_id_hash'].should == { @phone => message_id }
|
119
|
+
response['message_ids'].should == [message_id]
|
120
|
+
response['parts_count'].should == 1
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
context 'Message status command' do
|
125
|
+
|
126
|
+
setup do
|
127
|
+
@username, @password = random_string, random_string
|
128
|
+
@api = TextMagic::API.new(@username, @password)
|
129
|
+
end
|
130
|
+
|
131
|
+
should 'call Executor execute with correct arguments' do
|
132
|
+
id = random_string
|
133
|
+
TextMagic::API::Executor.expects(:execute).with('message_status', @username, @password, :ids => id)
|
134
|
+
@api.message_status(id)
|
135
|
+
end
|
136
|
+
|
137
|
+
should 'join ids supplied as array' do
|
138
|
+
ids = Array.new(3) { random_string }
|
139
|
+
TextMagic::API::Executor.expects(:execute).with('message_status', @username, @password, :ids => ids.join(','))
|
140
|
+
@api.message_status(ids)
|
141
|
+
end
|
142
|
+
|
143
|
+
should 'join ids supplied as arguments' do
|
144
|
+
ids = Array.new(3) { random_string }
|
145
|
+
TextMagic::API::Executor.expects(:execute).with('message_status', @username, @password, :ids => ids.join(','))
|
146
|
+
@api.message_status(*ids)
|
147
|
+
end
|
148
|
+
|
149
|
+
should 'not call execute and should raise an exception if no ids are specified' do
|
150
|
+
TextMagic::API::Executor.expects(:execute).never
|
151
|
+
lambda { @api.message_status }.should raise_error(TextMagic::API::Error)
|
152
|
+
end
|
153
|
+
|
154
|
+
should 'return a hash extended with TextMagic::API::Response::MessageStatus' do
|
155
|
+
TextMagic::API::Executor.expects(:execute).returns({ '8659912' => {} })
|
156
|
+
response = @api.message_status(random_string)
|
157
|
+
response.class.should == Hash
|
158
|
+
response.is_a?(TextMagic::API::Response::MessageStatus).should == true
|
159
|
+
end
|
160
|
+
|
161
|
+
should 'return a hash with message ids as keys' do
|
162
|
+
TextMagic::API::Executor.expects(:execute).returns({ '8659912' => {} })
|
163
|
+
response = @api.message_status(random_string)
|
164
|
+
response['8659912'].should == {}
|
165
|
+
end
|
166
|
+
end
|
167
|
+
|
168
|
+
context 'Receive command' do
|
169
|
+
|
170
|
+
setup do
|
171
|
+
@username, @password = random_string, random_string
|
172
|
+
@api = TextMagic::API.new(@username, @password)
|
173
|
+
end
|
174
|
+
|
175
|
+
should 'call Executor execute with correct arguments' do
|
176
|
+
TextMagic::API::Executor.expects(:execute).with('receive', @username, @password, :last_retrieved_id => nil)
|
177
|
+
@api.receive
|
178
|
+
end
|
179
|
+
|
180
|
+
should 'accept last_retrieved_id optional value' do
|
181
|
+
last_retrieved_id = rand(1e10)
|
182
|
+
TextMagic::API::Executor.expects(:execute).with('receive', @username, @password, :last_retrieved_id => last_retrieved_id)
|
183
|
+
@api.receive(last_retrieved_id)
|
184
|
+
end
|
185
|
+
|
186
|
+
should 'return a hash extended with TextMagic::API::Response::Receive' do
|
187
|
+
TextMagic::API::Executor.expects(:execute).returns({ 'messages' => [], 'unread' => 0 })
|
188
|
+
response = @api.receive
|
189
|
+
response.class.should == Hash
|
190
|
+
response.is_a?(TextMagic::API::Response::Receive).should == true
|
191
|
+
end
|
192
|
+
|
193
|
+
should 'return a hash with unread and messages values' do
|
194
|
+
TextMagic::API::Executor.expects(:execute).returns({ 'messages' => [], 'unread' => 0 })
|
195
|
+
response = @api.receive
|
196
|
+
response['unread'].should == 0
|
197
|
+
response['messages'].should == []
|
198
|
+
end
|
199
|
+
end
|
200
|
+
|
201
|
+
context 'Delete reply command' do
|
202
|
+
|
203
|
+
setup do
|
204
|
+
@username, @password = random_string, random_string
|
205
|
+
@api = TextMagic::API.new(@username, @password)
|
206
|
+
end
|
207
|
+
|
208
|
+
should 'call Executor execute with correct arguments' do
|
209
|
+
id = random_string
|
210
|
+
TextMagic::API::Executor.expects(:execute).with('delete_reply', @username, @password, :ids => id)
|
211
|
+
@api.delete_reply(id)
|
212
|
+
end
|
213
|
+
|
214
|
+
should 'join ids supplied as array' do
|
215
|
+
ids = Array.new(3) { random_string }
|
216
|
+
TextMagic::API::Executor.expects(:execute).with('delete_reply', @username, @password, :ids => ids.join(','))
|
217
|
+
@api.delete_reply(ids)
|
218
|
+
end
|
219
|
+
|
220
|
+
should 'join ids supplied as arguments' do
|
221
|
+
ids = Array.new(3) { random_string }
|
222
|
+
TextMagic::API::Executor.expects(:execute).with('delete_reply', @username, @password, :ids => ids.join(','))
|
223
|
+
@api.delete_reply(*ids)
|
224
|
+
end
|
225
|
+
|
226
|
+
should 'not call execute and should raise an exception if no ids are specified' do
|
227
|
+
TextMagic::API::Executor.expects(:execute).never
|
228
|
+
lambda { @api.delete_reply }.should raise_error(TextMagic::API::Error)
|
229
|
+
end
|
230
|
+
|
231
|
+
should 'return a hash extended with TextMagic::API::Response::DeleteReply' do
|
232
|
+
ids = Array.new(3) { random_string }
|
233
|
+
TextMagic::API::Executor.expects(:execute).returns({ 'deleted' => ids })
|
234
|
+
response = @api.delete_reply(ids)
|
235
|
+
response.class.should == Hash
|
236
|
+
response.is_a?(TextMagic::API::Response::DeleteReply).should == true
|
237
|
+
end
|
238
|
+
|
239
|
+
should 'return a hash with deleted value' do
|
240
|
+
ids = Array.new(3) { random_string }
|
241
|
+
TextMagic::API::Executor.expects(:execute).returns({ 'deleted' => ids })
|
242
|
+
response = @api.delete_reply(ids)
|
243
|
+
response.deleted.should == ids
|
244
|
+
end
|
245
|
+
end
|
246
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
require 'json'
|
3
|
+
|
4
|
+
class CharsetTest < Test::Unit::TestCase
|
5
|
+
|
6
|
+
context 'is_gsm method' do
|
7
|
+
|
8
|
+
should 'return true if all characters are in GSM 03.38 charset' do
|
9
|
+
TextMagic::API.is_gsm(('a'..'z').to_a.join).should == true
|
10
|
+
TextMagic::API.is_gsm(('A'..'Z').to_a.join).should == true
|
11
|
+
TextMagic::API.is_gsm(('0'..'9').to_a.join).should == true
|
12
|
+
TextMagic::API.is_gsm("@£$¥€").should == true
|
13
|
+
TextMagic::API.is_gsm("\n\r\e\f\\\"").should == true
|
14
|
+
TextMagic::API.is_gsm("èéùìòÇØøÅåÉÆæß").should == true
|
15
|
+
TextMagic::API.is_gsm("ΔΦΓΛΩΠΨΣΘΞ").should == true
|
16
|
+
TextMagic::API.is_gsm("^{}[~]| !#¤%&'()").should == true
|
17
|
+
TextMagic::API.is_gsm("*+,-./_:;<=>?¡¿§").should == true
|
18
|
+
TextMagic::API.is_gsm("ÖÑÜöñüàäÄ").should == true
|
19
|
+
end
|
20
|
+
|
21
|
+
should 'return false if some characters are outside of GSM 03.38 charset' do
|
22
|
+
TextMagic::API.is_gsm('Arabic: مرحبا فيلما').should == false
|
23
|
+
TextMagic::API.is_gsm('Chinese: 您好').should == false
|
24
|
+
TextMagic::API.is_gsm('Cyrilic: Вильма Привет').should == false
|
25
|
+
TextMagic::API.is_gsm('Thai: สวัสดี').should == false
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
data/test/test_error.rb
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
class ErrorTest < Test::Unit::TestCase
|
4
|
+
|
5
|
+
context 'Initialization' do
|
6
|
+
|
7
|
+
setup do
|
8
|
+
@code = rand(1e3)
|
9
|
+
@message = random_string
|
10
|
+
end
|
11
|
+
|
12
|
+
should 'accept a hash with error_code and error_message' do
|
13
|
+
e = TextMagic::API::Error.new('error_code' => @code, 'error_message' => @message)
|
14
|
+
e.code.should == @code
|
15
|
+
e.message.should == @message
|
16
|
+
end
|
17
|
+
|
18
|
+
should 'accept error_code and error_message' do
|
19
|
+
e = TextMagic::API::Error.new(@code, @message)
|
20
|
+
e.code.should == @code
|
21
|
+
e.message.should == @message
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
require 'json'
|
3
|
+
|
4
|
+
class ExecutorTest < Test::Unit::TestCase
|
5
|
+
|
6
|
+
context "execute method" do
|
7
|
+
|
8
|
+
setup do
|
9
|
+
FakeWeb.allow_net_connect = false
|
10
|
+
|
11
|
+
@username, @password = random_string, random_string
|
12
|
+
@command, @options = random_string, random_hash
|
13
|
+
@uri = build_uri(@command, @username, @password, @options)
|
14
|
+
end
|
15
|
+
|
16
|
+
should 'not send HTTP request without command' do
|
17
|
+
TextMagic::API::Executor.expects(:get).never
|
18
|
+
lambda {
|
19
|
+
TextMagic::API::Executor.execute(nil, @username, @password, @options)
|
20
|
+
}.should raise_error(TextMagic::API::Error)
|
21
|
+
end
|
22
|
+
|
23
|
+
should 'not send HTTP request without username' do
|
24
|
+
TextMagic::API::Executor.expects(:get).never
|
25
|
+
lambda {
|
26
|
+
TextMagic::API::Executor.execute(@command, nil, @password, @options)
|
27
|
+
}.should raise_error(TextMagic::API::Error)
|
28
|
+
end
|
29
|
+
|
30
|
+
should 'not send HTTP request without password' do
|
31
|
+
TextMagic::API::Executor.expects(:get).never
|
32
|
+
lambda {
|
33
|
+
TextMagic::API::Executor.execute(@command, @username, nil, @options)
|
34
|
+
}.should raise_error(TextMagic::API::Error)
|
35
|
+
end
|
36
|
+
|
37
|
+
should 'send a GET request to proper uri' do
|
38
|
+
response = random_string
|
39
|
+
FakeWeb.register_uri(:get, @uri, :string => response)
|
40
|
+
TextMagic::API::Executor.execute(@command, @username, @password, @options)
|
41
|
+
end
|
42
|
+
|
43
|
+
should 'not send parameters with empty keys' do
|
44
|
+
options_with_empty_values = @options.merge(nil => random_string, '' => random_string)
|
45
|
+
TextMagic::API::Executor.expects(:get).with('/api', :query => @options, :format => :json)
|
46
|
+
TextMagic::API::Executor.execute(@command, @username, @password, options_with_empty_values)
|
47
|
+
end
|
48
|
+
|
49
|
+
should 'not send parameters with empty values' do
|
50
|
+
options_with_empty_values = @options.merge(random_string => nil, random_string => '')
|
51
|
+
TextMagic::API::Executor.expects(:get).with('/api', :query => @options, :format => :json)
|
52
|
+
TextMagic::API::Executor.execute(@command, @username, @password, options_with_empty_values)
|
53
|
+
end
|
54
|
+
|
55
|
+
should 'raise an error if the response contains error_code' do
|
56
|
+
response = "{error_code:#{1 + rand(10)}}"
|
57
|
+
FakeWeb.register_uri(:get, @uri, :string => response)
|
58
|
+
lambda {
|
59
|
+
TextMagic::API::Executor.execute(@command, @username, @password, @options)
|
60
|
+
}.should raise_error(TextMagic::API::Error)
|
61
|
+
end
|
62
|
+
|
63
|
+
should 'return a hash with values from the response' do
|
64
|
+
hash = random_hash
|
65
|
+
FakeWeb.register_uri(:get, @uri, :string => hash.to_json)
|
66
|
+
response = TextMagic::API::Executor.execute(@command, @username, @password, @options)
|
67
|
+
response.should == hash
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
data/test/test_helper.rb
ADDED
@@ -0,0 +1,38 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'test/unit'
|
3
|
+
require 'shoulda'
|
4
|
+
require 'mocha'
|
5
|
+
require 'matchy'
|
6
|
+
require 'fakeweb'
|
7
|
+
|
8
|
+
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
9
|
+
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
10
|
+
|
11
|
+
require 'textmagic'
|
12
|
+
|
13
|
+
class Test::Unit::TestCase
|
14
|
+
end
|
15
|
+
|
16
|
+
def random_string(legth = 5)
|
17
|
+
Array.new(legth) { rand(36).to_s(36) }.join
|
18
|
+
end
|
19
|
+
|
20
|
+
def random_phone
|
21
|
+
rand(10 ** 12).to_s
|
22
|
+
end
|
23
|
+
|
24
|
+
def random_hash
|
25
|
+
hash = {}
|
26
|
+
3.times { hash[random_string] = random_string }
|
27
|
+
hash
|
28
|
+
end
|
29
|
+
|
30
|
+
def build_uri(command, username, password, options = {})
|
31
|
+
options.merge!(:cmd => command, :username => username, :password => password)
|
32
|
+
uri = "http://www.textmagic.com/app/api?"
|
33
|
+
uri << options.collect { |key, value| "#{key}=#{value}"}.join('&')
|
34
|
+
end
|
35
|
+
|
36
|
+
def load_response(filename)
|
37
|
+
File.read(File.join(File.dirname(__FILE__), 'fixtures', filename) + '.json')
|
38
|
+
end
|