api-model 0.0.2 → 0.0.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,78 @@
1
+ require 'spec_helper'
2
+ require 'support/mock_models/banana'
3
+ require 'support/mock_models/multiple_hosts'
4
+
5
+ describe ApiModel, "Configuration" do
6
+
7
+ after(:each) do
8
+ Banana.reset_api_configuration
9
+ end
10
+
11
+ describe "api_host" do
12
+ it "should set the api host for all classes which inherit ApiModel::Base" do
13
+ ApiModel::Base.api_config do |config|
14
+ config.host = "foobarbaz.com"
15
+ end
16
+
17
+ Banana.api_model_configuration.host.should eq "foobarbaz.com"
18
+ end
19
+
20
+ it "should not override different classes configurations" do
21
+ MultipleHostsFoo.api_model_configuration.host.should eq("http://foo.com")
22
+ MultipleHostsBar.api_model_configuration.host.should eq("http://bar.com")
23
+ MultipleHostsNone.api_model_configuration.host.should be_nil
24
+ end
25
+ end
26
+
27
+ describe "json_root" do
28
+ it 'should be possible to set on a class' do
29
+ Banana.api_config do |config|
30
+ config.json_root = "foo_bar"
31
+ end
32
+
33
+ Banana.api_model_configuration.json_root.should eq "foo_bar"
34
+ end
35
+ end
36
+
37
+ describe "headers" do
38
+ it 'should create default headers for content type and accepts' do
39
+ headers = Banana.api_model_configuration.headers
40
+ headers["Content-Type"].should eq "application/json; charset=utf-8"
41
+ headers["Accept"].should eq "application/json"
42
+ end
43
+
44
+ it 'should be possible to set new headers' do
45
+ ApiModel::Base.api_config { |config| config.headers = { foo: "bar" } }
46
+ Banana.api_model_configuration.headers[:foo].should eq "bar"
47
+ end
48
+
49
+ it 'should retain the default headers when you add a new one' do
50
+ ApiModel::Base.api_config { |config| config.headers = { foo: "bar" } }
51
+
52
+ headers = Banana.api_model_configuration.headers
53
+ headers.should have_key "Accept"
54
+ headers.should have_key "Content-Type"
55
+ end
56
+
57
+ it 'should be possible to override default headers' do
58
+ ApiModel::Base.api_config { |config| config.headers = { "Accept" => "image/gif" } }
59
+ Banana.api_model_configuration.headers["Accept"].should eq "image/gif"
60
+ end
61
+ end
62
+
63
+ it 'should not unset other config values when you set a new one' do
64
+ ApiModel::Base.api_config { |c| c.host = "foo.com" }
65
+ Banana.api_config { |c| c.json_root = "banana" }
66
+
67
+ Banana.api_model_configuration.host.should eq "foo.com"
68
+ Banana.api_model_configuration.json_root.should eq "banana"
69
+ end
70
+
71
+ it 'should override config values from the superclass if it is changed' do
72
+ ApiModel::Base.api_config { |c| c.host = "will-go.com" }
73
+ Banana.api_config { |c| c.host = "new-host.com" }
74
+
75
+ Banana.api_model_configuration.host.should eq "new-host.com"
76
+ end
77
+
78
+ end
@@ -0,0 +1,74 @@
1
+ require 'spec_helper'
2
+ require 'support/mock_models/blog_post'
3
+
4
+ describe ApiModel::HttpRequest do
5
+
6
+ describe "default attributes" do
7
+ subject { ApiModel::HttpRequest.new }
8
+
9
+ it "should default #method to :get" do
10
+ subject.method.should eq :get
11
+ end
12
+
13
+ it "should default #options to a hash with headers" do
14
+ subject.options.should be_a Hash
15
+ subject.options[:headers].should_not be_nil
16
+ end
17
+ end
18
+
19
+ describe "callbacks" do
20
+ class ApiModel::HttpRequest
21
+ before_run :do_something_before_run
22
+ def do_something_before_run; end
23
+ end
24
+
25
+ it 'should be possible to set callbacks on the run method' do
26
+ ApiModel::HttpRequest.any_instance.should_receive(:do_something_before_run).once
27
+ VCR.use_cassette('posts') { BlogPost.get_json "http://api-model-specs.com/single_post"}
28
+ end
29
+ end
30
+
31
+ describe "using api_host" do
32
+ let(:blog_post) do
33
+ BlogPost.api_config do |config|
34
+ config.host = "http://api-model-specs.com"
35
+ end
36
+
37
+ VCR.use_cassette('posts') do
38
+ BlogPost.get_json "/single_post"
39
+ end
40
+ end
41
+
42
+ it "should be used with #path to generate a #full_path" do
43
+ blog_post.http_response.api_call.request.url.should eq "http://api-model-specs.com/single_post"
44
+ end
45
+ end
46
+
47
+ describe "headers" do
48
+ let :request_headers do
49
+ BlogPost.api_config { |config| config.host = "http://api-model-specs.com" }
50
+ blog_post = VCR.use_cassette('posts') { BlogPost.get_json "/single_post" }
51
+ blog_post.http_response.api_call.request.options[:headers]
52
+ end
53
+
54
+ it 'should use the default content type header' do
55
+ request_headers["Content-Type"].should eq ApiModel::Configuration.new.headers["Content-Type"]
56
+ end
57
+
58
+ it 'should use the default accept header' do
59
+ request_headers["Accept"].should eq ApiModel::Configuration.new.headers["Accept"]
60
+ end
61
+ end
62
+
63
+ describe "sending a GET request" do
64
+ let(:request) { ApiModel::HttpRequest.new path: "http://api-model-specs.com/posts", method: :get }
65
+
66
+ it "should use typhoeus to send a request" do
67
+ VCR.use_cassette('posts') do
68
+ request.run
69
+ end
70
+
71
+ request.api_call.success?.should eq true
72
+ end
73
+ end
74
+ end
@@ -3,6 +3,10 @@ require 'support/mock_models/banana'
3
3
 
