aboutsummaryrefslogtreecommitdiffstats
path: root/activerecord/test/cases/json_serialization_test.rb
diff options
context:
space:
mode:
Diffstat (limited to 'activerecord/test/cases/json_serialization_test.rb')
-rw-r--r--activerecord/test/cases/json_serialization_test.rb311
1 files changed, 311 insertions, 0 deletions
diff --git a/activerecord/test/cases/json_serialization_test.rb b/activerecord/test/cases/json_serialization_test.rb
new file mode 100644
index 0000000000..82cf281cff
--- /dev/null
+++ b/activerecord/test/cases/json_serialization_test.rb
@@ -0,0 +1,311 @@
+# frozen_string_literal: true
+
+require "cases/helper"
+require "models/contact"
+require "models/post"
+require "models/author"
+require "models/tagging"
+require "models/tag"
+require "models/comment"
+
+module JsonSerializationHelpers
+ private
+
+ def set_include_root_in_json(value)
+ original_root_in_json = ActiveRecord::Base.include_root_in_json
+ ActiveRecord::Base.include_root_in_json = value
+ yield
+ ensure
+ ActiveRecord::Base.include_root_in_json = original_root_in_json
+ end
+end
+
+class JsonSerializationTest < ActiveRecord::TestCase
+ include JsonSerializationHelpers
+
+ class NamespacedContact < Contact
+ column :name, :string
+ end
+
+ def setup
+ @contact = Contact.new(
+ name: "Konata Izumi",
+ age: 16,
+ avatar: "binarydata",
+ created_at: Time.utc(2006, 8, 1),
+ awesome: true,
+ preferences: { shows: "anime" }
+ )
+ end
+
+ def test_should_demodulize_root_in_json
+ set_include_root_in_json(true) do
+ @contact = NamespacedContact.new name: "whatever"
+ json = @contact.to_json
+ assert_match %r{^\{"namespaced_contact":\{}, json
+ end
+ end
+
+ def test_should_include_root_in_json
+ set_include_root_in_json(true) do
+ json = @contact.to_json
+
+ assert_match %r{^\{"contact":\{}, json
+ assert_match %r{"name":"Konata Izumi"}, json
+ assert_match %r{"age":16}, json
+ assert_includes json, %("created_at":#{ActiveSupport::JSON.encode(Time.utc(2006, 8, 1))})
+ assert_match %r{"awesome":true}, json
+ assert_match %r{"preferences":\{"shows":"anime"\}}, json
+ end
+ end
+
+ def test_should_encode_all_encodable_attributes
+ json = @contact.to_json
+
+ assert_match %r{"name":"Konata Izumi"}, json
+ assert_match %r{"age":16}, json
+ assert_includes json, %("created_at":#{ActiveSupport::JSON.encode(Time.utc(2006, 8, 1))})
+ assert_match %r{"awesome":true}, json
+ assert_match %r{"preferences":\{"shows":"anime"\}}, json
+ end
+
+ def test_should_allow_attribute_filtering_with_only
+ json = @contact.to_json(only: [:name, :age])
+
+ assert_match %r{"name":"Konata Izumi"}, json
+ assert_match %r{"age":16}, json
+ assert_no_match %r{"awesome":true}, json
+ assert_not_includes json, %("created_at":#{ActiveSupport::JSON.encode(Time.utc(2006, 8, 1))})
+ assert_no_match %r{"preferences":\{"shows":"anime"\}}, json
+ end
+
+ def test_should_allow_attribute_filtering_with_except
+ json = @contact.to_json(except: [:name, :age])
+
+ assert_no_match %r{"name":"Konata Izumi"}, json
+ assert_no_match %r{"age":16}, json
+ assert_match %r{"awesome":true}, json
+ assert_includes json, %("created_at":#{ActiveSupport::JSON.encode(Time.utc(2006, 8, 1))})
+ assert_match %r{"preferences":\{"shows":"anime"\}}, json
+ end
+
+ def test_methods_are_called_on_object
+ # Define methods on fixture.
+ def @contact.label; "Has cheezburger"; end
+ def @contact.favorite_quote; "Constraints are liberating"; end
+
+ # Single method.
+ assert_match %r{"label":"Has cheezburger"}, @contact.to_json(only: :name, methods: :label)
+
+ # Both methods.
+ methods_json = @contact.to_json(only: :name, methods: [:label, :favorite_quote])
+ assert_match %r{"label":"Has cheezburger"}, methods_json
+ assert_match %r{"favorite_quote":"Constraints are liberating"}, methods_json
+ end
+
+ def test_uses_serializable_hash_with_frozen_hash
+ def @contact.serializable_hash(options = nil)
+ super({ only: %w(name) }.freeze)
+ end
+
+ json = @contact.to_json
+ assert_match %r{"name":"Konata Izumi"}, json
+ assert_no_match %r{awesome}, json
+ assert_no_match %r{age}, json
+ end
+
+ def test_uses_serializable_hash_with_only_option
+ def @contact.serializable_hash(options = nil)
+ super(only: %w(name))
+ end
+
+ json = @contact.to_json
+ assert_match %r{"name":"Konata Izumi"}, json
+ assert_no_match %r{awesome}, json
+ assert_no_match %r{age}, json
+ end
+
+ def test_uses_serializable_hash_with_except_option
+ def @contact.serializable_hash(options = nil)
+ super(except: %w(age))
+ end
+
+ json = @contact.to_json
+ assert_match %r{"name":"Konata Izumi"}, json
+ assert_match %r{"awesome":true}, json
+ assert_no_match %r{age}, json
+ end
+
+ def test_does_not_include_inheritance_column_from_sti
+ @contact = ContactSti.new(@contact.attributes)
+ assert_equal "ContactSti", @contact.type
+
+ json = @contact.to_json
+ assert_match %r{"name":"Konata Izumi"}, json
+ assert_no_match %r{type}, json
+ assert_no_match %r{ContactSti}, json
+ end
+
+ def test_serializable_hash_with_default_except_option_and_excluding_inheritance_column_from_sti
+ @contact = ContactSti.new(@contact.attributes)
+ assert_equal "ContactSti", @contact.type
+
+ def @contact.serializable_hash(options = {})
+ super({ except: %w(age) }.merge!(options))
+ end
+
+ json = @contact.to_json
+ assert_match %r{"name":"Konata Izumi"}, json
+ assert_no_match %r{age}, json
+ assert_no_match %r{type}, json
+ assert_no_match %r{ContactSti}, json
+ end
+
+ def test_serializable_hash_should_not_modify_options_in_argument
+ options = { only: :name }.freeze
+ assert_nothing_raised { @contact.serializable_hash(options) }
+ end
+end
+
+class DatabaseConnectedJsonEncodingTest < ActiveRecord::TestCase
+ fixtures :authors, :author_addresses, :posts, :comments, :tags, :taggings
+
+ include JsonSerializationHelpers
+
+ def setup
+ @david = authors(:david)
+ @mary = authors(:mary)
+ end
+
+ def test_includes_uses_association_name
+ json = @david.to_json(include: :posts)
+
+ assert_match %r{"posts":\[}, json
+
+ assert_match %r{"id":1}, json
+ assert_match %r{"name":"David"}, json
+
+ assert_match %r{"author_id":1}, json
+ assert_match %r{"title":"Welcome to the weblog"}, json
+ assert_match %r{"body":"Such a lovely day"}, json
+
+ assert_match %r{"title":"So I was thinking"}, json
+ assert_match %r{"body":"Like I hopefully always am"}, json
+ end
+
+ def test_includes_uses_association_name_and_applies_attribute_filters
+ json = @david.to_json(include: { posts: { only: :title } })
+
+ assert_match %r{"name":"David"}, json
+ assert_match %r{"posts":\[}, json
+
+ assert_match %r{"title":"Welcome to the weblog"}, json
+ assert_no_match %r{"body":"Such a lovely day"}, json
+
+ assert_match %r{"title":"So I was thinking"}, json
+ assert_no_match %r{"body":"Like I hopefully always am"}, json
+ end
+
+ def test_includes_fetches_second_level_associations
+ json = @david.to_json(include: { posts: { include: { comments: { only: :body } } } })
+
+ assert_match %r{"name":"David"}, json
+ assert_match %r{"posts":\[}, json
+
+ assert_match %r{"comments":\[}, json
+ assert_match %r{\{"body":"Thank you again for the welcome"\}}, json
+ assert_match %r{\{"body":"Don't think too hard"\}}, json
+ assert_no_match %r{"post_id":}, json
+ end
+
+ def test_includes_fetches_nth_level_associations
+ json = @david.to_json(
+ include: {
+ posts: {
+ include: {
+ taggings: {
+ include: {
+ tag: { only: :name }
+ }
+ }
+ }
+ }
+ })
+
+ assert_match %r{"name":"David"}, json
+ assert_match %r{"posts":\[}, json
+
+ assert_match %r{"taggings":\[}, json
+ assert_match %r{"tag":\{"name":"General"\}}, json
+ end
+
+ def test_includes_doesnt_merge_opts_from_base
+ json = @david.to_json(
+ only: :id,
+ include: :posts
+ )
+
+ assert_match %{"title":"Welcome to the weblog"}, json
+ end
+
+ def test_should_not_call_methods_on_associations_that_dont_respond
+ def @david.favorite_quote; "Constraints are liberating"; end
+ json = @david.to_json(include: :posts, methods: :favorite_quote)
+
+ assert_not_respond_to @david.posts.first, :favorite_quote
+ assert_match %r{"favorite_quote":"Constraints are liberating"}, json
+ assert_equal 1, %r{"favorite_quote":}.match(json).size
+ end
+
+ def test_should_allow_only_option_for_list_of_authors
+ set_include_root_in_json(false) do
+ authors = [@david, @mary]
+ assert_equal %([{"name":"David"},{"name":"Mary"}]), ActiveSupport::JSON.encode(authors, only: :name)
+ end
+ end
+
+ def test_should_allow_except_option_for_list_of_authors
+ set_include_root_in_json(false) do
+ authors = [@david, @mary]
+ encoded = ActiveSupport::JSON.encode(authors, except: [
+ :name, :author_address_id, :author_address_extra_id,
+ :organization_id, :owned_essay_id
+ ])
+ assert_equal %([{"id":1},{"id":2}]), encoded
+ end
+ end
+
+ def test_should_allow_includes_for_list_of_authors
+ authors = [@david, @mary]
+ json = ActiveSupport::JSON.encode(authors,
+ only: :name,
+ include: {
+ posts: { only: :id }
+ }
+ )
+
+ ['"name":"David"', '"posts":[', '{"id":1}', '{"id":2}', '{"id":4}',
+ '{"id":5}', '{"id":6}', '"name":"Mary"', '"posts":[', '{"id":7}', '{"id":9}'].each do |fragment|
+ assert_includes json, fragment, json
+ end
+ end
+
+ def test_should_allow_options_for_hash_of_authors
+ set_include_root_in_json(true) do
+ authors_hash = {
+ 1 => @david,
+ 2 => @mary
+ }
+ assert_equal %({"1":{"author":{"name":"David"}}}), ActiveSupport::JSON.encode(authors_hash, only: [1, :name])
+ end
+ end
+
+ def test_should_be_able_to_encode_relation
+ set_include_root_in_json(true) do
+ authors_relation = Author.where(id: [@david.id, @mary.id])
+
+ json = ActiveSupport::JSON.encode authors_relation, only: :name
+ assert_equal '[{"author":{"name":"David"}},{"author":{"name":"Mary"}}]', json
+ end
+ end
+end