harpy 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.
- data/.gitignore +4 -0
- data/.rspec +3 -0
- data/Gemfile +4 -0
- data/LICENSE +22 -0
- data/README.md +168 -0
- data/Rakefile +8 -0
- data/harpy.gemspec +29 -0
- data/lib/harpy/client.rb +66 -0
- data/lib/harpy/entry_point.rb +35 -0
- data/lib/harpy/resource.rb +197 -0
- data/lib/harpy/version.rb +3 -0
- data/lib/harpy.rb +46 -0
- data/spec/harpy/client_spec.rb +96 -0
- data/spec/harpy/entry_point_spec.rb +56 -0
- data/spec/harpy/resource_spec.rb +516 -0
- data/spec/harpy_spec.rb +97 -0
- data/spec/spec_helper.rb +13 -0
- metadata +159 -0
@@ -0,0 +1,56 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe Harpy::EntryPoint do
|
4
|
+
let(:url) { "http://localhost" }
|
5
|
+
subject { Harpy::EntryPoint.new url}
|
6
|
+
|
7
|
+
it "should store url" do
|
8
|
+
subject.url.should == url
|
9
|
+
end
|
10
|
+
|
11
|
+
describe "#resource_url(resource_type)" do
|
12
|
+
let(:company_url) { "#{url}/company"}
|
13
|
+
let(:json_response) { %Q|{"link": [{"rel": "company", "href": "#{company_url}"}]}| }
|
14
|
+
let(:success_response) { Typhoeus::Response.new :code => 200, :body => json_response }
|
15
|
+
let(:error_response) { Typhoeus::Response.new :code => 500 }
|
16
|
+
it "gets entry point from url using client" do
|
17
|
+
Harpy.client.should_receive(:get).with(url).and_return success_response
|
18
|
+
subject.resource_url "user"
|
19
|
+
end
|
20
|
+
it "return nil if no link for resource_type" do
|
21
|
+
Harpy.client.should_receive(:get).with(url).and_return success_response
|
22
|
+
subject.resource_url("user").should be_nil
|
23
|
+
end
|
24
|
+
it "return url for existing resource_type" do
|
25
|
+
Harpy.client.should_receive(:get).with(url).and_return success_response
|
26
|
+
subject.resource_url("company_url").should be_nil
|
27
|
+
end
|
28
|
+
it "delegates response code != 200 to client" do
|
29
|
+
Harpy.client.should_receive(:get).with(url).and_return error_response
|
30
|
+
Harpy.client.should_receive(:invalid_code).with error_response
|
31
|
+
subject.resource_url("company_url")
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
describe "#urn(urn)" do
|
36
|
+
let(:urn) { "urn:harpy:company:1" }
|
37
|
+
let(:company_url) { "#{url}/company/1"}
|
38
|
+
let(:success_response) { Typhoeus::Response.new :code => 301, :headers => "Location: #{company_url}" }
|
39
|
+
let(:error_response) { Typhoeus::Response.new :code => 500 }
|
40
|
+
let(:not_found_response) { Typhoeus::Response.new :code => 404 }
|
41
|
+
it "query remote for this urn using client" do
|
42
|
+
Harpy.client.should_receive(:get).with("#{url}/#{urn}").and_return success_response
|
43
|
+
subject.urn(urn).should == company_url
|
44
|
+
end
|
45
|
+
it "return nil if not found" do
|
46
|
+
Harpy.client.should_receive(:get).with("#{url}/#{urn}").and_return not_found_response
|
47
|
+
subject.urn(urn).should be_nil
|
48
|
+
end
|
49
|
+
it "delegates response code != 301 or 404 to client" do
|
50
|
+
Harpy.client.should_receive(:get).with("#{url}/#{urn}").and_return error_response
|
51
|
+
Harpy.client.should_receive(:invalid_code).with error_response
|
52
|
+
subject.urn(urn)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
end
|
@@ -0,0 +1,516 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe Harpy::Resource do
|
4
|
+
describe ".from_url(hash)" do
|
5
|
+
let(:company_url) { "http://localhost/company/1" }
|
6
|
+
let(:json_response) { %Q|{"name": "Harpy ltd", "link": [{"rel": "self", "href": "#{company_url}"}]}| }
|
7
|
+
let(:success_response) { Typhoeus::Response.new :code => 200, :body => json_response}
|
8
|
+
it "queries multiple resources in parallel and return instances" do
|
9
|
+
Typhoeus::Hydra.hydra.stub(:get, company_url).and_return success_response
|
10
|
+
responses = Harpy::Resource.from_url({ Harpy::Spec::Company => [company_url] })
|
11
|
+
responses.should have(1).keys
|
12
|
+
responses[Harpy::Spec::Company].should have(1).item
|
13
|
+
responses[Harpy::Spec::Company].first.name.should == "Harpy ltd"
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
module Harpy
|
19
|
+
module Spec
|
20
|
+
class Company
|
21
|
+
include Harpy::Resource
|
22
|
+
end
|
23
|
+
class User
|
24
|
+
include Harpy::Resource
|
25
|
+
validates_presence_of :firstname
|
26
|
+
before_validation :check_lastname
|
27
|
+
before_save :callback_before_save
|
28
|
+
before_create :callback_before_create
|
29
|
+
before_update :callback_before_update
|
30
|
+
attr_reader :callbacks
|
31
|
+
def initialize(*args)
|
32
|
+
@callbacks = []
|
33
|
+
super
|
34
|
+
end
|
35
|
+
def check_lastname
|
36
|
+
!!lastname
|
37
|
+
end
|
38
|
+
def callback_before_save
|
39
|
+
@callbacks << :save
|
40
|
+
end
|
41
|
+
def callback_before_create
|
42
|
+
@callbacks << :create
|
43
|
+
end
|
44
|
+
def callback_before_update
|
45
|
+
@callbacks << :update
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
describe "class including Harpy::Resource" do
|
51
|
+
subject{ Harpy::Spec::Company.new }
|
52
|
+
after{ Harpy.reset }
|
53
|
+
describe ".from_url(url)" do
|
54
|
+
context "called with only one url" do
|
55
|
+
let(:url){ "http://localhost/company/1" }
|
56
|
+
it "is a properly filled-in instance of Harpy::Spec::Company on success" do
|
57
|
+
response = Typhoeus::Response.new :code => 200, :body => <<-eos
|
58
|
+
{
|
59
|
+
"name": "Harpy Ltd",
|
60
|
+
"link": [
|
61
|
+
{"rel": "self", "href": "#{url}"}
|
62
|
+
]
|
63
|
+
}
|
64
|
+
eos
|
65
|
+
Harpy.client.should_receive(:get).with(url).and_return response
|
66
|
+
result = Harpy::Spec::Company.from_url url
|
67
|
+
result.should be_kind_of Harpy::Spec::Company
|
68
|
+
result.name.should == "Harpy Ltd"
|
69
|
+
result.link("self").should == url
|
70
|
+
end
|
71
|
+
it "is nil when not found" do
|
72
|
+
response = Typhoeus::Response.new :code => 404
|
73
|
+
Harpy.client.should_receive(:get).with(url).and_return response
|
74
|
+
Harpy::Spec::Company.from_url(url).should be_nil
|
75
|
+
end
|
76
|
+
it "delegates response code != 200 or 404 to client" do
|
77
|
+
response = Typhoeus::Response.new :code => 500
|
78
|
+
Harpy.client.should_receive(:get).with(url).and_return response
|
79
|
+
Harpy.client.should_receive(:invalid_code).with response
|
80
|
+
Harpy::Spec::Company.from_url(url)
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
describe ".from_id(id)" do
|
85
|
+
context "when entry point is configured" do
|
86
|
+
it "raises Harpy::EntryPointRequired" do
|
87
|
+
lambda{
|
88
|
+
Harpy::Spec::Company.from_id(1)
|
89
|
+
}.should raise_error Harpy::EntryPointRequired
|
90
|
+
end
|
91
|
+
end
|
92
|
+
context "when entry point is set but urn has not been overriden" do
|
93
|
+
it "raises NotImplementedError" do
|
94
|
+
Harpy.entry_point_url = "http://localhost"
|
95
|
+
lambda{
|
96
|
+
Harpy::Spec::Company.from_id(1)
|
97
|
+
}.should raise_error NotImplementedError
|
98
|
+
end
|
99
|
+
end
|
100
|
+
context "when entry point is set and urn has been overriden" do
|
101
|
+
let(:urn) { "urn:harpy:company:1" }
|
102
|
+
let(:url) { "http://localhost/company/1" }
|
103
|
+
it "asks Harpy.entry_point to convert urn to url then call .from_url" do
|
104
|
+
# 1 -> urn:harpy:company:1
|
105
|
+
Harpy::Spec::Company.should_receive(:urn).with(1).and_return urn
|
106
|
+
|
107
|
+
# urn:harpy:company:1 -> http://localhost/company/1
|
108
|
+
Harpy.entry_point = mock
|
109
|
+
Harpy.entry_point.should_receive(:urn).with(urn).and_return url
|
110
|
+
|
111
|
+
# http://localhost/company/1 -> Harpy::Spec::Company instance
|
112
|
+
Harpy::Spec::Company.should_receive(:from_url).with(url).and_return(expected = mock)
|
113
|
+
Harpy::Spec::Company.from_id(1).should be expected
|
114
|
+
end
|
115
|
+
it "is nil if urn is not found" do
|
116
|
+
# 1 -> urn:harpy:company:1
|
117
|
+
Harpy::Spec::Company.should_receive(:urn).with(1).and_return urn
|
118
|
+
|
119
|
+
# urn:harpy:company:1 -> nil
|
120
|
+
Harpy.entry_point = mock
|
121
|
+
Harpy.entry_point.should_receive(:urn).with(urn)
|
122
|
+
|
123
|
+
Harpy::Spec::Company.from_id(1).should be_nil
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
127
|
+
describe ".urn(id)" do
|
128
|
+
it "raises NotImplementedError" do
|
129
|
+
lambda{
|
130
|
+
Harpy::Spec::Company.urn(1)
|
131
|
+
}.should raise_error NotImplementedError
|
132
|
+
end
|
133
|
+
end
|
134
|
+
describe ".resource_name" do
|
135
|
+
it "defaults to underscored class name" do
|
136
|
+
Harpy::Spec::Company.resource_name.should == "harpy/spec/company"
|
137
|
+
end
|
138
|
+
end
|
139
|
+
describe ".search(conditions)" do
|
140
|
+
let(:url){ "http://localhost/company" }
|
141
|
+
before do
|
142
|
+
Harpy.entry_point = mock
|
143
|
+
Harpy.entry_point.should_receive(:resource_url).with("harpy/spec/company").and_return url
|
144
|
+
end
|
145
|
+
it "return properly filled instances on 200" do
|
146
|
+
response = Typhoeus::Response.new :code => 200, :body => <<-eos
|
147
|
+
{
|
148
|
+
"harpy/spec/company": [
|
149
|
+
{
|
150
|
+
"firstname": "Anthony",
|
151
|
+
"urn": "urn:harpy:company:1",
|
152
|
+
"link": [
|
153
|
+
{"rel": "self", "href": "#{url}/1"}
|
154
|
+
]
|
155
|
+
}
|
156
|
+
],
|
157
|
+
"link": [
|
158
|
+
{"rel": "self", "href": "#{url}"}
|
159
|
+
]
|
160
|
+
}
|
161
|
+
eos
|
162
|
+
Harpy.client.should_receive(:get).with(url, :params => {"firstname" => "Anthony"}).and_return response
|
163
|
+
companies = Harpy::Spec::Company.search "firstname" => "Anthony"
|
164
|
+
companies.should have(1).item
|
165
|
+
companies.first.should be_kind_of Harpy::Spec::Company
|
166
|
+
companies.first.firstname.should == "Anthony"
|
167
|
+
companies.first.id.should == "1"
|
168
|
+
end
|
169
|
+
it "delegates other response codes to client" do
|
170
|
+
response = Typhoeus::Response.new :code => 500
|
171
|
+
Harpy.client.should_receive(:get).with(url, :params => {}).and_return response
|
172
|
+
Harpy.client.should_receive(:invalid_code).with response
|
173
|
+
Harpy::Spec::Company.search
|
174
|
+
end
|
175
|
+
end
|
176
|
+
describe ".with_url(url)" do
|
177
|
+
let(:url){ "http://localhost/user/1/company" }
|
178
|
+
it "overrides url used for searches" do
|
179
|
+
response = Typhoeus::Response.new :code => 500
|
180
|
+
Harpy.client.should_receive(:get).with(url, :params => {}).and_return response
|
181
|
+
Harpy.client.should_receive(:invalid_code).with response
|
182
|
+
Harpy::Spec::Company.with_url(url) do
|
183
|
+
Harpy::Spec::Company.search
|
184
|
+
end
|
185
|
+
end
|
186
|
+
it "can be nested" do
|
187
|
+
url2 = "http://localhost/user/2/company"
|
188
|
+
response = Typhoeus::Response.new :code => 500
|
189
|
+
Harpy.client.should_receive(:get).ordered.with(url2, :params => {}).and_return response
|
190
|
+
Harpy.client.should_receive(:get).ordered.with(url, :params => {}).and_return response
|
191
|
+
Harpy.client.should_receive(:invalid_code).twice.with response
|
192
|
+
Harpy::Spec::Company.with_url(url) do
|
193
|
+
Harpy::Spec::Company.with_url(url2) do
|
194
|
+
Harpy::Spec::Company.search
|
195
|
+
end
|
196
|
+
Harpy::Spec::Company.search
|
197
|
+
end
|
198
|
+
end
|
199
|
+
end
|
200
|
+
describe "mass assignment" do
|
201
|
+
it "sets any attribute on initialization" do
|
202
|
+
company = Harpy::Spec::Company.new "name" => "Harpy Ltd"
|
203
|
+
company.name.should == "Harpy Ltd"
|
204
|
+
end
|
205
|
+
it "initialization works with string keys only" do
|
206
|
+
company = Harpy::Spec::Company.new :name => "Harpy Ltd"
|
207
|
+
company.name.should be_nil
|
208
|
+
end
|
209
|
+
it "#attributes=(attrs) merges new attributes with previous ones" do
|
210
|
+
company = Harpy::Spec::Company.new "name" => "Harpy Ltd", "deleted" => false
|
211
|
+
company.attributes = {"name" => "Harpy Inc", "custom" => true}
|
212
|
+
company.name.should eql "Harpy Inc"
|
213
|
+
company.deleted.should be_false
|
214
|
+
company.custom.should be_true
|
215
|
+
end
|
216
|
+
it "#attributes=(attrs) works with string keys only" do
|
217
|
+
company = Harpy::Spec::Company.new "name" => "Harpy Ltd"
|
218
|
+
company.attributes = {:name => "Harpy Inc"}
|
219
|
+
company.name.should eql "Harpy Ltd"
|
220
|
+
end
|
221
|
+
it "doesn't define writers for each attribute" do
|
222
|
+
company = Harpy::Spec::Company.new "name" => "Harpy Ltd"
|
223
|
+
company.should_not respond_to(:name=)
|
224
|
+
end
|
225
|
+
it "allows accessing undefined attributes when not persisted" do
|
226
|
+
subject.name.should be_nil
|
227
|
+
end
|
228
|
+
it "doesn't allows accessing undefined attributes when not persisted" do
|
229
|
+
subject.should_receive(:persisted?).and_return true
|
230
|
+
lambda{ subject.name }.should raise_error NoMethodError
|
231
|
+
end
|
232
|
+
end
|
233
|
+
describe "advanced attribute readers" do
|
234
|
+
let(:url){ "http://localhost/company/1" }
|
235
|
+
subject{ Harpy::Spec::Company.new "link" => [{"rel" => "self", "href" => url}] }
|
236
|
+
describe "#link(rel)" do
|
237
|
+
it "searches link with matching rel and return href" do
|
238
|
+
subject.link("self").should == url
|
239
|
+
end
|
240
|
+
it "works with symbols too" do
|
241
|
+
subject.link(:self).should == url
|
242
|
+
end
|
243
|
+
it "is nil if no matching link can be found" do
|
244
|
+
subject.link("user").should be_nil
|
245
|
+
end
|
246
|
+
end
|
247
|
+
describe "#url" do
|
248
|
+
it "searches url to self inside links" do
|
249
|
+
subject.should_receive(:link).with("self").and_return (expected = mock)
|
250
|
+
subject.url.should be expected
|
251
|
+
end
|
252
|
+
it "is nil when no link to self can be found" do
|
253
|
+
subject.should_receive(:link).with "self"
|
254
|
+
subject.url.should be_nil
|
255
|
+
end
|
256
|
+
end
|
257
|
+
describe "#url_collection" do
|
258
|
+
it "defaults to entry_point link which rel matches resource name" do
|
259
|
+
Harpy.entry_point = mock
|
260
|
+
Harpy.entry_point.should_receive(:resource_url).with("harpy/spec/company").and_return (expected = mock)
|
261
|
+
subject.url_collection.should be expected
|
262
|
+
end
|
263
|
+
end
|
264
|
+
describe "#id" do
|
265
|
+
it "extracts last part from urn" do
|
266
|
+
company = Harpy::Spec::Company.new "urn" => "urn:harpy:company:1"
|
267
|
+
company.id.should == "1"
|
268
|
+
end
|
269
|
+
it "works with alphanumeric ids too" do
|
270
|
+
company = Harpy::Spec::Company.new "urn" => "urn:harpy:company:e{39,^"
|
271
|
+
company.id.should == "e{39,^"
|
272
|
+
end
|
273
|
+
it "works with urns having more than 4 parts" do
|
274
|
+
company = Harpy::Spec::Company.new "urn" => "urn:harpy:api:company:1"
|
275
|
+
company.id.should == "1"
|
276
|
+
end
|
277
|
+
it "is nil without urn" do
|
278
|
+
subject.id.should be_nil
|
279
|
+
end
|
280
|
+
it "is nil if urn is an empty string" do
|
281
|
+
company = Harpy::Spec::Company.new "urn" => ""
|
282
|
+
company.id.should be_nil
|
283
|
+
end
|
284
|
+
it "never uses manually assigned id attribute" do
|
285
|
+
company = Harpy::Spec::Company.new "id" => "1"
|
286
|
+
company.id.should be_nil
|
287
|
+
end
|
288
|
+
end
|
289
|
+
describe "#persisted?" do
|
290
|
+
it "defaults to false when no urn is defined" do
|
291
|
+
company = Harpy::Spec::Company.new
|
292
|
+
company.persisted?.should be_false
|
293
|
+
end
|
294
|
+
it "is true when an urn is present" do
|
295
|
+
company = Harpy::Spec::Company.new "urn" => "urn:harpy:company:1"
|
296
|
+
company.persisted?.should be_true
|
297
|
+
end
|
298
|
+
it "is false when urn is an empty string" do
|
299
|
+
company = Harpy::Spec::Company.new "urn" => ""
|
300
|
+
company.persisted?.should be_false
|
301
|
+
end
|
302
|
+
end
|
303
|
+
describe "#inspect" do
|
304
|
+
subject { Harpy::Spec::Company.new "firstname" => "Anthony" }
|
305
|
+
it "shows class name, attributes, errors and persisted state" do
|
306
|
+
if RUBY_VERSION.to_f < 1.9
|
307
|
+
subject.inspect.should == '<Harpy::Spec::Company @attrs:{"firstname"=>"Anthony"} @errors:#<OrderedHash {}> persisted:false>'
|
308
|
+
else
|
309
|
+
subject.inspect.should == '<Harpy::Spec::Company @attrs:{"firstname"=>"Anthony"} @errors:{} persisted:false>'
|
310
|
+
end
|
311
|
+
end
|
312
|
+
end
|
313
|
+
describe "#has_key?(key)" do
|
314
|
+
it "is true when attribute is present" do
|
315
|
+
subject.has_key?("link").should be_true
|
316
|
+
end
|
317
|
+
it "does accept symbols too" do
|
318
|
+
subject.has_key?(:link).should be_true
|
319
|
+
end
|
320
|
+
it "is true when attribute is an empty string" do
|
321
|
+
company = Harpy::Spec::Company.new "urn" => ""
|
322
|
+
company.has_key?("urn").should be_true
|
323
|
+
end
|
324
|
+
it "is false when attribute is not defined" do
|
325
|
+
subject.has_key?("custom").should be_false
|
326
|
+
end
|
327
|
+
it "is false when key matches an existing method but not an attribute" do
|
328
|
+
subject.has_key?(:url).should be_false
|
329
|
+
end
|
330
|
+
it "does not raise error when checking for an undefined attribute even when persisted" do
|
331
|
+
subject.stub(:persisted?).and_return true
|
332
|
+
subject.has_key?("custom").should be_false
|
333
|
+
end
|
334
|
+
end
|
335
|
+
end
|
336
|
+
describe "#valid?" do
|
337
|
+
it "has ActiveModel validations" do
|
338
|
+
user = Harpy::Spec::User.new "lastname" => "Stark"
|
339
|
+
user.should_not be_valid
|
340
|
+
user.should have(1).error
|
341
|
+
user.errors[:firstname].should =~ ["can't be blank"]
|
342
|
+
end
|
343
|
+
it "calls before_validation which prevents validation on false" do
|
344
|
+
user = Harpy::Spec::User.new
|
345
|
+
user.should_not be_valid
|
346
|
+
user.should have(:no).error
|
347
|
+
end
|
348
|
+
end
|
349
|
+
describe "#as_json" do
|
350
|
+
let(:url) { "http://localhost/user/1" }
|
351
|
+
subject do
|
352
|
+
Harpy::Spec::User.new({
|
353
|
+
"urn" => "urn:harpy:user:1",
|
354
|
+
"company_name" => "Stark Enterprises",
|
355
|
+
"link" => [{"rel" => "self", "href" => url}],
|
356
|
+
})
|
357
|
+
end
|
358
|
+
|
359
|
+
it "exclude link and urn from attributes" do
|
360
|
+
subject.as_json.should == {"company_name" => "Stark Enterprises"}
|
361
|
+
end
|
362
|
+
it "does not remove link and urn from object attributes" do
|
363
|
+
subject.as_json
|
364
|
+
subject.urn.should == "urn:harpy:user:1"
|
365
|
+
subject.url.should == url
|
366
|
+
end
|
367
|
+
end
|
368
|
+
describe "#save" do
|
369
|
+
subject{ Harpy::Spec::User.new "company_name" => "Stark Enterprises" }
|
370
|
+
it "is false and does not call before_save callback if not valid" do
|
371
|
+
subject.should_receive :valid?
|
372
|
+
subject.save.should be_false
|
373
|
+
subject.callbacks.should =~ []
|
374
|
+
end
|
375
|
+
context "on create (valid, not persisted)" do
|
376
|
+
let(:url) { "http://localhost/user" }
|
377
|
+
let(:body) { '{"company_name":"Stark Enterprises"}' }
|
378
|
+
before do
|
379
|
+
subject.should_receive(:valid?).and_return true
|
380
|
+
subject.should_receive(:url_collection).and_return url
|
381
|
+
end
|
382
|
+
[200, 201, 302].each do |response_code|
|
383
|
+
it "is true and merges response attributes on #{response_code}" do
|
384
|
+
response = Typhoeus::Response.new :code => response_code, :body => <<-eos
|
385
|
+
{
|
386
|
+
"firstname": "Anthony",
|
387
|
+
"urn": "urn:harpy:user:1",
|
388
|
+
"link": [
|
389
|
+
{"rel": "self", "href": "#{url}/1"}
|
390
|
+
]
|
391
|
+
}
|
392
|
+
eos
|
393
|
+
Harpy.client.should_receive(:post).with(url, :body => body).and_return response
|
394
|
+
subject.save.should be_true
|
395
|
+
subject.callbacks.should =~ [:save, :create]
|
396
|
+
subject.firstname.should == "Anthony"
|
397
|
+
subject.company_name.should == "Stark Enterprises"
|
398
|
+
subject.urn.should == "urn:harpy:user:1"
|
399
|
+
end
|
400
|
+
end
|
401
|
+
it "raises Harpy::InvalidResponseCode on 204" do
|
402
|
+
response = Typhoeus::Response.new :code => 204
|
403
|
+
Harpy.client.should_receive(:post).with(url, :body => body).and_return response
|
404
|
+
lambda { subject.save }.should raise_error Harpy::InvalidResponseCode, "204"
|
405
|
+
subject.callbacks.should =~ [:save, :create]
|
406
|
+
end
|
407
|
+
it "raises Harpy::Unauthorized on 401" do
|
408
|
+
response = Typhoeus::Response.new :code => 401
|
409
|
+
Harpy.client.should_receive(:post).with(url, :body => body).and_return response
|
410
|
+
lambda { subject.save }.should raise_error Harpy::Unauthorized, "Server returned a 401 response code"
|
411
|
+
subject.callbacks.should =~ [:save, :create]
|
412
|
+
end
|
413
|
+
it "is false and fills in errors on 422" do
|
414
|
+
response = Typhoeus::Response.new :code => 422, :body => <<-eos
|
415
|
+
{
|
416
|
+
"firstname": "Anthony",
|
417
|
+
"errors": {
|
418
|
+
"lastname": ["can't be blank", "must be unique"]
|
419
|
+
}
|
420
|
+
}
|
421
|
+
eos
|
422
|
+
Harpy.client.should_receive(:post).with(url, :body => body).and_return response
|
423
|
+
subject.save.should be_false
|
424
|
+
subject.callbacks.should =~ [:save, :create]
|
425
|
+
subject.should have(1).error
|
426
|
+
subject.errors[:lastname].should =~ ["can't be blank", "must be unique"]
|
427
|
+
subject.firstname.should be_nil
|
428
|
+
subject.company_name.should == "Stark Enterprises"
|
429
|
+
end
|
430
|
+
it "delegates other response codes to client" do
|
431
|
+
response = Typhoeus::Response.new :code => 500
|
432
|
+
Harpy.client.should_receive(:post).with(url, :body => body).and_return response
|
433
|
+
Harpy.client.should_receive(:invalid_code).with(response)
|
434
|
+
subject.save
|
435
|
+
subject.callbacks.should =~ [:save, :create]
|
436
|
+
end
|
437
|
+
end
|
438
|
+
context "on update (valid, persisted but link to self is not present)" do
|
439
|
+
it "raises Harpy::UrlRequired" do
|
440
|
+
subject.should_receive(:valid?).and_return true
|
441
|
+
subject.should_receive(:persisted?).and_return true
|
442
|
+
lambda{ subject.save }.should raise_error Harpy::UrlRequired
|
443
|
+
subject.callbacks.should =~ [:save, :update]
|
444
|
+
end
|
445
|
+
end
|
446
|
+
context "on update (valid, persisted and link to self is present)" do
|
447
|
+
let(:url) { "http://localhost/user/1" }
|
448
|
+
let(:body) { '{"company_name":"Stark Enterprises"}' }
|
449
|
+
subject do
|
450
|
+
Harpy::Spec::User.new({
|
451
|
+
"urn" => "urn:harpy:user:1",
|
452
|
+
"company_name" => "Stark Enterprises",
|
453
|
+
"link" => [{"rel" => "self", "href" => url}],
|
454
|
+
})
|
455
|
+
end
|
456
|
+
before do
|
457
|
+
subject.should_receive(:valid?).and_return true
|
458
|
+
end
|
459
|
+
[200, 201, 302].each do |response_code|
|
460
|
+
it "is true and merges response attributes on #{response_code}" do
|
461
|
+
response = Typhoeus::Response.new :code => response_code, :body => <<-eos
|
462
|
+
{
|
463
|
+
"firstname": "Anthony",
|
464
|
+
"urn": "urn:harpy:user:1",
|
465
|
+
"link": [
|
466
|
+
{"rel": "self", "href": "#{url}/1"}
|
467
|
+
]
|
468
|
+
}
|
469
|
+
eos
|
470
|
+
Harpy.client.should_receive(:put).with(url, :body => body).and_return response
|
471
|
+
subject.save.should be_true
|
472
|
+
subject.callbacks.should =~ [:save, :update]
|
473
|
+
subject.firstname.should == "Anthony"
|
474
|
+
subject.company_name.should == "Stark Enterprises"
|
475
|
+
end
|
476
|
+
end
|
477
|
+
it "is true but doesn't touch attributes on 204" do
|
478
|
+
response = Typhoeus::Response.new :code => 204
|
479
|
+
Harpy.client.should_receive(:put).with(url, :body => body).and_return response
|
480
|
+
subject.save.should be_true
|
481
|
+
subject.callbacks.should =~ [:save, :update]
|
482
|
+
subject.company_name.should == "Stark Enterprises"
|
483
|
+
end
|
484
|
+
it "raises Harpy::Unauthorized on 401" do
|
485
|
+
response = Typhoeus::Response.new :code => 401
|
486
|
+
Harpy.client.should_receive(:put).with(url, :body => body).and_return response
|
487
|
+
lambda { subject.save }.should raise_error Harpy::Unauthorized, "Server returned a 401 response code"
|
488
|
+
subject.callbacks.should =~ [:save, :update]
|
489
|
+
end
|
490
|
+
it "is false and fills in errors on 422" do
|
491
|
+
response = Typhoeus::Response.new :code => 422, :body => <<-eos
|
492
|
+
{
|
493
|
+
"firstname": "Anthony",
|
494
|
+
"errors": {
|
495
|
+
"lastname": ["can't be blank", "must be unique"]
|
496
|
+
}
|
497
|
+
}
|
498
|
+
eos
|
499
|
+
Harpy.client.should_receive(:put).with(url, :body => body).and_return response
|
500
|
+
subject.save.should be_false
|
501
|
+
subject.callbacks.should =~ [:save, :update]
|
502
|
+
subject.should have(1).error
|
503
|
+
subject.errors[:lastname].should =~ ["can't be blank", "must be unique"]
|
504
|
+
lambda { subject.firstname }.should raise_error NoMethodError
|
505
|
+
subject.company_name.should == "Stark Enterprises"
|
506
|
+
end
|
507
|
+
it "delegates other response codes to client" do
|
508
|
+
response = Typhoeus::Response.new :code => 500
|
509
|
+
Harpy.client.should_receive(:put).with(url, :body => body).and_return response
|
510
|
+
Harpy.client.should_receive(:invalid_code).with(response)
|
511
|
+
subject.save
|
512
|
+
subject.callbacks.should =~ [:save, :update]
|
513
|
+
end
|
514
|
+
end
|
515
|
+
end
|
516
|
+
end
|
data/spec/harpy_spec.rb
ADDED
@@ -0,0 +1,97 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe Harpy do
|
4
|
+
after{ Harpy.reset }
|
5
|
+
|
6
|
+
it "defaults to Harpy::Client" do
|
7
|
+
Harpy.client.should be_kind_of Harpy::Client
|
8
|
+
end
|
9
|
+
|
10
|
+
it "does allow using another client" do
|
11
|
+
custom_client = mock
|
12
|
+
Harpy.client = custom_client
|
13
|
+
Harpy.client.should be custom_client
|
14
|
+
end
|
15
|
+
|
16
|
+
it "has no default entry_point_url" do
|
17
|
+
Harpy.entry_point_url.should be_nil
|
18
|
+
end
|
19
|
+
|
20
|
+
it "does allow setting an entry_point_url" do
|
21
|
+
Harpy.entry_point_url = "http://localhost"
|
22
|
+
Harpy.entry_point_url.should == "http://localhost"
|
23
|
+
end
|
24
|
+
|
25
|
+
it "raises Harpy::EntryPointRequired when trying to access entry_point and none has been created yet" do
|
26
|
+
lambda{ Harpy.entry_point }.should raise_error Harpy::EntryPointRequired
|
27
|
+
end
|
28
|
+
|
29
|
+
it "returns a valid Harpy::EntryPoint object if entry_point_url has been set" do
|
30
|
+
Harpy.entry_point_url = "http://localhost"
|
31
|
+
Harpy.entry_point.should be_kind_of Harpy::EntryPoint
|
32
|
+
Harpy.entry_point.url.should == "http://localhost"
|
33
|
+
end
|
34
|
+
|
35
|
+
it "does allow setting entry_point manually" do
|
36
|
+
Harpy.entry_point = (entry_point = mock)
|
37
|
+
Harpy.entry_point.should be entry_point
|
38
|
+
entry_point.should_receive(:url).and_return "http://localhost"
|
39
|
+
Harpy.entry_point_url.should == "http://localhost"
|
40
|
+
end
|
41
|
+
|
42
|
+
it "Harpy.reset clears both client and entry_point" do
|
43
|
+
Harpy.entry_point_url = "http://localhost"
|
44
|
+
Harpy.client = (custom_client = mock)
|
45
|
+
Harpy.reset
|
46
|
+
Harpy.entry_point_url.should be_nil
|
47
|
+
Harpy.client.should_not be custom_client
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
describe Harpy::Exception do
|
52
|
+
it "is an ::Exception" do
|
53
|
+
Harpy::Exception.ancestors.should include ::Exception
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
describe Harpy::EntryPointRequired do
|
58
|
+
it "is an Harpy::Exception" do
|
59
|
+
Harpy::EntryPointRequired.ancestors.should include Harpy::Exception
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
describe Harpy::UrlRequired do
|
64
|
+
it "is an Harpy::Exception" do
|
65
|
+
Harpy::UrlRequired.ancestors.should include Harpy::Exception
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
describe Harpy::BodyToBig do
|
70
|
+
it "is an Harpy::Exception" do
|
71
|
+
Harpy::BodyToBig.ancestors.should include Harpy::Exception
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
describe Harpy::ClientTimeout do
|
76
|
+
it "is an Harpy::Exception" do
|
77
|
+
Harpy::ClientTimeout.ancestors.should include Harpy::Exception
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
describe Harpy::ClientError do
|
82
|
+
it "is an Harpy::Exception" do
|
83
|
+
Harpy::ClientError.ancestors.should include Harpy::Exception
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
describe Harpy::Unauthorized do
|
88
|
+
it "is an Harpy::Exception" do
|
89
|
+
Harpy::Unauthorized.ancestors.should include Harpy::Exception
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
describe Harpy::InvalidResponseCode do
|
94
|
+
it "is an Harpy::Exception" do
|
95
|
+
Harpy::InvalidResponseCode.ancestors.should include Harpy::Exception
|
96
|
+
end
|
97
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
require "harpy"
|
2
|
+
require "typhoeus"
|
3
|
+
|
4
|
+
Dir[File.expand_path("../support/**/*.rb", __FILE__)].each{|f| require f}
|
5
|
+
|
6
|
+
# Do not allow external connections
|
7
|
+
Typhoeus::Hydra.allow_net_connect = false
|
8
|
+
|
9
|
+
RSpec.configure do |config|
|
10
|
+
config.after(:each) do
|
11
|
+
Typhoeus::Hydra.hydra.clear_stubs
|
12
|
+
end
|
13
|
+
end
|