Rewrite tests.

This commit is contained in:
Stas SUȘCOV 2020-03-08 21:59:31 +00:00 committed by Stas
parent 73814d1be5
commit c533634293
16 changed files with 615 additions and 26 deletions

View File

@ -1,6 +1,6 @@
require:
- rubocop-performance
# - rubocop-rspec
- rubocop-rspec
Style/FrozenStringLiteralComment:
Enabled: false
@ -11,42 +11,68 @@ Style/SymbolArray:
Style/WordArray:
Enabled: false
# RSpec/DescribedClass:
# Enabled: false
Style/SymbolProc:
Exclude:
- 'spec/fixtures/*.rb'
Lint/DuplicateMethods:
Exclude:
- 'spec/fixtures/*.rb'
RSpec/FilePath:
Enabled: false
RSpec/DescribedClass:
Enabled: false
RSpec/ExampleLength:
Enabled: false
RSpec/MultipleExpectations:
Enabled: false
RSpec/NestedGroups:
Enabled: false
Performance/TimesMap:
Exclude:
- 'spec/**/**.rb'
# TODO: Fix these...
Style/Documentation:
Enabled: false
Style/GuardClause:
Enabled: false
Exclude:
- 'lib/**/**.rb'
Style/ConditionalAssignment:
Enabled: false
Exclude:
- 'lib/**/**.rb'
Style/ClassAndModuleChildren:
Enabled: false
Style/IfUnlessModifier:
Exclude:
- 'lib/**/**.rb'
Lint/AssignmentInCondition:
Enabled: false
Exclude:
- 'lib/**/**.rb'
Metrics/ModuleLength:
Metrics:
Exclude:
- 'lib/**/**.rb'
Metrics/BlockLength:
Enabled: false
Layout/LineLength:
Enabled: false
Metrics:
Enabled: false
Exclude:
- 'lib/**/**.rb'
Naming/PredicateName:
Enabled: false
Exclude:
- 'lib/**/**.rb'
Naming/AccessorMethodName:
Enabled: false
Performance/TimesMap:
Enabled: false
# RSpec/BeforeAfterAll:
# Enabled: false
Exclude:
- 'lib/**/**.rb'

View File

@ -2,3 +2,6 @@ source 'https://rubygems.org'
# Specify your gem's dependencies in fast_jsonapi.gemspec
gemspec
# TODO: Remove once the gem is released...
gem 'jsonapi-rspec', github: 'jsonapi-rb/jsonapi-rspec'

View File

@ -11,5 +11,5 @@ RuboCop::RakeTask.new('rubocop') do |task|
]
end
RSpec::Core::RakeTask.new(spec: ['rubocop'])
task(default: :spec)
RSpec::Core::RakeTask.new(:spec)
task(default: [:rubocop, :spec])

View File

@ -150,6 +150,7 @@ module FastJsonapi
# ensure that the record type is correctly transformed
if record_type
set_type(record_type)
# TODO: Remove dead code
elsif reflected_record_type
set_type(reflected_record_type)
end
@ -231,6 +232,8 @@ module FastJsonapi
self.cachable_relationships_to_serialize = {} if cachable_relationships_to_serialize.nil?
self.uncachable_relationships_to_serialize = {} if uncachable_relationships_to_serialize.nil?
# TODO: Remove this undocumented option.
# Delegate the caching to the serializer exclusively.
if !relationship.cached
uncachable_relationships_to_serialize[relationship.name] = relationship
else

View File

@ -63,6 +63,7 @@ module FastJsonapi
end
def serializer_for(record, serialization_params)
# TODO: Remove this, dead code...
if @static_serializer
@static_serializer
@ -78,6 +79,7 @@ module FastJsonapi
serializer_for_name(record.class.name)
else
# TODO: Remove this, dead code...
raise "Unknown serializer for object #{record.inspect}"
end
end

28
spec/fixtures/_user.rb vendored Normal file
View File

@ -0,0 +1,28 @@
class User
attr_accessor :uid, :first_name, :last_name, :email
def self.fake(id = nil)
faked = new
faked.uid = id || SecureRandom.uuid
faked.first_name = FFaker::Name.first_name
faked.last_name = FFaker::Name.last_name
faked.email = FFaker::Internet.email
faked
end
end
class NoSerializerUser < User
end
class UserSerializer
include FastJsonapi::ObjectSerializer
set_id :uid
attributes :first_name, :last_name, :email
meta do |obj|
{
email_length: obj.email.size
}
end
end

