From 85658c0230c07ac90fff9d6f393efe584594f8b6 Mon Sep 17 00:00:00 2001 From: Benjamin Fleischer Date: Wed, 10 Feb 2016 00:44:06 -0600 Subject: [PATCH] Add better serialization_scope tests; uncover bug --- lib/active_model/serializer.rb | 2 + .../serialization_scope_name_test.rb | 266 ++++++++++++++---- 2 files changed, 214 insertions(+), 54 deletions(-) diff --git a/lib/active_model/serializer.rb b/lib/active_model/serializer.rb index d17e1879..6b3d4090 100644 --- a/lib/active_model/serializer.rb +++ b/lib/active_model/serializer.rb @@ -96,6 +96,8 @@ module ActiveModel _serializer_instance_methods.include?(name) end + # TODO: Fix load-order failures when different serializer instances define different + # scope methods def self._serializer_instance_methods @_serializer_instance_methods ||= (public_instance_methods - Object.public_instance_methods).to_set end diff --git a/test/action_controller/serialization_scope_name_test.rb b/test/action_controller/serialization_scope_name_test.rb index 5969f522..1d49bddf 100644 --- a/test/action_controller/serialization_scope_name_test.rb +++ b/test/action_controller/serialization_scope_name_test.rb @@ -1,63 +1,221 @@ require 'test_helper' -require 'pathname' -class DefaultScopeNameTest < ActionController::TestCase - class UserSerializer < ActiveModel::Serializer +module SerializationScopeTesting + class User < ActiveModelSerializers::Model + attr_accessor :id, :name, :admin def admin? - current_user.admin - end - attributes :admin? - end - - class UserTestController < ActionController::Base - protect_from_forgery - - before_action { request.format = :json } - - def current_user - User.new(id: 1, name: 'Pete', admin: false) - end - - def render_new_user - render json: User.new(id: 1, name: 'Pete', admin: false), serializer: UserSerializer, adapter: :json_api + admin end end + class Comment < ActiveModelSerializers::Model + attr_accessor :id, :body + end + class Post < ActiveModelSerializers::Model + attr_accessor :id, :title, :body, :comments + end + class PostSerializer < ActiveModel::Serializer + attributes :id, :title, :body, :comments - tests UserTestController + def body + "The 'scope' is the 'current_user': #{scope == current_user}" + end - def test_default_scope_name - get :render_new_user - assert_equal '{"data":{"id":"1","type":"users","attributes":{"admin?":false}}}', @response.body - end -end - -class SerializationScopeNameTest < ActionController::TestCase - class AdminUserSerializer < ActiveModel::Serializer - def admin? - current_admin.admin - end - attributes :admin? - end - - class AdminUserTestController < ActionController::Base - protect_from_forgery - - serialization_scope :current_admin - before_action { request.format = :json } - - def current_admin - User.new(id: 2, name: 'Bob', admin: true) - end - - def render_new_user - render json: User.new(id: 1, name: 'Pete', admin: false), serializer: AdminUserSerializer, adapter: :json_api - end - end - - tests AdminUserTestController - - def test_override_scope_name_with_controller - get :render_new_user - assert_equal '{"data":{"id":"1","type":"users","attributes":{"admin?":true}}}', @response.body + def comments + if current_user.admin? + [Comment.new(id: 1, body: 'Admin')] + else + [Comment.new(id: 2, body: 'Scoped')] + end + end + + def json_key + 'post' + end + end + class PostTestController < ActionController::Base + attr_accessor :current_user + def render_post_by_non_admin + self.current_user = User.new(id: 3, name: 'Pete', admin: false) + render json: new_post, serializer: serializer, adapter: :json + end + + def render_post_by_admin + self.current_user = User.new(id: 3, name: 'Pete', admin: true) + render json: new_post, serializer: serializer, adapter: :json + end + + private + + def new_post + Post.new(id: 4, title: 'Title') + end + + def serializer + PostSerializer + end + end + class PostViewContextSerializer < PostSerializer + def body + "The 'scope' is the 'view_context': #{scope == view_context}" + end + + def comments + if view_context.controller.current_user.admin? + [Comment.new(id: 1, body: 'Admin')] + else + [Comment.new(id: 2, body: 'Scoped')] + end + end + end + class DefaultScopeTest < ActionController::TestCase + tests PostTestController + + def test_default_serialization_scope + assert_equal :current_user, @controller._serialization_scope + end + + def test_default_serialization_scope_object + assert_equal @controller.current_user, @controller.serialization_scope + end + + def test_default_scope_non_admin + get :render_post_by_non_admin + expected_json = { + post: { + id: 4, + title: 'Title', + body: "The 'scope' is the 'current_user': true", + comments: [ + { id: 2, body: 'Scoped' } + ] + } + }.to_json + assert_equal expected_json, @response.body + end + + def test_default_scope_admin + get :render_post_by_admin + expected_json = { + post: { + id: 4, + title: 'Title', + body: "The 'scope' is the 'current_user': true", + comments: [ + { id: 1, body: 'Admin' } + ] + } + }.to_json + assert_equal expected_json, @response.body + end + end + class SerializationScopeTest < ActionController::TestCase + class PostViewContextTestController < PostTestController + serialization_scope :view_context + + private + + def serializer + PostViewContextSerializer + end + end + tests PostViewContextTestController + + def test_defined_serialization_scope + assert_equal :view_context, @controller._serialization_scope + end + + def test_defined_serialization_scope_object + assert_equal @controller.view_context.class, @controller.serialization_scope.class + end + + def test_serialization_scope_non_admin + get :render_post_by_non_admin + expected_json = { + post: { + id: 4, + title: 'Title', + body: "The 'scope' is the 'view_context': true", + comments: [ + { id: 2, body: 'Scoped' } + ] + } + }.to_json + assert_equal expected_json, @response.body + end + + def test_serialization_scope_admin + get :render_post_by_admin + expected_json = { + post: { + id: 4, + title: 'Title', + body: "The 'scope' is the 'view_context': true", + comments: [ + { id: 1, body: 'Admin' } + ] + } + }.to_json + assert_equal expected_json, @response.body + end + end + class NilSerializationScopeTest < ActionController::TestCase + class PostViewContextTestController < ActionController::Base + serialization_scope nil + + attr_accessor :current_user + + def render_post_with_no_scope + self.current_user = User.new(id: 3, name: 'Pete', admin: false) + render json: new_post, serializer: PostSerializer, adapter: :json + end + + # TODO: run test when + # global state in Serializer._serializer_instance_methods is fixed + # def render_post_with_passed_in_scope + # self.current_user = User.new(id: 3, name: 'Pete', admin: false) + # render json: new_post, serializer: PostSerializer, adapter: :json, scope: current_user, scope_name: :current_user + # end + + private + + def new_post + Post.new(id: 4, title: 'Title') + end + end + tests PostViewContextTestController + + def test_nil_serialization_scope + assert_nil @controller._serialization_scope + end + + def test_nil_serialization_scope_object + assert_nil @controller.serialization_scope + end + + # TODO: change to NoMethodError and match 'admin?' when the + # global state in Serializer._serializer_instance_methods is fixed + def test_nil_scope + exception = assert_raises(NameError) do + get :render_post_with_no_scope + end + assert_match(/admin|current_user/, exception.message) + end + + # TODO: run test when + # global state in Serializer._serializer_instance_methods is fixed + # def test_nil_scope_passed_in_current_user + # get :render_post_with_passed_in_scope + # expected_json = { + # post: { + # id: 4, + # title: 'Title', + # body: "The 'scope' is the 'current_user': true", + # comments: [ + # { id: 2, body: 'Scoped' } + # ] + # } + # }.to_json + # assert_equal expected_json, @response.body + # end end end