frenetic 1.0.0 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -11,15 +11,25 @@ describe Frenetic::MemberRestMethods do
11
11
  end
12
12
  end
13
13
 
14
+ let(:my_filtered_resource) do
15
+ cfg = test_cfg
16
+
17
+ Class.new(Frenetic::Resource) do
18
+ api_client { Frenetic.new(cfg) }
19
+ end
20
+ end
21
+
14
22
  before do
15
23
  stub_const 'MyTempResource', my_temp_resource
16
24
  MyTempResource.send :include, described_class
17
25
  end
18
26
 
19
27
  describe '.find' do
28
+ let(:params) { 1 }
29
+
20
30
  before { @stubs.api_description }
21
31
 
22
- subject { MyTempResource.find 1 }
32
+ subject { MyTempResource.find(params) }
23
33
 
24
34
  context 'for a known instance' do
25
35
  before { @stubs.known_instance }
@@ -41,7 +51,33 @@ describe Frenetic::MemberRestMethods do
41
51
  before { @stubs.unknown_instance }
42
52
 
43
53
  it 'raises an error' do
44
- expect{subject}.to raise_error Frenetic::ClientError
54
+ expect{subject}.to(
55
+ raise_error Frenetic::ResourceNotFound, "Couldn't find MyTempResource with id=1"
56
+ )
57
+ end
58
+
59
+ context 'with an unparseable response' do
60
+ before { @stubs.invalid_unknown_instance }
61
+
62
+ it 'raises an error' do
63
+ expect{subject}.to(
64
+ raise_error Frenetic::ResourceNotFound, "Couldn't find MyTempResource with id=1"
65
+ )
66
+ end
67
+ end
68
+ end
69
+
70
+ context 'that results in a client-level error' do
71
+ before do
72
+ @stubs.api_error(
73
+ status: 422,
74
+ body: 'Unprocessable Entity',
75
+ url: 'example.com/api/my_temp_resources/1'
76
+ )
77
+ end
78
+
79
+ it 'raises an error' do
80
+ expect{subject}.to raise_error Frenetic::ClientError, '422 Unprocessable Entity'
45
81
  end
46
82
  end
47
83
 
@@ -56,6 +92,35 @@ describe Frenetic::MemberRestMethods do
56
92
  it 'returns a mock resource' do
57
93
  expect(subject).to be_an_instance_of MyMockResource
58
94
  end
95
+
96
+ context 'with blank parameters' do
97
+ let(:params) { {} }
98
+
99
+ it 'raises an error' do
100
+ expect{subject}.to(
101
+ raise_error Frenetic::ResourceNotFound, "Couldn't find MyTempResource without an ID"
102
+ )
103
+ end
104
+ end
105
+ end
106
+ end
107
+
108
+ describe '.find_by' do
109
+ before { @stubs.api_description }
110
+
111
+ subject { MyTempResource.find_by(id:1) }
112
+
113
+ it 'delegates to .find' do
114
+ allow(MyTempResource).to receive(:find).and_return(nil)
115
+ subject
116
+ expect(MyTempResource).to have_received(:find)
117
+ end
118
+
119
+ context 'for an unknown resource' do
120
+ it 'returns nil' do
121
+ allow(MyTempResource).to receive(:find).and_raise(Frenetic::ClientError.new({}))
122
+ expect{subject}.to_not raise_error
123
+ end
59
124
  end
60
125
  end
61
126
 
@@ -83,5 +148,23 @@ describe Frenetic::MemberRestMethods do
83
148
  expect(subject).to be_empty
84
149
  end
85
150
  end
