spec/serializer_spec.rb in jsonapi-serializers-1.0.1 vs spec/serializer_spec.rb in jsonapi-serializers-2.0.0.pre.beta.1
- old
+ new
@@ -1,8 +1,9 @@
require 'active_model/errors'
require 'active_model/naming'
require 'active_model/translation'
+require 'pry'
describe JSONAPI::Serializer do
def serialize_primary(object, options = {})
# Note: intentional high-coupling to protected method for tests.
JSONAPI::Serializer.send(:serialize_primary, object, options)
@@ -14,10 +15,11 @@
# - a single resource object or null, for requests that target single resources
# http://jsonapi.org/format/#document-structure-top-level
primary_data = serialize_primary(nil, {serializer: MyApp::PostSerializer})
expect(primary_data).to be_nil
end
+
it 'can serialize primary data for a simple object' do
post = create(:post)
primary_data = serialize_primary(post, {serializer: MyApp::SimplestPostSerializer})
expect(primary_data).to eq({
'id' => '1',
@@ -29,10 +31,11 @@
'links' => {
'self' => '/posts/1',
},
})
end
+
it 'can serialize primary data for a simple object with a long name' do
long_comment = create(:long_comment, post: create(:post))
primary_data = serialize_primary(long_comment, {serializer: MyApp::LongCommentSerializer})
expect(primary_data).to eq({
'id' => '1',
@@ -57,10 +60,11 @@
},
},
},
})
end
+
it 'can serialize primary data for a simple object with resource-level metadata' do
post = create(:post)
primary_data = serialize_primary(post, {serializer: MyApp::PostSerializerWithMetadata})
expect(primary_data).to eq({
'id' => '1',
@@ -78,10 +82,11 @@
'Aliens',
],
},
})
end
+
context 'without any linkage includes (default)' do
it 'can serialize primary data for an object with to-one and to-many relationships' do
post = create(:post)
primary_data = serialize_primary(post, {serializer: MyApp::PostSerializer})
expect(primary_data).to eq({
@@ -109,10 +114,11 @@
},
},
},
})
end
+
it 'does not include relationship links if relationship_{self_link,_related_link} are nil' do
post = create(:post)
primary_data = serialize_primary(post, {serializer: MyApp::PostSerializerWithoutLinks})
expect(primary_data).to eq({
'id' => '1',
@@ -127,10 +133,11 @@
'author' => {},
'long-comments' => {},
},
})
end
+
it 'does not include id when it is nil' do
post = create(:post)
post.id = nil
primary_data = serialize_primary(post, {serializer: MyApp::PostSerializerWithoutLinks})
expect(primary_data).to eq({
@@ -143,10 +150,11 @@
'author' => {},
'long-comments' => {},
},
})
end
+
it 'serializes object when multiple attributes are declared once' do
post = create(:post)
primary_data = serialize_primary(post, {serializer: MyApp::MultipleAttributesSerializer})
expect(primary_data).to eq({
'id' => '1',
@@ -159,10 +167,11 @@
'self' => '/posts/1',
}
})
end
end
+
context 'with linkage includes' do
it 'can serialize primary data for a null to-one relationship' do
post = create(:post, author: nil)
options = {
serializer: MyApp::PostSerializer,
@@ -198,10 +207,11 @@
'data' => [],
},
},
})
end
+
it 'can serialize primary data for a simple to-one relationship' do
post = create(:post, :with_author)
options = {
serializer: MyApp::PostSerializer,
include_linkages: ['author', 'long-comments'],
@@ -239,10 +249,11 @@
'data' => [],
},
},
})
end
+
it 'can serialize primary data for an empty to-many relationship' do
post = create(:post, long_comments: [])
options = {
serializer: MyApp::PostSerializer,
include_linkages: ['author', 'long-comments'],
@@ -277,10 +288,11 @@
'data' => [],
},
},
})
end
+
it 'can serialize primary data for a simple to-many relationship' do
long_comments = create_list(:long_comment, 2)
post = create(:post, long_comments: long_comments)
options = {
serializer: MyApp::PostSerializer,
@@ -326,10 +338,11 @@
},
},
})
end
end
+
it 'can serialize primary data for an empty serializer with no attributes' do
post = create(:post)
primary_data = serialize_primary(post, {serializer: MyApp::EmptySerializer})
expect(primary_data).to eq({
'id' => '1',
@@ -337,10 +350,11 @@
'links' => {
'self' => '/posts/1',
},
})
end
+
it 'can find the correct serializer by object class name' do
post = create(:post)
primary_data = serialize_primary(post)
expect(primary_data).to eq({
'id' => '1',
@@ -500,50 +514,57 @@
# primary data is not explicitly tested here. If things are broken, look above here first.
it 'can serialize a nil object' do
expect(JSONAPI::Serializer.serialize(nil)).to eq({'data' => nil})
end
+
it 'can serialize a nil object with includes' do
# Also, the include argument is not validated in this case because we don't know the type.
data = JSONAPI::Serializer.serialize(nil, include: ['fake'])
expect(data).to eq({'data' => nil, 'included' => []})
end
+
it 'can serialize an empty array' do
# Also, the include argument is not validated in this case because we don't know the type.
data = JSONAPI::Serializer.serialize([], is_collection: true, include: ['fake'])
expect(data).to eq({'data' => [], 'included' => []})
end
+
it 'can serialize a simple object' do
post = create(:post)
expect(JSONAPI::Serializer.serialize(post)).to eq({
'data' => serialize_primary(post, {serializer: MyApp::PostSerializer}),
})
end
+
it 'can include a top level jsonapi node' do
post = create(:post)
jsonapi_version = {'version' => '1.0'}
expect(JSONAPI::Serializer.serialize(post, jsonapi: jsonapi_version)).to eq({
'jsonapi' => {'version' => '1.0'},
'data' => serialize_primary(post, {serializer: MyApp::PostSerializer}),
})
end
+
it 'can include a top level meta node' do
post = create(:post)
meta = {authors: ['Yehuda Katz', 'Steve Klabnik'], copyright: 'Copyright 2015 Example Corp.'}
expect(JSONAPI::Serializer.serialize(post, meta: meta)).to eq({
'meta' => meta,
'data' => serialize_primary(post, {serializer: MyApp::PostSerializer}),
})
end
+
it 'can include a top level links node' do
post = create(:post)
links = {self: 'http://example.com/posts'}
expect(JSONAPI::Serializer.serialize(post, links: links)).to eq({
'links' => links,
'data' => serialize_primary(post, {serializer: MyApp::PostSerializer}),
})
end
+
# TODO: remove this code on next major release
it 'can include a top level errors node - deprecated' do
post = create(:post)
errors = [
{
@@ -560,28 +581,32 @@
expect(JSONAPI::Serializer.serialize(post, errors: errors)).to eq({
'errors' => errors,
'data' => serialize_primary(post, {serializer: MyApp::PostSerializer}),
})
end
+
it 'can serialize a single object with an `each` method by passing skip_collection_check: true' do
post = create(:post)
post.define_singleton_method(:each) do
"defining this just to defeat the duck-type check"
end
+
expect(JSONAPI::Serializer.serialize(post, skip_collection_check: true)).to eq({
'data' => serialize_primary(post, {serializer: MyApp::PostSerializer}),
})
end
+
it 'can serialize a collection' do
posts = create_list(:post, 2)
expect(JSONAPI::Serializer.serialize(posts, is_collection: true)).to eq({
'data' => [
serialize_primary(posts.first, {serializer: MyApp::PostSerializer}),
serialize_primary(posts.last, {serializer: MyApp::PostSerializer}),
],
})
end
+
it 'raises AmbiguousCollectionError if is_collection is not passed' do
posts = create_list(:post, 2)
error = JSONAPI::Serializer::AmbiguousCollectionError
expect { JSONAPI::Serializer.serialize(posts) }.to raise_error(error)
end
@@ -594,21 +619,24 @@
it 'can serialize a nil object when given serializer' do
options = {serializer: MyApp::PostSerializer}
expect(JSONAPI::Serializer.serialize(nil, options)).to eq({'data' => nil})
end
+
it 'can serialize an empty array when given serializer' do
options = {is_collection: true, serializer: MyApp::PostSerializer}
expect(JSONAPI::Serializer.serialize([], options)).to eq({'data' => []})
end
+
it 'can serialize a simple object when given serializer' do
post = create(:post)
options = {serializer: MyApp::SimplestPostSerializer}
expect(JSONAPI::Serializer.serialize(post, options)).to eq({
'data' => serialize_primary(post, {serializer: MyApp::SimplestPostSerializer}),
})
end
+
it 'handles include of nil to-one relationship with compound document' do
post = create(:post)
expected_primary_data = serialize_primary(post, {
serializer: MyApp::PostSerializer,
@@ -617,10 +645,11 @@
expect(JSONAPI::Serializer.serialize(post, include: ['author'])).to eq({
'data' => expected_primary_data,
'included' => [],
})
end
+
it 'handles include of simple to-one relationship with compound document' do
post = create(:post, :with_author)
expected_primary_data = serialize_primary(post, {
serializer: MyApp::PostSerializer,
@@ -631,10 +660,11 @@
'included' => [
serialize_primary(post.author, {serializer: MyAppOtherNamespace::UserSerializer}),
],
})
end
+
it 'handles include of empty to-many relationships with compound document' do
post = create(:post, :with_author, long_comments: [])
expected_primary_data = serialize_primary(post, {
serializer: MyApp::PostSerializer,
@@ -643,10 +673,11 @@
expect(JSONAPI::Serializer.serialize(post, include: ['long-comments'])).to eq({
'data' => expected_primary_data,
'included' => [],
})
end
+
it 'handles include of to-many relationships with compound document' do
long_comments = create_list(:long_comment, 2)
post = create(:post, :with_author, long_comments: long_comments)
expected_primary_data = serialize_primary(post, {
@@ -659,10 +690,11 @@
serialize_primary(long_comments.first, {serializer: MyApp::LongCommentSerializer}),
serialize_primary(long_comments.last, {serializer: MyApp::LongCommentSerializer}),
],
})
end
+
it 'only includes one copy of each referenced relationship' do
long_comment = create(:long_comment)
long_comments = [long_comment, long_comment]
post = create(:post, :with_author, long_comments: long_comments)
@@ -675,10 +707,11 @@
'included' => [
serialize_primary(long_comment, {serializer: MyApp::LongCommentSerializer}),
],
})
end
+
it 'handles circular-referencing relationships with compound document' do
long_comments = create_list(:long_comment, 2)
post = create(:post, :with_author, long_comments: long_comments)
# Make sure each long-comment has a circular reference back to the post.
@@ -694,14 +727,16 @@
serialize_primary(post.long_comments.first, {serializer: MyApp::LongCommentSerializer}),
serialize_primary(post.long_comments.last, {serializer: MyApp::LongCommentSerializer}),
],
})
end
+
it 'errors if include is not a defined attribute' do
user = create(:user)
expect { JSONAPI::Serializer.serialize(user, include: ['fake-attr']) }.to raise_error
end
+
it 'handles recursive loading of relationships' do
user = create(:user)
long_comments = create_list(:long_comment, 2, user: user)
post = create(:post, :with_author, long_comments: long_comments)
# Make sure each long-comment has a circular reference back to the post.
@@ -735,10 +770,11 @@
# Multiple expectations for better diff output for debugging.
expect(actual_data['data']).to eq(expected_data['data'])
expect(actual_data['included']).to eq(expected_data['included'])
expect(actual_data).to eq(expected_data)
end
+
it 'handles recursive loading of multiple to-one relationships on children' do
first_user = create(:user)
second_user = create(:user)
first_comment = create(:long_comment, user: first_user)
second_comment = create(:long_comment, user: second_user)
@@ -772,10 +808,11 @@
# Multiple expectations for better diff output for debugging.
expect(actual_data['data']).to eq(expected_data['data'])
expect(actual_data['included']).to eq(expected_data['included'])
expect(actual_data).to eq(expected_data)
end
+
it 'includes linkage in compounded resources only if the immediate parent was also included' do
comment_user = create(:user)
long_comments = [create(:long_comment, user: comment_user)]
post = create(:post, :with_author, long_comments: long_comments)
@@ -800,10 +837,11 @@
# Multiple expectations for better diff output for debugging.
expect(actual_data['data']).to eq(expected_data['data'])
expect(actual_data['included']).to eq(expected_data['included'])
expect(actual_data).to eq(expected_data)
end
+
it 'handles recursive loading of to-many relationships with overlapping include paths' do
user = create(:user)
long_comments = create_list(:long_comment, 2, user: user)
post = create(:post, :with_author, long_comments: long_comments)
# Make sure each long-comment has a circular reference back to the post.
@@ -882,10 +920,11 @@
'self' => '/posts/1'
}
}
})
end
+
it 'allows to limit fields(relationships) for serialized resource' do
first_user = create(:user)
second_user = create(:user)
first_comment = create(:long_comment, user: first_user)
second_comment = create(:long_comment, user: second_user)
@@ -907,10 +946,11 @@
'related' => '/posts/1/long-comments'
}
}
})
end
+
it "allows also to pass specific fields as array instead of comma-separates values" do
first_user = create(:user)
second_user = create(:user)
first_comment = create(:long_comment, user: first_user)
second_comment = create(:long_comment, user: second_user)
@@ -928,10 +968,11 @@
'related' => '/posts/1/author'
}
}
})
end
+
it 'allows to limit fields(attributes and relationships) for included resources' do
first_user = create(:user)
second_user = create(:user)
first_comment = create(:long_comment, user: first_user)
second_comment = create(:long_comment, user: second_user)
@@ -981,52 +1022,59 @@
describe 'internal-only parse_relationship_paths' do
it 'correctly handles empty arrays' do
result = JSONAPI::Serializer.send(:parse_relationship_paths, [])
expect(result).to eq({})
end
+
it 'correctly handles single-level relationship paths' do
result = JSONAPI::Serializer.send(:parse_relationship_paths, ['foo'])
expect(result).to eq({
'foo' => {_include: true}
})
end
+
it 'correctly handles multi-level relationship paths' do
result = JSONAPI::Serializer.send(:parse_relationship_paths, ['foo.bar'])
expect(result).to eq({
'foo' => {_include: true, 'bar' => {_include: true}}
})
end
+
it 'correctly handles multi-level relationship paths with same parent' do
paths = ['foo', 'foo.bar']
result = JSONAPI::Serializer.send(:parse_relationship_paths, paths)
expect(result).to eq({
'foo' => {_include: true, 'bar' => {_include: true}}
})
end
+
it 'correctly handles multi-level relationship paths with different parent' do
paths = ['foo', 'bar', 'bar.baz']
result = JSONAPI::Serializer.send(:parse_relationship_paths, paths)
expect(result).to eq({
'foo' => {_include: true},
'bar' => {_include: true, 'baz' => {_include: true}},
})
end
+
it 'correctly handles three-leveled path' do
paths = ['foo', 'foo.bar', 'foo.bar.baz']
result = JSONAPI::Serializer.send(:parse_relationship_paths, paths)
expect(result).to eq({
'foo' => {_include: true, 'bar' => {_include: true, 'baz' => {_include: true}}}
})
end
+
it 'correctly handles three-leveled path with skipped middle' do
paths = ['foo', 'foo.bar.baz']
result = JSONAPI::Serializer.send(:parse_relationship_paths, paths)
expect(result).to eq({
'foo' => {_include: true, 'bar' => {_include: true, 'baz' => {_include: true}}}
})
end
end
+
describe 'if/unless handling with contexts' do
it 'can be used to show/hide attributes' do
post = create(:post)
options = {serializer: MyApp::PostSerializerWithContext}
@@ -1066,10 +1114,11 @@
options[:context] = {show_body: true, hide_body: true}
data = JSONAPI::Serializer.serialize(post, options)
expect(data['data']['attributes']).to_not have_key('body')
end
end
+
describe 'context' do
it 'is passed through all relationship serializers' do
# Force long_comments to be serialized by the context-sensitive serializer.
expect_any_instance_of(MyApp::LongComment).to receive(:jsonapi_serializer_class_name)
.at_least(:once)
@@ -1111,20 +1160,22 @@
expect(data['data']['relationships']['author']['links']).to eq({
'self' => '/posts/1/relationships/author',
'related' => '/posts/1/author'
})
end
+
it 'adds base_url to links if passed' do
long_comments = create_list(:long_comment, 1)
post = create(:post, long_comments: long_comments)
data = JSONAPI::Serializer.serialize(post, base_url: 'http://example.com')
expect(data['data']['links']['self']).to eq('http://example.com/posts/1')
expect(data['data']['relationships']['author']['links']).to eq({
'self' => 'http://example.com/posts/1/relationships/author',
'related' => 'http://example.com/posts/1/author'
})
end
+
it 'uses overriden base_url method if it exists' do
long_comments = create_list(:long_comment, 1)
post = create(:post, long_comments: long_comments)
data = JSONAPI::Serializer.serialize(post, serializer: MyApp::PostSerializerWithBaseUrl)
expect(data['data']['links']['self']).to eq('http://example.com/posts/1')
@@ -1229,8 +1280,58 @@
actual_data = JSONAPI::Serializer.serialize(post, include: includes, namespace: Api::V1)
# Multiple expectations for better diff output for debugging.
expect(actual_data['data']).to eq(expected_data['data'])
expect(actual_data['included']).to eq(expected_data['included'])
expect(actual_data).to eq(expected_data)
+ end
+ end
+
+ describe 'instrumentation' do
+ let(:post) { create(:post, :with_author) }
+ let(:events) { [] }
+
+ before do
+ ActiveSupport::Notifications.subscribe(notification_name) do |*args|
+ events << ActiveSupport::Notifications::Event.new(*args)
+ end
+ end
+
+ describe 'serialize_primary' do
+ let(:notification_name) { 'render.jsonapi_serializers.serialize_primary' }
+
+ it 'sends an event for a single serialize call' do
+ JSONAPI::Serializer.serialize(post)
+
+ expect(events.length).to eq(1)
+ expect(events[0].name).to eq(notification_name)
+ expect(events[0].payload).to eq({class_name: "MyApp::Post"})
+ end
+
+ it 'sends events for includes' do
+ JSONAPI::Serializer.serialize(post, include: ['author'])
+
+ expect(events.length).to eq(2)
+ expect(events[0].payload).to eq({class_name: "MyApp::Post"})
+ expect(events[1].payload).to eq({class_name: "MyApp::User"})
+ end
+ end
+
+ describe 'find_recursive_relationships' do
+ let(:notification_name) { 'render.jsonapi_serializers.find_recursive_relationships' }
+
+ it 'does not send event when there are no includes' do
+ JSONAPI::Serializer.serialize(post)
+ expect(events.length).to eq(0)
+ end
+
+ it 'sends events for includes' do
+ JSONAPI::Serializer.serialize(post, include: ['author'])
+
+ expect(events.length).to eq(2)
+ expect(events[0].name).to eq(notification_name)
+ expect(events[0].payload).to eq({class_name: "MyApp::User"})
+ expect(events[1].name).to eq(notification_name)
+ expect(events[1].payload).to eq({class_name: "MyApp::Post"})
+ end
end
end
end