screenbeacon 0.1.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.
Files changed (41) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +4 -0
  3. data/.travis.yml +29 -0
  4. data/Gemfile +8 -0
  5. data/LICENSE +21 -0
  6. data/README.rdoc +44 -0
  7. data/Rakefile +7 -0
  8. data/VERSION +1 -0
  9. data/bin/screenbeacon-console +7 -0
  10. data/gemfiles/default-with-activesupport.gemfile +10 -0
  11. data/gemfiles/json.gemfile +12 -0
  12. data/gemfiles/yajl.gemfile +12 -0
  13. data/lib/data/ca-certificates.crt +5165 -0
  14. data/lib/screenbeacon/alert.rb +32 -0
  15. data/lib/screenbeacon/api_operations/create.rb +16 -0
  16. data/lib/screenbeacon/api_operations/delete.rb +11 -0
  17. data/lib/screenbeacon/api_operations/list.rb +17 -0
  18. data/lib/screenbeacon/api_operations/request.rb +42 -0
  19. data/lib/screenbeacon/api_operations/update.rb +17 -0
  20. data/lib/screenbeacon/api_resource.rb +35 -0
  21. data/lib/screenbeacon/errors/api_connection_error.rb +4 -0
  22. data/lib/screenbeacon/errors/api_error.rb +4 -0
  23. data/lib/screenbeacon/errors/authentication_error.rb +4 -0
  24. data/lib/screenbeacon/errors/invalid_request_error.rb +10 -0
  25. data/lib/screenbeacon/errors/screenbeacon_error.rb +20 -0
  26. data/lib/screenbeacon/project.rb +13 -0
  27. data/lib/screenbeacon/screenbeacon_object.rb +263 -0
  28. data/lib/screenbeacon/test.rb +8 -0
  29. data/lib/screenbeacon/util.rb +130 -0
  30. data/lib/screenbeacon/version.rb +3 -0
  31. data/lib/screenbeacon.rb +305 -0
  32. data/screenbeacon.gemspec +27 -0
  33. data/test/screenbeacon/alert_test.rb +14 -0
  34. data/test/screenbeacon/api_resource_test.rb +82 -0
  35. data/test/screenbeacon/project_test.rb +36 -0
  36. data/test/screenbeacon/screenbeacon_object_test.rb +28 -0
  37. data/test/screenbeacon/test_test.rb +36 -0
  38. data/test/screenbeacon/util_test.rb +34 -0
  39. data/test/test_data.rb +147 -0
  40. data/test/test_helper.rb +43 -0
  41. metadata +176 -0