151
+
152
+ context 'with additional parameters' do
153
+ before do
154
+ stub_const('MyFilteredResource', my_filtered_resource)
155
+ MyFilteredResource.send(:include, described_class)
156
+ @stubs.filtered_resource
157
+ end
158
+
159
+ subject { MyFilteredResource.all(filter: 'bar') }
160
+
161
+ it 'returns a resource collection' do
162
+ expect(subject).to be_an_instance_of Frenetic::ResourceCollection
163
+ end
164
+
165
+ it 'instantiates all resources in the collection' do
166
+ expect(subject.first).to be_an_instance_of MyFilteredResource
167
+ end
168
+ end
86
169
  end
87
170
  end
@@ -0,0 +1,95 @@
1
+ require 'spec_helper'
2
+
3
+ describe Frenetic::Persistence do
4
+ let(:test_cfg) { { url:'http://example.com/api' } }
5
+
6
+ let(:my_temp_resource) do
7
+ cfg = test_cfg
8
+
9
+ Class.new(Frenetic::Resource) do
10
+ api_client { Frenetic.new(cfg) }
11
+ end
12
+ end
13
+
14
+ before do
15
+ stub_const 'MyTempResource', my_temp_resource
16
+ MyTempResource.send(:include, described_class)
17
+ end
18
+
19
+ subject(:instance) { MyTempResource.new }
20
+
21
+ describe '#save' do
22
+ before { @stubs.api_description }
23
+
24
+ subject { super().save }
25
+
26
+ context 'for a valid resource' do
27
+ before { @stubs.valid_created_resource }
28
+
29
+ it 'returns TRUE' do
30
+ expect(subject).to eql true
31
+ end
32
+
33
+ it 'returns a valid instance' do
34
+ subject
35
+ expect(instance.valid?).to eql true
36
+ end
37
+ end
38
+
39
+ context 'for an invalid resource' do
40
+ before { @stubs.invalid_created_resource }
41
+
42
+ it 'returns FALSE' do
43
+ expect(subject).to eql false
44
+ end
45
+
46
+ it 'returns an invalid instance' do
47
+ subject
48
+ expect(instance.valid?).to eql false
49
+ end
50
+
51
+ it 'maps resource errors onto the instance' do
52
+ subject
53
+ expect(instance.errors).to include 'name' => ['cannot be blank', 'must be a name']
54
+ end
55
+ end
56
+
57
+ context 'that triggers an exception' do
58
+ let(:mock_api) { double('api_client') }
59
+
60
+ before do
61
+ @stubs.valid_created_resource
62
+ allow(mock_api).to receive(:post).and_raise(StandardError)
63
+ expect(instance).to receive(:api).and_return(mock_api)
64
+ end
65
+
66
+ it 're-raises the exception' do
67
+ expect{subject}.to raise_error(StandardError)
68
+ end
69
+ end
70
+ end
71
+
72
+ describe '#save!' do
73
+ before { @stubs.api_description }
74
+
75
+ subject { super().save! }
76
+
77
+ context 'for a valid resource' do
78
+ before { @stubs.valid_created_resource }
79
+
80
+ it 'returns TRUE' do
81
+ expect(subject).to eql true
82
+ end
83
+ end
84
+
85
+ context 'for an invalid resource' do
86
+ before { @stubs.invalid_created_resource }
87
+
88
+ it 'raises an error' do
89
+ # rubocop:disable Metrics/LineLength
90
+ expect{subject}.to raise_error(Frenetic::ClientError, 'Validation failed: Name cannot be blank, Name must be a name, Cannot be valid')
91
+ # rubocop:enable Metrics/LineLength
92
+ end
93
+ end
94
+ end
95
+ end
@@ -1,5 +1,6 @@
1
1
  require 'json'
2
2
 
3
+ # rubocop:disable Metrics/ClassLength
3
4
  class HttpStubs
4
5
  def initialize(rspec)
5
6
  @rspec = rspec
@@ -29,22 +30,12 @@ class HttpStubs
29
30
  .to_return response(body:'Non-JSON response', status:200)
30
31
  end
31
32
 
