Skip to content

Commit 6b1a487

Browse files
authored
Merge pull request #2092 from bf4/reflection_block_changes_meta_links
Add reflection tests
2 parents f76ea3d + 758e44e commit 6b1a487

File tree

1 file changed

+389
-0
lines changed

1 file changed

+389
-0
lines changed

test/serializers/reflection_test.rb

Lines changed: 389 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,389 @@
1+
require 'test_helper'
2+
module ActiveModel
3+
class Serializer
4+
class ReflectionTest < ActiveSupport::TestCase
5+
class Blog < ActiveModelSerializers::Model
6+
attributes :id
7+
end
8+
class BlogSerializer < ActiveModel::Serializer
9+
type 'blog'
10+
attributes :id
11+
end
12+
13+
setup do
14+
@expected_meta = { id: 1 }
15+
@expected_links = { self: 'no_uri_validation' }
16+
@empty_links = {}
17+
model_attributes = { blog: Blog.new(@expected_meta) }
18+
@model = Class.new(ActiveModelSerializers::Model) do
19+
attributes(*model_attributes.keys)
20+
21+
def self.name
22+
'TestModel'
23+
end
24+
end.new(model_attributes)
25+
@instance_options = {}
26+
end
27+
28+
# TODO: Remaining tests
29+
# test_reflection_value_block_with_scope
30+
# test_reflection_value_uses_serializer_instance_method
31+
# test_reflection_excluded_eh_blank_is_false
32+
# test_reflection_excluded_eh_if
33+
# test_reflection_excluded_eh_unless
34+
# test_evaluate_condition_symbol_serializer_method
35+
# test_evaluate_condition_string_serializer_method
36+
# test_evaluate_condition_proc
37+
# test_evaluate_condition_proc_yields_serializer
38+
# test_evaluate_condition_other
39+
# test_options_key
40+
# test_options_polymorphic
41+
# test_options_serializer
42+
# test_options_virtual_value
43+
# test_options_namespace
44+
45+
def test_reflection_value
46+
serializer_class = Class.new(ActiveModel::Serializer) do
47+
has_one :blog
48+
end
49+
serializer_instance = serializer_class.new(@model, @instance_options)
50+
51+
# Get Reflection
52+
reflection = serializer_class._reflections.fetch(:blog)
53+
54+
# Assert
55+
assert_nil reflection.block
56+
assert_equal Serializer.config.include_data_default, reflection.options.fetch(:include_data_setting)
57+
assert_equal true, reflection.options.fetch(:include_data_setting)
58+
59+
include_slice = :does_not_matter
60+
assert_equal @model.blog, reflection.value(serializer_instance, include_slice)
61+
end
62+
63+
def test_reflection_value_block
64+
serializer_class = Class.new(ActiveModel::Serializer) do
65+
has_one :blog do
66+
object.blog
67+
end
68+
end
69+
serializer_instance = serializer_class.new(@model, @instance_options)
70+
71+
# Get Reflection
72+
reflection = serializer_class._reflections.fetch(:blog)
73+
74+
# Assert
75+
assert_respond_to reflection.block, :call
76+
assert_equal Serializer.config.include_data_default, reflection.options.fetch(:include_data_setting)
77+
assert_equal true, reflection.options.fetch(:include_data_setting)
78+
79+
include_slice = :does_not_matter
80+
assert_equal @model.blog, reflection.value(serializer_instance, include_slice)
81+
end
82+
83+
def test_reflection_value_block_with_explicit_include_data_true
84+
serializer_class = Class.new(ActiveModel::Serializer) do
85+
has_one :blog do
86+
include_data true
87+
object.blog
88+
end
89+
end
90+
serializer_instance = serializer_class.new(@model, @instance_options)
91+
92+
# Get Reflection
93+
reflection = serializer_class._reflections.fetch(:blog)
94+
95+
# Assert
96+
assert_respond_to reflection.block, :call
97+
assert_equal Serializer.config.include_data_default, reflection.options.fetch(:include_data_setting)
98+
assert_equal true, reflection.options.fetch(:include_data_setting)
99+
100+
include_slice = :does_not_matter
101+
assert_equal @model.blog, reflection.value(serializer_instance, include_slice)
102+
end
103+
104+
def test_reflection_value_block_with_include_data_false_mutates_the_reflection_include_data
105+
serializer_class = Class.new(ActiveModel::Serializer) do
106+
has_one :blog do
107+
include_data false
108+
object.blog
109+
end
110+
end
111+
serializer_instance = serializer_class.new(@model, @instance_options)
112+
113+
# Get Reflection
114+
reflection = serializer_class._reflections.fetch(:blog)
115+
116+
# Assert
117+
assert_respond_to reflection.block, :call
118+
assert_equal true, reflection.options.fetch(:include_data_setting)
119+
include_slice = :does_not_matter
120+
assert_nil reflection.value(serializer_instance, include_slice)
121+
assert_equal false, reflection.options.fetch(:include_data_setting)
122+
end
123+
124+
def test_reflection_value_block_with_include_data_if_sideloaded_included_mutates_the_reflection_include_data
125+
serializer_class = Class.new(ActiveModel::Serializer) do
126+
has_one :blog do
127+
include_data :if_sideloaded
128+
object.blog
129+
end
130+
end
131+
serializer_instance = serializer_class.new(@model, @instance_options)
132+
133+
# Get Reflection
134+
reflection = serializer_class._reflections.fetch(:blog)
135+
136+
# Assert
137+
assert_respond_to reflection.block, :call
138+
assert_equal true, reflection.options.fetch(:include_data_setting)
139+
include_slice = {}
140+
assert_nil reflection.value(serializer_instance, include_slice)
141+
assert_equal :if_sideloaded, reflection.options.fetch(:include_data_setting)
142+
end
143+
144+
def test_reflection_value_block_with_include_data_if_sideloaded_excluded_mutates_the_reflection_include_data
145+
serializer_class = Class.new(ActiveModel::Serializer) do
146+
has_one :blog do
147+
include_data :if_sideloaded
148+
object.blog
149+
end
150+
end
151+
serializer_instance = serializer_class.new(@model, @instance_options)
152+
153+
# Get Reflection
154+
reflection = serializer_class._reflections.fetch(:blog)
155+
156+
# Assert
157+
assert_respond_to reflection.block, :call
158+
assert_equal true, reflection.options.fetch(:include_data_setting)
159+
include_slice = { blog: :does_not_matter }
160+
assert_equal @model.blog, reflection.value(serializer_instance, include_slice)
161+
assert_equal :if_sideloaded, reflection.options.fetch(:include_data_setting)
162+
end
163+
164+
def test_reflection_block_with_link_mutates_the_reflection_links
165+
serializer_class = Class.new(ActiveModel::Serializer) do
166+
has_one :blog do
167+
link :self, 'no_uri_validation'
168+
end
169+
end
170+
serializer_instance = serializer_class.new(@model, @instance_options)
171+
172+
# Get Reflection
173+
reflection = serializer_class._reflections.fetch(:blog)
174+
assert_equal @empty_links, reflection.options.fetch(:links)
175+
176+
# Build Association
177+
association = reflection.build_association(serializer_instance, @instance_options)
178+
assert_equal @expected_links, association.links
179+
assert_equal @expected_links, reflection.options.fetch(:links)
180+
end
181+
182+
def test_reflection_block_with_link_block_mutates_the_reflection_links
183+
serializer_class = Class.new(ActiveModel::Serializer) do
184+
has_one :blog do
185+
link :self do
186+
'no_uri_validation'
187+
end
188+
end
189+
end
190+
serializer_instance = serializer_class.new(@model, @instance_options)
191+
192+
# Get Reflection
193+
reflection = serializer_class._reflections.fetch(:blog)
194+
assert_equal @empty_links, reflection.options.fetch(:links)
195+
196+
# Build Association
197+
association = reflection.build_association(serializer_instance, @instance_options)
198+
# Assert before instance_eval link
199+
link = association.links.fetch(:self)
200+
assert_respond_to link, :call
201+
202+
# Assert after instance_eval link
203+
assert_equal @expected_links.fetch(:self), reflection.instance_eval(&link)
204+
assert_respond_to reflection.options.fetch(:links).fetch(:self), :call
205+
end
206+
207+
def test_reflection_block_with_meta_mutates_the_reflection_meta
208+
serializer_class = Class.new(ActiveModel::Serializer) do
209+
has_one :blog do
210+
meta(id: object.blog.id)
211+
end
212+
end
213+
serializer_instance = serializer_class.new(@model, @instance_options)
214+
215+
# Get Reflection
216+
reflection = serializer_class._reflections.fetch(:blog)
217+
assert_nil reflection.options.fetch(:meta)
218+
219+
# Build Association
220+
association = reflection.build_association(serializer_instance, @instance_options)
221+
assert_equal @expected_meta, association.meta
222+
assert_equal @expected_meta, reflection.options.fetch(:meta)
223+
end
224+
225+
def test_reflection_block_with_meta_block_mutates_the_reflection_meta
226+
serializer_class = Class.new(ActiveModel::Serializer) do
227+
has_one :blog do
228+
meta do
229+
{ id: object.blog.id }
230+
end
231+
end
232+
end
233+
serializer_instance = serializer_class.new(@model, @instance_options)
234+
235+
# Get Reflection
236+
reflection = serializer_class._reflections.fetch(:blog)
237+
assert_nil reflection.options.fetch(:meta)
238+
239+
# Build Association
240+
association = reflection.build_association(serializer_instance, @instance_options)
241+
# Assert before instance_eval meta
242+
assert_respond_to association.meta, :call
243+
assert_respond_to reflection.options.fetch(:meta), :call
244+
245+
# Assert after instance_eval meta
246+
assert_equal @expected_meta, reflection.instance_eval(&association.meta)
247+
assert_respond_to reflection.options.fetch(:meta), :call
248+
assert_respond_to association.meta, :call
249+
end
250+
251+
# rubocop:disable Metrics/AbcSize
252+
def test_reflection_block_with_meta_in_link_block_mutates_the_reflection_meta
253+
serializer_class = Class.new(ActiveModel::Serializer) do
254+
has_one :blog do
255+
link :self do
256+
meta(id: object.blog.id)
257+
'no_uri_validation'
258+
end
259+
end
260+
end
261+
serializer_instance = serializer_class.new(@model, @instance_options)
262+
263+
# Get Reflection
264+
reflection = serializer_class._reflections.fetch(:blog)
265+
assert_nil reflection.options.fetch(:meta)
266+
assert_equal @empty_links, reflection.options.fetch(:links)
267+
268+
# Build Association
269+
association = reflection.build_association(serializer_instance, @instance_options)
270+
# Assert before instance_eval link meta
271+
assert_nil association.meta
272+
assert_nil reflection.options.fetch(:meta)
273+
274+
link = association.links.fetch(:self)
275+
assert_respond_to link, :call
276+
assert_respond_to reflection.options.fetch(:links).fetch(:self), :call
277+
assert_nil reflection.options.fetch(:meta)
278+
279+
# Assert after instance_eval link
280+
assert_equal 'no_uri_validation', reflection.instance_eval(&link)
281+
assert_equal @expected_meta, reflection.options.fetch(:meta)
282+
assert_nil association.meta
283+
end
284+
# rubocop:enable Metrics/AbcSize
285+
286+
# rubocop:disable Metrics/AbcSize
287+
def test_reflection_block_with_meta_block_in_link_block_mutates_the_reflection_meta
288+
serializer_class = Class.new(ActiveModel::Serializer) do
289+
has_one :blog do
290+
link :self do
291+
meta do
292+
{ id: object.blog.id }
293+
end
294+
'no_uri_validation'
295+
end
296+
end
297+
end
298+
serializer_instance = serializer_class.new(@model, @instance_options)
299+
300+
# Get Reflection
301+
reflection = serializer_class._reflections.fetch(:blog)
302+
assert_nil reflection.options.fetch(:meta)
303+
304+
# Build Association
305+
association = reflection.build_association(serializer_instance, @instance_options)
306+
assert_nil association.meta
307+
assert_nil reflection.options.fetch(:meta)
308+
309+
# Assert before instance_eval link
310+
link = association.links.fetch(:self)
311+
assert_nil reflection.options.fetch(:meta)
312+
assert_respond_to link, :call
313+
assert_respond_to association.links.fetch(:self), :call
314+
315+
# Assert after instance_eval link
316+
assert_equal 'no_uri_validation', reflection.instance_eval(&link)
317+
assert_respond_to association.links.fetch(:self), :call
318+
# Assert before instance_eval link meta
319+
assert_respond_to reflection.options.fetch(:meta), :call
320+
assert_nil association.meta
321+
322+
# Assert after instance_eval link meta
323+
assert_equal @expected_meta, reflection.instance_eval(&reflection.options.fetch(:meta))
324+
assert_nil association.meta
325+
end
326+
# rubocop:enable Metrics/AbcSize
327+
328+
def test_no_href_in_vanilla_reflection
329+
serializer_class = Class.new(ActiveModel::Serializer) do
330+
has_one :blog do
331+
link :self do
332+
href 'no_uri_validation'
333+
end
334+
end
335+
end
336+
serializer_instance = serializer_class.new(@model, @instance_options)
337+
338+
# Get Reflection
339+
reflection = serializer_class._reflections.fetch(:blog)
340+
assert_equal @empty_links, reflection.options.fetch(:links)
341+
342+
# Build Association
343+
association = reflection.build_association(serializer_instance, @instance_options)
344+
# Assert before instance_eval link
345+
link = association.links.fetch(:self)
346+
assert_respond_to link, :call
347+
348+
# Assert after instance_eval link
349+
exception = assert_raise(NoMethodError) do
350+
reflection.instance_eval(&link)
351+
end
352+
assert_match(/undefined method `href'/, exception.message)
353+
end
354+
355+
# rubocop:disable Metrics/AbcSize
356+
def test_mutating_reflection_block_is_not_thread_safe
357+
serializer_class = Class.new(ActiveModel::Serializer) do
358+
has_one :blog do
359+
meta(id: object.blog.id)
360+
end
361+
end
362+
model1_meta = @expected_meta
363+
# Evaluate reflection meta for model with id 1
364+
serializer_instance = serializer_class.new(@model, @instance_options)
365+
reflection = serializer_class._reflections.fetch(:blog)
366+
assert_nil reflection.options.fetch(:meta)
367+
association = reflection.build_association(serializer_instance, @instance_options)
368+
assert_equal model1_meta, association.meta
369+
assert_equal model1_meta, reflection.options.fetch(:meta)
370+
371+
model2_meta = @expected_meta.merge(id: 2)
372+
# Evaluate reflection meta for model with id 2
373+
@model.blog.id = 2
374+
assert_equal 2, @model.blog.id # sanity check
375+
serializer_instance = serializer_class.new(@model, @instance_options)
376+
reflection = serializer_class._reflections.fetch(:blog)
377+
378+
# WARN: Thread-safety issue
379+
# Before the reflection is evaluated, it has the value from the previous evaluation
380+
assert_equal model1_meta, reflection.options.fetch(:meta)
381+
382+
association = reflection.build_association(serializer_instance, @instance_options)
383+
assert_equal model2_meta, association.meta
384+
assert_equal model2_meta, reflection.options.fetch(:meta)
385+
end
386+
# rubocop:enable Metrics/AbcSize
387+
end
388+
end
389+
end

0 commit comments

Comments
 (0)