textmagic 0.2.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.
- 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
|