32
- def api_server_error(type = :json)
33
- body = '500 Server Error'
33
+ def api_error(type: :json, body: 'Not Found', status: 404, url: 'example.com/api')
34
+ body = "#{status} #{body}"
35
+ body = { 'error' => body } if type == :json
34
36
 
35
- body = { 'error' => body }.to_json if type == :json
36
-
37
- @rspec.stub_request(:any, 'example.com/api')
38
- .to_return response(body:body, status:500)
39
- end
40
-
41
- def api_client_error(type = :json)
42
- body = '404 Not Found'
43
-
44
- body = { 'error' => body }.to_json if type == :json
45
-
46
- @rspec.stub_request(:any, 'example.com/api')
47
- .to_return defaults.merge(body:body, status:404)
37
+ @rspec.stub_request(:get, url)
38
+ .to_return response(body:body, status:status.to_i)
48
39
  end
49
40
 
50
41
  def api_description
@@ -57,6 +48,11 @@ class HttpStubs
57
48
  .to_return response(body:{ 'error' => '404 Not Found' }, status:404)
58
49
  end
59
50
 
51
+ def invalid_unknown_instance
52
+ @rspec.stub_request(:get, 'example.com/api/my_temp_resources/1')
53
+ .to_return response(body:'Unparseable Not Found', status:404)
54
+ end
55
+
60
56
  def known_instance
61
57
  @rspec.stub_request(:get, 'example.com/api/my_temp_resources/1')
62
58
  .to_return response(body:{ 'name' => 'Resource Name' })
@@ -68,13 +64,46 @@ class HttpStubs
68
64
  body: {
69
65
  '_embedded' => {
70
66
  'my_temp_resources' => [
71
- { 'name' => 'Resource Name' }
67
+ persisted_resource
72
68
  ]
73
69
  }
74
70
  }
75
71
  )
76
72
  end
77
73
 
74
+ def filtered_resource
75
+ @rspec.stub_request(:get, 'example.com/api/my_filtered_resources/filters/bar')
76
+ .to_return response(
77
+ body: {
78
+ '_embedded' => {
79
+ 'my_filtered_resources' => [
80
+ persisted_resource
81
+ ]
82
+ }
83
+ }
84
+ )
85
+ end
86
+
87
+ def valid_created_resource
88
+ @rspec.stub_request(:post, 'example.com/api/my_temp_resources/')
89
+ .to_return response(
90
+ body: persisted_resource
91
+ )
92
+ end
93
+
94
+ def invalid_created_resource
95
+ @rspec.stub_request(:post, 'example.com/api/my_temp_resources/')
96
+ .to_return response(
97
+ status: 422,
98
+ body: persisted_resource.merge(
99
+ 'errors' => {
100
+ 'name' => ['cannot be blank', 'must be a name'],
101
+ 'base' => ['cannot be valid']
102
+ }
103
+ )
104
+ )
105
+ end
106
+
78
107
  # rubocop:disable Metrics/MethodLength
79
108
  def schema
80
109
  {
@@ -87,6 +116,13 @@ class HttpStubs
87
116
  name: 'string'
88
117
  }
89
118
  },
