lookout-clickatell 0.8.3

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,31 @@
1
+ require "cgi"
2
+ module Clickatell
3
+ class API
4
+
5
+ # Represents a Clickatell HTTP gateway command in the form
6
+ # of a complete URL (the raw, low-level request).
7
+ class Command
8
+ API_SERVICE_HOST = 'api.clickatell.com'
9
+
10
+ def initialize(command_name, service = 'http', opts={})
11
+ @command_name = command_name
12
+ @service = service
13
+ @options = { :secure => false }.merge(opts)
14
+ end
15
+
16
+ # Returns a URL for the given parameters (a hash).
17
+ def with_params(param_hash)
18
+ param_string = '?' + param_hash.map { |key, value| "#{::CGI.escape(key.to_s)}=#{::CGI.escape(value.to_s)}" }.sort.join('&')
19
+ return URI.parse(File.join(api_service_uri, @command_name + param_string))
20
+ end
21
+
22
+ protected
23
+ def api_service_uri
24
+ protocol = @options[:secure] ? 'https' : 'http'
25
+ api_service_host = ((Clickatell::API.api_service_host.nil? || Clickatell::API.api_service_host.empty?) ? API_SERVICE_HOST : Clickatell::API.api_service_host)
26
+ return "#{protocol}://#{api_service_host}/#{@service}/"
27
+ end
28
+ end
29
+
30
+ end
31
+ end
@@ -0,0 +1,91 @@
1
+ require 'net/http'
2
+ require 'net/https'
3
+
4
+ module Clickatell
5
+ class API
6
+
7
+ class FakeHttpResponse
8
+ def body
9
+ "test"
10
+ end
11
+ end
12
+
13
+ # Used to run commands agains the Clickatell gateway.
14
+ class CommandExecutor
15
+
16
+ def initialize(authentication_hash)
17
+ @authentication_hash = authentication_hash
18
+
19
+ allow_request_recording if in_test_mode?
20
+ end
21
+
22
+ def in_test_mode?
23
+ API.test_mode
24
+ end
25
+
26
+ # Builds a command object and sends it using HTTP GET.
27
+ # Will output URLs as they are requested to stdout when
28
+ # debugging is enabled.
29
+ def execute(command_name, service, parameters={})
30
+ request_uri = command(command_name, service, parameters)
31
+ puts "[debug] Sending request to #{request_uri}" if API.debug_mode
32
+ result = get_response(request_uri)
33
+ if result.is_a?(Array)
34
+ result = result.first
35
+ end
36
+ result
37
+ end
38
+
39
+ protected
40
+ def command(command_name, service, parameters) #:nodoc:
41
+ Command.new(command_name, service, :secure => API.secure_mode).with_params(
42
+ parameters.merge(@authentication_hash)
43
+ )
44
+ end
45
+
46
+ def get_response(uri)
47
+ if in_test_mode?
48
+ sms_requests << uri
49
+ [FakeHttpResponse.new]
50
+ else
51
+ request = [uri.path, uri.query].join('?')
52
+
53
+ if API.proxy_host
54
+ http = Net::HTTP::Proxy(API.proxy_host, API.proxy_port, API.proxy_username, API.proxy_password)
55
+ else
56
+ http = Net::HTTP.new(uri.host, uri.port)
57
+ end
58
+
59
+ # If we're using a secure connection, validate the ssl
60
+ # certificate
61
+ if http.use_ssl = (uri.scheme == 'https')
62
+ http.ca_path = API.ca_path
63
+ http.verify_mode = OpenSSL::SSL::VERIFY_PEER
64
+ end
65
+
66
+ if API.proxy_host
67
+ http.start(uri.host) do |http|
68
+ resp, body = http.get(request)
69
+ end
70
+ else
71
+ http.start do |http|
72
+ resp, body = http.get(request)
73
+ end
74
+ end
75
+
76
+ end
77
+ end
78
+
79
+ private
80
+
81
+ def allow_request_recording
82
+ class << self
83
+ define_method :sms_requests do
84
+ @sms_requests ||= []
85
+ end
86
+ end
87
+ end
88
+ end
89
+
90
+ end
91
+ end
@@ -0,0 +1,29 @@
1
+ module Clickatell
2
+ class API
3
+
4
+ # Clickatell API Error exception.
5
+ class Error < StandardError
6
+ attr_reader :code, :message
7
+
8
+ def initialize(code, message)
9
+ @code, @message = code, message
10
+ end
11
+
12
+ def to_s
13
+ "#{@code}: #{@message}"
14
+ end
15
+
16
+ # Creates a new Error from a Clickatell HTTP response string
17
+ # e.g.:
18
+ #
19
+ # Error.parse("ERR: 001, Authentication error")
20
+ # # => #<Clickatell::API::Error code='001' message='Authentication error'>
21
+ def self.parse(error_string)
22
+ error_details = error_string.split(':').last.strip
23
+ code, message = error_details.split(',').map { |s| s.strip }
24
+ self.new(code, message)
25
+ end
26
+ end
27
+
28
+ end
29
+ end
@@ -0,0 +1,26 @@
1
+ module Clickatell
2
+ class API
3
+
4
+ class MessageStatus
5
+ STATUS_MAP = {
6
+ '001' => 'Message unknown',
7
+ '002' => 'Message queued',
8
+ '003' => 'Delivered to gateway',
9
+ '004' => 'Received by recipient',
10
+ '005' => 'Error with message',
11
+ '006' => 'User cancelled messaged delivery',
12
+ '007' => 'Error delivering message',
13
+ '008' => 'OK',
14
+ '009' => 'Routing error',
15
+ '010' => 'Message expired',
16
+ '011' => 'Message queued for later delivery',
17
+ '012' => 'Out of credit'
18
+ }
19
+
20
+ def self.[](code)
21
+ STATUS_MAP[code]
22
+ end
23
+ end
24
+
25
+ end
26
+ end
@@ -0,0 +1,32 @@
1
+ require 'yaml'
2
+
3
+ module Clickatell
4
+
5
+ # Used to parse HTTP responses returned from Clickatell API calls.
6
+ class Response
7
+
8
+ class << self
9
+ PARSE_REGEX = /[A-Za-z0-9]+:.*?(?:(?=[A-Za-z0-9]+:)|$)/
10
+
11
+ # Returns the HTTP response body data as a hash.
12
+ def parse(http_response)
13
+ return { 'OK' => 'session_id' } if API.test_mode
14
+
15
+ if http_response.body.scan(/ERR/).any?
16
+ raise Clickatell::API::Error.parse(http_response.body)
17
+ end
18
+ results = http_response.body.split("\n").map do |line|
19
+ # YAML.load converts integer strings that have leading zeroes into integers
20
+ # using octal rather than decimal. This isn't what we want, so we'll strip out any
21
+ # leading zeroes in numbers here.
22
+ response_fields = line.scan(PARSE_REGEX)
23
+ response_fields = response_fields.collect { |field| field.gsub(/\b0+(\d+)\b/, '\1') }
24
+ YAML.load(response_fields.join("\n"))
25
+ end
26
+ results.length == 1 ? results.first : results
27
+ end
28
+
29
+ end
30
+
31
+ end
32
+ end
@@ -0,0 +1,6 @@
1
+ module Clickatell
2
+ module Utility
3
+ end
4
+ end
5
+
6
+ require File.join(File.dirname(__FILE__), *%w[utility/options])
@@ -0,0 +1,109 @@
1
+ require 'optparse'
2
+ require 'ostruct'
3
+
4
+ module Clickatell
5
+ module Utility
6
+ class Options #:nodoc:
7
+ class << self
8
+
9
+ def parse(args)
10
+ @options = self.default_options
11
+ parser = OptionParser.new do |opts|
12
+ opts.banner = "Usage: sms [options] recipient(s) message"
13
+ opts.separator " Recipients can be a comma-separated list, up to 100 max."
14
+ opts.separator ""
15
+ opts.separator "Specific options:"
16
+
17
+ opts.on('-u', '--username USERNAME',
18
+ "Specify the clickatell username (overrides ~/.clickatell setting)") do |username|
19
+ @options.username = username
20
+ end
21
+
22
+ opts.on('-p', '--password PASSWORD',
23
+ "Specify the clickatell password (overrides ~/.clickatell setting)") do |password|
24
+ @options.password = password
25
+ end
26
+
27
+ opts.on('-k', '--apikey API_KEY',
28
+ "Specify the clickatell API key (overrides ~/.clickatell setting)") do |key|
29
+ @options.api_key = key
30
+ end
31
+
32
+ opts.on('-f', '--from NAME_OR_NUMBER',
33
+ "Specify the name or number that the SMS will appear from") do |from|
34
+ @options.from = from
35
+ end
36
+
37
+ opts.on('-b', '--show-balance',
38
+ "Shows the total number of credits remaining on your account") do
39
+ @options.show_balance = true
40
+ end
41
+
42
+ opts.on('-s', '--status MESSAGE_ID',
43
+ "Displays the status of the specified message.") do |message_id|
44
+ @options.message_id = message_id
45
+ @options.show_status = true
46
+ end
47
+
48
+ opts.on('-S', '--secure',
49
+ "Sends request using HTTPS") do
50
+ Clickatell::API.secure_mode = true
51
+ end
52
+
53
+ opts.on('-d', '--debug') do
54
+ Clickatell::API.debug_mode = true
55
+ end
56
+
57
+ opts.on_tail('-h', '--help', "Show this message") do
58
+ puts opts
59
+ exit
60
+ end
61
+
62
+ opts.on_tail('-v', '--version') do
63
+ puts "Ruby Clickatell SMS Utility #{Clickatell::VERSION}"
64
+ exit
65
+ end
66
+ end
67
+
68
+ @options.recipient = args[-2].split(',').map { |r| r.gsub(/^\+/, '') } rescue nil
69
+ @options.message = args[-1]
70
+
71
+ parser.parse!(args)
72
+
73
+ if (@options.message.nil? || @options.recipient.nil?) && send_message?
74
+ puts "You must specify a recipient and message!"
75
+ puts parser
76
+ exit
77
+ end
78
+
79
+ return @options
80
+
81
+ rescue OptionParser::MissingArgument => e
82
+ switch_given = e.message.split(':').last.strip
83
+ puts "The #{switch_given} option requires an argument."
84
+ puts parser
85
+ exit
86
+ end
87
+
88
+ def default_options
89
+ options = OpenStruct.new
90
+ config_file = File.open(File.join(ENV['HOME'], '.clickatell'))
91
+ config = YAML.load(config_file)
92
+ options.username = config['username']
93
+ options.password = config['password']
94
+ options.api_key = config['api_key']
95
+ options.from = config['from']
96
+ return options
97
+ rescue Errno::ENOENT
98
+ return options
99
+ end
100
+
101
+ def send_message?
102
+ (@options.show_status.nil? &&
103
+ @options.show_balance.nil?)
104
+ end
105
+
106
+ end
107
+ end
108
+ end
109
+ end
@@ -0,0 +1,3 @@
1
+ module Clickatell
2
+ VERSION = "0.8.3"
3
+ end
@@ -0,0 +1,15 @@
1
+ class Hash
2
+ # Returns a new hash containing only the keys specified
3
+ # that exist in the current hash.
4
+ #
5
+ # {:a => '1', :b => '2', :c => '3'}.only(:a, :c)
6
+ # # => {:a => '1', :c => '3'}
7
+ #
8
+ # Keys that do not exist in the original hash are ignored.
9
+ def only(*keys)
10
+ inject( {} ) do |new_hash, (key, value)|
11
+ new_hash[key] = value if keys.include?(key)
12
+ new_hash
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,67 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'rubygems'
4
+ require 'redcloth'
5
+ require 'syntax/convertors/html'
6
+ require 'erb'
7
+ require File.dirname(__FILE__) + '/../lib/clickatell/version.rb'
8
+
9
+ version = Clickatell::VERSION::STRING
10
+ download = 'http://rubyforge.org/projects/clickatell'
11
+
12
+ class Fixnum
13
+ def ordinal
14
+ # teens
15
+ return 'th' if (10..19).include?(self % 100)
16
+ # others
17
+ case self % 10
18
+ when 1: return 'st'
19
+ when 2: return 'nd'
20
+ when 3: return 'rd'
21
+ else return 'th'
22
+ end
23
+ end
24
+ end
25
+
26
+ class Time
27
+ def pretty
28
+ return "#{mday}#{mday.ordinal} #{strftime('%B')} #{year}"
29
+ end
30
+ end
31
+
32
+ def convert_syntax(syntax, source)
33
+ return Syntax::Convertors::HTML.for_syntax(syntax).convert(source).gsub(%r!^<pre>|</pre>$!,'')
34
+ end
35
+
36
+ if ARGV.length >= 1
37
+ src, template = ARGV
38
+ template ||= File.dirname(__FILE__) + '/../website/template.rhtml'
39
+
40
+ else
41
+ puts("Usage: #{File.split($0).last} source.txt [template.rhtml] > output.html")
42
+ exit!
43
+ end
44
+
45
+ template = ERB.new(File.open(template).read)
46
+
47
+ title = nil
48
+ body = nil
49
+ File.open(src) do |fsrc|
50
+ title_text = fsrc.readline
51
+ body_text = fsrc.read
52
+ syntax_items = []
53
+ body_text.gsub!(%r!<(pre|code)[^>]*?syntax=['"]([^'"]+)[^>]*>(.*?)</>!m){
54
+ ident = syntax_items.length
55
+ element, syntax, source = $1, $2, $3
56
+ syntax_items << "<#{element} class='syntax'>#{convert_syntax(syntax, source)}</#{element}>"
57
+ "syntax-temp-#{ident}"
58
+ }
59
+ title = RedCloth.new(title_text).to_html.gsub(%r!<.*?>!,'').strip
60
+ body = RedCloth.new(body_text).to_html
61
+ body.gsub!(%r!(?:<pre><code>)?syntax-temp-(d+)(?:</code></pre>)?!){ syntax_items[$1.to_i] }
62
+ end
63
+ stat = File.stat(src)
64
+ created = stat.ctime
65
+ modified = stat.mtime
66
+
67
+ $stdout << template.result(binding)
@@ -0,0 +1,259 @@
1
+ require File.dirname(__FILE__) + '/spec_helper'
2
+ require File.dirname(__FILE__) + '/../lib/clickatell'
3
+
4
+ class FakeHttp
5
+ def start(&block)
6
+ yield self
7
+ end
8
+ end
9
+
10
+ module Clickatell
11
+
12
+ describe "API Command" do
13
+ before do
14
+ @command = API::Command.new('cmdname')
15
+ end
16
+
17
+ after do
18
+ Clickatell::API.api_service_host = nil
19
+ end
20
+
21
+ it "should return encoded URL for the specified command and parameters" do
22
+ url = @command.with_params(:param_one => 'abc', :param_two => '123')
23
+ url.should == URI.parse("http://api.clickatell.com/http/cmdname?param_one=abc&param_two=123")
24
+ end
25
+
26
+ it "should URL encode any special characters in parameters" do
27
+ url = @command.with_params(:param_one => 'abc', :param_two => 'hello world & goodbye cruel world <grin>')
28
+ url.should == URI.parse("http://api.clickatell.com/http/cmdname?param_one=abc&param_two=hello+world+%26+goodbye+cruel+world+%3Cgrin%3E")
29
+ end
30
+
31
+ it "should use a custom host when constructing command URLs if specified" do
32
+ Clickatell::API.api_service_host = 'api.clickatell-custom.co.uk'
33
+ url = @command.with_params(:param_one => 'abc', :param_two => '123')
34
+ url.should == URI.parse("http://api.clickatell-custom.co.uk/http/cmdname?param_one=abc&param_two=123")
35
+ end
36
+
37
+ it "should use the default host if specified custom host is nil" do
38
+ Clickatell::API.api_service_host = nil
39
+ url = @command.with_params(:param_one => 'abc', :param_two => '123')
40
+ url.should == URI.parse("http://api.clickatell.com/http/cmdname?param_one=abc&param_two=123")
41
+ end
42
+
43
+ it "should use the default host if specified custom host is an empty string" do
44
+ Clickatell::API.api_service_host = ''
45
+ url = @command.with_params(:param_one => 'abc', :param_two => '123')
46
+ url.should == URI.parse("http://api.clickatell.com/http/cmdname?param_one=abc&param_two=123")
47
+ end
48
+ end
49
+
50
+ describe "Secure API Command" do
51
+ before do
52
+ @command = API::Command.new('cmdname', 'http', :secure => true)
53
+ end
54
+
55
+ it "should use HTTPS" do
56
+ url = @command.with_params(:param_one => 'abc', :param_two => '123')
57
+ url.should == URI.parse("https://api.clickatell.com/http/cmdname?param_one=abc&param_two=123")
58
+ end
59
+ end
60
+
61
+ describe "Command executor" do
62
+ before do
63
+ API.proxy_host = false
64
+ API.test_mode = false
65
+ end
66
+ it "should create an API command with the given params" do
67
+ executor = API::CommandExecutor.new(:session_id => '12345')
68
+ executor.stubs(:get_response).returns([])
69
+ API.secure_mode = false
70
+ API::Command.expects(:new).with('cmdname', 'http', :secure => false).returns(command = stub('Command'))
71
+ command.expects(:with_params).with(:param_one => 'foo', :session_id => '12345').returns(stub_everything('URI'))
72
+ executor.execute('cmdname', 'http', :param_one => 'foo')
73
+ end
74
+
75
+ it "should send the URI generated by the created command via HTTP get and return the response" do
76
+ executor = API::CommandExecutor.new(:session_id => '12345')
77
+ command_uri = URI.parse('http://clickatell.com:8080/path?foo=bar')
78
+ API::Command.stubs(:new).returns(command = stub('Command', :with_params => command_uri))
79
+ Net::HTTP.stubs(:new).with(command_uri.host, command_uri.port).returns(http = FakeHttp.new)
80
+ http.stubs(:use_ssl=)
81
+ http.stubs(:get).with('/path?foo=bar').returns([response = stub('HTTP Response'), 'response body'])
82
+ executor.execute('cmdname', 'http').should == response
83
+ end
84
+
85
+ it "should send the command over a secure HTTPS connection if :secure option is set to true" do
86
+ API.secure_mode = true
87
+ executor = API::CommandExecutor.new({:session_id => '12345'})
88
+ Net::HTTP.stubs(:new).returns(http = mock('HTTP'))
89
+ http.expects(:use_ssl=).with(true)
90
+ http.expects(:ca_path=)
91
+ http.expects(:verify_mode=).with(OpenSSL::SSL::VERIFY_PEER)
92
+ http.stubs(:start).returns([])
93
+ executor.execute('cmdname', 'http')
94
+ end
95
+ end
96
+
97
+ describe "API" do
98
+ before do
99
+ API.debug_mode = false
100
+ API.secure_mode = false
101
+ API.test_mode = false
102
+
103
+ @executor = mock('command executor')
104
+ @api = API.new(:session_id => '1234')
105
+ API::CommandExecutor.stubs(:new).with({:session_id => '1234'}).returns(@executor)
106
+ end
107
+
108
+ it "should use the api_id, username and password to authenticate and return the new session id" do
109
+ @executor.expects(:execute).with('auth', 'http',
110
+ :api_id => '1234',
111
+ :user => 'joebloggs',
112
+ :password => 'superpass'
113
+ ).returns(response = stub('response'))
114
+ Response.stubs(:parse).with(response).returns('OK' => 'new_session_id')
115
+ @api.authenticate('1234', 'joebloggs', 'superpass').should == 'new_session_id'
116
+ end
117
+
118
+ it "should support ping, using the current session_id" do
119
+ @executor.expects(:execute).with('ping', 'http', :session_id => 'abcdefg').returns(response = stub('response'))
120
+ @api.ping('abcdefg').should == response
121
+ end
122
+
123
+ it "should support sending messages to a specified number, returning the message id" do
124
+ @executor.expects(:execute).with('sendmsg', 'http',
125
+ :to => '4477791234567',
126
+ :text => 'hello world & goodbye'
127
+ ).returns(response = stub('response'))
128
+ Response.stubs(:parse).with(response).returns('ID' => 'message_id')
129
+ @api.send_message('4477791234567', 'hello world & goodbye').should == 'message_id'
130
+ end
131
+
132
+ it "should support sending messages to a multiple numbers, returning the message ids" do
133
+ @executor.expects(:execute).with('sendmsg', 'http',
134
+ :to => '4477791234567,447779999999',
135
+ :text => 'hello world & goodbye'
136
+ ).returns(response = stub('response'))
137
+ Response.stubs(:parse).with(response).returns([{'ID' => 'message_1_id'}, {'ID' => 'message_2_id'}])
138
+ @api.send_message(['4477791234567', '447779999999'], 'hello world & goodbye').should == ['message_1_id', 'message_2_id']
139
+ end
140
+
141
+ it "should set the :from parameter and set the :req_feat to 48 when using a custom from string when sending a message" do
142
+ @executor.expects(:execute).with('sendmsg', 'http', has_entries(:from => 'LUKE', :req_feat => '48')).returns(response = stub('response'))
143
+ Response.stubs(:parse).with(response).returns('ID' => 'message_id')
144
+ @api.send_message('4477791234567', 'hello world', :from => 'LUKE')
145
+ end
146
+
147
+ it "should set the :concat parameter when the message is longer than 160 characters" do
148
+ @executor.expects(:execute).with('sendmsg', 'http', has_entries(:concat => 2)).returns(response = stub('response'))
149
+ Response.stubs(:parse).with(response).returns('ID' => 'message_id')
150
+ @api.send_message('4477791234567', 't'*180)
151
+ end
152
+
153
+ it "should set the callback flag to the number passed in the options hash" do
154
+ @executor.expects(:execute).with('sendmsg', 'http', has_entry(:callback => 1)).returns(response=mock('response'))
155
+ Response.stubs(:parse).with(response).returns('ID' => 'message_id')
156
+ @api.send_message('4477791234567', 'hello world', :callback => 1)
157
+ end
158
+
159
+ it "should set the client message id to the number passed in the options hash" do
160
+ @executor.expects(:execute).with('sendmsg', 'http', has_entry(:climsgid => 12345678)).returns(response=mock('response'))
161
+ Response.stubs(:parse).with(response).returns('ID' => 'message_id')
162
+ @api.send_message('4477791234567', 'hello world', :client_message_id => 12345678)
163
+ end
164
+
165
+ it "should set the concat flag to the number passed in the options hash" do
166
+ @executor.expects(:execute).with('sendmsg', 'http', has_entry(:concat => 3)).returns(response=mock('response'))
167
+ Response.stubs(:parse).with(response).returns('ID' => 'message_id')
168
+ @api.send_message('4477791234567', 'hello world', :concat => 3)
169
+ end
170
+
171
+ it "should ignore any invalid parameters when sending a message" do
172
+ @executor.expects(:execute).with('sendmsg', 'http', Not(has_key(:any_old_param))).returns(response = stub('response'))
173
+ Response.stubs(:parse).returns('ID' => 'foo')
174
+ @api.send_message('4477791234567', 'hello world', :from => 'LUKE', :any_old_param => 'test')
175
+ end
176
+
177
+ it "should support message status query for a given message id, returning the message status" do
178
+ @executor.expects(:execute).with('querymsg', 'http', :apimsgid => 'messageid').returns(response = stub('response'))
179
+ Response.expects(:parse).with(response).returns('ID' => 'message_id', 'Status' => 'message_status')
180
+ @api.message_status('messageid').should == 'message_status'
181
+ end
182
+
183
+ it "should support balance query, returning number of credits as a float" do
184
+ @executor.expects(:execute).with('getbalance', 'http', {}).returns(response=mock('response'))
185
+ Response.stubs(:parse).with(response).returns('Credit' => '10.0')
186
+ @api.account_balance.should == 10.0
187
+ end
188
+
189
+ it "should raise an API::Error if the response parser raises" do
190
+ @executor.stubs(:execute)
191
+ Response.stubs(:parse).raises(Clickatell::API::Error.new('', ''))
192
+ proc { @api.account_balance }.should raise_error(Clickatell::API::Error)
193
+ end
194
+ end
195
+
196
+ describe API, ' when authenticating' do
197
+ it "should authenticate to retrieve a session_id and return a new API instance using that session id" do
198
+ API.stubs(:new).returns(api = mock('api'))
199
+ api.stubs(:authenticate).with('my_api_key', 'joebloggs', 'mypassword').returns('new_session_id')
200
+ api.expects(:auth_options=).with(:session_id => 'new_session_id')
201
+ API.authenticate('my_api_key', 'joebloggs', 'mypassword')
202
+ end
203
+ end
204
+
205
+ describe API, ' with no authentication options set' do
206
+ it "should build commands with no authentication options" do
207
+ api = API.new
208
+ API::CommandExecutor.stubs(:new).with({}).returns(executor = stub('command executor'))
209
+ executor.stubs(:execute)
210
+ api.ping('1234')
211
+ end
212
+ end
213
+
214
+ describe API, ' in secure mode' do
215
+ it "should execute commands securely" do
216
+ API.secure_mode = true
217
+ api = API.new
218
+ API::CommandExecutor.expects(:new).with({}).returns(executor = stub('command executor'))
219
+ executor.stubs(:execute)
220
+ api.ping('1234')
221
+ end
222
+ end
223
+
224
+ describe "API Error" do
225
+ it "should parse http response string to create error" do
226
+ response_string = "ERR: 001, Authentication error"
227
+ error = Clickatell::API::Error.parse(response_string)
228
+ error.code.should == '001'
229
+ error.message.should == 'Authentication error'
230
+ end
231
+ end
232
+
233
+ describe API, "#test_mode" do
234
+ before(:each) do
235
+ API.secure_mode = false
236
+ API.test_mode = true
237
+ @api = API.new
238
+ end
239
+
240
+ it "should create a new CommandExecutor with test_mode parameter set to true" do
241
+ API::CommandExecutor.expects(:new).with({}).once.returns(executor = mock('command executor'))
242
+ executor.stubs(:execute)
243
+ executor.stubs(:sms_requests).returns([])
244
+ @api.ping('1234')
245
+ end
246
+
247
+ it "should record all commands" do
248
+ @api.ping('1234')
249
+ @api.sms_requests.should_not be_empty
250
+ end
251
+
252
+ it "should return the recorded commands in a flattened array" do
253
+ @api.ping('1234')
254
+ @api.sms_requests.size.should == 1
255
+ @api.sms_requests.first.should_not be_instance_of(Array)
256
+ end
257
+ end
258
+
259
+ end