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.
@@ -0,0 +1,12 @@
1
+ require 'rubygems'
2
+ gem 'httparty'
3
+ require 'httparty'
4
+ require 'charset'
5
+ require 'validation'
6
+ require 'api'
7
+ require 'response'
8
+ require 'executor'
9
+ require 'error'
10
+
11
+ module TextMagic #:nodoc:
12
+ end
@@ -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
@@ -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
@@ -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
@@ -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