72
spec/fixtures/actor.rb vendored Normal file
View File

@ -0,0 +1,72 @@
require 'active_support/cache'
class Actor < User
attr_accessor :movies, :movie_ids
def self.fake(id = nil)
faked = super(id)
faked.movies = []
faked.movie_ids = []
faked
end
def movie_urls
{
movie_url: movies[0]&.url
}
end
end
class ActorSerializer < UserSerializer
set_type :actor
attribute :email, if: ->(_object, params) { params[:conditionals_off].nil? }
has_many(
:played_movies,
serializer: :movie,
links: :movie_urls,
if: ->(_object, params) { params[:conditionals_off].nil? }
) do |object|
object.movies
end
end
class CamelCaseActorSerializer
include FastJsonapi::ObjectSerializer
set_key_transform :camel
set_id :uid
set_type :user_actor
attributes :first_name
link :movie_url do |obj|
obj.movie_urls.values[0]
end
has_many(
:played_movies,
serializer: :movie
) do |object|
object.movies
end
end
class BadMovieSerializerActorSerializer < ActorSerializer
has_many :played_movies, serializer: :bad, object_method_name: :movies
end
module Cached
class ActorSerializer < ::ActorSerializer
# TODO: Fix this, the serializer gets cached on inherited classes...
has_many :played_movies, serializer: :movie do |object|
object.movies
end
cache_options(
store: ActiveSupport::Cache::MemoryStore.new,
namespace: 'test'
)
end
end

116
spec/fixtures/movie.rb vendored Normal file
View File

@ -0,0 +1,116 @@
class Movie
attr_accessor(
:id,
:name,
:year,
:actors,
:actor_ids,
:polymorphics,
:owner,
:owner_id
)
def self.fake(id = nil)
faked = new
faked.id = id || SecureRandom.uuid
faked.name = FFaker::Movie.title
faked.year = FFaker::Vehicle.year
faked.actors = []
faked.actor_ids = []
faked.polymorphics = []
faked
end
def url(obj = nil)
@url ||= FFaker::Internet.http_url
return @url if obj.nil?
@url + '?' + obj.hash.to_s
end
def owner=(ownr)
@owner = ownr
@owner_id = ownr.uid
end
def actors=(acts)
@actors = acts
@actor_ids = actors.map do |actor|
actor.movies << self
actor.uid
end
end
end
class MovieSerializer
include FastJsonapi::ObjectSerializer
set_type :movie
attributes :name
attribute :release_year do |object|
object.year
end
link :self, :url
belongs_to :owner, serializer: UserSerializer
has_many(
:actors,
links: {
actors_self: :url,
related: ->(obj) { obj.url(obj) }
}
)
has_one(
:creator,
object_method_name: :owner,
id_method_name: :uid,
serializer: ->(object, _params) { UserSerializer if object.is_a?(User) }
)
has_many(
:actors_and_users,
id_method_name: :uid,
polymorphic: {
Actor => :actor,
User => :user
}
) do |obj|
obj.polymorphics
end
has_many(
:dynamic_actors_and_users,
id_method_name: :uid,
polymorphic: true
) do |obj|
obj.polymorphics
end
has_many(
:auto_detected_actors_and_users,
id_method_name: :uid
) do |obj|
obj.polymorphics
end
end
module Cached
class MovieSerializer < ::MovieSerializer
cache_options(
store: ActorSerializer.cache_store_instance,
namespace: 'test'
)
has_one(
:creator,
id_method_name: :uid,
serializer: :actor,
# TODO: Remove this undocumented option.
# Delegate the caching to the serializer exclusively.
cached: false
) do |obj|
obj.owner
end
end
end

View File