@@ -0,0 +1,305 @@
1
+ # Screenbeacon Ruby bindings
2
+ # API spec at https://screenbeacon.readme.io
3
+ require 'cgi'
4
+ require 'openssl'
5
+ require 'rbconfig'
6
+ require 'set'
7
+ require 'socket'
8
+
9
+ require 'rest-client'
10
+ require 'json'
11
+
12
+ # Version
13
+ require 'screenbeacon/version'
14
+
15
+ # API operations
16
+ require 'screenbeacon/api_operations/create'
17
+ require 'screenbeacon/api_operations/update'
18
+ require 'screenbeacon/api_operations/delete'
19
+ require 'screenbeacon/api_operations/list'
20
+ require 'screenbeacon/api_operations/request'
21
+
22
+ # Resources
23
+ require 'screenbeacon/util'
24
+ require 'screenbeacon/screenbeacon_object'
25
+ require 'screenbeacon/api_resource'
26
+ require 'screenbeacon/project'
27
+ require 'screenbeacon/test'
28
+ require 'screenbeacon/alert'
29
+
30
+ # Errors
31
+ require 'screenbeacon/errors/screenbeacon_error'
32
+ require 'screenbeacon/errors/api_error'
33
+ require 'screenbeacon/errors/api_connection_error'
34
+ require 'screenbeacon/errors/invalid_request_error'
35
+ require 'screenbeacon/errors/authentication_error'
36
+
37
+ module Screenbeacon
38
+ DEFAULT_CA_BUNDLE_PATH = File.dirname(__FILE__) + '/data/ca-certificates.crt'
39
+ @api_base = 'https://api.screenbeacon.com'
40
+
41
+ @ssl_bundle_path = DEFAULT_CA_BUNDLE_PATH
42
+ @verify_ssl_certs = true
43
+
44
+
45
+ class << self
46
+ attr_accessor :api_id, :api_token, :api_base, :verify_ssl_certs, :api_version #, :connect_base, :uploads_base
47
+ end
48
+
49
+ def self.api_url(url='', api_base_url=nil)
50
+ (api_base_url || @api_base) + url
51
+ end
52
+
53
+ def self.request(method, url, api_id, api_token, params={}, headers={}, api_base_url=nil)
54
+ api_base_url = api_base_url || @api_base
55
+
56
+ unless api_id ||= @api_id
57
+ raise AuthenticationError.new('No API ID provided. ' \
58
+ 'Set your API ID using "Screenbeacon.api_id = <API-ID>". ' \
59
+ 'You can generate API ID from the Screenbeacon web interface. ' \
60
+ 'See https://screenbeacon.com/dashboard/settings for details, or email support@screenbeacon.com ' \
61
+ 'if you have any questions.')
62
+ end
63
+
64
+ unless api_token ||= @api_token
65
+ raise AuthenticationError.new('No API token provided. ' \
66
+ 'Set your API token using "Screenbeacon.api_token = <API-TOKEN>". ' \
67
+ 'You can generate API token from the Screenbeacon web interface. ' \
68
+ 'See https://screenbeacon.com/dashboard/settings for details, or email support@screenbeacon.com ' \
69
+ 'if you have any questions.')
70
+ end
71
+
72
+ if api_id =~ /\s/
73
+ raise AuthenticationError.new('Your API key is invalid, as it contains ' \
74
+ 'whitespace. (HINT: You can double-check your API key from the ' \
75
+ 'Screenbeacon web interface. See https://screenbeacon.com/dashboard/settings for details, or ' \
76
+ 'email support@screenbeacon.com if you have any questions.)')
77
+ end
78
+
79
+ if api_token =~ /\s/
80
+ raise AuthenticationError.new('Your API token is invalid, as it contains ' \
81
+ 'whitespace. (HINT: You can double-check your API token from the ' \
82
+ 'Screenbeacon web interface. See https://screenbeacon.com/dashboard/settings for details, or ' \
83
+ 'email support@screenbeacon.com if you have any questions.)')
84
+ end
85
+
86
+ if verify_ssl_certs
87
+ request_opts = {:verify_ssl => OpenSSL::SSL::VERIFY_PEER,
88
+ :ssl_ca_file => @ssl_bundle_path}
89
+ else
90
+ request_opts = {:verify_ssl => false}
91
+ unless @verify_ssl_warned
92
+ @verify_ssl_warned = true
93
+ $stderr.puts("WARNING: Running without SSL cert verification. " \
94
+ "You should never do this in production. " \
95
+ "Execute 'Screenbeacon.verify_ssl_certs = true' to enable verification.")
96
+ end
97
+ end
98
+
99
+ params = Util.objects_to_ids(params)
100
+ url = api_url(url, api_base_url)
101
+
102
+ case method.to_s.downcase.to_sym
103
+ when :get, :head, :delete
104
+ # Make params into GET parameters
105
+ url += "#{URI.parse(url).query ? '&' : '?'}#{uri_encode(params)}" if params && params.any?
106
+ payload = nil
107
+ else
108
+ if headers[:content_type] && headers[:content_type] == "multipart/form-data"
109
+ payload = params
110
+ else
111
+ payload = uri_encode(params)
112
+ end
113
+ end
114
+
115
+ request_opts.update(:headers => request_headers(api_id, api_token).update(headers),
116
+ :method => method, :open_timeout => 30,
117
+ :payload => payload, :url => url, :timeout => 80)
118
+
119
+ begin
120
+ response = execute_request(request_opts)
121
+ rescue SocketError => e
122
+ handle_restclient_error(e, api_base_url)
123
+ rescue NoMethodError => e
124
+ # Work around RestClient bug
125
+ if e.message =~ /\WRequestFailed\W/
126
+ e = APIConnectionError.new('Unexpected HTTP response code')
127
+ handle_restclient_error(e, api_base_url)
128
+ else
129
+ raise
130
+ end
131
+ rescue RestClient::ExceptionWithResponse => e
132
+ if rcode = e.http_code and rbody = e.http_body
133
+ handle_api_error(rcode, rbody)
134
+ else
135
+ handle_restclient_error(e, api_base_url)
136
+ end
137
+ rescue RestClient::Exception, Errno::ECONNREFUSED => e
138
+ handle_restclient_error(e, api_base_url)
139
+ end
140
+
141
+ [parse(response), api_id, api_token]
142
+ end
143
+
144
+ private
145
+
146
+ def self.user_agent
147
+ @uname ||= get_uname
148
+ lang_version = "#{RUBY_VERSION} p#{RUBY_PATCHLEVEL} (#{RUBY_RELEASE_DATE})"
149
+
150
+ {
151
+ :bindings_version => Screenbeacon::VERSION,
152
+ :lang => 'ruby',
153
+ :lang_version => lang_version,
154
+ :platform => RUBY_PLATFORM,
155
+ :engine => defined?(RUBY_ENGINE) ? RUBY_ENGINE : '',
156
+ :publisher => 'screenbeacon',
157
+ :uname => @uname,
158
+ :hostname => Socket.gethostname,
159
+ }
160
+
161
+ end
162
+
163
+ def self.get_uname
164
+ if File.exist?('/proc/version')
165
+ File.read('/proc/version').strip
166
+ else
167
+ case RbConfig::CONFIG['host_os']
168
+ when /linux|darwin|bsd|sunos|solaris|cygwin/i
169
+ _uname_uname
170
+ when /mswin|mingw/i
171
+ _uname_ver
172
+ else
173
+ "unknown platform"
174
+ end
175
+ end
176
+ end
177
+
178
+ def self._uname_uname
179
+ (`uname -a 2>/dev/null` || '').strip
180
+ rescue Errno::ENOMEM # couldn't create subprocess
181
+ "uname lookup failed"
182
+ end
183
+
184
+ def self._uname_ver
185
+ (`ver` || '').strip
186
+ rescue Errno::ENOMEM # couldn't create subprocess
187
+ "uname lookup failed"
188
+ end
189
+
190
+
191
+ def self.uri_encode(params)
192
+ Util.flatten_params(params).
193
+ map { |k,v| "#{k}=#{Util.url_encode(v)}" }.join('&')
194
+ end
195
+
196
+ def self.request_headers(api_id, api_token)
197
+ headers = {
198
+ :user_agent => "Screenbeacon/v1 RubyBindings/#{Screenbeacon::VERSION}",
199
+ # :authorization => "Bearer #{api_id}",
200
+ :content_type => 'application/x-www-form-urlencoded'
201
+ }
202
+
203
+ headers['Screenbeacon-Version'] = api_version if api_version
204
+ headers['X-API-ID'] = api_id
205
+ headers['X-API-TOKEN'] = api_token
206
+
207
+ begin
208
+ headers.update(:x_screenbeacon_client_user_agent => JSON.generate(user_agent))
209
+ rescue => e
210
+ headers.update(:x_screenbeacon_client_raw_user_agent => user_agent.inspect,
211
+ :error => "#{e} (#{e.class})")
212
+ end
213
+ end
214
+
215
+ def self.execute_request(opts)
216
+ RestClient::Request.execute(opts)
217
+ end
218
+
219
+ def self.parse(response)
220
+ begin
221
+ # Would use :symbolize_names => true, but apparently there is
222
+ # some library out there that makes symbolize_names not work.
223
+ response = JSON.parse(response.body)
224
+ rescue JSON::ParserError
225
+ raise general_api_error(response.code, response.body)
226
+ end
227
+
228
+ Util.symbolize_names(response)
229
+ end
230
+
231
+ def self.general_api_error(rcode, rbody)
232
+ APIError.new("Invalid response object from API: #{rbody.inspect} " +
233
+ "(HTTP response code was #{rcode})", rcode, rbody)
234
+ end
235
+
236
+ def self.handle_api_error(rcode, rbody)
237
+ begin
238
+ error_obj = JSON.parse(rbody)
239
+ error_obj = Util.symbolize_names(error_obj)
240
+ error = error_obj[:error] or raise ScreenbeaconError.new # escape from parsing
241
+
242
+ rescue JSON::ParserError, ScreenbeaconError
243
+ raise general_api_error(rcode, rbody)
244
+ end
245
+
246
+ case rcode
247
+ when 400, 404
248
+ raise invalid_request_error error, rcode, rbody, error_obj
249
+ when 401
250
+ raise authentication_error error, rcode, rbody, error_obj
251
+ when 402
252
+ raise card_error error, rcode, rbody, error_obj
253
+ else
254
+ raise api_error error, rcode, rbody, error_obj
255
+ end
256
+
257
+ end
258
+
259
+ def self.invalid_request_error(error, rcode, rbody, error_obj)
260
+ # We're not returning a params as part of the error for now, so set it to nil.
261
+ InvalidRequestError.new(error, nil, rcode, rbody, error_obj)
262
+ end
263
+
264
+ def self.authentication_error(error, rcode, rbody, error_obj)
265
+ AuthenticationError.new(error, rcode, rbody, error_obj)
266
+ end
267
+
268
+ def self.api_error(error, rcode, rbody, error_obj)
269
+ APIError.new(error, rcode, rbody, error_obj)
270
+ end
271
+
272
+ def self.handle_restclient_error(e, api_base_url=nil)
273
+ api_base_url = @api_base unless api_base_url
274
+ connection_message = "Please check your internet connection and try again. " \
275
+ "If this problem persists, you should check Screenbeacon's service status at " \
276
+ "https://twitter.com/screenbeacon, or let us know at support@screenbeacon.com."
277
+
278
+ case e
279
+ when RestClient::RequestTimeout
280
+ message = "Could not connect to Screenbeacon (#{api_base_url}). #{connection_message}"
281
+
282
+ when RestClient::ServerBrokeConnection
283
+ message = "The connection to the server (#{api_base_url}) broke before the " \
284
+ "request completed. #{connection_message}"
285
+
286
+ when RestClient::SSLCertificateNotVerified
287
+ message = "Could not verify Screenbeacon's SSL certificate. " \
288
+ "Please make sure that your network is not intercepting certificates. " \
289
+ "(Try going to https://api.screenbeacon.com in your browser.) " \
290
+ "If this problem persists, let us know at support@screenbeacon.com."
291
+
292
+ when SocketError
293
+ message = "Unexpected error communicating when trying to connect to Screenbeacon. " \
294
+ "You may be seeing this message because your DNS is not working. " \
295
+ "To check, try running 'host screenbeacon.com' from the command line."
296
+
297
+ else
298
+ message = "Unexpected error communicating with Screenbeacon. " \
299
+ "If this problem persists, let us know at support@screenbeacon.com."
300
+
301
+ end
302
+
303
+ raise APIConnectionError.new(message + "\n\n(Network error: #{e.message})")
304
+ end
305
+ end
@@ -0,0 +1,27 @@
1
+ $:.unshift(File.join(File.dirname(__FILE__), 'lib'))
2
+
3
+ require 'screenbeacon/version'
4
+
5
+ spec = Gem::Specification.new do |s|
6
+ s.name = 'screenbeacon'
7
+ s.version = Screenbeacon::VERSION
8
+ s.summary = 'Ruby bindings for the Screenbeacon API'
9
+ s.description = 'Screenbeacon is a visual design testing service.'
10
+ s.authors = ['Jordan Humphreys']
11
+ s.email = ['jordan@screenbeacon.com']
12
+ s.homepage = 'https://screenbeacon.readme.io'
13
+ s.license = 'MIT'
14
+
15
+ s.add_dependency('rest-client', '~> 1.4')
16
+ s.add_dependency('json', '~> 1.8.1')
17
+
18
+ s.add_development_dependency('mocha', '~> 0.13.2')
19
+ s.add_development_dependency('shoulda', '~> 3.4.0')
20
+ s.add_development_dependency('test-unit')
21
+ s.add_development_dependency('rake')
22
+
23
+ s.files = `git ls-files`.split("\n")
24
+ s.test_files = `git ls-files -- test/*`.split("\n")
25
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
26
+ s.require_paths = ['lib']
27
+ end
@@ -0,0 +1,14 @@
1
+ require File.expand_path('../../test_helper', __FILE__)
2
+
3
+ module Screenbeacon
4
+ class AlertTest < ::Test::Unit::TestCase
5
+ should "alerts should be listable" do
6
+ @mock.expects(:get).once.returns(test_response(test_alert_array))
7
+ c = Screenbeacon::Alert.all
8
+ assert c.kind_of? Array
9
+ # TODO: Should be a Screenbeacon::Alert
10
+ assert c[0].kind_of? Screenbeacon::ScreenbeaconObject
11
+ end
12
+
13
+ end
14
+ end
@@ -0,0 +1,82 @@
1
+ # -*- coding: utf-8 -*-
2
+ require File.expand_path('../../test_helper', __FILE__)
3
+
4
+ module Screenbeacon
5
+ class ApiResourceTest < ::Test::Unit::TestCase
6
+ should "creating a new APIResource should not fetch over the network" do
7
+ @mock.expects(:get).never
8
+ Screenbeacon::Project.new("someid")
9
+ end
10
+
11
+ should "creating a new APIResource from a hash should not fetch over the network" do
12
+ @mock.expects(:get).never
13
+ Screenbeacon::Project.construct_from({
14
+ :id => "somecustomer",
15
+ :object => "customer"
16
+ })
17
+ end
18
+
19
+ should "setting an attribute should not cause a network request" do
20
+ @mock.expects(:get).never
21
+ @mock.expects(:post).never
22
+ c = Screenbeacon::Project.new("test_project");
23
+ c.card = {:id => "somecard", :object => "card"}
24
+ end
25
+
26
+ should "accessing id should not issue a fetch" do
27
+ @mock.expects(:get).never
28
+ c = Screenbeacon::Project.new("test_project")
29
+ c.id
30
+ end
31
+
32
+ should "not specifying api credentials should raise an exception" do
33
+ Screenbeacon.api_id = nil
34
+ Screenbeacon.api_token = nil
35
+ assert_raises Screenbeacon::AuthenticationError do
36
+ Screenbeacon::Project.new("test_project").refresh
37
+ end
38
+ end
39
+
40
+ should "using a nil api key should raise an exception" do
41
+ assert_raises TypeError do
42
+ Screenbeacon::Project.all({}, nil)
43
+ end
44
+ assert_raises TypeError do
45
+ Screenbeacon::Project.all({}, { :api_id => nil, :api_token => nil })
46
+ end
47
+ end
48
+
49
+ should "specifying api credentials containing whitespace should raise an exception" do
50
+ Screenbeacon.api_id = "id "
51
+ Screenbeacon.api_token = " token"
52
+ assert_raises Screenbeacon::AuthenticationError do
53
+ Screenbeacon::Project.new("test_project").refresh
54
+ end
55
+ end
56
+
57
+ should "specifying invalid api credentials should raise an exception" do
58
+ Screenbeacon.api_id = "invalid"
59
+ Screenbeacon.api_token = "token"
60
+ response = test_response(test_invalid_api_id_error, 401)
61
+ assert_raises Screenbeacon::AuthenticationError do
62
+ @mock.expects(:get).once.raises(RestClient::ExceptionWithResponse.new(response, 401))
63
+ Screenbeacon::Project.retrieve("failing_project")
64
+ end
65
+ end
66
+
67
+ should "AuthenticationErrors should have an http status, http body, and JSON body" do
68
+ Screenbeacon.api_id = "invalid"
69
+ Screenbeacon.api_token = "token"
70
+ response = test_response(test_invalid_api_id_error, 401)
71
+ begin
72
+ @mock.expects(:get).once.raises(RestClient::ExceptionWithResponse.new(response, 401))
73
+ Screenbeacon::Project.retrieve("failing_project")
74
+ rescue Screenbeacon::AuthenticationError => e
75
+ assert_equal(401, e.http_status)
76
+ assert_equal(true, !!e.http_body)
77
+ assert_equal(true, !!e.json_body[:error])
78
+ assert_equal(test_invalid_api_id_error[:error], e.json_body[:error])
79
+ end
80
+ end
81
+ end
82
+ end
@@ -0,0 +1,36 @@
1
+ require File.expand_path('../../test_helper', __FILE__)
2
+
3
+ module Screenbeacon
4
+ class ProjectTest < ::Test::Unit::TestCase
5
+ should "projects should be listable" do
6
+ @mock.expects(:get).once.returns(test_response(test_project_array))
7
+ c = Screenbeacon::Project.all
8
+ assert c.kind_of? Array
9
+ assert c[0].kind_of? Screenbeacon::ScreenbeaconObject
10
+ end
11
+
12
+ should "project should be deletable" do
13
+ @mock.expects(:delete).once.returns(test_response(test_project({:deleted => true})))
14
+ c = Screenbeacon::Project.new("test_project")
15
+ c.delete
16
+ assert c.deleted
17
+ end
18
+
19
+ # should "projects should be updateable" do
20
+ # @mock.expects(:get).once.returns(test_response(test_project({:name => "fubu"})))
21
+ # @mock.expects(:post).once.returns(test_response(test_project({:name => "barbu"})))
22
+ # c = Screenbeacon::Project.new(test_project({:name => "fubu"})).refresh
23
+ # assert_equal "fubu", c.name
24
+ # c.name = "barbu"
25
+ # c.save
26
+ # assert_equal "barbu", c.name
27
+ # end
28
+
29
+ should "create should return a new project" do
30
+ @mock.expects(:post).once.returns(test_response(test_project))
31
+ c = Screenbeacon::Project.create
32
+ assert_equal "Screenbeacon", c.name
33
+ end
34
+
35
+ end
36
+ end
@@ -0,0 +1,28 @@
1
+ require File.expand_path('../../test_helper', __FILE__)
2
+
3
+ module Screenbeacon
4
+ class ScreenbeaconObjectTest < ::Test::Unit::TestCase
5
+ should "implement #respond_to correctly" do
6
+ obj = Screenbeacon::ScreenbeaconObject.construct_from({ :id => 1, :foo => 'bar' })
7
+ assert obj.respond_to?(:id)
8
+ assert obj.respond_to?(:foo)
9
+ assert !obj.respond_to?(:baz)
10
+ end
11
+
12
+ should "marshal a screenbeacon object correctly" do
13
+ obj = Screenbeacon::ScreenbeaconObject.construct_from({ :id => 1, :name => 'Screenbeacon' }, {:api_id => 'apiid', :api_token => 'apitoken'})
14
+ m = Marshal.load(Marshal.dump(obj))
15
+ assert_equal 1, m.id
16
+ assert_equal 'Screenbeacon', m.name
17
+ expected_hash = {:api_id => 'apiid', :api_token => 'apitoken'}
18
+ assert_equal expected_hash, m.instance_variable_get('@opts')
19
+ end
20
+
21
+ should "recursively call to_hash on its values" do
22
+ nested = Screenbeacon::ScreenbeaconObject.construct_from({ :id => 7, :foo => 'bar' })
23
+ obj = Screenbeacon::ScreenbeaconObject.construct_from({ :id => 1, :nested => nested })
24
+ expected_hash = { :id => 1, :nested => { :id => 7, :foo => 'bar' } }
25
+ assert_equal expected_hash, obj.to_hash
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,36 @@
1
+ require File.expand_path('../../test_helper', __FILE__)
2
+
3
+ module Screenbeacon
4
+ class TestTest < ::Test::Unit::TestCase
5
+ should "tests should be listable" do
6
+ @mock.expects(:get).once.returns(test_response(test_test_array))
7
+ c = Screenbeacon::Test.all
8
+ assert c.kind_of? Array
9
+ assert c[0].kind_of? Screenbeacon::ScreenbeaconObject
10
+ end
11
+
12
+ should "test should be deletable" do
13
+ @mock.expects(:delete).once.returns(test_response(test_test({:deleted => true})))
14
+ c = Screenbeacon::Test.new("test_test")
15
+ c.delete
16
+ assert c.deleted
17
+ end
18
+
19
+ # should "tests should be updateable" do
20
+ # @mock.expects(:get).once.returns(test_response(test_test({:name => "fubu"})))
21
+ # @mock.expects(:post).once.returns(test_response(test_test({:name => "barbu"})))
22
+ # c = Screenbeacon::Test.new("test_test").refresh
23
+ # assert_equal "fubu", c.name
24
+ # c.name = "barbu"
25
+ # c.save
26
+ # assert_equal "barbu", c.name
27
+ # end
28
+
29
+ should "create should return a new test" do
30
+ @mock.expects(:post).once.returns(test_response(test_test({:name => "Screenbeacon"})))
31
+ c = Screenbeacon::Test.create
32
+ assert_equal "Screenbeacon", c.name
33
+ end
34
+
35
+ end
36
+ end
@@ -0,0 +1,34 @@
1
+ require File.expand_path('../../test_helper', __FILE__)
2
+
3
+ module Screenbeacon
4
+ class UtilTest < ::Test::Unit::TestCase
5
+ should "symbolize_names should convert names to symbols" do
6
+ start = {
7
+ 'foo' => 'bar',
8
+ 'array' => [{ 'foo' => 'bar' }],
9
+ 'nested' => {
10
+ 1 => 2,
11
+ :symbol => 9,
12
+ 'string' => nil
13
+ }
14
+ }
15
+ finish = {
16
+ :foo => 'bar',
17
+ :array => [{ :foo => 'bar' }],
18
+ :nested => {
19
+ 1 => 2,
20
+ :symbol => 9,
21
+ :string => nil
22
+ }
23
+ }
24
+
25
+ symbolized = Screenbeacon::Util.symbolize_names(start)
26
+ assert_equal(finish, symbolized)
27
+ end
28
+
29
+ should "normalize_opts should reject nil keys" do
30
+ assert_raise { Screenbeacon::Util.normalize_opts(nil) }
31
+ assert_raise { Screenbeacon::Util.normalize_opts(:api_id => nil, :api_token => nil) }
32
+ end
33
+ end
34
+ end