119
+ my_filtered_resource: {
120
+ description: 'Humanized resource description',
121
+ properties: {
122
+ id: 'number',
123
+ name: 'string'
124
+ }
125
+ },
90
126
  abstract_resource: {
91
127
  description: 'A random thing',
92
128
  properties: {
@@ -103,6 +139,10 @@ class HttpStubs
103
139
  my_temp_resources: {
104
140
  href: '/api/my_temp_resources'
105
141
  },
142
+ my_filtered_resources: {
143
+ href: '/api/my_filtered_resources/filters/{filter}',
144
+ templated: true
145
+ },
106
146
  my_temp_resource: {
107
147
  href: '/api/my_temp_resources/{id}',
108
148
  templated: true
@@ -114,7 +154,21 @@ class HttpStubs
114
154
  }
115
155
  end
116
156
  # rubocop:enable Metrics/MethodLength
157
+
158
+ def persisted_resource(params = {})
159
+ id = params.fetch('id', 1)
160
+ {
161
+ 'id' => id,
162
+ 'name' => 'Resource Name',
163
+ '_links' => {
164
+ 'self' => {
165
+ 'href' => "/api/my_temp_resources/#{id}"
166
+ }
167
+ }
168
+ }.merge!(params)
169
+ end
117
170
  end
171
+ # rubocop:enable Metrics/ClassLength
118
172
 
119
173
  RSpec.configure do |c|
120
174
  c.before :all do
@@ -54,190 +54,176 @@ describe Frenetic do
54
54
  end
55
55
  end
56
56
 
57
- # describe '#connection' do
58
- # subject { super().connection }
59
-
60
- # it 'returns a Faraday Connection' do
61
- # expect(subject).to be_a Faraday::Connection
62
- # end
63
-
64
- # context 'when Frenetic is initialized with a block' do
65
- # subject do
66
- # builder = nil
67
- # described_class.new(test_cfg) do |b|
68
- # builder = b
69
- # end.connection
70
- # builder
71
- # end
72
-
73
- # it 'yields the Faraday builder to the block argument' do
74
- # expect(subject).to be_a Faraday::Connection
75
- # end
76
- # end
77
-
78
- # describe 'middleware' do
79
- # subject { super().builder.handlers }
80
-
81
- # context 'configured with a :username' do
82
- # let(:test_cfg) { super().merge username:'foo' }
83
-
84
- # it 'includes Basic Auth middleware' do
85
- # expect(subject).to include Faraday::Request::BasicAuthentication
86
- # end
87
- # end
88
-
89
- # context 'configured with a :api_token' do
90
- # let(:test_cfg) { super().merge api_token:'API_TOKEN' }
91
-
92
- # it 'includes Token Authentication middleware' do
93
- # expect(subject).to include Faraday::Request::TokenAuthentication
94
- # end
95
- # end
96
-
97
- # context 'configured to use Rack::Cache' do
98
- # let(:test_cfg) { super().merge cache: :rack }
99
-
100
- # it 'includes Rack middleware' do
101
- # expect(subject).to include FaradayMiddleware::RackCompatible
102
- # end
103
- # end
104
- # end
105
- # end
106
-
107
- # describe '#description' do
108
- # subject { super().description }
109
-
110
- # context 'with a URL that returns a' do
111
- # context 'valid response' do
112
- # before { @stubs.api_description }
113
-
114
- # it 'includes meta Hypermedia properties' do
115
- # expect(subject).to include '_embedded'
116
- # expect(subject).to include '_links'
117
- # end
118
- # end
119
-
120
- # context 'server error' do
121
- # before { @stubs.api_server_error }
122
-
123
- # it 'raises an error' do
124
- # expect{subject}.to raise_error Frenetic::ServerParsingError
125
- # end
126
- # end
127
-
128
- # context 'client error' do
129
- # before { @stubs.api_client_error :json }
130
-
131
- # it 'raises an error' do
132
- # expect{subject}.to raise_error Frenetic::ClientError
133
- # end
134
- # end
135
-
136
- # context 'JSON parsing error' do
137
- # context 'for an otherwise successful response' do
138
- # before { @stubs.api_html_response }
139
-
140
- # it 'raises an error' do
141
- # expect{subject}.to raise_error Frenetic::UnknownParsingError
142
- # end
143
- # end
144
-
145
- # context 'for a server error' do
146
- # before { @stubs.api_server_error :text }
147
-
148
- # it 'raises an error' do
149
- # expect{subject}.to raise_error Frenetic::ServerParsingError
150
- # end
151
- # end
152
-
153
- # context 'for a client error' do
154
- # before { @stubs.api_client_error :text }
155
-
156
- # it 'raises an error' do
157
- # expect{subject}.to raise_error Frenetic::ClientParsingError
158
- # end
159
- # end
160
- # end
161
- # end
162
- # end
163
-
164
- # describe '.schema' do
165
- # before { @stubs.api_description }
166
-
167
- # subject { super().schema }
168
-
169
- # it 'includes a list of defined resources' do
170
- # expect(subject).to include 'my_temp_resource'
171
- # end
172
- # end
173
-
174
- # describe '#get' do
175
- # subject { super().get '/foo' }
176
-
177
- # it 'delegates to Faraday' do
178
- # allow(instance.connection).to receive(:get)
179
- # subject
180
- # expect(instance.connection).to have_received(:get)
181
- # end
182
- # end
183
-
184
- # describe '#put' do
185
- # subject { super().put '/foo' }
186
-
187
- # it 'delegates to Faraday' do
188
- # allow(instance.connection).to receive(:put)
189
- # subject
190
- # expect(instance.connection).to have_received(:put)
191
- # end
192
- # end
193
-
194
- # describe '#patch' do
195
- # subject { super().patch '/foo' }
196
-
197
- # it 'delegates to Faraday' do
198
- # allow(instance.connection).to receive(:patch)
199
- # subject
200
- # expect(instance.connection).to have_received(:patch)
201
- # end
202
- # end
203
-
204
- # describe '#head' do
205
- # subject { instance.head '/foo' }
206
-
207
- # it 'delegates to Faraday' do
208
- # allow(instance.connection).to receive(:head)
209
- # subject
210
- # expect(instance.connection).to have_received(:head)
211
- # end
212
- # end
213
-
214
- # describe '#options' do
215
- # subject { instance.options '/foo' }
216
-
217
- # it 'delegates to Faraday' do
218
- # allow(instance.connection).to receive(:options)
219
- # subject
220
- # expect(instance.connection).to have_received(:options)
221
- # end
222
- # end
223
-
224
- # describe '#post' do
225
- # subject { super().post '/foo' }
226
-
227
- # it 'delegates to Faraday' do
228
- # allow(instance.connection).to receive(:post)
229
- # subject
230
- # expect(instance.connection).to have_received(:post)
231
- # end
232
- # end
233
-
234
- # describe '#delete' do
235
- # subject { super().delete '/foo' }
236
-
237
- # it 'delegates to Faraday' do
238
- # allow(instance.connection).to receive(:delete)
239
- # subject
240
- # expect(instance.connection).to have_received(:delete)
241
- # end
242
- # end
57
+ describe '#connection' do
58
+ subject { super().connection }
59
+
60
+ it 'returns a Frenetic Connection instance' do
61
+ expect(subject).to be_a Frenetic::Connection
62
+ end
63
+
64
+ describe 'middleware' do
65
+ subject { super().builder.handlers }
66
+
67
+ context 'configured with a :username' do
68
+ let(:test_cfg) { super().merge username:'foo' }
69
+
70
+ it 'includes Basic Auth middleware' do
71
+ expect(subject).to include Faraday::Request::BasicAuthentication
72
+ end
73
+ end
74
+
75
+ context 'configured with a :api_token' do
76
+ let(:test_cfg) { super().merge api_token:'API_TOKEN' }
77
+
78
+ it 'includes Token Authentication middleware' do
79
+ expect(subject).to include Faraday::Request::TokenAuthentication
80
+ end
81
+ end
82
+
83
+ context 'configured to use Rack::Cache' do
84
+ let(:test_cfg) { super().merge cache: :rack }
85
+
86
+ it 'includes Rack middleware' do
87
+ expect(subject).to include FaradayMiddleware::RackCompatible
88
+ end
89
+ end
90
+ end
91
+ end
92
+
93
+ describe '#description' do
94
+ subject { super().description }
95
+
96
+ context 'with a URL that returns a' do
97
+ context 'valid response' do
98
+ before { @stubs.api_description }
99
+
100
+ it 'includes meta Hypermedia properties' do
101
+ expect(subject).to include '_embedded'
102
+ expect(subject).to include '_links'
103
+ end
104
+ end
105
+
106
+ context 'server error' do
107
+ before { @stubs.api_error(type: :text, status: 500) }
108
+
109
+ it 'raises an error' do
110
+ expect{subject}.to raise_error Frenetic::ServerParsingError
111
+ end
112
+ end
113
+
114
+ context 'client error' do
115
+ before { @stubs.api_error }
116
+
117
+ it 'raises an error' do
118
+ expect{subject}.to raise_error Frenetic::ClientError
119
+ end
120
+ end
121
+
122
+ context 'JSON parsing error' do
123
+ context 'for an otherwise successful response' do
124
+ before { @stubs.api_html_response }
125
+
126
+ it 'raises an error' do
127
+ expect{subject}.to raise_error Frenetic::UnknownParsingError
128
+ end
129
+ end
130
+
131
+ context 'for a server error' do
132
+ before { @stubs.api_error(type: :text, status: 500) }
133
+
134
+ it 'raises an error' do
135
+ expect{subject}.to raise_error Frenetic::ServerParsingError
136
+ end
137
+ end
138
+
139
+ context 'for a client error' do
140
+ before { @stubs.api_error(type: :text) }
141
+
142
+ it 'raises an error' do
143
+ expect{subject}.to raise_error Frenetic::ClientParsingError
144
+ end
145
+ end
146
+ end
147
+ end
148
+ end
149
+
150
+ describe '.schema' do
151
+ before { @stubs.api_description }
152
+
153
+ subject { super().schema }
154
+
155
+ it 'includes a list of defined resources' do
156
+ expect(subject).to include 'my_temp_resource'
157
+ end
158
+ end
159
+
160
+ describe '#get' do
161
+ subject { super().get '/foo' }
162
+
163
+ it 'delegates to Faraday' do
164
+ allow(instance.connection).to receive(:get)
165
+ subject
166
+ expect(instance.connection).to have_received(:get)
167
+ end
168
+ end
169
+
170
+ describe '#put' do
171
+ subject { super().put '/foo' }
172
+
173
+ it 'delegates to Faraday' do
174
+ allow(instance.connection).to receive(:put)
175
+ subject
176
+ expect(instance.connection).to have_received(:put)
177
+ end
178
+ end
179
+
180
+ describe '#patch' do
181
+ subject { super().patch '/foo' }
182
+
183
+ it 'delegates to Faraday' do
184
+ allow(instance.connection).to receive(:patch)
185
+ subject
186
+ expect(instance.connection).to have_received(:patch)
187
+ end
188
+ end
189
+
190
+ describe '#head' do
191
+ subject { instance.head '/foo' }
192
+
193
+ it 'delegates to Faraday' do
194
+ allow(instance.connection).to receive(:head)
195
+ subject
196
+ expect(instance.connection).to have_received(:head)
197
+ end
198
+ end
199
+
200
+ describe '#options' do
201
+ subject { instance.options '/foo' }
202
+
203
+ it 'delegates to Faraday' do
204
+ allow(instance.connection).to receive(:options)
205
+ subject
206
+ expect(instance.connection).to have_received(:options)
207
+ end
208
+ end
209
+
210
+ describe '#post' do
211
+ subject { super().post '/foo' }
212
+
213
+ it 'delegates to Faraday' do
214
+ allow(instance.connection).to receive(:post)
215
+ subject
216
+ expect(instance.connection).to have_received(:post)
217
+ end
218
+ end
219
+
220
+ describe '#delete' do
221
+ subject { super().delete '/foo' }
222
+
223
+ it 'delegates to Faraday' do
224
+ allow(instance.connection).to receive(:delete)
225
+ subject
226
+ expect(instance.connection).to have_received(:delete)
227
+ end
228
+ end
243
229
  end