@ -0,0 +1,63 @@
require 'spec_helper'
RSpec.describe FastJsonapi::ObjectSerializer do
let(:actor) do
act = Actor.fake
act.movies = [Movie.fake]
act
end
let(:params) { {} }
let(:serialized) do
ActorSerializer.new(actor, params).serializable_hash.as_json
end
describe 'attributes' do
it do
expect(serialized['data']).to have_id(actor.uid)
expect(serialized['data']).to have_type('actor')
expect(serialized['data'])
.to have_jsonapi_attributes('first_name', 'last_name', 'email').exactly
expect(serialized['data']).to have_attribute('first_name')
.with_value(actor.first_name)
expect(serialized['data']).to have_attribute('last_name')
.with_value(actor.last_name)
expect(serialized['data']).to have_attribute('email')
.with_value(actor.email)
end
context 'with nil identifier' do
before { actor.uid = nil }
it { expect(serialized['data']).to have_id(nil) }
end
context 'with `if` conditions' do
let(:params) { { params: { conditionals_off: 'yes' } } }
it do
expect(serialized['data']).not_to have_attribute('email')
end
end
context 'with include and fields' do
let(:params) do
{
include: [:played_movies],
fields: { movie: [:release_year], actor: [:first_name] }
}
end
it do
expect(serialized['data'])
.to have_jsonapi_attributes(:first_name).exactly
expect(serialized['included']).to include(
have_type('movie')
.and(have_id(actor.movies[0].id))
.and(have_jsonapi_attributes('release_year').exactly)
)
end
end
end
end

View File

@ -0,0 +1,29 @@
require 'spec_helper'
RSpec.describe FastJsonapi::ObjectSerializer do
let(:actor) do
faked = Actor.fake
movie = Movie.fake
movie.owner = User.fake
movie.actors = [faked]
faked.movies = [movie]
faked
end
let(:cache_store) { Cached::ActorSerializer.cache_store_instance }
describe 'with caching' do
it do
expect(cache_store.delete(actor, namespace: 'test')).to be(false)
Cached::ActorSerializer.new(
[actor, actor], include: ['played_movies', 'played_movies.owner']
).serializable_hash
expect(cache_store.delete(actor, namespace: 'test')).to be(true)
expect(cache_store.delete(actor.movies[0], namespace: 'test')).to be(true)
expect(
cache_store.delete(actor.movies[0].owner, namespace: 'test')
).to be(false)
end
end
end

View File

@ -0,0 +1,25 @@
require 'spec_helper'
RSpec.describe FastJsonapi::ObjectSerializer do
let(:actor) { Actor.fake }
let(:params) { {} }
describe 'with errors' do
it do
expect do
BadMovieSerializerActorSerializer.new(
actor, include: ['played_movies']
)
end.to raise_error(
NameError, /cannot resolve a serializer class for 'bad'/
)
end
it do
expect { ActorSerializer.new(actor, include: ['bad_include']) }
.to raise_error(
ArgumentError, /bad_include is not specified as a relationship/
)
end
end
end

View File

@ -0,0 +1,19 @@
require 'spec_helper'
RSpec.describe FastJsonapi::ObjectSerializer do
let(:actor) { Actor.fake }
let(:params) { {} }
let(:serialized) do
CamelCaseActorSerializer.new(actor, params).serializable_hash.as_json
end
describe 'camel case key tranformation' do
it do
expect(serialized['data']).to have_id(actor.uid)
expect(serialized['data']).to have_type('UserActor')
expect(serialized['data']).to have_attribute('FirstName')
expect(serialized['data']).to have_relationship('PlayedMovies')
expect(serialized['data']).to have_link('MovieUrl').with_value(nil)
end
end
end

View File

@ -0,0 +1,47 @@
require 'spec_helper'
RSpec.describe FastJsonapi::ObjectSerializer do
let(:movie) do
faked = Movie.fake
faked.actors = [Actor.fake]
faked
end
let(:params) { {} }
let(:serialized) do
MovieSerializer.new(movie, params).serializable_hash.as_json
end
describe 'links' do
it do
expect(serialized['data']).to have_link('self').with_value(movie.url)
expect(serialized['data']['relationships']['actors'])
.to have_link('actors_self').with_value(movie.url)
expect(serialized['data']['relationships']['actors'])
.to have_link('related').with_value(movie.url(movie))
end
context 'with included records' do
let(:serialized) do
ActorSerializer.new(movie.actors[0]).serializable_hash.as_json
end
it do
expect(serialized['data']['relationships']['played_movies'])
.to have_link('movie_url').with_value(movie.url)
end
end
context 'with root link' do
let(:params) do
{
links: { 'root_link' => FFaker::Internet.http_url }
}
end
it do
expect(serialized)
.to have_link('root_link').with_value(params[:links]['root_link'])
end
end
end
end

View File

