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.
Files changed (104) hide show
  1. checksums.yaml +7 -0
  2. data/.editorconfig +27 -0
  3. data/.gitignore +10 -0
  4. data/.ruby-gemset +1 -0
  5. data/.ruby-version +1 -0
  6. data/.travis.yml +21 -0
  7. data/CODE_OF_CONDUCT.md +49 -0
  8. data/Gemfile +32 -0
  9. data/Guardfile +36 -0
  10. data/LICENSE +21 -0
  11. data/LICENSE.txt +21 -0
  12. data/README.md +41 -0
  13. data/Rakefile +11 -0
  14. data/bin/console +11 -0
  15. data/bin/setup +11 -0
  16. data/docs/.gitignore +3 -0
  17. data/docs/LICENSE_sphinx_deployment +27 -0
  18. data/docs/Makefile +179 -0
  19. data/docs/source/conf.py +257 -0
  20. data/docs/source/example.rst +10 -0
  21. data/docs/source/index.rst +23 -0
  22. data/docs/sphinx_deployment.mk +117 -0
  23. data/examples/active_attr.rb +33 -0
  24. data/examples/http_failures.rb +18 -0
  25. data/examples/local_pdc_dev.rb +15 -0
  26. data/examples/logger.rb +50 -0
  27. data/examples/pdc_curb_access_token.rb +36 -0
  28. data/examples/pdc_resource_tests.rb +173 -0
  29. data/examples/pdc_test_cache.rb +48 -0
  30. data/examples/prod_failures.rb +26 -0
  31. data/examples/prod_pdc.rb +14 -0
  32. data/lib/pdc/base.rb +14 -0
  33. data/lib/pdc/config.rb +157 -0
  34. data/lib/pdc/errors.rb +8 -0
  35. data/lib/pdc/http/errors.rb +43 -0
  36. data/lib/pdc/http/request/append_slash.rb +19 -0
  37. data/lib/pdc/http/request.rb +12 -0
  38. data/lib/pdc/http/response/pagination.rb +43 -0
  39. data/lib/pdc/http/response/parser.rb +62 -0
  40. data/lib/pdc/http/response/raise_error.rb +13 -0
  41. data/lib/pdc/http/response.rb +3 -0
  42. data/lib/pdc/http/result.rb +28 -0
  43. data/lib/pdc/http.rb +12 -0
  44. data/lib/pdc/logger.rb +19 -0
  45. data/lib/pdc/resource/attribute_modifier.rb +43 -0
  46. data/lib/pdc/resource/attribute_store.rb +22 -0
  47. data/lib/pdc/resource/attributes.rb +144 -0
  48. data/lib/pdc/resource/errors.rb +3 -0
  49. data/lib/pdc/resource/identity.rb +75 -0
  50. data/lib/pdc/resource/path.rb +63 -0
  51. data/lib/pdc/resource/per_thread_registry.rb +54 -0
  52. data/lib/pdc/resource/relation/finder.rb +24 -0
  53. data/lib/pdc/resource/relation/pagination.rb +33 -0
  54. data/lib/pdc/resource/relation/query.rb +14 -0
  55. data/lib/pdc/resource/relation.rb +81 -0
  56. data/lib/pdc/resource/rest_api.rb +34 -0
  57. data/lib/pdc/resource/scope_registry.rb +19 -0
  58. data/lib/pdc/resource/scopes.rb +29 -0
  59. data/lib/pdc/resource/value_parser.rb +31 -0
  60. data/lib/pdc/resource/wip.rb +0 -0
  61. data/lib/pdc/resource.rb +12 -0
  62. data/lib/pdc/v1/arch.rb +6 -0
  63. data/lib/pdc/v1/product.rb +5 -0
  64. data/lib/pdc/v1/release.rb +18 -0
  65. data/lib/pdc/v1/release_variant.rb +17 -0
  66. data/lib/pdc/v1.rb +8 -0
  67. data/lib/pdc/version.rb +3 -0
  68. data/lib/pdc.rb +25 -0
  69. data/pdc.gemspec +38 -0
  70. data/spec/fixtures/vcr/_page_count_returns_total_count.yml +141 -0
  71. data/spec/fixtures/vcr/brew_can_be_nil.yml +61 -0
  72. data/spec/fixtures/vcr/brew_may_be_present.yml +50 -0
  73. data/spec/fixtures/vcr/caches_multiple_response.yml +173 -0
  74. data/spec/fixtures/vcr/caches_response_with_a_query.yml +64 -0
  75. data/spec/fixtures/vcr/caches_response_with_multiple_query.yml +64 -0
  76. data/spec/fixtures/vcr/caches_response_without_query.yml +61 -0
  77. data/spec/fixtures/vcr/can_iterate_using_each.yml +187 -0
  78. data/spec/fixtures/vcr/fetches_variants_of_a_release.yml +663 -0
  79. data/spec/fixtures/vcr/must_return_number_of_resources.yml +61 -0
  80. data/spec/fixtures/vcr/preserves_the_filters.yml +49 -0
  81. data/spec/fixtures/vcr/returns_resources_on_that_page.yml +49 -0
  82. data/spec/fixtures/vcr/returns_the_total_count_and_not_items_in_page.yml +135 -0
  83. data/spec/fixtures/vcr/should_not_be_in_the_list_of_attributes.yml +95 -0
  84. data/spec/fixtures/vcr/works_with_where.yml +49 -0
  85. data/spec/pdc/config_spec.rb +115 -0
  86. data/spec/pdc/http/errors_spec.rb +58 -0
  87. data/spec/pdc/resource/attributes_spec.rb +231 -0
  88. data/spec/pdc/resource/cache_spec.rb +88 -0
  89. data/spec/pdc/resource/count_spec.rb +45 -0
  90. data/spec/pdc/resource/identity_spec.rb +96 -0
  91. data/spec/pdc/resource/pagination_spec.rb +51 -0
  92. data/spec/pdc/resource/path_spec.rb +30 -0
  93. data/spec/pdc/resource/relation_spec.rb +94 -0
  94. data/spec/pdc/resource/rest_api_spec.rb +23 -0
  95. data/spec/pdc/resource/value_parser_spec.rb +15 -0
  96. data/spec/pdc/resource/wip_spec.rb +0 -0
  97. data/spec/pdc/v1/arch_spec.rb +34 -0
  98. data/spec/pdc/v1/release_spec.rb +54 -0
  99. data/spec/pdc/version_spec.rb +5 -0
  100. data/spec/spec_helper.rb +39 -0
  101. data/spec/support/fixtures.rb +116 -0
  102. data/spec/support/vcr.rb +10 -0
  103. data/spec/support/webmock.rb +34 -0
  104. 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