aboutsummaryrefslogtreecommitdiffstats
path: root/activerecord/test/cases/connection_adapters
diff options
context:
space:
mode:
Diffstat (limited to 'activerecord/test/cases/connection_adapters')
-rw-r--r--activerecord/test/cases/connection_adapters/adapter_leasing_test.rb58
-rw-r--r--activerecord/test/cases/connection_adapters/connection_handler_test.rb306
-rw-r--r--activerecord/test/cases/connection_adapters/connection_specification_test.rb14
-rw-r--r--activerecord/test/cases/connection_adapters/merge_and_resolve_default_url_config_test.rb257
-rw-r--r--activerecord/test/cases/connection_adapters/mysql_type_lookup_test.rb78
-rw-r--r--activerecord/test/cases/connection_adapters/schema_cache_test.rb101
-rw-r--r--activerecord/test/cases/connection_adapters/type_lookup_test.rb120
7 files changed, 934 insertions, 0 deletions
diff --git a/activerecord/test/cases/connection_adapters/adapter_leasing_test.rb b/activerecord/test/cases/connection_adapters/adapter_leasing_test.rb
new file mode 100644
index 0000000000..72838ff56b
--- /dev/null
+++ b/activerecord/test/cases/connection_adapters/adapter_leasing_test.rb
@@ -0,0 +1,58 @@
+# frozen_string_literal: true
+
+require "cases/helper"
+
+module ActiveRecord
+ module ConnectionAdapters
+ class AdapterLeasingTest < ActiveRecord::TestCase
+ class Pool < ConnectionPool
+ def insert_connection_for_test!(c)
+ synchronize do
+ adopt_connection(c)
+ @available.add c
+ end
+ end
+ end
+
+ def setup
+ @adapter = AbstractAdapter.new nil, nil
+ end
+
+ def test_in_use?
+ assert_not @adapter.in_use?, "adapter is not in use"
+ assert @adapter.lease, "lease adapter"
+ assert @adapter.in_use?, "adapter is in use"
+ end
+
+ def test_lease_twice
+ assert @adapter.lease, "should lease adapter"
+ assert_raises(ActiveRecordError) do
+ @adapter.lease
+ end
+ end
+
+ def test_expire_mutates_in_use
+ assert @adapter.lease, "lease adapter"
+ assert @adapter.in_use?, "adapter is in use"
+ @adapter.expire
+ assert_not @adapter.in_use?, "adapter is in use"
+ end
+
+ def test_close
+ pool = Pool.new(ConnectionSpecification.new("primary", {}, nil))
+ pool.insert_connection_for_test! @adapter
+ @adapter.pool = pool
+
+ # Make sure the pool marks the connection in use
+ assert_equal @adapter, pool.connection
+ assert_predicate @adapter, :in_use?
+
+ # Close should put the adapter back in the pool
+ @adapter.close
+ assert_not_predicate @adapter, :in_use?
+
+ assert_equal @adapter, pool.connection
+ end
+ end
+ end
+end
diff --git a/activerecord/test/cases/connection_adapters/connection_handler_test.rb b/activerecord/test/cases/connection_adapters/connection_handler_test.rb
new file mode 100644
index 0000000000..f4cc251fb9
--- /dev/null
+++ b/activerecord/test/cases/connection_adapters/connection_handler_test.rb
@@ -0,0 +1,306 @@
+# frozen_string_literal: true
+
+require "cases/helper"
+require "models/person"
+
+module ActiveRecord
+ module ConnectionAdapters
+ class ConnectionHandlerTest < ActiveRecord::TestCase
+ self.use_transactional_tests = false
+
+ fixtures :people
+
+ def setup
+ @handler = ConnectionHandler.new
+ @spec_name = "primary"
+ @pool = @handler.establish_connection(ActiveRecord::Base.configurations["arunit"])
+ end
+
+ def test_default_env_fall_back_to_default_env_when_rails_env_or_rack_env_is_empty_string
+ original_rails_env = ENV["RAILS_ENV"]
+ original_rack_env = ENV["RACK_ENV"]
+ ENV["RAILS_ENV"] = ENV["RACK_ENV"] = ""
+
+ assert_equal "default_env", ActiveRecord::ConnectionHandling::DEFAULT_ENV.call
+ ensure
+ ENV["RAILS_ENV"] = original_rails_env
+ ENV["RACK_ENV"] = original_rack_env
+ end
+
+ def test_establish_connection_uses_spec_name
+ config = { "readonly" => { "adapter" => "sqlite3" } }
+ resolver = ConnectionAdapters::ConnectionSpecification::Resolver.new(config)
+ spec = resolver.spec(:readonly)
+ @handler.establish_connection(spec.to_hash)
+
+ assert_not_nil @handler.retrieve_connection_pool("readonly")
+ ensure
+ @handler.remove_connection("readonly")
+ end
+
+ def test_establish_connection_using_3_levels_config
+ previous_env, ENV["RAILS_ENV"] = ENV["RAILS_ENV"], "default_env"
+
+ config = {
+ "default_env" => {
+ "readonly" => { "adapter" => "sqlite3", "database" => "db/readonly.sqlite3" },
+ "primary" => { "adapter" => "sqlite3", "database" => "db/primary.sqlite3" }
+ },
+ "another_env" => {
+ "readonly" => { "adapter" => "sqlite3", "database" => "db/bad-readonly.sqlite3" },
+ "primary" => { "adapter" => "sqlite3", "database" => "db/bad-primary.sqlite3" }
+ },
+ "common" => { "adapter" => "sqlite3", "database" => "db/common.sqlite3" }
+ }
+ @prev_configs, ActiveRecord::Base.configurations = ActiveRecord::Base.configurations, config
+
+ @handler.establish_connection(:common)
+ @handler.establish_connection(:primary)
+ @handler.establish_connection(:readonly)
+
+ assert_not_nil pool = @handler.retrieve_connection_pool("readonly")
+ assert_equal "db/readonly.sqlite3", pool.spec.config[:database]
+
+ assert_not_nil pool = @handler.retrieve_connection_pool("primary")
+ assert_equal "db/primary.sqlite3", pool.spec.config[:database]
+
+ assert_not_nil pool = @handler.retrieve_connection_pool("common")
+ assert_equal "db/common.sqlite3", pool.spec.config[:database]
+ ensure
+ ActiveRecord::Base.configurations = @prev_configs
+ ENV["RAILS_ENV"] = previous_env
+ end
+
+ def test_establish_connection_using_two_level_configurations
+ config = { "development" => { "adapter" => "sqlite3", "database" => "db/primary.sqlite3" } }
+ @prev_configs, ActiveRecord::Base.configurations = ActiveRecord::Base.configurations, config
+
+ @handler.establish_connection(:development)
+
+ assert_not_nil pool = @handler.retrieve_connection_pool("development")
+ assert_equal "db/primary.sqlite3", pool.spec.config[:database]
+ ensure
+ ActiveRecord::Base.configurations = @prev_configs
+ end
+
+ def test_establish_connection_using_top_level_key_in_two_level_config
+ config = {
+ "development" => { "adapter" => "sqlite3", "database" => "db/primary.sqlite3" },
+ "development_readonly" => { "adapter" => "sqlite3", "database" => "db/readonly.sqlite3" }
+ }
+ @prev_configs, ActiveRecord::Base.configurations = ActiveRecord::Base.configurations, config
+
+ @handler.establish_connection(:development_readonly)
+
+ assert_not_nil pool = @handler.retrieve_connection_pool("development_readonly")
+ assert_equal "db/readonly.sqlite3", pool.spec.config[:database]
+ ensure
+ ActiveRecord::Base.configurations = @prev_configs
+ end
+
+ def test_retrieve_connection
+ assert @handler.retrieve_connection(@spec_name)
+ end
+
+ def test_active_connections?
+ assert_not_predicate @handler, :active_connections?
+ assert @handler.retrieve_connection(@spec_name)
+ assert_predicate @handler, :active_connections?
+ @handler.clear_active_connections!
+ assert_not_predicate @handler, :active_connections?
+ end
+
+ def test_retrieve_connection_pool
+ assert_not_nil @handler.retrieve_connection_pool(@spec_name)
+ end
+
+ def test_retrieve_connection_pool_with_invalid_id
+ assert_nil @handler.retrieve_connection_pool("foo")
+ end
+
+ def test_connection_pools
+ assert_equal([@pool], @handler.connection_pools)
+ end
+
+ if Process.respond_to?(:fork)
+ def test_connection_pool_per_pid
+ object_id = ActiveRecord::Base.connection.object_id
+
+ rd, wr = IO.pipe
+ rd.binmode
+ wr.binmode
+
+ pid = fork {
+ rd.close
+ wr.write Marshal.dump ActiveRecord::Base.connection.object_id
+ wr.close
+ exit!
+ }
+
+ wr.close
+
+ Process.waitpid pid
+ assert_not_equal object_id, Marshal.load(rd.read)
+ rd.close
+ end
+
+ def test_forked_child_doesnt_mangle_parent_connection
+ object_id = ActiveRecord::Base.connection.object_id
+ assert_predicate ActiveRecord::Base.connection, :active?
+
+ rd, wr = IO.pipe
+ rd.binmode
+ wr.binmode
+
+ pid = fork {
+ rd.close
+ if ActiveRecord::Base.connection.active?
+ wr.write Marshal.dump ActiveRecord::Base.connection.object_id
+ end
+ wr.close
+
+ exit # allow finalizers to run
+ }
+
+ wr.close
+
+ Process.waitpid pid
+ assert_not_equal object_id, Marshal.load(rd.read)
+ rd.close
+
+ assert_equal 3, ActiveRecord::Base.connection.select_value("SELECT COUNT(*) FROM people")
+ end
+
+ unless in_memory_db?
+ def test_forked_child_recovers_from_disconnected_parent
+ object_id = ActiveRecord::Base.connection.object_id
+ assert_predicate ActiveRecord::Base.connection, :active?
+
+ rd, wr = IO.pipe
+ rd.binmode
+ wr.binmode
+
+ outer_pid = fork {
+ ActiveRecord::Base.connection.disconnect!
+
+ pid = fork {
+ rd.close
+ if ActiveRecord::Base.connection.active?
+ pair = [ActiveRecord::Base.connection.object_id,
+ ActiveRecord::Base.connection.select_value("SELECT COUNT(*) FROM people")]
+ wr.write Marshal.dump pair
+ end
+ wr.close
+
+ exit # allow finalizers to run
+ }
+
+ Process.waitpid pid
+ }
+
+ wr.close
+
+ Process.waitpid outer_pid
+ child_id, child_count = Marshal.load(rd.read)
+
+ assert_not_equal object_id, child_id
+ rd.close
+
+ assert_equal 3, child_count
+
+ # Outer connection is unaffected
+ assert_equal 6, ActiveRecord::Base.connection.select_value("SELECT 2 * COUNT(*) FROM people")
+ end
+ end
+
+ def test_retrieve_connection_pool_copies_schema_cache_from_ancestor_pool
+ @pool.schema_cache = @pool.connection.schema_cache
+ @pool.schema_cache.add("posts")
+
+ rd, wr = IO.pipe
+ rd.binmode
+ wr.binmode
+
+ pid = fork {
+ rd.close
+ pool = @handler.retrieve_connection_pool(@spec_name)
+ wr.write Marshal.dump pool.schema_cache.size
+ wr.close
+ exit!
+ }
+
+ wr.close
+
+ Process.waitpid pid
+ assert_equal @pool.schema_cache.size, Marshal.load(rd.read)
+ rd.close
+ end
+
+ def test_pool_from_any_process_for_uses_most_recent_spec
+ skip unless current_adapter?(:SQLite3Adapter)
+
+ file = Tempfile.new "lol.sqlite3"
+
+ rd, wr = IO.pipe
+ rd.binmode
+ wr.binmode
+
+ pid = fork do
+ ActiveRecord::Base.configurations["arunit"]["database"] = file.path
+ ActiveRecord::Base.establish_connection(:arunit)
+
+ pid2 = fork do
+ wr.write ActiveRecord::Base.connection_config[:database]
+ wr.close
+ end
+
+ Process.waitpid pid2
+ end
+
+ Process.waitpid pid
+
+ wr.close
+
+ assert_equal file.path, rd.read
+
+ rd.close
+ ensure
+ if file
+ file.close
+ file.unlink
+ end
+ end
+
+ def test_a_class_using_custom_pool_and_switching_back_to_primary
+ klass2 = Class.new(Base) { def self.name; "klass2"; end }
+
+ assert_same klass2.connection, ActiveRecord::Base.connection
+
+ pool = klass2.establish_connection(ActiveRecord::Base.connection_pool.spec.config)
+ assert_same klass2.connection, pool.connection
+ refute_same klass2.connection, ActiveRecord::Base.connection
+
+ klass2.remove_connection
+
+ assert_same klass2.connection, ActiveRecord::Base.connection
+ end
+
+ def test_connection_specification_name_should_fallback_to_parent
+ klassA = Class.new(Base)
+ klassB = Class.new(klassA)
+
+ assert_equal klassB.connection_specification_name, klassA.connection_specification_name
+ klassA.connection_specification_name = "readonly"
+ assert_equal "readonly", klassB.connection_specification_name
+ end
+
+ def test_remove_connection_should_not_remove_parent
+ klass2 = Class.new(Base) { def self.name; "klass2"; end }
+ klass2.remove_connection
+ refute_nil ActiveRecord::Base.connection
+ assert_same klass2.connection, ActiveRecord::Base.connection
+ end
+ end
+ end
+ end
+end
diff --git a/activerecord/test/cases/connection_adapters/connection_specification_test.rb b/activerecord/test/cases/connection_adapters/connection_specification_test.rb
new file mode 100644
index 0000000000..f81b73c344
--- /dev/null
+++ b/activerecord/test/cases/connection_adapters/connection_specification_test.rb
@@ -0,0 +1,14 @@
+# frozen_string_literal: true
+
+require "cases/helper"
+
+module ActiveRecord
+ module ConnectionAdapters
+ class ConnectionSpecificationTest < ActiveRecord::TestCase
+ def test_dup_deep_copy_config
+ spec = ConnectionSpecification.new("primary", { a: :b }, "bar")
+ assert_not_equal(spec.config.object_id, spec.dup.config.object_id)
+ end
+ end
+ end
+end
diff --git a/activerecord/test/cases/connection_adapters/merge_and_resolve_default_url_config_test.rb b/activerecord/test/cases/connection_adapters/merge_and_resolve_default_url_config_test.rb
new file mode 100644
index 0000000000..1b64324cc4
--- /dev/null
+++ b/activerecord/test/cases/connection_adapters/merge_and_resolve_default_url_config_test.rb
@@ -0,0 +1,257 @@
+# frozen_string_literal: true
+
+require "cases/helper"
+
+module ActiveRecord
+ module ConnectionAdapters
+ class MergeAndResolveDefaultUrlConfigTest < ActiveRecord::TestCase
+ def setup
+ @previous_database_url = ENV.delete("DATABASE_URL")
+ @previous_rack_env = ENV.delete("RACK_ENV")
+ @previous_rails_env = ENV.delete("RAILS_ENV")
+ end
+
+ teardown do
+ ENV["DATABASE_URL"] = @previous_database_url
+ ENV["RACK_ENV"] = @previous_rack_env
+ ENV["RAILS_ENV"] = @previous_rails_env
+ end
+
+ def resolve_config(config)
+ ActiveRecord::ConnectionHandling::MergeAndResolveDefaultUrlConfig.new(config).resolve
+ end
+
+ def resolve_spec(spec, config)
+ ConnectionSpecification::Resolver.new(resolve_config(config)).resolve(spec)
+ end
+
+ def test_resolver_with_database_uri_and_current_env_symbol_key
+ ENV["DATABASE_URL"] = "postgres://localhost/foo"
+ config = { "not_production" => { "adapter" => "not_postgres", "database" => "not_foo" } }
+ actual = resolve_spec(:default_env, config)
+ expected = { "adapter" => "postgresql", "database" => "foo", "host" => "localhost", "name" => "default_env" }
+ assert_equal expected, actual
+ end
+
+ def test_resolver_with_database_uri_and_current_env_symbol_key_and_rails_env
+ ENV["DATABASE_URL"] = "postgres://localhost/foo"
+ ENV["RAILS_ENV"] = "foo"
+
+ config = { "not_production" => { "adapter" => "not_postgres", "database" => "not_foo" } }
+ actual = resolve_spec(:foo, config)
+ expected = { "adapter" => "postgresql", "database" => "foo", "host" => "localhost", "name" => "foo" }
+ assert_equal expected, actual
+ end
+
+ def test_resolver_with_database_uri_and_current_env_symbol_key_and_rack_env
+ ENV["DATABASE_URL"] = "postgres://localhost/foo"
+ ENV["RACK_ENV"] = "foo"
+
+ config = { "not_production" => { "adapter" => "not_postgres", "database" => "not_foo" } }
+ actual = resolve_spec(:foo, config)
+ expected = { "adapter" => "postgresql", "database" => "foo", "host" => "localhost", "name" => "foo" }
+ assert_equal expected, actual
+ end
+
+ def test_resolver_with_database_uri_and_known_key
+ ENV["DATABASE_URL"] = "postgres://localhost/foo"
+ config = { "production" => { "adapter" => "not_postgres", "database" => "not_foo", "host" => "localhost" } }
+ actual = resolve_spec(:production, config)
+ expected = { "adapter" => "not_postgres", "database" => "not_foo", "host" => "localhost", "name" => "production" }
+ assert_equal expected, actual
+ end
+
+ def test_resolver_with_database_uri_and_unknown_symbol_key
+ ENV["DATABASE_URL"] = "postgres://localhost/foo"
+ config = { "not_production" => { "adapter" => "not_postgres", "database" => "not_foo" } }
+ assert_raises AdapterNotSpecified do
+ resolve_spec(:production, config)
+ end
+ end
+
+ def test_resolver_with_database_uri_and_supplied_url
+ ENV["DATABASE_URL"] = "not-postgres://not-localhost/not_foo"
+ config = { "production" => { "adapter" => "also_not_postgres", "database" => "also_not_foo" } }
+ actual = resolve_spec("postgres://localhost/foo", config)
+ expected = { "adapter" => "postgresql", "database" => "foo", "host" => "localhost" }
+ assert_equal expected, actual
+ end
+
+ def test_jdbc_url
+ config = { "production" => { "url" => "jdbc:postgres://localhost/foo" } }
+ actual = resolve_config(config)
+ assert_equal config, actual
+ end
+
+ def test_environment_does_not_exist_in_config_url_does_exist
+ ENV["DATABASE_URL"] = "postgres://localhost/foo"
+ config = { "not_default_env" => { "adapter" => "not_postgres", "database" => "not_foo" } }
+ actual = resolve_config(config)
+ expect_prod = { "adapter" => "postgresql", "database" => "foo", "host" => "localhost" }
+ assert_equal expect_prod, actual["default_env"]
+ end
+
+ def test_url_with_hyphenated_scheme
+ ENV["DATABASE_URL"] = "ibm-db://localhost/foo"
+ config = { "default_env" => { "adapter" => "not_postgres", "database" => "not_foo", "host" => "localhost" } }
+ actual = resolve_spec(:default_env, config)
+ expected = { "adapter" => "ibm_db", "database" => "foo", "host" => "localhost", "name" => "default_env" }
+ assert_equal expected, actual
+ end
+
+ def test_string_connection
+ config = { "default_env" => "postgres://localhost/foo" }
+ actual = resolve_config(config)
+ expected = { "default_env" =>
+ { "adapter" => "postgresql",
+ "database" => "foo",
+ "host" => "localhost"
+ }
+ }
+ assert_equal expected, actual
+ end
+
+ def test_url_sub_key
+ config = { "default_env" => { "url" => "postgres://localhost/foo" } }
+ actual = resolve_config(config)
+ expected = { "default_env" =>
+ { "adapter" => "postgresql",
+ "database" => "foo",
+ "host" => "localhost"
+ }
+ }
+ assert_equal expected, actual
+ end
+
+ def test_hash
+ config = { "production" => { "adapter" => "postgres", "database" => "foo" } }
+ actual = resolve_config(config)
+ assert_equal config, actual
+ end
+
+ def test_blank
+ config = {}
+ actual = resolve_config(config)
+ assert_equal config, actual
+ end
+
+ def test_blank_with_database_url
+ ENV["DATABASE_URL"] = "postgres://localhost/foo"
+
+ config = {}
+ actual = resolve_config(config)
+ expected = { "adapter" => "postgresql",
+ "database" => "foo",
+ "host" => "localhost" }
+ assert_equal expected, actual["default_env"]
+ assert_nil actual["production"]
+ assert_nil actual["development"]
+ assert_nil actual["test"]
+ assert_nil actual[:default_env]
+ assert_nil actual[:production]
+ assert_nil actual[:development]
+ assert_nil actual[:test]
+ end
+
+ def test_blank_with_database_url_with_rails_env
+ ENV["RAILS_ENV"] = "not_production"
+ ENV["DATABASE_URL"] = "postgres://localhost/foo"
+
+ config = {}
+ actual = resolve_config(config)
+ expected = { "adapter" => "postgresql",
+ "database" => "foo",
+ "host" => "localhost" }
+
+ assert_equal expected, actual["not_production"]
+ assert_nil actual["production"]
+ assert_nil actual["default_env"]
+ assert_nil actual["development"]
+ assert_nil actual["test"]
+ assert_nil actual[:default_env]
+ assert_nil actual[:not_production]
+ assert_nil actual[:production]
+ assert_nil actual[:development]
+ assert_nil actual[:test]
+ end
+
+ def test_blank_with_database_url_with_rack_env
+ ENV["RACK_ENV"] = "not_production"
+ ENV["DATABASE_URL"] = "postgres://localhost/foo"
+
+ config = {}
+ actual = resolve_config(config)
+ expected = { "adapter" => "postgresql",
+ "database" => "foo",
+ "host" => "localhost" }
+
+ assert_equal expected, actual["not_production"]
+ assert_nil actual["production"]
+ assert_nil actual["default_env"]
+ assert_nil actual["development"]
+ assert_nil actual["test"]
+ assert_nil actual[:default_env]
+ assert_nil actual[:not_production]
+ assert_nil actual[:production]
+ assert_nil actual[:development]
+ assert_nil actual[:test]
+ end
+
+ def test_database_url_with_ipv6_host_and_port
+ ENV["DATABASE_URL"] = "postgres://[::1]:5454/foo"
+
+ config = {}
+ actual = resolve_config(config)
+ expected = { "adapter" => "postgresql",
+ "database" => "foo",
+ "host" => "::1",
+ "port" => 5454 }
+ assert_equal expected, actual["default_env"]
+ end
+
+ def test_url_sub_key_with_database_url
+ ENV["DATABASE_URL"] = "NOT-POSTGRES://localhost/NOT_FOO"
+
+ config = { "default_env" => { "url" => "postgres://localhost/foo" } }
+ actual = resolve_config(config)
+ expected = { "default_env" =>
+ { "adapter" => "postgresql",
+ "database" => "foo",
+ "host" => "localhost"
+ }
+ }
+ assert_equal expected, actual
+ end
+
+ def test_merge_no_conflicts_with_database_url
+ ENV["DATABASE_URL"] = "postgres://localhost/foo"
+
+ config = { "default_env" => { "pool" => "5" } }
+ actual = resolve_config(config)
+ expected = { "default_env" =>
+ { "adapter" => "postgresql",
+ "database" => "foo",
+ "host" => "localhost",
+ "pool" => "5"
+ }
+ }
+ assert_equal expected, actual
+ end
+
+ def test_merge_conflicts_with_database_url
+ ENV["DATABASE_URL"] = "postgres://localhost/foo"
+
+ config = { "default_env" => { "adapter" => "NOT-POSTGRES", "database" => "NOT-FOO", "pool" => "5" } }
+ actual = resolve_config(config)
+ expected = { "default_env" =>
+ { "adapter" => "postgresql",
+ "database" => "foo",
+ "host" => "localhost",
+ "pool" => "5"
+ }
+ }
+ assert_equal expected, actual
+ end
+ end
+ end
+end
diff --git a/activerecord/test/cases/connection_adapters/mysql_type_lookup_test.rb b/activerecord/test/cases/connection_adapters/mysql_type_lookup_test.rb
new file mode 100644
index 0000000000..02e76ce146
--- /dev/null
+++ b/activerecord/test/cases/connection_adapters/mysql_type_lookup_test.rb
@@ -0,0 +1,78 @@
+# frozen_string_literal: true
+
+require "cases/helper"
+require "support/connection_helper"
+
+if current_adapter?(:Mysql2Adapter)
+ module ActiveRecord
+ module ConnectionAdapters
+ class MysqlTypeLookupTest < ActiveRecord::TestCase
+ include ConnectionHelper
+
+ setup do
+ @connection = ActiveRecord::Base.connection
+ end
+
+ def teardown
+ reset_connection
+ end
+
+ def test_boolean_types
+ emulate_booleans(true) do
+ assert_lookup_type :boolean, "tinyint(1)"
+ assert_lookup_type :boolean, "TINYINT(1)"
+ end
+ end
+
+ def test_string_types
+ assert_lookup_type :string, "enum('one', 'two', 'three')"
+ assert_lookup_type :string, "ENUM('one', 'two', 'three')"
+ assert_lookup_type :string, "set('one', 'two', 'three')"
+ assert_lookup_type :string, "SET('one', 'two', 'three')"
+ end
+
+ def test_set_type_with_value_matching_other_type
+ assert_lookup_type :string, "SET('unicode', '8bit', 'none', 'time')"
+ end
+
+ def test_enum_type_with_value_matching_other_type
+ assert_lookup_type :string, "ENUM('unicode', '8bit', 'none')"
+ end
+
+ def test_binary_types
+ assert_lookup_type :binary, "bit"
+ assert_lookup_type :binary, "BIT"
+ end
+
+ def test_integer_types
+ emulate_booleans(false) do
+ assert_lookup_type :integer, "tinyint(1)"
+ assert_lookup_type :integer, "TINYINT(1)"
+ assert_lookup_type :integer, "year"
+ assert_lookup_type :integer, "YEAR"
+ end
+ end
+
+ private
+
+ def assert_lookup_type(type, lookup)
+ cast_type = @connection.send(:type_map).lookup(lookup)
+ assert_equal type, cast_type.type
+ end
+
+ def emulate_booleans(value)
+ old_emulate_booleans = @connection.emulate_booleans
+ change_emulate_booleans(value)
+ yield
+ ensure
+ change_emulate_booleans(old_emulate_booleans)
+ end
+
+ def change_emulate_booleans(value)
+ @connection.emulate_booleans = value
+ @connection.clear_cache!
+ end
+ end
+ end
+ end
+end
diff --git a/activerecord/test/cases/connection_adapters/schema_cache_test.rb b/activerecord/test/cases/connection_adapters/schema_cache_test.rb
new file mode 100644
index 0000000000..67496381d1
--- /dev/null
+++ b/activerecord/test/cases/connection_adapters/schema_cache_test.rb
@@ -0,0 +1,101 @@
+# frozen_string_literal: true
+
+require "cases/helper"
+
+module ActiveRecord
+ module ConnectionAdapters
+ class SchemaCacheTest < ActiveRecord::TestCase
+ def setup
+ connection = ActiveRecord::Base.connection
+ @cache = SchemaCache.new connection
+ end
+
+ def test_primary_key
+ assert_equal "id", @cache.primary_keys("posts")
+ end
+
+ def test_yaml_dump_and_load
+ @cache.columns("posts")
+ @cache.columns_hash("posts")
+ @cache.data_sources("posts")
+ @cache.primary_keys("posts")
+
+ new_cache = YAML.load(YAML.dump(@cache))
+ assert_no_queries do
+ assert_equal 12, new_cache.columns("posts").size
+ assert_equal 12, new_cache.columns_hash("posts").size
+ assert new_cache.data_sources("posts")
+ assert_equal "id", new_cache.primary_keys("posts")
+ end
+ end
+
+ def test_yaml_loads_5_1_dump
+ body = File.open(schema_dump_path).read
+ cache = YAML.load(body)
+
+ assert_no_queries do
+ assert_equal 11, cache.columns("posts").size
+ assert_equal 11, cache.columns_hash("posts").size
+ assert cache.data_sources("posts")
+ assert_equal "id", cache.primary_keys("posts")
+ end
+ end
+
+ def test_primary_key_for_non_existent_table
+ assert_nil @cache.primary_keys("omgponies")
+ end
+
+ def test_caches_columns
+ columns = @cache.columns("posts")
+ assert_equal columns, @cache.columns("posts")
+ end
+
+ def test_caches_columns_hash
+ columns_hash = @cache.columns_hash("posts")
+ assert_equal columns_hash, @cache.columns_hash("posts")
+ end
+
+ def test_clearing
+ @cache.columns("posts")
+ @cache.columns_hash("posts")
+ @cache.data_sources("posts")
+ @cache.primary_keys("posts")
+
+ @cache.clear!
+
+ assert_equal 0, @cache.size
+ end
+
+ def test_dump_and_load
+ @cache.columns("posts")
+ @cache.columns_hash("posts")
+ @cache.data_sources("posts")
+ @cache.primary_keys("posts")
+
+ @cache = Marshal.load(Marshal.dump(@cache))
+
+ assert_no_queries do
+ assert_equal 12, @cache.columns("posts").size
+ assert_equal 12, @cache.columns_hash("posts").size
+ assert @cache.data_sources("posts")
+ assert_equal "id", @cache.primary_keys("posts")
+ end
+ end
+
+ def test_data_source_exist
+ assert @cache.data_source_exists?("posts")
+ assert_not @cache.data_source_exists?("foo")
+ end
+
+ def test_clear_data_source_cache
+ @cache.clear_data_source_cache!("posts")
+ end
+
+ private
+
+ def schema_dump_path
+ "test/assets/schema_dump_5_1.yml"
+ end
+ end
+ end
+end
diff --git a/activerecord/test/cases/connection_adapters/type_lookup_test.rb b/activerecord/test/cases/connection_adapters/type_lookup_test.rb
new file mode 100644
index 0000000000..1c79d776f0
--- /dev/null
+++ b/activerecord/test/cases/connection_adapters/type_lookup_test.rb
@@ -0,0 +1,120 @@
+# frozen_string_literal: true
+
+require "cases/helper"
+
+unless current_adapter?(:PostgreSQLAdapter) # PostgreSQL does not use type strings for lookup
+ module ActiveRecord
+ module ConnectionAdapters
+ class TypeLookupTest < ActiveRecord::TestCase
+ setup do
+ @connection = ActiveRecord::Base.connection
+ end
+
+ def test_boolean_types
+ assert_lookup_type :boolean, "boolean"
+ assert_lookup_type :boolean, "BOOLEAN"
+ end
+
+ def test_string_types
+ assert_lookup_type :string, "char"
+ assert_lookup_type :string, "varchar"
+ assert_lookup_type :string, "VARCHAR"
+ assert_lookup_type :string, "varchar(255)"
+ assert_lookup_type :string, "character varying"
+ end
+
+ def test_binary_types
+ assert_lookup_type :binary, "binary"
+ assert_lookup_type :binary, "BINARY"
+ assert_lookup_type :binary, "blob"
+ assert_lookup_type :binary, "BLOB"
+ end
+
+ def test_text_types
+ assert_lookup_type :text, "text"
+ assert_lookup_type :text, "TEXT"
+ assert_lookup_type :text, "clob"
+ assert_lookup_type :text, "CLOB"
+ end
+
+ def test_date_types
+ assert_lookup_type :date, "date"
+ assert_lookup_type :date, "DATE"
+ end
+
+ def test_time_types
+ assert_lookup_type :time, "time"
+ assert_lookup_type :time, "TIME"
+ end
+
+ def test_datetime_types
+ assert_lookup_type :datetime, "datetime"
+ assert_lookup_type :datetime, "DATETIME"
+ assert_lookup_type :datetime, "timestamp"
+ assert_lookup_type :datetime, "TIMESTAMP"
+ end
+
+ def test_decimal_types
+ assert_lookup_type :decimal, "decimal"
+ assert_lookup_type :decimal, "decimal(2,8)"
+ assert_lookup_type :decimal, "DECIMAL"
+ assert_lookup_type :decimal, "numeric"
+ assert_lookup_type :decimal, "numeric(2,8)"
+ assert_lookup_type :decimal, "NUMERIC"
+ assert_lookup_type :decimal, "number"
+ assert_lookup_type :decimal, "number(2,8)"
+ assert_lookup_type :decimal, "NUMBER"
+ end
+
+ def test_float_types
+ assert_lookup_type :float, "float"
+ assert_lookup_type :float, "FLOAT"
+ assert_lookup_type :float, "double"
+ assert_lookup_type :float, "DOUBLE"
+ end
+
+ def test_integer_types
+ assert_lookup_type :integer, "integer"
+ assert_lookup_type :integer, "INTEGER"
+ assert_lookup_type :integer, "tinyint"
+ assert_lookup_type :integer, "smallint"
+ assert_lookup_type :integer, "bigint"
+ end
+
+ def test_bigint_limit
+ limit = @connection.send(:type_map).lookup("bigint").send(:_limit)
+ if current_adapter?(:OracleAdapter)
+ assert_equal 19, limit
+ else
+ assert_equal 8, limit
+ end
+ end
+
+ def test_decimal_without_scale
+ if current_adapter?(:OracleAdapter)
+ {
+ decimal: %w{decimal(2) decimal(2,0) numeric(2) numeric(2,0)},
+ integer: %w{number(2) number(2,0)}
+ }
+ else
+ { decimal: %w{decimal(2) decimal(2,0) numeric(2) numeric(2,0) number(2) number(2,0)} }
+ end.each do |expected_type, types|
+ types.each do |type|
+ cast_type = @connection.send(:type_map).lookup(type)
+
+ assert_equal expected_type, cast_type.type
+ assert_equal 2, cast_type.cast(2.1)
+ end
+ end
+ end
+
+ private
+
+ def assert_lookup_type(type, lookup)
+ cast_type = @connection.send(:type_map).lookup(lookup)
+ assert_equal type, cast_type.type
+ end
+ end
+ end
+ end
+end