@ -0,0 +1,25 @@
require 'spec_helper'
RSpec.describe FastJsonapi::ObjectSerializer do
let(:user) { User.fake }
let(:params) { {} }
let(:serialized) do
UserSerializer.new(user, params).serializable_hash.as_json
end
it do
expect(serialized['data']).to have_meta('email_length' => user.email.size)
end
context 'with root meta' do
let(:params) do
{
meta: { 'code' => FFaker::Internet.password }
}
end
it do
expect(serialized).to have_meta(params[:meta])
end
end
end

View File

@ -0,0 +1,126 @@
require 'spec_helper'
RSpec.describe FastJsonapi::ObjectSerializer do
let(:movie) do
mov = Movie.fake
mov.actors = rand(2..5).times.map { Actor.fake }
mov.owner = User.fake
poly_act = Actor.fake
poly_act.movies = [Movie.fake]
mov.polymorphics = [User.fake, poly_act]
mov
end
let(:params) { {} }
let(:serialized) do
MovieSerializer.new(movie, params).serializable_hash.as_json
end
describe 'relationships' do
it do
actors_rel = movie.actors.map { |a| { 'id' => a.uid, 'type' => 'actor' } }
expect(serialized['data'])
.to have_relationship('actors').with_data(actors_rel)
expect(serialized['data'])
.to have_relationship('owner')
.with_data('id' => movie.owner.uid, 'type' => 'user')
expect(serialized['data'])
.to have_relationship('creator')
.with_data('id' => movie.owner.uid, 'type' => 'user')
expect(serialized['data'])
.to have_relationship('actors_and_users')
.with_data(
[
{ 'id' => movie.polymorphics[0].uid, 'type' => 'user' },
{ 'id' => movie.polymorphics[1].uid, 'type' => 'actor' }
]
)
expect(serialized['data'])
.to have_relationship('dynamic_actors_and_users')
.with_data(
[
{ 'id' => movie.polymorphics[0].uid, 'type' => 'user' },
{ 'id' => movie.polymorphics[1].uid, 'type' => 'actor' }
]
)
expect(serialized['data'])
.to have_relationship('auto_detected_actors_and_users')
.with_data(
[
{ 'id' => movie.polymorphics[0].uid, 'type' => 'user' },
{ 'id' => movie.polymorphics[1].uid, 'type' => 'actor' }
]
)
end
context 'with include' do
let(:params) do
{ include: [:actors] }
end
it do
movie.actors.each do |actor|
expect(serialized['included']).to include(
have_type('actor')
.and(have_id(actor.uid))
.and(have_relationship('played_movies')
.with_data([{ 'id' => actor.movies[0].id, 'type' => 'movie' }]))
)
end
end
context 'with `if` conditions' do
let(:params) do
{
include: ['actors'],
params: { conditionals_off: 'yes' }
}
end
it do
movie.actors.each do |actor|
expect(serialized['included']).not_to include(
have_type('actor')
.and(have_id(actor.uid))
.and(have_relationship('played_movies'))
)
end
end
end
context 'with polymorphic' do
let(:params) do
{ include: ['actors_and_users.played_movies'] }
end
it do
expect(serialized['included']).to include(
have_type('user').and(have_id(movie.polymorphics[0].uid))
)
expect(serialized['included']).to include(
have_type('movie').and(have_id(movie.polymorphics[1].movies[0].id))
)
expect(serialized['included']).to include(
have_type('actor')
.and(have_id(movie.polymorphics[1].uid))
.and(
have_relationship('played_movies').with_data(
[{
'id' => movie.polymorphics[1].movies[0].id,
'type' => 'movie'
}]
)
)
)
end
end
end
end
end

View File

@ -6,14 +6,19 @@ SimpleCov.start do
end
SimpleCov.minimum_coverage 90
require 'active_record'
require 'active_support/core_ext/object/json'
require 'fast_jsonapi'
require 'ffaker'
require 'rspec'
require 'jsonapi/rspec'
require 'byebug'
require 'securerandom'
Dir[File.dirname(__FILE__) + '/shared/contexts/*.rb'].each {|file| require file }
Dir[File.dirname(__FILE__) + '/shared/examples/*.rb'].each {|file| require file }
Dir[File.expand_path('spec/fixtures/*.rb')].sort.each { |f| require f }
RSpec.configure do |config|
config.include JSONAPI::RSpec
config.mock_with :rspec
config.filter_run_when_matching :focus
config.disable_monkey_patching!