rod-rest 0.0.1

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,210 @@
1
+ require 'bundler/setup'
2
+ require_relative 'test_helper'
3
+ require 'rod/rest/api'
4
+
5
+ module Rod
6
+ module Rest
7
+ describe API do
8
+ include Rack::Test::Methods
9
+
10
+ def app
11
+ Rod::Rest::API
12
+ end
13
+
14
+ context "resource API" do
15
+ # We need different resource name for each test due to Sinatra.
16
+ let(:resource_name) { "cars_" + rand.to_s }
17
+ let(:serializer) { stub!.serialize(nil) { nil_body }.subject }
18
+ let(:nil_body) { nil }
19
+ let(:plural_associations) { [] }
20
+ let(:resource) { resource = stub!.name { resource_name }.subject
21
+ stub(resource).plural_associations { plural_associations }
22
+ resource
23
+ }
24
+
25
+ before do
26
+ Rod::Rest::API.build_api_for(resource,serializer: serializer,resource_name: resource_name)
27
+ end
28
+
29
+ describe "GET /cars" do
30
+ let(:count) { 3 }
31
+ let(:count_body) { Object.new.to_s }
32
+
33
+ before do
34
+ stub(resource).count { count }
35
+ stub(serializer).serialize({count: count}) { count_body }
36
+ end
37
+
38
+ it "returns count of cars" do
39
+ get "/#{resource_name}"
40
+ last_response.status.should == 200
41
+ last_response.body.should == count_body
42
+ end
43
+
44
+ it "returns 404 for non-existing car" do
45
+ get "/non_existing"
46
+ last_response.status.should == 404
47
+ end
48
+ end
49
+
50
+ describe "GET /cars?name=Mercedes" do
51
+ let(:property_name) { "name" }
52
+ let(:invalid_property_name) { "surname" }
53
+ let(:property_value) { "Mercedes" }
54
+ let(:empty_property_value) { "Audi" }
55
+ let(:mercedes_300) { Object.new }
56
+ let(:mercedes_180) { Object.new }
57
+ let(:cars_body) { Object.new.to_s }
58
+ let(:empty_collection_body) { Object.new.to_s }
59
+
60
+ before do
61
+ stub(resource).find_all_by_name(property_value) { [mercedes_300,mercedes_180] }
62
+ stub(resource).find_all_by_name(empty_property_value) { [] }
63
+ stub(serializer).serialize([mercedes_300,mercedes_180]) { cars_body }
64
+ stub(serializer).serialize([]) { empty_collection_body }
65
+ end
66
+
67
+ it "returns serialized cars matching given indexed property" do
68
+ get "/#{resource_name}?#{property_name}=#{property_value}"
69
+ last_response.status.should == 200
70
+ last_response.body.should == cars_body
71
+ end
72
+
73
+ it "returns an empty array if there are no matching objects" do
74
+ get "/#{resource_name}?#{property_name}=#{empty_property_value}"
75
+ last_response.status.should == 200
76
+ last_response.body.should == empty_collection_body
77
+ end
78
+
79
+ it "returns 404 for non-indexed property" do
80
+ get "/#{resource_name}?#{invalid_property_name}=#{property_value}"
81
+ last_response.status.should == 404
82
+ end
83
+ end
84
+
85
+ describe "GET /cars/1" do
86
+ let(:mercedes_300_id) { 1 }
87
+ let(:audi_a4_id) { 2 }
88
+ let(:mercedes_300) { Object.new }
89
+ let(:mercedes_300_response) { Object.new.to_s }
90
+
91
+ before do
92
+ stub(resource).find_by_rod_id(mercedes_300_id) { mercedes_300 }
93
+ stub(resource).find_by_rod_id(audi_a4_id) { nil }
94
+ stub(serializer).serialize(mercedes_300) { mercedes_300_response }
95
+ end
96
+
97
+ it "returns serialized car" do
98
+ get "/#{resource_name}/#{mercedes_300_id}"
99
+ last_response.status.should == 200
100
+ last_response.body.should == mercedes_300_response
101
+ end
102
+
103
+ it "returns 404 for non-existing car" do
104
+ get "/#{resource_name}/#{audi_a4_id}"
105
+ last_response.status.should == 404
106
+ end
107
+ end
108
+
109
+ describe "GET /cars/1/drivers" do
110
+ let(:relation_name) { "drivers" }
111
+ let(:mercedes_300_id) { 1 }
112
+ let(:mercedes_300) { stub!.drivers_count { drivers_count }.subject }
113
+ let(:audi_a4_id) { 2 }
114
+ let(:drivers_count) { 4 }
115
+ let(:drivers_response){ Object.new.to_s }
116
+ let(:plural_associations) { [ property1 ] }
117
+ let(:property1) { stub!.name { relation_name }.subject }
118
+
119
+ before do
120
+ stub(resource).find_by_rod_id(mercedes_300_id) { mercedes_300 }
121
+ stub(resource).find_by_rod_id(audi_a4_id) { nil }
122
+ stub(serializer).serialize({count: drivers_count}) { drivers_response }
123
+ end
124
+
125
+ it "returns number of the drivers" do
126
+ get "/#{resource_name}/#{mercedes_300_id}/#{relation_name}"
127
+ last_response.status.should == 200
128
+ last_response.body.should == drivers_response
129
+ end
130
+
131
+ it "returns 404 for non-existing car" do
132
+ get "/#{resource_name}/#{audi_a4_id}/#{relation_name}"
133
+ last_response.status.should == 404
134
+ end
135
+
136
+ it "returns 404 for non-existing relation" do
137
+ get "/#{resource_name}/#{mercedes_300_id}/non_existing"
138
+ last_response.status.should == 404
139
+ end
140
+ end
141
+
142
+ describe "GET /cars/1/drivers/0" do
143
+ let(:relation_name) { "drivers" }
144
+ let(:plural_associations) { [ drivers_property ] }
145
+ let(:drivers_property) { stub!.name { relation_name }.subject }
146
+
147
+ let(:mercedes_300_id) { 1 }
148
+ let(:audi_a4_id) { 2 }
149
+ let(:driver_index) { 0 }
150
+ let(:invalid_driver_index){ 10 }
151
+
152
+ before do
153
+ stub(resource).find_by_rod_id(mercedes_300_id) { mercedes_300 }
154
+ stub(resource).find_by_rod_id(audi_a4_id) { nil }
155
+ end
156
+
157
+ it "returns 404 for non-existing car" do
158
+ get "/#{resource_name}/#{audi_a4_id}/#{relation_name}/#{driver_index}"
159
+ last_response.status.should == 404
160
+ end
161
+
162
+ it "returns 404 for non-existing relation" do
163
+ get "/#{resource_name}/#{mercedes_300_id}/non_existing/#{driver_index}"
164
+ last_response.status.should == 404
165
+ end
166
+
167
+ describe "with one driver" do
168
+ let(:mercedes_300) { stub!.drivers { drivers }.subject }
169
+ let(:drivers) { [schumaher] }
170
+ let(:schumaher) { Object.new }
171
+ let(:schumaher_response) { Object.new.to_s }
172
+
173
+ before do
174
+ stub(serializer).serialize(schumaher) { schumaher_response }
175
+ end
176
+
177
+ it "returns the serialized driver" do
178
+ get "/#{resource_name}/#{mercedes_300_id}/#{relation_name}/#{driver_index}"
179
+ last_response.status.should == 200
180
+ last_response.body.should == schumaher_response
181
+ end
182
+
183
+ it "returns 404 for out-of-bounds driver" do
184
+ get "/#{resource_name}/#{mercedes_300_id}/#{relation_name}/#{invalid_driver_index}"
185
+ last_response.status.should == 404
186
+ end
187
+ end
188
+ end
189
+ end
190
+
191
+ context "metadata API" do
192
+ before do
193
+ API.build_metadata_api(metadata,serializer: serializer)
194
+ end
195
+
196
+ let(:metadata) { Object.new }
197
+ let(:serializer) { stub!.dump(metadata) { dumped_metadata }.subject }
198
+ let(:dumped_metadata) { "metadata" }
199
+
200
+ describe "GET /metadata" do
201
+ it "retunrs the metadata" do
202
+ get "/metadata"
203
+ last_response.status == 200
204
+ last_response.body == dumped_metadata
205
+ end
206
+ end
207
+ end
208
+ end
209
+ end
210
+ end
@@ -0,0 +1,248 @@
1
+ require 'bundler/setup'
2
+ require_relative 'test_helper'
3
+ require 'rod/rest/client'
4
+ require 'json'
5
+ require 'cgi'
6
+
7
+ stub_class 'Rod::Rest::ProxyFactory'
8
+
9
+ module Rod
10
+ module Rest
11
+ describe Client do
12
+ let(:factory_class) { stub!.new([resource1],is_a(Client)) { factory }.subject }
13
+ let(:factory) { Object.new }
14
+ let(:metadata) { stub!.resources { [resource1] }.subject }
15
+ let(:resource1) { resource = stub!.name { resource_name }.subject
16
+ stub(resource).indexed_properties { indexed_properties }
17
+ stub(resource).plural_associations { plural_associations }
18
+ resource
19
+ }
20
+ let(:resource_name) { "Car" }
21
+ let(:indexed_properties) { [] }
22
+ let(:plural_associations) { [] }
23
+ let(:car_type) { resource_name }
24
+ let(:response) { stub!.status{ 200 }.subject }
25
+ let(:web_client) { Object.new }
26
+
27
+ describe "without metadata provided to the client" do
28
+ let(:client) { Client.new(http_client: web_client,metadata_factory: metadata_factory, factory: factory_class) }
29
+ let(:metadata_factory) { stub!.new(description: metadata_description) { metadata }.subject }
30
+ let(:metadata_description) { "{}" }
31
+
32
+ before do
33
+ stub(web_client).get("/metadata") { response }
34
+ stub(response).body { metadata_description }
35
+ end
36
+
37
+ it "fetches the metadata via the API" do
38
+ client.metadata.should == metadata
39
+ end
40
+
41
+ describe "when fetching the data via the API" do
42
+ let(:json_cars_count) { { count: 3 }.to_json }
43
+ let(:cars_response) { response = stub!.status { 200 }.subject
44
+ stub(response).body { json_cars_count }
45
+ response
46
+ }
47
+
48
+ before do
49
+ stub(web_client).get("/cars") { cars_response }
50
+ end
51
+
52
+ it "fetches the metadata before the call" do
53
+ client.cars_count.should == 3
54
+ end
55
+ end
56
+ end
57
+
58
+ describe "with metadata provided to the client" do
59
+ let(:client) { Client.new(http_client: web_client,metadata: metadata, factory: factory_class) }
60
+
61
+ let(:invalid_id) { 1000 }
62
+ let(:invalid_index) { 2000 }
63
+ let(:invalid_response) { stub!.status{ 404 }.subject }
64
+
65
+ describe "#cars_count" do
66
+ let(:json_cars_count) { { count: 3 }.to_json }
67
+
68
+ before do
69
+ stub(web_client).get("/cars") { response }
70
+ stub(response).body { json_cars_count }
71
+ end
72
+
73
+ it "returns the number of cars" do
74
+ client.cars_count.should == 3
75
+ end
76
+ end
77
+
78
+ describe "with two cars defined" do
79
+ let(:mercedes_300_id) { 1 }
80
+ let(:mercedes_300_hash) { {rod_id: mercedes_300_id, type: car_type } }
81
+ let(:mercedes_180_hash) { {rod_id: 2, type: car_type } }
82
+ let(:mercedes_300_proxy){ Object.new }
83
+ let(:mercedes_180_proxy){ Object.new }
84
+ let(:factory) { factory = stub!.build(mercedes_300_hash) { mercedes_300_proxy }.subject
85
+ stub(factory).build(mercedes_180_hash) { mercedes_180_proxy }
86
+ factory
87
+ }
88
+
89
+ describe "#find_cars_by_name(name)" do
90
+ let(:car_name) { "Mercedes" }
91
+ let(:property_name) { "name" }
92
+ let(:json_cars) { [mercedes_300_hash,mercedes_180_hash].to_json }
93
+ let(:indexed_properties){ [indexed_property] }
94
+ let(:indexed_property) { stub!.name { property_name }.subject }
95
+
96
+ before do
97
+ stub(web_client).get("/cars?#{property_name}=#{car_name}") { response }
98
+ stub(response).body { json_cars }
99
+ end
100
+
101
+ it "finds the cars by their name" do
102
+ cars = client.find_cars_by_name(car_name)
103
+ expected_cars = [mercedes_300_proxy,mercedes_180_proxy]
104
+ cars.size.should == expected_cars.size
105
+ cars.zip(expected_cars).each do |result,expected|
106
+ result.should == expected
107
+ end
108
+ end
109
+ end
110
+
111
+ describe "with car response defined" do
112
+ let(:json_mercedes_300) { mercedes_300_hash.to_json }
113
+
114
+ before do
115
+ stub(web_client).get("/cars/#{mercedes_300_id}") { response }
116
+ stub(web_client).get("/cars/#{invalid_id}") { invalid_response }
117
+ stub(response).body { json_mercedes_300 }
118
+ end
119
+
120
+ describe "#find_car(rod_id)" do
121
+ it "finds the car by its rod_id" do
122
+ client.find_car(mercedes_300_id).should == mercedes_300_proxy
123
+ end
124
+
125
+ it "raises MissingResource exception for invalid car rod_id" do
126
+ lambda { client.find_car(invalid_id)}.should raise_exception(MissingResource)
127
+ end
128
+ end
129
+
130
+ describe "#fetch_object(car_stub)" do
131
+ let(:car_stub) { { rod_id: mercedes_300_id, type: car_type } }
132
+ let(:invalid_id_stub) { { rod_id: invalid_id, type: car_type } }
133
+ let(:invalid_type_stub) { { rod_id: mercedes_300_id, type: invalid_type } }
134
+ let(:invalid_type) { "InvalidType" }
135
+
136
+ it "finds the car by its stub" do
137
+ client.fetch_object(car_stub).should == mercedes_300_proxy
138
+ end
139
+
140
+ it "raises MissingResource execption for invalid car rod_id" do
141
+ lambda { client.fetch_object(invalid_id_stub)}.should raise_exception(MissingResource)
142
+ end
143
+
144
+ it "raises APIError execption for invalid type" do
145
+ lambda { client.fetch_object(invalid_type_stub)}.should raise_exception(APIError)
146
+ end
147
+ end
148
+ end
149
+
150
+ describe "with associations" do
151
+ let(:plural_associations) { [plural_association] }
152
+ let(:plural_association) { stub!.name { association_name}.subject }
153
+ let(:association_name) { "drivers" }
154
+
155
+ describe "#car_drivers_count(rod_id)" do
156
+ let(:drivers_count) { 3 }
157
+ let(:json_driver_count) { { count: drivers_count }.to_json }
158
+
159
+
160
+ before do
161
+ stub(web_client).get("/cars/#{mercedes_300_id}/#{association_name}") { response }
162
+ stub(web_client).get("/cars/#{invalid_id}/#{association_name}") { invalid_response }
163
+ stub(response).body { json_driver_count }
164
+ end
165
+
166
+ it "returns the number of car drivers" do
167
+ client.car_drivers_count(mercedes_300_id).should == drivers_count
168
+ end
169
+
170
+ it "raises MissingResource exception for invalid car rod_id" do
171
+ lambda { client.car_drivers_count(invalid_id)}.should raise_exception(MissingResource)
172
+ end
173
+ end
174
+
175
+ describe "with reponse defined" do
176
+ let(:schumaher_index) { 0 }
177
+ let(:schumaher_hash) { { rod_id: schumaher_id, name: "Schumaher", type: "Driver" } }
178
+ let(:schumaher_json) { schumaher_hash.to_json }
179
+ let(:schumaher_proxy) { Object.new }
180
+ let(:schumaher_id) { 1 }
181
+
182
+ before do
183
+ stub(web_client).get("/cars/#{mercedes_300_id}/#{association_name}/#{schumaher_index}") { response }
184
+ stub(web_client).get("/cars/#{invalid_id}/#{association_name}/#{schumaher_index}") { invalid_response }
185
+ stub(web_client).get("/cars/#{mercedes_300_id}/#{association_name}/#{invalid_index}") { invalid_response }
186
+ stub(response).body { schumaher_json }
187
+ stub(factory).build(schumaher_hash) { schumaher_proxy }
188
+ end
189
+
190
+ describe "#car_driver(rod_id,index)" do
191
+ it "returns the driver" do
192
+ client.car_driver(mercedes_300_id,schumaher_index).should == schumaher_proxy
193
+ end
194
+
195
+ it "raises MissingResource exception for invalid car rod_id" do
196
+ lambda { client.car_driver(invalid_id,schumaher_index)}.should raise_exception(MissingResource)
197
+ end
198
+
199
+ it "raises MissingResource exception for invalid index" do
200
+ lambda { client.car_driver(mercedes_300_id,invalid_index)}.should raise_exception(MissingResource)
201
+ end
202
+ end
203
+
204
+ describe "#fetch_related_object(subject,relation,index)" do
205
+ let(:association_name) { "drivers" }
206
+ let(:invalid_association_name){ "owners" }
207
+ let(:invalid_type) { "InvalidType" }
208
+ let(:proxy_with_invalid_id) { proxy = stub!.rod_id { invalid_id }.subject
209
+ stub(proxy).type { car_type }
210
+ proxy
211
+ }
212
+ let(:proxy_with_invalid_type) { proxy = stub!.rod_id { mercedes_300_id }.subject
213
+ stub(proxy).type { invalid_type }
214
+ proxy
215
+ }
216
+
217
+ before do
218
+ stub(mercedes_300_proxy).rod_id { mercedes_300_id }
219
+ stub(mercedes_300_proxy).type { car_type }
220
+ end
221
+
222
+ it "returns the driver" do
223
+ client.fetch_related_object(mercedes_300_proxy,association_name,schumaher_index).should == schumaher_proxy
224
+ end
225
+
226
+ it "raises MissingResource exception for invalid car proxy id" do
227
+ lambda { client.fetch_related_object(proxy_with_invalid_id,association_name,schumaher_index)}.should raise_exception(MissingResource)
228
+ end
229
+
230
+ it "raises MissingResource exception for invalid index" do
231
+ lambda { client.fetch_related_object(mercedes_300_proxy,association_name,invalid_index)}.should raise_exception(MissingResource)
232
+ end
233
+
234
+ it "raises APIError exception for invalid resource type" do
235
+ lambda { client.fetch_related_object(proxy_with_invalid_type,association_name,schumaher_index)}.should raise_exception(APIError)
236
+ end
237
+
238
+ it "raises APIError exception for invalid association name" do
239
+ lambda { client.fetch_related_object(mercedes_300_proxy,invalid_association_name,schumaher_index)}.should raise_exception(APIError)
240
+ end
241
+ end
242
+ end
243
+ end
244
+ end
245
+ end
246
+ end
247
+ end
248
+ end