pdc 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.
- checksums.yaml +7 -0
- data/.editorconfig +27 -0
- data/.gitignore +10 -0
- data/.ruby-gemset +1 -0
- data/.ruby-version +1 -0
- data/.travis.yml +21 -0
- data/CODE_OF_CONDUCT.md +49 -0
- data/Gemfile +32 -0
- data/Guardfile +36 -0
- data/LICENSE +21 -0
- data/LICENSE.txt +21 -0
- data/README.md +41 -0
- data/Rakefile +11 -0
- data/bin/console +11 -0
- data/bin/setup +11 -0
- data/docs/.gitignore +3 -0
- data/docs/LICENSE_sphinx_deployment +27 -0
- data/docs/Makefile +179 -0
- data/docs/source/conf.py +257 -0
- data/docs/source/example.rst +10 -0
- data/docs/source/index.rst +23 -0
- data/docs/sphinx_deployment.mk +117 -0
- data/examples/active_attr.rb +33 -0
- data/examples/http_failures.rb +18 -0
- data/examples/local_pdc_dev.rb +15 -0
- data/examples/logger.rb +50 -0
- data/examples/pdc_curb_access_token.rb +36 -0
- data/examples/pdc_resource_tests.rb +173 -0
- data/examples/pdc_test_cache.rb +48 -0
- data/examples/prod_failures.rb +26 -0
- data/examples/prod_pdc.rb +14 -0
- data/lib/pdc/base.rb +14 -0
- data/lib/pdc/config.rb +157 -0
- data/lib/pdc/errors.rb +8 -0
- data/lib/pdc/http/errors.rb +43 -0
- data/lib/pdc/http/request/append_slash.rb +19 -0
- data/lib/pdc/http/request.rb +12 -0
- data/lib/pdc/http/response/pagination.rb +43 -0
- data/lib/pdc/http/response/parser.rb +62 -0
- data/lib/pdc/http/response/raise_error.rb +13 -0
- data/lib/pdc/http/response.rb +3 -0
- data/lib/pdc/http/result.rb +28 -0
- data/lib/pdc/http.rb +12 -0
- data/lib/pdc/logger.rb +19 -0
- data/lib/pdc/resource/attribute_modifier.rb +43 -0
- data/lib/pdc/resource/attribute_store.rb +22 -0
- data/lib/pdc/resource/attributes.rb +144 -0
- data/lib/pdc/resource/errors.rb +3 -0
- data/lib/pdc/resource/identity.rb +75 -0
- data/lib/pdc/resource/path.rb +63 -0
- data/lib/pdc/resource/per_thread_registry.rb +54 -0
- data/lib/pdc/resource/relation/finder.rb +24 -0
- data/lib/pdc/resource/relation/pagination.rb +33 -0
- data/lib/pdc/resource/relation/query.rb +14 -0
- data/lib/pdc/resource/relation.rb +81 -0
- data/lib/pdc/resource/rest_api.rb +34 -0
- data/lib/pdc/resource/scope_registry.rb +19 -0
- data/lib/pdc/resource/scopes.rb +29 -0
- data/lib/pdc/resource/value_parser.rb +31 -0
- data/lib/pdc/resource/wip.rb +0 -0
- data/lib/pdc/resource.rb +12 -0
- data/lib/pdc/v1/arch.rb +6 -0
- data/lib/pdc/v1/product.rb +5 -0
- data/lib/pdc/v1/release.rb +18 -0
- data/lib/pdc/v1/release_variant.rb +17 -0
- data/lib/pdc/v1.rb +8 -0
- data/lib/pdc/version.rb +3 -0
- data/lib/pdc.rb +25 -0
- data/pdc.gemspec +38 -0
- data/spec/fixtures/vcr/_page_count_returns_total_count.yml +141 -0
- data/spec/fixtures/vcr/brew_can_be_nil.yml +61 -0
- data/spec/fixtures/vcr/brew_may_be_present.yml +50 -0
- data/spec/fixtures/vcr/caches_multiple_response.yml +173 -0
- data/spec/fixtures/vcr/caches_response_with_a_query.yml +64 -0
- data/spec/fixtures/vcr/caches_response_with_multiple_query.yml +64 -0
- data/spec/fixtures/vcr/caches_response_without_query.yml +61 -0
- data/spec/fixtures/vcr/can_iterate_using_each.yml +187 -0
- data/spec/fixtures/vcr/fetches_variants_of_a_release.yml +663 -0
- data/spec/fixtures/vcr/must_return_number_of_resources.yml +61 -0
- data/spec/fixtures/vcr/preserves_the_filters.yml +49 -0
- data/spec/fixtures/vcr/returns_resources_on_that_page.yml +49 -0
- data/spec/fixtures/vcr/returns_the_total_count_and_not_items_in_page.yml +135 -0
- data/spec/fixtures/vcr/should_not_be_in_the_list_of_attributes.yml +95 -0
- data/spec/fixtures/vcr/works_with_where.yml +49 -0
- data/spec/pdc/config_spec.rb +115 -0
- data/spec/pdc/http/errors_spec.rb +58 -0
- data/spec/pdc/resource/attributes_spec.rb +231 -0
- data/spec/pdc/resource/cache_spec.rb +88 -0
- data/spec/pdc/resource/count_spec.rb +45 -0
- data/spec/pdc/resource/identity_spec.rb +96 -0
- data/spec/pdc/resource/pagination_spec.rb +51 -0
- data/spec/pdc/resource/path_spec.rb +30 -0
- data/spec/pdc/resource/relation_spec.rb +94 -0
- data/spec/pdc/resource/rest_api_spec.rb +23 -0
- data/spec/pdc/resource/value_parser_spec.rb +15 -0
- data/spec/pdc/resource/wip_spec.rb +0 -0
- data/spec/pdc/v1/arch_spec.rb +34 -0
- data/spec/pdc/v1/release_spec.rb +54 -0
- data/spec/pdc/version_spec.rb +5 -0
- data/spec/spec_helper.rb +39 -0
- data/spec/support/fixtures.rb +116 -0
- data/spec/support/vcr.rb +10 -0
- data/spec/support/webmock.rb +34 -0
- metadata +295 -0
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
|
|
3
|
+
describe PDC::Resource::AttributeStore do
|
|
4
|
+
subject { PDC::Resource::AttributeStore }
|
|
5
|
+
|
|
6
|
+
it '#to_params' do
|
|
7
|
+
attr = PDC::Resource::AttributeStore.new(
|
|
8
|
+
id: 3, 'title' => 'Fish',
|
|
9
|
+
groups: [
|
|
10
|
+
{ name: 'Dessert' }
|
|
11
|
+
]
|
|
12
|
+
)
|
|
13
|
+
attr.to_params.must_equal(
|
|
14
|
+
'id' => 3,
|
|
15
|
+
'title' => 'Fish',
|
|
16
|
+
'groups' => [{ 'name' => 'Dessert' }]
|
|
17
|
+
)
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
describe PDC::Resource::Attributes do
|
|
22
|
+
it 'returns all attributes' do
|
|
23
|
+
Product.attributes.wont_be_empty
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
it 'primary_key will be in attributes by default' do
|
|
27
|
+
Product.attributes.must_include Product.primary_key
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
it 'allows block initialization' do
|
|
31
|
+
prod = Product.new do |p|
|
|
32
|
+
p.name = 'RHEL'
|
|
33
|
+
p.description = 'Enterprise Linux'
|
|
34
|
+
end
|
|
35
|
+
assert_equal 'RHEL', prod.name
|
|
36
|
+
assert_equal 'Enterprise Linux', prod.description
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
it 'can handle predicate' do
|
|
40
|
+
stub_get('products/1').to_return_json(data: [{ id: 1, name: 'RHEL' }])
|
|
41
|
+
product = Product.find(1)
|
|
42
|
+
assert_equal true, product.name?
|
|
43
|
+
assert_equal false, product.description?
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
it 'can assign a hash' do
|
|
47
|
+
product = Product.new(id: 2)
|
|
48
|
+
product.attributes = { name: 'RHEL' }
|
|
49
|
+
|
|
50
|
+
product.name.must_equal 'RHEL'
|
|
51
|
+
product.id.must_equal 2
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
it 'can get and set value' do
|
|
55
|
+
product = Product.new
|
|
56
|
+
product.name = 'RHEL'
|
|
57
|
+
product.name.must_equal 'RHEL'
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
it 'works with []' do
|
|
61
|
+
product = Product.new(name: 'Fedora')
|
|
62
|
+
product[:name].must_equal 'Fedora'
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
it 'works with []=' do
|
|
66
|
+
product = Product.new
|
|
67
|
+
product[:name] = 'bar'
|
|
68
|
+
product.name.must_equal 'bar'
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
it 'allows assigning unknown attributes' do
|
|
72
|
+
product = Product.new
|
|
73
|
+
|
|
74
|
+
product.wont_respond_to :foobar
|
|
75
|
+
product.must_respond_to :foobar=
|
|
76
|
+
|
|
77
|
+
product.foobar = 'bar'
|
|
78
|
+
product.foobar.must_equal 'bar'
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
describe '#respond_to' do
|
|
82
|
+
it 'returns true for known attributes' do
|
|
83
|
+
stub_get('products/1').to_return_json(data: [{ id: 1, name: 'RHEL' }])
|
|
84
|
+
|
|
85
|
+
product = Product.find(1)
|
|
86
|
+
product.must_respond_to :name
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
it 'returns false for unknown attributes that are not set' do
|
|
90
|
+
stub_get('products/1').to_return_json(data: [{ id: 1, name: 'RHEL' }])
|
|
91
|
+
|
|
92
|
+
product = Product.find(1)
|
|
93
|
+
product.wont_respond_to :title
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
it 'returns true for unknown attributes after it is set' do
|
|
97
|
+
product = Product.new
|
|
98
|
+
product.wont_respond_to :foobar
|
|
99
|
+
|
|
100
|
+
product.foobar = 'bar'
|
|
101
|
+
product.must_respond_to :foobar
|
|
102
|
+
Product.attributes.wont_include :foobar
|
|
103
|
+
end
|
|
104
|
+
end
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
describe 'nested hash attributes' do
|
|
108
|
+
it 'returns OpenStruct for nested hash' do
|
|
109
|
+
allowed_tags = %w(foo bar baz)
|
|
110
|
+
stub_get('nested-models/1').to_return_json(
|
|
111
|
+
data: [{
|
|
112
|
+
id: 1, name: 'RHEL',
|
|
113
|
+
nested: {
|
|
114
|
+
tag: 'foobar',
|
|
115
|
+
allowed_tags: allowed_tags.clone
|
|
116
|
+
}
|
|
117
|
+
}]
|
|
118
|
+
)
|
|
119
|
+
|
|
120
|
+
model = NestedModel.find(1)
|
|
121
|
+
model.must_respond_to :nested
|
|
122
|
+
model.nested.must_be_instance_of OpenStruct
|
|
123
|
+
model.nested.must_respond_to :tag
|
|
124
|
+
model.nested.must_respond_to :allowed_tags
|
|
125
|
+
model.nested.tag.must_equal 'foobar'
|
|
126
|
+
model.nested.allowed_tags.must_equal allowed_tags
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
it 'returns OpenStruct for deep nested hash' do
|
|
130
|
+
stub_get('nested-models/1').to_return_json(
|
|
131
|
+
data: [{
|
|
132
|
+
id: 1, name: 'RHEL',
|
|
133
|
+
nested: {
|
|
134
|
+
second_level: {
|
|
135
|
+
value: 'deep'
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
}]
|
|
139
|
+
)
|
|
140
|
+
|
|
141
|
+
model = NestedModel.find(1)
|
|
142
|
+
model.must_respond_to :nested
|
|
143
|
+
|
|
144
|
+
model.nested.must_respond_to :second_level
|
|
145
|
+
model.nested.second_level.must_be_instance_of OpenStruct
|
|
146
|
+
model.nested.second_level.must_respond_to :value
|
|
147
|
+
model.nested.second_level.value.must_equal 'deep'
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
it 'returns OpenStruct for even unregistered attributes' do
|
|
151
|
+
stub_get('nested-models/1').to_return_json(
|
|
152
|
+
data: [{
|
|
153
|
+
id: 1, name: 'RHEL',
|
|
154
|
+
unregistered_nested: {
|
|
155
|
+
value: 'unregistered'
|
|
156
|
+
}
|
|
157
|
+
}]
|
|
158
|
+
)
|
|
159
|
+
|
|
160
|
+
model = NestedModel.find(1)
|
|
161
|
+
model.must_respond_to :unregistered_nested
|
|
162
|
+
model.unregistered_nested.must_be_instance_of OpenStruct
|
|
163
|
+
model.unregistered_nested.must_respond_to :value
|
|
164
|
+
model.unregistered_nested.value.must_equal 'unregistered'
|
|
165
|
+
end
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
|
|
169
|
+
describe 'Custom ValueParser' do
|
|
170
|
+
subject { CustomParserModel }
|
|
171
|
+
it 'must return fixnum for age' do
|
|
172
|
+
stub_get('custom-parser-models/1').to_return_json(
|
|
173
|
+
data: [{
|
|
174
|
+
id: 1, name: 'RHEL', age: '20'
|
|
175
|
+
}]
|
|
176
|
+
)
|
|
177
|
+
|
|
178
|
+
model = subject.find(1)
|
|
179
|
+
model.must_respond_to :age
|
|
180
|
+
model.age.must_be_instance_of Fixnum
|
|
181
|
+
model.age.must_equal 20
|
|
182
|
+
end
|
|
183
|
+
end
|
|
184
|
+
|
|
185
|
+
# class AttributesTest < MiniTest::Test
|
|
186
|
+
|
|
187
|
+
# def test_equality
|
|
188
|
+
# assert_equal Product.new(id: 2, title: 'Fish'), Product.new(id: 2, title: 'Fish')
|
|
189
|
+
# refute_equal Product.new(id: 2, title: 'Fish'), Product.new(id: 1, title: 'Fish')
|
|
190
|
+
# refute_equal Product.new(id: 2, title: 'Fish'), 'not_a_spyke_object'
|
|
191
|
+
# refute_equal Product.new(id: 2, title: 'Fish'), Image.new(id: 2, title: 'Fish')
|
|
192
|
+
# refute_equal Product.new, Product.new
|
|
193
|
+
# refute_equal StepImage.new(id: 1), Image.new(id: 1)
|
|
194
|
+
# end
|
|
195
|
+
|
|
196
|
+
# def test_uniqueness
|
|
197
|
+
# product_1 = Product.new(id: 1)
|
|
198
|
+
# product_2 = Product.new(id: 1)
|
|
199
|
+
# product_3 = Product.new(id: 2)
|
|
200
|
+
# image_1 = Image.new(id: 2)
|
|
201
|
+
# records = [product_1, product_2, product_3, image_1]
|
|
202
|
+
# assert_equal [product_1, product_3, image_1], records.uniq
|
|
203
|
+
# end
|
|
204
|
+
|
|
205
|
+
# def test_super_with_explicit_attributes
|
|
206
|
+
# assert_equal nil, Product.new.description
|
|
207
|
+
# end
|
|
208
|
+
|
|
209
|
+
# def test_inheriting_explicit_attributes
|
|
210
|
+
# assert_equal nil, Image.new.description
|
|
211
|
+
# assert_equal nil, Image.new.caption
|
|
212
|
+
# assert_raises NoMethodError do
|
|
213
|
+
# Image.new.note
|
|
214
|
+
# end
|
|
215
|
+
# assert_equal nil, StepImage.new.description
|
|
216
|
+
# assert_equal nil, StepImage.new.caption
|
|
217
|
+
# assert_equal nil, StepImage.new.note
|
|
218
|
+
# end
|
|
219
|
+
|
|
220
|
+
# def test_inspect
|
|
221
|
+
# product = Product.new(id: 2, title: 'Pizza', description: 'Delicious')
|
|
222
|
+
# assert_equal '#<Product(products/(:id)) id: 2 title: "Pizza" description: "Delicious">', product.inspect
|
|
223
|
+
# product = Product.new
|
|
224
|
+
# assert_equal '#<Product(products/(:id)) id: nil >', product.inspect
|
|
225
|
+
# require 'pry'; binding.pry
|
|
226
|
+
# user = Product.new.build_user
|
|
227
|
+
# assert_equal '#<User(users/:uuid) id: nil >', user.inspect
|
|
228
|
+
# group = Product.new.groups.build
|
|
229
|
+
# assert_equal '#<Group(products/:product_id/groups/(:id)) id: nil product_id: nil>', group.inspect
|
|
230
|
+
# end
|
|
231
|
+
# end
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
require 'timecop' # for Timecop.freeze
|
|
3
|
+
|
|
4
|
+
# How it works
|
|
5
|
+
#
|
|
6
|
+
# The test records http response using +vcr+ as fixtures which are
|
|
7
|
+
# stored under spec/fixtures/vcr directory. After the response is
|
|
8
|
+
# stored as fixtures, vcr will start replaying those requests when
|
|
9
|
+
# same url is accessed by faraday.
|
|
10
|
+
#
|
|
11
|
+
# To deal with the cache expiry, the cache_store is set to in-memory so
|
|
12
|
+
# that the first fetch will be missed and handled by vcr. Time is
|
|
13
|
+
# frozen to 2016 which makes the cache valid/fresh after it is
|
|
14
|
+
# fetched/played once by vcr.
|
|
15
|
+
|
|
16
|
+
# NOTE: disable webmock to record a new test
|
|
17
|
+
# WebMock.disable!
|
|
18
|
+
|
|
19
|
+
# returns http cache status for all http calls made
|
|
20
|
+
# using faraday in the +&block+ passed
|
|
21
|
+
def record_cache_access(&block)
|
|
22
|
+
cache_access = []
|
|
23
|
+
record_status = lambda do |*args|
|
|
24
|
+
event = ActiveSupport::Notifications::Event.new(*args)
|
|
25
|
+
cache_access << event.payload[:cache_status]
|
|
26
|
+
end
|
|
27
|
+
ActiveSupport::Notifications.subscribed(record_status, 'http_cache.faraday', &block)
|
|
28
|
+
cache_access
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
describe 'Caching' do
|
|
33
|
+
before do
|
|
34
|
+
@old_config = PDC.config
|
|
35
|
+
config = @old_config.clone
|
|
36
|
+
config.disable_caching = false
|
|
37
|
+
config.cache_store = ActiveSupport::Cache.lookup_store(:memory_store)
|
|
38
|
+
|
|
39
|
+
PDC.config = config
|
|
40
|
+
|
|
41
|
+
VCR.insert_cassette fixture_name
|
|
42
|
+
Timecop.travel(Time.local(2016)) # based on fixtures
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
after do
|
|
46
|
+
Timecop.return
|
|
47
|
+
VCR.eject_cassette
|
|
48
|
+
PDC.config = @old_config
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
it 'caches response without query' do
|
|
52
|
+
cache = record_cache_access { PDC::V1::Release.scoped.contents! }
|
|
53
|
+
cache.must_equal [:miss]
|
|
54
|
+
|
|
55
|
+
cache = record_cache_access { PDC::V1::Release.scoped.contents! }
|
|
56
|
+
cache.must_equal [:fresh]
|
|
57
|
+
|
|
58
|
+
cache = record_cache_access { PDC::V1::Release.scoped.contents! }
|
|
59
|
+
cache.must_equal [:fresh]
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
it 'caches response with a query' do
|
|
63
|
+
cache = record_cache_access { PDC::V1::Release.page(2).contents! }
|
|
64
|
+
cache.must_equal [:miss]
|
|
65
|
+
|
|
66
|
+
cache = record_cache_access { PDC::V1::Release.page(2).contents! }
|
|
67
|
+
cache.must_equal [:fresh]
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
it 'caches response with multiple query' do
|
|
71
|
+
cache = record_cache_access do
|
|
72
|
+
PDC::V1::Release.page(2).page_size(30).contents!
|
|
73
|
+
PDC::V1::Release.page(2).page_size(30).contents!
|
|
74
|
+
end
|
|
75
|
+
cache.must_equal [:miss, :fresh]
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
it 'caches multiple response' do
|
|
79
|
+
cache = record_cache_access { PDC::V1::Release.where(active: true).all }
|
|
80
|
+
|
|
81
|
+
# must paginate so at least 2 pages
|
|
82
|
+
cache.length.must_be :>=, 2
|
|
83
|
+
cache.must_equal [:miss] * cache.length
|
|
84
|
+
|
|
85
|
+
second_time = record_cache_access { PDC::V1::Release.where(active: true).all }
|
|
86
|
+
second_time.must_equal [:fresh] * cache.length
|
|
87
|
+
end
|
|
88
|
+
end
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
|
|
3
|
+
#WebMock.disable! # enable to re-record
|
|
4
|
+
|
|
5
|
+
describe PDC do
|
|
6
|
+
before do
|
|
7
|
+
VCR.insert_cassette fixture_name
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
after do
|
|
11
|
+
VCR.eject_cassette
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
describe 'count' do
|
|
15
|
+
let(:release) { PDC::V1::Release }
|
|
16
|
+
let(:arch) { PDC::V1::Arch }
|
|
17
|
+
|
|
18
|
+
it 'must return number of resources' do
|
|
19
|
+
count = release.count
|
|
20
|
+
count.must_equal 54
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
it 'works with where' do
|
|
24
|
+
count = release.where(active: false).count
|
|
25
|
+
count.must_equal 0
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
it 'returns the total count and not items in page' do
|
|
29
|
+
total_count = arch.count
|
|
30
|
+
page_count = arch.page(2).count
|
|
31
|
+
page_count.must_equal total_count
|
|
32
|
+
|
|
33
|
+
arch_in_page = arch.page(2).contents!
|
|
34
|
+
arch_in_page.length.must_be :<, page_count
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
it '.page.count returns total count' do
|
|
38
|
+
pg2 = arch.page(2)
|
|
39
|
+
pg2_count = pg2.all.length
|
|
40
|
+
|
|
41
|
+
arches_count = pg2.count # must return all arches count
|
|
42
|
+
arches_count.wont_equal pg2_count
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
require 'ap'
|
|
3
|
+
|
|
4
|
+
describe ModelWithIdentity do
|
|
5
|
+
subject { ModelWithIdentity }
|
|
6
|
+
|
|
7
|
+
describe '##primary_key' do
|
|
8
|
+
it 'must exist' do
|
|
9
|
+
class_methods = subject.methods - subject.instance_methods
|
|
10
|
+
class_methods.must_include :primary_key
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
it 'must default to class_name_id' do
|
|
14
|
+
subject.primary_key.must_equal 'model_with_identity_id'
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
describe 'uri' do
|
|
19
|
+
it 'must have a default uri' do
|
|
20
|
+
subject.uri.wont_be_nil
|
|
21
|
+
subject.uri.must_equal 'fixtures/model-with-identities/(:model_with_identity_id)'
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
it 'returns path with variables expanded for an object' do
|
|
25
|
+
instance = subject.new(subject.primary_key => :foobar)
|
|
26
|
+
expected_uri = PDC::Resource::Path.new('fixtures/model-with-identities/foobar').expanded
|
|
27
|
+
instance.uri.must_equal expected_uri
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
describe 'new' do
|
|
32
|
+
it 'returns nil for id' do
|
|
33
|
+
object = subject.new
|
|
34
|
+
object.id.must_be_nil
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
it 'can accept primary_key' do
|
|
38
|
+
pkey = subject.primary_key
|
|
39
|
+
instance = subject.new(pkey => 'something')
|
|
40
|
+
instance.id.must_equal 'something'
|
|
41
|
+
instance.model_with_identity_id.must_equal 'something'
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
describe CustomPrimaryKeyModel do
|
|
47
|
+
subject { CustomPrimaryKeyModel }
|
|
48
|
+
|
|
49
|
+
describe '##primary_key' do
|
|
50
|
+
it 'can be set' do
|
|
51
|
+
subject.primary_key.must_equal :foobar
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
it 'can accept primary_key' do
|
|
56
|
+
pkey = subject.primary_key
|
|
57
|
+
object = subject.new(pkey => 'something')
|
|
58
|
+
object.id.must_equal 'something'
|
|
59
|
+
object.foobar.must_equal 'something'
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
describe 'uri' do
|
|
63
|
+
it 'must use custom primary_key' do
|
|
64
|
+
subject.uri.must_equal 'fixtures/custom-primary-key-models/(:foobar)'
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
describe V1::Foobar do
|
|
71
|
+
subject { V1::Foobar }
|
|
72
|
+
it 'must have a pkey' do
|
|
73
|
+
subject.primary_key.must_equal 'foobar_id'
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
it 'must have a uri' do
|
|
77
|
+
subject.uri.must_equal 'fixtures/v1/foobars/(:foobar_id)'
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
describe 'attributes' do
|
|
82
|
+
it 'base must have primary key' do
|
|
83
|
+
ModelBase.new.id.must_be_nil
|
|
84
|
+
base = ModelBase.new(model_base_id: 1)
|
|
85
|
+
base.id.must_equal 1
|
|
86
|
+
base.attributes.must_include :model_base_id
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
it 'sub must not have primary key of base' do
|
|
90
|
+
Model.new.id.must_be_nil
|
|
91
|
+
model = Model.new(model_id: 3)
|
|
92
|
+
model.id.must_equal 3
|
|
93
|
+
model.attributes.must_include :model_id
|
|
94
|
+
model.attributes.wont_include :model_base_id
|
|
95
|
+
end
|
|
96
|
+
end
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
|
|
3
|
+
describe PDC do
|
|
4
|
+
before do
|
|
5
|
+
VCR.insert_cassette fixture_name
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
after do
|
|
9
|
+
VCR.eject_cassette
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
describe 'pagination' do
|
|
13
|
+
let(:arch) { PDC::V1::Arch }
|
|
14
|
+
let(:release_variant) { PDC::V1::ReleaseVariant }
|
|
15
|
+
|
|
16
|
+
it 'can iterate using each' do
|
|
17
|
+
count = arch.count # makes single call
|
|
18
|
+
|
|
19
|
+
all = arch.scoped.each_with_object([]) do |a, o|
|
|
20
|
+
o << a
|
|
21
|
+
end
|
|
22
|
+
count.must_equal all.length
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
it 'preserves the filters' do
|
|
26
|
+
variants = release_variant.where(type: 'variant', name: 'rh-common')
|
|
27
|
+
count = variants.count
|
|
28
|
+
|
|
29
|
+
all = variants.each_with_object([]) do |a, o|
|
|
30
|
+
o << a
|
|
31
|
+
end
|
|
32
|
+
count.must_equal all.length
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
describe 'page' do
|
|
37
|
+
subject { PDC::V1::Arch }
|
|
38
|
+
it 'returns resources on that page' do
|
|
39
|
+
pg2 = subject.page(2)
|
|
40
|
+
resources = pg2.contents!
|
|
41
|
+
resources.length.must_equal 20
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
it 'should not be in the list of attributes' do
|
|
45
|
+
pg2 = subject.page(2)
|
|
46
|
+
arch = pg2.all.first
|
|
47
|
+
arch.attributes.wont_include :page
|
|
48
|
+
arch.attributes.wont_include 'page'
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
|
|
3
|
+
describe PDC::Resource::Path do
|
|
4
|
+
subject { PDC::Resource::Path }
|
|
5
|
+
|
|
6
|
+
it 'can handle optional variables' do
|
|
7
|
+
assert_equal '/recipes/(:id)', subject.new('/recipes/(:id)').to_s
|
|
8
|
+
assert_equal '/recipes', subject.new('/recipes/(:id)').expanded
|
|
9
|
+
assert_equal '/recipes/2', subject.new('/recipes/(:id)', id: 2).expanded
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
it 'nested collections' do
|
|
13
|
+
path = subject.new('/users/:user_id/recipes/(:id)',
|
|
14
|
+
user_id: 1, status: 'published')
|
|
15
|
+
|
|
16
|
+
assert_equal [:user_id, :id], path.variables
|
|
17
|
+
assert_equal '/users/1/recipes', path.expanded
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
it 'nested_resource_path' do
|
|
22
|
+
assert_equal '/users/1/recipes/2', subject.new('/users/:user_id/recipes/:id', user_id: 1, id: 2).expanded
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
it 'raises if required_params are missing' do
|
|
26
|
+
lambda {
|
|
27
|
+
subject.new('/users/:user_id/recipes/(:id)', id: 2).expanded
|
|
28
|
+
}.must_raise PDC::InvalidPathError, 'Missing required params: user_id in /users/:user_id/recipes/(:id)'
|
|
29
|
+
end
|
|
30
|
+
end
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
|
|
3
|
+
class SearchableModel < Base
|
|
4
|
+
attributes :name, :product
|
|
5
|
+
|
|
6
|
+
### NOTE: where() will have no impact since the data is hardcoded
|
|
7
|
+
def self.fake_data
|
|
8
|
+
[
|
|
9
|
+
{ name: 'foobar-1', product: 'prod' },
|
|
10
|
+
{ name: 'foobar-2', product: 'prod' },
|
|
11
|
+
{ name: 'foobar-3', product: 'prod' },
|
|
12
|
+
]
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def self.fetch(params)
|
|
16
|
+
response = OpenStruct.new(
|
|
17
|
+
body: { data: fake_data },
|
|
18
|
+
status: 200,
|
|
19
|
+
env: OpenStruct.new(url: 'http://foobar')
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
PDC::Http::Result.new(response)
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
describe SearchableModel do
|
|
27
|
+
subject { SearchableModel }
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
describe '#where' do
|
|
31
|
+
it 'returns a scope with params' do
|
|
32
|
+
filters = { name: :foobar, age: 10 }
|
|
33
|
+
rel = subject.where(filters)
|
|
34
|
+
|
|
35
|
+
rel.must_be_instance_of PDC::Resource::Relation
|
|
36
|
+
rel.params.must_equal(filters)
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
it 'can be called multiple times' do
|
|
40
|
+
filter_name = { name: :foobar }
|
|
41
|
+
filter_age = { age: 10 }
|
|
42
|
+
|
|
43
|
+
rel = subject.where(filter_name).where(filter_age)
|
|
44
|
+
.where(filter_name)
|
|
45
|
+
rel.must_be_instance_of PDC::Resource::Relation
|
|
46
|
+
|
|
47
|
+
filters = filter_name.merge(filter_age)
|
|
48
|
+
rel.params.must_equal filters
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
describe '#find' do
|
|
53
|
+
let(:one_result) { [{ name: 'foobar2', product: 'prod' }] }
|
|
54
|
+
let(:no_result) { [] }
|
|
55
|
+
|
|
56
|
+
it 'returns a record' do
|
|
57
|
+
subject.stub(:fake_data, one_result) do
|
|
58
|
+
result = subject.find(:foobar)
|
|
59
|
+
result.must_be_instance_of SearchableModel
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
it 'raises MultipleResultsError more than a record exists' do
|
|
64
|
+
-> { subject.find(:foobar) }.must_raise PDC::MultipleResultsError
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
it 'raise ResourceNotFound when no record exists' do
|
|
68
|
+
subject.stub(:fake_data, no_result) do
|
|
69
|
+
-> { subject.find(:foobar) }.must_raise PDC::ResourceNotFound
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
it 'can call first' do
|
|
75
|
+
first = subject.first
|
|
76
|
+
first.must_be_instance_of subject
|
|
77
|
+
first.name.must_equal subject.fake_data.first[:name]
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
describe 'relation' do
|
|
81
|
+
it 'can call all on a relation' do
|
|
82
|
+
relation = subject.where(name: :foobar)
|
|
83
|
+
relation.must_be_instance_of PDC::Resource::Relation
|
|
84
|
+
results = relation.all
|
|
85
|
+
results.length.must_equal subject.fake_data.length
|
|
86
|
+
results.first.must_be_instance_of subject
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
it 'all works with resource' do
|
|
90
|
+
results = subject.all
|
|
91
|
+
results.length.must_equal subject.fake_data.length
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
end
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
|
|
3
|
+
describe :find do
|
|
4
|
+
it 'finds a specific resource' do
|
|
5
|
+
stub_get('products/1')
|
|
6
|
+
.to_return_json(data: [{ product_id: 1, title: 'foobar' }])
|
|
7
|
+
|
|
8
|
+
product = Fixtures::Product.find(1)
|
|
9
|
+
product.id.must_equal 1
|
|
10
|
+
product.product_id.must_equal 1
|
|
11
|
+
product.title.must_equal 'foobar'
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
it 'must work when primary_key is user defined' do
|
|
15
|
+
stub_get('product-variants/v2')
|
|
16
|
+
.to_return_json(data: [{ v_id: 'v2', title: 'variant' }])
|
|
17
|
+
|
|
18
|
+
variant = Fixtures::ProductVariant.find('v2')
|
|
19
|
+
variant.id.must_equal 'v2'
|
|
20
|
+
variant.v_id.must_equal 'v2'
|
|
21
|
+
variant.title.must_equal 'variant'
|
|
22
|
+
end
|
|
23
|
+
end
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
|
|
3
|
+
describe PDC::Resource::ValueParser do
|
|
4
|
+
subject { PDC::Resource::ValueParser }
|
|
5
|
+
|
|
6
|
+
it 'must respond_to? parse' do
|
|
7
|
+
subject.must_respond_to :parse
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
describe '#parse' do
|
|
11
|
+
it 'must return value for plain types' do
|
|
12
|
+
subject.parse(:foobar).must_equal :foobar
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
File without changes
|