4
4
  describe ApiModel::Initializer do
5
5
 
6
+ Banana.class_eval do
7
+ include ApiModel::Initializer
8
+ end
9
+
6
10
  let(:banana) { Banana.new color: "yellow", size: "large" }
7
11
 
8
12
  it "should set attributes when initializing with a hash" do
@@ -16,16 +20,12 @@ describe ApiModel::Initializer do
16
20
  expect(banana.size).to eq "small"
17
21
  end
18
22
 
19
- it "should not blog up if update_attributes is called with nil" do
23
+ it "should not blow up if update_attributes is called with nil" do
20
24
  expect {
21
25
  banana.update_attributes nil
22
26
  }.to_not raise_error
23
27
  end
24
28
 
25
- it "should run callbacks on initialize" do
26
- banana.ripe.should eq true
27
- end
28
-
29
29
  it "should log if an attempt was made to set an attribute which is not defined" do
30
30
  ApiModel::Log.should_receive(:debug).with "Could not set foo on Banana"
31
31
  Banana.new foo: "bar"
@@ -0,0 +1,186 @@
1
+ require 'spec_helper'
2
+ require 'support/mock_models/blog_post'
3
+ require 'support/mock_models/user'
4
+
5
+ describe ApiModel::Response do
6
+
7
+ let(:valid_response) do
8
+ VCR.use_cassette('posts') do
9
+ ApiModel::HttpRequest.new(path: "http://api-model-specs.com/single_post", method: :get, builder: BlogPost).run
10
+ end
11
+ end
12
+
13
+ describe "parsing the json body" do
14
+ it "should produce a hash given valid json" do
15
+ valid_response.json_response_body.should be_a(Hash)
16
+ valid_response.json_response_body["name"].should eq "foo"
17
+ end
18
+
19
+ it "should catch errors from parsing invalid json" do
20
+ valid_response.stub_chain(:http_response, :api_call, :body).and_return "blah"
21
+ ApiModel::Log.should_receive(:info).with "Could not parse JSON response: blah"
22
+
23
+ expect {
24
+ valid_response.json_response_body
25
+ }.to_not raise_error
26
+ end
27
+ end
28
+
29
+ describe "using a custom json root on the response body" do
30
+ let :users do
31
+ User.api_config do |c|
32
+ c.json_root = "users"
33
+ end
34
+ VCR.use_cassette('users') { User.get_json "http://api-model-specs.com/users" }
35
+ end
36
+
37
+ it 'should use the json root to build from' do
38
+ users.should be_a Array
39
+ users.size.should eq 3
40
+
41
+ users.each do |user|
42
+ user.should be_a User
43
+ end
44
+ end
45
+ end
46
+
47
+ describe "using a multi-level json root on the response body" do
48
+ let :user_search do
49
+ VCR.use_cassette('users') { User.get_json "http://api-model-specs.com/search" }
50
+ end
51
+
52
+ it 'should use the deep json root to build from' do
53
+ User.api_config { |c| c.json_root = "search.results.users" }
54
+
55
+ user_search.should be_a Array
56
+ user_search.size.should eq 3
57
+
58
+ user_search.each do |user|
59
+ user.should be_a User
60
+ end
61
+ end
62
+
63
+ it 'should raise a ApiModel::ResponseBuilderError exception if the hash does not contain the key' do
64
+ User.api_config { |c| c.json_root = "search.results.users.foo" }
65
+
66
+ expect {
67
+ user_search
68
+ }.to raise_error(ApiModel::ResponseBuilderError)
69
+ end
70
+ end
71
+
72
+ describe "#build" do
73
+ it "should use the builder.build method if present" do
74
+ builder = double
75
+ builder.should_receive(:build).with something: "foo"
76
+
77
+ valid_response.build builder, something: "foo"
78
+ end
79
+
80
+ it "should use builder.new if there's no builder.build method" do
81
+ builder = double
82
+ builder.should_receive(:new).with something_else: "hi"
83
+
84
+ valid_response.build builder, something_else: "hi"
85
+ end
86
+ end
87
+
88
+ describe "#build_objects" do
89
+ let(:single_object) do
90
+ valid_response.stub(:json_response_body).and_return name: "foo"
91
+ valid_response.build_objects
92
+ end
93
+
94
+ let(:array_of_objects) do
95
+ valid_response.stub(:json_response_body).and_return [{name: "foo"}, {name: "bar"}]
96
+ valid_response.build_objects
97
+ end
98
+
99
+ let(:empty_response) do
100
+ valid_response.stub(:json_response_body).and_return nil
101
+ valid_response.build_objects
102
+ end
103
+
104
+ it "should build a single object" do
105
+ single_object.should be_a(BlogPost)
106
+ single_object.name.should eq "foo"
107
+ end
108
+
109
+ it "should build an array of objects" do
110
+ array_of_objects[0].should be_a(BlogPost)
111
+ array_of_objects[0].name.should eq "foo"
112
+
113
+ array_of_objects[1].should be_a(BlogPost)
114
+ array_of_objects[1].name.should eq "bar"
115
+ end
116
+
117
+ it "should include the ApiModel::HttpRequest object" do
118
+ single_object.http_response.should be_a(ApiModel::HttpRequest)
119
+ end
120
+
121
+ it "should include the #json_response_body" do
122
+ single_object.json_response_body.should eq name: "foo"
123
+ end
124
+
125
+ it 'should return nil if the api returns an empty body' do
126
+ empty_response.should be_nil
127
+ end
128
+ end
129
+
130
+ describe "passing core methods down to the built class" do
131
+ ApiModel::Response::FALL_THROUGH_METHODS.each do |fall_trhough_method|
132
+ it "should pass ##{fall_trhough_method} on the built object class" do
133
+ allow_message_expectations_on_nil
134
+ valid_response.objects.should_receive(fall_trhough_method)
135
+ valid_response.send fall_trhough_method
136
+ end
137
+ end
138
+ end
139
+
140
+ describe "raising exceptions" do
141
+ describe "for requests which return a 401" do
142
+ let :api_request do
143
+ VCR.use_cassette('errors') do
144
+ BlogPost.get_json "http://api-model-specs.com/needs_auth"
145
+ end
146
+ end
147
+
148
+ it 'should raise an ApiModel::UnauthenticatedError if raise_on_unauthenticated is true' do
149
+ BlogPost.api_config { |c| c.raise_on_unauthenticated = true }
150
+ expect {
151
+ api_request
152
+ }.to raise_error(ApiModel::UnauthenticatedError)
153
+ end
154
+
155
+ it 'should not raise an ApiModel::UnauthenticatedError if raise_on_unauthenticated is false' do
156
+ BlogPost.api_config { |c| c.raise_on_unauthenticated = false }
157
+ expect {
158
+ api_request
159
+ }.to_not raise_error
160
+ end
161
+ end
162
+
163
+ describe "for requests which return a 404" do
164
+ let :api_request do
165
+ VCR.use_cassette('errors') do
166
+ BlogPost.get_json "http://api-model-specs.com/not_found"
167
+ end
168
+ end
169
+
170
+ it 'should raise an ApiModel::NotFoundError if raise_on_not_found is true' do
171
+ BlogPost.api_config { |c| c.raise_on_not_found = true }
172
+ expect {
173
+ api_request
174
+ }.to raise_error(ApiModel::NotFoundError)
175
+ end
176
+
177
+ it 'should not raise an ApiModel::NotFoundError if raise_on_not_found is false' do
178
+ BlogPost.api_config { |c| c.raise_on_not_found = false }
179
+ expect {
180
+ api_request
181
+ }.to_not raise_error
182
+ end
183
+ end
184
+ end
185
+
186
+ end
@@ -8,4 +8,13 @@ require 'api-model'
8
8
  VCR.configure do |c|
9
9
  c.cassette_library_dir = 'spec/support/fixtures'
10
10
  c.hook_into :webmock # or :fakeweb
11
+ end
12
+
13
+ RSpec.configure do |config|
14
+
15
+ # Reset any config changes after each spec
16
+ config.after(:each) do
17
+ ApiModel::Base.reset_api_configuration
18
+ end
19
+
11
20
  end
@@ -0,0 +1,84 @@
1
+ ---
2
+ http_interactions:
3
+ - request:
4
+ method: get
5
+ uri: http://cars.com/one_convertable
6
+ headers:
7
+ User-Agent:
8
+ - Typhoeus - https://github.com/typhoeus/typhoeus
9
+ response:
10
+ status:
11
+ code: 200
12
+ message: OK
13
+ headers:
14
+ Server:
15
+ - nginx/1.4.1
16
+ Date:
17
+ - Thu, 28 Nov 2013 16:02:56 GMT
18
+ Content-Type:
19
+ - text/plain; charset=utf-8
20
+ Content-Length:
21
+ - '248'
22
+ Connection:
23
+ - keep-alive
24
+ body:
25
+ encoding: UTF-8
26
+ string: "{\"numberOfDoors\":2,\"top_speed\":60}"
27
+ http_version:
28
+ recorded_at: Thu, 28 Nov 2013 16:02:20 GMT
29
+
30
+ - request:
31
+ method: get
32
+ uri: http://cars.com/fast_ones
33
+ headers:
34
+ User-Agent:
35
+ - Typhoeus - https://github.com/typhoeus/typhoeus
36
+ response:
37
+ status:
38
+ code: 200
39
+ message: OK
40
+ headers:
41
+ Server:
42
+ - nginx/1.4.1
43
+ Date:
44
+ - Thu, 28 Nov 2013 16:02:56 GMT
45
+ Content-Type:
46
+ - text/plain; charset=utf-8
47
+ Content-Length:
48
+ - '248'
49
+ Connection:
50
+ - keep-alive
51
+ body:
52
+ encoding: UTF-8
53
+ string: "[{\"numberOfDoors\":2,\"top_speed\":60},{\"numberOfDoors\":4,\"top_speed\":30,\"name\":\"Ford\"}]"
54
+ http_version:
55
+ recorded_at: Thu, 28 Nov 2013 16:02:20 GMT
56
+
57
+ - request:
58
+ method: get
59
+ uri: http://cars.com/new_model
60
+ headers:
61
+ User-Agent:
62
+ - Typhoeus - https://github.com/typhoeus/typhoeus
63
+ response:
64
+ status:
65
+ code: 200
66
+ message: OK
67
+ headers:
68
+ Server:
69
+ - nginx/1.4.1
70
+ Date:
71
+ - Thu, 28 Nov 2013 16:02:56 GMT
72
+ Content-Type:
73
+ - text/plain; charset=utf-8
74
+ Content-Length:
75
+ - '248'
76
+ Connection:
77
+ - keep-alive
78
+ body:
79
+ encoding: UTF-8
80
+ string: "{\"numberOfDoors\":2,\"top_speed\":60,\"shiney\":true}"
81
+ http_version:
82
+ recorded_at: Thu, 28 Nov 2013 16:02:20 GMT
83
+
84
+ recorded_with: VCR 2.8.0