aboutsummaryrefslogtreecommitdiffstats
path: root/activerecord/test
diff options
context:
space:
mode:
Diffstat (limited to 'activerecord/test')
-rw-r--r--activerecord/test/active_record/connection_adapters/fake_adapter.rb2
-rw-r--r--activerecord/test/cases/adapter_test.rb114
-rw-r--r--activerecord/test/cases/adapters/mysql2/active_schema_test.rb12
-rw-r--r--activerecord/test/cases/adapters/mysql2/auto_increment_test.rb34
-rw-r--r--activerecord/test/cases/adapters/mysql2/bind_parameter_test.rb2
-rw-r--r--activerecord/test/cases/adapters/mysql2/boolean_test.rb2
-rw-r--r--activerecord/test/cases/adapters/mysql2/case_sensitivity_test.rb6
-rw-r--r--activerecord/test/cases/adapters/mysql2/charset_collation_test.rb2
-rw-r--r--activerecord/test/cases/adapters/mysql2/connection_test.rb30
-rw-r--r--activerecord/test/cases/adapters/mysql2/datetime_precision_quoting_test.rb2
-rw-r--r--activerecord/test/cases/adapters/mysql2/enum_test.rb6
-rw-r--r--activerecord/test/cases/adapters/mysql2/explain_test.rb24
-rw-r--r--activerecord/test/cases/adapters/mysql2/json_test.rb2
-rw-r--r--activerecord/test/cases/adapters/mysql2/mysql2_adapter_test.rb2
-rw-r--r--activerecord/test/cases/adapters/mysql2/schema_migrations_test.rb6
-rw-r--r--activerecord/test/cases/adapters/mysql2/schema_test.rb2
-rw-r--r--activerecord/test/cases/adapters/mysql2/sp_test.rb4
-rw-r--r--activerecord/test/cases/adapters/mysql2/sql_types_test.rb2
-rw-r--r--activerecord/test/cases/adapters/mysql2/table_options_test.rb85
-rw-r--r--activerecord/test/cases/adapters/mysql2/transaction_test.rb88
-rw-r--r--activerecord/test/cases/adapters/mysql2/unsigned_type_test.rb6
-rw-r--r--activerecord/test/cases/adapters/mysql2/virtual_column_test.rb4
-rw-r--r--activerecord/test/cases/adapters/postgresql/active_schema_test.rb11
-rw-r--r--activerecord/test/cases/adapters/postgresql/array_test.rb53
-rw-r--r--activerecord/test/cases/adapters/postgresql/bit_string_test.rb10
-rw-r--r--activerecord/test/cases/adapters/postgresql/bytea_test.rb10
-rw-r--r--activerecord/test/cases/adapters/postgresql/case_insensitive_test.rb2
-rw-r--r--activerecord/test/cases/adapters/postgresql/change_schema_test.rb4
-rw-r--r--activerecord/test/cases/adapters/postgresql/cidr_test.rb2
-rw-r--r--activerecord/test/cases/adapters/postgresql/citext_test.rb112
-rw-r--r--activerecord/test/cases/adapters/postgresql/collation_test.rb2
-rw-r--r--activerecord/test/cases/adapters/postgresql/composite_test.rb12
-rw-r--r--activerecord/test/cases/adapters/postgresql/connection_test.rb19
-rw-r--r--activerecord/test/cases/adapters/postgresql/datatype_test.rb2
-rw-r--r--activerecord/test/cases/adapters/postgresql/domain_test.rb8
-rw-r--r--activerecord/test/cases/adapters/postgresql/enum_test.rb8
-rw-r--r--activerecord/test/cases/adapters/postgresql/explain_test.rb18
-rw-r--r--activerecord/test/cases/adapters/postgresql/extension_migration_test.rb6
-rw-r--r--activerecord/test/cases/adapters/postgresql/foreign_table_test.rb109
-rw-r--r--activerecord/test/cases/adapters/postgresql/full_text_test.rb6
-rw-r--r--activerecord/test/cases/adapters/postgresql/geometric_test.rb14
-rw-r--r--activerecord/test/cases/adapters/postgresql/hstore_test.rb596
-rw-r--r--activerecord/test/cases/adapters/postgresql/infinity_test.rb2
-rw-r--r--activerecord/test/cases/adapters/postgresql/integer_test.rb2
-rw-r--r--activerecord/test/cases/adapters/postgresql/json_test.rb6
-rw-r--r--activerecord/test/cases/adapters/postgresql/ltree_test.rb6
-rw-r--r--activerecord/test/cases/adapters/postgresql/money_test.rb22
-rw-r--r--activerecord/test/cases/adapters/postgresql/network_test.rb14
-rw-r--r--activerecord/test/cases/adapters/postgresql/numbers_test.rb2
-rw-r--r--activerecord/test/cases/adapters/postgresql/postgresql_adapter_test.rb38
-rw-r--r--activerecord/test/cases/adapters/postgresql/prepared_statements_disabled_test.rb2
-rw-r--r--activerecord/test/cases/adapters/postgresql/quoting_test.rb6
-rw-r--r--activerecord/test/cases/adapters/postgresql/range_test.rb79
-rw-r--r--activerecord/test/cases/adapters/postgresql/referential_integrity_test.rb2
-rw-r--r--activerecord/test/cases/adapters/postgresql/rename_table_test.rb2
-rw-r--r--activerecord/test/cases/adapters/postgresql/schema_authorization_test.rb8
-rw-r--r--activerecord/test/cases/adapters/postgresql/schema_test.rb76
-rw-r--r--activerecord/test/cases/adapters/postgresql/serial_test.rb78
-rw-r--r--activerecord/test/cases/adapters/postgresql/statement_pool_test.rb4
-rw-r--r--activerecord/test/cases/adapters/postgresql/timestamp_test.rb2
-rw-r--r--activerecord/test/cases/adapters/postgresql/transaction_test.rb88
-rw-r--r--activerecord/test/cases/adapters/postgresql/type_lookup_test.rb16
-rw-r--r--activerecord/test/cases/adapters/postgresql/utils_test.rb2
-rw-r--r--activerecord/test/cases/adapters/postgresql/uuid_test.rb215
-rw-r--r--activerecord/test/cases/adapters/postgresql/xml_test.rb2
-rw-r--r--activerecord/test/cases/adapters/sqlite3/collation_test.rb2
-rw-r--r--activerecord/test/cases/adapters/sqlite3/copy_table_test.rb2
-rw-r--r--activerecord/test/cases/adapters/sqlite3/explain_test.rb24
-rw-r--r--activerecord/test/cases/adapters/sqlite3/json_test.rb29
-rw-r--r--activerecord/test/cases/adapters/sqlite3/quoting_test.rb17
-rw-r--r--activerecord/test/cases/adapters/sqlite3/sqlite3_adapter_test.rb142
-rw-r--r--activerecord/test/cases/adapters/sqlite3/sqlite3_create_folder_test.rb2
-rw-r--r--activerecord/test/cases/adapters/sqlite3/statement_pool_test.rb4
-rw-r--r--activerecord/test/cases/aggregations_test.rb4
-rw-r--r--activerecord/test/cases/ar_schema_test.rb6
-rw-r--r--activerecord/test/cases/associations/association_scope_test.rb16
-rw-r--r--activerecord/test/cases/associations/belongs_to_associations_test.rb67
-rw-r--r--activerecord/test/cases/associations/bidirectional_destroy_dependencies_test.rb2
-rw-r--r--activerecord/test/cases/associations/callbacks_test.rb18
-rw-r--r--activerecord/test/cases/associations/cascaded_eager_loading_test.rb6
-rw-r--r--activerecord/test/cases/associations/eager_load_includes_full_sti_class_test.rb29
-rw-r--r--activerecord/test/cases/associations/eager_load_nested_include_test.rb2
-rw-r--r--activerecord/test/cases/associations/eager_singularization_test.rb2
-rw-r--r--activerecord/test/cases/associations/eager_test.rb77
-rw-r--r--activerecord/test/cases/associations/extension_test.rb2
-rw-r--r--activerecord/test/cases/associations/has_and_belongs_to_many_associations_test.rb103
-rw-r--r--activerecord/test/cases/associations/has_many_associations_test.rb301
-rw-r--r--activerecord/test/cases/associations/has_many_through_associations_test.rb213
-rw-r--r--activerecord/test/cases/associations/has_one_associations_test.rb71
-rw-r--r--activerecord/test/cases/associations/has_one_through_associations_test.rb32
-rw-r--r--activerecord/test/cases/associations/inner_join_association_test.rb32
-rw-r--r--activerecord/test/cases/associations/inverse_associations_test.rb118
-rw-r--r--activerecord/test/cases/associations/join_model_test.rb58
-rw-r--r--activerecord/test/cases/associations/left_outer_join_association_test.rb12
-rw-r--r--activerecord/test/cases/associations/nested_through_associations_test.rb69
-rw-r--r--activerecord/test/cases/associations/required_test.rb2
-rw-r--r--activerecord/test/cases/associations_test.rb42
-rw-r--r--activerecord/test/cases/attribute_decorators_test.rb2
-rw-r--r--activerecord/test/cases/attribute_methods/read_test.rb5
-rw-r--r--activerecord/test/cases/attribute_methods_test.rb76
-rw-r--r--activerecord/test/cases/attribute_set_test.rb253
-rw-r--r--activerecord/test/cases/attribute_test.rb253
-rw-r--r--activerecord/test/cases/attributes_test.rb12
-rw-r--r--activerecord/test/cases/autosave_association_test.rb315
-rw-r--r--activerecord/test/cases/base_test.rb171
-rw-r--r--activerecord/test/cases/batches_test.rb102
-rw-r--r--activerecord/test/cases/binary_test.rb4
-rw-r--r--activerecord/test/cases/bind_parameter_test.rb9
-rw-r--r--activerecord/test/cases/cache_key_test.rb6
-rw-r--r--activerecord/test/cases/calculations_test.rb110
-rw-r--r--activerecord/test/cases/callbacks_test.rb12
-rw-r--r--activerecord/test/cases/clone_test.rb4
-rw-r--r--activerecord/test/cases/coders/json_test.rb2
-rw-r--r--activerecord/test/cases/coders/yaml_column_test.rb2
-rw-r--r--activerecord/test/cases/collection_cache_key_test.rb52
-rw-r--r--activerecord/test/cases/column_alias_test.rb2
-rw-r--r--activerecord/test/cases/column_definition_test.rb2
-rw-r--r--activerecord/test/cases/comment_test.rb32
-rw-r--r--activerecord/test/cases/connection_adapters/adapter_leasing_test.rb6
-rw-r--r--activerecord/test/cases/connection_adapters/connection_handler_test.rb94
-rw-r--r--activerecord/test/cases/connection_adapters/connection_specification_test.rb2
-rw-r--r--activerecord/test/cases/connection_adapters/merge_and_resolve_default_url_config_test.rb2
-rw-r--r--activerecord/test/cases/connection_adapters/mysql_type_lookup_test.rb11
-rw-r--r--activerecord/test/cases/connection_adapters/schema_cache_test.rb10
-rw-r--r--activerecord/test/cases/connection_adapters/type_lookup_test.rb12
-rw-r--r--activerecord/test/cases/connection_management_test.rb16
-rw-r--r--activerecord/test/cases/connection_pool_test.rb88
-rw-r--r--activerecord/test/cases/connection_specification/resolver_test.rb4
-rw-r--r--activerecord/test/cases/core_test.rb10
-rw-r--r--activerecord/test/cases/counter_cache_test.rb2
-rw-r--r--activerecord/test/cases/custom_locking_test.rb2
-rw-r--r--activerecord/test/cases/database_statements_test.rb2
-rw-r--r--activerecord/test/cases/date_test.rb2
-rw-r--r--activerecord/test/cases/date_time_precision_test.rb2
-rw-r--r--activerecord/test/cases/date_time_test.rb15
-rw-r--r--activerecord/test/cases/defaults_test.rb4
-rw-r--r--activerecord/test/cases/dirty_test.rb195
-rw-r--r--activerecord/test/cases/disconnected_test.rb2
-rw-r--r--activerecord/test/cases/dup_test.rb34
-rw-r--r--activerecord/test/cases/enum_test.rb176
-rw-r--r--activerecord/test/cases/errors_test.rb2
-rw-r--r--activerecord/test/cases/explain_subscriber_test.rb12
-rw-r--r--activerecord/test/cases/explain_test.rb8
-rw-r--r--activerecord/test/cases/finder_respond_to_test.rb14
-rw-r--r--activerecord/test/cases/finder_test.rb158
-rw-r--r--activerecord/test/cases/fixture_set/file_test.rb2
-rw-r--r--activerecord/test/cases/fixtures_test.rb165
-rw-r--r--activerecord/test/cases/forbidden_attributes_protection_test.rb2
-rw-r--r--activerecord/test/cases/habtm_destroy_order_test.rb8
-rw-r--r--activerecord/test/cases/helper.rb4
-rw-r--r--activerecord/test/cases/hot_compatibility_test.rb2
-rw-r--r--activerecord/test/cases/i18n_test.rb2
-rw-r--r--activerecord/test/cases/inheritance_test.rb77
-rw-r--r--activerecord/test/cases/instrumentation_test.rb72
-rw-r--r--activerecord/test/cases/integration_test.rb2
-rw-r--r--activerecord/test/cases/invalid_connection_test.rb2
-rw-r--r--activerecord/test/cases/invertible_migration_test.rb31
-rw-r--r--activerecord/test/cases/json_attribute_test.rb8
-rw-r--r--activerecord/test/cases/json_serialization_test.rb10
-rw-r--r--activerecord/test/cases/json_shared_test_cases.rb88
-rw-r--r--activerecord/test/cases/locking_test.rb93
-rw-r--r--activerecord/test/cases/log_subscriber_test.rb23
-rw-r--r--activerecord/test/cases/migration/change_schema_test.rb19
-rw-r--r--activerecord/test/cases/migration/change_table_test.rb2
-rw-r--r--activerecord/test/cases/migration/column_attributes_test.rb28
-rw-r--r--activerecord/test/cases/migration/column_positioning_test.rb12
-rw-r--r--activerecord/test/cases/migration/columns_test.rb20
-rw-r--r--activerecord/test/cases/migration/command_recorder_test.rb11
-rw-r--r--activerecord/test/cases/migration/compatibility_test.rb161
-rw-r--r--activerecord/test/cases/migration/create_join_table_test.rb4
-rw-r--r--activerecord/test/cases/migration/foreign_key_test.rb70
-rw-r--r--activerecord/test/cases/migration/helper.rb2
-rw-r--r--activerecord/test/cases/migration/index_test.rb8
-rw-r--r--activerecord/test/cases/migration/logger_test.rb2
-rw-r--r--activerecord/test/cases/migration/pending_migrations_test.rb52
-rw-r--r--activerecord/test/cases/migration/references_foreign_key_test.rb2
-rw-r--r--activerecord/test/cases/migration/references_index_test.rb2
-rw-r--r--activerecord/test/cases/migration/references_statements_test.rb2
-rw-r--r--activerecord/test/cases/migration/rename_table_test.rb6
-rw-r--r--activerecord/test/cases/migration_test.rb149
-rw-r--r--activerecord/test/cases/migrator_test.rb154
-rw-r--r--activerecord/test/cases/mixin_test.rb6
-rw-r--r--activerecord/test/cases/modules_test.rb2
-rw-r--r--activerecord/test/cases/multiparameter_attributes_test.rb64
-rw-r--r--activerecord/test/cases/multiple_db_test.rb11
-rw-r--r--activerecord/test/cases/nested_attributes_test.rb38
-rw-r--r--activerecord/test/cases/nested_attributes_with_callbacks_test.rb12
-rw-r--r--activerecord/test/cases/null_relation_test.rb84
-rw-r--r--activerecord/test/cases/numeric_data_test.rb2
-rw-r--r--activerecord/test/cases/persistence_test.rb342
-rw-r--r--activerecord/test/cases/pooled_connections_test.rb2
-rw-r--r--activerecord/test/cases/primary_keys_test.rb56
-rw-r--r--activerecord/test/cases/query_cache_test.rb77
-rw-r--r--activerecord/test/cases/quoting_test.rb59
-rw-r--r--activerecord/test/cases/readonly_test.rb46
-rw-r--r--activerecord/test/cases/reaper_test.rb12
-rw-r--r--activerecord/test/cases/reflection_test.rb151
-rw-r--r--activerecord/test/cases/relation/delegation_test.rb28
-rw-r--r--activerecord/test/cases/relation/merging_test.rb20
-rw-r--r--activerecord/test/cases/relation/mutation_test.rb51
-rw-r--r--activerecord/test/cases/relation/or_test.rb15
-rw-r--r--activerecord/test/cases/relation/predicate_builder_test.rb2
-rw-r--r--activerecord/test/cases/relation/record_fetch_warning_test.rb2
-rw-r--r--activerecord/test/cases/relation/where_chain_test.rb4
-rw-r--r--activerecord/test/cases/relation/where_clause_test.rb159
-rw-r--r--activerecord/test/cases/relation/where_test.rb39
-rw-r--r--activerecord/test/cases/relation_test.rb135
-rw-r--r--activerecord/test/cases/relations_test.rb518
-rw-r--r--activerecord/test/cases/reload_models_test.rb6
-rw-r--r--activerecord/test/cases/reserved_word_test.rb13
-rw-r--r--activerecord/test/cases/result_test.rb2
-rw-r--r--activerecord/test/cases/sanitize_test.rb81
-rw-r--r--activerecord/test/cases/schema_dumper_test.rb100
-rw-r--r--activerecord/test/cases/schema_loading_test.rb2
-rw-r--r--activerecord/test/cases/scoping/default_scoping_test.rb87
-rw-r--r--activerecord/test/cases/scoping/named_scoping_test.rb86
-rw-r--r--activerecord/test/cases/scoping/relation_scoping_test.rb45
-rw-r--r--activerecord/test/cases/secure_token_test.rb2
-rw-r--r--activerecord/test/cases/serialization_test.rb2
-rw-r--r--activerecord/test/cases/serialized_attribute_test.rb6
-rw-r--r--activerecord/test/cases/statement_cache_test.rb21
-rw-r--r--activerecord/test/cases/store_test.rb8
-rw-r--r--activerecord/test/cases/suppressor_test.rb2
-rw-r--r--activerecord/test/cases/tasks/database_tasks_test.rb279
-rw-r--r--activerecord/test/cases/tasks/mysql_rake_test.rb85
-rw-r--r--activerecord/test/cases/tasks/postgresql_rake_test.rb14
-rw-r--r--activerecord/test/cases/tasks/sqlite_rake_test.rb27
-rw-r--r--activerecord/test/cases/test_case.rb14
-rw-r--r--activerecord/test/cases/test_fixtures_test.rb2
-rw-r--r--activerecord/test/cases/time_precision_test.rb2
-rw-r--r--activerecord/test/cases/timestamp_test.rb48
-rw-r--r--activerecord/test/cases/touch_later_test.rb6
-rw-r--r--activerecord/test/cases/transaction_callbacks_test.rb10
-rw-r--r--activerecord/test/cases/transaction_isolation_test.rb6
-rw-r--r--activerecord/test/cases/transactions_test.rb248
-rw-r--r--activerecord/test/cases/type/adapter_specific_registry_test.rb2
-rw-r--r--activerecord/test/cases/type/date_time_test.rb2
-rw-r--r--activerecord/test/cases/type/integer_test.rb2
-rw-r--r--activerecord/test/cases/type/string_test.rb8
-rw-r--r--activerecord/test/cases/type/type_map_test.rb2
-rw-r--r--activerecord/test/cases/type/unsigned_integer_test.rb2
-rw-r--r--activerecord/test/cases/type_test.rb2
-rw-r--r--activerecord/test/cases/types_test.rb2
-rw-r--r--activerecord/test/cases/unconnected_test.rb2
-rw-r--r--activerecord/test/cases/unsafe_raw_sql_test.rb299
-rw-r--r--activerecord/test/cases/validations/absence_validation_test.rb12
-rw-r--r--activerecord/test/cases/validations/association_validation_test.rb26
-rw-r--r--activerecord/test/cases/validations/i18n_generate_message_validation_test.rb2
-rw-r--r--activerecord/test/cases/validations/i18n_validation_test.rb2
-rw-r--r--activerecord/test/cases/validations/length_validation_test.rb28
-rw-r--r--activerecord/test/cases/validations/presence_validation_test.rb20
-rw-r--r--activerecord/test/cases/validations/uniqueness_validation_test.rb49
-rw-r--r--activerecord/test/cases/validations_repair_helper.rb2
-rw-r--r--activerecord/test/cases/validations_test.rb16
-rw-r--r--activerecord/test/cases/view_test.rb2
-rw-r--r--activerecord/test/cases/yaml_serialization_test.rb14
-rw-r--r--activerecord/test/config.rb2
-rw-r--r--activerecord/test/fixtures/binaries.yml4
-rw-r--r--activerecord/test/fixtures/customers.yml11
-rw-r--r--activerecord/test/fixtures/minimalistics.yml3
-rw-r--r--activerecord/test/fixtures/other_posts.yml1
-rw-r--r--activerecord/test/fixtures/posts.yml8
-rw-r--r--activerecord/test/fixtures/reserved_words/values.yml4
-rw-r--r--activerecord/test/fixtures/teapots.yml3
-rw-r--r--activerecord/test/migrations/10_urban/9_add_expressions.rb2
-rw-r--r--activerecord/test/migrations/decimal/1_give_me_big_numbers.rb2
-rw-r--r--activerecord/test/migrations/empty/.keep (renamed from activerecord/test/migrations/empty/.gitkeep)0
-rw-r--r--activerecord/test/migrations/magic/1_currencies_have_symbols.rb1
-rw-r--r--activerecord/test/migrations/missing/1000_people_have_middle_names.rb2
-rw-r--r--activerecord/test/migrations/missing/1_people_have_last_names.rb2
-rw-r--r--activerecord/test/migrations/missing/3_we_need_reminders.rb2
-rw-r--r--activerecord/test/migrations/missing/4_innocent_jointable.rb2
-rw-r--r--activerecord/test/migrations/rename/1_we_need_things.rb2
-rw-r--r--activerecord/test/migrations/rename/2_rename_things.rb2
-rw-r--r--activerecord/test/migrations/to_copy/1_people_have_hobbies.rb2
-rw-r--r--activerecord/test/migrations/to_copy/2_people_have_descriptions.rb2
-rw-r--r--activerecord/test/migrations/to_copy2/1_create_articles.rb2
-rw-r--r--activerecord/test/migrations/to_copy2/2_create_comments.rb2
-rw-r--r--activerecord/test/migrations/to_copy_with_name_collision/1_people_have_hobbies.rb2
-rw-r--r--activerecord/test/migrations/to_copy_with_timestamps/20090101010101_people_have_hobbies.rb2
-rw-r--r--activerecord/test/migrations/to_copy_with_timestamps/20090101010202_people_have_descriptions.rb2
-rw-r--r--activerecord/test/migrations/to_copy_with_timestamps2/20090101010101_create_articles.rb2
-rw-r--r--activerecord/test/migrations/to_copy_with_timestamps2/20090101010202_create_comments.rb2
-rw-r--r--activerecord/test/migrations/valid/1_valid_people_have_last_names.rb2
-rw-r--r--activerecord/test/migrations/valid/2_we_need_reminders.rb2
-rw-r--r--activerecord/test/migrations/valid/3_innocent_jointable.rb2
-rw-r--r--activerecord/test/migrations/valid_with_subdirectories/1_valid_people_have_last_names.rb2
-rw-r--r--activerecord/test/migrations/valid_with_subdirectories/sub/2_we_need_reminders.rb2
-rw-r--r--activerecord/test/migrations/valid_with_subdirectories/sub1/3_innocent_jointable.rb2
-rw-r--r--activerecord/test/migrations/valid_with_timestamps/20100101010101_valid_with_timestamps_people_have_last_names.rb2
-rw-r--r--activerecord/test/migrations/valid_with_timestamps/20100201010101_valid_with_timestamps_we_need_reminders.rb2
-rw-r--r--activerecord/test/migrations/valid_with_timestamps/20100301010101_valid_with_timestamps_innocent_jointable.rb2
-rw-r--r--activerecord/test/migrations/version_check/20131219224947_migration_version_check.rb2
-rw-r--r--activerecord/test/models/account.rb34
-rw-r--r--activerecord/test/models/admin.rb2
-rw-r--r--activerecord/test/models/admin/account.rb2
-rw-r--r--activerecord/test/models/admin/randomly_named_c1.rb16
-rw-r--r--activerecord/test/models/admin/user.rb2
-rw-r--r--activerecord/test/models/aircraft.rb2
-rw-r--r--activerecord/test/models/arunit2_model.rb2
-rw-r--r--activerecord/test/models/author.rb13
-rw-r--r--activerecord/test/models/auto_id.rb2
-rw-r--r--activerecord/test/models/autoloadable/extra_firm.rb2
-rw-r--r--activerecord/test/models/binary.rb2
-rw-r--r--activerecord/test/models/bird.rb2
-rw-r--r--activerecord/test/models/book.rb2
-rw-r--r--activerecord/test/models/boolean.rb2
-rw-r--r--activerecord/test/models/bulb.rb2
-rw-r--r--activerecord/test/models/cake_designer.rb2
-rw-r--r--activerecord/test/models/car.rb2
-rw-r--r--activerecord/test/models/carrier.rb2
-rw-r--r--activerecord/test/models/cat.rb2
-rw-r--r--activerecord/test/models/categorization.rb2
-rw-r--r--activerecord/test/models/category.rb2
-rw-r--r--activerecord/test/models/chef.rb2
-rw-r--r--activerecord/test/models/citation.rb2
-rw-r--r--activerecord/test/models/club.rb4
-rw-r--r--activerecord/test/models/college.rb2
-rw-r--r--activerecord/test/models/column.rb2
-rw-r--r--activerecord/test/models/column_name.rb2
-rw-r--r--activerecord/test/models/comment.rb16
-rw-r--r--activerecord/test/models/company.rb39
-rw-r--r--activerecord/test/models/company_in_module.rb2
-rw-r--r--activerecord/test/models/computer.rb2
-rw-r--r--activerecord/test/models/contact.rb2
-rw-r--r--activerecord/test/models/content.rb2
-rw-r--r--activerecord/test/models/contract.rb4
-rw-r--r--activerecord/test/models/country.rb2
-rw-r--r--activerecord/test/models/course.rb2
-rw-r--r--activerecord/test/models/customer.rb4
-rw-r--r--activerecord/test/models/customer_carrier.rb2
-rw-r--r--activerecord/test/models/dashboard.rb2
-rw-r--r--activerecord/test/models/default.rb2
-rw-r--r--activerecord/test/models/department.rb2
-rw-r--r--activerecord/test/models/developer.rb13
-rw-r--r--activerecord/test/models/dog.rb2
-rw-r--r--activerecord/test/models/dog_lover.rb2
-rw-r--r--activerecord/test/models/doubloon.rb2
-rw-r--r--activerecord/test/models/drink_designer.rb5
-rw-r--r--activerecord/test/models/edge.rb2
-rw-r--r--activerecord/test/models/electron.rb2
-rw-r--r--activerecord/test/models/engine.rb2
-rw-r--r--activerecord/test/models/entrant.rb2
-rw-r--r--activerecord/test/models/essay.rb2
-rw-r--r--activerecord/test/models/event.rb2
-rw-r--r--activerecord/test/models/eye.rb2
-rw-r--r--activerecord/test/models/face.rb6
-rw-r--r--activerecord/test/models/family.rb2
-rw-r--r--activerecord/test/models/family_tree.rb2
-rw-r--r--activerecord/test/models/friendship.rb2
-rw-r--r--activerecord/test/models/guid.rb2
-rw-r--r--activerecord/test/models/guitar.rb2
-rw-r--r--activerecord/test/models/hotel.rb2
-rw-r--r--activerecord/test/models/image.rb2
-rw-r--r--activerecord/test/models/interest.rb2
-rw-r--r--activerecord/test/models/invoice.rb2
-rw-r--r--activerecord/test/models/item.rb2
-rw-r--r--activerecord/test/models/job.rb2
-rw-r--r--activerecord/test/models/joke.rb2
-rw-r--r--activerecord/test/models/keyboard.rb2
-rw-r--r--activerecord/test/models/legacy_thing.rb2
-rw-r--r--activerecord/test/models/lesson.rb2
-rw-r--r--activerecord/test/models/line_item.rb2
-rw-r--r--activerecord/test/models/liquid.rb2
-rw-r--r--activerecord/test/models/man.rb2
-rw-r--r--activerecord/test/models/matey.rb2
-rw-r--r--activerecord/test/models/member.rb3
-rw-r--r--activerecord/test/models/member_detail.rb2
-rw-r--r--activerecord/test/models/member_type.rb2
-rw-r--r--activerecord/test/models/membership.rb3
-rw-r--r--activerecord/test/models/mentor.rb2
-rw-r--r--activerecord/test/models/minimalistic.rb2
-rw-r--r--activerecord/test/models/minivan.rb2
-rw-r--r--activerecord/test/models/mixed_case_monkey.rb2
-rw-r--r--activerecord/test/models/mocktail_designer.rb2
-rw-r--r--activerecord/test/models/molecule.rb2
-rw-r--r--activerecord/test/models/movie.rb2
-rw-r--r--activerecord/test/models/node.rb2
-rw-r--r--activerecord/test/models/non_primary_key.rb2
-rw-r--r--activerecord/test/models/notification.rb2
-rw-r--r--activerecord/test/models/numeric_data.rb2
-rw-r--r--activerecord/test/models/order.rb2
-rw-r--r--activerecord/test/models/organization.rb2
-rw-r--r--activerecord/test/models/other_dog.rb2
-rw-r--r--activerecord/test/models/owner.rb2
-rw-r--r--activerecord/test/models/parrot.rb4
-rw-r--r--activerecord/test/models/person.rb2
-rw-r--r--activerecord/test/models/personal_legacy_thing.rb2
-rw-r--r--activerecord/test/models/pet.rb2
-rw-r--r--activerecord/test/models/pet_treasure.rb2
-rw-r--r--activerecord/test/models/pirate.rb2
-rw-r--r--activerecord/test/models/possession.rb2
-rw-r--r--activerecord/test/models/post.rb65
-rw-r--r--activerecord/test/models/price_estimate.rb2
-rw-r--r--activerecord/test/models/professor.rb2
-rw-r--r--activerecord/test/models/project.rb2
-rw-r--r--activerecord/test/models/publisher.rb2
-rw-r--r--activerecord/test/models/publisher/article.rb2
-rw-r--r--activerecord/test/models/publisher/magazine.rb2
-rw-r--r--activerecord/test/models/randomly_named_c1.rb8
-rw-r--r--activerecord/test/models/rating.rb2
-rw-r--r--activerecord/test/models/reader.rb2
-rw-r--r--activerecord/test/models/recipe.rb2
-rw-r--r--activerecord/test/models/record.rb2
-rw-r--r--activerecord/test/models/reference.rb2
-rw-r--r--activerecord/test/models/reply.rb2
-rw-r--r--activerecord/test/models/ship.rb2
-rw-r--r--activerecord/test/models/ship_part.rb2
-rw-r--r--activerecord/test/models/shop.rb2
-rw-r--r--activerecord/test/models/shop_account.rb2
-rw-r--r--activerecord/test/models/speedometer.rb2
-rw-r--r--activerecord/test/models/sponsor.rb2
-rw-r--r--activerecord/test/models/string_key_object.rb2
-rw-r--r--activerecord/test/models/student.rb2
-rw-r--r--activerecord/test/models/subject.rb14
-rw-r--r--activerecord/test/models/subscriber.rb2
-rw-r--r--activerecord/test/models/subscription.rb2
-rw-r--r--activerecord/test/models/tag.rb5
-rw-r--r--activerecord/test/models/tagging.rb7
-rw-r--r--activerecord/test/models/task.rb2
-rw-r--r--activerecord/test/models/topic.rb9
-rw-r--r--activerecord/test/models/toy.rb2
-rw-r--r--activerecord/test/models/traffic_light.rb2
-rw-r--r--activerecord/test/models/treasure.rb2
-rw-r--r--activerecord/test/models/treaty.rb2
-rw-r--r--activerecord/test/models/tree.rb2
-rw-r--r--activerecord/test/models/tuning_peg.rb2
-rw-r--r--activerecord/test/models/tyre.rb2
-rw-r--r--activerecord/test/models/user.rb2
-rw-r--r--activerecord/test/models/uuid_child.rb2
-rw-r--r--activerecord/test/models/uuid_item.rb2
-rw-r--r--activerecord/test/models/uuid_parent.rb2
-rw-r--r--activerecord/test/models/vegetables.rb2
-rw-r--r--activerecord/test/models/vehicle.rb2
-rw-r--r--activerecord/test/models/vertex.rb2
-rw-r--r--activerecord/test/models/warehouse_thing.rb2
-rw-r--r--activerecord/test/models/wheel.rb4
-rw-r--r--activerecord/test/models/without_table.rb2
-rw-r--r--activerecord/test/models/zine.rb2
-rw-r--r--activerecord/test/schema/mysql2_specific_schema.rb2
-rw-r--r--activerecord/test/schema/oracle_specific_schema.rb2
-rw-r--r--activerecord/test/schema/postgresql_specific_schema.rb2
-rw-r--r--activerecord/test/schema/schema.rb35
-rw-r--r--activerecord/test/support/config.rb2
-rw-r--r--activerecord/test/support/connection.rb2
-rw-r--r--activerecord/test/support/connection_helper.rb2
-rw-r--r--activerecord/test/support/ddl_helper.rb2
-rw-r--r--activerecord/test/support/schema_dumping_helper.rb2
448 files changed, 7746 insertions, 3788 deletions
diff --git a/activerecord/test/active_record/connection_adapters/fake_adapter.rb b/activerecord/test/active_record/connection_adapters/fake_adapter.rb
index b0d8050721..f977b2997b 100644
--- a/activerecord/test/active_record/connection_adapters/fake_adapter.rb
+++ b/activerecord/test/active_record/connection_adapters/fake_adapter.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module ActiveRecord
module ConnectionHandling
def fake_connection(config)
diff --git a/activerecord/test/cases/adapter_test.rb b/activerecord/test/cases/adapter_test.rb
index a1fb6427f9..0092146e09 100644
--- a/activerecord/test/cases/adapter_test.rb
+++ b/activerecord/test/cases/adapter_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "cases/helper"
require "models/book"
require "models/post"
@@ -76,7 +78,7 @@ module ActiveRecord
idx_name = "accounts_idx"
indexes = @connection.indexes("accounts")
- assert indexes.empty?
+ assert_empty indexes
@connection.add_index :accounts, :firm_id, name: idx_name
indexes = @connection.indexes("accounts")
@@ -156,26 +158,6 @@ module ActiveRecord
end
end
- # test resetting sequences in odd tables in PostgreSQL
- if ActiveRecord::Base.connection.respond_to?(:reset_pk_sequence!)
- require "models/movie"
- require "models/subscriber"
-
- def test_reset_empty_table_with_custom_pk
- Movie.delete_all
- Movie.connection.reset_pk_sequence! "movies"
- assert_equal 1, Movie.create(name: "fight club").id
- end
-
- def test_reset_table_with_non_integer_pk
- Subscriber.delete_all
- Subscriber.connection.reset_pk_sequence! "subscribers"
- sub = Subscriber.new(name: "robert drake")
- sub.id = "bob drake"
- assert_nothing_raised { sub.save! }
- end
- end
-
def test_uniqueness_violations_are_translated_to_specific_exception
@connection.execute "INSERT INTO subscribers(nick) VALUES('me')"
error = assert_raises(ActiveRecord::RecordNotUnique) do
@@ -211,6 +193,28 @@ module ActiveRecord
end
end
+ def test_exceptions_from_notifications_are_not_translated
+ original_error = StandardError.new("This StandardError shouldn't get translated")
+ subscriber = ActiveSupport::Notifications.subscribe("sql.active_record") { raise original_error }
+ actual_error = assert_raises(StandardError) do
+ @connection.execute("SELECT * FROM posts")
+ end
+
+ assert_equal original_error, actual_error
+
+ ensure
+ ActiveSupport::Notifications.unsubscribe(subscriber) if subscriber
+ end
+
+ def test_database_related_exceptions_are_translated_to_statement_invalid
+ error = assert_raises(ActiveRecord::StatementInvalid) do
+ @connection.execute("This is a syntax error")
+ end
+
+ assert_instance_of ActiveRecord::StatementInvalid, error
+ assert_kind_of Exception, error.cause
+ end
+
def test_select_all_always_return_activerecord_result
result = @connection.select_all "SELECT * FROM posts"
assert result.is_a?(ActiveRecord::Result)
@@ -220,16 +224,47 @@ module ActiveRecord
def test_select_all_with_legacy_binds
post = Post.create!(title: "foo", body: "bar")
expected = @connection.select_all("SELECT * FROM posts WHERE id = #{post.id}")
- result = @connection.select_all("SELECT * FROM posts WHERE id = #{bind_param.to_sql}", nil, [[nil, post.id]])
+ result = @connection.select_all("SELECT * FROM posts WHERE id = #{Arel::Nodes::BindParam.new(nil).to_sql}", nil, [[nil, post.id]])
assert_equal expected.to_hash, result.to_hash
end
+
+ def test_insert_update_delete_with_legacy_binds
+ binds = [[nil, 1]]
+ bind_param = Arel::Nodes::BindParam.new(nil)
+
+ id = @connection.insert("INSERT INTO events(id) VALUES (#{bind_param.to_sql})", nil, nil, nil, nil, binds)
+ assert_equal 1, id
+
+ @connection.update("UPDATE events SET title = 'foo' WHERE id = #{bind_param.to_sql}", nil, binds)
+ result = @connection.select_all("SELECT * FROM events WHERE id = #{bind_param.to_sql}", nil, binds)
+ assert_equal({ "id" => 1, "title" => "foo" }, result.first)
+
+ @connection.delete("DELETE FROM events WHERE id = #{bind_param.to_sql}", nil, binds)
+ result = @connection.select_all("SELECT * FROM events WHERE id = #{bind_param.to_sql}", nil, binds)
+ assert_nil result.first
+ end
+
+ def test_insert_update_delete_with_binds
+ binds = [Relation::QueryAttribute.new("id", 1, Type.default_value)]
+ bind_param = Arel::Nodes::BindParam.new(nil)
+
+ id = @connection.insert("INSERT INTO events(id) VALUES (#{bind_param.to_sql})", nil, nil, nil, nil, binds)
+ assert_equal 1, id
+
+ @connection.update("UPDATE events SET title = 'foo' WHERE id = #{bind_param.to_sql}", nil, binds)
+ result = @connection.select_all("SELECT * FROM events WHERE id = #{bind_param.to_sql}", nil, binds)
+ assert_equal({ "id" => 1, "title" => "foo" }, result.first)
+
+ @connection.delete("DELETE FROM events WHERE id = #{bind_param.to_sql}", nil, binds)
+ result = @connection.select_all("SELECT * FROM events WHERE id = #{bind_param.to_sql}", nil, binds)
+ assert_nil result.first
+ end
end
def test_select_methods_passing_a_association_relation
author = Author.create!(name: "john")
Post.create!(author: author, title: "foo", body: "bar")
query = author.posts.where(title: "foo").select(:title)
- assert_equal({ "title" => "foo" }, @connection.select_one(query.arel, nil, query.bound_attributes))
assert_equal({ "title" => "foo" }, @connection.select_one(query))
assert @connection.select_all(query).is_a?(ActiveRecord::Result)
assert_equal "foo", @connection.select_value(query)
@@ -239,7 +274,6 @@ module ActiveRecord
def test_select_methods_passing_a_relation
Post.create!(title: "foo", body: "bar")
query = Post.where(title: "foo").select(:title)
- assert_equal({ "title" => "foo" }, @connection.select_one(query.arel, nil, query.bound_attributes))
assert_equal({ "title" => "foo" }, @connection.select_one(query))
assert @connection.select_all(query).is_a?(ActiveRecord::Result)
assert_equal "foo", @connection.select_value(query)
@@ -254,11 +288,11 @@ module ActiveRecord
def test_log_invalid_encoding
error = assert_raises RuntimeError do
@connection.send :log, "SELECT 'ы' FROM DUAL" do
- raise "ы".force_encoding(Encoding::ASCII_8BIT)
+ raise "ы".dup.force_encoding(Encoding::ASCII_8BIT)
end
end
- assert_not_nil error.message
+ assert_equal "ы", error.message
end
end
end
@@ -334,16 +368,36 @@ module ActiveRecord
unless in_memory_db?
test "transaction state is reset after a reconnect" do
@connection.begin_transaction
- assert @connection.transaction_open?
+ assert_predicate @connection, :transaction_open?
@connection.reconnect!
- assert !@connection.transaction_open?
+ assert_not_predicate @connection, :transaction_open?
end
test "transaction state is reset after a disconnect" do
@connection.begin_transaction
- assert @connection.transaction_open?
+ assert_predicate @connection, :transaction_open?
@connection.disconnect!
- assert !@connection.transaction_open?
+ assert_not_predicate @connection, :transaction_open?
+ end
+ end
+
+ # test resetting sequences in odd tables in PostgreSQL
+ if ActiveRecord::Base.connection.respond_to?(:reset_pk_sequence!)
+ require "models/movie"
+ require "models/subscriber"
+
+ def test_reset_empty_table_with_custom_pk
+ Movie.delete_all
+ Movie.connection.reset_pk_sequence! "movies"
+ assert_equal 1, Movie.create(name: "fight club").id
+ end
+
+ def test_reset_table_with_non_integer_pk
+ Subscriber.delete_all
+ Subscriber.connection.reset_pk_sequence! "subscribers"
+ sub = Subscriber.new(name: "robert drake")
+ sub.id = "bob drake"
+ assert_nothing_raised { sub.save! }
end
end
end
diff --git a/activerecord/test/cases/adapters/mysql2/active_schema_test.rb b/activerecord/test/cases/adapters/mysql2/active_schema_test.rb
index 67e1efde27..9ae2c42368 100644
--- a/activerecord/test/cases/adapters/mysql2/active_schema_test.rb
+++ b/activerecord/test/cases/adapters/mysql2/active_schema_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "cases/helper"
require "support/connection_helper"
@@ -7,7 +9,7 @@ class Mysql2ActiveSchemaTest < ActiveRecord::Mysql2TestCase
def setup
ActiveRecord::Base.connection.singleton_class.class_eval do
alias_method :execute_without_stub, :execute
- def execute(sql, name = nil) return sql end
+ def execute(sql, name = nil) sql end
end
end
@@ -66,14 +68,14 @@ class Mysql2ActiveSchemaTest < ActiveRecord::Mysql2TestCase
def (ActiveRecord::Base.connection).data_source_exists?(*); false; end
%w(SPATIAL FULLTEXT UNIQUE).each do |type|
- expected = "CREATE TABLE `people` (#{type} INDEX `index_people_on_last_name` (`last_name`)) ENGINE=InnoDB"
+ expected = "CREATE TABLE `people` (#{type} INDEX `index_people_on_last_name` (`last_name`))"
actual = ActiveRecord::Base.connection.create_table(:people, id: false) do |t|
t.index :last_name, type: type
end
assert_equal expected, actual
end
- expected = "CREATE TABLE `people` ( INDEX `index_people_on_last_name` USING btree (`last_name`(10))) ENGINE=InnoDB"
+ expected = "CREATE TABLE `people` ( INDEX `index_people_on_last_name` USING btree (`last_name`(10)))"
actual = ActiveRecord::Base.connection.create_table(:people, id: false) do |t|
t.index :last_name, length: 10, using: :btree
end
@@ -106,7 +108,7 @@ class Mysql2ActiveSchemaTest < ActiveRecord::Mysql2TestCase
def test_create_mysql_database_with_encoding
assert_equal "CREATE DATABASE `matt` DEFAULT CHARACTER SET `utf8`", create_database(:matt)
assert_equal "CREATE DATABASE `aimonetti` DEFAULT CHARACTER SET `latin1`", create_database(:aimonetti, charset: "latin1")
- assert_equal "CREATE DATABASE `matt_aimonetti` DEFAULT CHARACTER SET `big5` COLLATE `big5_chinese_ci`", create_database(:matt_aimonetti, charset: :big5, collation: :big5_chinese_ci)
+ assert_equal "CREATE DATABASE `matt_aimonetti` DEFAULT COLLATE `utf8mb4_bin`", create_database(:matt_aimonetti, collation: "utf8mb4_bin")
end
def test_recreate_mysql_database_with_encoding
@@ -158,7 +160,7 @@ class Mysql2ActiveSchemaTest < ActiveRecord::Mysql2TestCase
ActiveRecord::Base.connection.stubs(:data_source_exists?).with(:temp).returns(false)
ActiveRecord::Base.connection.stubs(:index_name_exists?).with(:index_temp_on_zip).returns(false)
- expected = "CREATE TEMPORARY TABLE `temp` ( INDEX `index_temp_on_zip` (`zip`)) ENGINE=InnoDB AS SELECT id, name, zip FROM a_really_complicated_query"
+ expected = "CREATE TEMPORARY TABLE `temp` ( INDEX `index_temp_on_zip` (`zip`)) AS SELECT id, name, zip FROM a_really_complicated_query"
actual = ActiveRecord::Base.connection.create_table(:temp, temporary: true, as: "SELECT id, name, zip FROM a_really_complicated_query") do |t|
t.index :zip
end
diff --git a/activerecord/test/cases/adapters/mysql2/auto_increment_test.rb b/activerecord/test/cases/adapters/mysql2/auto_increment_test.rb
new file mode 100644
index 0000000000..4c67633946
--- /dev/null
+++ b/activerecord/test/cases/adapters/mysql2/auto_increment_test.rb
@@ -0,0 +1,34 @@
+# frozen_string_literal: true
+
+require "cases/helper"
+require "support/schema_dumping_helper"
+
+class Mysql2AutoIncrementTest < ActiveRecord::Mysql2TestCase
+ include SchemaDumpingHelper
+
+ def setup
+ @connection = ActiveRecord::Base.connection
+ end
+
+ def teardown
+ @connection.drop_table :auto_increments, if_exists: true
+ end
+
+ def test_auto_increment_without_primary_key
+ @connection.create_table :auto_increments, id: false, force: true do |t|
+ t.integer :id, null: false, auto_increment: true
+ t.index :id
+ end
+ output = dump_table_schema("auto_increments")
+ assert_match(/t\.integer\s+"id",\s+null: false,\s+auto_increment: true$/, output)
+ end
+
+ def test_auto_increment_with_composite_primary_key
+ @connection.create_table :auto_increments, primary_key: [:id, :created_at], force: true do |t|
+ t.integer :id, null: false, auto_increment: true
+ t.datetime :created_at, null: false
+ end
+ output = dump_table_schema("auto_increments")
+ assert_match(/t\.integer\s+"id",\s+null: false,\s+auto_increment: true$/, output)
+ end
+end
diff --git a/activerecord/test/cases/adapters/mysql2/bind_parameter_test.rb b/activerecord/test/cases/adapters/mysql2/bind_parameter_test.rb
index 8f7c803a21..825bddfb73 100644
--- a/activerecord/test/cases/adapters/mysql2/bind_parameter_test.rb
+++ b/activerecord/test/cases/adapters/mysql2/bind_parameter_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "cases/helper"
require "models/topic"
diff --git a/activerecord/test/cases/adapters/mysql2/boolean_test.rb b/activerecord/test/cases/adapters/mysql2/boolean_test.rb
index 58698d59db..db09b30361 100644
--- a/activerecord/test/cases/adapters/mysql2/boolean_test.rb
+++ b/activerecord/test/cases/adapters/mysql2/boolean_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "cases/helper"
class Mysql2BooleanTest < ActiveRecord::Mysql2TestCase
diff --git a/activerecord/test/cases/adapters/mysql2/case_sensitivity_test.rb b/activerecord/test/cases/adapters/mysql2/case_sensitivity_test.rb
index 50ba9ab831..aa870349be 100644
--- a/activerecord/test/cases/adapters/mysql2/case_sensitivity_test.rb
+++ b/activerecord/test/cases/adapters/mysql2/case_sensitivity_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "cases/helper"
class Mysql2CaseSensitivityTest < ActiveRecord::Mysql2TestCase
@@ -12,8 +14,8 @@ class Mysql2CaseSensitivityTest < ActiveRecord::Mysql2TestCase
end
def test_case_sensitive
- assert !CollationTest.columns_hash["string_ci_column"].case_sensitive?
- assert CollationTest.columns_hash["string_cs_column"].case_sensitive?
+ assert_not_predicate CollationTest.columns_hash["string_ci_column"], :case_sensitive?
+ assert_predicate CollationTest.columns_hash["string_cs_column"], :case_sensitive?
end
def test_case_insensitive_comparison_for_ci_column
diff --git a/activerecord/test/cases/adapters/mysql2/charset_collation_test.rb b/activerecord/test/cases/adapters/mysql2/charset_collation_test.rb
index e4a6ed5482..d0c57de65d 100644
--- a/activerecord/test/cases/adapters/mysql2/charset_collation_test.rb
+++ b/activerecord/test/cases/adapters/mysql2/charset_collation_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "cases/helper"
require "support/schema_dumping_helper"
diff --git a/activerecord/test/cases/adapters/mysql2/connection_test.rb b/activerecord/test/cases/adapters/mysql2/connection_test.rb
index a2faf43b0d..726f58d58e 100644
--- a/activerecord/test/cases/adapters/mysql2/connection_test.rb
+++ b/activerecord/test/cases/adapters/mysql2/connection_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "cases/helper"
require "support/connection_helper"
@@ -38,41 +40,29 @@ class Mysql2ConnectionTest < ActiveRecord::Mysql2TestCase
end
def test_no_automatic_reconnection_after_timeout
- assert @connection.active?
+ assert_predicate @connection, :active?
@connection.update("set @@wait_timeout=1")
sleep 2
- assert !@connection.active?
+ assert_not_predicate @connection, :active?
ensure
# Repair all fixture connections so other tests won't break.
@fixture_connections.each(&:verify!)
end
def test_successful_reconnection_after_timeout_with_manual_reconnect
- assert @connection.active?
+ assert_predicate @connection, :active?
@connection.update("set @@wait_timeout=1")
sleep 2
@connection.reconnect!
- assert @connection.active?
+ assert_predicate @connection, :active?
end
def test_successful_reconnection_after_timeout_with_verify
- assert @connection.active?
+ assert_predicate @connection, :active?
@connection.update("set @@wait_timeout=1")
sleep 2
@connection.verify!
- assert @connection.active?
- end
-
- def test_verify_with_args_is_deprecated
- assert_deprecated do
- @connection.verify!(option: true)
- end
- assert_deprecated do
- @connection.verify!([])
- end
- assert_deprecated do
- @connection.verify!({})
- end
+ assert_predicate @connection, :active?
end
def test_execute_after_disconnect
@@ -184,10 +174,10 @@ class Mysql2ConnectionTest < ActiveRecord::Mysql2TestCase
assert_equal "SCHEMA", @subscriber.logged[0][1]
end
- def test_logs_name_rename_column_sql
+ def test_logs_name_rename_column_for_alter
@connection.execute "CREATE TABLE `bar_baz` (`foo` varchar(255))"
@subscriber.logged.clear
- @connection.send(:rename_column_sql, "bar_baz", "foo", "foo2")
+ @connection.send(:rename_column_for_alter, "bar_baz", "foo", "foo2")
assert_equal "SCHEMA", @subscriber.logged[0][1]
ensure
@connection.execute "DROP TABLE `bar_baz`"
diff --git a/activerecord/test/cases/adapters/mysql2/datetime_precision_quoting_test.rb b/activerecord/test/cases/adapters/mysql2/datetime_precision_quoting_test.rb
index c131a5169c..fa54f39992 100644
--- a/activerecord/test/cases/adapters/mysql2/datetime_precision_quoting_test.rb
+++ b/activerecord/test/cases/adapters/mysql2/datetime_precision_quoting_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "cases/helper"
class Mysql2DatetimePrecisionQuotingTest < ActiveRecord::Mysql2TestCase
diff --git a/activerecord/test/cases/adapters/mysql2/enum_test.rb b/activerecord/test/cases/adapters/mysql2/enum_test.rb
index 7ad3e3ca2d..832f5d61d1 100644
--- a/activerecord/test/cases/adapters/mysql2/enum_test.rb
+++ b/activerecord/test/cases/adapters/mysql2/enum_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "cases/helper"
class Mysql2EnumTest < ActiveRecord::Mysql2TestCase
@@ -11,11 +13,11 @@ class Mysql2EnumTest < ActiveRecord::Mysql2TestCase
def test_should_not_be_unsigned
column = EnumTest.columns_hash["enum_column"]
- assert_not column.unsigned?
+ assert_not_predicate column, :unsigned?
end
def test_should_not_be_bigint
column = EnumTest.columns_hash["enum_column"]
- assert_not column.bigint?
+ assert_not_predicate column, :bigint?
end
end
diff --git a/activerecord/test/cases/adapters/mysql2/explain_test.rb b/activerecord/test/cases/adapters/mysql2/explain_test.rb
index 7916921e5a..b8e778f0b0 100644
--- a/activerecord/test/cases/adapters/mysql2/explain_test.rb
+++ b/activerecord/test/cases/adapters/mysql2/explain_test.rb
@@ -1,21 +1,23 @@
+# frozen_string_literal: true
+
require "cases/helper"
-require "models/developer"
-require "models/computer"
+require "models/author"
+require "models/post"
class Mysql2ExplainTest < ActiveRecord::Mysql2TestCase
- fixtures :developers
+ fixtures :authors
def test_explain_for_one_query
- explain = Developer.where(id: 1).explain
- assert_match %(EXPLAIN for: SELECT `developers`.* FROM `developers` WHERE `developers`.`id` = 1), explain
- assert_match %r(developers |.* const), explain
+ explain = Author.where(id: 1).explain
+ assert_match %(EXPLAIN for: SELECT `authors`.* FROM `authors` WHERE `authors`.`id` = 1), explain
+ assert_match %r(authors |.* const), explain
end
def test_explain_with_eager_loading
- explain = Developer.where(id: 1).includes(:audit_logs).explain
- assert_match %(EXPLAIN for: SELECT `developers`.* FROM `developers` WHERE `developers`.`id` = 1), explain
- assert_match %r(developers |.* const), explain
- assert_match %(EXPLAIN for: SELECT `audit_logs`.* FROM `audit_logs` WHERE `audit_logs`.`developer_id` = 1), explain
- assert_match %r(audit_logs |.* ALL), explain
+ explain = Author.where(id: 1).includes(:posts).explain
+ assert_match %(EXPLAIN for: SELECT `authors`.* FROM `authors` WHERE `authors`.`id` = 1), explain
+ assert_match %r(authors |.* const), explain
+ assert_match %(EXPLAIN for: SELECT `posts`.* FROM `posts` WHERE `posts`.`author_id` = 1), explain
+ assert_match %r(posts |.* ALL), explain
end
end
diff --git a/activerecord/test/cases/adapters/mysql2/json_test.rb b/activerecord/test/cases/adapters/mysql2/json_test.rb
index 26c69edc7b..de78ba91f5 100644
--- a/activerecord/test/cases/adapters/mysql2/json_test.rb
+++ b/activerecord/test/cases/adapters/mysql2/json_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "cases/helper"
require "cases/json_shared_test_cases"
diff --git a/activerecord/test/cases/adapters/mysql2/mysql2_adapter_test.rb b/activerecord/test/cases/adapters/mysql2/mysql2_adapter_test.rb
index 565130c38f..d18fb97e05 100644
--- a/activerecord/test/cases/adapters/mysql2/mysql2_adapter_test.rb
+++ b/activerecord/test/cases/adapters/mysql2/mysql2_adapter_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "cases/helper"
require "support/ddl_helper"
diff --git a/activerecord/test/cases/adapters/mysql2/schema_migrations_test.rb b/activerecord/test/cases/adapters/mysql2/schema_migrations_test.rb
index 251a50e41e..d7d9a2d732 100644
--- a/activerecord/test/cases/adapters/mysql2/schema_migrations_test.rb
+++ b/activerecord/test/cases/adapters/mysql2/schema_migrations_test.rb
@@ -1,6 +1,10 @@
+# frozen_string_literal: true
+
require "cases/helper"
class SchemaMigrationsTest < ActiveRecord::Mysql2TestCase
+ self.use_transactional_tests = false
+
def test_renaming_index_on_foreign_key
connection.add_index "engines", "car_id"
connection.add_foreign_key :engines, :cars, name: "fk_engines_cars"
@@ -31,6 +35,8 @@ class SchemaMigrationsTest < ActiveRecord::Mysql2TestCase
assert connection.column_exists?(table_name, :key, :string)
end
+ ensure
+ ActiveRecord::InternalMetadata[:environment] = connection.migration_context.current_environment
end
private
diff --git a/activerecord/test/cases/adapters/mysql2/schema_test.rb b/activerecord/test/cases/adapters/mysql2/schema_test.rb
index 1fad5585de..b587e756cf 100644
--- a/activerecord/test/cases/adapters/mysql2/schema_test.rb
+++ b/activerecord/test/cases/adapters/mysql2/schema_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "cases/helper"
require "models/post"
require "models/comment"
diff --git a/activerecord/test/cases/adapters/mysql2/sp_test.rb b/activerecord/test/cases/adapters/mysql2/sp_test.rb
index 4182532535..7b6dce71e9 100644
--- a/activerecord/test/cases/adapters/mysql2/sp_test.rb
+++ b/activerecord/test/cases/adapters/mysql2/sp_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "cases/helper"
require "models/topic"
require "models/reply"
@@ -15,7 +17,7 @@ class Mysql2StoredProcedureTest < ActiveRecord::Mysql2TestCase
# Test that MySQL allows multiple results for stored procedures
#
# In MySQL 5.6, CLIENT_MULTI_RESULTS is enabled by default.
- # http://dev.mysql.com/doc/refman/5.6/en/call.html
+ # https://dev.mysql.com/doc/refman/5.6/en/call.html
def test_multi_results
rows = @connection.select_rows("CALL ten();")
assert_equal 10, rows[0][0].to_i, "ten() did not return 10 as expected: #{rows.inspect}"
diff --git a/activerecord/test/cases/adapters/mysql2/sql_types_test.rb b/activerecord/test/cases/adapters/mysql2/sql_types_test.rb
index d6e7f29a5c..e10642cbb4 100644
--- a/activerecord/test/cases/adapters/mysql2/sql_types_test.rb
+++ b/activerecord/test/cases/adapters/mysql2/sql_types_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "cases/helper"
class Mysql2SqlTypesTest < ActiveRecord::Mysql2TestCase
diff --git a/activerecord/test/cases/adapters/mysql2/table_options_test.rb b/activerecord/test/cases/adapters/mysql2/table_options_test.rb
index 61a8ce9bc0..1c92df940f 100644
--- a/activerecord/test/cases/adapters/mysql2/table_options_test.rb
+++ b/activerecord/test/cases/adapters/mysql2/table_options_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "cases/helper"
require "support/schema_dumping_helper"
@@ -15,28 +17,103 @@ class Mysql2TableOptionsTest < ActiveRecord::Mysql2TestCase
test "table options with ENGINE" do
@connection.create_table "mysql_table_options", force: true, options: "ENGINE=MyISAM"
output = dump_table_schema("mysql_table_options")
- options = %r{create_table "mysql_table_options", force: :cascade, options: "(?<options>.*)"}.match(output)[:options]
+ options = %r{create_table "mysql_table_options", options: "(?<options>.*)"}.match(output)[:options]
assert_match %r{ENGINE=MyISAM}, options
end
test "table options with ROW_FORMAT" do
@connection.create_table "mysql_table_options", force: true, options: "ROW_FORMAT=REDUNDANT"
output = dump_table_schema("mysql_table_options")
- options = %r{create_table "mysql_table_options", force: :cascade, options: "(?<options>.*)"}.match(output)[:options]
+ options = %r{create_table "mysql_table_options", options: "(?<options>.*)"}.match(output)[:options]
assert_match %r{ROW_FORMAT=REDUNDANT}, options
end
test "table options with CHARSET" do
@connection.create_table "mysql_table_options", force: true, options: "CHARSET=utf8mb4"
output = dump_table_schema("mysql_table_options")
- options = %r{create_table "mysql_table_options", force: :cascade, options: "(?<options>.*)"}.match(output)[:options]
+ options = %r{create_table "mysql_table_options", options: "(?<options>.*)"}.match(output)[:options]
assert_match %r{CHARSET=utf8mb4}, options
end
test "table options with COLLATE" do
@connection.create_table "mysql_table_options", force: true, options: "COLLATE=utf8mb4_bin"
output = dump_table_schema("mysql_table_options")
- options = %r{create_table "mysql_table_options", force: :cascade, options: "(?<options>.*)"}.match(output)[:options]
+ options = %r{create_table "mysql_table_options", options: "(?<options>.*)"}.match(output)[:options]
assert_match %r{COLLATE=utf8mb4_bin}, options
end
end
+
+class Mysql2DefaultEngineOptionSchemaDumpTest < ActiveRecord::Mysql2TestCase
+ include SchemaDumpingHelper
+ self.use_transactional_tests = false
+
+ def setup
+ @verbose_was = ActiveRecord::Migration.verbose
+ ActiveRecord::Migration.verbose = false
+ end
+
+ def teardown
+ ActiveRecord::Base.connection.drop_table "mysql_table_options", if_exists: true
+ ActiveRecord::Migration.verbose = @verbose_was
+ ActiveRecord::SchemaMigration.delete_all rescue nil
+ end
+
+ test "schema dump includes ENGINE=InnoDB if not provided" do
+ ActiveRecord::Base.connection.create_table "mysql_table_options", force: true
+
+ output = dump_table_schema("mysql_table_options")
+ options = %r{create_table "mysql_table_options", options: "(?<options>.*)"}.match(output)[:options]
+ assert_match %r{ENGINE=InnoDB}, options
+ end
+
+ test "schema dump includes ENGINE=InnoDB in legacy migrations" do
+ migration = Class.new(ActiveRecord::Migration[5.1]) do
+ def migrate(x)
+ create_table "mysql_table_options", force: true
+ end
+ end.new
+
+ ActiveRecord::Migrator.new(:up, [migration]).migrate
+
+ output = dump_table_schema("mysql_table_options")
+ options = %r{create_table "mysql_table_options", options: "(?<options>.*)"}.match(output)[:options]
+ assert_match %r{ENGINE=InnoDB}, options
+ end
+end
+
+class Mysql2DefaultEngineOptionSqlOutputTest < ActiveRecord::Mysql2TestCase
+ self.use_transactional_tests = false
+
+ def setup
+ @logger_was = ActiveRecord::Base.logger
+ @log = StringIO.new
+ @verbose_was = ActiveRecord::Migration.verbose
+ ActiveRecord::Base.logger = ActiveSupport::Logger.new(@log)
+ ActiveRecord::Migration.verbose = false
+ end
+
+ def teardown
+ ActiveRecord::Base.logger = @logger_was
+ ActiveRecord::Migration.verbose = @verbose_was
+ ActiveRecord::Base.connection.drop_table "mysql_table_options", if_exists: true
+ ActiveRecord::SchemaMigration.delete_all rescue nil
+ end
+
+ test "new migrations do not contain default ENGINE=InnoDB option" do
+ ActiveRecord::Base.connection.create_table "mysql_table_options", force: true
+
+ assert_no_match %r{ENGINE=InnoDB}, @log.string
+ end
+
+ test "legacy migrations contain default ENGINE=InnoDB option" do
+ migration = Class.new(ActiveRecord::Migration[5.1]) do
+ def migrate(x)
+ create_table "mysql_table_options", force: true
+ end
+ end.new
+
+ ActiveRecord::Migrator.new(:up, [migration]).migrate
+
+ assert_match %r{ENGINE=InnoDB}, @log.string
+ end
+end
diff --git a/activerecord/test/cases/adapters/mysql2/transaction_test.rb b/activerecord/test/cases/adapters/mysql2/transaction_test.rb
index 16101e38cb..f921515c10 100644
--- a/activerecord/test/cases/adapters/mysql2/transaction_test.rb
+++ b/activerecord/test/cases/adapters/mysql2/transaction_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "cases/helper"
require "support/connection_helper"
@@ -11,6 +13,7 @@ module ActiveRecord
setup do
@abort, Thread.abort_on_exception = Thread.abort_on_exception, false
+ Thread.report_on_exception, @original_report_on_exception = false, Thread.report_on_exception if Thread.respond_to?(:report_on_exception)
@connection = ActiveRecord::Base.connection
@connection.clear_cache!
@@ -29,6 +32,7 @@ module ActiveRecord
@connection.drop_table "samples", if_exists: true
Thread.abort_on_exception = @abort
+ Thread.report_on_exception = @original_report_on_exception if Thread.respond_to?(:report_on_exception)
end
test "raises Deadlocked when a deadlock is encountered" do
@@ -57,5 +61,89 @@ module ActiveRecord
end
end
end
+
+ test "raises LockWaitTimeout when lock wait timeout exceeded" do
+ assert_raises(ActiveRecord::LockWaitTimeout) do
+ s = Sample.create!(value: 1)
+ latch1 = Concurrent::CountDownLatch.new
+ latch2 = Concurrent::CountDownLatch.new
+
+ thread = Thread.new do
+ Sample.transaction do
+ Sample.lock.find(s.id)
+ latch1.count_down
+ latch2.wait
+ end
+ end
+
+ begin
+ Sample.transaction do
+ latch1.wait
+ Sample.connection.execute("SET innodb_lock_wait_timeout = 1")
+ Sample.lock.find(s.id)
+ end
+ ensure
+ Sample.connection.execute("SET innodb_lock_wait_timeout = DEFAULT")
+ latch2.count_down
+ thread.join
+ end
+ end
+ end
+
+ test "raises StatementTimeout when statement timeout exceeded" do
+ skip unless ActiveRecord::Base.connection.show_variable("max_execution_time")
+ assert_raises(ActiveRecord::StatementTimeout) do
+ s = Sample.create!(value: 1)
+ latch1 = Concurrent::CountDownLatch.new
+ latch2 = Concurrent::CountDownLatch.new
+
+ thread = Thread.new do
+ Sample.transaction do
+ Sample.lock.find(s.id)
+ latch1.count_down
+ latch2.wait
+ end
+ end
+
+ begin
+ Sample.transaction do
+ latch1.wait
+ Sample.connection.execute("SET max_execution_time = 1")
+ Sample.lock.find(s.id)
+ end
+ ensure
+ Sample.connection.execute("SET max_execution_time = DEFAULT")
+ latch2.count_down
+ thread.join
+ end
+ end
+ end
+
+ test "raises QueryCanceled when canceling statement due to user request" do
+ assert_raises(ActiveRecord::QueryCanceled) do
+ s = Sample.create!(value: 1)
+ latch = Concurrent::CountDownLatch.new
+
+ thread = Thread.new do
+ Sample.transaction do
+ Sample.lock.find(s.id)
+ latch.count_down
+ sleep(0.5)
+ conn = Sample.connection
+ pid = conn.query_value("SELECT id FROM information_schema.processlist WHERE info LIKE '% FOR UPDATE'")
+ conn.execute("KILL QUERY #{pid}")
+ end
+ end
+
+ begin
+ Sample.transaction do
+ latch.wait
+ Sample.lock.find(s.id)
+ end
+ ensure
+ thread.join
+ end
+ end
+ end
end
end
diff --git a/activerecord/test/cases/adapters/mysql2/unsigned_type_test.rb b/activerecord/test/cases/adapters/mysql2/unsigned_type_test.rb
index 71dcfaa241..97da96003d 100644
--- a/activerecord/test/cases/adapters/mysql2/unsigned_type_test.rb
+++ b/activerecord/test/cases/adapters/mysql2/unsigned_type_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "cases/helper"
require "support/schema_dumping_helper"
@@ -52,7 +54,7 @@ class Mysql2UnsignedTypeTest < ActiveRecord::Mysql2TestCase
end
@connection.columns("unsigned_types").select { |c| /^unsigned_/.match?(c.name) }.each do |column|
- assert column.unsigned?
+ assert_predicate column, :unsigned?
end
end
@@ -60,7 +62,7 @@ class Mysql2UnsignedTypeTest < ActiveRecord::Mysql2TestCase
schema = dump_table_schema "unsigned_types"
assert_match %r{t\.integer\s+"unsigned_integer",\s+unsigned: true$}, schema
assert_match %r{t\.bigint\s+"unsigned_bigint",\s+unsigned: true$}, schema
- assert_match %r{t\.float\s+"unsigned_float",\s+limit: 24,\s+unsigned: true$}, schema
+ assert_match %r{t\.float\s+"unsigned_float",\s+unsigned: true$}, schema
assert_match %r{t\.decimal\s+"unsigned_decimal",\s+precision: 10,\s+scale: 2,\s+unsigned: true$}, schema
end
end
diff --git a/activerecord/test/cases/adapters/mysql2/virtual_column_test.rb b/activerecord/test/cases/adapters/mysql2/virtual_column_test.rb
index 442a4fb7b5..ffde8ed4d8 100644
--- a/activerecord/test/cases/adapters/mysql2/virtual_column_test.rb
+++ b/activerecord/test/cases/adapters/mysql2/virtual_column_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "cases/helper"
require "support/schema_dumping_helper"
@@ -52,7 +54,7 @@ if ActiveRecord::Base.connection.supports_virtual_columns?
def test_schema_dumping
output = dump_table_schema("virtual_columns")
- assert_match(/t\.virtual\s+"upper_name",\s+type: :string,\s+as: "UPPER\(`name`\)"$/i, output)
+ assert_match(/t\.virtual\s+"upper_name",\s+type: :string,\s+as: "(?:UPPER|UCASE)\(`name`\)"$/i, output)
assert_match(/t\.virtual\s+"name_length",\s+type: :integer,\s+as: "LENGTH\(`name`\)",\s+stored: true$/i, output)
end
end
diff --git a/activerecord/test/cases/adapters/postgresql/active_schema_test.rb b/activerecord/test/cases/adapters/postgresql/active_schema_test.rb
index b787de8453..308ad1d854 100644
--- a/activerecord/test/cases/adapters/postgresql/active_schema_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/active_schema_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "cases/helper"
class PostgresqlActiveSchemaTest < ActiveRecord::PostgreSQLTestCase
@@ -57,6 +59,15 @@ class PostgresqlActiveSchemaTest < ActiveRecord::PostgreSQLTestCase
assert_equal expected, add_index(:people, "lower(last_name)", using: type, unique: true)
end
+ expected = %(CREATE INDEX "index_people_on_last_name" ON "people" USING gist ("last_name" bpchar_pattern_ops))
+ assert_equal expected, add_index(:people, :last_name, using: :gist, opclass: { last_name: :bpchar_pattern_ops })
+
+ expected = %(CREATE INDEX "index_people_on_last_name_and_first_name" ON "people" ("last_name" DESC NULLS LAST, "first_name" ASC))
+ assert_equal expected, add_index(:people, [:last_name, :first_name], order: { last_name: "DESC NULLS LAST", first_name: :asc })
+
+ expected = %(CREATE INDEX "index_people_on_last_name" ON "people" ("last_name" NULLS FIRST))
+ assert_equal expected, add_index(:people, :last_name, order: "NULLS FIRST")
+
assert_raise ArgumentError do
add_index(:people, :last_name, algorithm: :copy)
end
diff --git a/activerecord/test/cases/adapters/postgresql/array_test.rb b/activerecord/test/cases/adapters/postgresql/array_test.rb
index 121c62dadf..58fa7532a2 100644
--- a/activerecord/test/cases/adapters/postgresql/array_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/array_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "cases/helper"
require "support/schema_dumping_helper"
@@ -37,12 +39,45 @@ class PostgresqlArrayTest < ActiveRecord::PostgreSQLTestCase
def test_column
assert_equal :string, @column.type
assert_equal "character varying(255)", @column.sql_type
- assert @column.array?
- assert_not @type.binary?
+ assert_predicate @column, :array?
+ assert_not_predicate @type, :binary?
ratings_column = PgArray.columns_hash["ratings"]
assert_equal :integer, ratings_column.type
- assert ratings_column.array?
+ assert_predicate ratings_column, :array?
+ end
+
+ def test_not_compatible_with_serialize_array
+ new_klass = Class.new(PgArray) do
+ serialize :tags, Array
+ end
+ assert_raises(ActiveRecord::AttributeMethods::Serialization::ColumnNotSerializableError) do
+ new_klass.new
+ end
+ end
+
+ class MyTags
+ def initialize(tags); @tags = tags end
+ def to_a; @tags end
+ def self.load(tags); new(tags) end
+ def self.dump(object); object.to_a end
+ end
+
+ def test_array_with_serialized_attributes
+ new_klass = Class.new(PgArray) do
+ serialize :tags, MyTags
+ end
+
+ new_klass.create!(tags: MyTags.new(["one", "two"]))
+ record = new_klass.first
+
+ assert_instance_of MyTags, record.tags
+ assert_equal ["one", "two"], record.tags.to_a
+
+ record.tags = MyTags.new(["three", "four"])
+ record.save!
+
+ assert_equal ["three", "four"], record.reload.tags.to_a
end
def test_default
@@ -74,7 +109,7 @@ class PostgresqlArrayTest < ActiveRecord::PostgreSQLTestCase
assert_equal :text, column.type
assert_equal [], PgArray.column_defaults["snippets"]
- assert column.array?
+ assert_predicate column, :array?
end
def test_change_column_cant_make_non_array_column_to_array
@@ -193,7 +228,9 @@ class PostgresqlArrayTest < ActiveRecord::PostgreSQLTestCase
def test_insert_fixtures
tag_values = ["val1", "val2", "val3_with_'_multiple_quote_'_chars"]
- @connection.insert_fixtures([{ "tags" => tag_values }], "pg_arrays")
+ assert_deprecated do
+ @connection.insert_fixtures([{ "tags" => tag_values }], "pg_arrays")
+ end
assert_equal(PgArray.last.tags, tag_values)
end
@@ -220,7 +257,7 @@ class PostgresqlArrayTest < ActiveRecord::PostgreSQLTestCase
x = PgArray.create!(tags: tags)
x.reload
- refute x.changed?
+ assert_not_predicate x, :changed?
end
def test_quoting_non_standard_delimiters
@@ -242,7 +279,7 @@ class PostgresqlArrayTest < ActiveRecord::PostgreSQLTestCase
x.reload
assert_equal %w(one two three), x.tags
- assert_not x.changed?
+ assert_not_predicate x, :changed?
end
def test_mutate_value_in_array
@@ -253,7 +290,7 @@ class PostgresqlArrayTest < ActiveRecord::PostgreSQLTestCase
x.reload
assert_equal [{ "a" => "c" }, { "b" => "b" }], x.hstores
- assert_not x.changed?
+ assert_not_predicate x, :changed?
end
def test_datetime_with_timezone_awareness
diff --git a/activerecord/test/cases/adapters/postgresql/bit_string_test.rb b/activerecord/test/cases/adapters/postgresql/bit_string_test.rb
index 7712e809a2..c8e728bbb6 100644
--- a/activerecord/test/cases/adapters/postgresql/bit_string_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/bit_string_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "cases/helper"
require "support/connection_helper"
require "support/schema_dumping_helper"
@@ -27,20 +29,20 @@ class PostgresqlBitStringTest < ActiveRecord::PostgreSQLTestCase
column = PostgresqlBitString.columns_hash["a_bit"]
assert_equal :bit, column.type
assert_equal "bit(8)", column.sql_type
- assert_not column.array?
+ assert_not_predicate column, :array?
type = PostgresqlBitString.type_for_attribute("a_bit")
- assert_not type.binary?
+ assert_not_predicate type, :binary?
end
def test_bit_string_varying_column
column = PostgresqlBitString.columns_hash["a_bit_varying"]
assert_equal :bit_varying, column.type
assert_equal "bit varying(4)", column.sql_type
- assert_not column.array?
+ assert_not_predicate column, :array?
type = PostgresqlBitString.type_for_attribute("a_bit_varying")
- assert_not type.binary?
+ assert_not_predicate type, :binary?
end
def test_default
diff --git a/activerecord/test/cases/adapters/postgresql/bytea_test.rb b/activerecord/test/cases/adapters/postgresql/bytea_test.rb
index 539c90f0bc..64bb6906cd 100644
--- a/activerecord/test/cases/adapters/postgresql/bytea_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/bytea_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "cases/helper"
require "support/schema_dumping_helper"
@@ -47,7 +49,7 @@ class PostgresqlByteaTest < ActiveRecord::PostgreSQLTestCase
end
def test_type_cast_binary_value
- data = "\u001F\x8B".force_encoding("BINARY")
+ data = "\u001F\x8B".dup.force_encoding("BINARY")
assert_equal(data, @type.deserialize(data))
end
@@ -73,7 +75,7 @@ class PostgresqlByteaTest < ActiveRecord::PostgreSQLTestCase
def test_write_value
data = "\u001F"
record = ByteaDataType.create(payload: data)
- assert_not record.new_record?
+ assert_not_predicate record, :new_record?
assert_equal(data, record.payload)
end
@@ -99,14 +101,14 @@ class PostgresqlByteaTest < ActiveRecord::PostgreSQLTestCase
data = File.read(File.join(__dir__, "..", "..", "..", "assets", "example.log"))
assert(data.size > 1)
record = ByteaDataType.create(payload: data)
- assert_not record.new_record?
+ assert_not_predicate record, :new_record?
assert_equal(data, record.payload)
assert_equal(data, ByteaDataType.where(id: record.id).first.payload)
end
def test_write_nil
record = ByteaDataType.create(payload: nil)
- assert_not record.new_record?
+ assert_not_predicate record, :new_record?
assert_nil(record.payload)
assert_nil(ByteaDataType.where(id: record.id).first.payload)
end
diff --git a/activerecord/test/cases/adapters/postgresql/case_insensitive_test.rb b/activerecord/test/cases/adapters/postgresql/case_insensitive_test.rb
index 03b44feab6..305e033642 100644
--- a/activerecord/test/cases/adapters/postgresql/case_insensitive_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/case_insensitive_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "cases/helper"
class PostgresqlCaseInsensitiveTest < ActiveRecord::PostgreSQLTestCase
diff --git a/activerecord/test/cases/adapters/postgresql/change_schema_test.rb b/activerecord/test/cases/adapters/postgresql/change_schema_test.rb
index ea642069d2..6dba4f3e14 100644
--- a/activerecord/test/cases/adapters/postgresql/change_schema_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/change_schema_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "cases/helper"
module ActiveRecord
@@ -31,7 +33,7 @@ module ActiveRecord
connection.change_column :strings, :somedate, :timestamp, array: true, cast_as: :timestamp
column = connection.columns(:strings).find { |c| c.name == "somedate" }
assert_equal :datetime, column.type
- assert column.array?
+ assert_predicate column, :array?
end
end
end
diff --git a/activerecord/test/cases/adapters/postgresql/cidr_test.rb b/activerecord/test/cases/adapters/postgresql/cidr_test.rb
index 52f2a0096c..f20958fbd2 100644
--- a/activerecord/test/cases/adapters/postgresql/cidr_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/cidr_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "cases/helper"
require "ipaddr"
diff --git a/activerecord/test/cases/adapters/postgresql/citext_test.rb b/activerecord/test/cases/adapters/postgresql/citext_test.rb
index ca95e4b626..9eb0b7d99c 100644
--- a/activerecord/test/cases/adapters/postgresql/citext_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/citext_test.rb
@@ -1,78 +1,78 @@
+# frozen_string_literal: true
+
require "cases/helper"
require "support/schema_dumping_helper"
-if ActiveRecord::Base.connection.supports_extensions?
- class PostgresqlCitextTest < ActiveRecord::PostgreSQLTestCase
- include SchemaDumpingHelper
- class Citext < ActiveRecord::Base
- self.table_name = "citexts"
- end
+class PostgresqlCitextTest < ActiveRecord::PostgreSQLTestCase
+ include SchemaDumpingHelper
+ class Citext < ActiveRecord::Base
+ self.table_name = "citexts"
+ end
- def setup
- @connection = ActiveRecord::Base.connection
+ def setup
+ @connection = ActiveRecord::Base.connection
- enable_extension!("citext", @connection)
+ enable_extension!("citext", @connection)
- @connection.create_table("citexts") do |t|
- t.citext "cival"
- end
+ @connection.create_table("citexts") do |t|
+ t.citext "cival"
end
+ end
- teardown do
- @connection.drop_table "citexts", if_exists: true
- disable_extension!("citext", @connection)
- end
+ teardown do
+ @connection.drop_table "citexts", if_exists: true
+ disable_extension!("citext", @connection)
+ end
- def test_citext_enabled
- assert @connection.extension_enabled?("citext")
- end
+ def test_citext_enabled
+ assert @connection.extension_enabled?("citext")
+ end
- def test_column
- column = Citext.columns_hash["cival"]
- assert_equal :citext, column.type
- assert_equal "citext", column.sql_type
- assert_not column.array?
+ def test_column
+ column = Citext.columns_hash["cival"]
+ assert_equal :citext, column.type
+ assert_equal "citext", column.sql_type
+ assert_not_predicate column, :array?
- type = Citext.type_for_attribute("cival")
- assert_not type.binary?
- end
-
- def test_change_table_supports_json
- @connection.transaction do
- @connection.change_table("citexts") do |t|
- t.citext "username"
- end
- Citext.reset_column_information
- column = Citext.columns_hash["username"]
- assert_equal :citext, column.type
+ type = Citext.type_for_attribute("cival")
+ assert_not_predicate type, :binary?
+ end
- raise ActiveRecord::Rollback # reset the schema change
+ def test_change_table_supports_json
+ @connection.transaction do
+ @connection.change_table("citexts") do |t|
+ t.citext "username"
end
- ensure
Citext.reset_column_information
+ column = Citext.columns_hash["username"]
+ assert_equal :citext, column.type
+
+ raise ActiveRecord::Rollback # reset the schema change
end
+ ensure
+ Citext.reset_column_information
+ end
- def test_write
- x = Citext.new(cival: "Some CI Text")
- x.save!
- citext = Citext.first
- assert_equal "Some CI Text", citext.cival
+ def test_write
+ x = Citext.new(cival: "Some CI Text")
+ x.save!
+ citext = Citext.first
+ assert_equal "Some CI Text", citext.cival
- citext.cival = "Some NEW CI Text"
- citext.save!
+ citext.cival = "Some NEW CI Text"
+ citext.save!
- assert_equal "Some NEW CI Text", citext.reload.cival
- end
+ assert_equal "Some NEW CI Text", citext.reload.cival
+ end
- def test_select_case_insensitive
- @connection.execute "insert into citexts (cival) values('Cased Text')"
- x = Citext.where(cival: "cased text").first
- assert_equal "Cased Text", x.cival
- end
+ def test_select_case_insensitive
+ @connection.execute "insert into citexts (cival) values('Cased Text')"
+ x = Citext.where(cival: "cased text").first
+ assert_equal "Cased Text", x.cival
+ end
- def test_schema_dump_with_shorthand
- output = dump_table_schema("citexts")
- assert_match %r[t\.citext "cival"], output
- end
+ def test_schema_dump_with_shorthand
+ output = dump_table_schema("citexts")
+ assert_match %r[t\.citext "cival"], output
end
end
diff --git a/activerecord/test/cases/adapters/postgresql/collation_test.rb b/activerecord/test/cases/adapters/postgresql/collation_test.rb
index a603221d8f..7468f4c4f8 100644
--- a/activerecord/test/cases/adapters/postgresql/collation_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/collation_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "cases/helper"
require "support/schema_dumping_helper"
diff --git a/activerecord/test/cases/adapters/postgresql/composite_test.rb b/activerecord/test/cases/adapters/postgresql/composite_test.rb
index 1da2a9e2ac..b0ce2694a3 100644
--- a/activerecord/test/cases/adapters/postgresql/composite_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/composite_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "cases/helper"
require "support/connection_helper"
@@ -49,10 +51,10 @@ class PostgresqlCompositeTest < ActiveRecord::PostgreSQLTestCase
column = PostgresqlComposite.columns_hash["address"]
assert_nil column.type
assert_equal "full_address", column.sql_type
- assert_not column.array?
+ assert_not_predicate column, :array?
type = PostgresqlComposite.type_for_attribute("address")
- assert_not type.binary?
+ assert_not_predicate type, :binary?
end
def test_composite_mapping
@@ -104,17 +106,17 @@ class PostgresqlCompositeWithCustomOIDTest < ActiveRecord::PostgreSQLTestCase
def setup
super
- @connection.type_map.register_type "full_address", FullAddressType.new
+ @connection.send(:type_map).register_type "full_address", FullAddressType.new
end
def test_column
column = PostgresqlComposite.columns_hash["address"]
assert_equal :full_address, column.type
assert_equal "full_address", column.sql_type
- assert_not column.array?
+ assert_not_predicate column, :array?
type = PostgresqlComposite.type_for_attribute("address")
- assert_not type.binary?
+ assert_not_predicate type, :binary?
end
def test_composite_mapping
diff --git a/activerecord/test/cases/adapters/postgresql/connection_test.rb b/activerecord/test/cases/adapters/postgresql/connection_test.rb
index 32afe331fa..d1b3c434e1 100644
--- a/activerecord/test/cases/adapters/postgresql/connection_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/connection_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "cases/helper"
require "support/connection_helper"
@@ -101,7 +103,7 @@ module ActiveRecord
end
def test_indexes_logs_name
- assert_deprecated { @connection.indexes("items", "hello") }
+ @connection.indexes("items")
assert_equal "SCHEMA", @subscriber.logged[0][1]
end
@@ -133,8 +135,8 @@ module ActiveRecord
if ActiveRecord::Base.connection.prepared_statements
def test_statement_key_is_logged
- binds = [bind_attribute(nil, 1)]
- @connection.exec_query("SELECT $1::integer", "SQL", binds, prepare: true)
+ bind = Relation::QueryAttribute.new(nil, 1, Type::Value.new)
+ @connection.exec_query("SELECT $1::integer", "SQL", [bind], prepare: true)
name = @subscriber.payloads.last[:statement_name]
assert name
res = @connection.exec_query("EXPLAIN (FORMAT JSON) EXECUTE #{name}(1)")
@@ -155,7 +157,7 @@ module ActiveRecord
original_connection_pid = @connection.query("select pg_backend_pid()")
# Sanity check.
- assert @connection.active?
+ assert_predicate @connection, :active?
if @connection.send(:postgresql_version) >= 90200
secondary_connection = ActiveRecord::Base.connection_pool.checkout
@@ -174,7 +176,7 @@ module ActiveRecord
@connection.verify!
- assert @connection.active?
+ assert_predicate @connection, :active?
# If we get no exception here, then either we re-connected successfully, or
# we never actually got disconnected.
@@ -218,6 +220,13 @@ module ActiveRecord
end
end
+ def test_set_session_timezone
+ run_without_connection do |orig_connection|
+ ActiveRecord::Base.establish_connection(orig_connection.deep_merge(variables: { timezone: "America/New_York" }))
+ assert_equal "America/New_York", ActiveRecord::Base.connection.query_value("SHOW TIME ZONE")
+ end
+ end
+
def test_get_and_release_advisory_lock
lock_id = 5295901941911233559
list_advisory_locks = <<-SQL
diff --git a/activerecord/test/cases/adapters/postgresql/datatype_test.rb b/activerecord/test/cases/adapters/postgresql/datatype_test.rb
index 0725fde5ae..b7535d5c9a 100644
--- a/activerecord/test/cases/adapters/postgresql/datatype_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/datatype_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "cases/helper"
require "support/ddl_helper"
diff --git a/activerecord/test/cases/adapters/postgresql/domain_test.rb b/activerecord/test/cases/adapters/postgresql/domain_test.rb
index f1eb8adb15..eeaad94c27 100644
--- a/activerecord/test/cases/adapters/postgresql/domain_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/domain_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "cases/helper"
require "support/connection_helper"
@@ -28,10 +30,10 @@ class PostgresqlDomainTest < ActiveRecord::PostgreSQLTestCase
column = PostgresqlDomain.columns_hash["price"]
assert_equal :decimal, column.type
assert_equal "custom_money", column.sql_type
- assert_not column.array?
+ assert_not_predicate column, :array?
type = PostgresqlDomain.type_for_attribute("price")
- assert_not type.binary?
+ assert_not_predicate type, :binary?
end
def test_domain_acts_like_basetype
@@ -42,6 +44,6 @@ class PostgresqlDomainTest < ActiveRecord::PostgreSQLTestCase
record.price = "34.15"
record.save!
- assert_equal BigDecimal.new("34.15"), record.reload.price
+ assert_equal BigDecimal("34.15"), record.reload.price
end
end
diff --git a/activerecord/test/cases/adapters/postgresql/enum_test.rb b/activerecord/test/cases/adapters/postgresql/enum_test.rb
index 5e5a3158ba..6789ff63e7 100644
--- a/activerecord/test/cases/adapters/postgresql/enum_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/enum_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "cases/helper"
require "support/connection_helper"
@@ -30,10 +32,10 @@ class PostgresqlEnumTest < ActiveRecord::PostgreSQLTestCase
column = PostgresqlEnum.columns_hash["current_mood"]
assert_equal :enum, column.type
assert_equal "mood", column.sql_type
- assert_not column.array?
+ assert_not_predicate column, :array?
type = PostgresqlEnum.type_for_attribute("current_mood")
- assert_not type.binary?
+ assert_not_predicate type, :binary?
end
def test_enum_defaults
@@ -71,7 +73,7 @@ class PostgresqlEnumTest < ActiveRecord::PostgreSQLTestCase
@connection.execute "INSERT INTO postgresql_enums VALUES (1, 'sad');"
stderr_output = capture(:stderr) { PostgresqlEnum.first }
- assert stderr_output.blank?
+ assert_predicate stderr_output, :blank?
end
def test_enum_type_cast
diff --git a/activerecord/test/cases/adapters/postgresql/explain_test.rb b/activerecord/test/cases/adapters/postgresql/explain_test.rb
index d79fbccf47..be525383e9 100644
--- a/activerecord/test/cases/adapters/postgresql/explain_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/explain_test.rb
@@ -1,20 +1,22 @@
+# frozen_string_literal: true
+
require "cases/helper"
-require "models/developer"
-require "models/computer"
+require "models/author"
+require "models/post"
class PostgreSQLExplainTest < ActiveRecord::PostgreSQLTestCase
- fixtures :developers
+ fixtures :authors
def test_explain_for_one_query
- explain = Developer.where(id: 1).explain
- assert_match %r(EXPLAIN for: SELECT "developers"\.\* FROM "developers" WHERE "developers"\."id" = (?:\$1 \[\["id", 1\]\]|1)), explain
+ explain = Author.where(id: 1).explain
+ assert_match %r(EXPLAIN for: SELECT "authors"\.\* FROM "authors" WHERE "authors"\."id" = (?:\$1 \[\["id", 1\]\]|1)), explain
assert_match %(QUERY PLAN), explain
end
def test_explain_with_eager_loading
- explain = Developer.where(id: 1).includes(:audit_logs).explain
+ explain = Author.where(id: 1).includes(:posts).explain
assert_match %(QUERY PLAN), explain
- assert_match %r(EXPLAIN for: SELECT "developers"\.\* FROM "developers" WHERE "developers"\."id" = (?:\$1 \[\["id", 1\]\]|1)), explain
- assert_match %(EXPLAIN for: SELECT "audit_logs".* FROM "audit_logs" WHERE "audit_logs"."developer_id" = 1), explain
+ assert_match %r(EXPLAIN for: SELECT "authors"\.\* FROM "authors" WHERE "authors"\."id" = (?:\$1 \[\["id", 1\]\]|1)), explain
+ assert_match %r(EXPLAIN for: SELECT "posts"\.\* FROM "posts" WHERE "posts"\."author_id" = (?:\$1 \[\["author_id", 1\]\]|1)), explain
end
end
diff --git a/activerecord/test/cases/adapters/postgresql/extension_migration_test.rb b/activerecord/test/cases/adapters/postgresql/extension_migration_test.rb
index b56c226763..df97ab11e7 100644
--- a/activerecord/test/cases/adapters/postgresql/extension_migration_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/extension_migration_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "cases/helper"
class PostgresqlExtensionMigrationTest < ActiveRecord::PostgreSQLTestCase
@@ -20,10 +22,6 @@ class PostgresqlExtensionMigrationTest < ActiveRecord::PostgreSQLTestCase
@connection = ActiveRecord::Base.connection
- unless @connection.supports_extensions?
- return skip("no extension support")
- end
-
@old_schema_migration_table_name = ActiveRecord::SchemaMigration.table_name
@old_table_name_prefix = ActiveRecord::Base.table_name_prefix
@old_table_name_suffix = ActiveRecord::Base.table_name_suffix
diff --git a/activerecord/test/cases/adapters/postgresql/foreign_table_test.rb b/activerecord/test/cases/adapters/postgresql/foreign_table_test.rb
new file mode 100644
index 0000000000..4fa315ad23
--- /dev/null
+++ b/activerecord/test/cases/adapters/postgresql/foreign_table_test.rb
@@ -0,0 +1,109 @@
+# frozen_string_literal: true
+
+require "cases/helper"
+require "models/professor"
+
+if ActiveRecord::Base.connection.supports_foreign_tables?
+ class ForeignTableTest < ActiveRecord::TestCase
+ self.use_transactional_tests = false
+
+ class ForeignProfessor < ActiveRecord::Base
+ self.table_name = "foreign_professors"
+ end
+
+ class ForeignProfessorWithPk < ForeignProfessor
+ self.primary_key = "id"
+ end
+
+ def setup
+ @professor = Professor.create(name: "Nicola")
+
+ @connection = ActiveRecord::Base.connection
+ enable_extension!("postgres_fdw", @connection)
+
+ foreign_db_config = ARTest.connection_config["arunit2"]
+ @connection.execute <<-SQL
+ CREATE SERVER foreign_server
+ FOREIGN DATA WRAPPER postgres_fdw
+ OPTIONS (dbname '#{foreign_db_config["database"]}')
+ SQL
+
+ @connection.execute <<-SQL
+ CREATE USER MAPPING FOR CURRENT_USER
+ SERVER foreign_server
+ SQL
+
+ @connection.execute <<-SQL
+ CREATE FOREIGN TABLE foreign_professors (
+ id int,
+ name character varying NOT NULL
+ ) SERVER foreign_server OPTIONS (
+ table_name 'professors'
+ )
+ SQL
+ end
+
+ def teardown
+ disable_extension!("postgres_fdw", @connection)
+ @connection.execute <<-SQL
+ DROP SERVER IF EXISTS foreign_server CASCADE
+ SQL
+ end
+
+ def test_table_exists
+ table_name = ForeignProfessor.table_name
+ assert_not ActiveRecord::Base.connection.table_exists?(table_name)
+ end
+
+ def test_foreign_tables_are_valid_data_sources
+ table_name = ForeignProfessor.table_name
+ assert @connection.data_source_exists?(table_name), "'#{table_name}' should be a data source"
+ end
+
+ def test_foreign_tables
+ assert_equal ["foreign_professors"], @connection.foreign_tables
+ end
+
+ def test_foreign_table_exists
+ assert @connection.foreign_table_exists?("foreign_professors")
+ assert @connection.foreign_table_exists?(:foreign_professors)
+ assert_not @connection.foreign_table_exists?("nonexistingtable")
+ assert_not @connection.foreign_table_exists?("'")
+ assert_not @connection.foreign_table_exists?(nil)
+ end
+
+ def test_attribute_names
+ assert_equal ["id", "name"], ForeignProfessor.attribute_names
+ end
+
+ def test_attributes
+ professor = ForeignProfessorWithPk.find(@professor.id)
+ assert_equal @professor.attributes, professor.attributes
+ end
+
+ def test_does_not_have_a_primary_key
+ assert_nil ForeignProfessor.primary_key
+ end
+
+ def test_insert_record
+ # Explicit `id` here to avoid complex configurations to implicitly work with remote table
+ ForeignProfessorWithPk.create!(id: 100, name: "Leonardo")
+
+ professor = ForeignProfessorWithPk.last
+ assert_equal "Leonardo", professor.name
+ end
+
+ def test_update_record
+ professor = ForeignProfessorWithPk.find(@professor.id)
+ professor.name = "Albert"
+ professor.save!
+ professor.reload
+ assert_equal "Albert", professor.name
+ end
+
+ def test_delete_record
+ professor = ForeignProfessorWithPk.find(@professor.id)
+ assert_difference("ForeignProfessor.count", -1) { professor.destroy }
+ end
+ end
+end
diff --git a/activerecord/test/cases/adapters/postgresql/full_text_test.rb b/activerecord/test/cases/adapters/postgresql/full_text_test.rb
index 5ddfe32007..95dee3bf44 100644
--- a/activerecord/test/cases/adapters/postgresql/full_text_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/full_text_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "cases/helper"
require "support/schema_dumping_helper"
@@ -20,10 +22,10 @@ class PostgresqlFullTextTest < ActiveRecord::PostgreSQLTestCase
column = Tsvector.columns_hash["text_vector"]
assert_equal :tsvector, column.type
assert_equal "tsvector", column.sql_type
- assert_not column.array?
+ assert_not_predicate column, :array?
type = Tsvector.type_for_attribute("text_vector")
- assert_not type.binary?
+ assert_not_predicate type, :binary?
end
def test_update_tsvector
diff --git a/activerecord/test/cases/adapters/postgresql/geometric_test.rb b/activerecord/test/cases/adapters/postgresql/geometric_test.rb
index 3b6840a1c9..8c6f046553 100644
--- a/activerecord/test/cases/adapters/postgresql/geometric_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/geometric_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "cases/helper"
require "support/connection_helper"
require "support/schema_dumping_helper"
@@ -37,10 +39,10 @@ class PostgresqlPointTest < ActiveRecord::PostgreSQLTestCase
column = PostgresqlPoint.columns_hash["x"]
assert_equal :point, column.type
assert_equal "point", column.sql_type
- assert_not column.array?
+ assert_not_predicate column, :array?
type = PostgresqlPoint.type_for_attribute("x")
- assert_not type.binary?
+ assert_not_predicate type, :binary?
end
def test_default
@@ -77,7 +79,7 @@ class PostgresqlPointTest < ActiveRecord::PostgreSQLTestCase
p.reload
assert_equal ActiveRecord::Point.new(10.0, 25.0), p.x
- assert_not p.changed?
+ assert_not_predicate p, :changed?
end
def test_array_assignment
@@ -115,10 +117,10 @@ class PostgresqlPointTest < ActiveRecord::PostgreSQLTestCase
column = PostgresqlPoint.columns_hash["legacy_x"]
assert_equal :point, column.type
assert_equal "point", column.sql_type
- assert_not column.array?
+ assert_not_predicate column, :array?
type = PostgresqlPoint.type_for_attribute("legacy_x")
- assert_not type.binary?
+ assert_not_predicate type, :binary?
end
def test_legacy_default
@@ -155,7 +157,7 @@ class PostgresqlPointTest < ActiveRecord::PostgreSQLTestCase
p.reload
assert_equal [10.0, 25.0], p.legacy_x
- assert_not p.changed?
+ assert_not_predicate p, :changed?
end
end
diff --git a/activerecord/test/cases/adapters/postgresql/hstore_test.rb b/activerecord/test/cases/adapters/postgresql/hstore_test.rb
index f9cce10fb8..4b061a9375 100644
--- a/activerecord/test/cases/adapters/postgresql/hstore_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/hstore_test.rb
@@ -1,382 +1,378 @@
+# frozen_string_literal: true
+
require "cases/helper"
require "support/schema_dumping_helper"
-if ActiveRecord::Base.connection.supports_extensions?
- class PostgresqlHstoreTest < ActiveRecord::PostgreSQLTestCase
- include SchemaDumpingHelper
- class Hstore < ActiveRecord::Base
- self.table_name = "hstores"
+class PostgresqlHstoreTest < ActiveRecord::PostgreSQLTestCase
+ include SchemaDumpingHelper
+ class Hstore < ActiveRecord::Base
+ self.table_name = "hstores"
- store_accessor :settings, :language, :timezone
- end
+ store_accessor :settings, :language, :timezone
+ end
- class FakeParameters
- def to_unsafe_h
- { "hi" => "hi" }
- end
+ class FakeParameters
+ def to_unsafe_h
+ { "hi" => "hi" }
end
+ end
- def setup
- @connection = ActiveRecord::Base.connection
-
- unless @connection.extension_enabled?("hstore")
- @connection.enable_extension "hstore"
- @connection.commit_db_transaction
- end
+ def setup
+ @connection = ActiveRecord::Base.connection
- @connection.reconnect!
+ enable_extension!("hstore", @connection)
- @connection.transaction do
- @connection.create_table("hstores") do |t|
- t.hstore "tags", default: ""
- t.hstore "payload", array: true
- t.hstore "settings"
- end
+ @connection.transaction do
+ @connection.create_table("hstores") do |t|
+ t.hstore "tags", default: ""
+ t.hstore "payload", array: true
+ t.hstore "settings"
end
- Hstore.reset_column_information
- @column = Hstore.columns_hash["tags"]
- @type = Hstore.type_for_attribute("tags")
- end
-
- teardown do
- @connection.drop_table "hstores", if_exists: true
end
+ Hstore.reset_column_information
+ @column = Hstore.columns_hash["tags"]
+ @type = Hstore.type_for_attribute("tags")
+ end
- def test_hstore_included_in_extensions
- assert @connection.respond_to?(:extensions), "connection should have a list of extensions"
- assert_includes @connection.extensions, "hstore", "extension list should include hstore"
- end
+ teardown do
+ @connection.drop_table "hstores", if_exists: true
+ disable_extension!("hstore", @connection)
+ end
- def test_disable_enable_hstore
- assert @connection.extension_enabled?("hstore")
- @connection.disable_extension "hstore"
- assert_not @connection.extension_enabled?("hstore")
- @connection.enable_extension "hstore"
- assert @connection.extension_enabled?("hstore")
- ensure
- # Restore column(s) dropped by `drop extension hstore cascade;`
- load_schema
- end
+ def test_hstore_included_in_extensions
+ assert_respond_to @connection, :extensions
+ assert_includes @connection.extensions, "hstore", "extension list should include hstore"
+ end
- def test_column
- assert_equal :hstore, @column.type
- assert_equal "hstore", @column.sql_type
- assert_not @column.array?
+ def test_disable_enable_hstore
+ assert @connection.extension_enabled?("hstore")
+ @connection.disable_extension "hstore"
+ assert_not @connection.extension_enabled?("hstore")
+ @connection.enable_extension "hstore"
+ assert @connection.extension_enabled?("hstore")
+ ensure
+ # Restore column(s) dropped by `drop extension hstore cascade;`
+ load_schema
+ end
- assert_not @type.binary?
- end
+ def test_column
+ assert_equal :hstore, @column.type
+ assert_equal "hstore", @column.sql_type
+ assert_not_predicate @column, :array?
- def test_default
- @connection.add_column "hstores", "permissions", :hstore, default: '"users"=>"read", "articles"=>"write"'
- Hstore.reset_column_information
+ assert_not_predicate @type, :binary?
+ end
- assert_equal({ "users" => "read", "articles" => "write" }, Hstore.column_defaults["permissions"])
- assert_equal({ "users" => "read", "articles" => "write" }, Hstore.new.permissions)
- ensure
- Hstore.reset_column_information
- end
+ def test_default
+ @connection.add_column "hstores", "permissions", :hstore, default: '"users"=>"read", "articles"=>"write"'
+ Hstore.reset_column_information
- def test_change_table_supports_hstore
- @connection.transaction do
- @connection.change_table("hstores") do |t|
- t.hstore "users", default: ""
- end
- Hstore.reset_column_information
- column = Hstore.columns_hash["users"]
- assert_equal :hstore, column.type
+ assert_equal({ "users" => "read", "articles" => "write" }, Hstore.column_defaults["permissions"])
+ assert_equal({ "users" => "read", "articles" => "write" }, Hstore.new.permissions)
+ ensure
+ Hstore.reset_column_information
+ end
- raise ActiveRecord::Rollback # reset the schema change
+ def test_change_table_supports_hstore
+ @connection.transaction do
+ @connection.change_table("hstores") do |t|
+ t.hstore "users", default: ""
end
- ensure
Hstore.reset_column_information
+ column = Hstore.columns_hash["users"]
+ assert_equal :hstore, column.type
+
+ raise ActiveRecord::Rollback # reset the schema change
end
+ ensure
+ Hstore.reset_column_information
+ end
- def test_hstore_migration
- hstore_migration = Class.new(ActiveRecord::Migration::Current) do
- def change
- change_table("hstores") do |t|
- t.hstore :keys
- end
+ def test_hstore_migration
+ hstore_migration = Class.new(ActiveRecord::Migration::Current) do
+ def change
+ change_table("hstores") do |t|
+ t.hstore :keys
end
end
-
- hstore_migration.new.suppress_messages do
- hstore_migration.migrate(:up)
- assert_includes @connection.columns(:hstores).map(&:name), "keys"
- hstore_migration.migrate(:down)
- assert_not_includes @connection.columns(:hstores).map(&:name), "keys"
- end
end
- def test_cast_value_on_write
- x = Hstore.new tags: { "bool" => true, "number" => 5 }
- assert_equal({ "bool" => true, "number" => 5 }, x.tags_before_type_cast)
- assert_equal({ "bool" => "true", "number" => "5" }, x.tags)
- x.save
- assert_equal({ "bool" => "true", "number" => "5" }, x.reload.tags)
+ hstore_migration.new.suppress_messages do
+ hstore_migration.migrate(:up)
+ assert_includes @connection.columns(:hstores).map(&:name), "keys"
+ hstore_migration.migrate(:down)
+ assert_not_includes @connection.columns(:hstores).map(&:name), "keys"
end
+ end
- def test_type_cast_hstore
- assert_equal({ "1" => "2" }, @type.deserialize("\"1\"=>\"2\""))
- assert_equal({}, @type.deserialize(""))
- assert_equal({ "key" => nil }, @type.deserialize("key => NULL"))
- assert_equal({ "c" => "}", '"a"' => 'b "a b' }, @type.deserialize(%q(c=>"}", "\"a\""=>"b \"a b")))
- end
+ def test_cast_value_on_write
+ x = Hstore.new tags: { "bool" => true, "number" => 5 }
+ assert_equal({ "bool" => true, "number" => 5 }, x.tags_before_type_cast)
+ assert_equal({ "bool" => "true", "number" => "5" }, x.tags)
+ x.save
+ assert_equal({ "bool" => "true", "number" => "5" }, x.reload.tags)
+ end
- def test_with_store_accessors
- x = Hstore.new(language: "fr", timezone: "GMT")
- assert_equal "fr", x.language
- assert_equal "GMT", x.timezone
+ def test_type_cast_hstore
+ assert_equal({ "1" => "2" }, @type.deserialize("\"1\"=>\"2\""))
+ assert_equal({}, @type.deserialize(""))
+ assert_equal({ "key" => nil }, @type.deserialize("key => NULL"))
+ assert_equal({ "c" => "}", '"a"' => 'b "a b' }, @type.deserialize(%q(c=>"}", "\"a\""=>"b \"a b")))
+ end
- x.save!
- x = Hstore.first
- assert_equal "fr", x.language
- assert_equal "GMT", x.timezone
+ def test_with_store_accessors
+ x = Hstore.new(language: "fr", timezone: "GMT")
+ assert_equal "fr", x.language
+ assert_equal "GMT", x.timezone
- x.language = "de"
- x.save!
+ x.save!
+ x = Hstore.first
+ assert_equal "fr", x.language
+ assert_equal "GMT", x.timezone
- x = Hstore.first
- assert_equal "de", x.language
- assert_equal "GMT", x.timezone
- end
+ x.language = "de"
+ x.save!
- def test_duplication_with_store_accessors
- x = Hstore.new(language: "fr", timezone: "GMT")
- assert_equal "fr", x.language
- assert_equal "GMT", x.timezone
+ x = Hstore.first
+ assert_equal "de", x.language
+ assert_equal "GMT", x.timezone
+ end
- y = x.dup
- assert_equal "fr", y.language
- assert_equal "GMT", y.timezone
- end
+ def test_duplication_with_store_accessors
+ x = Hstore.new(language: "fr", timezone: "GMT")
+ assert_equal "fr", x.language
+ assert_equal "GMT", x.timezone
- def test_yaml_round_trip_with_store_accessors
- x = Hstore.new(language: "fr", timezone: "GMT")
- assert_equal "fr", x.language
- assert_equal "GMT", x.timezone
+ y = x.dup
+ assert_equal "fr", y.language
+ assert_equal "GMT", y.timezone
+ end
- y = YAML.load(YAML.dump(x))
- assert_equal "fr", y.language
- assert_equal "GMT", y.timezone
- end
+ def test_yaml_round_trip_with_store_accessors
+ x = Hstore.new(language: "fr", timezone: "GMT")
+ assert_equal "fr", x.language
+ assert_equal "GMT", x.timezone
- def test_changes_in_place
- hstore = Hstore.create!(settings: { "one" => "two" })
- hstore.settings["three"] = "four"
- hstore.save!
- hstore.reload
+ y = YAML.load(YAML.dump(x))
+ assert_equal "fr", y.language
+ assert_equal "GMT", y.timezone
+ end
- assert_equal "four", hstore.settings["three"]
- assert_not hstore.changed?
- end
+ def test_changes_in_place
+ hstore = Hstore.create!(settings: { "one" => "two" })
+ hstore.settings["three"] = "four"
+ hstore.save!
+ hstore.reload
- def test_dirty_from_user_equal
- settings = { "alongkey" => "anything", "key" => "value" }
- hstore = Hstore.create!(settings: settings)
+ assert_equal "four", hstore.settings["three"]
+ assert_not_predicate hstore, :changed?
+ end
- hstore.settings = { "key" => "value", "alongkey" => "anything" }
- assert_equal settings, hstore.settings
- refute hstore.changed?
- end
+ def test_dirty_from_user_equal
+ settings = { "alongkey" => "anything", "key" => "value" }
+ hstore = Hstore.create!(settings: settings)
- def test_hstore_dirty_from_database_equal
- settings = { "alongkey" => "anything", "key" => "value" }
- hstore = Hstore.create!(settings: settings)
- hstore.reload
+ hstore.settings = { "key" => "value", "alongkey" => "anything" }
+ assert_equal settings, hstore.settings
+ assert_not_predicate hstore, :changed?
+ end
- assert_equal settings, hstore.settings
- hstore.settings = settings
- refute hstore.changed?
- end
+ def test_hstore_dirty_from_database_equal
+ settings = { "alongkey" => "anything", "key" => "value" }
+ hstore = Hstore.create!(settings: settings)
+ hstore.reload
- def test_gen1
- assert_equal('" "=>""', @type.serialize(" " => ""))
- end
+ assert_equal settings, hstore.settings
+ hstore.settings = settings
+ assert_not_predicate hstore, :changed?
+ end
- def test_gen2
- assert_equal('","=>""', @type.serialize("," => ""))
- end
+ def test_gen1
+ assert_equal('" "=>""', @type.serialize(" " => ""))
+ end
- def test_gen3
- assert_equal('"="=>""', @type.serialize("=" => ""))
- end
+ def test_gen2
+ assert_equal('","=>""', @type.serialize("," => ""))
+ end
- def test_gen4
- assert_equal('">"=>""', @type.serialize(">" => ""))
- end
+ def test_gen3
+ assert_equal('"="=>""', @type.serialize("=" => ""))
+ end
- def test_parse1
- assert_equal({ "a" => nil, "b" => nil, "c" => "NuLl", "null" => "c" }, @type.deserialize('a=>null,b=>NuLl,c=>"NuLl",null=>c'))
- end
+ def test_gen4
+ assert_equal('">"=>""', @type.serialize(">" => ""))
+ end
- def test_parse2
- assert_equal({ " " => " " }, @type.deserialize("\\ =>\\ "))
- end
+ def test_parse1
+ assert_equal({ "a" => nil, "b" => nil, "c" => "NuLl", "null" => "c" }, @type.deserialize('a=>null,b=>NuLl,c=>"NuLl",null=>c'))
+ end
- def test_parse3
- assert_equal({ "=" => ">" }, @type.deserialize("==>>"))
- end
+ def test_parse2
+ assert_equal({ " " => " " }, @type.deserialize("\\ =>\\ "))
+ end
- def test_parse4
- assert_equal({ "=a" => "q=w" }, @type.deserialize('\=a=>q=w'))
- end
+ def test_parse3
+ assert_equal({ "=" => ">" }, @type.deserialize("==>>"))
+ end
- def test_parse5
- assert_equal({ "=a" => "q=w" }, @type.deserialize('"=a"=>q\=w'))
- end
+ def test_parse4
+ assert_equal({ "=a" => "q=w" }, @type.deserialize('\=a=>q=w'))
+ end
- def test_parse6
- assert_equal({ "\"a" => "q>w" }, @type.deserialize('"\"a"=>q>w'))
- end
+ def test_parse5
+ assert_equal({ "=a" => "q=w" }, @type.deserialize('"=a"=>q\=w'))
+ end
- def test_parse7
- assert_equal({ "\"a" => "q\"w" }, @type.deserialize('\"a=>q"w'))
- end
+ def test_parse6
+ assert_equal({ "\"a" => "q>w" }, @type.deserialize('"\"a"=>q>w'))
+ end
- def test_rewrite
- @connection.execute "insert into hstores (tags) VALUES ('1=>2')"
- x = Hstore.first
- x.tags = { '"a\'' => "b" }
- assert x.save!
- end
+ def test_parse7
+ assert_equal({ "\"a" => "q\"w" }, @type.deserialize('\"a=>q"w'))
+ end
- def test_select
- @connection.execute "insert into hstores (tags) VALUES ('1=>2')"
- x = Hstore.first
- assert_equal({ "1" => "2" }, x.tags)
- end
+ def test_rewrite
+ @connection.execute "insert into hstores (tags) VALUES ('1=>2')"
+ x = Hstore.first
+ x.tags = { '"a\'' => "b" }
+ assert x.save!
+ end
- def test_array_cycle
- assert_array_cycle([{ "AA" => "BB", "CC" => "DD" }, { "AA" => nil }])
- end
+ def test_select
+ @connection.execute "insert into hstores (tags) VALUES ('1=>2')"
+ x = Hstore.first
+ assert_equal({ "1" => "2" }, x.tags)
+ end
- def test_array_strings_with_quotes
- assert_array_cycle([{ "this has" => 'some "s that need to be escaped"' }])
- end
+ def test_array_cycle
+ assert_array_cycle([{ "AA" => "BB", "CC" => "DD" }, { "AA" => nil }])
+ end
- def test_array_strings_with_commas
- assert_array_cycle([{ "this,has" => "many,values" }])
- end
+ def test_array_strings_with_quotes
+ assert_array_cycle([{ "this has" => 'some "s that need to be escaped"' }])
+ end
- def test_array_strings_with_array_delimiters
- assert_array_cycle(["{" => "}"])
- end
+ def test_array_strings_with_commas
+ assert_array_cycle([{ "this,has" => "many,values" }])
+ end
- def test_array_strings_with_null_strings
- assert_array_cycle([{ "NULL" => "NULL" }])
- end
+ def test_array_strings_with_array_delimiters
+ assert_array_cycle(["{" => "}"])
+ end
- def test_contains_nils
- assert_array_cycle([{ "NULL" => nil }])
- end
+ def test_array_strings_with_null_strings
+ assert_array_cycle([{ "NULL" => "NULL" }])
+ end
- def test_select_multikey
- @connection.execute "insert into hstores (tags) VALUES ('1=>2,2=>3')"
- x = Hstore.first
- assert_equal({ "1" => "2", "2" => "3" }, x.tags)
- end
+ def test_contains_nils
+ assert_array_cycle([{ "NULL" => nil }])
+ end
- def test_create
- assert_cycle("a" => "b", "1" => "2")
- end
+ def test_select_multikey
+ @connection.execute "insert into hstores (tags) VALUES ('1=>2,2=>3')"
+ x = Hstore.first
+ assert_equal({ "1" => "2", "2" => "3" }, x.tags)
+ end
- def test_nil
- assert_cycle("a" => nil)
- end
+ def test_create
+ assert_cycle("a" => "b", "1" => "2")
+ end
- def test_quotes
- assert_cycle("a" => 'b"ar', '1"foo' => "2")
- end
+ def test_nil
+ assert_cycle("a" => nil)
+ end
- def test_whitespace
- assert_cycle("a b" => "b ar", '1"foo' => "2")
- end
+ def test_quotes
+ assert_cycle("a" => 'b"ar', '1"foo' => "2")
+ end
- def test_backslash
- assert_cycle('a\\b' => 'b\\ar', '1"foo' => "2")
- end
+ def test_whitespace
+ assert_cycle("a b" => "b ar", '1"foo' => "2")
+ end
- def test_comma
- assert_cycle("a, b" => "bar", '1"foo' => "2")
- end
+ def test_backslash
+ assert_cycle('a\\b' => 'b\\ar', '1"foo' => "2")
+ end
- def test_arrow
- assert_cycle("a=>b" => "bar", '1"foo' => "2")
- end
+ def test_comma
+ assert_cycle("a, b" => "bar", '1"foo' => "2")
+ end
- def test_quoting_special_characters
- assert_cycle("ca" => "cà", "ac" => "àc")
- end
+ def test_arrow
+ assert_cycle("a=>b" => "bar", '1"foo' => "2")
+ end
- def test_multiline
- assert_cycle("a\nb" => "c\nd")
- end
+ def test_quoting_special_characters
+ assert_cycle("ca" => "cà", "ac" => "àc")
+ end
- class TagCollection
- def initialize(hash); @hash = hash end
- def to_hash; @hash end
- def self.load(hash); new(hash) end
- def self.dump(object); object.to_hash end
- end
+ def test_multiline
+ assert_cycle("a\nb" => "c\nd")
+ end
- class HstoreWithSerialize < Hstore
- serialize :tags, TagCollection
- end
+ class TagCollection
+ def initialize(hash); @hash = hash end
+ def to_hash; @hash end
+ def self.load(hash); new(hash) end
+ def self.dump(object); object.to_hash end
+ end
- def test_hstore_with_serialized_attributes
- HstoreWithSerialize.create! tags: TagCollection.new("one" => "two")
- record = HstoreWithSerialize.first
- assert_instance_of TagCollection, record.tags
- assert_equal({ "one" => "two" }, record.tags.to_hash)
- record.tags = TagCollection.new("three" => "four")
- record.save!
- assert_equal({ "three" => "four" }, HstoreWithSerialize.first.tags.to_hash)
- end
+ class HstoreWithSerialize < Hstore
+ serialize :tags, TagCollection
+ end
- def test_clone_hstore_with_serialized_attributes
- HstoreWithSerialize.create! tags: TagCollection.new("one" => "two")
- record = HstoreWithSerialize.first
- dupe = record.dup
- assert_equal({ "one" => "two" }, dupe.tags.to_hash)
- end
+ def test_hstore_with_serialized_attributes
+ HstoreWithSerialize.create! tags: TagCollection.new("one" => "two")
+ record = HstoreWithSerialize.first
+ assert_instance_of TagCollection, record.tags
+ assert_equal({ "one" => "two" }, record.tags.to_hash)
+ record.tags = TagCollection.new("three" => "four")
+ record.save!
+ assert_equal({ "three" => "four" }, HstoreWithSerialize.first.tags.to_hash)
+ end
- def test_schema_dump_with_shorthand
- output = dump_table_schema("hstores")
- assert_match %r[t\.hstore "tags",\s+default: {}], output
- end
+ def test_clone_hstore_with_serialized_attributes
+ HstoreWithSerialize.create! tags: TagCollection.new("one" => "two")
+ record = HstoreWithSerialize.first
+ dupe = record.dup
+ assert_equal({ "one" => "two" }, dupe.tags.to_hash)
+ end
+
+ def test_schema_dump_with_shorthand
+ output = dump_table_schema("hstores")
+ assert_match %r[t\.hstore "tags",\s+default: {}], output
+ end
+
+ def test_supports_to_unsafe_h_values
+ assert_equal("\"hi\"=>\"hi\"", @type.serialize(FakeParameters.new))
+ end
- def test_supports_to_unsafe_h_values
- assert_equal("\"hi\"=>\"hi\"", @type.serialize(FakeParameters.new))
+ private
+ def assert_array_cycle(array)
+ # test creation
+ x = Hstore.create!(payload: array)
+ x.reload
+ assert_equal(array, x.payload)
+
+ # test updating
+ x = Hstore.create!(payload: [])
+ x.payload = array
+ x.save!
+ x.reload
+ assert_equal(array, x.payload)
end
- private
- def assert_array_cycle(array)
- # test creation
- x = Hstore.create!(payload: array)
- x.reload
- assert_equal(array, x.payload)
-
- # test updating
- x = Hstore.create!(payload: [])
- x.payload = array
- x.save!
- x.reload
- assert_equal(array, x.payload)
- end
+ def assert_cycle(hash)
+ # test creation
+ x = Hstore.create!(tags: hash)
+ x.reload
+ assert_equal(hash, x.tags)
- def assert_cycle(hash)
- # test creation
- x = Hstore.create!(tags: hash)
- x.reload
- assert_equal(hash, x.tags)
-
- # test updating
- x = Hstore.create!(tags: {})
- x.tags = hash
- x.save!
- x.reload
- assert_equal(hash, x.tags)
- end
- end
+ # test updating
+ x = Hstore.create!(tags: {})
+ x.tags = hash
+ x.save!
+ x.reload
+ assert_equal(hash, x.tags)
+ end
end
diff --git a/activerecord/test/cases/adapters/postgresql/infinity_test.rb b/activerecord/test/cases/adapters/postgresql/infinity_test.rb
index b9e177e6ec..0b18c0c9d7 100644
--- a/activerecord/test/cases/adapters/postgresql/infinity_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/infinity_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "cases/helper"
class PostgresqlInfinityTest < ActiveRecord::PostgreSQLTestCase
diff --git a/activerecord/test/cases/adapters/postgresql/integer_test.rb b/activerecord/test/cases/adapters/postgresql/integer_test.rb
index b4e55964b9..3e45b057ff 100644
--- a/activerecord/test/cases/adapters/postgresql/integer_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/integer_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "cases/helper"
require "active_support/core_ext/numeric/bytes"
diff --git a/activerecord/test/cases/adapters/postgresql/json_test.rb b/activerecord/test/cases/adapters/postgresql/json_test.rb
index 6aa60630c2..ee08841eb3 100644
--- a/activerecord/test/cases/adapters/postgresql/json_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/json_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "cases/helper"
require "cases/json_shared_test_cases"
@@ -19,8 +21,8 @@ module PostgresqlJSONSharedTestCases
@connection.add_column "json_data_type", "permissions", column_type, default: { "users": "read", "posts": ["read", "write"] }
klass.reset_column_information
- assert_equal({ "users" => "read", "posts" => ["read", "write"] }, JsonDataType.column_defaults["permissions"])
- assert_equal({ "users" => "read", "posts" => ["read", "write"] }, JsonDataType.new.permissions)
+ assert_equal({ "users" => "read", "posts" => ["read", "write"] }, klass.column_defaults["permissions"])
+ assert_equal({ "users" => "read", "posts" => ["read", "write"] }, klass.new.permissions)
end
def test_deserialize_with_array
diff --git a/activerecord/test/cases/adapters/postgresql/ltree_test.rb b/activerecord/test/cases/adapters/postgresql/ltree_test.rb
index 2b5ac1cac6..8349ee6ee2 100644
--- a/activerecord/test/cases/adapters/postgresql/ltree_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/ltree_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "cases/helper"
require "support/schema_dumping_helper"
@@ -29,10 +31,10 @@ class PostgresqlLtreeTest < ActiveRecord::PostgreSQLTestCase
column = Ltree.columns_hash["path"]
assert_equal :ltree, column.type
assert_equal "ltree", column.sql_type
- assert_not column.array?
+ assert_not_predicate column, :array?
type = Ltree.type_for_attribute("path")
- assert_not type.binary?
+ assert_not_predicate type, :binary?
end
def test_write
diff --git a/activerecord/test/cases/adapters/postgresql/money_test.rb b/activerecord/test/cases/adapters/postgresql/money_test.rb
index 1b5d8362af..be3590e8dd 100644
--- a/activerecord/test/cases/adapters/postgresql/money_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/money_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "cases/helper"
require "support/schema_dumping_helper"
@@ -24,15 +26,15 @@ class PostgresqlMoneyTest < ActiveRecord::PostgreSQLTestCase
assert_equal :money, column.type
assert_equal "money", column.sql_type
assert_equal 2, column.scale
- assert_not column.array?
+ assert_not_predicate column, :array?
type = PostgresqlMoney.type_for_attribute("wealth")
- assert_not type.binary?
+ assert_not_predicate type, :binary?
end
def test_default
- assert_equal BigDecimal.new("150.55"), PostgresqlMoney.column_defaults["depth"]
- assert_equal BigDecimal.new("150.55"), PostgresqlMoney.new.depth
+ assert_equal BigDecimal("150.55"), PostgresqlMoney.column_defaults["depth"]
+ assert_equal BigDecimal("150.55"), PostgresqlMoney.new.depth
end
def test_money_values
@@ -47,10 +49,10 @@ class PostgresqlMoneyTest < ActiveRecord::PostgreSQLTestCase
def test_money_type_cast
type = PostgresqlMoney.type_for_attribute("wealth")
- assert_equal(12345678.12, type.cast("$12,345,678.12"))
- assert_equal(12345678.12, type.cast("$12.345.678,12"))
- assert_equal(-1.15, type.cast("-$1.15"))
- assert_equal(-2.25, type.cast("($2.25)"))
+ assert_equal(12345678.12, type.cast("$12,345,678.12".dup))
+ assert_equal(12345678.12, type.cast("$12.345.678,12".dup))
+ assert_equal(-1.15, type.cast("-$1.15".dup))
+ assert_equal(-2.25, type.cast("($2.25)".dup))
end
def test_schema_dumping
@@ -60,10 +62,10 @@ class PostgresqlMoneyTest < ActiveRecord::PostgreSQLTestCase
end
def test_create_and_update_money
- money = PostgresqlMoney.create(wealth: "987.65")
+ money = PostgresqlMoney.create(wealth: "987.65".dup)
assert_equal 987.65, money.wealth
- new_value = BigDecimal.new("123.45")
+ new_value = BigDecimal("123.45")
money.wealth = new_value
money.save!
money.reload
diff --git a/activerecord/test/cases/adapters/postgresql/network_test.rb b/activerecord/test/cases/adapters/postgresql/network_test.rb
index a33b0ef8a7..736570451b 100644
--- a/activerecord/test/cases/adapters/postgresql/network_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/network_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "cases/helper"
require "support/schema_dumping_helper"
@@ -22,30 +24,30 @@ class PostgresqlNetworkTest < ActiveRecord::PostgreSQLTestCase
column = PostgresqlNetworkAddress.columns_hash["cidr_address"]
assert_equal :cidr, column.type
assert_equal "cidr", column.sql_type
- assert_not column.array?
+ assert_not_predicate column, :array?
type = PostgresqlNetworkAddress.type_for_attribute("cidr_address")
- assert_not type.binary?
+ assert_not_predicate type, :binary?
end
def test_inet_column
column = PostgresqlNetworkAddress.columns_hash["inet_address"]
assert_equal :inet, column.type
assert_equal "inet", column.sql_type
- assert_not column.array?
+ assert_not_predicate column, :array?
type = PostgresqlNetworkAddress.type_for_attribute("inet_address")
- assert_not type.binary?
+ assert_not_predicate type, :binary?
end
def test_macaddr_column
column = PostgresqlNetworkAddress.columns_hash["mac_address"]
assert_equal :macaddr, column.type
assert_equal "macaddr", column.sql_type
- assert_not column.array?
+ assert_not_predicate column, :array?
type = PostgresqlNetworkAddress.type_for_attribute("mac_address")
- assert_not type.binary?
+ assert_not_predicate type, :binary?
end
def test_network_types
diff --git a/activerecord/test/cases/adapters/postgresql/numbers_test.rb b/activerecord/test/cases/adapters/postgresql/numbers_test.rb
index bfb2b7c27a..b53a12254d 100644
--- a/activerecord/test/cases/adapters/postgresql/numbers_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/numbers_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "cases/helper"
class PostgresqlNumberTest < ActiveRecord::PostgreSQLTestCase
diff --git a/activerecord/test/cases/adapters/postgresql/postgresql_adapter_test.rb b/activerecord/test/cases/adapters/postgresql/postgresql_adapter_test.rb
index 76e0ad60fe..1951230c8a 100644
--- a/activerecord/test/cases/adapters/postgresql/postgresql_adapter_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/postgresql_adapter_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "cases/helper"
require "support/ddl_helper"
require "support/connection_helper"
@@ -202,8 +204,8 @@ module ActiveRecord
string = @connection.quote("foo")
@connection.exec_query("INSERT INTO ex (id, data) VALUES (1, #{string})")
- binds = [bind_attribute("id", 1)]
- result = @connection.exec_query("SELECT id, data FROM ex WHERE id = $1", nil, binds)
+ bind = Relation::QueryAttribute.new("id", 1, Type::Value.new)
+ result = @connection.exec_query("SELECT id, data FROM ex WHERE id = $1", nil, [bind])
assert_equal 1, result.rows.length
assert_equal 2, result.columns.length
@@ -217,8 +219,8 @@ module ActiveRecord
string = @connection.quote("foo")
@connection.exec_query("INSERT INTO ex (id, data) VALUES (1, #{string})")
- binds = [bind_attribute("id", "1-fuu", Type::Integer.new)]
- result = @connection.exec_query("SELECT id, data FROM ex WHERE id = $1", nil, binds)
+ bind = Relation::QueryAttribute.new("id", "1-fuu", Type::Integer.new)
+ result = @connection.exec_query("SELECT id, data FROM ex WHERE id = $1", nil, [bind])
assert_equal 1, result.rows.length
assert_equal 2, result.columns.length
@@ -246,12 +248,12 @@ module ActiveRecord
def test_index_with_opclass
with_example_table do
- @connection.add_index "ex", "data varchar_pattern_ops"
- index = @connection.indexes("ex").find { |idx| idx.name == "index_ex_on_data_varchar_pattern_ops" }
- assert_equal "data varchar_pattern_ops", index.columns
+ @connection.add_index "ex", "data", opclass: "varchar_pattern_ops"
+ index = @connection.indexes("ex").find { |idx| idx.name == "index_ex_on_data" }
+ assert_equal ["data"], index.columns
- @connection.remove_index "ex", "data varchar_pattern_ops"
- assert_not @connection.indexes("ex").find { |idx| idx.name == "index_ex_on_data_varchar_pattern_ops" }
+ @connection.remove_index "ex", "data"
+ assert_not @connection.indexes("ex").find { |idx| idx.name == "index_ex_on_data" }
end
end
@@ -325,15 +327,18 @@ module ActiveRecord
end
def test_only_reload_type_map_once_for_every_unrecognized_type
+ reset_connection
+ connection = ActiveRecord::Base.connection
+
silence_warnings do
assert_queries 2, ignore_none: true do
- @connection.select_all "select 'pg_catalog.pg_class'::regclass"
+ connection.select_all "select 'pg_catalog.pg_class'::regclass"
end
assert_queries 1, ignore_none: true do
- @connection.select_all "select 'pg_catalog.pg_class'::regclass"
+ connection.select_all "select 'pg_catalog.pg_class'::regclass"
end
assert_queries 2, ignore_none: true do
- @connection.select_all "SELECT NULL::anyarray"
+ connection.select_all "SELECT NULL::anyarray"
end
end
ensure
@@ -341,10 +346,13 @@ module ActiveRecord
end
def test_only_warn_on_first_encounter_of_unrecognized_oid
+ reset_connection
+ connection = ActiveRecord::Base.connection
+
warning = capture(:stderr) {
- @connection.select_all "select 'pg_catalog.pg_class'::regclass"
- @connection.select_all "select 'pg_catalog.pg_class'::regclass"
- @connection.select_all "select 'pg_catalog.pg_class'::regclass"
+ connection.select_all "select 'pg_catalog.pg_class'::regclass"
+ connection.select_all "select 'pg_catalog.pg_class'::regclass"
+ connection.select_all "select 'pg_catalog.pg_class'::regclass"
}
assert_match(/\Aunknown OID \d+: failed to recognize type of 'regclass'\. It will be treated as String\.\n\z/, warning)
ensure
diff --git a/activerecord/test/cases/adapters/postgresql/prepared_statements_disabled_test.rb b/activerecord/test/cases/adapters/postgresql/prepared_statements_disabled_test.rb
index 8c62690866..f7478b50c3 100644
--- a/activerecord/test/cases/adapters/postgresql/prepared_statements_disabled_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/prepared_statements_disabled_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "cases/helper"
require "models/computer"
require "models/developer"
diff --git a/activerecord/test/cases/adapters/postgresql/quoting_test.rb b/activerecord/test/cases/adapters/postgresql/quoting_test.rb
index a1e966b915..d50dc49276 100644
--- a/activerecord/test/cases/adapters/postgresql/quoting_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/quoting_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "cases/helper"
module ActiveRecord
@@ -9,11 +11,11 @@ module ActiveRecord
end
def test_type_cast_true
- assert_equal "t", @conn.type_cast(true)
+ assert_equal true, @conn.type_cast(true)
end
def test_type_cast_false
- assert_equal "f", @conn.type_cast(false)
+ assert_equal false, @conn.type_cast(false)
end
def test_quote_float_nan
diff --git a/activerecord/test/cases/adapters/postgresql/range_test.rb b/activerecord/test/cases/adapters/postgresql/range_test.rb
index f411884dfd..261c24634e 100644
--- a/activerecord/test/cases/adapters/postgresql/range_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/range_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "cases/helper"
require "support/connection_helper"
@@ -132,10 +134,10 @@ _SQL
end
def test_numrange_values
- assert_equal BigDecimal.new("0.1")..BigDecimal.new("0.2"), @first_range.num_range
- assert_equal BigDecimal.new("0.1")...BigDecimal.new("0.2"), @second_range.num_range
- assert_equal BigDecimal.new("0.1")...BigDecimal.new("Infinity"), @third_range.num_range
- assert_equal BigDecimal.new("-Infinity")...BigDecimal.new("Infinity"), @fourth_range.num_range
+ assert_equal BigDecimal("0.1")..BigDecimal("0.2"), @first_range.num_range
+ assert_equal BigDecimal("0.1")...BigDecimal("0.2"), @second_range.num_range
+ assert_equal BigDecimal("0.1")...BigDecimal("Infinity"), @third_range.num_range
+ assert_equal BigDecimal("-Infinity")...BigDecimal("Infinity"), @fourth_range.num_range
assert_nil @empty_range.num_range
end
@@ -230,16 +232,67 @@ _SQL
end
end
+ def test_create_tstzrange_preserve_usec
+ tstzrange = Time.parse("2010-01-01 14:30:00.670277 +0100")...Time.parse("2011-02-02 14:30:00.745125 CDT")
+ round_trip(@new_range, :tstz_range, tstzrange)
+ assert_equal @new_range.tstz_range, tstzrange
+ assert_equal @new_range.tstz_range, Time.parse("2010-01-01 13:30:00.670277 UTC")...Time.parse("2011-02-02 19:30:00.745125 UTC")
+ end
+
+ def test_update_tstzrange_preserve_usec
+ assert_equal_round_trip(@first_range, :tstz_range,
+ Time.parse("2010-01-01 14:30:00.245124 CDT")...Time.parse("2011-02-02 14:30:00.451274 CET"))
+ assert_nil_round_trip(@first_range, :tstz_range,
+ Time.parse("2010-01-01 14:30:00.245124 +0100")...Time.parse("2010-01-01 13:30:00.245124 +0000"))
+ end
+
+ def test_create_tsrange_preseve_usec
+ tz = ::ActiveRecord::Base.default_timezone
+ assert_equal_round_trip(@new_range, :ts_range,
+ Time.send(tz, 2010, 1, 1, 14, 30, 0, 125435)...Time.send(tz, 2011, 2, 2, 14, 30, 0, 225435))
+ end
+
+ def test_update_tsrange_preserve_usec
+ tz = ::ActiveRecord::Base.default_timezone
+ assert_equal_round_trip(@first_range, :ts_range,
+ Time.send(tz, 2010, 1, 1, 14, 30, 0, 142432)...Time.send(tz, 2011, 2, 2, 14, 30, 0, 224242))
+ assert_nil_round_trip(@first_range, :ts_range,
+ Time.send(tz, 2010, 1, 1, 14, 30, 0, 142432)...Time.send(tz, 2010, 1, 1, 14, 30, 0, 142432))
+ end
+
+ def test_timezone_awareness_tsrange_preserve_usec
+ tz = "Pacific Time (US & Canada)"
+
+ in_time_zone tz do
+ PostgresqlRange.reset_column_information
+ time_string = "2017-09-26 07:30:59.132451 -0700"
+ time = Time.zone.parse(time_string)
+ assert time.usec > 0
+
+ record = PostgresqlRange.new(ts_range: time_string..time_string)
+ assert_equal time..time, record.ts_range
+ assert_equal ActiveSupport::TimeZone[tz], record.ts_range.begin.time_zone
+ assert_equal time.usec, record.ts_range.begin.usec
+
+ record.save!
+ record.reload
+
+ assert_equal time..time, record.ts_range
+ assert_equal ActiveSupport::TimeZone[tz], record.ts_range.begin.time_zone
+ assert_equal time.usec, record.ts_range.begin.usec
+ end
+ end
+
def test_create_numrange
assert_equal_round_trip(@new_range, :num_range,
- BigDecimal.new("0.5")...BigDecimal.new("1"))
+ BigDecimal("0.5")...BigDecimal("1"))
end
def test_update_numrange
assert_equal_round_trip(@first_range, :num_range,
- BigDecimal.new("0.5")...BigDecimal.new("1"))
+ BigDecimal("0.5")...BigDecimal("1"))
assert_nil_round_trip(@first_range, :num_range,
- BigDecimal.new("0.5")...BigDecimal.new("0.5"))
+ BigDecimal("0.5")...BigDecimal("0.5"))
end
def test_create_daterange
@@ -305,6 +358,18 @@ _SQL
end
end
+ def test_infinity_values
+ PostgresqlRange.create!(int4_range: 1..Float::INFINITY,
+ int8_range: -Float::INFINITY..0,
+ float_range: -Float::INFINITY..Float::INFINITY)
+
+ record = PostgresqlRange.first
+
+ assert_equal(1...Float::INFINITY, record.int4_range)
+ assert_equal(-Float::INFINITY...1, record.int8_range)
+ assert_equal(-Float::INFINITY...Float::INFINITY, record.float_range)
+ end
+
private
def assert_equal_round_trip(range, attribute, value)
round_trip(range, attribute, value)
diff --git a/activerecord/test/cases/adapters/postgresql/referential_integrity_test.rb b/activerecord/test/cases/adapters/postgresql/referential_integrity_test.rb
index 0ff04bfa27..0bcc214c24 100644
--- a/activerecord/test/cases/adapters/postgresql/referential_integrity_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/referential_integrity_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "cases/helper"
require "support/connection_helper"
diff --git a/activerecord/test/cases/adapters/postgresql/rename_table_test.rb b/activerecord/test/cases/adapters/postgresql/rename_table_test.rb
index e9e7f717ac..100d247113 100644
--- a/activerecord/test/cases/adapters/postgresql/rename_table_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/rename_table_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "cases/helper"
class PostgresqlRenameTableTest < ActiveRecord::PostgreSQLTestCase
diff --git a/activerecord/test/cases/adapters/postgresql/schema_authorization_test.rb b/activerecord/test/cases/adapters/postgresql/schema_authorization_test.rb
index f86a76e08a..fcb0aec81b 100644
--- a/activerecord/test/cases/adapters/postgresql/schema_authorization_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/schema_authorization_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "cases/helper"
class SchemaThing < ActiveRecord::Base
@@ -68,7 +70,7 @@ class SchemaAuthorizationTest < ActiveRecord::PostgreSQLTestCase
USERS.each do |u|
@connection.clear_cache!
set_session_auth u
- assert_equal u, @connection.select_value("SELECT name FROM #{TABLE_NAME} WHERE id = $1", "SQL", [bind_attribute("id", 1)])
+ assert_equal u, @connection.select_value("SELECT name FROM #{TABLE_NAME} WHERE id = $1", "SQL", [bind_param(1)])
set_session_auth
end
end
@@ -101,4 +103,8 @@ class SchemaAuthorizationTest < ActiveRecord::PostgreSQLTestCase
def set_session_auth(auth = nil)
@connection.session_auth = auth || "default"
end
+
+ def bind_param(value)
+ ActiveRecord::Relation::QueryAttribute.new(nil, value, ActiveRecord::Type::Value.new)
+ end
end
diff --git a/activerecord/test/cases/adapters/postgresql/schema_test.rb b/activerecord/test/cases/adapters/postgresql/schema_test.rb
index f6b957476b..7ad03c194f 100644
--- a/activerecord/test/cases/adapters/postgresql/schema_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/schema_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "cases/helper"
require "models/default"
require "support/schema_dumping_helper"
@@ -169,17 +171,17 @@ class SchemaTest < ActiveRecord::PostgreSQLTestCase
def test_raise_wrapped_exception_on_bad_prepare
assert_raises(ActiveRecord::StatementInvalid) do
- @connection.exec_query "select * from developers where id = ?", "sql", [bind_attribute("id", 1)]
+ @connection.exec_query "select * from developers where id = ?", "sql", [bind_param(1)]
end
end
if ActiveRecord::Base.connection.prepared_statements
def test_schema_change_with_prepared_stmt
altered = false
- @connection.exec_query "select * from developers where id = $1", "sql", [bind_attribute("id", 1)]
+ @connection.exec_query "select * from developers where id = $1", "sql", [bind_param(1)]
@connection.exec_query "alter table developers add column zomg int", "sql", []
altered = true
- @connection.exec_query "select * from developers where id = $1", "sql", [bind_attribute("id", 1)]
+ @connection.exec_query "select * from developers where id = $1", "sql", [bind_param(1)]
ensure
# We are not using DROP COLUMN IF EXISTS because that syntax is only
# supported by pg 9.X
@@ -457,7 +459,7 @@ class SchemaTest < ActiveRecord::PostgreSQLTestCase
assert_equal :btree, index_d.using
assert_equal :gin, index_e.using
- assert_equal :desc, index_d.orders[INDEX_D_COLUMN]
+ assert_equal :desc, index_d.orders
end
end
@@ -467,6 +469,10 @@ class SchemaTest < ActiveRecord::PostgreSQLTestCase
assert_equal this_index_column, this_index.columns[0]
assert_equal this_index_name, this_index.name
end
+
+ def bind_param(value)
+ ActiveRecord::Relation::QueryAttribute.new(nil, value, ActiveRecord::Type::Value.new)
+ end
end
class SchemaForeignKeyTest < ActiveRecord::PostgreSQLTestCase
@@ -494,6 +500,66 @@ class SchemaForeignKeyTest < ActiveRecord::PostgreSQLTestCase
end
end
+class SchemaIndexOpclassTest < ActiveRecord::PostgreSQLTestCase
+ include SchemaDumpingHelper
+
+ setup do
+ @connection = ActiveRecord::Base.connection
+ @connection.create_table "trains" do |t|
+ t.string :name
+ t.text :description
+ end
+ end
+
+ teardown do
+ @connection.drop_table "trains", if_exists: true
+ end
+
+ def test_string_opclass_is_dumped
+ @connection.execute "CREATE INDEX trains_name_and_description ON trains USING btree(name text_pattern_ops, description text_pattern_ops)"
+
+ output = dump_table_schema "trains"
+
+ assert_match(/opclass: :text_pattern_ops/, output)
+ end
+
+ def test_non_default_opclass_is_dumped
+ @connection.execute "CREATE INDEX trains_name_and_description ON trains USING btree(name, description text_pattern_ops)"
+
+ output = dump_table_schema "trains"
+
+ assert_match(/opclass: \{ description: :text_pattern_ops \}/, output)
+ end
+end
+
+class SchemaIndexNullsOrderTest < ActiveRecord::PostgreSQLTestCase
+ include SchemaDumpingHelper
+
+ setup do
+ @connection = ActiveRecord::Base.connection
+ @connection.create_table "trains" do |t|
+ t.string :name
+ t.text :description
+ end
+ end
+
+ teardown do
+ @connection.drop_table "trains", if_exists: true
+ end
+
+ def test_nulls_order_is_dumped
+ @connection.execute "CREATE INDEX trains_name_and_description ON trains USING btree(name NULLS FIRST, description)"
+ output = dump_table_schema "trains"
+ assert_match(/order: \{ name: "NULLS FIRST" \}/, output)
+ end
+
+ def test_non_default_order_with_nulls_is_dumped
+ @connection.execute "CREATE INDEX trains_name_and_desc ON trains USING btree(name DESC NULLS LAST, description)"
+ output = dump_table_schema "trains"
+ assert_match(/order: \{ name: "DESC NULLS LAST" \}/, output)
+ end
+end
+
class DefaultsUsingMultipleSchemasAndDomainTest < ActiveRecord::PostgreSQLTestCase
setup do
@connection = ActiveRecord::Base.connection
@@ -528,7 +594,7 @@ class DefaultsUsingMultipleSchemasAndDomainTest < ActiveRecord::PostgreSQLTestCa
end
def test_decimal_defaults_in_new_schema_when_overriding_domain
- assert_equal BigDecimal.new("3.14159265358979323846"), Default.new.decimal_col, "Default of decimal column was not correctly parsed"
+ assert_equal BigDecimal("3.14159265358979323846"), Default.new.decimal_col, "Default of decimal column was not correctly parsed"
end
def test_bpchar_defaults_in_new_schema_when_overriding_domain
diff --git a/activerecord/test/cases/adapters/postgresql/serial_test.rb b/activerecord/test/cases/adapters/postgresql/serial_test.rb
index d711b3b729..83ea86be6d 100644
--- a/activerecord/test/cases/adapters/postgresql/serial_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/serial_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "cases/helper"
require "support/schema_dumping_helper"
@@ -22,14 +24,14 @@ class PostgresqlSerialTest < ActiveRecord::PostgreSQLTestCase
column = PostgresqlSerial.columns_hash["seq"]
assert_equal :integer, column.type
assert_equal "integer", column.sql_type
- assert column.serial?
+ assert_predicate column, :serial?
end
def test_not_serial_column
column = PostgresqlSerial.columns_hash["serials_id"]
assert_equal :integer, column.type
assert_equal "integer", column.sql_type
- assert_not column.serial?
+ assert_not_predicate column, :serial?
end
def test_schema_dump_with_shorthand
@@ -64,14 +66,14 @@ class PostgresqlBigSerialTest < ActiveRecord::PostgreSQLTestCase
column = PostgresqlBigSerial.columns_hash["seq"]
assert_equal :integer, column.type
assert_equal "bigint", column.sql_type
- assert column.serial?
+ assert_predicate column, :serial?
end
def test_not_bigserial_column
column = PostgresqlBigSerial.columns_hash["serials_id"]
assert_equal :integer, column.type
assert_equal "bigint", column.sql_type
- assert_not column.serial?
+ assert_not_predicate column, :serial?
end
def test_schema_dump_with_shorthand
@@ -84,3 +86,71 @@ class PostgresqlBigSerialTest < ActiveRecord::PostgreSQLTestCase
assert_match %r{t\.bigint\s+"serials_id",\s+default: -> \{ "nextval\('postgresql_big_serials_id_seq'::regclass\)" \}$}, output
end
end
+
+module SequenceNameDetectionTestCases
+ class CollidedSequenceNameTest < ActiveRecord::PostgreSQLTestCase
+ include SchemaDumpingHelper
+
+ def setup
+ @connection = ActiveRecord::Base.connection
+ @connection.create_table :foo_bar, force: true do |t|
+ t.serial :baz_id
+ end
+ @connection.create_table :foo, force: true do |t|
+ t.serial :bar_id
+ t.bigserial :bar_baz_id
+ end
+ end
+
+ def teardown
+ @connection.drop_table :foo_bar, if_exists: true
+ @connection.drop_table :foo, if_exists: true
+ end
+
+ def test_serial_columns
+ columns = @connection.columns(:foo)
+ columns.each do |column|
+ assert_equal :integer, column.type
+ assert_predicate column, :serial?
+ end
+ end
+
+ def test_schema_dump_with_collided_sequence_name
+ output = dump_table_schema "foo"
+ assert_match %r{t\.serial\s+"bar_id",\s+null: false$}, output
+ assert_match %r{t\.bigserial\s+"bar_baz_id",\s+null: false$}, output
+ end
+ end
+
+ class LongerSequenceNameDetectionTest < ActiveRecord::PostgreSQLTestCase
+ include SchemaDumpingHelper
+
+ def setup
+ @table_name = "long_table_name_to_test_sequence_name_detection_for_serial_cols"
+ @connection = ActiveRecord::Base.connection
+ @connection.create_table @table_name, force: true do |t|
+ t.serial :seq
+ t.bigserial :bigseq
+ end
+ end
+
+ def teardown
+ @connection.drop_table @table_name, if_exists: true
+ end
+
+ def test_serial_columns
+ columns = @connection.columns(@table_name)
+ columns.each do |column|
+ assert_equal :integer, column.type
+ assert_predicate column, :serial?
+ end
+ end
+
+ def test_schema_dump_with_long_table_name
+ output = dump_table_schema @table_name
+ assert_match %r{create_table "#{@table_name}", force: :cascade}, output
+ assert_match %r{t\.serial\s+"seq",\s+null: false$}, output
+ assert_match %r{t\.bigserial\s+"bigseq",\s+null: false$}, output
+ end
+ end
+end
diff --git a/activerecord/test/cases/adapters/postgresql/statement_pool_test.rb b/activerecord/test/cases/adapters/postgresql/statement_pool_test.rb
index 146b619a4b..fef4b02b04 100644
--- a/activerecord/test/cases/adapters/postgresql/statement_pool_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/statement_pool_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "cases/helper"
module ActiveRecord
@@ -21,7 +23,7 @@ module ActiveRecord
assert_equal "bar", cache["foo"]
pid = fork {
- lookup = cache["foo"];
+ lookup = cache["foo"]
exit!(!lookup)
}
diff --git a/activerecord/test/cases/adapters/postgresql/timestamp_test.rb b/activerecord/test/cases/adapters/postgresql/timestamp_test.rb
index 962450aada..b7f213efc8 100644
--- a/activerecord/test/cases/adapters/postgresql/timestamp_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/timestamp_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "cases/helper"
require "models/developer"
require "models/topic"
diff --git a/activerecord/test/cases/adapters/postgresql/transaction_test.rb b/activerecord/test/cases/adapters/postgresql/transaction_test.rb
index 9b42d0383d..9821b103df 100644
--- a/activerecord/test/cases/adapters/postgresql/transaction_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/transaction_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "cases/helper"
require "support/connection_helper"
require "concurrent/atomic/cyclic_barrier"
@@ -12,6 +14,7 @@ module ActiveRecord
setup do
@abort, Thread.abort_on_exception = Thread.abort_on_exception, false
+ Thread.report_on_exception, @original_report_on_exception = false, Thread.report_on_exception if Thread.respond_to?(:report_on_exception)
@connection = ActiveRecord::Base.connection
@@ -29,6 +32,7 @@ module ActiveRecord
@connection.drop_table "samples", if_exists: true
Thread.abort_on_exception = @abort
+ Thread.report_on_exception = @original_report_on_exception if Thread.respond_to?(:report_on_exception)
end
test "raises SerializationFailure when a serialization failure occurs" do
@@ -89,6 +93,90 @@ module ActiveRecord
end
end
+ test "raises LockWaitTimeout when lock wait timeout exceeded" do
+ skip unless ActiveRecord::Base.connection.postgresql_version >= 90300
+ assert_raises(ActiveRecord::LockWaitTimeout) do
+ s = Sample.create!(value: 1)
+ latch1 = Concurrent::CountDownLatch.new
+ latch2 = Concurrent::CountDownLatch.new
+
+ thread = Thread.new do
+ Sample.transaction do
+ Sample.lock.find(s.id)
+ latch1.count_down
+ latch2.wait
+ end
+ end
+
+ begin
+ Sample.transaction do
+ latch1.wait
+ Sample.connection.execute("SET lock_timeout = 1")
+ Sample.lock.find(s.id)
+ end
+ ensure
+ Sample.connection.execute("SET lock_timeout = DEFAULT")
+ latch2.count_down
+ thread.join
+ end
+ end
+ end
+
+ test "raises QueryCanceled when statement timeout exceeded" do
+ assert_raises(ActiveRecord::QueryCanceled) do
+ s = Sample.create!(value: 1)
+ latch1 = Concurrent::CountDownLatch.new
+ latch2 = Concurrent::CountDownLatch.new
+
+ thread = Thread.new do
+ Sample.transaction do
+ Sample.lock.find(s.id)
+ latch1.count_down
+ latch2.wait
+ end
+ end
+
+ begin
+ Sample.transaction do
+ latch1.wait
+ Sample.connection.execute("SET statement_timeout = 1")
+ Sample.lock.find(s.id)
+ end
+ ensure
+ Sample.connection.execute("SET statement_timeout = DEFAULT")
+ latch2.count_down
+ thread.join
+ end
+ end
+ end
+
+ test "raises QueryCanceled when canceling statement due to user request" do
+ assert_raises(ActiveRecord::QueryCanceled) do
+ s = Sample.create!(value: 1)
+ latch = Concurrent::CountDownLatch.new
+
+ thread = Thread.new do
+ Sample.transaction do
+ Sample.lock.find(s.id)
+ latch.count_down
+ sleep(0.5)
+ conn = Sample.connection
+ pid = conn.query_value("SELECT pid FROM pg_stat_activity WHERE query LIKE '% FOR UPDATE'")
+ conn.execute("SELECT pg_cancel_backend(#{pid})")
+ end
+ end
+
+ begin
+ Sample.transaction do
+ latch.wait
+ Sample.lock.find(s.id)
+ end
+ ensure
+ thread.join
+ end
+ end
+ end
+
private
def with_warning_suppression
diff --git a/activerecord/test/cases/adapters/postgresql/type_lookup_test.rb b/activerecord/test/cases/adapters/postgresql/type_lookup_test.rb
index 784d77a8d1..8212ed4263 100644
--- a/activerecord/test/cases/adapters/postgresql/type_lookup_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/type_lookup_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "cases/helper"
class PostgresqlTypeLookupTest < ActiveRecord::PostgreSQLTestCase
@@ -6,16 +8,16 @@ class PostgresqlTypeLookupTest < ActiveRecord::PostgreSQLTestCase
end
test "array delimiters are looked up correctly" do
- box_array = @connection.type_map.lookup(1020)
- int_array = @connection.type_map.lookup(1007)
+ box_array = @connection.send(:type_map).lookup(1020)
+ int_array = @connection.send(:type_map).lookup(1007)
assert_equal ";", box_array.delimiter
assert_equal ",", int_array.delimiter
end
test "array types correctly respect registration of subtypes" do
- int_array = @connection.type_map.lookup(1007, -1, "integer[]")
- bigint_array = @connection.type_map.lookup(1016, -1, "bigint[]")
+ int_array = @connection.send(:type_map).lookup(1007, -1, "integer[]")
+ bigint_array = @connection.send(:type_map).lookup(1016, -1, "bigint[]")
big_array = [123456789123456789]
assert_raises(ActiveModel::RangeError) { int_array.serialize(big_array) }
@@ -23,11 +25,11 @@ class PostgresqlTypeLookupTest < ActiveRecord::PostgreSQLTestCase
end
test "range types correctly respect registration of subtypes" do
- int_range = @connection.type_map.lookup(3904, -1, "int4range")
- bigint_range = @connection.type_map.lookup(3926, -1, "int8range")
+ int_range = @connection.send(:type_map).lookup(3904, -1, "int4range")
+ bigint_range = @connection.send(:type_map).lookup(3926, -1, "int8range")
big_range = 0..123456789123456789
assert_raises(ActiveModel::RangeError) { int_range.serialize(big_range) }
- assert_equal "[0,123456789123456789]", bigint_range.serialize(big_range)
+ assert_equal "[0,123456789123456789]", @connection.type_cast(bigint_range.serialize(big_range))
end
end
diff --git a/activerecord/test/cases/adapters/postgresql/utils_test.rb b/activerecord/test/cases/adapters/postgresql/utils_test.rb
index 9f9e3bda2f..c91884f384 100644
--- a/activerecord/test/cases/adapters/postgresql/utils_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/utils_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "cases/helper"
require "active_record/connection_adapters/postgresql/utils"
diff --git a/activerecord/test/cases/adapters/postgresql/uuid_test.rb b/activerecord/test/cases/adapters/postgresql/uuid_test.rb
index 8eddd81c38..71d07e2f4c 100644
--- a/activerecord/test/cases/adapters/postgresql/uuid_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/uuid_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "cases/helper"
require "support/schema_dumping_helper"
@@ -74,14 +76,27 @@ class PostgresqlUUIDTest < ActiveRecord::PostgreSQLTestCase
assert_nil column.default
end
+ def test_add_column_with_default_array
+ connection.add_column :uuid_data_type, :thingy, :uuid, array: true, default: []
+
+ UUIDType.reset_column_information
+ column = UUIDType.columns_hash["thingy"]
+
+ assert_predicate column, :array?
+ assert_equal "{}", column.default
+
+ schema = dump_table_schema "uuid_data_type"
+ assert_match %r{t\.uuid "thingy", default: \[\], array: true$}, schema
+ end
+
def test_data_type_of_uuid_types
column = UUIDType.columns_hash["guid"]
assert_equal :uuid, column.type
assert_equal "uuid", column.sql_type
- assert_not column.array?
+ assert_not_predicate column, :array?
type = UUIDType.type_for_attribute("guid")
- assert_not type.binary?
+ assert_not_predicate type, :binary?
end
def test_treat_blank_uuid_as_nil
@@ -124,7 +139,9 @@ class PostgresqlUUIDTest < ActiveRecord::PostgreSQLTestCase
"Z0000C99-9C0B-4EF8-BB6D-6BB9BD380A11",
"a0eebc999r0b4ef8ab6d6bb9bd380a11",
"a0ee-bc99------4ef8-bb6d-6bb9-bd38-0a11",
- "{a0eebc99-bb6d6bb9-bd380a11}"].each do |invalid_uuid|
+ "{a0eebc99-bb6d6bb9-bd380a11}",
+ "{a0eebc99-9c0b4ef8-bb6d6bb9-bd380a11",
+ "a0eebc99-9c0b4ef8-bb6d6bb9-bd380a11}"].each do |invalid_uuid|
uuid = UUIDType.new guid: invalid_uuid
assert_nil uuid.guid
end
@@ -161,7 +178,7 @@ class PostgresqlUUIDTest < ActiveRecord::PostgreSQLTestCase
duplicate = klass.new(guid: record.guid)
assert record.guid.present? # Ensure we actually are testing a UUID
- assert_not duplicate.valid?
+ assert_not_predicate duplicate, :valid?
end
end
@@ -205,68 +222,66 @@ class PostgresqlUUIDGenerationTest < ActiveRecord::PostgreSQLTestCase
connection.execute "DROP FUNCTION IF EXISTS my_uuid_generator();"
end
- if ActiveRecord::Base.connection.supports_extensions?
- def test_id_is_uuid
- assert_equal :uuid, UUID.columns_hash["id"].type
- assert UUID.primary_key
- end
+ def test_id_is_uuid
+ assert_equal :uuid, UUID.columns_hash["id"].type
+ assert UUID.primary_key
+ end
- def test_id_has_a_default
- u = UUID.create
- assert_not_nil u.id
- end
+ def test_id_has_a_default
+ u = UUID.create
+ assert_not_nil u.id
+ end
- def test_auto_create_uuid
- u = UUID.create
- u.reload
- assert_not_nil u.other_uuid
- end
+ def test_auto_create_uuid
+ u = UUID.create
+ u.reload
+ assert_not_nil u.other_uuid
+ end
- def test_pk_and_sequence_for_uuid_primary_key
- pk, seq = connection.pk_and_sequence_for("pg_uuids")
- assert_equal "id", pk
- assert_nil seq
- end
+ def test_pk_and_sequence_for_uuid_primary_key
+ pk, seq = connection.pk_and_sequence_for("pg_uuids")
+ assert_equal "id", pk
+ assert_nil seq
+ end
- def test_schema_dumper_for_uuid_primary_key
- schema = dump_table_schema "pg_uuids"
- assert_match(/\bcreate_table "pg_uuids", id: :uuid, default: -> { "uuid_generate_v1\(\)" }/, schema)
- assert_match(/t\.uuid "other_uuid", default: -> { "uuid_generate_v4\(\)" }/, schema)
- end
+ def test_schema_dumper_for_uuid_primary_key
+ schema = dump_table_schema "pg_uuids"
+ assert_match(/\bcreate_table "pg_uuids", id: :uuid, default: -> { "uuid_generate_v1\(\)" }/, schema)
+ assert_match(/t\.uuid "other_uuid", default: -> { "uuid_generate_v4\(\)" }/, schema)
+ end
+
+ def test_schema_dumper_for_uuid_primary_key_with_custom_default
+ schema = dump_table_schema "pg_uuids_2"
+ assert_match(/\bcreate_table "pg_uuids_2", id: :uuid, default: -> { "my_uuid_generator\(\)" }/, schema)
+ assert_match(/t\.uuid "other_uuid_2", default: -> { "my_uuid_generator\(\)" }/, schema)
+ end
- def test_schema_dumper_for_uuid_primary_key_with_custom_default
- schema = dump_table_schema "pg_uuids_2"
- assert_match(/\bcreate_table "pg_uuids_2", id: :uuid, default: -> { "my_uuid_generator\(\)" }/, schema)
- assert_match(/t\.uuid "other_uuid_2", default: -> { "my_uuid_generator\(\)" }/, schema)
+ def test_schema_dumper_for_uuid_primary_key_default
+ schema = dump_table_schema "pg_uuids_3"
+ if connection.supports_pgcrypto_uuid?
+ assert_match(/\bcreate_table "pg_uuids_3", id: :uuid, default: -> { "gen_random_uuid\(\)" }/, schema)
+ else
+ assert_match(/\bcreate_table "pg_uuids_3", id: :uuid, default: -> { "uuid_generate_v4\(\)" }/, schema)
end
+ end
+
+ def test_schema_dumper_for_uuid_primary_key_default_in_legacy_migration
+ @verbose_was = ActiveRecord::Migration.verbose
+ ActiveRecord::Migration.verbose = false
- def test_schema_dumper_for_uuid_primary_key_default
- schema = dump_table_schema "pg_uuids_3"
- if connection.supports_pgcrypto_uuid?
- assert_match(/\bcreate_table "pg_uuids_3", id: :uuid, default: -> { "gen_random_uuid\(\)" }/, schema)
- else
- assert_match(/\bcreate_table "pg_uuids_3", id: :uuid, default: -> { "uuid_generate_v4\(\)" }/, schema)
+ migration = Class.new(ActiveRecord::Migration[5.0]) do
+ def version; 101 end
+ def migrate(x)
+ create_table("pg_uuids_4", id: :uuid)
end
- end
+ end.new
+ ActiveRecord::Migrator.new(:up, [migration]).migrate
- def test_schema_dumper_for_uuid_primary_key_default_in_legacy_migration
- @verbose_was = ActiveRecord::Migration.verbose
- ActiveRecord::Migration.verbose = false
-
- migration = Class.new(ActiveRecord::Migration[5.0]) do
- def version; 101 end
- def migrate(x)
- create_table("pg_uuids_4", id: :uuid)
- end
- end.new
- ActiveRecord::Migrator.new(:up, [migration]).migrate
-
- schema = dump_table_schema "pg_uuids_4"
- assert_match(/\bcreate_table "pg_uuids_4", id: :uuid, default: -> { "uuid_generate_v4\(\)" }/, schema)
- ensure
- drop_table "pg_uuids_4"
- ActiveRecord::Migration.verbose = @verbose_was
- end
+ schema = dump_table_schema "pg_uuids_4"
+ assert_match(/\bcreate_table "pg_uuids_4", id: :uuid, default: -> { "uuid_generate_v4\(\)" }/, schema)
+ ensure
+ drop_table "pg_uuids_4"
+ ActiveRecord::Migration.verbose = @verbose_was
end
end
@@ -285,38 +300,36 @@ class PostgresqlUUIDTestNilDefault < ActiveRecord::PostgreSQLTestCase
drop_table "pg_uuids"
end
- if ActiveRecord::Base.connection.supports_extensions?
- def test_id_allows_default_override_via_nil
- col_desc = connection.execute("SELECT pg_get_expr(d.adbin, d.adrelid) as default
- FROM pg_attribute a
- LEFT JOIN pg_attrdef d ON a.attrelid = d.adrelid AND a.attnum = d.adnum
- WHERE a.attname='id' AND a.attrelid = 'pg_uuids'::regclass").first
- assert_nil col_desc["default"]
- end
+ def test_id_allows_default_override_via_nil
+ col_desc = connection.execute("SELECT pg_get_expr(d.adbin, d.adrelid) as default
+ FROM pg_attribute a
+ LEFT JOIN pg_attrdef d ON a.attrelid = d.adrelid AND a.attnum = d.adnum
+ WHERE a.attname='id' AND a.attrelid = 'pg_uuids'::regclass").first
+ assert_nil col_desc["default"]
+ end
- def test_schema_dumper_for_uuid_primary_key_with_default_override_via_nil
- schema = dump_table_schema "pg_uuids"
- assert_match(/\bcreate_table "pg_uuids", id: :uuid, default: nil/, schema)
- end
+ def test_schema_dumper_for_uuid_primary_key_with_default_override_via_nil
+ schema = dump_table_schema "pg_uuids"
+ assert_match(/\bcreate_table "pg_uuids", id: :uuid, default: nil/, schema)
+ end
- def test_schema_dumper_for_uuid_primary_key_with_default_nil_in_legacy_migration
- @verbose_was = ActiveRecord::Migration.verbose
- ActiveRecord::Migration.verbose = false
-
- migration = Class.new(ActiveRecord::Migration[5.0]) do
- def version; 101 end
- def migrate(x)
- create_table("pg_uuids_4", id: :uuid, default: nil)
- end
- end.new
- ActiveRecord::Migrator.new(:up, [migration]).migrate
-
- schema = dump_table_schema "pg_uuids_4"
- assert_match(/\bcreate_table "pg_uuids_4", id: :uuid, default: nil/, schema)
- ensure
- drop_table "pg_uuids_4"
- ActiveRecord::Migration.verbose = @verbose_was
- end
+ def test_schema_dumper_for_uuid_primary_key_with_default_nil_in_legacy_migration
+ @verbose_was = ActiveRecord::Migration.verbose
+ ActiveRecord::Migration.verbose = false
+
+ migration = Class.new(ActiveRecord::Migration[5.0]) do
+ def version; 101 end
+ def migrate(x)
+ create_table("pg_uuids_4", id: :uuid, default: nil)
+ end
+ end.new
+ ActiveRecord::Migrator.new(:up, [migration]).migrate
+
+ schema = dump_table_schema "pg_uuids_4"
+ assert_match(/\bcreate_table "pg_uuids_4", id: :uuid, default: nil/, schema)
+ ensure
+ drop_table "pg_uuids_4"
+ ActiveRecord::Migration.verbose = @verbose_was
end
end
@@ -350,23 +363,21 @@ class PostgresqlUUIDTestInverseOf < ActiveRecord::PostgreSQLTestCase
drop_table "pg_uuid_posts"
end
- if ActiveRecord::Base.connection.supports_extensions?
- def test_collection_association_with_uuid
- post = UuidPost.create!
- comment = post.uuid_comments.create!
- assert post.uuid_comments.find(comment.id)
- end
+ def test_collection_association_with_uuid
+ post = UuidPost.create!
+ comment = post.uuid_comments.create!
+ assert post.uuid_comments.find(comment.id)
+ end
- def test_find_with_uuid
- UuidPost.create!
- assert_raise ActiveRecord::RecordNotFound do
- UuidPost.find(123456)
- end
+ def test_find_with_uuid
+ UuidPost.create!
+ assert_raise ActiveRecord::RecordNotFound do
+ UuidPost.find(123456)
end
+ end
- def test_find_by_with_uuid
- UuidPost.create!
- assert_nil UuidPost.find_by(id: 789)
- end
+ def test_find_by_with_uuid
+ UuidPost.create!
+ assert_nil UuidPost.find_by(id: 789)
end
end
diff --git a/activerecord/test/cases/adapters/postgresql/xml_test.rb b/activerecord/test/cases/adapters/postgresql/xml_test.rb
index 826b384fb3..71ead6f7f3 100644
--- a/activerecord/test/cases/adapters/postgresql/xml_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/xml_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "cases/helper"
require "support/schema_dumping_helper"
diff --git a/activerecord/test/cases/adapters/sqlite3/collation_test.rb b/activerecord/test/cases/adapters/sqlite3/collation_test.rb
index dd88ed3656..76c8f7d8dd 100644
--- a/activerecord/test/cases/adapters/sqlite3/collation_test.rb
+++ b/activerecord/test/cases/adapters/sqlite3/collation_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "cases/helper"
require "support/schema_dumping_helper"
diff --git a/activerecord/test/cases/adapters/sqlite3/copy_table_test.rb b/activerecord/test/cases/adapters/sqlite3/copy_table_test.rb
index e1cfd703e8..ffb1d6afce 100644
--- a/activerecord/test/cases/adapters/sqlite3/copy_table_test.rb
+++ b/activerecord/test/cases/adapters/sqlite3/copy_table_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "cases/helper"
class CopyTableTest < ActiveRecord::SQLite3TestCase
diff --git a/activerecord/test/cases/adapters/sqlite3/explain_test.rb b/activerecord/test/cases/adapters/sqlite3/explain_test.rb
index 29d97ae78c..b6d2ccdb53 100644
--- a/activerecord/test/cases/adapters/sqlite3/explain_test.rb
+++ b/activerecord/test/cases/adapters/sqlite3/explain_test.rb
@@ -1,21 +1,23 @@
+# frozen_string_literal: true
+
require "cases/helper"
-require "models/developer"
-require "models/computer"
+require "models/author"
+require "models/post"
class SQLite3ExplainTest < ActiveRecord::SQLite3TestCase
- fixtures :developers
+ fixtures :authors
def test_explain_for_one_query
- explain = Developer.where(id: 1).explain
- assert_match %r(EXPLAIN for: SELECT "developers"\.\* FROM "developers" WHERE "developers"\."id" = (?:\? \[\["id", 1\]\]|1)), explain
- assert_match(/(SEARCH )?TABLE developers USING (INTEGER )?PRIMARY KEY/, explain)
+ explain = Author.where(id: 1).explain
+ assert_match %r(EXPLAIN for: SELECT "authors"\.\* FROM "authors" WHERE "authors"\."id" = (?:\? \[\["id", 1\]\]|1)), explain
+ assert_match(/(SEARCH )?TABLE authors USING (INTEGER )?PRIMARY KEY/, explain)
end
def test_explain_with_eager_loading
- explain = Developer.where(id: 1).includes(:audit_logs).explain
- assert_match %r(EXPLAIN for: SELECT "developers"\.\* FROM "developers" WHERE "developers"\."id" = (?:\? \[\["id", 1\]\]|1)), explain
- assert_match(/(SEARCH )?TABLE developers USING (INTEGER )?PRIMARY KEY/, explain)
- assert_match %(EXPLAIN for: SELECT "audit_logs".* FROM "audit_logs" WHERE "audit_logs"."developer_id" = 1), explain
- assert_match(/(SCAN )?TABLE audit_logs/, explain)
+ explain = Author.where(id: 1).includes(:posts).explain
+ assert_match %r(EXPLAIN for: SELECT "authors"\.\* FROM "authors" WHERE "authors"\."id" = (?:\? \[\["id", 1\]\]|1)), explain
+ assert_match(/(SEARCH )?TABLE authors USING (INTEGER )?PRIMARY KEY/, explain)
+ assert_match %r(EXPLAIN for: SELECT "posts"\.\* FROM "posts" WHERE "posts"\."author_id" = (?:\? \[\["author_id", 1\]\]|1)), explain
+ assert_match(/(SCAN )?TABLE posts/, explain)
end
end
diff --git a/activerecord/test/cases/adapters/sqlite3/json_test.rb b/activerecord/test/cases/adapters/sqlite3/json_test.rb
new file mode 100644
index 0000000000..6f247fcd22
--- /dev/null
+++ b/activerecord/test/cases/adapters/sqlite3/json_test.rb
@@ -0,0 +1,29 @@
+# frozen_string_literal: true
+
+require "cases/helper"
+require "cases/json_shared_test_cases"
+
+class SQLite3JSONTest < ActiveRecord::SQLite3TestCase
+ include JSONSharedTestCases
+
+ def setup
+ super
+ @connection.create_table("json_data_type") do |t|
+ t.json "payload", default: {}
+ t.json "settings"
+ end
+ end
+
+ def test_default
+ @connection.add_column "json_data_type", "permissions", column_type, default: { "users": "read", "posts": ["read", "write"] }
+ klass.reset_column_information
+
+ assert_equal({ "users" => "read", "posts" => ["read", "write"] }, klass.column_defaults["permissions"])
+ assert_equal({ "users" => "read", "posts" => ["read", "write"] }, klass.new.permissions)
+ end
+
+ private
+ def column_type
+ :json
+ end
+end
diff --git a/activerecord/test/cases/adapters/sqlite3/quoting_test.rb b/activerecord/test/cases/adapters/sqlite3/quoting_test.rb
index aefbb309e6..6fdb353368 100644
--- a/activerecord/test/cases/adapters/sqlite3/quoting_test.rb
+++ b/activerecord/test/cases/adapters/sqlite3/quoting_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "cases/helper"
require "bigdecimal"
require "securerandom"
@@ -5,6 +7,11 @@ require "securerandom"
class SQLite3QuotingTest < ActiveRecord::SQLite3TestCase
def setup
@conn = ActiveRecord::Base.connection
+ @initial_represent_boolean_as_integer = ActiveRecord::ConnectionAdapters::SQLite3Adapter.represent_boolean_as_integer
+ end
+
+ def teardown
+ ActiveRecord::ConnectionAdapters::SQLite3Adapter.represent_boolean_as_integer = @initial_represent_boolean_as_integer
end
def test_type_cast_binary_encoding_without_logger
@@ -15,15 +22,23 @@ class SQLite3QuotingTest < ActiveRecord::SQLite3TestCase
end
def test_type_cast_true
+ ActiveRecord::ConnectionAdapters::SQLite3Adapter.represent_boolean_as_integer = false
assert_equal "t", @conn.type_cast(true)
+
+ ActiveRecord::ConnectionAdapters::SQLite3Adapter.represent_boolean_as_integer = true
+ assert_equal 1, @conn.type_cast(true)
end
def test_type_cast_false
+ ActiveRecord::ConnectionAdapters::SQLite3Adapter.represent_boolean_as_integer = false
assert_equal "f", @conn.type_cast(false)
+
+ ActiveRecord::ConnectionAdapters::SQLite3Adapter.represent_boolean_as_integer = true
+ assert_equal 0, @conn.type_cast(false)
end
def test_type_cast_bigdecimal
- bd = BigDecimal.new "10.0"
+ bd = BigDecimal "10.0"
assert_equal bd.to_f, @conn.type_cast(bd)
end
diff --git a/activerecord/test/cases/adapters/sqlite3/sqlite3_adapter_test.rb b/activerecord/test/cases/adapters/sqlite3/sqlite3_adapter_test.rb
index 9a812e325e..7e0ce3a28e 100644
--- a/activerecord/test/cases/adapters/sqlite3/sqlite3_adapter_test.rb
+++ b/activerecord/test/cases/adapters/sqlite3/sqlite3_adapter_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "cases/helper"
require "models/owner"
require "tempfile"
@@ -66,11 +68,11 @@ module ActiveRecord
def test_exec_insert
with_example_table do
- binds = [bind_attribute("number", 10)]
- @conn.exec_insert("insert into ex (number) VALUES (?)", "SQL", binds)
+ vals = [Relation::QueryAttribute.new("number", 10, Type::Value.new)]
+ @conn.exec_insert("insert into ex (number) VALUES (?)", "SQL", vals)
result = @conn.exec_query(
- "select number from ex where number = ?", "SQL", binds)
+ "select number from ex where number = ?", "SQL", vals)
assert_equal 1, result.rows.length
assert_equal 10, result.rows.first.first
@@ -134,7 +136,7 @@ module ActiveRecord
with_example_table "id int, data string" do
@conn.exec_query('INSERT INTO ex (id, data) VALUES (1, "foo")')
result = @conn.exec_query(
- "SELECT id, data FROM ex WHERE id = ?", nil, [bind_attribute("id", 1)])
+ "SELECT id, data FROM ex WHERE id = ?", nil, [Relation::QueryAttribute.new(nil, 1, Type::Value.new)])
assert_equal 1, result.rows.length
assert_equal 2, result.columns.length
@@ -148,7 +150,7 @@ module ActiveRecord
@conn.exec_query('INSERT INTO ex (id, data) VALUES (1, "foo")')
result = @conn.exec_query(
- "SELECT id, data FROM ex WHERE id = ?", nil, [bind_attribute("id", "1-fuu", Type::Integer.new)])
+ "SELECT id, data FROM ex WHERE id = ?", nil, [Relation::QueryAttribute.new("id", "1-fuu", Type::Integer.new)])
assert_equal 1, result.rows.length
assert_equal 2, result.columns.length
@@ -165,7 +167,7 @@ module ActiveRecord
data binary
)
eosql
- str = "\x80".force_encoding("ASCII-8BIT")
+ str = "\x80".dup.force_encoding("ASCII-8BIT")
binary = DualEncoding.new name: "いただきます!", data: str
binary.save!
assert_equal str, binary.data
@@ -174,7 +176,7 @@ module ActiveRecord
end
def test_type_cast_should_not_mutate_encoding
- name = "hello".force_encoding(Encoding::ASCII_8BIT)
+ name = "hello".dup.force_encoding(Encoding::ASCII_8BIT)
Owner.create(name: name)
assert_equal Encoding::ASCII_8BIT, name.encoding
ensure
@@ -267,14 +269,6 @@ module ActiveRecord
end
end
- def test_indexes_logs_name
- with_example_table do
- assert_logged [["PRAGMA index_list(\"ex\")", "SCHEMA", []]] do
- assert_deprecated { @conn.indexes("ex", "hello") }
- end
- end
- end
-
def test_table_exists_logs_name
with_example_table do
sql = <<-SQL
@@ -366,16 +360,126 @@ module ActiveRecord
end
end
+ class Barcode < ActiveRecord::Base
+ self.primary_key = "code"
+ end
+
+ def test_copy_table_with_existing_records_have_custom_primary_key
+ connection = Barcode.connection
+ connection.create_table(:barcodes, primary_key: "code", id: :string, limit: 42, force: true) do |t|
+ t.text :other_attr
+ end
+ code = "214fe0c2-dd47-46df-b53b-66090b3c1d40"
+ Barcode.create!(code: code, other_attr: "xxx")
+
+ connection.remove_column("barcodes", "other_attr")
+
+ assert_equal code, Barcode.first.id
+ ensure
+ Barcode.reset_column_information
+ end
+
+ def test_copy_table_with_composite_primary_keys
+ connection = Barcode.connection
+ connection.create_table(:barcodes, primary_key: ["region", "code"], force: true) do |t|
+ t.string :region
+ t.string :code
+ t.text :other_attr
+ end
+ region = "US"
+ code = "214fe0c2-dd47-46df-b53b-66090b3c1d40"
+ Barcode.create!(region: region, code: code, other_attr: "xxx")
+
+ connection.remove_column("barcodes", "other_attr")
+
+ assert_equal ["region", "code"], connection.primary_keys("barcodes")
+
+ barcode = Barcode.first
+ assert_equal region, barcode.region
+ assert_equal code, barcode.code
+ ensure
+ Barcode.reset_column_information
+ end
+
+ def test_custom_primary_key_in_create_table
+ connection = Barcode.connection
+ connection.create_table :barcodes, id: false, force: true do |t|
+ t.primary_key :id, :string
+ end
+
+ assert_equal "id", connection.primary_key("barcodes")
+
+ custom_pk = Barcode.columns_hash["id"]
+
+ assert_equal :string, custom_pk.type
+ assert_not custom_pk.null
+ ensure
+ Barcode.reset_column_information
+ end
+
+ def test_custom_primary_key_in_change_table
+ connection = Barcode.connection
+ connection.create_table :barcodes, id: false, force: true do |t|
+ t.integer :dummy
+ end
+ connection.change_table :barcodes do |t|
+ t.primary_key :id, :string
+ end
+
+ assert_equal "id", connection.primary_key("barcodes")
+
+ custom_pk = Barcode.columns_hash["id"]
+
+ assert_equal :string, custom_pk.type
+ assert_not custom_pk.null
+ ensure
+ Barcode.reset_column_information
+ end
+
+ def test_add_column_with_custom_primary_key
+ connection = Barcode.connection
+ connection.create_table :barcodes, id: false, force: true do |t|
+ t.integer :dummy
+ end
+ connection.add_column :barcodes, :id, :string, primary_key: true
+
+ assert_equal "id", connection.primary_key("barcodes")
+
+ custom_pk = Barcode.columns_hash["id"]
+
+ assert_equal :string, custom_pk.type
+ assert_not custom_pk.null
+ ensure
+ Barcode.reset_column_information
+ end
+
+ def test_remove_column_preserves_partial_indexes
+ connection = Barcode.connection
+ connection.create_table :barcodes, force: true do |t|
+ t.string :code
+ t.string :region
+ t.boolean :bool_attr
+
+ t.index :code, unique: true, where: :bool_attr, name: "partial"
+ end
+ connection.remove_column :barcodes, :region
+
+ index = connection.indexes("barcodes").find { |idx| idx.name == "partial" }
+ assert_equal "bool_attr", index.where
+ ensure
+ Barcode.reset_column_information
+ end
+
def test_supports_extensions
assert_not @conn.supports_extensions?, "does not support extensions"
end
def test_respond_to_enable_extension
- assert @conn.respond_to?(:enable_extension)
+ assert_respond_to @conn, :enable_extension
end
def test_respond_to_disable_extension
- assert @conn.respond_to?(:disable_extension)
+ assert_respond_to @conn, :disable_extension
end
def test_statement_closed
@@ -396,6 +500,10 @@ module ActiveRecord
end
end
+ def test_deprecate_valid_alter_table_type
+ assert_deprecated { @conn.valid_alter_table_type?(:string) }
+ end
+
private
def assert_logged(logs)
diff --git a/activerecord/test/cases/adapters/sqlite3/sqlite3_create_folder_test.rb b/activerecord/test/cases/adapters/sqlite3/sqlite3_create_folder_test.rb
index b1b4463bf1..d70486605f 100644
--- a/activerecord/test/cases/adapters/sqlite3/sqlite3_create_folder_test.rb
+++ b/activerecord/test/cases/adapters/sqlite3/sqlite3_create_folder_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "cases/helper"
require "models/owner"
diff --git a/activerecord/test/cases/adapters/sqlite3/statement_pool_test.rb b/activerecord/test/cases/adapters/sqlite3/statement_pool_test.rb
index 37ff973397..61002435a4 100644
--- a/activerecord/test/cases/adapters/sqlite3/statement_pool_test.rb
+++ b/activerecord/test/cases/adapters/sqlite3/statement_pool_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "cases/helper"
class SQLite3StatementPoolTest < ActiveRecord::SQLite3TestCase
@@ -8,7 +10,7 @@ class SQLite3StatementPoolTest < ActiveRecord::SQLite3TestCase
assert_equal "bar", cache["foo"]
pid = fork {
- lookup = cache["foo"];
+ lookup = cache["foo"]
exit!(!lookup)
}
diff --git a/activerecord/test/cases/aggregations_test.rb b/activerecord/test/cases/aggregations_test.rb
index f8136fde72..fbdf2ada4b 100644
--- a/activerecord/test/cases/aggregations_test.rb
+++ b/activerecord/test/cases/aggregations_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "cases/helper"
require "models/customer"
@@ -25,7 +27,7 @@ class AggregationsTest < ActiveRecord::TestCase
def test_immutable_value_objects
customers(:david).balance = Money.new(100)
- assert_raise(RuntimeError) { customers(:david).balance.instance_eval { @amount = 20 } }
+ assert_raise(frozen_error_class) { customers(:david).balance.instance_eval { @amount = 20 } }
end
def test_inferred_mapping
diff --git a/activerecord/test/cases/ar_schema_test.rb b/activerecord/test/cases/ar_schema_test.rb
index 5b608d8e83..140d7cbcae 100644
--- a/activerecord/test/cases/ar_schema_test.rb
+++ b/activerecord/test/cases/ar_schema_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "cases/helper"
class ActiveRecordSchemaTest < ActiveRecord::TestCase
@@ -46,7 +48,7 @@ class ActiveRecordSchemaTest < ActiveRecord::TestCase
assert_nothing_raised { @connection.select_all "SELECT * FROM fruits" }
assert_nothing_raised { @connection.select_all "SELECT * FROM schema_migrations" }
- assert_equal 7, ActiveRecord::Migrator::current_version
+ assert_equal 7, @connection.migration_context.current_version
end
def test_schema_define_w_table_name_prefix
@@ -62,7 +64,7 @@ class ActiveRecordSchemaTest < ActiveRecord::TestCase
t.column :flavor, :string
end
end
- assert_equal 7, ActiveRecord::Migrator::current_version
+ assert_equal 7, @connection.migration_context.current_version
ensure
ActiveRecord::Base.table_name_prefix = old_table_name_prefix
ActiveRecord::SchemaMigration.table_name = table_name
diff --git a/activerecord/test/cases/associations/association_scope_test.rb b/activerecord/test/cases/associations/association_scope_test.rb
deleted file mode 100644
index c322333f6d..0000000000
--- a/activerecord/test/cases/associations/association_scope_test.rb
+++ /dev/null
@@ -1,16 +0,0 @@
-require "cases/helper"
-require "models/post"
-require "models/author"
-
-module ActiveRecord
- module Associations
- class AssociationScopeTest < ActiveRecord::TestCase
- test "does not duplicate conditions" do
- scope = AssociationScope.scope(Author.new.association(:welcome_posts),
- Author.connection)
- binds = scope.where_clause.binds.map(&:value)
- assert_equal binds.uniq, binds
- end
- end
- end
-end
diff --git a/activerecord/test/cases/associations/belongs_to_associations_test.rb b/activerecord/test/cases/associations/belongs_to_associations_test.rb
index c8b26232b6..9e6d94191b 100644
--- a/activerecord/test/cases/associations/belongs_to_associations_test.rb
+++ b/activerecord/test/cases/associations/belongs_to_associations_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "cases/helper"
require "models/developer"
require "models/project"
@@ -77,7 +79,7 @@ class BelongsToAssociationsTest < ActiveRecord::TestCase
end
account = model.new
- assert account.valid?
+ assert_predicate account, :valid?
ensure
ActiveRecord::Base.belongs_to_required_by_default = original_value
end
@@ -93,7 +95,7 @@ class BelongsToAssociationsTest < ActiveRecord::TestCase
end
account = model.new
- assert_not account.valid?
+ assert_not_predicate account, :valid?
assert_equal [{ error: :blank }], account.errors.details[:company]
ensure
ActiveRecord::Base.belongs_to_required_by_default = original_value
@@ -110,7 +112,7 @@ class BelongsToAssociationsTest < ActiveRecord::TestCase
end
account = model.new
- assert_not account.valid?
+ assert_not_predicate account, :valid?
assert_equal [{ error: :blank }], account.errors.details[:company]
ensure
ActiveRecord::Base.belongs_to_required_by_default = original_value
@@ -215,6 +217,8 @@ class BelongsToAssociationsTest < ActiveRecord::TestCase
remove_column :admin_users, :region_id if column_exists?(:admin_users, :region_id)
drop_table :admin_regions, if_exists: true
end
+
+ Admin::User.reset_column_information
end
def test_natural_assignment
@@ -242,14 +246,14 @@ class BelongsToAssociationsTest < ActiveRecord::TestCase
Firm.create("name" => "Apple")
Client.create("name" => "Citibank", :firm_name => "Apple")
citibank_result = Client.all.merge!(where: { name: "Citibank" }, includes: :firm_with_primary_key).first
- assert citibank_result.association(:firm_with_primary_key).loaded?
+ assert_predicate citibank_result.association(:firm_with_primary_key), :loaded?
end
def test_eager_loading_with_primary_key_as_symbol
Firm.create("name" => "Apple")
Client.create("name" => "Citibank", :firm_name => "Apple")
citibank_result = Client.all.merge!(where: { name: "Citibank" }, includes: :firm_with_primary_key_symbols).first
- assert citibank_result.association(:firm_with_primary_key_symbols).loaded?
+ assert_predicate citibank_result.association(:firm_with_primary_key_symbols), :loaded?
end
def test_creating_the_belonging_object
@@ -316,7 +320,7 @@ class BelongsToAssociationsTest < ActiveRecord::TestCase
client = Client.create!(name: "Jimmy")
account = client.create_account!(credit_limit: 10)
assert_equal account, client.account
- assert account.persisted?
+ assert_predicate account, :persisted?
client.save
client.reload
assert_equal account, client.account
@@ -326,7 +330,7 @@ class BelongsToAssociationsTest < ActiveRecord::TestCase
client = Client.create!(name: "Jimmy")
assert_raise(ActiveRecord::RecordInvalid) { client.create_account! }
assert_not_nil client.account
- assert client.account.new_record?
+ assert_predicate client.account, :new_record?
end
def test_reloading_the_belonging_object
@@ -380,7 +384,6 @@ class BelongsToAssociationsTest < ActiveRecord::TestCase
sponsor.sponsorable = Member.new name: "Bert"
assert_equal Member, sponsor.association(:sponsorable).send(:klass)
- assert_equal "members", sponsor.association(:sponsorable).aliased_table_name
end
def test_with_polymorphic_and_condition
@@ -624,10 +627,10 @@ class BelongsToAssociationsTest < ActiveRecord::TestCase
final_cut = Client.new("name" => "Final Cut")
firm = Firm.find(1)
final_cut.firm = firm
- assert !final_cut.persisted?
+ assert_not_predicate final_cut, :persisted?
assert final_cut.save
- assert final_cut.persisted?
- assert firm.persisted?
+ assert_predicate final_cut, :persisted?
+ assert_predicate firm, :persisted?
assert_equal firm, final_cut.firm
final_cut.association(:firm).reload
assert_equal firm, final_cut.firm
@@ -637,10 +640,10 @@ class BelongsToAssociationsTest < ActiveRecord::TestCase
final_cut = Client.new("name" => "Final Cut")
firm = Firm.find(1)
final_cut.firm_with_primary_key = firm
- assert !final_cut.persisted?
+ assert_not_predicate final_cut, :persisted?
assert final_cut.save
- assert final_cut.persisted?
- assert firm.persisted?
+ assert_predicate final_cut, :persisted?
+ assert_predicate firm, :persisted?
assert_equal firm, final_cut.firm_with_primary_key
final_cut.association(:firm_with_primary_key).reload
assert_equal firm, final_cut.firm_with_primary_key
@@ -648,8 +651,7 @@ class BelongsToAssociationsTest < ActiveRecord::TestCase
def test_new_record_with_foreign_key_but_no_object
client = Client.new("firm_id" => 1)
- # sometimes tests on Oracle fail if ORDER BY is not provided therefore add always :order with :first
- assert_equal Firm.all.merge!(order: "id").first, client.firm_with_basic_id
+ assert_equal Firm.first, client.firm_with_basic_id
end
def test_setting_foreign_key_after_nil_target_loaded
@@ -788,7 +790,7 @@ class BelongsToAssociationsTest < ActiveRecord::TestCase
def test_cant_save_readonly_association
assert_raise(ActiveRecord::ReadOnlyRecord) { companies(:first_client).readonly_firm.save! }
- assert companies(:first_client).readonly_firm.readonly?
+ assert_predicate companies(:first_client).readonly_firm, :readonly?
end
def test_polymorphic_assignment_foreign_key_type_string
@@ -947,15 +949,15 @@ class BelongsToAssociationsTest < ActiveRecord::TestCase
firm_proxy = client.send(:association_instance_get, :firm)
firm_with_condition_proxy = client.send(:association_instance_get, :firm_with_condition)
- assert !firm_proxy.stale_target?
- assert !firm_with_condition_proxy.stale_target?
+ assert_not_predicate firm_proxy, :stale_target?
+ assert_not_predicate firm_with_condition_proxy, :stale_target?
assert_equal companies(:first_firm), client.firm
assert_equal companies(:first_firm), client.firm_with_condition
client.client_of = companies(:another_firm).id
- assert firm_proxy.stale_target?
- assert firm_with_condition_proxy.stale_target?
+ assert_predicate firm_proxy, :stale_target?
+ assert_predicate firm_with_condition_proxy, :stale_target?
assert_equal companies(:another_firm), client.firm
assert_equal companies(:another_firm), client.firm_with_condition
end
@@ -966,12 +968,12 @@ class BelongsToAssociationsTest < ActiveRecord::TestCase
sponsor.sponsorable
proxy = sponsor.send(:association_instance_get, :sponsorable)
- assert !proxy.stale_target?
+ assert_not_predicate proxy, :stale_target?
assert_equal members(:groucho), sponsor.sponsorable
sponsor.sponsorable_id = members(:some_other_guy).id
- assert proxy.stale_target?
+ assert_predicate proxy, :stale_target?
assert_equal members(:some_other_guy), sponsor.sponsorable
end
@@ -981,12 +983,12 @@ class BelongsToAssociationsTest < ActiveRecord::TestCase
sponsor.sponsorable
proxy = sponsor.send(:association_instance_get, :sponsorable)
- assert !proxy.stale_target?
+ assert_not_predicate proxy, :stale_target?
assert_equal members(:groucho), sponsor.sponsorable
sponsor.sponsorable_type = "Firm"
- assert proxy.stale_target?
+ assert_predicate proxy, :stale_target?
assert_equal companies(:first_firm), sponsor.sponsorable
end
@@ -1120,7 +1122,7 @@ class BelongsToAssociationsTest < ActiveRecord::TestCase
comment.post_id = 9223372036854775808 # out of range in the bigint
assert_nil comment.post
- assert_not comment.valid?
+ assert_not_predicate comment, :valid?
assert_equal [{ error: :blank }], comment.errors.details[:post]
end
@@ -1140,7 +1142,7 @@ class BelongsToAssociationsTest < ActiveRecord::TestCase
citibank.firm_id = apple.id.to_s
- assert !citibank.association(:firm).stale_target?
+ assert_not_predicate citibank.association(:firm), :stale_target?
end
def test_reflect_the_most_recent_change
@@ -1169,6 +1171,17 @@ class BelongsToAssociationsTest < ActiveRecord::TestCase
Column.create! record: record
assert_equal 1, Column.count
end
+
+ def test_multiple_counter_cache_with_after_create_update
+ post = posts(:welcome)
+ parent = comments(:greetings)
+
+ assert_difference "parent.reload.children_count", +1 do
+ assert_difference "post.reload.comments_count", +1 do
+ CommentWithAfterCreateUpdate.create(body: "foo", post: post, parent: parent)
+ end
+ end
+ end
end
class BelongsToWithForeignKeyTest < ActiveRecord::TestCase
diff --git a/activerecord/test/cases/associations/bidirectional_destroy_dependencies_test.rb b/activerecord/test/cases/associations/bidirectional_destroy_dependencies_test.rb
index 8a0e041864..88221b012e 100644
--- a/activerecord/test/cases/associations/bidirectional_destroy_dependencies_test.rb
+++ b/activerecord/test/cases/associations/bidirectional_destroy_dependencies_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "cases/helper"
require "models/content"
diff --git a/activerecord/test/cases/associations/callbacks_test.rb b/activerecord/test/cases/associations/callbacks_test.rb
index f9d1e44595..25d55dc4c9 100644
--- a/activerecord/test/cases/associations/callbacks_test.rb
+++ b/activerecord/test/cases/associations/callbacks_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "cases/helper"
require "models/post"
require "models/author"
@@ -13,7 +15,7 @@ class AssociationCallbacksTest < ActiveRecord::TestCase
@david = authors(:david)
@thinking = posts(:thinking)
@authorless = posts(:authorless)
- assert @david.post_log.empty?
+ assert_empty @david.post_log
end
def test_adding_macro_callbacks
@@ -94,7 +96,7 @@ class AssociationCallbacksTest < ActiveRecord::TestCase
def test_has_and_belongs_to_many_add_callback
david = developers(:david)
ar = projects(:active_record)
- assert ar.developers_log.empty?
+ assert_empty ar.developers_log
ar.developers_with_callbacks << david
assert_equal ["before_adding#{david.id}", "after_adding#{david.id}"], ar.developers_log
ar.developers_with_callbacks << david
@@ -120,12 +122,12 @@ class AssociationCallbacksTest < ActiveRecord::TestCase
assert_equal alice, dev
assert_not_nil new_dev
assert new_dev, "record should not have been saved"
- assert_not alice.new_record?
+ assert_not_predicate alice, :new_record?
end
def test_has_and_belongs_to_many_after_add_called_after_save
ar = projects(:active_record)
- assert ar.developers_log.empty?
+ assert_empty ar.developers_log
alice = Developer.new(name: "alice")
ar.developers_with_callbacks << alice
assert_equal "after_adding#{alice.id}", ar.developers_log.last
@@ -141,7 +143,7 @@ class AssociationCallbacksTest < ActiveRecord::TestCase
david = developers(:david)
jamis = developers(:jamis)
activerecord = projects(:active_record)
- assert activerecord.developers_log.empty?
+ assert_empty activerecord.developers_log
activerecord.developers_with_callbacks.delete(david)
assert_equal ["before_removing#{david.id}", "after_removing#{david.id}"], activerecord.developers_log
@@ -152,7 +154,7 @@ class AssociationCallbacksTest < ActiveRecord::TestCase
def test_has_and_belongs_to_many_does_not_fire_callbacks_on_clear
activerecord = projects(:active_record)
- assert activerecord.developers_log.empty?
+ assert_empty activerecord.developers_log
if activerecord.developers_with_callbacks.size == 0
activerecord.developers << developers(:david)
activerecord.developers << developers(:jamis)
@@ -161,7 +163,7 @@ class AssociationCallbacksTest < ActiveRecord::TestCase
end
activerecord.developers_with_callbacks.flat_map { |d| ["before_removing#{d.id}", "after_removing#{d.id}"] }.sort
assert activerecord.developers_with_callbacks.clear
- assert_predicate activerecord.developers_log, :empty?
+ assert_empty activerecord.developers_log
end
def test_has_many_and_belongs_to_many_callbacks_for_save_on_parent
@@ -181,7 +183,7 @@ class AssociationCallbacksTest < ActiveRecord::TestCase
@david.unchangeable_posts << @authorless
rescue Exception
end
- assert @david.post_log.empty?
+ assert_empty @david.post_log
assert_not_includes @david.unchangeable_posts, @authorless
@david.reload
assert_not_includes @david.unchangeable_posts, @authorless
diff --git a/activerecord/test/cases/associations/cascaded_eager_loading_test.rb b/activerecord/test/cases/associations/cascaded_eager_loading_test.rb
index 7b0445025c..e717621928 100644
--- a/activerecord/test/cases/associations/cascaded_eager_loading_test.rb
+++ b/activerecord/test/cases/associations/cascaded_eager_loading_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "cases/helper"
require "models/post"
require "models/comment"
@@ -35,7 +37,7 @@ class CascadedEagerLoadingTest < ActiveRecord::TestCase
def test_eager_association_loading_with_hmt_does_not_table_name_collide_when_joining_associations
authors = Author.joins(:posts).eager_load(:comments).where(posts: { tags_count: 1 }).to_a
- assert_equal 1, assert_no_queries { authors.size }
+ assert_equal 3, assert_no_queries { authors.size }
assert_equal 10, assert_no_queries { authors[0].comments.size }
end
@@ -134,7 +136,7 @@ class CascadedEagerLoadingTest < ActiveRecord::TestCase
end
def test_eager_association_loading_with_multiple_stis_and_order
- author = Author.all.merge!(includes: { posts: [ :special_comments , :very_special_comment ] }, order: ["authors.name", "comments.body", "very_special_comments_posts.body"], where: "posts.id = 4").first
+ author = Author.all.merge!(includes: { posts: [ :special_comments, :very_special_comment ] }, order: ["authors.name", "comments.body", "very_special_comments_posts.body"], where: "posts.id = 4").first
assert_equal authors(:david), author
assert_no_queries do
author.posts.first.special_comments
diff --git a/activerecord/test/cases/associations/eager_load_includes_full_sti_class_test.rb b/activerecord/test/cases/associations/eager_load_includes_full_sti_class_test.rb
index 4f0fe3236e..8754889143 100644
--- a/activerecord/test/cases/associations/eager_load_includes_full_sti_class_test.rb
+++ b/activerecord/test/cases/associations/eager_load_includes_full_sti_class_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "cases/helper"
require "models/post"
require "models/tagging"
@@ -11,25 +13,32 @@ end
class EagerLoadIncludeFullStiClassNamesTest < ActiveRecord::TestCase
def setup
- generate_test_objects
- end
-
- def generate_test_objects
post = Namespaced::Post.create(title: "Great stuff", body: "This is not", author_id: 1)
- Tagging.create(taggable: post)
+ @tagging = Tagging.create(taggable: post)
+ @old = ActiveRecord::Base.store_full_sti_class
end
- def test_class_names
- old = ActiveRecord::Base.store_full_sti_class
+ def teardown
+ ActiveRecord::Base.store_full_sti_class = @old
+ end
+ def test_class_names_with_includes
ActiveRecord::Base.store_full_sti_class = false
post = Namespaced::Post.includes(:tagging).find_by_title("Great stuff")
assert_nil post.tagging
ActiveRecord::Base.store_full_sti_class = true
post = Namespaced::Post.includes(:tagging).find_by_title("Great stuff")
- assert_instance_of Tagging, post.tagging
- ensure
- ActiveRecord::Base.store_full_sti_class = old
+ assert_equal @tagging, post.tagging
+ end
+
+ def test_class_names_with_eager_load
+ ActiveRecord::Base.store_full_sti_class = false
+ post = Namespaced::Post.eager_load(:tagging).find_by_title("Great stuff")
+ assert_nil post.tagging
+
+ ActiveRecord::Base.store_full_sti_class = true
+ post = Namespaced::Post.eager_load(:tagging).find_by_title("Great stuff")
+ assert_equal @tagging, post.tagging
end
end
diff --git a/activerecord/test/cases/associations/eager_load_nested_include_test.rb b/activerecord/test/cases/associations/eager_load_nested_include_test.rb
index e9f551b6b2..c5b2b77bd4 100644
--- a/activerecord/test/cases/associations/eager_load_nested_include_test.rb
+++ b/activerecord/test/cases/associations/eager_load_nested_include_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "cases/helper"
require "models/post"
require "models/tag"
diff --git a/activerecord/test/cases/associations/eager_singularization_test.rb b/activerecord/test/cases/associations/eager_singularization_test.rb
index 16eff15026..420a5a805b 100644
--- a/activerecord/test/cases/associations/eager_singularization_test.rb
+++ b/activerecord/test/cases/associations/eager_singularization_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "cases/helper"
class EagerSingularizationTest < ActiveRecord::TestCase
diff --git a/activerecord/test/cases/associations/eager_test.rb b/activerecord/test/cases/associations/eager_test.rb
index 55b294cfaa..cb07ca5cd3 100644
--- a/activerecord/test/cases/associations/eager_test.rb
+++ b/activerecord/test/cases/associations/eager_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "cases/helper"
require "models/post"
require "models/tagging"
@@ -38,6 +40,12 @@ class EagerAssociationTest < ActiveRecord::TestCase
assert_nil member.favourite_club
end
+ def test_should_work_inverse_of_with_eager_load
+ author = authors(:david)
+ assert_same author, author.posts.first.author
+ assert_same author, author.posts.eager_load(:comments).first.author
+ end
+
def test_loading_with_one_association
posts = Post.all.merge!(includes: :comments).to_a
post = posts.find { |p| p.id == 1 }
@@ -68,6 +76,11 @@ class EagerAssociationTest < ActiveRecord::TestCase
"expected to find only david's posts"
end
+ def test_loading_with_scope_including_joins
+ assert_equal clubs(:boring_club), Member.preload(:general_club).find(1).general_club
+ assert_equal clubs(:boring_club), Member.eager_load(:general_club).find(1).general_club
+ end
+
def test_with_ordering
list = Post.all.merge!(includes: :comments, order: "posts.id DESC").to_a
[:other_by_mary, :other_by_bob, :misc_by_mary, :misc_by_bob, :eager_other,
@@ -271,7 +284,7 @@ class EagerAssociationTest < ActiveRecord::TestCase
end
def test_loading_from_an_association_that_has_a_hash_of_conditions
- assert !Author.all.merge!(includes: :hello_posts_with_hash_conditions).find(authors(:david).id).hello_posts.empty?
+ assert_not_empty Author.all.merge!(includes: :hello_posts_with_hash_conditions).find(authors(:david).id).hello_posts
end
def test_loading_with_no_associations
@@ -414,7 +427,7 @@ class EagerAssociationTest < ActiveRecord::TestCase
def test_eager_association_loading_with_belongs_to_and_order_string_with_quoted_table_name
quoted_posts_id = Comment.connection.quote_table_name("posts") + "." + Comment.connection.quote_column_name("id")
assert_nothing_raised do
- Comment.includes(:post).references(:posts).order(quoted_posts_id)
+ Comment.includes(:post).references(:posts).order(Arel.sql(quoted_posts_id))
end
end
@@ -517,6 +530,14 @@ class EagerAssociationTest < ActiveRecord::TestCase
assert_equal [comments(:does_it_hurt)], assert_no_queries { author.special_post_comments }
end
+ def test_preloading_has_many_through_with_implicit_source
+ authors = Author.includes(:very_special_comments).to_a
+ assert_no_queries do
+ special_comment_authors = authors.map { |author| [author.name, author.very_special_comments.size] }
+ assert_equal [["David", 1], ["Mary", 0], ["Bob", 0]], special_comment_authors
+ end
+ end
+
def test_eager_with_has_many_through_an_sti_join_model_with_conditions_on_both
author = Author.all.merge!(includes: :special_nonexistent_post_comments, order: "authors.id").first
assert_equal [], author.special_nonexistent_post_comments
@@ -766,7 +787,7 @@ class EagerAssociationTest < ActiveRecord::TestCase
Tagging.create!(taggable_type: "Post", taggable_id: post2.id, tag: tag)
tag_with_includes = OrderedTag.includes(:tagged_posts).find(tag.id)
- assert_equal(tag_with_includes.taggings.map(&:taggable).map(&:title), tag_with_includes.tagged_posts.map(&:title))
+ assert_equal tag_with_includes.ordered_taggings.map(&:taggable).map(&:title), tag_with_includes.tagged_posts.map(&:title)
end
def test_eager_has_many_through_multiple_with_order
@@ -848,23 +869,19 @@ class EagerAssociationTest < ActiveRecord::TestCase
end
end
- def find_all_ordered(className, include = nil)
- className.all.merge!(order: "#{className.table_name}.#{className.primary_key}", includes: include).to_a
- end
-
def test_limited_eager_with_order
assert_equal(
posts(:thinking, :sti_comments),
Post.all.merge!(
includes: [:author, :comments], where: { "authors.name" => "David" },
- order: "UPPER(posts.title)", limit: 2, offset: 1
+ order: Arel.sql("UPPER(posts.title)"), limit: 2, offset: 1
).to_a
)
assert_equal(
posts(:sti_post_and_comments, :sti_comments),
Post.all.merge!(
includes: [:author, :comments], where: { "authors.name" => "David" },
- order: "UPPER(posts.title) DESC", limit: 2, offset: 1
+ order: Arel.sql("UPPER(posts.title) DESC"), limit: 2, offset: 1
).to_a
)
end
@@ -874,14 +891,14 @@ class EagerAssociationTest < ActiveRecord::TestCase
posts(:thinking, :sti_comments),
Post.all.merge!(
includes: [:author, :comments], where: { "authors.name" => "David" },
- order: ["UPPER(posts.title)", "posts.id"], limit: 2, offset: 1
+ order: [Arel.sql("UPPER(posts.title)"), "posts.id"], limit: 2, offset: 1
).to_a
)
assert_equal(
posts(:sti_post_and_comments, :sti_comments),
Post.all.merge!(
includes: [:author, :comments], where: { "authors.name" => "David" },
- order: ["UPPER(posts.title) DESC", "posts.id"], limit: 2, offset: 1
+ order: [Arel.sql("UPPER(posts.title) DESC"), "posts.id"], limit: 2, offset: 1
).to_a
)
end
@@ -1056,7 +1073,7 @@ class EagerAssociationTest < ActiveRecord::TestCase
end
def test_load_with_sti_sharing_association
- assert_queries(2) do #should not do 1 query per subclass
+ assert_queries(2) do # should not do 1 query per subclass
Comment.includes(:post).to_a
end
end
@@ -1286,6 +1303,11 @@ class EagerAssociationTest < ActiveRecord::TestCase
assert_equal projects.last.mentor.developers.first.contracts, projects.last.developers.last.contracts
end
+ def test_preloading_has_many_through_with_custom_scope
+ project = Project.includes(:developers_named_david_with_hash_conditions).find(projects(:active_record).id)
+ assert_equal [developers(:david)], project.developers_named_david_with_hash_conditions
+ end
+
test "scoping with a circular preload" do
assert_equal Comment.find(1), Comment.preload(post: :comments).scoping { Comment.find(1) }
end
@@ -1422,51 +1444,51 @@ class EagerAssociationTest < ActiveRecord::TestCase
test "preloading readonly association" do
# has-one
firm = Firm.where(id: "1").preload(:readonly_account).first!
- assert firm.readonly_account.readonly?
+ assert_predicate firm.readonly_account, :readonly?
# has_and_belongs_to_many
project = Project.where(id: "2").preload(:readonly_developers).first!
- assert project.readonly_developers.first.readonly?
+ assert_predicate project.readonly_developers.first, :readonly?
# has-many :through
david = Author.where(id: "1").preload(:readonly_comments).first!
- assert david.readonly_comments.first.readonly?
+ assert_predicate david.readonly_comments.first, :readonly?
end
test "eager-loading non-readonly association" do
# has_one
firm = Firm.where(id: "1").eager_load(:account).first!
- assert_not firm.account.readonly?
+ assert_not_predicate firm.account, :readonly?
# has_and_belongs_to_many
project = Project.where(id: "2").eager_load(:developers).first!
- assert_not project.developers.first.readonly?
+ assert_not_predicate project.developers.first, :readonly?
# has_many :through
david = Author.where(id: "1").eager_load(:comments).first!
- assert_not david.comments.first.readonly?
+ assert_not_predicate david.comments.first, :readonly?
# belongs_to
post = Post.where(id: "1").eager_load(:author).first!
- assert_not post.author.readonly?
+ assert_not_predicate post.author, :readonly?
end
test "eager-loading readonly association" do
# has-one
firm = Firm.where(id: "1").eager_load(:readonly_account).first!
- assert firm.readonly_account.readonly?
+ assert_predicate firm.readonly_account, :readonly?
# has_and_belongs_to_many
project = Project.where(id: "2").eager_load(:readonly_developers).first!
- assert project.readonly_developers.first.readonly?
+ assert_predicate project.readonly_developers.first, :readonly?
# has-many :through
david = Author.where(id: "1").eager_load(:readonly_comments).first!
- assert david.readonly_comments.first.readonly?
+ assert_predicate david.readonly_comments.first, :readonly?
# belongs_to
post = Post.where(id: "1").eager_load(:readonly_author).first!
- assert post.readonly_author.readonly?
+ assert_predicate post.readonly_author, :readonly?
end
test "preloading a polymorphic association with references to the associated table" do
@@ -1479,9 +1501,18 @@ class EagerAssociationTest < ActiveRecord::TestCase
assert_equal posts(:welcome), post
end
+ test "eager-loading with a polymorphic association and using the existential predicate" do
+ assert_equal true, authors(:david).essays.eager_load(:writer).exists?
+ end
+
# CollectionProxy#reader is expensive, so the preloader avoids calling it.
test "preloading has_many_through association avoids calling association.reader" do
ActiveRecord::Associations::HasManyAssociation.any_instance.expects(:reader).never
Author.preload(:readonly_comments).first!
end
+
+ private
+ def find_all_ordered(klass, include = nil)
+ klass.order("#{klass.table_name}.#{klass.primary_key}").includes(include).to_a
+ end
end
diff --git a/activerecord/test/cases/associations/extension_test.rb b/activerecord/test/cases/associations/extension_test.rb
index f707a170f5..5eacb5a3d8 100644
--- a/activerecord/test/cases/associations/extension_test.rb
+++ b/activerecord/test/cases/associations/extension_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "cases/helper"
require "models/post"
require "models/comment"
diff --git a/activerecord/test/cases/associations/has_and_belongs_to_many_associations_test.rb b/activerecord/test/cases/associations/has_and_belongs_to_many_associations_test.rb
index f73005b3cb..d9e05cf7e2 100644
--- a/activerecord/test/cases/associations/has_and_belongs_to_many_associations_test.rb
+++ b/activerecord/test/cases/associations/has_and_belongs_to_many_associations_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "cases/helper"
require "models/developer"
require "models/computer"
@@ -86,12 +88,6 @@ class DeveloperWithSymbolClassName < Developer
has_and_belongs_to_many :projects, class_name: :ProjectWithSymbolsForKeys
end
-ActiveSupport::Deprecation.silence do
- class DeveloperWithConstantClassName < Developer
- has_and_belongs_to_many :projects, class_name: ProjectWithSymbolsForKeys
- end
-end
-
class DeveloperWithExtendOption < Developer
module NamedExtension
def category
@@ -184,11 +180,11 @@ class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase
def test_has_and_belongs_to_many
david = Developer.find(1)
- assert !david.projects.empty?
+ assert_not_empty david.projects
assert_equal 2, david.projects.size
active_record = Project.find(1)
- assert !active_record.developers.empty?
+ assert_not_empty active_record.developers
assert_equal 3, active_record.developers.size
assert_includes active_record.developers, david
end
@@ -266,10 +262,10 @@ class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase
no_of_projects = Project.count
aredridel = Developer.new("name" => "Aredridel")
aredridel.projects.concat([Project.find(1), p = Project.new("name" => "Projekt")])
- assert !aredridel.persisted?
- assert !p.persisted?
+ assert_not_predicate aredridel, :persisted?
+ assert_not_predicate p, :persisted?
assert aredridel.save
- assert aredridel.persisted?
+ assert_predicate aredridel, :persisted?
assert_equal no_of_devels + 1, Developer.count
assert_equal no_of_projects + 1, Project.count
assert_equal 2, aredridel.projects.size
@@ -315,14 +311,14 @@ class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase
def test_build
devel = Developer.find(1)
proj = assert_no_queries(ignore_none: false) { devel.projects.build("name" => "Projekt") }
- assert !devel.projects.loaded?
+ assert_not_predicate devel.projects, :loaded?
assert_equal devel.projects.last, proj
- assert devel.projects.loaded?
+ assert_predicate devel.projects, :loaded?
- assert !proj.persisted?
+ assert_not_predicate proj, :persisted?
devel.save
- assert proj.persisted?
+ assert_predicate proj, :persisted?
assert_equal devel.projects.last, proj
assert_equal Developer.find(1).projects.sort_by(&:id).last, proj # prove join table is updated
end
@@ -330,14 +326,14 @@ class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase
def test_new_aliased_to_build
devel = Developer.find(1)
proj = assert_no_queries(ignore_none: false) { devel.projects.new("name" => "Projekt") }
- assert !devel.projects.loaded?
+ assert_not_predicate devel.projects, :loaded?
assert_equal devel.projects.last, proj
- assert devel.projects.loaded?
+ assert_predicate devel.projects, :loaded?
- assert !proj.persisted?
+ assert_not_predicate proj, :persisted?
devel.save
- assert proj.persisted?
+ assert_predicate proj, :persisted?
assert_equal devel.projects.last, proj
assert_equal Developer.find(1).projects.sort_by(&:id).last, proj # prove join table is updated
end
@@ -347,10 +343,10 @@ class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase
devel.projects.build(name: "Make bed")
proj2 = devel.projects.build(name: "Lie in it")
assert_equal devel.projects.last, proj2
- assert !proj2.persisted?
+ assert_not_predicate proj2, :persisted?
devel.save
- assert devel.persisted?
- assert proj2.persisted?
+ assert_predicate devel, :persisted?
+ assert_predicate proj2, :persisted?
assert_equal devel.projects.last, proj2
assert_equal Developer.find_by_name("Marcel").projects.last, proj2 # prove join table is updated
end
@@ -358,12 +354,12 @@ class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase
def test_create
devel = Developer.find(1)
proj = devel.projects.create("name" => "Projekt")
- assert !devel.projects.loaded?
+ assert_not_predicate devel.projects, :loaded?
assert_equal devel.projects.last, proj
- assert !devel.projects.loaded?
+ assert_not_predicate devel.projects, :loaded?
- assert proj.persisted?
+ assert_predicate proj, :persisted?
assert_equal Developer.find(1).projects.sort_by(&:id).last, proj # prove join table is updated
end
@@ -371,14 +367,14 @@ class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase
# in Oracle '' is saved as null therefore need to save ' ' in not null column
post = categories(:general).post_with_conditions.build(body: " ")
- assert post.save
- assert_equal "Yet Another Testing Title", post.title
+ assert post.save
+ assert_equal "Yet Another Testing Title", post.title
# in Oracle '' is saved as null therefore need to save ' ' in not null column
another_post = categories(:general).post_with_conditions.create(body: " ")
- assert another_post.persisted?
- assert_equal "Yet Another Testing Title", another_post.title
+ assert_predicate another_post, :persisted?
+ assert_equal "Yet Another Testing Title", another_post.title
end
def test_distinct_after_the_fact
@@ -445,10 +441,10 @@ class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase
def test_removing_associations_on_destroy
david = DeveloperWithBeforeDestroyRaise.find(1)
- assert !david.projects.empty?
+ assert_not_empty david.projects
david.destroy
- assert david.projects.empty?
- assert DeveloperWithBeforeDestroyRaise.connection.select_all("SELECT * FROM developers_projects WHERE developer_id = 1").empty?
+ assert_empty david.projects
+ assert_empty DeveloperWithBeforeDestroyRaise.connection.select_all("SELECT * FROM developers_projects WHERE developer_id = 1")
end
def test_destroying
@@ -463,7 +459,7 @@ class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase
end
join_records = Developer.connection.select_all("SELECT * FROM developers_projects WHERE developer_id = #{david.id} AND project_id = #{project.id}")
- assert join_records.empty?
+ assert_empty join_records
assert_equal 1, david.reload.projects.size
assert_equal 1, david.projects.reload.size
@@ -479,7 +475,7 @@ class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase
end
join_records = Developer.connection.select_all("SELECT * FROM developers_projects WHERE developer_id = #{david.id}")
- assert join_records.empty?
+ assert_empty join_records
assert_equal 0, david.reload.projects.size
assert_equal 0, david.projects.reload.size
@@ -488,23 +484,23 @@ class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase
def test_destroy_all
david = Developer.find(1)
david.projects.reload
- assert !david.projects.empty?
+ assert_not_empty david.projects
assert_no_difference "Project.count" do
david.projects.destroy_all
end
join_records = Developer.connection.select_all("SELECT * FROM developers_projects WHERE developer_id = #{david.id}")
- assert join_records.empty?
+ assert_empty join_records
- assert david.projects.empty?
- assert david.projects.reload.empty?
+ assert_empty david.projects
+ assert_empty david.projects.reload
end
def test_destroy_associations_destroys_multiple_associations
george = parrots(:george)
- assert !george.pirates.empty?
- assert !george.treasures.empty?
+ assert_not_empty george.pirates
+ assert_not_empty george.treasures
assert_no_difference "Pirate.count" do
assert_no_difference "Treasure.count" do
@@ -513,12 +509,12 @@ class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase
end
join_records = Parrot.connection.select_all("SELECT * FROM parrots_pirates WHERE parrot_id = #{george.id}")
- assert join_records.empty?
- assert george.pirates.reload.empty?
+ assert_empty join_records
+ assert_empty george.pirates.reload
join_records = Parrot.connection.select_all("SELECT * FROM parrots_treasures WHERE parrot_id = #{george.id}")
- assert join_records.empty?
- assert george.treasures.reload.empty?
+ assert_empty join_records
+ assert_empty george.treasures.reload
end
def test_associations_with_conditions
@@ -551,7 +547,7 @@ class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase
developer = project.developers.first
assert_no_queries(ignore_none: false) do
- assert project.developers.loaded?
+ assert_predicate project.developers, :loaded?
assert_includes project.developers, developer
end
end
@@ -561,18 +557,18 @@ class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase
developer = project.developers.first
project.reload
- assert ! project.developers.loaded?
+ assert_not_predicate project.developers, :loaded?
assert_queries(1) do
assert_includes project.developers, developer
end
- assert ! project.developers.loaded?
+ assert_not_predicate project.developers, :loaded?
end
def test_include_returns_false_for_non_matching_record_to_verify_scoping
project = projects(:active_record)
developer = Developer.create name: "Bryan", salary: 50_000
- assert ! project.developers.loaded?
+ assert_not_predicate project.developers, :loaded?
assert ! project.developers.include?(developer)
end
@@ -767,9 +763,9 @@ class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase
def test_get_ids_for_unloaded_associations_does_not_load_them
developer = developers(:david)
- assert !developer.projects.loaded?
+ assert_not_predicate developer.projects, :loaded?
assert_equal projects(:active_record, :action_controller).map(&:id).sort, developer.project_ids.sort
- assert !developer.projects.loaded?
+ assert_not_predicate developer.projects, :loaded?
end
def test_assign_ids
@@ -952,13 +948,6 @@ class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase
end
end
- def test_with_constant_class_name
- assert_nothing_raised do
- developer = DeveloperWithConstantClassName.new
- developer.projects
- end
- end
-
def test_alternate_database
professor = Professor.create(name: "Plum")
course = Course.create(name: "Forensics")
diff --git a/activerecord/test/cases/associations/has_many_associations_test.rb b/activerecord/test/cases/associations/has_many_associations_test.rb
index 590d3642bd..9ee42cef0f 100644
--- a/activerecord/test/cases/associations/has_many_associations_test.rb
+++ b/activerecord/test/cases/associations/has_many_associations_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "cases/helper"
require "models/developer"
require "models/computer"
@@ -55,29 +57,40 @@ class HasManyAssociationsTestPrimaryKeys < ActiveRecord::TestCase
def test_custom_primary_key_on_new_record_should_fetch_with_query
subscriber = Subscriber.new(nick: "webster132")
- assert !subscriber.subscriptions.loaded?
+ assert_not_predicate subscriber.subscriptions, :loaded?
assert_queries 1 do
assert_equal 2, subscriber.subscriptions.size
end
- assert_equal subscriber.subscriptions, Subscription.where(subscriber_id: "webster132")
+ assert_equal Subscription.where(subscriber_id: "webster132"), subscriber.subscriptions
end
def test_association_primary_key_on_new_record_should_fetch_with_query
author = Author.new(name: "David")
- assert !author.essays.loaded?
+ assert_not_predicate author.essays, :loaded?
assert_queries 1 do
assert_equal 1, author.essays.size
end
- assert_equal author.essays, Essay.where(writer_id: "David")
+ assert_equal Essay.where(writer_id: "David"), author.essays
end
def test_has_many_custom_primary_key
david = authors(:david)
- assert_equal david.essays, Essay.where(writer_id: "David")
+ assert_equal Essay.where(writer_id: "David"), david.essays
+ end
+
+ def test_ids_on_unloaded_association_with_custom_primary_key
+ david = people(:david)
+ assert_equal Essay.where(writer_id: "David").pluck(:id), david.essay_ids
+ end
+
+ def test_ids_on_loaded_association_with_custom_primary_key
+ david = people(:david)
+ david.essays.load
+ assert_equal Essay.where(writer_id: "David").pluck(:id), david.essay_ids
end
def test_has_many_assignment_with_custom_primary_key
@@ -90,7 +103,7 @@ class HasManyAssociationsTestPrimaryKeys < ActiveRecord::TestCase
def test_blank_custom_primary_key_on_new_record_should_not_run_queries
author = Author.new
- assert !author.essays.loaded?
+ assert_not_predicate author.essays, :loaded?
assert_queries 0 do
assert_equal 0, author.essays.size
@@ -188,7 +201,7 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
part.reload
assert_nil part.ship
- assert !part.updated_at_changed?
+ assert_not_predicate part, :updated_at_changed?
end
def test_create_from_association_should_respect_default_scope
@@ -241,6 +254,13 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
assert_equal "defaulty", bulb.name
end
+ def test_build_from_association_sets_inverse_instance
+ car = Car.new(name: "honda")
+
+ bulb = car.bulbs.build
+ assert_equal car, bulb.car
+ end
+
def test_do_not_call_callbacks_for_delete_all
car = Car.create(name: "honda")
car.funky_bulbs.create!
@@ -424,7 +444,7 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
new_clients << company.clients_of_firm.build(name: "Another Client III")
end
- assert_not company.clients_of_firm.loaded?
+ assert_not_predicate company.clients_of_firm, :loaded?
assert_queries(1) do
assert_same new_clients[0], company.clients_of_firm.third
assert_same new_clients[1], company.clients_of_firm.fourth
@@ -444,7 +464,7 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
new_clients << company.clients_of_firm.build(name: "Another Client III")
end
- assert_not company.clients_of_firm.loaded?
+ assert_not_predicate company.clients_of_firm, :loaded?
assert_queries(1) do
assert_same new_clients[0], company.clients_of_firm.third!
assert_same new_clients[1], company.clients_of_firm.fourth!
@@ -456,7 +476,10 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
end
def test_create_resets_cached_counters
+ Reader.delete_all
+
person = Person.create!(first_name: "tenderlove")
+
post = Post.first
assert_equal [], person.readers
@@ -490,21 +513,20 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
assert_predicate person.references, :exists?
end
- # sometimes tests on Oracle fail if ORDER BY is not provided therefore add always :order with :first
def test_counting_with_counter_sql
- assert_equal 3, Firm.all.merge!(order: "id").first.clients.count
+ assert_equal 3, Firm.first.clients.count
end
def test_counting
- assert_equal 3, Firm.all.merge!(order: "id").first.plain_clients.count
+ assert_equal 3, Firm.first.plain_clients.count
end
def test_counting_with_single_hash
- assert_equal 1, Firm.all.merge!(order: "id").first.plain_clients.where(name: "Microsoft").count
+ assert_equal 1, Firm.first.plain_clients.where(name: "Microsoft").count
end
def test_counting_with_column_name_and_hash
- assert_equal 3, Firm.all.merge!(order: "id").first.plain_clients.count(:name)
+ assert_equal 3, Firm.first.plain_clients.count(:name)
end
def test_counting_with_association_limit
@@ -514,7 +536,7 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
end
def test_finding
- assert_equal 3, Firm.all.merge!(order: "id").first.clients.length
+ assert_equal 3, Firm.first.clients.length
end
def test_finding_array_compatibility
@@ -554,17 +576,25 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
end
def test_taking_with_a_number
+ klass = Class.new(Author) do
+ has_many :posts, -> { order(:id) }
+
+ def self.name
+ "Author"
+ end
+ end
+
# taking from unloaded Relation
- bob = Author.find(authors(:bob).id)
+ bob = klass.find(authors(:bob).id)
new_post = bob.posts.build
- assert_not bob.posts.loaded?
+ assert_not_predicate bob.posts, :loaded?
assert_equal [posts(:misc_by_bob)], bob.posts.take(1)
assert_equal [posts(:misc_by_bob), posts(:other_by_bob)], bob.posts.take(2)
assert_equal [posts(:misc_by_bob), posts(:other_by_bob), new_post], bob.posts.take(3)
# taking from loaded Relation
bob.posts.load
- assert bob.posts.loaded?
+ assert_predicate bob.posts, :loaded?
assert_equal [posts(:misc_by_bob)], bob.posts.take(1)
assert_equal [posts(:misc_by_bob), posts(:other_by_bob)], bob.posts.take(2)
assert_equal [posts(:misc_by_bob), posts(:other_by_bob), new_post], bob.posts.take(3)
@@ -586,27 +616,27 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
end
def test_finding_default_orders
- assert_equal "Summit", Firm.all.merge!(order: "id").first.clients.first.name
+ assert_equal "Summit", Firm.first.clients.first.name
end
def test_finding_with_different_class_name_and_order
- assert_equal "Apex", Firm.all.merge!(order: "id").first.clients_sorted_desc.first.name
+ assert_equal "Apex", Firm.first.clients_sorted_desc.first.name
end
def test_finding_with_foreign_key
- assert_equal "Microsoft", Firm.all.merge!(order: "id").first.clients_of_firm.first.name
+ assert_equal "Microsoft", Firm.first.clients_of_firm.first.name
end
def test_finding_with_condition
- assert_equal "Microsoft", Firm.all.merge!(order: "id").first.clients_like_ms.first.name
+ assert_equal "Microsoft", Firm.first.clients_like_ms.first.name
end
def test_finding_with_condition_hash
- assert_equal "Microsoft", Firm.all.merge!(order: "id").first.clients_like_ms_with_hash_conditions.first.name
+ assert_equal "Microsoft", Firm.first.clients_like_ms_with_hash_conditions.first.name
end
def test_finding_using_primary_key
- assert_equal "Summit", Firm.all.merge!(order: "id").first.clients_using_primary_key.first.name
+ assert_equal "Summit", Firm.first.clients_using_primary_key.first.name
end
def test_update_all_on_association_accessed_before_save
@@ -629,7 +659,7 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
end
def test_find_ids
- firm = Firm.all.merge!(order: "id").first
+ firm = Firm.first
assert_raise(ActiveRecord::RecordNotFound) { firm.clients.find }
@@ -649,7 +679,7 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
end
def test_find_one_message_on_primary_key
- firm = Firm.all.merge!(order: "id").first
+ firm = Firm.first
e = assert_raises(ActiveRecord::RecordNotFound) do
firm.clients.find(0)
@@ -675,7 +705,7 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
end
def test_find_all
- firm = Firm.all.merge!(order: "id").first
+ firm = Firm.first
assert_equal 3, firm.clients.where("#{QUOTED_TYPE} = 'Client'").to_a.length
assert_equal 1, firm.clients.where("name = 'Summit'").to_a.length
end
@@ -683,13 +713,13 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
def test_find_each
firm = companies(:first_firm)
- assert ! firm.clients.loaded?
+ assert_not_predicate firm.clients, :loaded?
assert_queries(4) do
firm.clients.find_each(batch_size: 1) { |c| assert_equal firm.id, c.firm_id }
end
- assert ! firm.clients.loaded?
+ assert_not_predicate firm.clients, :loaded?
end
def test_find_each_with_conditions
@@ -702,13 +732,13 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
end
end
- assert ! firm.clients.loaded?
+ assert_not_predicate firm.clients, :loaded?
end
def test_find_in_batches
firm = companies(:first_firm)
- assert ! firm.clients.loaded?
+ assert_not_predicate firm.clients, :loaded?
assert_queries(2) do
firm.clients.find_in_batches(batch_size: 2) do |clients|
@@ -716,29 +746,63 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
end
end
- assert ! firm.clients.loaded?
+ assert_not_predicate firm.clients, :loaded?
end
def test_find_all_sanitized
- # sometimes tests on Oracle fail if ORDER BY is not provided therefore add always :order with :first
- firm = Firm.all.merge!(order: "id").first
+ firm = Firm.first
summit = firm.clients.where("name = 'Summit'").to_a
assert_equal summit, firm.clients.where("name = ?", "Summit").to_a
assert_equal summit, firm.clients.where("name = :name", name: "Summit").to_a
end
def test_find_first
- firm = Firm.all.merge!(order: "id").first
+ firm = Firm.first
client2 = Client.find(2)
assert_equal firm.clients.first, firm.clients.order("id").first
assert_equal client2, firm.clients.where("#{QUOTED_TYPE} = 'Client'").order("id").first
end
def test_find_first_sanitized
- firm = Firm.all.merge!(order: "id").first
+ firm = Firm.first
client2 = Client.find(2)
- assert_equal client2, firm.clients.merge!(where: ["#{QUOTED_TYPE} = ?", "Client"], order: "id").first
- assert_equal client2, firm.clients.merge!(where: ["#{QUOTED_TYPE} = :type", { type: "Client" }], order: "id").first
+ assert_equal client2, firm.clients.where("#{QUOTED_TYPE} = ?", "Client").first
+ assert_equal client2, firm.clients.where("#{QUOTED_TYPE} = :type", type: "Client").first
+ end
+
+ def test_find_first_after_reset_scope
+ firm = Firm.first
+ collection = firm.clients
+
+ original_object = collection.first
+ assert_same original_object, collection.first, "Expected second call to #first to cache the same object"
+
+ # It should return a different object, since the association has been reloaded
+ assert_not_same original_object, firm.clients.first, "Expected #first to return a new object"
+ end
+
+ def test_find_first_after_reset
+ firm = Firm.first
+ collection = firm.clients
+
+ original_object = collection.first
+ assert_same original_object, collection.first, "Expected second call to #first to cache the same object"
+ collection.reset
+
+ # It should return a different object, since the association has been reloaded
+ assert_not_same original_object, collection.first, "Expected #first after #reset to return a new object"
+ end
+
+ def test_find_first_after_reload
+ firm = Firm.first
+ collection = firm.clients
+
+ original_object = collection.first
+ assert_same original_object, collection.first, "Expected second call to #first to cache the same object"
+ collection.reload
+
+ # It should return a different object, since the association has been reloaded
+ assert_not_same original_object, collection.first, "Expected #first after #reload to return a new object"
end
def test_find_all_with_include_and_conditions
@@ -767,7 +831,7 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
end
def test_find_scoped_grouped_having
- assert_equal 1, authors(:david).popular_grouped_posts.length
+ assert_equal 2, authors(:david).popular_grouped_posts.length
assert_equal 0, authors(:mary).popular_grouped_posts.length
end
@@ -833,7 +897,7 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
def test_create_with_bang_on_has_many_raises_when_record_not_saved
assert_raise(ActiveRecord::RecordInvalid) do
- firm = Firm.all.merge!(order: "id").first
+ firm = Firm.first
firm.plain_clients.create!
end
end
@@ -891,20 +955,20 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
def test_new_aliased_to_build
company = companies(:first_firm)
new_client = assert_no_queries(ignore_none: false) { company.clients_of_firm.new("name" => "Another Client") }
- assert !company.clients_of_firm.loaded?
+ assert_not_predicate company.clients_of_firm, :loaded?
assert_equal "Another Client", new_client.name
- assert !new_client.persisted?
+ assert_not_predicate new_client, :persisted?
assert_equal new_client, company.clients_of_firm.last
end
def test_build
company = companies(:first_firm)
new_client = assert_no_queries(ignore_none: false) { company.clients_of_firm.build("name" => "Another Client") }
- assert !company.clients_of_firm.loaded?
+ assert_not_predicate company.clients_of_firm, :loaded?
assert_equal "Another Client", new_client.name
- assert !new_client.persisted?
+ assert_not_predicate new_client, :persisted?
assert_equal new_client, company.clients_of_firm.last
end
@@ -918,9 +982,9 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
def test_collection_not_empty_after_building
company = companies(:first_firm)
- assert_predicate company.contracts, :empty?
+ assert_empty company.contracts
company.contracts.build
- assert_not_predicate company.contracts, :empty?
+ assert_not_empty company.contracts
end
def test_collection_size_twice_for_regressions
@@ -944,7 +1008,7 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
def test_build_followed_by_save_does_not_load_target
companies(:first_firm).clients_of_firm.build("name" => "Another Client")
assert companies(:first_firm).save
- assert !companies(:first_firm).clients_of_firm.loaded?
+ assert_not_predicate companies(:first_firm).clients_of_firm, :loaded?
end
def test_build_without_loading_association
@@ -964,10 +1028,10 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
def test_build_via_block
company = companies(:first_firm)
new_client = assert_no_queries(ignore_none: false) { company.clients_of_firm.build { |client| client.name = "Another Client" } }
- assert !company.clients_of_firm.loaded?
+ assert_not_predicate company.clients_of_firm, :loaded?
assert_equal "Another Client", new_client.name
- assert !new_client.persisted?
+ assert_not_predicate new_client, :persisted?
assert_equal new_client, company.clients_of_firm.last
end
@@ -1005,7 +1069,7 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
assert_predicate companies(:first_firm).clients_of_firm, :loaded?
new_client = companies(:first_firm).clients_of_firm.create("name" => "Another Client")
- assert new_client.persisted?
+ assert_predicate new_client, :persisted?
assert_equal new_client, companies(:first_firm).clients_of_firm.last
assert_equal new_client, companies(:first_firm).clients_of_firm.reload.last
end
@@ -1018,7 +1082,7 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
def test_create_followed_by_save_does_not_load_target
companies(:first_firm).clients_of_firm.create("name" => "Another Client")
assert companies(:first_firm).save
- assert !companies(:first_firm).clients_of_firm.loaded?
+ assert_not_predicate companies(:first_firm).clients_of_firm, :loaded?
end
def test_deleting
@@ -1044,7 +1108,7 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
# option is not given on the association.
ship = Ship.create(name: "Countless", treasures_count: 10)
- assert_not Ship.reflect_on_association(:treasures).has_cached_counter?
+ assert_not_predicate Ship.reflect_on_association(:treasures), :has_cached_counter?
# Count should come from sql count() of treasures rather than treasures_count attribute
assert_equal ship.treasures.size, 0
@@ -1135,7 +1199,7 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
def test_calling_empty_with_counter_cache
post = posts(:welcome)
assert_queries(0) do
- assert_not post.comments.empty?
+ assert_not_empty post.comments
end
end
@@ -1377,13 +1441,13 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
def test_creation_respects_hash_condition
ms_client = companies(:first_firm).clients_like_ms_with_hash_conditions.build
- assert ms_client.save
- assert_equal "Microsoft", ms_client.name
+ assert ms_client.save
+ assert_equal "Microsoft", ms_client.name
another_ms_client = companies(:first_firm).clients_like_ms_with_hash_conditions.create
- assert another_ms_client.persisted?
- assert_equal "Microsoft", another_ms_client.name
+ assert_predicate another_ms_client, :persisted?
+ assert_equal "Microsoft", another_ms_client.name
end
def test_clearing_without_initial_access
@@ -1506,7 +1570,7 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
firm = companies(:first_firm)
assert_equal 3, firm.clients.size
firm.destroy
- assert Client.all.merge!(where: "firm_id=#{firm.id}").to_a.empty?
+ assert_empty Client.all.merge!(where: "firm_id=#{firm.id}").to_a
end
def test_dependence_for_associations_with_hash_condition
@@ -1515,8 +1579,7 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
end
def test_destroy_dependent_when_deleted_from_association
- # sometimes tests on Oracle fail if ORDER BY is not provided therefore add always :order with :first
- firm = Firm.all.merge!(order: "id").first
+ firm = Firm.first
assert_equal 3, firm.clients.size
client = firm.clients.first
@@ -1570,7 +1633,7 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
firm = RestrictedWithExceptionFirm.create!(name: "restrict")
firm.companies.create(name: "child")
- assert !firm.companies.empty?
+ assert_not_empty firm.companies
assert_raise(ActiveRecord::DeleteRestrictionError) { firm.destroy }
assert RestrictedWithExceptionFirm.exists?(name: "restrict")
assert firm.companies.exists?(name: "child")
@@ -1580,11 +1643,11 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
firm = RestrictedWithErrorFirm.create!(name: "restrict")
firm.companies.create(name: "child")
- assert !firm.companies.empty?
+ assert_not_empty firm.companies
firm.destroy
- assert !firm.errors.empty?
+ assert_not_empty firm.errors
assert_equal "Cannot delete record because dependent companies exist", firm.errors[:base].first
assert RestrictedWithErrorFirm.exists?(name: "restrict")
@@ -1597,11 +1660,11 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
firm = RestrictedWithErrorFirm.create!(name: "restrict")
firm.companies.create(name: "child")
- assert !firm.companies.empty?
+ assert_not_empty firm.companies
firm.destroy
- assert !firm.errors.empty?
+ assert_not_empty firm.errors
assert_equal "Cannot delete record because dependent client companies exist", firm.errors[:base].first
assert RestrictedWithErrorFirm.exists?(name: "restrict")
@@ -1626,7 +1689,7 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
end
def test_replace_with_less
- firm = Firm.all.merge!(order: "id").first
+ firm = Firm.first
firm.clients = [companies(:first_client)]
assert firm.save, "Could not save firm"
firm.reload
@@ -1640,7 +1703,7 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
end
def test_replace_with_new
- firm = Firm.all.merge!(order: "id").first
+ firm = Firm.first
firm.clients = [companies(:second_client), Client.new("name" => "New Client")]
firm.save
firm.reload
@@ -1653,8 +1716,8 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
account = Account.new
orig_accounts = firm.accounts.to_a
- assert !account.valid?
- assert !orig_accounts.empty?
+ assert_not_predicate account, :valid?
+ assert_not_empty orig_accounts
error = assert_raise ActiveRecord::RecordNotSaved do
firm.accounts = [account]
end
@@ -1712,9 +1775,9 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
def test_get_ids_for_unloaded_associations_does_not_load_them
company = companies(:first_firm)
- assert !company.clients.loaded?
+ assert_not_predicate company.clients, :loaded?
assert_equal [companies(:first_client).id, companies(:second_client).id, companies(:another_first_firm_client).id], company.client_ids
- assert !company.clients.loaded?
+ assert_not_predicate company.clients, :loaded?
end
def test_counter_cache_on_unloaded_association
@@ -1796,7 +1859,7 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
client = firm.clients.first
assert_no_queries do
- assert firm.clients.loaded?
+ assert_predicate firm.clients, :loaded?
assert_equal true, firm.clients.include?(client)
end
end
@@ -1806,18 +1869,18 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
client = firm.clients.first
firm.reload
- assert ! firm.clients.loaded?
+ assert_not_predicate firm.clients, :loaded?
assert_queries(1) do
assert_equal true, firm.clients.include?(client)
end
- assert ! firm.clients.loaded?
+ assert_not_predicate firm.clients, :loaded?
end
def test_include_returns_false_for_non_matching_record_to_verify_scoping
firm = companies(:first_firm)
client = Client.create!(name: "Not Associated")
- assert ! firm.clients.loaded?
+ assert_not_predicate firm.clients, :loaded?
assert_equal false, firm.clients.include?(client)
end
@@ -1826,13 +1889,13 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
firm.clients.first
firm.clients.second
firm.clients.last
- assert !firm.clients.loaded?
+ assert_not_predicate firm.clients, :loaded?
end
def test_calling_first_or_last_on_loaded_association_should_not_fetch_with_query
firm = companies(:first_firm)
firm.clients.load_target
- assert firm.clients.loaded?
+ assert_predicate firm.clients, :loaded?
assert_no_queries(ignore_none: false) do
firm.clients.first
@@ -1845,7 +1908,7 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
def test_calling_first_or_last_on_existing_record_with_build_should_load_association
firm = companies(:first_firm)
firm.clients.build(name: "Foo")
- assert !firm.clients.loaded?
+ assert_not_predicate firm.clients, :loaded?
assert_queries 1 do
firm.clients.first
@@ -1853,13 +1916,13 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
firm.clients.last
end
- assert firm.clients.loaded?
+ assert_predicate firm.clients, :loaded?
end
def test_calling_first_nth_or_last_on_existing_record_with_create_should_not_load_association
firm = companies(:first_firm)
firm.clients.create(name: "Foo")
- assert !firm.clients.loaded?
+ assert_not_predicate firm.clients, :loaded?
assert_queries 3 do
firm.clients.first
@@ -1867,7 +1930,7 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
firm.clients.last
end
- assert !firm.clients.loaded?
+ assert_not_predicate firm.clients, :loaded?
end
def test_calling_first_nth_or_last_on_new_record_should_not_run_queries
@@ -1883,14 +1946,14 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
def test_calling_first_or_last_with_integer_on_association_should_not_load_association
firm = companies(:first_firm)
firm.clients.create(name: "Foo")
- assert !firm.clients.loaded?
+ assert_not_predicate firm.clients, :loaded?
assert_queries 2 do
firm.clients.first(2)
firm.clients.last(2)
end
- assert !firm.clients.loaded?
+ assert_not_predicate firm.clients, :loaded?
end
def test_calling_many_should_count_instead_of_loading_association
@@ -1898,7 +1961,7 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
assert_queries(1) do
firm.clients.many? # use count query
end
- assert !firm.clients.loaded?
+ assert_not_predicate firm.clients, :loaded?
end
def test_calling_many_on_loaded_association_should_not_use_query
@@ -1913,22 +1976,22 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
firm.clients.expects(:size).never
firm.clients.many? { true }
end
- assert firm.clients.loaded?
+ assert_predicate firm.clients, :loaded?
end
def test_calling_many_should_return_false_if_none_or_one
firm = companies(:another_firm)
- assert !firm.clients_like_ms.many?
+ assert_not_predicate firm.clients_like_ms, :many?
assert_equal 0, firm.clients_like_ms.size
firm = companies(:first_firm)
- assert !firm.limited_clients.many?
+ assert_not_predicate firm.limited_clients, :many?
assert_equal 1, firm.limited_clients.size
end
def test_calling_many_should_return_true_if_more_than_one
firm = companies(:first_firm)
- assert firm.clients.many?
+ assert_predicate firm.clients, :many?
assert_equal 3, firm.clients.size
end
@@ -1937,7 +2000,7 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
assert_queries(1) do
firm.clients.none? # use count query
end
- assert !firm.clients.loaded?
+ assert_not_predicate firm.clients, :loaded?
end
def test_calling_none_on_loaded_association_should_not_use_query
@@ -1952,18 +2015,18 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
firm.clients.expects(:size).never
firm.clients.none? { true }
end
- assert firm.clients.loaded?
+ assert_predicate firm.clients, :loaded?
end
def test_calling_none_should_return_true_if_none
firm = companies(:another_firm)
- assert firm.clients_like_ms.none?
+ assert_predicate firm.clients_like_ms, :none?
assert_equal 0, firm.clients_like_ms.size
end
def test_calling_none_should_return_false_if_any
firm = companies(:first_firm)
- assert !firm.limited_clients.none?
+ assert_not_predicate firm.limited_clients, :none?
assert_equal 1, firm.limited_clients.size
end
@@ -1972,7 +2035,7 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
assert_queries(1) do
firm.clients.one? # use count query
end
- assert !firm.clients.loaded?
+ assert_not_predicate firm.clients, :loaded?
end
def test_calling_one_on_loaded_association_should_not_use_query
@@ -1987,24 +2050,24 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
firm.clients.expects(:size).never
firm.clients.one? { true }
end
- assert firm.clients.loaded?
+ assert_predicate firm.clients, :loaded?
end
def test_calling_one_should_return_false_if_zero
firm = companies(:another_firm)
- assert ! firm.clients_like_ms.one?
+ assert_not_predicate firm.clients_like_ms, :one?
assert_equal 0, firm.clients_like_ms.size
end
def test_calling_one_should_return_true_if_one
firm = companies(:first_firm)
- assert firm.limited_clients.one?
+ assert_predicate firm.limited_clients, :one?
assert_equal 1, firm.limited_clients.size
end
def test_calling_one_should_return_false_if_more_than_one
firm = companies(:first_firm)
- assert ! firm.clients.one?
+ assert_not_predicate firm.clients, :one?
assert_equal 3, firm.clients.size
end
@@ -2038,7 +2101,7 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
end
def test_creating_using_primary_key
- firm = Firm.all.merge!(order: "id").first
+ firm = Firm.first
client = firm.clients_using_primary_key.create!(name: "test")
assert_equal firm.name, client.firm_name
end
@@ -2140,6 +2203,13 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
assert_equal "Post", tagging.taggable_type
end
+ def test_build_from_polymorphic_association_sets_inverse_instance
+ post = Post.new
+ tagging = post.taggings.build
+
+ assert_equal post, tagging.taggable
+ end
+
def test_dont_call_save_callbacks_twice_on_has_many
firm = companies(:first_firm)
contract = firm.contracts.create!
@@ -2217,7 +2287,7 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
post = posts(:welcome)
assert post.taggings_with_delete_all.count > 0
- assert !post.taggings_with_delete_all.loaded?
+ assert_not_predicate post.taggings_with_delete_all, :loaded?
# 2 queries: one DELETE and another to update the counter cache
assert_queries(2) do
@@ -2239,7 +2309,7 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
test "collection proxy respects default scope" do
author = authors(:mary)
- assert !author.first_posts.exists?
+ assert_not_predicate author.first_posts, :exists?
end
test "association with extend option" do
@@ -2280,7 +2350,7 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
test "does not duplicate associations when used with natural primary keys" do
speedometer = Speedometer.create!(id: "4")
- speedometer.minivans.create!(minivan_id: "a-van-red" , name: "a van", color: "red")
+ speedometer.minivans.create!(minivan_id: "a-van-red", name: "a van", color: "red")
assert_equal 1, speedometer.minivans.to_a.size, "Only one association should be present:\n#{speedometer.minivans.to_a}"
assert_equal 1, speedometer.reload.minivans.to_a.size
@@ -2319,8 +2389,9 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
car = Car.create!
bulb = Bulb.create! name: "other", car: car
- assert_equal bulb, Car.find(car.id).all_bulbs.first
- assert_equal bulb, Car.includes(:all_bulbs).find(car.id).all_bulbs.first
+ assert_equal [bulb], Car.find(car.id).all_bulbs
+ assert_equal [bulb], Car.includes(:all_bulbs).find(car.id).all_bulbs
+ assert_equal [bulb], Car.eager_load(:all_bulbs).find(car.id).all_bulbs
end
test "raises RecordNotDestroyed when replaced child can't be destroyed" do
@@ -2357,7 +2428,7 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
pirate = FamousPirate.new
pirate.famous_ships << ship = FamousShip.new
- assert pirate.valid?
+ assert_predicate pirate, :valid?
assert_not pirate.valid?(:conference)
assert_equal "can't be blank", ship.errors[:name].first
end
@@ -2438,6 +2509,15 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
assert_same car, new_bulb.car
end
+ test "reattach to new objects replaces inverse association and foreign key" do
+ bulb = Bulb.create!(car: Car.create!)
+ assert bulb.car_id
+ car = Car.new
+ car.bulbs << bulb
+ assert_equal car, bulb.car
+ assert_nil bulb.car_id
+ end
+
test "in memory replacement maintains order" do
first_bulb = Bulb.create!
second_bulb = Bulb.create!
@@ -2449,6 +2529,14 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
assert_equal [first_bulb, second_bulb], car.bulbs
end
+ test "association size calculation works with default scoped selects when not previously fetched" do
+ firm = Firm.create!(name: "Firm")
+ 5.times { firm.developers_with_select << Developer.create!(name: "Developer") }
+
+ same_firm = Firm.find(firm.id)
+ assert_equal 5, same_firm.developers_with_select.size
+ end
+
test "prevent double insertion of new object when the parent association loaded in the after save callback" do
reset_callbacks(:save, Bulb) do
Bulb.after_save { |record| record.car.bulbs.load }
@@ -2508,6 +2596,11 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
assert_equal [bulb.id], car.bulb_ids
assert_no_queries { car.bulb_ids }
+
+ bulb2 = car.bulbs.create!
+
+ assert_equal [bulb.id, bulb2.id], car.bulb_ids
+ assert_no_queries { car.bulb_ids }
end
def test_loading_association_in_validate_callback_doesnt_affect_persistence
diff --git a/activerecord/test/cases/associations/has_many_through_associations_test.rb b/activerecord/test/cases/associations/has_many_through_associations_test.rb
index 9156f6d57a..25118b26f5 100644
--- a/activerecord/test/cases/associations/has_many_through_associations_test.rb
+++ b/activerecord/test/cases/associations/has_many_through_associations_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "cases/helper"
require "models/post"
require "models/person"
@@ -319,6 +321,17 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
assert_includes post.single_people, person
end
+ def test_build_then_remove_then_save
+ post = posts(:thinking)
+ post.people.build(first_name: "Bob")
+ ted = post.people.build(first_name: "Ted")
+ post.people.delete(ted)
+ post.save!
+ post.reload
+
+ assert_equal ["Bob"], post.people.collect(&:first_name)
+ end
+
def test_both_parent_ids_set_when_saving_new
post = Post.new(title: "Hello", body: "world")
person = Person.new(first_name: "Sean")
@@ -340,10 +353,10 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
end
assert_queries(1) do
- assert posts(:welcome).people.empty?
+ assert_empty posts(:welcome).people
end
- assert posts(:welcome).reload.people.reload.empty?
+ assert_empty posts(:welcome).reload.people.reload
end
def test_destroy_association
@@ -353,8 +366,8 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
end
end
- assert posts(:welcome).reload.people.empty?
- assert posts(:welcome).people.reload.empty?
+ assert_empty posts(:welcome).reload.people
+ assert_empty posts(:welcome).people.reload
end
def test_destroy_all
@@ -364,8 +377,8 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
end
end
- assert posts(:welcome).reload.people.empty?
- assert posts(:welcome).people.reload.empty?
+ assert_empty posts(:welcome).reload.people
+ assert_empty posts(:welcome).people.reload
end
def test_should_raise_exception_for_destroying_mismatching_records
@@ -525,6 +538,16 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
end
end
+ def test_update_counter_caches_on_destroy_with_indestructible_through_record
+ post = posts(:welcome)
+ tag = post.indestructible_tags.create!(name: "doomed")
+ post.update_columns(indestructible_tags_count: post.indestructible_tags.count)
+
+ assert_no_difference "post.reload.indestructible_tags_count" do
+ posts(:welcome).indestructible_tags.destroy(tag)
+ end
+ end
+
def test_replace_association
assert_queries(4) { posts(:welcome);people(:david);people(:michael); posts(:welcome).people.reload }
@@ -662,10 +685,10 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
end
assert_queries(0) do
- assert posts(:welcome).people.empty?
+ assert_empty posts(:welcome).people
end
- assert posts(:welcome).reload.people.reload.empty?
+ assert_empty posts(:welcome).reload.people.reload
end
def test_association_callback_ordering
@@ -747,9 +770,9 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
def test_get_ids_for_unloaded_associations_does_not_load_them
person = people(:michael)
- assert !person.posts.loaded?
+ assert_not_predicate person.posts, :loaded?
assert_equal [posts(:welcome).id, posts(:authorless).id].sort, person.post_ids.sort
- assert !person.posts.loaded?
+ assert_not_predicate person.posts, :loaded?
end
def test_association_proxy_transaction_method_starts_transaction_in_association_class
@@ -804,7 +827,7 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
sarah = Person.create!(first_name: "Sarah", primary_contact_id: people(:susan).id, gender: "F", number1_fan_id: 1)
john = Person.create!(first_name: "John", primary_contact_id: sarah.id, gender: "M", number1_fan_id: 1)
assert_equal sarah.agents, [john]
- assert_equal people(:susan).agents.flat_map(&:agents), people(:susan).agents_of_agents
+ assert_equal people(:susan).agents.flat_map(&:agents).sort, people(:susan).agents_of_agents.sort
end
def test_associate_existing_with_nonstandard_primary_key_on_belongs_to
@@ -839,7 +862,7 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
category = author.named_categories.create(name: "Primary")
author.named_categories.delete(category)
assert !Categorization.exists?(author_id: author.id, named_category_name: category.name)
- assert author.named_categories.reload.empty?
+ assert_empty author.named_categories.reload
end
def test_collection_singular_ids_getter_with_string_primary_keys
@@ -856,6 +879,14 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
assert_equal [dev], company.developers
end
+ def test_collection_singular_ids_setter_with_required_type_cast
+ company = companies(:rails_core)
+ dev = Developer.first
+
+ company.developer_ids = [dev.id.to_s]
+ assert_equal [dev], company.developers
+ end
+
def test_collection_singular_ids_setter_with_string_primary_keys
assert_nothing_raised do
book = books(:awdr)
@@ -867,32 +898,20 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
end
end
- def test_collection_singular_ids_setter_with_changed_primary_key
- company = companies(:first_firm)
- client = companies(:first_client)
- company.clients_using_primary_key_ids = [client.name]
- assert_equal [client], company.clients_using_primary_key
- end
-
def test_collection_singular_ids_setter_raises_exception_when_invalid_ids_set
company = companies(:rails_core)
ids = [Developer.first.id, -9999]
e = assert_raises(ActiveRecord::RecordNotFound) { company.developer_ids = ids }
- assert_match(/Couldn't find all Developers with 'id'/, e.message)
- end
-
- def test_collection_singular_ids_setter_raises_exception_when_invalid_ids_set_with_changed_primary_key
- company = companies(:first_firm)
- ids = [Client.first.name, "unknown client"]
- e = assert_raises(ActiveRecord::RecordNotFound) { company.clients_using_primary_key_ids = ids }
- assert_match(/Couldn't find all Clients with 'name'/, e.message)
+ msg = "Couldn't find all Developers with 'id': (1, -9999) (found 1 results, but was looking for 2). Couldn't find Developer with id -9999."
+ assert_equal(msg, e.message)
end
def test_collection_singular_ids_through_setter_raises_exception_when_invalid_ids_set
author = authors(:david)
ids = [categories(:general).name, "Unknown"]
e = assert_raises(ActiveRecord::RecordNotFound) { author.essay_category_ids = ids }
- assert_equal "Couldn't find all Categories with 'name': (General, Unknown) (found 1 results, but was looking for 2)", e.message
+ msg = "Couldn't find all Categories with 'name': (General, Unknown) (found 1 results, but was looking for 2). Couldn't find Category with name Unknown."
+ assert_equal msg, e.message
end
def test_build_a_model_from_hm_through_association_with_where_clause
@@ -925,8 +944,8 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
end
def test_through_association_readonly_should_be_false
- assert !people(:michael).posts.first.readonly?
- assert !people(:michael).posts.to_a.first.readonly?
+ assert_not_predicate people(:michael).posts.first, :readonly?
+ assert_not_predicate people(:michael).posts.to_a.first, :readonly?
end
def test_can_update_through_association
@@ -935,6 +954,13 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
end
end
+ def test_has_many_through_polymorphic_with_rewhere
+ post = TaggedPost.create!(title: "Tagged", body: "Post")
+ tag = post.tags.create!(name: "Tag")
+ assert_equal [tag], TaggedPost.preload(:tags).last.tags
+ assert_equal [tag], TaggedPost.eager_load(:tags).last.tags
+ end
+
def test_has_many_through_polymorphic_with_primary_key_option
assert_equal [categories(:general)], authors(:david).essay_categories
@@ -1008,12 +1034,12 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
post.author_categorizations
proxy = post.send(:association_instance_get, :author_categorizations)
- assert !proxy.stale_target?
+ assert_not_predicate proxy, :stale_target?
assert_equal authors(:mary).categorizations.sort_by(&:id), post.author_categorizations.sort_by(&:id)
post.author_id = authors(:david).id
- assert proxy.stale_target?
+ assert_predicate proxy, :stale_target?
assert_equal authors(:david).categorizations.sort_by(&:id), post.author_categorizations.sort_by(&:id)
end
@@ -1030,7 +1056,7 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
assert_includes post.author_addresses, address
post.author_addresses.delete(address)
- assert post[:author_count].nil?
+ assert_predicate post[:author_count], :nil?
end
def test_primary_key_option_on_source
@@ -1117,6 +1143,32 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
assert_equal ["parrot", "bulbul"], owner.toys.map { |r| r.pet.name }
end
+ def test_has_many_through_associations_sum_on_columns
+ post1 = Post.create(title: "active", body: "sample")
+ post2 = Post.create(title: "inactive", body: "sample")
+
+ person1 = Person.create(first_name: "aaron", followers_count: 1)
+ person2 = Person.create(first_name: "schmit", followers_count: 2)
+ person3 = Person.create(first_name: "bill", followers_count: 3)
+ person4 = Person.create(first_name: "cal", followers_count: 4)
+
+ Reader.create(post_id: post1.id, person_id: person1.id)
+ Reader.create(post_id: post1.id, person_id: person2.id)
+ Reader.create(post_id: post1.id, person_id: person3.id)
+ Reader.create(post_id: post1.id, person_id: person4.id)
+
+ Reader.create(post_id: post2.id, person_id: person1.id)
+ Reader.create(post_id: post2.id, person_id: person2.id)
+ Reader.create(post_id: post2.id, person_id: person3.id)
+ Reader.create(post_id: post2.id, person_id: person4.id)
+
+ active_persons = Person.joins(:readers).joins(:posts).distinct(true).where("posts.title" => "active")
+
+ assert_equal active_persons.map(&:followers_count).reduce(:+), 10
+ assert_equal active_persons.sum(:followers_count), 10
+ assert_equal active_persons.sum(:followers_count), active_persons.map(&:followers_count).reduce(:+)
+ end
+
def test_has_many_through_associations_on_new_records_use_null_relations
person = Person.new
@@ -1216,6 +1268,18 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
TenantMembership.current_member = nil
end
+ def test_has_many_through_with_scope_that_has_joined_same_table_with_parent_relation
+ assert_equal authors(:david), Author.joins(:comments_for_first_author).take
+ end
+
+ def test_has_many_through_with_unscope_should_affect_to_through_scope
+ assert_equal [comments(:eager_other_comment1)], authors(:mary).unordered_comments
+ end
+
+ def test_has_many_through_with_scope_should_accept_string_and_hash_join
+ assert_equal authors(:david), Author.joins({ comments_for_first_author: :post }, "inner join posts posts_alias on authors.id = posts_alias.author_id").eager_load(:categories).take
+ end
+
def test_has_many_through_with_scope_should_respect_table_alias
family = Family.create!
users = 3.times.map { User.create! }
@@ -1227,6 +1291,25 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
assert_equal 0, users[2].family_members.to_a.size
end
+ def test_through_scope_is_affected_by_unscoping
+ author = authors(:david)
+
+ expected = author.comments.to_a
+ FirstPost.unscoped do
+ assert_equal expected.sort_by(&:id), author.comments_on_first_posts.sort_by(&:id)
+ end
+ end
+
+ def test_through_scope_isnt_affected_by_scoping
+ author = authors(:david)
+
+ expected = author.comments_on_first_posts.to_a
+ FirstPost.where(id: 2).scoping do
+ author.comments_on_first_posts.reset
+ assert_equal expected.sort_by(&:id), author.comments_on_first_posts.sort_by(&:id)
+ end
+ end
+
def test_incorrectly_ordered_through_associations
assert_raises(ActiveRecord::HasManyThroughOrderError) do
DeveloperWithIncorrectlyOrderedHasManyThrough.create(
@@ -1235,6 +1318,70 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
end
end
+ def test_has_many_through_update_ids_with_conditions
+ author = Author.create!(name: "Bill")
+ category = categories(:general)
+
+ author.update(
+ special_categories_with_condition_ids: [category.id],
+ nonspecial_categories_with_condition_ids: [category.id]
+ )
+
+ assert_equal [category.id], author.special_categories_with_condition_ids
+ assert_equal [category.id], author.nonspecial_categories_with_condition_ids
+
+ author.update(nonspecial_categories_with_condition_ids: [])
+ author.reload
+
+ assert_equal [category.id], author.special_categories_with_condition_ids
+ assert_equal [], author.nonspecial_categories_with_condition_ids
+ end
+
+ def test_single_has_many_through_association_with_unpersisted_parent_instance
+ post_with_single_has_many_through = Class.new(Post) do
+ def self.name; "PostWithSingleHasManyThrough"; end
+ has_many :subscriptions, through: :author
+ end
+ post = post_with_single_has_many_through.new
+
+ post.author = authors(:mary)
+ book1 = Book.create!(name: "essays on single has many through associations 1")
+ post.author.books << book1
+ subscription1 = Subscription.first
+ book1.subscriptions << subscription1
+ assert_equal [subscription1], post.subscriptions.to_a
+
+ post.author = authors(:bob)
+ book2 = Book.create!(name: "essays on single has many through associations 2")
+ post.author.books << book2
+ subscription2 = Subscription.second
+ book2.subscriptions << subscription2
+ assert_equal [subscription2], post.subscriptions.to_a
+ end
+
+ def test_nested_has_many_through_association_with_unpersisted_parent_instance
+ post_with_nested_has_many_through = Class.new(Post) do
+ def self.name; "PostWithNestedHasManyThrough"; end
+ has_many :books, through: :author
+ has_many :subscriptions, through: :books
+ end
+ post = post_with_nested_has_many_through.new
+
+ post.author = authors(:mary)
+ book1 = Book.create!(name: "essays on nested has many through associations 1")
+ post.author.books << book1
+ subscription1 = Subscription.first
+ book1.subscriptions << subscription1
+ assert_equal [subscription1], post.subscriptions.to_a
+
+ post.author = authors(:bob)
+ book2 = Book.create!(name: "essays on nested has many through associations 2")
+ post.author.books << book2
+ subscription2 = Subscription.second
+ book2.subscriptions << subscription2
+ assert_equal [subscription2], post.subscriptions.to_a
+ end
+
private
def make_model(name)
Class.new(ActiveRecord::Base) { define_singleton_method(:name) { name } }
diff --git a/activerecord/test/cases/associations/has_one_associations_test.rb b/activerecord/test/cases/associations/has_one_associations_test.rb
index bf3b8dcd63..1a213ef7e4 100644
--- a/activerecord/test/cases/associations/has_one_associations_test.rb
+++ b/activerecord/test/cases/associations/has_one_associations_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "cases/helper"
require "models/developer"
require "models/computer"
@@ -13,7 +15,7 @@ require "models/post"
class HasOneAssociationsTest < ActiveRecord::TestCase
self.use_transactional_tests = false unless supports_savepoints?
- fixtures :accounts, :companies, :developers, :projects, :developers_projects, :ships, :pirates
+ fixtures :accounts, :companies, :developers, :projects, :developers_projects, :ships, :pirates, :authors, :author_addresses
def setup
Account.destroyed_account_ids.clear
@@ -28,7 +30,8 @@ class HasOneAssociationsTest < ActiveRecord::TestCase
ActiveRecord::SQLCounter.clear_log
companies(:first_firm).account
ensure
- assert ActiveRecord::SQLCounter.log_all.all? { |sql| /order by/i !~ sql }, "ORDER BY was used in the query"
+ log_all = ActiveRecord::SQLCounter.log_all
+ assert log_all.all? { |sql| /order by/i !~ sql }, "ORDER BY was used in the query: #{log_all}"
end
def test_has_one_cache_nils
@@ -111,8 +114,8 @@ class HasOneAssociationsTest < ActiveRecord::TestCase
developer = Developer.create!(name: "Someone")
ship = Ship.create!(name: "Planet Caravan", developer: developer)
ship.destroy
- assert !ship.persisted?
- assert !developer.persisted?
+ assert_not_predicate ship, :persisted?
+ assert_not_predicate developer, :persisted?
end
def test_natural_assignment_to_nil_after_destroy
@@ -183,7 +186,7 @@ class HasOneAssociationsTest < ActiveRecord::TestCase
assert_raise(ActiveRecord::DeleteRestrictionError) { firm.destroy }
assert RestrictedWithExceptionFirm.exists?(name: "restrict")
- assert firm.account.present?
+ assert_predicate firm.account, :present?
end
def test_restrict_with_error
@@ -194,10 +197,10 @@ class HasOneAssociationsTest < ActiveRecord::TestCase
firm.destroy
- assert !firm.errors.empty?
+ assert_not_empty firm.errors
assert_equal "Cannot delete record because a dependent account exists", firm.errors[:base].first
assert RestrictedWithErrorFirm.exists?(name: "restrict")
- assert firm.account.present?
+ assert_predicate firm.account, :present?
end
def test_restrict_with_error_with_locale
@@ -210,10 +213,10 @@ class HasOneAssociationsTest < ActiveRecord::TestCase
firm.destroy
- assert !firm.errors.empty?
+ assert_not_empty firm.errors
assert_equal "Cannot delete record because a dependent firm account exists", firm.errors[:base].first
assert RestrictedWithErrorFirm.exists?(name: "restrict")
- assert firm.account.present?
+ assert_predicate firm.account, :present?
ensure
I18n.backend.reload!
end
@@ -374,7 +377,7 @@ class HasOneAssociationsTest < ActiveRecord::TestCase
def test_assignment_before_child_saved
firm = Firm.find(1)
firm.account = a = Account.new("credit_limit" => 1000)
- assert a.persisted?
+ assert_predicate a, :persisted?
assert_equal a, firm.account
assert_equal a, firm.account
firm.association(:account).reload
@@ -392,7 +395,7 @@ class HasOneAssociationsTest < ActiveRecord::TestCase
def test_cant_save_readonly_association
assert_raise(ActiveRecord::ReadOnlyRecord) { companies(:first_firm).readonly_account.save! }
- assert companies(:first_firm).readonly_account.readonly?
+ assert_predicate companies(:first_firm).readonly_account, :readonly?
end
def test_has_one_proxy_should_not_respond_to_private_methods
@@ -430,7 +433,7 @@ class HasOneAssociationsTest < ActiveRecord::TestCase
def test_create_respects_hash_condition
account = companies(:first_firm).create_account_limit_500_with_hash_conditions
- assert account.persisted?
+ assert_predicate account, :persisted?
assert_equal 500, account.credit_limit
end
@@ -447,7 +450,7 @@ class HasOneAssociationsTest < ActiveRecord::TestCase
new_ship = pirate.create_ship
assert_not_equal ships(:black_pearl), new_ship
assert_equal new_ship, pirate.ship
- assert new_ship.new_record?
+ assert_predicate new_ship, :new_record?
assert_nil orig_ship.pirate_id
assert !orig_ship.changed? # check it was saved
end
@@ -457,8 +460,8 @@ class HasOneAssociationsTest < ActiveRecord::TestCase
orig_ship = pirate.dependent_ship
new_ship = pirate.create_dependent_ship
- assert new_ship.new_record?
- assert orig_ship.destroyed?
+ assert_predicate new_ship, :new_record?
+ assert_predicate orig_ship, :destroyed?
end
def test_creation_failure_due_to_new_record_should_raise_error
@@ -478,7 +481,7 @@ class HasOneAssociationsTest < ActiveRecord::TestCase
pirate = pirates(:blackbeard)
pirate.ship.name = nil
- assert !pirate.ship.valid?
+ assert_not_predicate pirate.ship, :valid?
error = assert_raise(ActiveRecord::RecordNotSaved) do
pirate.ship = ships(:interceptor)
end
@@ -585,7 +588,7 @@ class HasOneAssociationsTest < ActiveRecord::TestCase
ship.save!
ship.name = "new name"
- assert ship.changed?
+ assert_predicate ship, :changed?
assert_queries(1) do
# One query for updating name, not triggering query for updating pirate_id
pirate.ship = ship
@@ -688,4 +691,38 @@ class HasOneAssociationsTest < ActiveRecord::TestCase
SpecialAuthor.joins(book: :subscription).where.not(where_clause)
end
end
+
+ class DestroyByParentBook < ActiveRecord::Base
+ self.table_name = "books"
+ belongs_to :author, class_name: "DestroyByParentAuthor"
+ before_destroy :dont, unless: :destroyed_by_association
+
+ def dont
+ throw(:abort)
+ end
+ end
+
+ class DestroyByParentAuthor < ActiveRecord::Base
+ self.table_name = "authors"
+ has_one :book, class_name: "DestroyByParentBook", foreign_key: "author_id", dependent: :destroy
+ end
+
+ test "destroyed_by_association set in child destroy callback on parent destroy" do
+ author = DestroyByParentAuthor.create!(name: "Test")
+ book = DestroyByParentBook.create!(author: author)
+
+ author.destroy
+
+ assert_not DestroyByParentBook.exists?(book.id)
+ end
+
+ test "destroyed_by_association set in child destroy callback on replace" do
+ author = DestroyByParentAuthor.create!(name: "Test")
+ book = DestroyByParentBook.create!(author: author)
+
+ author.book = DestroyByParentBook.create!
+ author.save!
+
+ assert_not DestroyByParentBook.exists?(book.id)
+ end
end
diff --git a/activerecord/test/cases/associations/has_one_through_associations_test.rb b/activerecord/test/cases/associations/has_one_through_associations_test.rb
index 28b883586d..9964f084ac 100644
--- a/activerecord/test/cases/associations/has_one_through_associations_test.rb
+++ b/activerecord/test/cases/associations/has_one_through_associations_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "cases/helper"
require "models/club"
require "models/member_type"
@@ -40,6 +42,18 @@ class HasOneThroughAssociationsTest < ActiveRecord::TestCase
assert_not_nil new_member.club
end
+ def test_creating_association_builds_through_record
+ new_member = Member.create(name: "Chris")
+ new_club = new_member.association(:club).build
+ assert new_member.current_membership
+ assert_equal new_club, new_member.club
+ assert_predicate new_club, :new_record?
+ assert_predicate new_member.current_membership, :new_record?
+ assert new_member.save
+ assert_predicate new_club, :persisted?
+ assert_predicate new_member.current_membership, :persisted?
+ end
+
def test_creating_association_builds_through_record_for_new
new_member = Member.new(name: "Jane")
new_member.club = clubs(:moustache_club)
@@ -98,7 +112,7 @@ class HasOneThroughAssociationsTest < ActiveRecord::TestCase
end
def test_has_one_through_eager_loading
- members = assert_queries(3) do #base table, through table, clubs table
+ members = assert_queries(3) do # base table, through table, clubs table
Member.all.merge!(includes: :club, where: ["name = ?", "Groucho Marx"]).to_a
end
assert_equal 1, members.size
@@ -106,7 +120,7 @@ class HasOneThroughAssociationsTest < ActiveRecord::TestCase
end
def test_has_one_through_eager_loading_through_polymorphic
- members = assert_queries(3) do #base table, through table, clubs table
+ members = assert_queries(3) do # base table, through table, clubs table
Member.all.merge!(includes: :sponsor_club, where: ["name = ?", "Groucho Marx"]).to_a
end
assert_equal 1, members.size
@@ -137,7 +151,7 @@ class HasOneThroughAssociationsTest < ActiveRecord::TestCase
def test_has_one_through_nonpreload_eagerloading
members = assert_queries(1) do
- Member.all.merge!(includes: :club, where: ["members.name = ?", "Groucho Marx"], order: "clubs.name").to_a #force fallback
+ Member.all.merge!(includes: :club, where: ["members.name = ?", "Groucho Marx"], order: "clubs.name").to_a # force fallback
end
assert_equal 1, members.size
assert_not_nil assert_no_queries { members[0].club }
@@ -145,7 +159,7 @@ class HasOneThroughAssociationsTest < ActiveRecord::TestCase
def test_has_one_through_nonpreload_eager_loading_through_polymorphic
members = assert_queries(1) do
- Member.all.merge!(includes: :sponsor_club, where: ["members.name = ?", "Groucho Marx"], order: "clubs.name").to_a #force fallback
+ Member.all.merge!(includes: :sponsor_club, where: ["members.name = ?", "Groucho Marx"], order: "clubs.name").to_a # force fallback
end
assert_equal 1, members.size
assert_not_nil assert_no_queries { members[0].sponsor_club }
@@ -154,7 +168,7 @@ class HasOneThroughAssociationsTest < ActiveRecord::TestCase
def test_has_one_through_nonpreload_eager_loading_through_polymorphic_with_more_than_one_through_record
Sponsor.new(sponsor_club: clubs(:crazy_club), sponsorable: members(:groucho)).save!
members = assert_queries(1) do
- Member.all.merge!(includes: :sponsor_club, where: ["members.name = ?", "Groucho Marx"], order: "clubs.name DESC").to_a #force fallback
+ Member.all.merge!(includes: :sponsor_club, where: ["members.name = ?", "Groucho Marx"], order: "clubs.name DESC").to_a # force fallback
end
assert_equal 1, members.size
assert_not_nil assert_no_queries { members[0].sponsor_club }
@@ -227,7 +241,7 @@ class HasOneThroughAssociationsTest < ActiveRecord::TestCase
MemberDetail.all.merge!(includes: :member_type).to_a
end
@new_detail = @member_details[0]
- assert @new_detail.send(:association, :member_type).loaded?
+ assert_predicate @new_detail.send(:association, :member_type), :loaded?
assert_no_queries { @new_detail.member_type }
end
@@ -315,12 +329,12 @@ class HasOneThroughAssociationsTest < ActiveRecord::TestCase
minivan.dashboard
proxy = minivan.send(:association_instance_get, :dashboard)
- assert !proxy.stale_target?
+ assert_not_predicate proxy, :stale_target?
assert_equal dashboards(:cool_first), minivan.dashboard
minivan.speedometer_id = speedometers(:second).id
- assert proxy.stale_target?
+ assert_predicate proxy, :stale_target?
assert_equal dashboards(:second), minivan.dashboard
end
@@ -332,7 +346,7 @@ class HasOneThroughAssociationsTest < ActiveRecord::TestCase
minivan.speedometer_id = speedometers(:second).id
- assert proxy.stale_target?
+ assert_predicate proxy, :stale_target?
assert_equal dashboards(:second), minivan.dashboard
end
diff --git a/activerecord/test/cases/associations/inner_join_association_test.rb b/activerecord/test/cases/associations/inner_join_association_test.rb
index ddf5bc6f0b..ca0620dc3b 100644
--- a/activerecord/test/cases/associations/inner_join_association_test.rb
+++ b/activerecord/test/cases/associations/inner_join_association_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "cases/helper"
require "models/post"
require "models/comment"
@@ -25,6 +27,24 @@ class InnerJoinAssociationTest < ActiveRecord::TestCase
end
end
+ def test_construct_finder_sql_does_not_table_name_collide_on_duplicate_associations_with_left_outer_joins
+ sql = Person.joins(agents: :agents).left_outer_joins(agents: :agents).to_sql
+ assert_match(/agents_people_4/i, sql)
+ end
+
+ def test_construct_finder_sql_does_not_table_name_collide_with_string_joins
+ sql = Person.joins(:agents).joins("JOIN people agents_people ON agents_people.primary_contact_id = people.id").to_sql
+ assert_match(/agents_people_2/i, sql)
+ end
+
+ def test_construct_finder_sql_does_not_table_name_collide_with_aliased_joins
+ people = Person.arel_table
+ agents = people.alias("agents_people")
+ constraint = agents[:primary_contact_id].eq(people[:id])
+ sql = Person.joins(:agents).joins(agents.create_join(agents, agents.create_on(constraint))).to_sql
+ assert_match(/agents_people_2/i, sql)
+ end
+
def test_construct_finder_sql_ignores_empty_joins_hash
sql = Author.joins({}).to_sql
assert_no_match(/JOIN/i, sql)
@@ -95,19 +115,19 @@ class InnerJoinAssociationTest < ActiveRecord::TestCase
scope = Post.joins(:special_comments).where(id: posts(:sti_comments).id)
# The join should match SpecialComment and its subclasses only
- assert scope.where("comments.type" => "Comment").empty?
- assert !scope.where("comments.type" => "SpecialComment").empty?
- assert !scope.where("comments.type" => "SubSpecialComment").empty?
+ assert_empty scope.where("comments.type" => "Comment")
+ assert_not_empty scope.where("comments.type" => "SpecialComment")
+ assert_not_empty scope.where("comments.type" => "SubSpecialComment")
end
def test_find_with_conditions_on_reflection
- assert !posts(:welcome).comments.empty?
+ assert_not_empty posts(:welcome).comments
assert Post.joins(:nonexistent_comments).where(id: posts(:welcome).id).empty? # [sic!]
end
def test_find_with_conditions_on_through_reflection
- assert !posts(:welcome).tags.empty?
- assert Post.joins(:misc_tags).where(id: posts(:welcome).id).empty?
+ assert_not_empty posts(:welcome).tags
+ assert_empty Post.joins(:misc_tags).where(id: posts(:welcome).id)
end
test "the default scope of the target is applied when joining associations" do
diff --git a/activerecord/test/cases/associations/inverse_associations_test.rb b/activerecord/test/cases/associations/inverse_associations_test.rb
index 467cc73ecd..bad1bcdb67 100644
--- a/activerecord/test/cases/associations/inverse_associations_test.rb
+++ b/activerecord/test/cases/associations/inverse_associations_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "cases/helper"
require "models/man"
require "models/face"
@@ -16,6 +18,8 @@ require "models/admin/user"
require "models/developer"
require "models/company"
require "models/project"
+require "models/author"
+require "models/post"
class AutomaticInverseFindingTests < ActiveRecord::TestCase
fixtures :ratings, :comments, :cars
@@ -24,11 +28,9 @@ class AutomaticInverseFindingTests < ActiveRecord::TestCase
monkey_reflection = MixedCaseMonkey.reflect_on_association(:man)
man_reflection = Man.reflect_on_association(:mixed_case_monkey)
- assert_respond_to monkey_reflection, :has_inverse?
assert monkey_reflection.has_inverse?, "The monkey reflection should have an inverse"
assert_equal man_reflection, monkey_reflection.inverse_of, "The monkey reflection's inverse should be the man reflection"
- assert_respond_to man_reflection, :has_inverse?
assert man_reflection.has_inverse?, "The man reflection should have an inverse"
assert_equal monkey_reflection, man_reflection.inverse_of, "The man reflection's inverse should be the monkey reflection"
end
@@ -37,7 +39,6 @@ class AutomaticInverseFindingTests < ActiveRecord::TestCase
account_reflection = Admin::Account.reflect_on_association(:users)
user_reflection = Admin::User.reflect_on_association(:account)
- assert_respond_to account_reflection, :has_inverse?
assert account_reflection.has_inverse?, "The Admin::Account reflection should have an inverse"
assert_equal user_reflection, account_reflection.inverse_of, "The Admin::Account reflection's inverse should be the Admin::User reflection"
end
@@ -46,11 +47,9 @@ class AutomaticInverseFindingTests < ActiveRecord::TestCase
car_reflection = Car.reflect_on_association(:bulb)
bulb_reflection = Bulb.reflect_on_association(:car)
- assert_respond_to car_reflection, :has_inverse?
assert car_reflection.has_inverse?, "The Car reflection should have an inverse"
assert_equal bulb_reflection, car_reflection.inverse_of, "The Car reflection's inverse should be the Bulb reflection"
- assert_respond_to bulb_reflection, :has_inverse?
assert bulb_reflection.has_inverse?, "The Bulb reflection should have an inverse"
assert_equal car_reflection, bulb_reflection.inverse_of, "The Bulb reflection's inverse should be the Car reflection"
end
@@ -59,11 +58,24 @@ class AutomaticInverseFindingTests < ActiveRecord::TestCase
comment_reflection = Comment.reflect_on_association(:ratings)
rating_reflection = Rating.reflect_on_association(:comment)
- assert_respond_to comment_reflection, :has_inverse?
assert comment_reflection.has_inverse?, "The Comment reflection should have an inverse"
assert_equal rating_reflection, comment_reflection.inverse_of, "The Comment reflection's inverse should be the Rating reflection"
end
+ def test_has_many_and_belongs_to_should_find_inverse_automatically_for_sti
+ author_reflection = Author.reflect_on_association(:posts)
+ author_child_reflection = Author.reflect_on_association(:special_posts)
+ post_reflection = Post.reflect_on_association(:author)
+
+ assert_respond_to author_reflection, :has_inverse?
+ assert author_reflection.has_inverse?, "The Author reflection should have an inverse"
+ assert_equal post_reflection, author_reflection.inverse_of, "The Author reflection's inverse should be the Post reflection"
+
+ assert_respond_to author_child_reflection, :has_inverse?
+ assert author_child_reflection.has_inverse?, "The Author reflection should have an inverse"
+ assert_equal post_reflection, author_child_reflection.inverse_of, "The Author reflection's inverse should be the Post reflection"
+ end
+
def test_has_one_and_belongs_to_automatic_inverse_shares_objects
car = Car.first
bulb = Bulb.create!(car: car)
@@ -107,24 +119,17 @@ class AutomaticInverseFindingTests < ActiveRecord::TestCase
def test_polymorphic_and_has_many_through_relationships_should_not_have_inverses
sponsor_reflection = Sponsor.reflect_on_association(:sponsorable)
- assert_respond_to sponsor_reflection, :has_inverse?
assert !sponsor_reflection.has_inverse?, "A polymorphic association should not find an inverse automatically"
club_reflection = Club.reflect_on_association(:members)
- assert_respond_to club_reflection, :has_inverse?
assert !club_reflection.has_inverse?, "A has_many_through association should not find an inverse automatically"
end
- def test_polymorphic_relationships_should_still_not_have_inverses_when_non_polymorphic_relationship_has_the_same_name
+ def test_polymorphic_has_one_should_find_inverse_automatically
man_reflection = Man.reflect_on_association(:polymorphic_face_without_inverse)
- face_reflection = Face.reflect_on_association(:man)
-
- assert_respond_to face_reflection, :has_inverse?
- assert face_reflection.has_inverse?, "For this test, the non-polymorphic association must have an inverse"
- assert_respond_to man_reflection, :has_inverse?
- assert !man_reflection.has_inverse?, "The target of a polymorphic association should not find an inverse automatically"
+ assert_predicate man_reflection, :has_inverse?
end
end
@@ -145,39 +150,22 @@ class InverseAssociationTests < ActiveRecord::TestCase
def test_should_be_able_to_ask_a_reflection_if_it_has_an_inverse
has_one_with_inverse_ref = Man.reflect_on_association(:face)
- assert_respond_to has_one_with_inverse_ref, :has_inverse?
- assert has_one_with_inverse_ref.has_inverse?
+ assert_predicate has_one_with_inverse_ref, :has_inverse?
has_many_with_inverse_ref = Man.reflect_on_association(:interests)
- assert_respond_to has_many_with_inverse_ref, :has_inverse?
- assert has_many_with_inverse_ref.has_inverse?
+ assert_predicate has_many_with_inverse_ref, :has_inverse?
belongs_to_with_inverse_ref = Face.reflect_on_association(:man)
- assert_respond_to belongs_to_with_inverse_ref, :has_inverse?
- assert belongs_to_with_inverse_ref.has_inverse?
+ assert_predicate belongs_to_with_inverse_ref, :has_inverse?
has_one_without_inverse_ref = Club.reflect_on_association(:sponsor)
- assert_respond_to has_one_without_inverse_ref, :has_inverse?
- assert !has_one_without_inverse_ref.has_inverse?
+ assert_not_predicate has_one_without_inverse_ref, :has_inverse?
has_many_without_inverse_ref = Club.reflect_on_association(:memberships)
- assert_respond_to has_many_without_inverse_ref, :has_inverse?
- assert !has_many_without_inverse_ref.has_inverse?
+ assert_not_predicate has_many_without_inverse_ref, :has_inverse?
belongs_to_without_inverse_ref = Sponsor.reflect_on_association(:sponsor_club)
- assert_respond_to belongs_to_without_inverse_ref, :has_inverse?
- assert !belongs_to_without_inverse_ref.has_inverse?
- end
-
- def test_should_be_able_to_ask_a_reflection_what_it_is_the_inverse_of
- has_one_ref = Man.reflect_on_association(:face)
- assert_respond_to has_one_ref, :inverse_of
-
- has_many_ref = Man.reflect_on_association(:interests)
- assert_respond_to has_many_ref, :inverse_of
-
- belongs_to_ref = Face.reflect_on_association(:man)
- assert_respond_to belongs_to_ref, :inverse_of
+ assert_not_predicate belongs_to_without_inverse_ref, :has_inverse?
end
def test_inverse_of_method_should_supply_the_actual_reflection_instance_it_is_the_inverse_of
@@ -295,7 +283,7 @@ class InverseHasOneTests < ActiveRecord::TestCase
end
class InverseHasManyTests < ActiveRecord::TestCase
- fixtures :men, :interests
+ fixtures :men, :interests, :posts, :authors, :author_addresses
def test_parent_instance_should_be_shared_with_every_child_on_find
m = men(:gordon)
@@ -309,6 +297,27 @@ class InverseHasManyTests < ActiveRecord::TestCase
end
end
+ def test_parent_instance_should_be_shared_with_every_child_on_find_for_sti
+ a = authors(:david)
+ ps = a.posts
+ ps.each do |p|
+ assert_equal a.name, p.author.name, "Name of man should be the same before changes to parent instance"
+ a.name = "Bongo"
+ assert_equal a.name, p.author.name, "Name of man should be the same after changes to parent instance"
+ p.author.name = "Mungo"
+ assert_equal a.name, p.author.name, "Name of man should be the same after changes to child-owned instance"
+ end
+
+ sps = a.special_posts
+ sps.each do |sp|
+ assert_equal a.name, sp.author.name, "Name of man should be the same before changes to parent instance"
+ a.name = "Bongo"
+ assert_equal a.name, sp.author.name, "Name of man should be the same after changes to parent instance"
+ sp.author.name = "Mungo"
+ assert_equal a.name, sp.author.name, "Name of man should be the same after changes to child-owned instance"
+ end
+ end
+
def test_parent_instance_should_be_shared_with_eager_loaded_children
m = Man.all.merge!(where: { name: "Gordon" }, includes: :interests).first
is = m.interests
@@ -455,7 +464,7 @@ class InverseHasManyTests < ActiveRecord::TestCase
interest = Interest.create!(man: man)
man.interests.find(interest.id)
- assert_not man.interests.loaded?
+ assert_not_predicate man.interests, :loaded?
end
def test_raise_record_not_found_error_when_invalid_ids_are_passed
@@ -475,7 +484,10 @@ class InverseHasManyTests < ActiveRecord::TestCase
def test_raise_record_not_found_error_when_no_ids_are_passed
man = Man.create!
- assert_raise(ActiveRecord::RecordNotFound) { man.interests.find() }
+ exception = assert_raise(ActiveRecord::RecordNotFound) { man.interests.load.find() }
+
+ assert_equal exception.model, "Interest"
+ assert_equal exception.primary_key, "id"
end
def test_trying_to_use_inverses_that_dont_exist_should_raise_an_error
@@ -492,16 +504,16 @@ class InverseHasManyTests < ActiveRecord::TestCase
i.man.name = "Charles"
assert_equal i.man.name, man.name
- assert !man.persisted?
+ assert_not_predicate man, :persisted?
end
def test_inverse_instance_should_be_set_before_find_callbacks_are_run
reset_callbacks(Interest, :find) do
Interest.after_find { raise unless association(:man).loaded? && man.present? }
- assert Man.first.interests.reload.any?
- assert Man.includes(:interests).first.interests.any?
- assert Man.joins(:interests).includes(:interests).first.interests.any?
+ assert_predicate Man.first.interests.reload, :any?
+ assert_predicate Man.includes(:interests).first.interests, :any?
+ assert_predicate Man.joins(:interests).includes(:interests).first.interests, :any?
end
end
@@ -509,9 +521,9 @@ class InverseHasManyTests < ActiveRecord::TestCase
reset_callbacks(Interest, :initialize) do
Interest.after_initialize { raise unless association(:man).loaded? && man.present? }
- assert Man.first.interests.reload.any?
- assert Man.includes(:interests).first.interests.any?
- assert Man.joins(:interests).includes(:interests).first.interests.any?
+ assert_predicate Man.first.interests.reload, :any?
+ assert_predicate Man.includes(:interests).first.interests, :any?
+ assert_predicate Man.joins(:interests).includes(:interests).first.interests, :any?
end
end
@@ -663,6 +675,16 @@ class InversePolymorphicBelongsToTests < ActiveRecord::TestCase
assert_equal old_inversed_man.object_id, new_inversed_man.object_id
end
+ def test_inversed_instance_should_not_be_reloaded_after_stale_state_changed_with_validation
+ face = Face.new man: Man.new
+
+ old_inversed_man = face.man
+ face.save!
+ new_inversed_man = face.man
+
+ assert_equal old_inversed_man.object_id, new_inversed_man.object_id
+ end
+
def test_should_not_try_to_set_inverse_instances_when_the_inverse_is_a_has_many
i = interests(:llama_wrangling)
m = i.polymorphic_man
diff --git a/activerecord/test/cases/associations/join_model_test.rb b/activerecord/test/cases/associations/join_model_test.rb
index c078cef064..f19a9f5f7a 100644
--- a/activerecord/test/cases/associations/join_model_test.rb
+++ b/activerecord/test/cases/associations/join_model_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "cases/helper"
require "models/tag"
require "models/tagging"
@@ -42,11 +44,11 @@ class AssociationsJoinModelTest < ActiveRecord::TestCase
def test_has_many_distinct_through_count
author = authors(:mary)
- assert !authors(:mary).unique_categorized_posts.loaded?
+ assert_not_predicate authors(:mary).unique_categorized_posts, :loaded?
assert_queries(1) { assert_equal 1, author.unique_categorized_posts.count }
assert_queries(1) { assert_equal 1, author.unique_categorized_posts.count(:title) }
assert_queries(1) { assert_equal 0, author.unique_categorized_posts.where(title: nil).count(:title) }
- assert !authors(:mary).unique_categorized_posts.loaded?
+ assert_not_predicate authors(:mary).unique_categorized_posts, :loaded?
end
def test_has_many_distinct_through_find
@@ -97,11 +99,11 @@ class AssociationsJoinModelTest < ActiveRecord::TestCase
end
def test_polymorphic_has_many_create_model_with_inheritance_and_custom_base_class
- post = SubStiPost.create title: "SubStiPost", body: "SubStiPost body"
- assert_instance_of SubStiPost, post
+ post = SubAbstractStiPost.create title: "SubAbstractStiPost", body: "SubAbstractStiPost body"
+ assert_instance_of SubAbstractStiPost, post
tagging = tags(:misc).taggings.create(taggable: post)
- assert_equal "SubStiPost", tagging.taggable_type
+ assert_equal "SubAbstractStiPost", tagging.taggable_type
end
def test_polymorphic_has_many_going_through_join_model_with_inheritance
@@ -402,7 +404,7 @@ class AssociationsJoinModelTest < ActiveRecord::TestCase
end
def test_has_many_through_polymorphic_has_one
- assert_equal Tagging.find(1, 2).sort_by(&:id), authors(:david).taggings_2
+ assert_equal Tagging.find(1, 2).sort_by(&:id), authors(:david).taggings_2.sort_by(&:id)
end
def test_has_many_through_polymorphic_has_many
@@ -452,8 +454,8 @@ class AssociationsJoinModelTest < ActiveRecord::TestCase
def test_has_many_through_uses_conditions_specified_on_the_has_many_association
author = Author.first
- assert author.comments.present?
- assert author.nonexistent_comments.blank?
+ assert_predicate author.comments, :present?
+ assert_predicate author.nonexistent_comments, :blank?
end
def test_has_many_through_uses_correct_attributes
@@ -465,27 +467,27 @@ class AssociationsJoinModelTest < ActiveRecord::TestCase
new_tag = Tag.new(name: "new")
saved_post.tags << new_tag
- assert new_tag.persisted? #consistent with habtm!
- assert saved_post.persisted?
+ assert new_tag.persisted? # consistent with habtm!
+ assert_predicate saved_post, :persisted?
assert_includes saved_post.tags, new_tag
- assert new_tag.persisted?
+ assert_predicate new_tag, :persisted?
assert_includes saved_post.reload.tags.reload, new_tag
new_post = Post.new(title: "Association replacement works!", body: "You best believe it.")
saved_tag = tags(:general)
new_post.tags << saved_tag
- assert !new_post.persisted?
- assert saved_tag.persisted?
+ assert_not_predicate new_post, :persisted?
+ assert_predicate saved_tag, :persisted?
assert_includes new_post.tags, saved_tag
new_post.save!
- assert new_post.persisted?
+ assert_predicate new_post, :persisted?
assert_includes new_post.reload.tags.reload, saved_tag
- assert !posts(:thinking).tags.build.persisted?
- assert !posts(:thinking).tags.new.persisted?
+ assert_not_predicate posts(:thinking).tags.build, :persisted?
+ assert_not_predicate posts(:thinking).tags.new, :persisted?
end
def test_create_associate_when_adding_to_has_many_through
@@ -494,25 +496,25 @@ class AssociationsJoinModelTest < ActiveRecord::TestCase
post_thinking = posts(:thinking)
assert_nothing_raised { post_thinking.tags << push }
assert_nil(wrong = post_thinking.tags.detect { |t| t.class != Tag },
- message = "Expected a Tag in tags collection, got #{wrong.class}.")
+ "Expected a Tag in tags collection, got #{wrong.class}.")
assert_nil(wrong = post_thinking.taggings.detect { |t| t.class != Tagging },
- message = "Expected a Tagging in taggings collection, got #{wrong.class}.")
+ "Expected a Tagging in taggings collection, got #{wrong.class}.")
assert_equal(count + 1, post_thinking.reload.tags.size)
assert_equal(count + 1, post_thinking.tags.reload.size)
assert_kind_of Tag, post_thinking.tags.create!(name: "foo")
assert_nil(wrong = post_thinking.tags.detect { |t| t.class != Tag },
- message = "Expected a Tag in tags collection, got #{wrong.class}.")
+ "Expected a Tag in tags collection, got #{wrong.class}.")
assert_nil(wrong = post_thinking.taggings.detect { |t| t.class != Tagging },
- message = "Expected a Tagging in taggings collection, got #{wrong.class}.")
+ "Expected a Tagging in taggings collection, got #{wrong.class}.")
assert_equal(count + 2, post_thinking.reload.tags.size)
assert_equal(count + 2, post_thinking.tags.reload.size)
assert_nothing_raised { post_thinking.tags.concat(Tag.create!(name: "abc"), Tag.create!(name: "def")) }
assert_nil(wrong = post_thinking.tags.detect { |t| t.class != Tag },
- message = "Expected a Tag in tags collection, got #{wrong.class}.")
+ "Expected a Tag in tags collection, got #{wrong.class}.")
assert_nil(wrong = post_thinking.taggings.detect { |t| t.class != Tagging },
- message = "Expected a Tagging in taggings collection, got #{wrong.class}.")
+ "Expected a Tagging in taggings collection, got #{wrong.class}.")
assert_equal(count + 4, post_thinking.reload.tags.size)
assert_equal(count + 4, post_thinking.tags.reload.size)
@@ -527,14 +529,14 @@ class AssociationsJoinModelTest < ActiveRecord::TestCase
def test_has_many_through_collection_size_doesnt_load_target_if_not_loaded
author = authors(:david)
assert_equal 10, author.comments.size
- assert !author.comments.loaded?
+ assert_not_predicate author.comments, :loaded?
end
def test_has_many_through_collection_size_uses_counter_cache_if_it_exists
c = categories(:general)
c.categorizations_count = 100
assert_equal 100, c.categorizations.size
- assert !c.categorizations.loaded?
+ assert_not_predicate c.categorizations, :loaded?
end
def test_adding_junk_to_has_many_through_should_raise_type_mismatch
@@ -708,7 +710,7 @@ class AssociationsJoinModelTest < ActiveRecord::TestCase
category = david.categories.first
assert_no_queries do
- assert david.categories.loaded?
+ assert_predicate david.categories, :loaded?
assert_includes david.categories, category
end
end
@@ -718,18 +720,18 @@ class AssociationsJoinModelTest < ActiveRecord::TestCase
category = david.categories.first
david.reload
- assert ! david.categories.loaded?
+ assert_not_predicate david.categories, :loaded?
assert_queries(1) do
assert_includes david.categories, category
end
- assert ! david.categories.loaded?
+ assert_not_predicate david.categories, :loaded?
end
def test_has_many_through_include_returns_false_for_non_matching_record_to_verify_scoping
david = authors(:david)
category = Category.create!(name: "Not Associated")
- assert ! david.categories.loaded?
+ assert_not_predicate david.categories, :loaded?
assert ! david.categories.include?(category)
end
diff --git a/activerecord/test/cases/associations/left_outer_join_association_test.rb b/activerecord/test/cases/associations/left_outer_join_association_test.rb
index 6d3757f467..7b5c394177 100644
--- a/activerecord/test/cases/associations/left_outer_join_association_test.rb
+++ b/activerecord/test/cases/associations/left_outer_join_association_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "cases/helper"
require "models/post"
require "models/comment"
@@ -67,15 +69,15 @@ class LeftOuterJoinAssociationTest < ActiveRecord::TestCase
scope = Post.left_outer_joins(:special_comments).where(id: posts(:sti_comments).id)
# The join should match SpecialComment and its subclasses only
- assert scope.where("comments.type" => "Comment").empty?
- assert !scope.where("comments.type" => "SpecialComment").empty?
- assert !scope.where("comments.type" => "SubSpecialComment").empty?
+ assert_empty scope.where("comments.type" => "Comment")
+ assert_not_empty scope.where("comments.type" => "SpecialComment")
+ assert_not_empty scope.where("comments.type" => "SubSpecialComment")
end
def test_does_not_override_select
authors = Author.select("authors.name, #{%{(authors.author_address_id || ' ' || authors.author_address_extra_id) as addr_id}}").left_outer_joins(:posts)
- assert authors.any?
- assert authors.first.respond_to?(:addr_id)
+ assert_predicate authors, :any?
+ assert_respond_to authors.first, :addr_id
end
test "the default scope of the target is applied when joining associations" do
diff --git a/activerecord/test/cases/associations/nested_through_associations_test.rb b/activerecord/test/cases/associations/nested_through_associations_test.rb
index 67ff7355b3..03ed1c1d47 100644
--- a/activerecord/test/cases/associations/nested_through_associations_test.rb
+++ b/activerecord/test/cases/associations/nested_through_associations_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "cases/helper"
require "models/author"
require "models/post"
@@ -22,6 +24,11 @@ require "models/category"
require "models/categorization"
require "models/membership"
require "models/essay"
+require "models/hotel"
+require "models/department"
+require "models/chef"
+require "models/cake_designer"
+require "models/drink_designer"
class NestedThroughAssociationsTest < ActiveRecord::TestCase
fixtures :authors, :author_addresses, :books, :posts, :subscriptions, :subscribers, :tags, :taggings,
@@ -71,7 +78,7 @@ class NestedThroughAssociationsTest < ActiveRecord::TestCase
# This ensures that the polymorphism of taggings is being observed correctly
authors = Author.joins(:tags).where("taggings.taggable_type" => "FakeModel")
- assert authors.empty?
+ assert_empty authors
end
# has_many through
@@ -170,7 +177,7 @@ class NestedThroughAssociationsTest < ActiveRecord::TestCase
members = Member.joins(:organization_member_details).
where("member_details.id" => 9)
- assert members.empty?
+ assert_empty members
end
# has_many through
@@ -202,7 +209,7 @@ class NestedThroughAssociationsTest < ActiveRecord::TestCase
members = Member.joins(:organization_member_details_2).
where("member_details.id" => 9)
- assert members.empty?
+ assert_empty members
end
# has_many through
@@ -418,9 +425,14 @@ class NestedThroughAssociationsTest < ActiveRecord::TestCase
# Check the polymorphism of taggings is being observed correctly (in both joins)
authors = Author.joins(:similar_posts).where("taggings.taggable_type" => "FakeModel")
- assert authors.empty?
+ assert_empty authors
authors = Author.joins(:similar_posts).where("taggings_authors_join.taggable_type" => "FakeModel")
- assert authors.empty?
+ assert_empty authors
+ end
+
+ def test_nested_has_many_through_with_scope_on_polymorphic_reflection
+ authors = Author.joins(:ordered_posts).where("posts.id" => posts(:misc_by_bob).id)
+ assert_equal [authors(:mary), authors(:bob)], authors.distinct.sort_by(&:id)
end
def test_has_many_through_with_foreign_key_option_on_through_reflection
@@ -444,9 +456,9 @@ class NestedThroughAssociationsTest < ActiveRecord::TestCase
# Ensure STI is respected in the join
scope = Post.joins(:special_comments_ratings).where(id: posts(:sti_comments).id)
- assert scope.where("comments.type" => "Comment").empty?
- assert !scope.where("comments.type" => "SpecialComment").empty?
- assert !scope.where("comments.type" => "SubSpecialComment").empty?
+ assert_empty scope.where("comments.type" => "Comment")
+ assert_not_empty scope.where("comments.type" => "SpecialComment")
+ assert_not_empty scope.where("comments.type" => "SubSpecialComment")
end
def test_has_many_through_with_sti_on_nested_through_reflection
@@ -454,8 +466,8 @@ class NestedThroughAssociationsTest < ActiveRecord::TestCase
assert_equal [taggings(:special_comment_rating)], taggings
scope = Post.joins(:special_comments_ratings_taggings).where(id: posts(:sti_comments).id)
- assert scope.where("comments.type" => "Comment").empty?
- assert !scope.where("comments.type" => "SpecialComment").empty?
+ assert_empty scope.where("comments.type" => "Comment")
+ assert_not_empty scope.where("comments.type" => "SpecialComment")
end
def test_nested_has_many_through_writers_should_raise_error
@@ -505,7 +517,7 @@ class NestedThroughAssociationsTest < ActiveRecord::TestCase
end
def test_nested_has_many_through_with_conditions_on_through_associations_preload
- assert Author.where("tags.id" => 100).joins(:misc_post_first_blue_tags).empty?
+ assert_empty Author.where("tags.id" => 100).joins(:misc_post_first_blue_tags)
authors = assert_queries(3) { Author.includes(:misc_post_first_blue_tags).to_a.sort_by(&:id) }
blue = tags(:blue)
@@ -562,9 +574,40 @@ class NestedThroughAssociationsTest < ActiveRecord::TestCase
c = Categorization.new
c.author = authors(:david)
c.post_taggings.to_a
- assert !c.post_taggings.empty?
+ assert_not_empty c.post_taggings
c.save
- assert !c.post_taggings.empty?
+ assert_not_empty c.post_taggings
+ end
+
+ def test_polymorphic_has_many_through_when_through_association_has_not_loaded
+ cake_designer = CakeDesigner.create!(chef: Chef.new)
+ drink_designer = DrinkDesigner.create!(chef: Chef.new)
+ department = Department.create!(chefs: [cake_designer.chef, drink_designer.chef])
+ Hotel.create!(departments: [department])
+ hotel = Hotel.includes(:cake_designers, :drink_designers).take
+
+ assert_equal [cake_designer], hotel.cake_designers
+ assert_equal [drink_designer], hotel.drink_designers
+ end
+
+ def test_polymorphic_has_many_through_when_through_association_has_already_loaded
+ cake_designer = CakeDesigner.create!(chef: Chef.new)
+ drink_designer = DrinkDesigner.create!(chef: Chef.new)
+ department = Department.create!(chefs: [cake_designer.chef, drink_designer.chef])
+ Hotel.create!(departments: [department])
+ hotel = Hotel.includes(:chefs, :cake_designers, :drink_designers).take
+
+ assert_equal [cake_designer], hotel.cake_designers
+ assert_equal [drink_designer], hotel.drink_designers
+ end
+
+ def test_polymorphic_has_many_through_joined_different_table_twice
+ cake_designer = CakeDesigner.create!(chef: Chef.new)
+ drink_designer = DrinkDesigner.create!(chef: Chef.new)
+ department = Department.create!(chefs: [cake_designer.chef, drink_designer.chef])
+ hotel = Hotel.create!(departments: [department])
+
+ assert_equal hotel, Hotel.joins(:cake_designers, :drink_designers).take
end
private
diff --git a/activerecord/test/cases/associations/required_test.rb b/activerecord/test/cases/associations/required_test.rb
index 45e1803858..65a3bb5efe 100644
--- a/activerecord/test/cases/associations/required_test.rb
+++ b/activerecord/test/cases/associations/required_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "cases/helper"
class RequiredAssociationsTest < ActiveRecord::TestCase
diff --git a/activerecord/test/cases/associations_test.rb b/activerecord/test/cases/associations_test.rb
index 2eb31326a5..fce2a7eed1 100644
--- a/activerecord/test/cases/associations_test.rb
+++ b/activerecord/test/cases/associations_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "cases/helper"
require "models/computer"
require "models/developer"
@@ -45,7 +47,7 @@ class AssociationsTest < ActiveRecord::TestCase
ship = Ship.create!(name: "The good ship Dollypop")
part = ship.parts.create!(name: "Mast")
part.mark_for_destruction
- assert ship.parts[0].marked_for_destruction?
+ assert_predicate ship.parts[0], :marked_for_destruction?
end
def test_loading_the_association_target_should_load_most_recent_attributes_for_child_records_marked_for_destruction
@@ -117,7 +119,7 @@ class AssociationProxyTest < ActiveRecord::TestCase
david = authors(:david)
david.posts << (post = Post.new(title: "New on Edge", body: "More cool stuff!"))
- assert !david.posts.loaded?
+ assert_not_predicate david.posts, :loaded?
assert_includes david.posts, post
end
@@ -125,7 +127,7 @@ class AssociationProxyTest < ActiveRecord::TestCase
david = authors(:david)
david.categories << categories(:technology)
- assert !david.categories.loaded?
+ assert_not_predicate david.categories, :loaded?
assert_includes david.categories, categories(:technology)
end
@@ -133,23 +135,23 @@ class AssociationProxyTest < ActiveRecord::TestCase
david = authors(:david)
david.posts << (post = Post.new(title: "New on Edge", body: "More cool stuff!"))
- assert !david.posts.loaded?
+ assert_not_predicate david.posts, :loaded?
david.save
- assert !david.posts.loaded?
+ assert_not_predicate david.posts, :loaded?
assert_includes david.posts, post
end
def test_push_does_not_lose_additions_to_new_record
josh = Author.new(name: "Josh")
josh.posts << Post.new(title: "New on Edge", body: "More cool stuff!")
- assert josh.posts.loaded?
+ assert_predicate josh.posts, :loaded?
assert_equal 1, josh.posts.size
end
def test_append_behaves_like_push
josh = Author.new(name: "Josh")
josh.posts.append Post.new(title: "New on Edge", body: "More cool stuff!")
- assert josh.posts.loaded?
+ assert_predicate josh.posts, :loaded?
assert_equal 1, josh.posts.size
end
@@ -161,22 +163,22 @@ class AssociationProxyTest < ActiveRecord::TestCase
def test_save_on_parent_does_not_load_target
david = developers(:david)
- assert !david.projects.loaded?
+ assert_not_predicate david.projects, :loaded?
david.update_columns(created_at: Time.now)
- assert !david.projects.loaded?
+ assert_not_predicate david.projects, :loaded?
end
def test_load_does_load_target
david = developers(:david)
- assert !david.projects.loaded?
+ assert_not_predicate david.projects, :loaded?
david.projects.load
- assert david.projects.loaded?
+ assert_predicate david.projects, :loaded?
end
def test_inspect_does_not_reload_a_not_yet_loaded_target
andreas = Developer.new name: "Andreas", log: "new developer added"
- assert !andreas.audit_logs.loaded?
+ assert_not_predicate andreas.audit_logs, :loaded?
assert_match(/message: "new developer added"/, andreas.audit_logs.inspect)
end
@@ -245,25 +247,25 @@ class AssociationProxyTest < ActiveRecord::TestCase
test "first! works on loaded associations" do
david = authors(:david)
- assert_equal david.posts.first, david.posts.reload.first!
- assert david.posts.loaded?
- assert_no_queries { david.posts.first! }
+ assert_equal david.first_posts.first, david.first_posts.reload.first!
+ assert_predicate david.first_posts, :loaded?
+ assert_no_queries { david.first_posts.first! }
end
def test_pluck_uses_loaded_target
david = authors(:david)
- assert_equal david.posts.pluck(:title), david.posts.load.pluck(:title)
- assert david.posts.loaded?
- assert_no_queries { david.posts.pluck(:title) }
+ assert_equal david.first_posts.pluck(:title), david.first_posts.load.pluck(:title)
+ assert_predicate david.first_posts, :loaded?
+ assert_no_queries { david.first_posts.pluck(:title) }
end
def test_reset_unloads_target
david = authors(:david)
david.posts.reload
- assert david.posts.loaded?
+ assert_predicate david.posts, :loaded?
david.posts.reset
- assert !david.posts.loaded?
+ assert_not_predicate david.posts, :loaded?
end
end
diff --git a/activerecord/test/cases/attribute_decorators_test.rb b/activerecord/test/cases/attribute_decorators_test.rb
index cfa6ed1da6..42eca233ce 100644
--- a/activerecord/test/cases/attribute_decorators_test.rb
+++ b/activerecord/test/cases/attribute_decorators_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "cases/helper"
module ActiveRecord
diff --git a/activerecord/test/cases/attribute_methods/read_test.rb b/activerecord/test/cases/attribute_methods/read_test.rb
index 1fc63a49d4..0170a6e98d 100644
--- a/activerecord/test/cases/attribute_methods/read_test.rb
+++ b/activerecord/test/cases/attribute_methods/read_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "cases/helper"
module ActiveRecord
@@ -8,11 +10,10 @@ module ActiveRecord
end
def setup
- @klass = Class.new do
+ @klass = Class.new(Class.new { def self.initialize_generated_modules; end }) do
def self.superclass; Base; end
def self.base_class; self; end
def self.decorate_matching_attribute_types(*); end
- def self.initialize_generated_modules; end
include ActiveRecord::DefineCallbacks
include ActiveRecord::AttributeMethods
diff --git a/activerecord/test/cases/attribute_methods_test.rb b/activerecord/test/cases/attribute_methods_test.rb
index 4d24a980dc..dc6638d45d 100644
--- a/activerecord/test/cases/attribute_methods_test.rb
+++ b/activerecord/test/cases/attribute_methods_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "cases/helper"
require "models/minimalistic"
require "models/developer"
@@ -97,8 +99,8 @@ class AttributeMethodsTest < ActiveRecord::TestCase
end
test "boolean attributes" do
- assert !Topic.find(1).approved?
- assert Topic.find(2).approved?
+ assert_not_predicate Topic.find(1), :approved?
+ assert_predicate Topic.find(2), :approved?
end
test "set attributes" do
@@ -140,16 +142,16 @@ class AttributeMethodsTest < ActiveRecord::TestCase
assert_respond_to topic, :title=
assert_respond_to topic, "author_name"
assert_respond_to topic, "attribute_names"
- assert !topic.respond_to?("nothingness")
- assert !topic.respond_to?(:nothingness)
+ assert_not_respond_to topic, "nothingness"
+ assert_not_respond_to topic, :nothingness
end
test "respond_to? with a custom primary key" do
keyboard = Keyboard.create
assert_not_nil keyboard.key_number
assert_equal keyboard.key_number, keyboard.id
- assert keyboard.respond_to?("key_number")
- assert keyboard.respond_to?("id")
+ assert_respond_to keyboard, "key_number"
+ assert_respond_to keyboard, "id"
end
test "id_before_type_cast with a custom primary key" do
@@ -168,8 +170,8 @@ class AttributeMethodsTest < ActiveRecord::TestCase
end
topic = klass.allocate
- assert !topic.respond_to?("nothingness")
- assert !topic.respond_to?(:nothingness)
+ assert_not_respond_to topic, "nothingness"
+ assert_not_respond_to topic, :nothingness
assert_respond_to topic, "title"
assert_respond_to topic, :title
end
@@ -198,12 +200,7 @@ class AttributeMethodsTest < ActiveRecord::TestCase
if current_adapter?(:Mysql2Adapter)
test "read attributes_before_type_cast on a boolean" do
bool = Boolean.create!("value" => false)
- if RUBY_PLATFORM.include?("java")
- # JRuby will return the value before typecast as string.
- assert_equal "0", bool.reload.attributes_before_type_cast["value"]
- else
- assert_equal 0, bool.reload.attributes_before_type_cast["value"]
- end
+ assert_equal 0, bool.reload.attributes_before_type_cast["value"]
end
end
@@ -460,30 +457,30 @@ class AttributeMethodsTest < ActiveRecord::TestCase
SQL
assert_equal "Firm", object.string_value
- assert object.string_value?
+ assert_predicate object, :string_value?
object.string_value = " "
- assert !object.string_value?
+ assert_not_predicate object, :string_value?
assert_equal 1, object.int_value.to_i
- assert object.int_value?
+ assert_predicate object, :int_value?
object.int_value = "0"
- assert !object.int_value?
+ assert_not_predicate object, :int_value?
end
test "non-attribute read and write" do
topic = Topic.new
- assert !topic.respond_to?("mumbo")
+ assert_not_respond_to topic, "mumbo"
assert_raise(NoMethodError) { topic.mumbo }
assert_raise(NoMethodError) { topic.mumbo = 5 }
end
test "undeclared attribute method does not affect respond_to? and method_missing" do
topic = @target.new(title: "Budget")
- assert topic.respond_to?("title")
+ assert_respond_to topic, "title"
assert_equal "Budget", topic.title
- assert !topic.respond_to?("title_hello_world")
+ assert_not_respond_to topic, "title_hello_world"
assert_raise(NoMethodError) { topic.title_hello_world }
end
@@ -494,7 +491,7 @@ class AttributeMethodsTest < ActiveRecord::TestCase
@target.attribute_method_prefix prefix
meth = "#{prefix}title"
- assert topic.respond_to?(meth)
+ assert_respond_to topic, meth
assert_equal ["title"], topic.send(meth)
assert_equal ["title", "a"], topic.send(meth, "a")
assert_equal ["title", 1, 2, 3], topic.send(meth, 1, 2, 3)
@@ -508,7 +505,7 @@ class AttributeMethodsTest < ActiveRecord::TestCase
topic = @target.new(title: "Budget")
meth = "title#{suffix}"
- assert topic.respond_to?(meth)
+ assert_respond_to topic, meth
assert_equal ["title"], topic.send(meth)
assert_equal ["title", "a"], topic.send(meth, "a")
assert_equal ["title", 1, 2, 3], topic.send(meth, 1, 2, 3)
@@ -522,7 +519,7 @@ class AttributeMethodsTest < ActiveRecord::TestCase
topic = @target.new(title: "Budget")
meth = "#{prefix}title#{suffix}"
- assert topic.respond_to?(meth)
+ assert_respond_to topic, meth
assert_equal ["title"], topic.send(meth)
assert_equal ["title", "a"], topic.send(meth, "a")
assert_equal ["title", 1, 2, 3], topic.send(meth, 1, 2, 3)
@@ -544,7 +541,7 @@ class AttributeMethodsTest < ActiveRecord::TestCase
else
topic = Topic.all.merge!(select: "topics.*, 1=2 as is_test").first
end
- assert !topic.is_test?
+ assert_not_predicate topic, :is_test?
end
test "typecast attribute from select to true" do
@@ -555,7 +552,7 @@ class AttributeMethodsTest < ActiveRecord::TestCase
else
topic = Topic.all.merge!(select: "topics.*, 2=2 as is_test").first
end
- assert topic.is_test?
+ assert_predicate topic, :is_test?
end
test "raises ActiveRecord::DangerousAttributeError when defining an AR method in a model" do
@@ -746,7 +743,7 @@ class AttributeMethodsTest < ActiveRecord::TestCase
expected_time = Time.utc(2000, 01, 01, 10)
assert_equal expected_time, record.bonus_time
- assert record.bonus_time.utc?
+ assert_predicate record.bonus_time, :utc?
end
end
end
@@ -770,7 +767,7 @@ class AttributeMethodsTest < ActiveRecord::TestCase
privatize("title")
topic = @target.new(title: "The pros and cons of programming naked.")
- assert !topic.respond_to?(:title)
+ assert_not_respond_to topic, :title
exception = assert_raise(NoMethodError) { topic.title }
assert_includes exception.message, "private method"
assert_equal "I'm private", topic.send(:title)
@@ -780,7 +777,7 @@ class AttributeMethodsTest < ActiveRecord::TestCase
privatize("title=(value)")
topic = @target.new
- assert !topic.respond_to?(:title=)
+ assert_not_respond_to topic, :title=
exception = assert_raise(NoMethodError) { topic.title = "Pants" }
assert_includes exception.message, "private method"
topic.send(:title=, "Very large pants")
@@ -790,7 +787,7 @@ class AttributeMethodsTest < ActiveRecord::TestCase
privatize("title?")
topic = @target.new(title: "Isaac Newton's pants")
- assert !topic.respond_to?(:title?)
+ assert_not_respond_to topic, :title?
exception = assert_raise(NoMethodError) { topic.title? }
assert_includes exception.message, "private method"
assert topic.send(:title?)
@@ -982,9 +979,9 @@ class AttributeMethodsTest < ActiveRecord::TestCase
test "came_from_user?" do
model = @target.first
- assert_not model.id_came_from_user?
+ assert_not_predicate model, :id_came_from_user?
model.id = "omg"
- assert model.id_came_from_user?
+ assert_predicate model, :id_came_from_user?
end
test "accessed_fields" do
@@ -997,6 +994,11 @@ class AttributeMethodsTest < ActiveRecord::TestCase
assert_equal ["title"], model.accessed_fields
end
+ test "generated attribute methods ancestors have correct class" do
+ mod = Topic.send(:generated_attribute_methods)
+ assert_match %r(GeneratedAttributeMethods), mod.inspect
+ end
+
private
def new_topic_like_ar_class(&block)
@@ -1005,7 +1007,7 @@ class AttributeMethodsTest < ActiveRecord::TestCase
class_eval(&block)
end
- assert_empty klass.generated_attribute_methods.instance_methods(false)
+ assert_empty klass.send(:generated_attribute_methods).instance_methods(false)
klass
end
@@ -1017,14 +1019,6 @@ class AttributeMethodsTest < ActiveRecord::TestCase
ActiveRecord::Base.time_zone_aware_types = old_types
end
- def cached_columns
- Topic.columns.map(&:name)
- end
-
- def time_related_columns_on_topic
- Topic.columns.select { |c| [:time, :date, :datetime, :timestamp].include?(c.type) }
- end
-
def privatize(method_signature)
@target.class_eval(<<-private_method, __FILE__, __LINE__ + 1)
private
diff --git a/activerecord/test/cases/attribute_set_test.rb b/activerecord/test/cases/attribute_set_test.rb
deleted file mode 100644
index bd4b200735..0000000000
--- a/activerecord/test/cases/attribute_set_test.rb
+++ /dev/null
@@ -1,253 +0,0 @@
-require "cases/helper"
-
-module ActiveRecord
- class AttributeSetTest < ActiveRecord::TestCase
- test "building a new set from raw attributes" do
- builder = AttributeSet::Builder.new(foo: Type::Integer.new, bar: Type::Float.new)
- attributes = builder.build_from_database(foo: "1.1", bar: "2.2")
-
- assert_equal 1, attributes[:foo].value
- assert_equal 2.2, attributes[:bar].value
- assert_equal :foo, attributes[:foo].name
- assert_equal :bar, attributes[:bar].name
- end
-
- test "building with custom types" do
- builder = AttributeSet::Builder.new(foo: Type::Float.new)
- attributes = builder.build_from_database({ foo: "3.3", bar: "4.4" }, bar: Type::Integer.new)
-
- assert_equal 3.3, attributes[:foo].value
- assert_equal 4, attributes[:bar].value
- end
-
- test "[] returns a null object" do
- builder = AttributeSet::Builder.new(foo: Type::Float.new)
- attributes = builder.build_from_database(foo: "3.3")
-
- assert_equal "3.3", attributes[:foo].value_before_type_cast
- assert_nil attributes[:bar].value_before_type_cast
- assert_equal :bar, attributes[:bar].name
- end
-
- test "duping creates a new hash, but does not dup the attributes" do
- builder = AttributeSet::Builder.new(foo: Type::Integer.new, bar: Type::String.new)
- attributes = builder.build_from_database(foo: 1, bar: "foo")
-
- # Ensure the type cast value is cached
- attributes[:foo].value
- attributes[:bar].value
-
- duped = attributes.dup
- duped.write_from_database(:foo, 2)
- duped[:bar].value << "bar"
-
- assert_equal 1, attributes[:foo].value
- assert_equal 2, duped[:foo].value
- assert_equal "foobar", attributes[:bar].value
- assert_equal "foobar", duped[:bar].value
- end
-
- test "deep_duping creates a new hash and dups each attribute" do
- builder = AttributeSet::Builder.new(foo: Type::Integer.new, bar: Type::String.new)
- attributes = builder.build_from_database(foo: 1, bar: "foo")
-
- # Ensure the type cast value is cached
- attributes[:foo].value
- attributes[:bar].value
-
- duped = attributes.deep_dup
- duped.write_from_database(:foo, 2)
- duped[:bar].value << "bar"
-
- assert_equal 1, attributes[:foo].value
- assert_equal 2, duped[:foo].value
- assert_equal "foo", attributes[:bar].value
- assert_equal "foobar", duped[:bar].value
- end
-
- test "freezing cloned set does not freeze original" do
- attributes = AttributeSet.new({})
- clone = attributes.clone
-
- clone.freeze
-
- assert clone.frozen?
- assert_not attributes.frozen?
- end
-
- test "to_hash returns a hash of the type cast values" do
- builder = AttributeSet::Builder.new(foo: Type::Integer.new, bar: Type::Float.new)
- attributes = builder.build_from_database(foo: "1.1", bar: "2.2")
-
- assert_equal({ foo: 1, bar: 2.2 }, attributes.to_hash)
- assert_equal({ foo: 1, bar: 2.2 }, attributes.to_h)
- end
-
- test "to_hash maintains order" do
- builder = AttributeSet::Builder.new(foo: Type::Integer.new, bar: Type::Float.new)
- attributes = builder.build_from_database(foo: "2.2", bar: "3.3")
-
- attributes[:bar]
- hash = attributes.to_h
-
- assert_equal [[:foo, 2], [:bar, 3.3]], hash.to_a
- end
-
- test "values_before_type_cast" do
- builder = AttributeSet::Builder.new(foo: Type::Integer.new, bar: Type::Integer.new)
- attributes = builder.build_from_database(foo: "1.1", bar: "2.2")
-
- assert_equal({ foo: "1.1", bar: "2.2" }, attributes.values_before_type_cast)
- end
-
- test "known columns are built with uninitialized attributes" do
- attributes = attributes_with_uninitialized_key
- assert attributes[:foo].initialized?
- assert_not attributes[:bar].initialized?
- end
-
- test "uninitialized attributes are not included in the attributes hash" do
- attributes = attributes_with_uninitialized_key
- assert_equal({ foo: 1 }, attributes.to_hash)
- end
-
- test "uninitialized attributes are not included in keys" do
- attributes = attributes_with_uninitialized_key
- assert_equal [:foo], attributes.keys
- end
-
- test "uninitialized attributes return false for key?" do
- attributes = attributes_with_uninitialized_key
- assert attributes.key?(:foo)
- assert_not attributes.key?(:bar)
- end
-
- test "unknown attributes return false for key?" do
- attributes = attributes_with_uninitialized_key
- assert_not attributes.key?(:wibble)
- end
-
- test "fetch_value returns the value for the given initialized attribute" do
- builder = AttributeSet::Builder.new(foo: Type::Integer.new, bar: Type::Float.new)
- attributes = builder.build_from_database(foo: "1.1", bar: "2.2")
-
- assert_equal 1, attributes.fetch_value(:foo)
- assert_equal 2.2, attributes.fetch_value(:bar)
- end
-
- test "fetch_value returns nil for unknown attributes" do
- attributes = attributes_with_uninitialized_key
- assert_nil attributes.fetch_value(:wibble) { "hello" }
- end
-
- test "fetch_value returns nil for unknown attributes when types has a default" do
- types = Hash.new(Type::Value.new)
- builder = AttributeSet::Builder.new(types)
- attributes = builder.build_from_database
-
- assert_nil attributes.fetch_value(:wibble) { "hello" }
- end
-
- test "fetch_value uses the given block for uninitialized attributes" do
- attributes = attributes_with_uninitialized_key
- value = attributes.fetch_value(:bar) { |n| n.to_s + "!" }
- assert_equal "bar!", value
- end
-
- test "fetch_value returns nil for uninitialized attributes if no block is given" do
- attributes = attributes_with_uninitialized_key
- assert_nil attributes.fetch_value(:bar)
- end
-
- test "the primary_key is always initialized" do
- builder = AttributeSet::Builder.new({ foo: Type::Integer.new }, :foo)
- attributes = builder.build_from_database
-
- assert attributes.key?(:foo)
- assert_equal [:foo], attributes.keys
- assert attributes[:foo].initialized?
- end
-
- class MyType
- def cast(value)
- return if value.nil?
- value + " from user"
- end
-
- def deserialize(value)
- return if value.nil?
- value + " from database"
- end
-
- def assert_valid_value(*)
- end
- end
-
- test "write_from_database sets the attribute with database typecasting" do
- builder = AttributeSet::Builder.new(foo: MyType.new)
- attributes = builder.build_from_database
-
- assert_nil attributes.fetch_value(:foo)
-
- attributes.write_from_database(:foo, "value")
-
- assert_equal "value from database", attributes.fetch_value(:foo)
- end
-
- test "write_from_user sets the attribute with user typecasting" do
- builder = AttributeSet::Builder.new(foo: MyType.new)
- attributes = builder.build_from_database
-
- assert_nil attributes.fetch_value(:foo)
-
- attributes.write_from_user(:foo, "value")
-
- assert_equal "value from user", attributes.fetch_value(:foo)
- end
-
- def attributes_with_uninitialized_key
- builder = AttributeSet::Builder.new(foo: Type::Integer.new, bar: Type::Float.new)
- builder.build_from_database(foo: "1.1")
- end
-
- test "freezing doesn't prevent the set from materializing" do
- builder = AttributeSet::Builder.new(foo: Type::String.new)
- attributes = builder.build_from_database(foo: "1")
-
- attributes.freeze
- assert_equal({ foo: "1" }, attributes.to_hash)
- end
-
- test "#accessed_attributes returns only attributes which have been read" do
- builder = AttributeSet::Builder.new(foo: Type::Value.new, bar: Type::Value.new)
- attributes = builder.build_from_database(foo: "1", bar: "2")
-
- assert_equal [], attributes.accessed
-
- attributes.fetch_value(:foo)
-
- assert_equal [:foo], attributes.accessed
- end
-
- test "#map returns a new attribute set with the changes applied" do
- builder = AttributeSet::Builder.new(foo: Type::Integer.new, bar: Type::Integer.new)
- attributes = builder.build_from_database(foo: "1", bar: "2")
- new_attributes = attributes.map do |attr|
- attr.with_cast_value(attr.value + 1)
- end
-
- assert_equal 2, new_attributes.fetch_value(:foo)
- assert_equal 3, new_attributes.fetch_value(:bar)
- end
-
- test "comparison for equality is correctly implemented" do
- builder = AttributeSet::Builder.new(foo: Type::Integer.new, bar: Type::Integer.new)
- attributes = builder.build_from_database(foo: "1", bar: "2")
- attributes2 = builder.build_from_database(foo: "1", bar: "2")
- attributes3 = builder.build_from_database(foo: "2", bar: "2")
-
- assert_equal attributes, attributes2
- assert_not_equal attributes2, attributes3
- end
- end
-end
diff --git a/activerecord/test/cases/attribute_test.rb b/activerecord/test/cases/attribute_test.rb
deleted file mode 100644
index 7cf6b498c9..0000000000
--- a/activerecord/test/cases/attribute_test.rb
+++ /dev/null
@@ -1,253 +0,0 @@
-require "cases/helper"
-
-module ActiveRecord
- class AttributeTest < ActiveRecord::TestCase
- setup do
- @type = Minitest::Mock.new
- end
-
- teardown do
- assert @type.verify
- end
-
- test "from_database + read type casts from database" do
- @type.expect(:deserialize, "type cast from database", ["a value"])
- attribute = Attribute.from_database(nil, "a value", @type)
-
- type_cast_value = attribute.value
-
- assert_equal "type cast from database", type_cast_value
- end
-
- test "from_user + read type casts from user" do
- @type.expect(:cast, "type cast from user", ["a value"])
- attribute = Attribute.from_user(nil, "a value", @type)
-
- type_cast_value = attribute.value
-
- assert_equal "type cast from user", type_cast_value
- end
-
- test "reading memoizes the value" do
- @type.expect(:deserialize, "from the database", ["whatever"])
- attribute = Attribute.from_database(nil, "whatever", @type)
-
- type_cast_value = attribute.value
- second_read = attribute.value
-
- assert_equal "from the database", type_cast_value
- assert_same type_cast_value, second_read
- end
-
- test "reading memoizes falsy values" do
- @type.expect(:deserialize, false, ["whatever"])
- attribute = Attribute.from_database(nil, "whatever", @type)
-
- attribute.value
- attribute.value
- end
-
- test "read_before_typecast returns the given value" do
- attribute = Attribute.from_database(nil, "raw value", @type)
-
- raw_value = attribute.value_before_type_cast
-
- assert_equal "raw value", raw_value
- end
-
- test "from_database + read_for_database type casts to and from database" do
- @type.expect(:deserialize, "read from database", ["whatever"])
- @type.expect(:serialize, "ready for database", ["read from database"])
- attribute = Attribute.from_database(nil, "whatever", @type)
-
- serialize = attribute.value_for_database
-
- assert_equal "ready for database", serialize
- end
-
- test "from_user + read_for_database type casts from the user to the database" do
- @type.expect(:cast, "read from user", ["whatever"])
- @type.expect(:serialize, "ready for database", ["read from user"])
- attribute = Attribute.from_user(nil, "whatever", @type)
-
- serialize = attribute.value_for_database
-
- assert_equal "ready for database", serialize
- end
-
- test "duping dups the value" do
- @type.expect(:deserialize, "type cast", ["a value"])
- attribute = Attribute.from_database(nil, "a value", @type)
-
- value_from_orig = attribute.value
- value_from_clone = attribute.dup.value
- value_from_orig << " foo"
-
- assert_equal "type cast foo", value_from_orig
- assert_equal "type cast", value_from_clone
- end
-
- test "duping does not dup the value if it is not dupable" do
- @type.expect(:deserialize, false, ["a value"])
- attribute = Attribute.from_database(nil, "a value", @type)
-
- assert_same attribute.value, attribute.dup.value
- end
-
- test "duping does not eagerly type cast if we have not yet type cast" do
- attribute = Attribute.from_database(nil, "a value", @type)
- attribute.dup
- end
-
- class MyType
- def cast(value)
- value + " from user"
- end
-
- def deserialize(value)
- value + " from database"
- end
-
- def assert_valid_value(*)
- end
- end
-
- test "with_value_from_user returns a new attribute with the value from the user" do
- old = Attribute.from_database(nil, "old", MyType.new)
- new = old.with_value_from_user("new")
-
- assert_equal "old from database", old.value
- assert_equal "new from user", new.value
- end
-
- test "with_value_from_database returns a new attribute with the value from the database" do
- old = Attribute.from_user(nil, "old", MyType.new)
- new = old.with_value_from_database("new")
-
- assert_equal "old from user", old.value
- assert_equal "new from database", new.value
- end
-
- test "uninitialized attributes yield their name if a block is given to value" do
- block = proc { |name| name.to_s + "!" }
- foo = Attribute.uninitialized(:foo, nil)
- bar = Attribute.uninitialized(:bar, nil)
-
- assert_equal "foo!", foo.value(&block)
- assert_equal "bar!", bar.value(&block)
- end
-
- test "uninitialized attributes have no value" do
- assert_nil Attribute.uninitialized(:foo, nil).value
- end
-
- test "attributes equal other attributes with the same constructor arguments" do
- first = Attribute.from_database(:foo, 1, Type::Integer.new)
- second = Attribute.from_database(:foo, 1, Type::Integer.new)
- assert_equal first, second
- end
-
- test "attributes do not equal attributes with different names" do
- first = Attribute.from_database(:foo, 1, Type::Integer.new)
- second = Attribute.from_database(:bar, 1, Type::Integer.new)
- assert_not_equal first, second
- end
-
- test "attributes do not equal attributes with different types" do
- first = Attribute.from_database(:foo, 1, Type::Integer.new)
- second = Attribute.from_database(:foo, 1, Type::Float.new)
- assert_not_equal first, second
- end
-
- test "attributes do not equal attributes with different values" do
- first = Attribute.from_database(:foo, 1, Type::Integer.new)
- second = Attribute.from_database(:foo, 2, Type::Integer.new)
- assert_not_equal first, second
- end
-
- test "attributes do not equal attributes of other classes" do
- first = Attribute.from_database(:foo, 1, Type::Integer.new)
- second = Attribute.from_user(:foo, 1, Type::Integer.new)
- assert_not_equal first, second
- end
-
- test "an attribute has not been read by default" do
- attribute = Attribute.from_database(:foo, 1, Type::Value.new)
- assert_not attribute.has_been_read?
- end
-
- test "an attribute has been read when its value is calculated" do
- attribute = Attribute.from_database(:foo, 1, Type::Value.new)
- attribute.value
- assert attribute.has_been_read?
- end
-
- test "an attribute is not changed if it hasn't been assigned or mutated" do
- attribute = Attribute.from_database(:foo, 1, Type::Value.new)
-
- refute attribute.changed?
- end
-
- test "an attribute is changed if it's been assigned a new value" do
- attribute = Attribute.from_database(:foo, 1, Type::Value.new)
- changed = attribute.with_value_from_user(2)
-
- assert changed.changed?
- end
-
- test "an attribute is not changed if it's assigned the same value" do
- attribute = Attribute.from_database(:foo, 1, Type::Value.new)
- unchanged = attribute.with_value_from_user(1)
-
- refute unchanged.changed?
- end
-
- test "an attribute can not be mutated if it has not been read,
- and skips expensive calculations" do
- type_which_raises_from_all_methods = Object.new
- attribute = Attribute.from_database(:foo, "bar", type_which_raises_from_all_methods)
-
- assert_not attribute.changed_in_place?
- end
-
- test "an attribute is changed if it has been mutated" do
- attribute = Attribute.from_database(:foo, "bar", Type::String.new)
- attribute.value << "!"
-
- assert attribute.changed_in_place?
- assert attribute.changed?
- end
-
- test "an attribute can forget its changes" do
- attribute = Attribute.from_database(:foo, "bar", Type::String.new)
- changed = attribute.with_value_from_user("foo")
- forgotten = changed.forgetting_assignment
-
- assert changed.changed? # sanity check
- refute forgotten.changed?
- end
-
- test "with_value_from_user validates the value" do
- type = Type::Value.new
- type.define_singleton_method(:assert_valid_value) do |value|
- if value == 1
- raise ArgumentError
- end
- end
-
- attribute = Attribute.from_database(:foo, 1, type)
- assert_equal 1, attribute.value
- assert_equal 2, attribute.with_value_from_user(2).value
- assert_raises ArgumentError do
- attribute.with_value_from_user(1)
- end
- end
-
- test "with_type preserves mutations" do
- attribute = Attribute.from_database(:foo, "", Type::Value.new)
- attribute.value << "1"
-
- assert_equal 1, attribute.with_type(Type::Integer.new).value
- end
- end
-end
diff --git a/activerecord/test/cases/attributes_test.rb b/activerecord/test/cases/attributes_test.rb
index 3705a6be89..3bc56694be 100644
--- a/activerecord/test/cases/attributes_test.rb
+++ b/activerecord/test/cases/attributes_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "cases/helper"
class OverloadedType < ActiveRecord::Base
@@ -57,7 +59,7 @@ module ActiveRecord
test "nonexistent attribute" do
data = OverloadedType.new(non_existent_decimal: 1)
- assert_equal BigDecimal.new(1), data.non_existent_decimal
+ assert_equal BigDecimal(1), data.non_existent_decimal
assert_raise ActiveRecord::UnknownAttributeError do
UnoverloadedType.new(non_existent_decimal: 1)
end
@@ -106,12 +108,14 @@ module ActiveRecord
assert_equal 6, klass.attribute_types.length
assert_equal 6, klass.column_defaults.length
+ assert_equal 6, klass.attribute_names.length
assert_not klass.attribute_types.include?("wibble")
klass.attribute :wibble, Type::Value.new
assert_equal 7, klass.attribute_types.length
assert_equal 7, klass.column_defaults.length
+ assert_equal 7, klass.attribute_names.length
assert_includes klass.attribute_types, "wibble"
end
@@ -207,7 +211,7 @@ module ActiveRecord
end
test "attributes not backed by database columns are not dirty when unchanged" do
- refute OverloadedType.new.non_existent_decimal_changed?
+ assert_not_predicate OverloadedType.new, :non_existent_decimal_changed?
end
test "attributes not backed by database columns are always initialized" do
@@ -241,13 +245,13 @@ module ActiveRecord
model.foo << "asdf"
assert_equal "lolasdf", model.foo
- assert model.foo_changed?
+ assert_predicate model, :foo_changed?
model.reload
assert_equal "lol", model.foo
model.foo = "lol"
- refute model.changed?
+ assert_not_predicate model, :changed?
end
test "attributes not backed by database columns appear in inspect" do
diff --git a/activerecord/test/cases/autosave_association_test.rb b/activerecord/test/cases/autosave_association_test.rb
index 2203aa1788..09684520d1 100644
--- a/activerecord/test/cases/autosave_association_test.rb
+++ b/activerecord/test/cases/autosave_association_test.rb
@@ -1,8 +1,11 @@
+# frozen_string_literal: true
+
require "cases/helper"
require "models/bird"
require "models/post"
require "models/comment"
require "models/company"
+require "models/contract"
require "models/customer"
require "models/developer"
require "models/computer"
@@ -10,9 +13,7 @@ require "models/invoice"
require "models/line_item"
require "models/order"
require "models/parrot"
-require "models/person"
require "models/pirate"
-require "models/reader"
require "models/ship"
require "models/ship_part"
require "models/tag"
@@ -50,7 +51,7 @@ class TestAutosaveAssociationsInGeneral < ActiveRecord::TestCase
u = person.create!(first_name: "cool")
u.update_attributes!(first_name: "nah") # still valid because validation only applies on 'create'
- assert reference.create!(person: u).persisted?
+ assert_predicate reference.create!(person: u), :persisted?
end
def test_should_not_add_the_same_callbacks_multiple_times_for_has_one
@@ -73,7 +74,7 @@ class TestAutosaveAssociationsInGeneral < ActiveRecord::TestCase
ship = ShipWithoutNestedAttributes.new
ship.prisoners.build
- assert_not ship.valid?
+ assert_not_predicate ship, :valid?
assert_equal 1, ship.errors[:name].length
end
@@ -98,35 +99,35 @@ class TestDefaultAutosaveAssociationOnAHasOneAssociation < ActiveRecord::TestCas
def test_should_save_parent_but_not_invalid_child
firm = Firm.new(name: "GlobalMegaCorp")
- assert firm.valid?
+ assert_predicate firm, :valid?
firm.build_account_using_primary_key
- assert !firm.build_account_using_primary_key.valid?
+ assert_not_predicate firm.build_account_using_primary_key, :valid?
assert firm.save
- assert !firm.account_using_primary_key.persisted?
+ assert_not_predicate firm.account_using_primary_key, :persisted?
end
def test_save_fails_for_invalid_has_one
firm = Firm.first
- assert firm.valid?
+ assert_predicate firm, :valid?
firm.build_account
- assert !firm.account.valid?
- assert !firm.valid?
+ assert_not_predicate firm.account, :valid?
+ assert_not_predicate firm, :valid?
assert !firm.save
assert_equal ["is invalid"], firm.errors["account"]
end
def test_save_succeeds_for_invalid_has_one_with_validate_false
firm = Firm.first
- assert firm.valid?
+ assert_predicate firm, :valid?
firm.build_unvalidated_account
- assert !firm.unvalidated_account.valid?
- assert firm.valid?
+ assert_not_predicate firm.unvalidated_account, :valid?
+ assert_predicate firm, :valid?
assert firm.save
end
@@ -135,10 +136,10 @@ class TestDefaultAutosaveAssociationOnAHasOneAssociation < ActiveRecord::TestCas
account = firm.build_account("credit_limit" => 1000)
assert_equal account, firm.account
- assert !account.persisted?
+ assert_not_predicate account, :persisted?
assert firm.save
assert_equal account, firm.account
- assert account.persisted?
+ assert_predicate account, :persisted?
end
def test_build_before_either_saved
@@ -146,16 +147,16 @@ class TestDefaultAutosaveAssociationOnAHasOneAssociation < ActiveRecord::TestCas
firm.account = account = Account.new("credit_limit" => 1000)
assert_equal account, firm.account
- assert !account.persisted?
+ assert_not_predicate account, :persisted?
assert firm.save
assert_equal account, firm.account
- assert account.persisted?
+ assert_predicate account, :persisted?
end
def test_assignment_before_parent_saved
firm = Firm.new("name" => "GlobalMegaCorp")
firm.account = a = Account.find(1)
- assert !firm.persisted?
+ assert_not_predicate firm, :persisted?
assert_equal a, firm.account
assert firm.save
assert_equal a, firm.account
@@ -166,12 +167,12 @@ class TestDefaultAutosaveAssociationOnAHasOneAssociation < ActiveRecord::TestCas
def test_assignment_before_either_saved
firm = Firm.new("name" => "GlobalMegaCorp")
firm.account = a = Account.new("credit_limit" => 1000)
- assert !firm.persisted?
- assert !a.persisted?
+ assert_not_predicate firm, :persisted?
+ assert_not_predicate a, :persisted?
assert_equal a, firm.account
assert firm.save
- assert firm.persisted?
- assert a.persisted?
+ assert_predicate firm, :persisted?
+ assert_predicate a, :persisted?
assert_equal a, firm.account
firm.association(:account).reload
assert_equal a, firm.account
@@ -220,13 +221,13 @@ class TestDefaultAutosaveAssociationOnABelongsToAssociation < ActiveRecord::Test
def test_should_save_parent_but_not_invalid_child
client = Client.new(name: "Joe (the Plumber)")
- assert client.valid?
+ assert_predicate client, :valid?
client.build_firm
- assert !client.firm.valid?
+ assert_not_predicate client.firm, :valid?
assert client.save
- assert !client.firm.persisted?
+ assert_not_predicate client.firm, :persisted?
end
def test_save_fails_for_invalid_belongs_to
@@ -234,8 +235,8 @@ class TestDefaultAutosaveAssociationOnABelongsToAssociation < ActiveRecord::Test
assert log = AuditLog.create(developer_id: 0, message: " ")
log.developer = Developer.new
- assert !log.developer.valid?
- assert !log.valid?
+ assert_not_predicate log.developer, :valid?
+ assert_not_predicate log, :valid?
assert !log.save
assert_equal ["is invalid"], log.errors["developer"]
end
@@ -245,8 +246,8 @@ class TestDefaultAutosaveAssociationOnABelongsToAssociation < ActiveRecord::Test
assert log = AuditLog.create(developer_id: 0, message: " ")
log.unvalidated_developer = Developer.new
- assert !log.unvalidated_developer.valid?
- assert log.valid?
+ assert_not_predicate log.unvalidated_developer, :valid?
+ assert_predicate log, :valid?
assert log.save
end
@@ -255,10 +256,10 @@ class TestDefaultAutosaveAssociationOnABelongsToAssociation < ActiveRecord::Test
apple = Firm.new("name" => "Apple")
client.firm = apple
assert_equal apple, client.firm
- assert !apple.persisted?
+ assert_not_predicate apple, :persisted?
assert client.save
assert apple.save
- assert apple.persisted?
+ assert_predicate apple, :persisted?
assert_equal apple, client.firm
client.association(:firm).reload
assert_equal apple, client.firm
@@ -268,11 +269,11 @@ class TestDefaultAutosaveAssociationOnABelongsToAssociation < ActiveRecord::Test
final_cut = Client.new("name" => "Final Cut")
apple = Firm.new("name" => "Apple")
final_cut.firm = apple
- assert !final_cut.persisted?
- assert !apple.persisted?
+ assert_not_predicate final_cut, :persisted?
+ assert_not_predicate apple, :persisted?
assert final_cut.save
- assert final_cut.persisted?
- assert apple.persisted?
+ assert_predicate final_cut, :persisted?
+ assert_predicate apple, :persisted?
assert_equal apple, final_cut.firm
final_cut.association(:firm).reload
assert_equal apple, final_cut.firm
@@ -381,7 +382,7 @@ class TestDefaultAutosaveAssociationOnABelongsToAssociation < ActiveRecord::Test
auditlog.developer = invalid_developer
auditlog.developer_id = valid_developer.id
- assert auditlog.valid?
+ assert_predicate auditlog, :valid?
end
end
@@ -394,8 +395,8 @@ class TestDefaultAutosaveAssociationOnAHasManyAssociationWithAcceptsNestedAttrib
molecule.electrons = [valid_electron, invalid_electron]
molecule.save
- assert_not invalid_electron.valid?
- assert valid_electron.valid?
+ assert_not_predicate invalid_electron, :valid?
+ assert_predicate valid_electron, :valid?
assert_not molecule.persisted?, "Molecule should not be persisted when its electrons are invalid"
end
@@ -407,9 +408,9 @@ class TestDefaultAutosaveAssociationOnAHasManyAssociationWithAcceptsNestedAttrib
guitar.tuning_pegs = [tuning_peg_valid, tuning_peg_invalid]
- assert_not tuning_peg_invalid.valid?
- assert tuning_peg_valid.valid?
- assert_not guitar.valid?
+ assert_not_predicate tuning_peg_invalid, :valid?
+ assert_predicate tuning_peg_valid, :valid?
+ assert_not_predicate guitar, :valid?
assert_equal ["is not a number"], guitar.errors["tuning_pegs[1].pitch"]
assert_not_equal ["is not a number"], guitar.errors["tuning_pegs.pitch"]
end
@@ -424,9 +425,9 @@ class TestDefaultAutosaveAssociationOnAHasManyAssociationWithAcceptsNestedAttrib
molecule.electrons = [valid_electron, invalid_electron]
- assert_not invalid_electron.valid?
- assert valid_electron.valid?
- assert_not molecule.valid?
+ assert_not_predicate invalid_electron, :valid?
+ assert_predicate valid_electron, :valid?
+ assert_not_predicate molecule, :valid?
assert_equal ["can't be blank"], molecule.errors["electrons[1].name"]
assert_not_equal ["can't be blank"], molecule.errors["electrons.name"]
ensure
@@ -440,9 +441,9 @@ class TestDefaultAutosaveAssociationOnAHasManyAssociationWithAcceptsNestedAttrib
molecule.electrons = [valid_electron, invalid_electron]
- assert_not invalid_electron.valid?
- assert valid_electron.valid?
- assert_not molecule.valid?
+ assert_not_predicate invalid_electron, :valid?
+ assert_predicate valid_electron, :valid?
+ assert_not_predicate molecule, :valid?
assert_equal [{ error: :blank }], molecule.errors.details[:"electrons.name"]
end
@@ -454,9 +455,9 @@ class TestDefaultAutosaveAssociationOnAHasManyAssociationWithAcceptsNestedAttrib
guitar.tuning_pegs = [tuning_peg_valid, tuning_peg_invalid]
- assert_not tuning_peg_invalid.valid?
- assert tuning_peg_valid.valid?
- assert_not guitar.valid?
+ assert_not_predicate tuning_peg_invalid, :valid?
+ assert_predicate tuning_peg_valid, :valid?
+ assert_not_predicate guitar, :valid?
assert_equal [{ error: :not_a_number, value: nil }], guitar.errors.details[:"tuning_pegs[1].pitch"]
assert_equal [], guitar.errors.details[:"tuning_pegs.pitch"]
end
@@ -471,9 +472,9 @@ class TestDefaultAutosaveAssociationOnAHasManyAssociationWithAcceptsNestedAttrib
molecule.electrons = [valid_electron, invalid_electron]
- assert_not invalid_electron.valid?
- assert valid_electron.valid?
- assert_not molecule.valid?
+ assert_not_predicate invalid_electron, :valid?
+ assert_predicate valid_electron, :valid?
+ assert_not_predicate molecule, :valid?
assert_equal [{ error: :blank }], molecule.errors.details[:"electrons[1].name"]
assert_equal [], molecule.errors.details[:"electrons.name"]
ensure
@@ -487,33 +488,33 @@ class TestDefaultAutosaveAssociationOnAHasManyAssociationWithAcceptsNestedAttrib
molecule.electrons = [valid_electron]
molecule.save
- assert valid_electron.valid?
- assert molecule.persisted?
+ assert_predicate valid_electron, :valid?
+ assert_predicate molecule, :persisted?
assert_equal 1, molecule.electrons.count
end
end
class TestDefaultAutosaveAssociationOnAHasManyAssociation < ActiveRecord::TestCase
- fixtures :companies, :people
+ fixtures :companies, :developers
def test_invalid_adding
firm = Firm.find(1)
assert !(firm.clients_of_firm << c = Client.new)
- assert !c.persisted?
- assert !firm.valid?
+ assert_not_predicate c, :persisted?
+ assert_not_predicate firm, :valid?
assert !firm.save
- assert !c.persisted?
+ assert_not_predicate c, :persisted?
end
def test_invalid_adding_before_save
new_firm = Firm.new("name" => "A New Firm, Inc")
new_firm.clients_of_firm.concat([c = Client.new, Client.new("name" => "Apple")])
- assert !c.persisted?
- assert !c.valid?
- assert !new_firm.valid?
+ assert_not_predicate c, :persisted?
+ assert_not_predicate c, :valid?
+ assert_not_predicate new_firm, :valid?
assert !new_firm.save
- assert !c.persisted?
- assert !new_firm.persisted?
+ assert_not_predicate c, :persisted?
+ assert_not_predicate new_firm, :persisted?
end
def test_invalid_adding_with_validate_false
@@ -521,10 +522,10 @@ class TestDefaultAutosaveAssociationOnAHasManyAssociation < ActiveRecord::TestCa
client = Client.new
firm.unvalidated_clients_of_firm << client
- assert firm.valid?
- assert !client.valid?
+ assert_predicate firm, :valid?
+ assert_not_predicate client, :valid?
assert firm.save
- assert !client.persisted?
+ assert_not_predicate client, :persisted?
end
def test_valid_adding_with_validate_false
@@ -533,24 +534,24 @@ class TestDefaultAutosaveAssociationOnAHasManyAssociation < ActiveRecord::TestCa
firm = Firm.first
client = Client.new("name" => "Apple")
- assert firm.valid?
- assert client.valid?
- assert !client.persisted?
+ assert_predicate firm, :valid?
+ assert_predicate client, :valid?
+ assert_not_predicate client, :persisted?
firm.unvalidated_clients_of_firm << client
assert firm.save
- assert client.persisted?
+ assert_predicate client, :persisted?
assert_equal no_of_clients + 1, Client.count
end
def test_invalid_build
new_client = companies(:first_firm).clients_of_firm.build
- assert !new_client.persisted?
- assert !new_client.valid?
+ assert_not_predicate new_client, :persisted?
+ assert_not_predicate new_client, :valid?
assert_equal new_client, companies(:first_firm).clients_of_firm.last
assert !companies(:first_firm).save
- assert !new_client.persisted?
+ assert_not_predicate new_client, :persisted?
assert_equal 2, companies(:first_firm).clients_of_firm.reload.size
end
@@ -569,8 +570,8 @@ class TestDefaultAutosaveAssociationOnAHasManyAssociation < ActiveRecord::TestCa
assert_equal no_of_firms, Firm.count # Firm was not saved to database.
assert_equal no_of_clients, Client.count # Clients were not saved to database.
assert new_firm.save
- assert new_firm.persisted?
- assert c.persisted?
+ assert_predicate new_firm, :persisted?
+ assert_predicate c, :persisted?
assert_equal new_firm, c.firm
assert_equal no_of_firms + 1, Firm.count # Firm was saved to database.
assert_equal no_of_clients + 2, Client.count # Clients were saved to database.
@@ -589,22 +590,22 @@ class TestDefaultAutosaveAssociationOnAHasManyAssociation < ActiveRecord::TestCa
end
def test_assign_ids_for_through_a_belongs_to
- post = Post.new(title: "Assigning IDs works!", body: "You heard it here first, folks!")
- post.person_ids = [people(:david).id, people(:michael).id]
- post.save
- post.reload
- assert_equal 2, post.people.length
- assert_includes post.people, people(:david)
+ firm = Firm.new("name" => "Apple")
+ firm.developer_ids = [developers(:david).id, developers(:jamis).id]
+ firm.save
+ firm.reload
+ assert_equal 2, firm.developers.length
+ assert_includes firm.developers, developers(:david)
end
def test_build_before_save
company = companies(:first_firm)
new_client = assert_no_queries(ignore_none: false) { company.clients_of_firm.build("name" => "Another Client") }
- assert !company.clients_of_firm.loaded?
+ assert_not_predicate company.clients_of_firm, :loaded?
company.name += "-changed"
assert_queries(2) { assert company.save }
- assert new_client.persisted?
+ assert_predicate new_client, :persisted?
assert_equal 3, company.clients_of_firm.reload.size
end
@@ -620,11 +621,11 @@ class TestDefaultAutosaveAssociationOnAHasManyAssociation < ActiveRecord::TestCa
def test_build_via_block_before_save
company = companies(:first_firm)
new_client = assert_no_queries(ignore_none: false) { company.clients_of_firm.build { |client| client.name = "Another Client" } }
- assert !company.clients_of_firm.loaded?
+ assert_not_predicate company.clients_of_firm, :loaded?
company.name += "-changed"
assert_queries(2) { assert company.save }
- assert new_client.persisted?
+ assert_predicate new_client, :persisted?
assert_equal 3, company.clients_of_firm.reload.size
end
@@ -656,62 +657,62 @@ class TestDefaultAutosaveAssociationOnNewRecord < ActiveRecord::TestCase
new_account = Account.new("credit_limit" => 1000)
new_firm = Firm.new("name" => "some firm")
- assert !new_firm.persisted?
+ assert_not_predicate new_firm, :persisted?
new_account.firm = new_firm
new_account.save!
- assert new_firm.persisted?
+ assert_predicate new_firm, :persisted?
new_account = Account.new("credit_limit" => 1000)
new_autosaved_firm = Firm.new("name" => "some firm")
- assert !new_autosaved_firm.persisted?
+ assert_not_predicate new_autosaved_firm, :persisted?
new_account.unautosaved_firm = new_autosaved_firm
new_account.save!
- assert !new_autosaved_firm.persisted?
+ assert_not_predicate new_autosaved_firm, :persisted?
end
def test_autosave_new_record_on_has_one_can_be_disabled_per_relationship
firm = Firm.new("name" => "some firm")
account = Account.new("credit_limit" => 1000)
- assert !account.persisted?
+ assert_not_predicate account, :persisted?
firm.account = account
firm.save!
- assert account.persisted?
+ assert_predicate account, :persisted?
firm = Firm.new("name" => "some firm")
account = Account.new("credit_limit" => 1000)
firm.unautosaved_account = account
- assert !account.persisted?
+ assert_not_predicate account, :persisted?
firm.unautosaved_account = account
firm.save!
- assert !account.persisted?
+ assert_not_predicate account, :persisted?
end
def test_autosave_new_record_on_has_many_can_be_disabled_per_relationship
firm = Firm.new("name" => "some firm")
account = Account.new("credit_limit" => 1000)
- assert !account.persisted?
+ assert_not_predicate account, :persisted?
firm.accounts << account
firm.save!
- assert account.persisted?
+ assert_predicate account, :persisted?
firm = Firm.new("name" => "some firm")
account = Account.new("credit_limit" => 1000)
- assert !account.persisted?
+ assert_not_predicate account, :persisted?
firm.unautosaved_accounts << account
firm.save!
- assert !account.persisted?
+ assert_not_predicate account, :persisted?
end
def test_autosave_new_record_with_after_create_callback
@@ -744,18 +745,18 @@ class TestDestroyAsPartOfAutosaveAssociation < ActiveRecord::TestCase
@pirate.mark_for_destruction
@pirate.ship.mark_for_destruction
- assert !@pirate.reload.marked_for_destruction?
- assert !@pirate.ship.reload.marked_for_destruction?
+ assert_not_predicate @pirate.reload, :marked_for_destruction?
+ assert_not_predicate @pirate.ship.reload, :marked_for_destruction?
end
# has_one
def test_should_destroy_a_child_association_as_part_of_the_save_transaction_if_it_was_marked_for_destruction
- assert !@pirate.ship.marked_for_destruction?
+ assert_not_predicate @pirate.ship, :marked_for_destruction?
@pirate.ship.mark_for_destruction
id = @pirate.ship.id
- assert @pirate.ship.marked_for_destruction?
+ assert_predicate @pirate.ship, :marked_for_destruction?
assert Ship.find_by_id(id)
@pirate.save
@@ -765,7 +766,7 @@ class TestDestroyAsPartOfAutosaveAssociation < ActiveRecord::TestCase
def test_should_skip_validation_on_a_child_association_if_marked_for_destruction
@pirate.ship.name = ""
- assert !@pirate.valid?
+ assert_not_predicate @pirate, :valid?
@pirate.ship.mark_for_destruction
@pirate.ship.expects(:valid?).never
@@ -811,12 +812,12 @@ class TestDestroyAsPartOfAutosaveAssociation < ActiveRecord::TestCase
# belongs_to
def test_should_destroy_a_parent_association_as_part_of_the_save_transaction_if_it_was_marked_for_destruction
- assert !@ship.pirate.marked_for_destruction?
+ assert_not_predicate @ship.pirate, :marked_for_destruction?
@ship.pirate.mark_for_destruction
id = @ship.pirate.id
- assert @ship.pirate.marked_for_destruction?
+ assert_predicate @ship.pirate, :marked_for_destruction?
assert Pirate.find_by_id(id)
@ship.save
@@ -826,7 +827,7 @@ class TestDestroyAsPartOfAutosaveAssociation < ActiveRecord::TestCase
def test_should_skip_validation_on_a_parent_association_if_marked_for_destruction
@ship.pirate.catchphrase = ""
- assert !@ship.valid?
+ assert_not_predicate @ship, :valid?
@ship.pirate.mark_for_destruction
@ship.pirate.expects(:valid?).never
@@ -880,7 +881,7 @@ class TestDestroyAsPartOfAutosaveAssociation < ActiveRecord::TestCase
ids.each { |id| assert klass.find_by_id(id) }
@pirate.save
- assert @pirate.reload.birds.empty?
+ assert_empty @pirate.reload.birds
ids.each { |id| assert_nil klass.find_by_id(id) }
end
@@ -888,14 +889,14 @@ class TestDestroyAsPartOfAutosaveAssociation < ActiveRecord::TestCase
@pirate.birds.create!(name: :parrot)
@pirate.birds.first.destroy
@pirate.save!
- assert @pirate.reload.birds.empty?
+ assert_empty @pirate.reload.birds
end
def test_should_skip_validation_on_has_many_if_marked_for_destruction
2.times { |i| @pirate.birds.create!(name: "birds_#{i}") }
@pirate.birds.each { |bird| bird.name = "" }
- assert !@pirate.valid?
+ assert_not_predicate @pirate, :valid?
@pirate.birds.each do |bird|
bird.mark_for_destruction
@@ -908,10 +909,10 @@ class TestDestroyAsPartOfAutosaveAssociation < ActiveRecord::TestCase
@pirate.birds.create!(name: "birds_1")
@pirate.birds.each { |bird| bird.name = "" }
- assert !@pirate.valid?
+ assert_not_predicate @pirate, :valid?
@pirate.birds.each(&:destroy)
- assert @pirate.valid?
+ assert_predicate @pirate, :valid?
end
def test_a_child_marked_for_destruction_should_not_be_destroyed_twice_while_saving_has_many
@@ -1009,17 +1010,17 @@ class TestDestroyAsPartOfAutosaveAssociation < ActiveRecord::TestCase
@pirate.save
end
- assert @pirate.reload.parrots.empty?
+ assert_empty @pirate.reload.parrots
join_records = Pirate.connection.select_all("SELECT * FROM parrots_pirates WHERE pirate_id = #{@pirate.id}")
- assert join_records.empty?
+ assert_empty join_records
end
def test_should_skip_validation_on_habtm_if_marked_for_destruction
2.times { |i| @pirate.parrots.create!(name: "parrots_#{i}") }
@pirate.parrots.each { |parrot| parrot.name = "" }
- assert !@pirate.valid?
+ assert_not_predicate @pirate, :valid?
@pirate.parrots.each do |parrot|
parrot.mark_for_destruction
@@ -1027,17 +1028,17 @@ class TestDestroyAsPartOfAutosaveAssociation < ActiveRecord::TestCase
end
@pirate.save!
- assert @pirate.reload.parrots.empty?
+ assert_empty @pirate.reload.parrots
end
def test_should_skip_validation_on_habtm_if_destroyed
@pirate.parrots.create!(name: "parrots_1")
@pirate.parrots.each { |parrot| parrot.name = "" }
- assert !@pirate.valid?
+ assert_not_predicate @pirate, :valid?
@pirate.parrots.each(&:destroy)
- assert @pirate.valid?
+ assert_predicate @pirate, :valid?
end
def test_a_child_marked_for_destruction_should_not_be_destroyed_twice_while_saving_habtm
@@ -1144,16 +1145,16 @@ class TestAutosaveAssociationOnAHasOneAssociation < ActiveRecord::TestCase
def test_should_automatically_validate_the_associated_model
@pirate.ship.name = ""
- assert @pirate.invalid?
- assert @pirate.errors[:"ship.name"].any?
+ assert_predicate @pirate, :invalid?
+ assert_predicate @pirate.errors[:"ship.name"], :any?
end
def test_should_merge_errors_on_the_associated_models_onto_the_parent_even_if_it_is_not_valid
@pirate.ship.name = nil
@pirate.catchphrase = nil
- assert @pirate.invalid?
- assert @pirate.errors[:"ship.name"].any?
- assert @pirate.errors[:catchphrase].any?
+ assert_predicate @pirate, :invalid?
+ assert_predicate @pirate.errors[:"ship.name"], :any?
+ assert_predicate @pirate.errors[:catchphrase], :any?
end
def test_should_not_ignore_different_error_messages_on_the_same_attribute
@@ -1162,7 +1163,7 @@ class TestAutosaveAssociationOnAHasOneAssociation < ActiveRecord::TestCase
Ship.validates_format_of :name, with: /\w/
@pirate.ship.name = ""
@pirate.catchphrase = nil
- assert @pirate.invalid?
+ assert_predicate @pirate, :invalid?
assert_equal ["can't be blank", "is invalid"], @pirate.errors[:"ship.name"]
ensure
Ship._validators = old_validators if old_validators
@@ -1243,7 +1244,7 @@ class TestAutosaveAssociationOnAHasOneAssociation < ActiveRecord::TestCase
ship = ShipWithoutNestedAttributes.new(name: "The Black Flag")
ship.parts.build.mark_for_destruction
- assert_not ship.valid?
+ assert_not_predicate ship, :valid?
end
end
@@ -1298,16 +1299,16 @@ class TestAutosaveAssociationOnABelongsToAssociation < ActiveRecord::TestCase
def test_should_automatically_validate_the_associated_model
@ship.pirate.catchphrase = ""
- assert @ship.invalid?
- assert @ship.errors[:"pirate.catchphrase"].any?
+ assert_predicate @ship, :invalid?
+ assert_predicate @ship.errors[:"pirate.catchphrase"], :any?
end
def test_should_merge_errors_on_the_associated_model_onto_the_parent_even_if_it_is_not_valid
@ship.name = nil
@ship.pirate.catchphrase = nil
- assert @ship.invalid?
- assert @ship.errors[:name].any?
- assert @ship.errors[:"pirate.catchphrase"].any?
+ assert_predicate @ship, :invalid?
+ assert_predicate @ship.errors[:name], :any?
+ assert_predicate @ship.errors[:"pirate.catchphrase"], :any?
end
def test_should_still_allow_to_bypass_validations_on_the_associated_model
@@ -1370,7 +1371,7 @@ module AutosaveAssociationOnACollectionAssociationTests
@pirate.send(@association_name).each_with_index { |child, i| child.name = new_names[i] }
@pirate.save
- assert_equal new_names, @pirate.reload.send(@association_name).map(&:name)
+ assert_equal new_names.sort, @pirate.reload.send(@association_name).map(&:name).sort
end
def test_should_automatically_save_bang_the_associated_models
@@ -1378,7 +1379,7 @@ module AutosaveAssociationOnACollectionAssociationTests
@pirate.send(@association_name).each_with_index { |child, i| child.name = new_names[i] }
@pirate.save!
- assert_equal new_names, @pirate.reload.send(@association_name).map(&:name)
+ assert_equal new_names.sort, @pirate.reload.send(@association_name).map(&:name).sort
end
def test_should_update_children_when_autosave_is_true_and_parent_is_new_but_child_is_not
@@ -1402,17 +1403,17 @@ module AutosaveAssociationOnACollectionAssociationTests
def test_should_automatically_validate_the_associated_models
@pirate.send(@association_name).each { |child| child.name = "" }
- assert !@pirate.valid?
+ assert_not_predicate @pirate, :valid?
assert_equal ["can't be blank"], @pirate.errors["#{@association_name}.name"]
- assert @pirate.errors[@association_name].empty?
+ assert_empty @pirate.errors[@association_name]
end
def test_should_not_use_default_invalid_error_on_associated_models
@pirate.send(@association_name).build(name: "")
- assert !@pirate.valid?
+ assert_not_predicate @pirate, :valid?
assert_equal ["can't be blank"], @pirate.errors["#{@association_name}.name"]
- assert @pirate.errors[@association_name].empty?
+ assert_empty @pirate.errors[@association_name]
end
def test_should_default_invalid_error_from_i18n
@@ -1422,10 +1423,10 @@ module AutosaveAssociationOnACollectionAssociationTests
@pirate.send(@association_name).build(name: "")
- assert !@pirate.valid?
+ assert_not_predicate @pirate, :valid?
assert_equal ["cannot be blank"], @pirate.errors["#{@association_name}.name"]
assert_equal ["#{@association_name.to_s.humanize} name cannot be blank"], @pirate.errors.full_messages
- assert @pirate.errors[@association_name].empty?
+ assert_empty @pirate.errors[@association_name]
ensure
I18n.backend = I18n::Backend::Simple.new
end
@@ -1434,9 +1435,9 @@ module AutosaveAssociationOnACollectionAssociationTests
@pirate.send(@association_name).each { |child| child.name = "" }
@pirate.catchphrase = nil
- assert !@pirate.valid?
+ assert_not_predicate @pirate, :valid?
assert_equal ["can't be blank"], @pirate.errors["#{@association_name}.name"]
- assert @pirate.errors[:catchphrase].any?
+ assert_predicate @pirate.errors[:catchphrase], :any?
end
def test_should_allow_to_bypass_validations_on_the_associated_models_on_update
@@ -1594,10 +1595,10 @@ class TestAutosaveAssociationValidationsOnAHasManyAssociation < ActiveRecord::Te
end
test "should automatically validate associations" do
- assert @pirate.valid?
+ assert_predicate @pirate, :valid?
@pirate.birds.each { |bird| bird.name = "" }
- assert !@pirate.valid?
+ assert_not_predicate @pirate, :valid?
end
end
@@ -1612,15 +1613,15 @@ class TestAutosaveAssociationValidationsOnAHasOneAssociation < ActiveRecord::Tes
end
test "should automatically validate associations with :validate => true" do
- assert @pirate.valid?
+ assert_predicate @pirate, :valid?
@pirate.ship.name = ""
- assert !@pirate.valid?
+ assert_not_predicate @pirate, :valid?
end
test "should not automatically add validate associations without :validate => true" do
- assert @pirate.valid?
+ assert_predicate @pirate, :valid?
@pirate.non_validated_ship.name = ""
- assert @pirate.valid?
+ assert_predicate @pirate, :valid?
end
end
@@ -1633,15 +1634,15 @@ class TestAutosaveAssociationValidationsOnABelongsToAssociation < ActiveRecord::
end
test "should automatically validate associations with :validate => true" do
- assert @pirate.valid?
+ assert_predicate @pirate, :valid?
@pirate.parrot = Parrot.new(name: "")
- assert !@pirate.valid?
+ assert_not_predicate @pirate, :valid?
end
test "should not automatically validate associations without :validate => true" do
- assert @pirate.valid?
+ assert_predicate @pirate, :valid?
@pirate.non_validated_parrot = Parrot.new(name: "")
- assert @pirate.valid?
+ assert_predicate @pirate, :valid?
end
end
@@ -1654,17 +1655,17 @@ class TestAutosaveAssociationValidationsOnAHABTMAssociation < ActiveRecord::Test
end
test "should automatically validate associations with :validate => true" do
- assert @pirate.valid?
+ assert_predicate @pirate, :valid?
@pirate.parrots = [ Parrot.new(name: "popuga") ]
@pirate.parrots.each { |parrot| parrot.name = "" }
- assert !@pirate.valid?
+ assert_not_predicate @pirate, :valid?
end
test "should not automatically validate associations without :validate => true" do
- assert @pirate.valid?
+ assert_predicate @pirate, :valid?
@pirate.non_validated_parrots = [ Parrot.new(name: "popuga") ]
@pirate.non_validated_parrots.each { |parrot| parrot.name = "" }
- assert @pirate.valid?
+ assert_predicate @pirate, :valid?
end
end
@@ -1685,7 +1686,7 @@ class TestAutosaveAssociationValidationMethodsGeneration < ActiveRecord::TestCas
end
test "should not generate validation methods for has_one associations without :validate => true" do
- assert !@pirate.respond_to?(:validate_associated_records_for_non_validated_ship)
+ assert_not_respond_to @pirate, :validate_associated_records_for_non_validated_ship
end
test "should generate validation methods for belongs_to associations with :validate => true" do
@@ -1693,7 +1694,7 @@ class TestAutosaveAssociationValidationMethodsGeneration < ActiveRecord::TestCas
end
test "should not generate validation methods for belongs_to associations without :validate => true" do
- assert !@pirate.respond_to?(:validate_associated_records_for_non_validated_parrot)
+ assert_not_respond_to @pirate, :validate_associated_records_for_non_validated_parrot
end
test "should generate validation methods for HABTM associations with :validate => true" do
@@ -1722,6 +1723,10 @@ class TestAutosaveAssociationOnAHasManyAssociationWithInverse < ActiveRecord::Te
end
end
+ def setup
+ Comment.delete_all
+ end
+
def test_after_save_callback_with_autosave
post = Post.new(title: "Test", body: "...")
comment = post.comments.build(body: "...")
diff --git a/activerecord/test/cases/base_test.rb b/activerecord/test/cases/base_test.rb
index dc32e995a4..983a3d366a 100644
--- a/activerecord/test/cases/base_test.rb
+++ b/activerecord/test/cases/base_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "cases/helper"
require "models/post"
require "models/author"
@@ -102,7 +104,7 @@ class BasicsTest < ActiveRecord::TestCase
pk = Author.columns_hash["id"]
ref = Post.columns_hash["author_id"]
- assert_equal pk.bigint?, ref.bigint?
+ assert_equal pk.sql_type, ref.sql_type
end
def test_many_mutations
@@ -145,8 +147,8 @@ class BasicsTest < ActiveRecord::TestCase
end
def test_table_exists
- assert !NonExistentTable.table_exists?
- assert Topic.table_exists?
+ assert_not_predicate NonExistentTable, :table_exists?
+ assert_predicate Topic, :table_exists?
end
def test_preserving_date_objects
@@ -448,7 +450,7 @@ class BasicsTest < ActiveRecord::TestCase
def test_default_values
topic = Topic.new
- assert topic.approved?
+ assert_predicate topic, :approved?
assert_nil topic.written_on
assert_nil topic.bonus_time
assert_nil topic.last_read
@@ -456,7 +458,7 @@ class BasicsTest < ActiveRecord::TestCase
topic.save
topic = Topic.find(topic.id)
- assert topic.approved?
+ assert_predicate topic, :approved?
assert_nil topic.last_read
# Oracle has some funky default handling, so it requires a bit of
@@ -627,7 +629,7 @@ class BasicsTest < ActiveRecord::TestCase
end
def test_readonly_attributes
- assert_equal Set.new([ "title" , "comments_count" ]), ReadonlyTitlePost.readonly_attributes
+ assert_equal Set.new([ "title", "comments_count" ]), ReadonlyTitlePost.readonly_attributes
post = ReadonlyTitlePost.create(title: "cannot change this", body: "changeable")
post.reload
@@ -725,9 +727,9 @@ class BasicsTest < ActiveRecord::TestCase
b_nil = Boolean.find(nil_id)
assert_nil b_nil.value
b_false = Boolean.find(false_id)
- assert !b_false.value?
+ assert_not_predicate b_false, :value?
b_true = Boolean.find(true_id)
- assert b_true.value?
+ assert_predicate b_true, :value?
end
def test_boolean_without_questionmark
@@ -751,9 +753,9 @@ class BasicsTest < ActiveRecord::TestCase
b_blank = Boolean.find(blank_id)
assert_nil b_blank.value
b_false = Boolean.find(false_id)
- assert !b_false.value?
+ assert_not_predicate b_false, :value?
b_true = Boolean.find(true_id)
- assert b_true.value?
+ assert_predicate b_true, :value?
end
def test_new_record_returns_boolean
@@ -766,7 +768,7 @@ class BasicsTest < ActiveRecord::TestCase
duped_topic = nil
assert_nothing_raised { duped_topic = topic.dup }
assert_equal topic.title, duped_topic.title
- assert !duped_topic.persisted?
+ assert_not_predicate duped_topic, :persisted?
# test if the attributes have been duped
topic.title = "a"
@@ -784,7 +786,7 @@ class BasicsTest < ActiveRecord::TestCase
# test if saved clone object differs from original
duped_topic.save
- assert duped_topic.persisted?
+ assert_predicate duped_topic, :persisted?
assert_not_equal duped_topic.id, topic.id
duped_topic.reload
@@ -805,7 +807,7 @@ class BasicsTest < ActiveRecord::TestCase
assert_nothing_raised { dup = dev.dup }
assert_kind_of DeveloperSalary, dup.salary
assert_equal dev.salary.amount, dup.salary.amount
- assert !dup.persisted?
+ assert_not_predicate dup, :persisted?
# test if the attributes have been duped
original_amount = dup.salary.amount
@@ -813,7 +815,7 @@ class BasicsTest < ActiveRecord::TestCase
assert_equal original_amount, dup.salary.amount
assert dup.save
- assert dup.persisted?
+ assert_predicate dup, :persisted?
assert_not_equal dup.id, dev.id
end
@@ -833,22 +835,22 @@ class BasicsTest < ActiveRecord::TestCase
def test_clone_of_new_object_with_defaults
developer = Developer.new
- assert !developer.name_changed?
- assert !developer.salary_changed?
+ assert_not_predicate developer, :name_changed?
+ assert_not_predicate developer, :salary_changed?
cloned_developer = developer.clone
- assert !cloned_developer.name_changed?
- assert !cloned_developer.salary_changed?
+ assert_not_predicate cloned_developer, :name_changed?
+ assert_not_predicate cloned_developer, :salary_changed?
end
def test_clone_of_new_object_marks_attributes_as_dirty
developer = Developer.new name: "Bjorn", salary: 100000
- assert developer.name_changed?
- assert developer.salary_changed?
+ assert_predicate developer, :name_changed?
+ assert_predicate developer, :salary_changed?
cloned_developer = developer.clone
- assert cloned_developer.name_changed?
- assert cloned_developer.salary_changed?
+ assert_predicate cloned_developer, :name_changed?
+ assert_predicate cloned_developer, :salary_changed?
end
def test_clone_of_new_object_marks_as_dirty_only_changed_attributes
@@ -857,24 +859,24 @@ class BasicsTest < ActiveRecord::TestCase
assert !developer.salary_changed? # attribute has non-nil default value, so treated as not changed
cloned_developer = developer.clone
- assert cloned_developer.name_changed?
+ assert_predicate cloned_developer, :name_changed?
assert !cloned_developer.salary_changed? # ... and cloned instance should behave same
end
def test_dup_of_saved_object_marks_attributes_as_dirty
developer = Developer.create! name: "Bjorn", salary: 100000
- assert !developer.name_changed?
- assert !developer.salary_changed?
+ assert_not_predicate developer, :name_changed?
+ assert_not_predicate developer, :salary_changed?
cloned_developer = developer.dup
assert cloned_developer.name_changed? # both attributes differ from defaults
- assert cloned_developer.salary_changed?
+ assert_predicate cloned_developer, :salary_changed?
end
def test_dup_of_saved_object_marks_as_dirty_only_changed_attributes
developer = Developer.create! name: "Bjorn"
assert !developer.name_changed? # both attributes of saved object should be treated as not changed
- assert !developer.salary_changed?
+ assert_not_predicate developer, :salary_changed?
cloned_developer = developer.dup
assert cloned_developer.name_changed? # ... but on cloned object should be
@@ -883,10 +885,15 @@ class BasicsTest < ActiveRecord::TestCase
def test_bignum
company = Company.find(1)
- company.rating = 2147483647
+ company.rating = 2147483648
company.save
company = Company.find(1)
- assert_equal 2147483647, company.rating
+ assert_equal 2147483648, company.rating
+ end
+
+ def test_bignum_pk
+ company = Company.create!(id: 2147483648, name: "foo")
+ assert_equal company, Company.find(company.id)
end
# TODO: extend defaults tests to other databases!
@@ -944,14 +951,14 @@ class BasicsTest < ActiveRecord::TestCase
end
def test_toggle_attribute
- assert !topics(:first).approved?
+ assert_not_predicate topics(:first), :approved?
topics(:first).toggle!(:approved)
- assert topics(:first).approved?
+ assert_predicate topics(:first), :approved?
topic = topics(:first)
topic.toggle(:approved)
- assert !topic.approved?
+ assert_not_predicate topic, :approved?
topic.reload
- assert topic.approved?
+ assert_predicate topic, :approved?
end
def test_reload
@@ -1052,29 +1059,15 @@ class BasicsTest < ActiveRecord::TestCase
def test_count_with_join
res = Post.count_by_sql "SELECT COUNT(*) FROM posts LEFT JOIN comments ON posts.id=comments.post_id WHERE posts.#{QUOTED_TYPE} = 'Post'"
-
res2 = Post.where("posts.#{QUOTED_TYPE} = 'Post'").joins("LEFT JOIN comments ON posts.id=comments.post_id").count
assert_equal res, res2
- res3 = nil
- assert_nothing_raised do
- res3 = Post.where("posts.#{QUOTED_TYPE} = 'Post'").joins("LEFT JOIN comments ON posts.id=comments.post_id").count
- end
- assert_equal res, res3
-
res4 = Post.count_by_sql "SELECT COUNT(p.id) FROM posts p, comments co WHERE p.#{QUOTED_TYPE} = 'Post' AND p.id=co.post_id"
- res5 = nil
- assert_nothing_raised do
- res5 = Post.where("p.#{QUOTED_TYPE} = 'Post' AND p.id=co.post_id").joins("p, comments co").select("p.id").count
- end
-
+ res5 = Post.where("p.#{QUOTED_TYPE} = 'Post' AND p.id=co.post_id").joins("p, comments co").select("p.id").count
assert_equal res4, res5
res6 = Post.count_by_sql "SELECT COUNT(DISTINCT p.id) FROM posts p, comments co WHERE p.#{QUOTED_TYPE} = 'Post' AND p.id=co.post_id"
- res7 = nil
- assert_nothing_raised do
- res7 = Post.where("p.#{QUOTED_TYPE} = 'Post' AND p.id=co.post_id").joins("p, comments co").select("p.id").distinct.count
- end
+ res7 = Post.where("p.#{QUOTED_TYPE} = 'Post' AND p.id=co.post_id").joins("p, comments co").select("p.id").distinct.count
assert_equal res6, res7
end
@@ -1438,28 +1431,92 @@ class BasicsTest < ActiveRecord::TestCase
test "resetting column information doesn't remove attribute methods" do
topic = topics(:first)
- assert_not topic.id_changed?
+ assert_not_predicate topic, :id_changed?
Topic.reset_column_information
- assert_not topic.id_changed?
+ assert_not_predicate topic, :id_changed?
end
test "ignored columns are not present in columns_hash" do
cache_columns = Developer.connection.schema_cache.columns_hash(Developer.table_name)
assert_includes cache_columns.keys, "first_name"
assert_not_includes Developer.columns_hash.keys, "first_name"
+ assert_not_includes SubDeveloper.columns_hash.keys, "first_name"
+ assert_not_includes SymbolIgnoredDeveloper.columns_hash.keys, "first_name"
end
test "ignored columns have no attribute methods" do
- refute Developer.new.respond_to?(:first_name)
- refute Developer.new.respond_to?(:first_name=)
- refute Developer.new.respond_to?(:first_name?)
+ assert_not_respond_to Developer.new, :first_name
+ assert_not_respond_to Developer.new, :first_name=
+ assert_not_respond_to Developer.new, :first_name?
+ assert_not_respond_to SubDeveloper.new, :first_name
+ assert_not_respond_to SubDeveloper.new, :first_name=
+ assert_not_respond_to SubDeveloper.new, :first_name?
+ assert_not_respond_to SymbolIgnoredDeveloper.new, :first_name
+ assert_not_respond_to SymbolIgnoredDeveloper.new, :first_name=
+ assert_not_respond_to SymbolIgnoredDeveloper.new, :first_name?
end
test "ignored columns don't prevent explicit declaration of attribute methods" do
- assert Developer.new.respond_to?(:last_name)
- assert Developer.new.respond_to?(:last_name=)
- assert Developer.new.respond_to?(:last_name?)
+ assert_respond_to Developer.new, :last_name
+ assert_respond_to Developer.new, :last_name=
+ assert_respond_to Developer.new, :last_name?
+ assert_respond_to SubDeveloper.new, :last_name
+ assert_respond_to SubDeveloper.new, :last_name=
+ assert_respond_to SubDeveloper.new, :last_name?
+ assert_respond_to SymbolIgnoredDeveloper.new, :last_name
+ assert_respond_to SymbolIgnoredDeveloper.new, :last_name=
+ assert_respond_to SymbolIgnoredDeveloper.new, :last_name?
+ end
+
+ test "ignored columns are stored as an array of string" do
+ assert_equal(%w(first_name last_name), Developer.ignored_columns)
+ assert_equal(%w(first_name last_name), SymbolIgnoredDeveloper.ignored_columns)
+ end
+
+ test "when #reload called, ignored columns' attribute methods are not defined" do
+ developer = Developer.create!(name: "Developer")
+ assert_not_respond_to developer, :first_name
+ assert_not_respond_to developer, :first_name=
+
+ developer.reload
+
+ assert_not_respond_to developer, :first_name
+ assert_not_respond_to developer, :first_name=
+ end
+
+ test "ignored columns not included in SELECT" do
+ query = Developer.all.to_sql.downcase
+
+ # ignored column
+ assert_not query.include?("first_name")
+
+ # regular column
+ assert query.include?("name")
+ end
+
+ test "column names are quoted when using #from clause and model has ignored columns" do
+ refute_empty Developer.ignored_columns
+ query = Developer.from("developers").to_sql
+ quoted_id = "#{Developer.quoted_table_name}.#{Developer.quoted_primary_key}"
+
+ assert_match(/SELECT #{quoted_id}.* FROM developers/, query)
+ end
+
+ test "using table name qualified column names unless having SELECT list explicitly" do
+ assert_equal developers(:david), Developer.from("developers").joins(:shared_computers).take
+ end
+
+ test "protected environments by default is an array with production" do
+ assert_equal ["production"], ActiveRecord::Base.protected_environments
+ end
+
+ def test_protected_environments_are_stored_as_an_array_of_string
+ previous_protected_environments = ActiveRecord::Base.protected_environments
+ ActiveRecord::Base.protected_environments = [:staging, "production"]
+ assert_equal ["staging", "production"], ActiveRecord::Base.protected_environments
+ ensure
+ ActiveRecord::Base.protected_environments = previous_protected_environments
end
end
diff --git a/activerecord/test/cases/batches_test.rb b/activerecord/test/cases/batches_test.rb
index fbc3fbb44f..ad701fb200 100644
--- a/activerecord/test/cases/batches_test.rb
+++ b/activerecord/test/cases/batches_test.rb
@@ -1,4 +1,7 @@
+# frozen_string_literal: true
+
require "cases/helper"
+require "models/comment"
require "models/post"
require "models/subscriber"
@@ -152,7 +155,7 @@ class EachTest < ActiveRecord::TestCase
end
def test_find_in_batches_should_not_use_records_after_yielding_them_in_case_original_array_is_modified
- not_a_post = "not a post"
+ not_a_post = "not a post".dup
def not_a_post.id; end
not_a_post.stub(:id, -> { raise StandardError.new("not_a_post had #id called on it") }) do
assert_nothing_raised do
@@ -310,7 +313,7 @@ class EachTest < ActiveRecord::TestCase
def test_in_batches_each_record_should_yield_record_if_block_is_given
assert_queries(6) do
Post.in_batches(of: 2).each_record do |post|
- assert post.title.present?
+ assert_predicate post.title, :present?
assert_kind_of Post, post
end
end
@@ -319,7 +322,7 @@ class EachTest < ActiveRecord::TestCase
def test_in_batches_each_record_should_return_enumerator_if_no_block_given
assert_queries(6) do
Post.in_batches(of: 2).each_record.with_index do |post, i|
- assert post.title.present?
+ assert_predicate post.title, :present?
assert_kind_of Post, post
end
end
@@ -350,24 +353,24 @@ class EachTest < ActiveRecord::TestCase
def test_in_batches_should_not_be_loaded
Post.in_batches(of: 1) do |relation|
- assert_not relation.loaded?
+ assert_not_predicate relation, :loaded?
end
Post.in_batches(of: 1, load: false) do |relation|
- assert_not relation.loaded?
+ assert_not_predicate relation, :loaded?
end
end
def test_in_batches_should_be_loaded
Post.in_batches(of: 1, load: true) do |relation|
- assert relation.loaded?
+ assert_predicate relation, :loaded?
end
end
def test_in_batches_if_not_loaded_executes_more_queries
assert_queries(@total + 1) do
Post.in_batches(of: 1, load: false) do |relation|
- assert_not relation.loaded?
+ assert_not_predicate relation, :loaded?
end
end
end
@@ -417,7 +420,7 @@ class EachTest < ActiveRecord::TestCase
end
def test_in_batches_should_not_use_records_after_yielding_them_in_case_original_array_is_modified
- not_a_post = "not a post"
+ not_a_post = "not a post".dup
def not_a_post.id
raise StandardError.new("not_a_post had #id called on it")
end
@@ -583,31 +586,78 @@ class EachTest < ActiveRecord::TestCase
end
end
- test ".error_on_ignored_order_or_limit= is deprecated" do
- begin
- prev = ActiveRecord::Base.error_on_ignored_order
- assert_deprecated "Please use error_on_ignored_order= instead." do
- ActiveRecord::Base.error_on_ignored_order_or_limit = true
+ test ".find_each respects table alias" do
+ assert_queries(1) do
+ table_alias = Post.arel_table.alias("omg_posts")
+ table_metadata = ActiveRecord::TableMetadata.new(Post, table_alias)
+ predicate_builder = ActiveRecord::PredicateBuilder.new(table_metadata)
+
+ posts = ActiveRecord::Relation.create(
+ Post,
+ table: table_alias,
+ predicate_builder: predicate_builder
+ )
+ posts.find_each {}
+ end
+ end
+
+ test ".find_each bypasses the query cache for its own queries" do
+ Post.cache do
+ assert_queries(2) do
+ Post.find_each {}
+ Post.find_each {}
end
- assert ActiveRecord::Base.error_on_ignored_order
- ensure
- ActiveRecord::Base.error_on_ignored_order = prev
end
end
- test ".error_on_ignored_order_or_limit is deprecated" do
- expected = ActiveRecord::Base.error_on_ignored_order
- actual = assert_deprecated "Please use error_on_ignored_order instead." do
- ActiveRecord::Base.error_on_ignored_order_or_limit
+ test ".find_each does not disable the query cache inside the given block" do
+ Post.cache do
+ Post.find_each(start: 1, finish: 1) do |post|
+ assert_queries(1) do
+ post.comments.count
+ post.comments.count
+ end
+ end
+ end
+ end
+
+ test ".find_in_batches bypasses the query cache for its own queries" do
+ Post.cache do
+ assert_queries(2) do
+ Post.find_in_batches {}
+ Post.find_in_batches {}
+ end
end
- assert_equal expected, actual
end
- test "#error_on_ignored_order_or_limit is deprecated" do
- expected = ActiveRecord::Base.error_on_ignored_order
- actual = assert_deprecated "Please use error_on_ignored_order instead." do
- Post.new.error_on_ignored_order_or_limit
+ test ".find_in_batches does not disable the query cache inside the given block" do
+ Post.cache do
+ Post.find_in_batches(start: 1, finish: 1) do |batch|
+ assert_queries(1) do
+ batch.first.comments.count
+ batch.first.comments.count
+ end
+ end
+ end
+ end
+
+ test ".in_batches bypasses the query cache for its own queries" do
+ Post.cache do
+ assert_queries(2) do
+ Post.in_batches {}
+ Post.in_batches {}
+ end
+ end
+ end
+
+ test ".in_batches does not disable the query cache inside the given block" do
+ Post.cache do
+ Post.in_batches(start: 1, finish: 1) do |relation|
+ assert_queries(1) do
+ relation.count
+ relation.count
+ end
+ end
end
- assert_equal expected, actual
end
end
diff --git a/activerecord/test/cases/binary_test.rb b/activerecord/test/cases/binary_test.rb
index 1fc30e24d2..d5376ece69 100644
--- a/activerecord/test/cases/binary_test.rb
+++ b/activerecord/test/cases/binary_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "cases/helper"
# Without using prepared statements, it makes no sense to test
@@ -10,7 +12,7 @@ unless current_adapter?(:DB2Adapter)
FIXTURES = %w(flowers.jpg example.log test.txt)
def test_mixed_encoding
- str = "\x80"
+ str = "\x80".dup
str.force_encoding("ASCII-8BIT")
binary = Binary.new name: "いただきます!", data: str
diff --git a/activerecord/test/cases/bind_parameter_test.rb b/activerecord/test/cases/bind_parameter_test.rb
index 5af44c27eb..91cc49385c 100644
--- a/activerecord/test/cases/bind_parameter_test.rb
+++ b/activerecord/test/cases/bind_parameter_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "cases/helper"
require "models/topic"
require "models/author"
@@ -39,8 +41,9 @@ if ActiveRecord::Base.connection.prepared_statements
end
def test_binds_are_logged
- binds = [bind_attribute("id", 1)]
- sql = "select * from topics where id = #{bind_param.to_sql}"
+ sub = Arel::Nodes::BindParam.new(1)
+ binds = [Relation::QueryAttribute.new("id", 1, Type::Value.new)]
+ sql = "select * from topics where id = #{sub.to_sql}"
@connection.exec_query(sql, "SQL", binds)
@@ -55,7 +58,7 @@ if ActiveRecord::Base.connection.prepared_statements
end
def test_logs_binds_after_type_cast
- binds = [bind_attribute("id", "10", Type::Integer.new)]
+ binds = [Relation::QueryAttribute.new("id", "10", Type::Integer.new)]
assert_logs_binds(binds)
end
diff --git a/activerecord/test/cases/cache_key_test.rb b/activerecord/test/cases/cache_key_test.rb
index 7b8264e6e8..3a569f226e 100644
--- a/activerecord/test/cases/cache_key_test.rb
+++ b/activerecord/test/cases/cache_key_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "cases/helper"
module ActiveRecord
@@ -36,8 +38,8 @@ module ActiveRecord
end
test "cache_version is only there when versioning is on" do
- assert CacheMeWithVersion.create.cache_version.present?
- assert_not CacheMe.create.cache_version.present?
+ assert_predicate CacheMeWithVersion.create.cache_version, :present?
+ assert_not_predicate CacheMe.create.cache_version, :present?
end
test "cache_key_with_version always has both key and version" do
diff --git a/activerecord/test/cases/calculations_test.rb b/activerecord/test/cases/calculations_test.rb
index 80baaac30a..82b15e565b 100644
--- a/activerecord/test/cases/calculations_test.rb
+++ b/activerecord/test/cases/calculations_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "cases/helper"
require "models/book"
require "models/club"
@@ -19,7 +21,7 @@ require "models/comment"
require "models/rating"
class CalculationsTest < ActiveRecord::TestCase
- fixtures :companies, :accounts, :topics, :speedometers, :minivans, :books
+ fixtures :companies, :accounts, :topics, :speedometers, :minivans, :books, :posts, :comments
def test_should_sum_field
assert_equal 318, Account.sum(:credit_limit)
@@ -234,6 +236,46 @@ class CalculationsTest < ActiveRecord::TestCase
end
end
+ def test_count_with_eager_loading_and_custom_order
+ posts = Post.includes(:comments).order("comments.id")
+ assert_queries(1) { assert_equal 11, posts.count }
+ assert_queries(1) { assert_equal 11, posts.count(:all) }
+ end
+
+ def test_distinct_count_all_with_custom_select_and_order
+ accounts = Account.distinct.select("credit_limit % 10").order(Arel.sql("credit_limit % 10"))
+ assert_queries(1) { assert_equal 3, accounts.count(:all) }
+ assert_queries(1) { assert_equal 3, accounts.load.size }
+ end
+
+ def test_distinct_count_with_order_and_limit
+ assert_equal 4, Account.distinct.order(:firm_id).limit(4).count
+ end
+
+ def test_distinct_count_with_order_and_offset
+ assert_equal 4, Account.distinct.order(:firm_id).offset(2).count
+ end
+
+ def test_distinct_count_with_order_and_limit_and_offset
+ assert_equal 4, Account.distinct.order(:firm_id).limit(4).offset(2).count
+ end
+
+ def test_distinct_joins_count_with_order_and_limit
+ assert_equal 3, Account.joins(:firm).distinct.order(:firm_id).limit(3).count
+ end
+
+ def test_distinct_joins_count_with_order_and_offset
+ assert_equal 3, Account.joins(:firm).distinct.order(:firm_id).offset(2).count
+ end
+
+ def test_distinct_joins_count_with_order_and_limit_and_offset
+ assert_equal 3, Account.joins(:firm).distinct.order(:firm_id).limit(3).offset(2).count
+ end
+
+ def test_distinct_count_with_group_by_and_order_and_limit
+ assert_equal({ 6 => 2 }, Account.group(:firm_id).distinct.order("1 DESC").limit(1).count)
+ end
+
def test_should_group_by_summed_field_having_condition
c = Account.group(:firm_id).having("sum(credit_limit) > 50").sum(:credit_limit)
assert_nil c[1]
@@ -492,8 +534,7 @@ class CalculationsTest < ActiveRecord::TestCase
end
def test_should_sum_expression
- # Oracle adapter returns floating point value 636.0 after SUM
- if current_adapter?(:OracleAdapter)
+ if current_adapter?(:SQLite3Adapter, :Mysql2Adapter, :PostgreSQLAdapter, :OracleAdapter)
assert_equal 636, Account.sum("2 * credit_limit")
else
assert_equal 636, Account.sum("2 * credit_limit").to_i
@@ -634,14 +675,14 @@ class CalculationsTest < ActiveRecord::TestCase
end
def test_pluck_with_selection_clause
- assert_equal [50, 53, 55, 60], Account.pluck("DISTINCT credit_limit").sort
- assert_equal [50, 53, 55, 60], Account.pluck("DISTINCT accounts.credit_limit").sort
- assert_equal [50, 53, 55, 60], Account.pluck("DISTINCT(credit_limit)").sort
+ assert_equal [50, 53, 55, 60], Account.pluck(Arel.sql("DISTINCT credit_limit")).sort
+ assert_equal [50, 53, 55, 60], Account.pluck(Arel.sql("DISTINCT accounts.credit_limit")).sort
+ assert_equal [50, 53, 55, 60], Account.pluck(Arel.sql("DISTINCT(credit_limit)")).sort
# MySQL returns "SUM(DISTINCT(credit_limit))" as the column name unless
# an alias is provided. Without the alias, the column cannot be found
# and properly typecast.
- assert_equal [50 + 53 + 55 + 60], Account.pluck("SUM(DISTINCT(credit_limit)) as credit_limit")
+ assert_equal [50 + 53 + 55 + 60], Account.pluck(Arel.sql("SUM(DISTINCT(credit_limit)) as credit_limit"))
end
def test_plucks_with_ids
@@ -653,6 +694,11 @@ class CalculationsTest < ActiveRecord::TestCase
assert_equal [], Topic.includes(:replies).limit(1).where("0 = 1").pluck(:id)
end
+ def test_pluck_with_includes_offset
+ assert_equal [5], Topic.includes(:replies).order(:id).offset(4).pluck(:id)
+ assert_equal [], Topic.includes(:replies).order(:id).offset(5).pluck(:id)
+ end
+
def test_pluck_not_auto_table_name_prefix_if_column_included
Company.create!(name: "test", contracts: [Contract.new(developer_id: 7)])
ids = Company.includes(:contracts).pluck(:developer_id)
@@ -721,23 +767,29 @@ class CalculationsTest < ActiveRecord::TestCase
end
def test_pluck_loaded_relation
+ Company.attribute_names # Load schema information so we don't query below
companies = Company.order(:id).limit(3).load
+
assert_no_queries do
assert_equal ["37signals", "Summit", "Microsoft"], companies.pluck(:name)
end
end
def test_pluck_loaded_relation_multiple_columns
+ Company.attribute_names # Load schema information so we don't query below
companies = Company.order(:id).limit(3).load
+
assert_no_queries do
assert_equal [[1, "37signals"], [2, "Summit"], [3, "Microsoft"]], companies.pluck(:id, :name)
end
end
def test_pluck_loaded_relation_sql_fragment
+ Company.attribute_names # Load schema information so we don't query below
companies = Company.order(:name).limit(3).load
+
assert_queries 1 do
- assert_equal ["37signals", "Apex", "Ex Nihilo"], companies.pluck("DISTINCT name")
+ assert_equal ["37signals", "Apex", "Ex Nihilo"], companies.pluck(Arel.sql("DISTINCT name"))
end
end
@@ -817,4 +869,46 @@ class CalculationsTest < ActiveRecord::TestCase
assert_equal 6, Account.sum(:firm_id) { 1 }
end
end
+
+ test "#skip_query_cache! for #pluck" do
+ Account.cache do
+ assert_queries(1) do
+ Account.pluck(:credit_limit)
+ Account.pluck(:credit_limit)
+ end
+
+ assert_queries(2) do
+ Account.all.skip_query_cache!.pluck(:credit_limit)
+ Account.all.skip_query_cache!.pluck(:credit_limit)
+ end
+ end
+ end
+
+ test "#skip_query_cache! for a simple calculation" do
+ Account.cache do
+ assert_queries(1) do
+ Account.calculate(:sum, :credit_limit)
+ Account.calculate(:sum, :credit_limit)
+ end
+
+ assert_queries(2) do
+ Account.all.skip_query_cache!.calculate(:sum, :credit_limit)
+ Account.all.skip_query_cache!.calculate(:sum, :credit_limit)
+ end
+ end
+ end
+
+ test "#skip_query_cache! for a grouped calculation" do
+ Account.cache do
+ assert_queries(1) do
+ Account.group(:firm_id).calculate(:sum, :credit_limit)
+ Account.group(:firm_id).calculate(:sum, :credit_limit)
+ end
+
+ assert_queries(2) do
+ Account.all.skip_query_cache!.group(:firm_id).calculate(:sum, :credit_limit)
+ Account.all.skip_query_cache!.group(:firm_id).calculate(:sum, :credit_limit)
+ end
+ end
+ end
end
diff --git a/activerecord/test/cases/callbacks_test.rb b/activerecord/test/cases/callbacks_test.rb
index b3c86586d0..3b283a3aa6 100644
--- a/activerecord/test/cases/callbacks_test.rb
+++ b/activerecord/test/cases/callbacks_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "cases/helper"
require "models/developer"
require "models/computer"
@@ -392,27 +394,27 @@ class CallbacksTest < ActiveRecord::TestCase
def test_before_create_throwing_abort
someone = CallbackHaltedDeveloper.new
someone.cancel_before_create = true
- assert someone.valid?
+ assert_predicate someone, :valid?
assert !someone.save
assert_save_callbacks_not_called(someone)
end
def test_before_save_throwing_abort
david = DeveloperWithCanceledCallbacks.find(1)
- assert david.valid?
+ assert_predicate david, :valid?
assert !david.save
exc = assert_raise(ActiveRecord::RecordNotSaved) { david.save! }
assert_equal david, exc.record
david = DeveloperWithCanceledCallbacks.find(1)
david.salary = 10_000_000
- assert !david.valid?
+ assert_not_predicate david, :valid?
assert !david.save
assert_raise(ActiveRecord::RecordInvalid) { david.save! }
someone = CallbackHaltedDeveloper.find(1)
someone.cancel_before_save = true
- assert someone.valid?
+ assert_predicate someone, :valid?
assert !someone.save
assert_save_callbacks_not_called(someone)
end
@@ -420,7 +422,7 @@ class CallbacksTest < ActiveRecord::TestCase
def test_before_update_throwing_abort
someone = CallbackHaltedDeveloper.find(1)
someone.cancel_before_update = true
- assert someone.valid?
+ assert_predicate someone, :valid?
assert !someone.save
assert_save_callbacks_not_called(someone)
end
diff --git a/activerecord/test/cases/clone_test.rb b/activerecord/test/cases/clone_test.rb
index b89294c094..65e5016040 100644
--- a/activerecord/test/cases/clone_test.rb
+++ b/activerecord/test/cases/clone_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "cases/helper"
require "models/topic"
@@ -34,7 +36,7 @@ module ActiveRecord
cloned = Topic.new
clone = cloned.clone
cloned.freeze
- assert_not clone.frozen?
+ assert_not_predicate clone, :frozen?
end
end
end
diff --git a/activerecord/test/cases/coders/json_test.rb b/activerecord/test/cases/coders/json_test.rb
index d22d93d129..e40d576b39 100644
--- a/activerecord/test/cases/coders/json_test.rb
+++ b/activerecord/test/cases/coders/json_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "cases/helper"
module ActiveRecord
diff --git a/activerecord/test/cases/coders/yaml_column_test.rb b/activerecord/test/cases/coders/yaml_column_test.rb
index a26a72712d..4a5559c62f 100644
--- a/activerecord/test/cases/coders/yaml_column_test.rb
+++ b/activerecord/test/cases/coders/yaml_column_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "cases/helper"
module ActiveRecord
diff --git a/activerecord/test/cases/collection_cache_key_test.rb b/activerecord/test/cases/collection_cache_key_test.rb
index f344c77691..a5d908344a 100644
--- a/activerecord/test/cases/collection_cache_key_test.rb
+++ b/activerecord/test/cases/collection_cache_key_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "cases/helper"
require "models/computer"
require "models/developer"
@@ -22,7 +24,7 @@ module ActiveRecord
/\Adevelopers\/query-(\h+)-(\d+)-(\d+)\z/ =~ developers.cache_key
- assert_equal Digest::MD5.hexdigest(developers.to_sql), $1
+ assert_equal ActiveSupport::Digest.hexdigest(developers.to_sql), $1
assert_equal developers.count.to_s, $2
assert_equal last_developer_timestamp.to_s(ActiveRecord::Base.cache_timestamp_format), $3
end
@@ -35,7 +37,7 @@ module ActiveRecord
/\Adevelopers\/query-(\h+)-(\d+)-(\d+)\z/ =~ developers.cache_key
- assert_equal Digest::MD5.hexdigest(developers.to_sql), $1
+ assert_equal ActiveSupport::Digest.hexdigest(developers.to_sql), $1
assert_equal developers.count.to_s, $2
assert_equal last_developer_timestamp.to_s(ActiveRecord::Base.cache_timestamp_format), $3
end
@@ -48,11 +50,43 @@ module ActiveRecord
/\Adevelopers\/query-(\h+)-(\d+)-(\d+)\z/ =~ developers.cache_key
- assert_equal Digest::MD5.hexdigest(developers.to_sql), $1
+ assert_equal ActiveSupport::Digest.hexdigest(developers.to_sql), $1
+ assert_equal developers.count.to_s, $2
+ assert_equal last_developer_timestamp.to_s(ActiveRecord::Base.cache_timestamp_format), $3
+ end
+
+ test "cache_key for relation with table alias" do
+ table_alias = Developer.arel_table.alias("omg_developers")
+ table_metadata = ActiveRecord::TableMetadata.new(Developer, table_alias)
+ predicate_builder = ActiveRecord::PredicateBuilder.new(table_metadata)
+
+ developers = ActiveRecord::Relation.create(
+ Developer,
+ table: table_alias,
+ predicate_builder: predicate_builder
+ )
+ developers = developers.where(salary: 100000).order(updated_at: :desc)
+ last_developer_timestamp = developers.first.updated_at
+
+ assert_match(/\Adevelopers\/query-(\h+)-(\d+)-(\d+)\z/, developers.cache_key)
+
+ /\Adevelopers\/query-(\h+)-(\d+)-(\d+)\z/ =~ developers.cache_key
+
+ assert_equal ActiveSupport::Digest.hexdigest(developers.to_sql), $1
assert_equal developers.count.to_s, $2
assert_equal last_developer_timestamp.to_s(ActiveRecord::Base.cache_timestamp_format), $3
end
+ test "cache_key for relation with includes" do
+ comments = Comment.includes(:post).where("posts.type": "Post")
+ assert_match(/\Acomments\/query-(\h+)-(\d+)-(\d+)\z/, comments.cache_key)
+ end
+
+ test "cache_key for loaded relation with includes" do
+ comments = Comment.includes(:post).where("posts.type": "Post").load
+ assert_match(/\Acomments\/query-(\h+)-(\d+)-(\d+)\z/, comments.cache_key)
+ end
+
test "it triggers at most one query" do
developers = Developer.where(name: "David")
@@ -111,5 +145,17 @@ module ActiveRecord
assert_match(/\Adevelopers\/query-(\h+)-(\d+)-(\d+)\z/, developers.cache_key)
end
+
+ test "cache_key with a relation having distinct and order" do
+ developers = Developer.distinct.order(:salary).limit(5)
+
+ assert_match(/\Adevelopers\/query-(\h+)-(\d+)-(\d+)\z/, developers.cache_key)
+ end
+
+ test "cache_key with a relation having custom select and order" do
+ developers = Developer.select("name AS dev_name").order("dev_name DESC").limit(5)
+
+ assert_match(/\Adevelopers\/query-(\h+)-(\d+)-(\d+)\z/, developers.cache_key)
+ end
end
end
diff --git a/activerecord/test/cases/column_alias_test.rb b/activerecord/test/cases/column_alias_test.rb
index 9893ba9580..a883d21fb8 100644
--- a/activerecord/test/cases/column_alias_test.rb
+++ b/activerecord/test/cases/column_alias_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "cases/helper"
require "models/topic"
diff --git a/activerecord/test/cases/column_definition_test.rb b/activerecord/test/cases/column_definition_test.rb
index 90c8d21c43..cbd2b44589 100644
--- a/activerecord/test/cases/column_definition_test.rb
+++ b/activerecord/test/cases/column_definition_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "cases/helper"
module ActiveRecord
diff --git a/activerecord/test/cases/comment_test.rb b/activerecord/test/cases/comment_test.rb
index c23be52a6c..584e03d196 100644
--- a/activerecord/test/cases/comment_test.rb
+++ b/activerecord/test/cases/comment_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "cases/helper"
require "support/schema_dumping_helper"
@@ -109,12 +111,14 @@ if ActiveRecord::Base.connection.supports_comments?
# And check that these changes are reflected in dump
output = dump_table_schema "commenteds"
- assert_match %r[create_table "commenteds",.+\s+comment: "A table with comment"], output
+ assert_match %r[create_table "commenteds",.*\s+comment: "A table with comment"], output
assert_match %r[t\.string\s+"name",\s+comment: "Comment should help clarify the column purpose"], output
assert_match %r[t\.string\s+"obvious"\n], output
assert_match %r[t\.string\s+"content",\s+comment: "Whoa, content describes itself!"], output
- assert_match %r[t\.integer\s+"rating",\s+comment: "I am running out of imagination"], output
- unless current_adapter?(:OracleAdapter)
+ if current_adapter?(:OracleAdapter)
+ assert_match %r[t\.integer\s+"rating",\s+precision: 38,\s+comment: "I am running out of imagination"], output
+ else
+ assert_match %r[t\.integer\s+"rating",\s+comment: "I am running out of imagination"], output
assert_match %r[t\.index\s+.+\s+comment: "\\\"Very important\\\" index that powers all the performance.\\nAnd it's fun!"], output
assert_match %r[t\.index\s+.+\s+name: "idx_obvious",\s+comment: "We need to see obvious comments"], output
end
@@ -138,5 +142,27 @@ if ActiveRecord::Base.connection.supports_comments?
assert_match %r[t\.string\s+"absent_comment"\n], output
assert_no_match %r[t\.string\s+"absent_comment", comment:\n], output
end
+
+ def test_change_table_comment
+ @connection.change_table_comment :commenteds, "Edited table comment"
+ assert_equal "Edited table comment", @connection.table_comment("commenteds")
+ end
+
+ def test_change_table_comment_to_nil
+ @connection.change_table_comment :commenteds, nil
+ assert_nil @connection.table_comment("commenteds")
+ end
+
+ def test_change_column_comment
+ @connection.change_column_comment :commenteds, :name, "Edited column comment"
+ column = Commented.columns_hash["name"]
+ assert_equal "Edited column comment", column.comment
+ end
+
+ def test_change_column_comment_to_nil
+ @connection.change_column_comment :commenteds, :name, nil
+ column = Commented.columns_hash["name"]
+ assert_nil column.comment
+ end
end
end
diff --git a/activerecord/test/cases/connection_adapters/adapter_leasing_test.rb b/activerecord/test/cases/connection_adapters/adapter_leasing_test.rb
index 64189381cb..72838ff56b 100644
--- a/activerecord/test/cases/connection_adapters/adapter_leasing_test.rb
+++ b/activerecord/test/cases/connection_adapters/adapter_leasing_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "cases/helper"
module ActiveRecord
@@ -43,11 +45,11 @@ module ActiveRecord
# Make sure the pool marks the connection in use
assert_equal @adapter, pool.connection
- assert @adapter.in_use?
+ assert_predicate @adapter, :in_use?
# Close should put the adapter back in the pool
@adapter.close
- assert_not @adapter.in_use?
+ assert_not_predicate @adapter, :in_use?
assert_equal @adapter, pool.connection
end
diff --git a/activerecord/test/cases/connection_adapters/connection_handler_test.rb b/activerecord/test/cases/connection_adapters/connection_handler_test.rb
index 2a71f08d90..f4cc251fb9 100644
--- a/activerecord/test/cases/connection_adapters/connection_handler_test.rb
+++ b/activerecord/test/cases/connection_adapters/connection_handler_test.rb
@@ -1,8 +1,15 @@
+# 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"
@@ -96,11 +103,11 @@ module ActiveRecord
end
def test_active_connections?
- assert !@handler.active_connections?
+ assert_not_predicate @handler, :active_connections?
assert @handler.retrieve_connection(@spec_name)
- assert @handler.active_connections?
+ assert_predicate @handler, :active_connections?
@handler.clear_active_connections!
- assert !@handler.active_connections?
+ assert_not_predicate @handler, :active_connections?
end
def test_retrieve_connection_pool
@@ -137,6 +144,75 @@ module ActiveRecord
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")
@@ -198,15 +274,15 @@ module ActiveRecord
def test_a_class_using_custom_pool_and_switching_back_to_primary
klass2 = Class.new(Base) { def self.name; "klass2"; end }
- assert_equal klass2.connection.object_id, ActiveRecord::Base.connection.object_id
+ assert_same klass2.connection, ActiveRecord::Base.connection
pool = klass2.establish_connection(ActiveRecord::Base.connection_pool.spec.config)
- assert_equal klass2.connection.object_id, pool.connection.object_id
- refute_equal klass2.connection.object_id, ActiveRecord::Base.connection.object_id
+ assert_same klass2.connection, pool.connection
+ refute_same klass2.connection, ActiveRecord::Base.connection
klass2.remove_connection
- assert_equal klass2.connection.object_id, ActiveRecord::Base.connection.object_id
+ assert_same klass2.connection, ActiveRecord::Base.connection
end
def test_connection_specification_name_should_fallback_to_parent
@@ -221,8 +297,8 @@ module ActiveRecord
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.object_id
- assert_equal klass2.connection.object_id, ActiveRecord::Base.connection.object_id
+ refute_nil ActiveRecord::Base.connection
+ assert_same klass2.connection, ActiveRecord::Base.connection
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
index 10a3521c79..f81b73c344 100644
--- a/activerecord/test/cases/connection_adapters/connection_specification_test.rb
+++ b/activerecord/test/cases/connection_adapters/connection_specification_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "cases/helper"
module ActiveRecord
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
index 8faa67255d..1b64324cc4 100644
--- 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
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "cases/helper"
module ActiveRecord
diff --git a/activerecord/test/cases/connection_adapters/mysql_type_lookup_test.rb b/activerecord/test/cases/connection_adapters/mysql_type_lookup_test.rb
index 3657b8340d..02e76ce146 100644
--- a/activerecord/test/cases/connection_adapters/mysql_type_lookup_test.rb
+++ b/activerecord/test/cases/connection_adapters/mysql_type_lookup_test.rb
@@ -1,13 +1,22 @@
+# 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)"
@@ -47,7 +56,7 @@ if current_adapter?(:Mysql2Adapter)
private
def assert_lookup_type(type, lookup)
- cast_type = @connection.type_map.lookup(lookup)
+ cast_type = @connection.send(:type_map).lookup(lookup)
assert_equal type, cast_type.type
end
diff --git a/activerecord/test/cases/connection_adapters/schema_cache_test.rb b/activerecord/test/cases/connection_adapters/schema_cache_test.rb
index 106323ccc9..67496381d1 100644
--- a/activerecord/test/cases/connection_adapters/schema_cache_test.rb
+++ b/activerecord/test/cases/connection_adapters/schema_cache_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "cases/helper"
module ActiveRecord
@@ -20,8 +22,8 @@ module ActiveRecord
new_cache = YAML.load(YAML.dump(@cache))
assert_no_queries do
- assert_equal 11, new_cache.columns("posts").size
- assert_equal 11, new_cache.columns_hash("posts").size
+ 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
@@ -73,8 +75,8 @@ module ActiveRecord
@cache = Marshal.load(Marshal.dump(@cache))
assert_no_queries do
- assert_equal 11, @cache.columns("posts").size
- assert_equal 11, @cache.columns_hash("posts").size
+ 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
diff --git a/activerecord/test/cases/connection_adapters/type_lookup_test.rb b/activerecord/test/cases/connection_adapters/type_lookup_test.rb
index a348c2d783..1c79d776f0 100644
--- a/activerecord/test/cases/connection_adapters/type_lookup_test.rb
+++ b/activerecord/test/cases/connection_adapters/type_lookup_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "cases/helper"
unless current_adapter?(:PostgreSQLAdapter) # PostgreSQL does not use type strings for lookup
@@ -80,11 +82,11 @@ unless current_adapter?(:PostgreSQLAdapter) # PostgreSQL does not use type strin
end
def test_bigint_limit
- cast_type = @connection.type_map.lookup("bigint")
+ limit = @connection.send(:type_map).lookup("bigint").send(:_limit)
if current_adapter?(:OracleAdapter)
- assert_equal 19, cast_type.limit
+ assert_equal 19, limit
else
- assert_equal 8, cast_type.limit
+ assert_equal 8, limit
end
end
@@ -98,7 +100,7 @@ unless current_adapter?(:PostgreSQLAdapter) # PostgreSQL does not use type strin
{ 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.type_map.lookup(type)
+ cast_type = @connection.send(:type_map).lookup(type)
assert_equal expected_type, cast_type.type
assert_equal 2, cast_type.cast(2.1)
@@ -109,7 +111,7 @@ unless current_adapter?(:PostgreSQLAdapter) # PostgreSQL does not use type strin
private
def assert_lookup_type(type, lookup)
- cast_type = @connection.type_map.lookup(lookup)
+ cast_type = @connection.send(:type_map).lookup(lookup)
assert_equal type, cast_type.type
end
end
diff --git a/activerecord/test/cases/connection_management_test.rb b/activerecord/test/cases/connection_management_test.rb
index d1e946d401..0941ee3309 100644
--- a/activerecord/test/cases/connection_management_test.rb
+++ b/activerecord/test/cases/connection_management_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "cases/helper"
require "rack"
@@ -25,7 +27,7 @@ module ActiveRecord
# make sure we have an active connection
assert ActiveRecord::Base.connection
- assert ActiveRecord::Base.connection_handler.active_connections?
+ assert_predicate ActiveRecord::Base.connection_handler, :active_connections?
end
def test_app_delegation
@@ -45,14 +47,14 @@ module ActiveRecord
def test_connections_are_cleared_after_body_close
_, _, body = @management.call(@env)
body.close
- assert !ActiveRecord::Base.connection_handler.active_connections?
+ assert_not_predicate ActiveRecord::Base.connection_handler, :active_connections?
end
def test_active_connections_are_not_cleared_on_body_close_during_transaction
ActiveRecord::Base.transaction do
_, _, body = @management.call(@env)
body.close
- assert ActiveRecord::Base.connection_handler.active_connections?
+ assert_predicate ActiveRecord::Base.connection_handler, :active_connections?
end
end
@@ -60,7 +62,7 @@ module ActiveRecord
app = Class.new(App) { def call(env); raise NotImplementedError; end }.new
explosive = middleware(app)
assert_raises(NotImplementedError) { explosive.call(@env) }
- assert !ActiveRecord::Base.connection_handler.active_connections?
+ assert_not_predicate ActiveRecord::Base.connection_handler, :active_connections?
end
def test_connections_not_closed_if_exception_inside_transaction
@@ -68,14 +70,14 @@ module ActiveRecord
app = Class.new(App) { def call(env); raise RuntimeError; end }.new
explosive = middleware(app)
assert_raises(RuntimeError) { explosive.call(@env) }
- assert ActiveRecord::Base.connection_handler.active_connections?
+ assert_predicate ActiveRecord::Base.connection_handler, :active_connections?
end
end
test "doesn't clear active connections when running in a test case" do
executor.wrap do
@management.call(@env)
- assert ActiveRecord::Base.connection_handler.active_connections?
+ assert_predicate ActiveRecord::Base.connection_handler, :active_connections?
end
end
@@ -83,7 +85,7 @@ module ActiveRecord
body = Class.new(String) { def to_path; "/path"; end }.new
app = lambda { |_| [200, {}, body] }
response_body = middleware(app).call(@env)[2]
- assert response_body.respond_to?(:to_path)
+ assert_respond_to response_body, :to_path
assert_equal "/path", response_body.to_path
end
diff --git a/activerecord/test/cases/connection_pool_test.rb b/activerecord/test/cases/connection_pool_test.rb
index 00a0187b57..bfaaa3c54e 100644
--- a/activerecord/test/cases/connection_pool_test.rb
+++ b/activerecord/test/cases/connection_pool_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "cases/helper"
require "concurrent/atomic/count_down_latch"
@@ -33,12 +35,12 @@ module ActiveRecord
def test_checkout_after_close
connection = pool.connection
- assert connection.in_use?
+ assert_predicate connection, :in_use?
connection.close
- assert !connection.in_use?
+ assert_not_predicate connection, :in_use?
- assert pool.connection.in_use?
+ assert_predicate pool.connection, :in_use?
end
def test_released_connection_moves_between_threads
@@ -78,18 +80,18 @@ module ActiveRecord
end
def test_active_connection_in_use
- assert !pool.active_connection?
+ assert_not_predicate pool, :active_connection?
main_thread = pool.connection
- assert pool.active_connection?
+ assert_predicate pool, :active_connection?
main_thread.close
- assert !pool.active_connection?
+ assert_not_predicate pool, :active_connection?
end
def test_full_pool_exception
- @pool.size.times { @pool.checkout }
+ @pool.size.times { assert @pool.checkout }
assert_raises(ConnectionTimeoutError) do
@pool.checkout
end
@@ -154,13 +156,60 @@ module ActiveRecord
@pool.connections.each { |conn| conn.close if conn.in_use? }
end
+ def test_flush
+ idle_conn = @pool.checkout
+ recent_conn = @pool.checkout
+ active_conn = @pool.checkout
+
+ @pool.checkin idle_conn
+ @pool.checkin recent_conn
+
+ assert_equal 3, @pool.connections.length
+
+ def idle_conn.seconds_idle
+ 1000
+ end
+
+ @pool.flush(30)
+
+ assert_equal 2, @pool.connections.length
+
+ assert_equal [recent_conn, active_conn].sort_by(&:__id__), @pool.connections.sort_by(&:__id__)
+ ensure
+ @pool.checkin active_conn
+ end
+
+ def test_flush_bang
+ idle_conn = @pool.checkout
+ recent_conn = @pool.checkout
+ active_conn = @pool.checkout
+ _dead_conn = Thread.new { @pool.checkout }.join
+
+ @pool.checkin idle_conn
+ @pool.checkin recent_conn
+
+ assert_equal 4, @pool.connections.length
+
+ def idle_conn.seconds_idle
+ 1000
+ end
+
+ @pool.flush!
+
+ assert_equal 1, @pool.connections.length
+
+ assert_equal [active_conn].sort_by(&:__id__), @pool.connections.sort_by(&:__id__)
+ ensure
+ @pool.checkin active_conn
+ end
+
def test_remove_connection
conn = @pool.checkout
- assert conn.in_use?
+ assert_predicate conn, :in_use?
length = @pool.connections.length
@pool.remove conn
- assert conn.in_use?
+ assert_predicate conn, :in_use?
assert_equal(length - 1, @pool.connections.length)
ensure
conn.close
@@ -175,11 +224,11 @@ module ActiveRecord
end
def test_active_connection?
- assert !@pool.active_connection?
+ assert_not_predicate @pool, :active_connection?
assert @pool.connection
- assert @pool.active_connection?
+ assert_predicate @pool, :active_connection?
@pool.release_connection
- assert !@pool.active_connection?
+ assert_not_predicate @pool, :active_connection?
end
def test_checkout_behaviour
@@ -203,6 +252,14 @@ module ActiveRecord
end.join
end
+ def test_checkout_order_is_lifo
+ conn1 = @pool.checkout
+ conn2 = @pool.checkout
+ @pool.checkin conn1
+ @pool.checkin conn2
+ assert_equal [conn2, conn1], 2.times.map { @pool.checkout }
+ end
+
# The connection pool is "fair" if threads waiting for
# connections receive them in the order in which they began
# waiting. This ensures that we don't timeout one HTTP request
@@ -412,6 +469,7 @@ module ActiveRecord
end
def test_non_bang_disconnect_and_clear_reloadable_connections_throw_exception_if_threads_dont_return_their_conns
+ Thread.report_on_exception, original_report_on_exception = false, Thread.report_on_exception if Thread.respond_to?(:report_on_exception)
@pool.checkout_timeout = 0.001 # no need to delay test suite by waiting the whole full default timeout
[:disconnect, :clear_reloadable_connections].each do |group_action_method|
@pool.with_connection do |connection|
@@ -420,6 +478,8 @@ module ActiveRecord
end
end
end
+ ensure
+ Thread.report_on_exception = original_report_on_exception if Thread.respond_to?(:report_on_exception)
end
def test_disconnect_and_clear_reloadable_connections_attempt_to_wait_for_threads_to_return_their_conns
@@ -436,7 +496,7 @@ module ActiveRecord
assert_nil timed_join_result
# assert that since this is within default timeout our connection hasn't been forcefully taken away from us
- assert @pool.active_connection?
+ assert_predicate @pool, :active_connection?
end
ensure
thread.join if thread && !timed_join_result # clean up the other thread
@@ -450,7 +510,7 @@ module ActiveRecord
@pool.with_connection do |connection|
Thread.new { @pool.send(group_action_method) }.join
# assert connection has been forcefully taken away from us
- assert_not @pool.active_connection?
+ assert_not_predicate @pool, :active_connection?
# make a new connection for with_connection to clean up
@pool.connection
diff --git a/activerecord/test/cases/connection_specification/resolver_test.rb b/activerecord/test/cases/connection_specification/resolver_test.rb
index 13b5bae13c..5b80f16a44 100644
--- a/activerecord/test/cases/connection_specification/resolver_test.rb
+++ b/activerecord/test/cases/connection_specification/resolver_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "cases/helper"
module ActiveRecord
@@ -17,7 +19,7 @@ module ActiveRecord
spec "ridiculous://foo?encoding=utf8"
end
- assert_match "Could not load 'active_record/connection_adapters/ridiculous_adapter'", error.message
+ assert_match "Could not load the 'ridiculous' Active Record adapter. Ensure that the adapter is spelled correctly in config/database.yml and that you've added the necessary adapter gem to your Gemfile.", error.message
end
# The abstract adapter is used simply to bypass the bit of code that
diff --git a/activerecord/test/cases/core_test.rb b/activerecord/test/cases/core_test.rb
index 3735572898..356afdbd2b 100644
--- a/activerecord/test/cases/core_test.rb
+++ b/activerecord/test/cases/core_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "cases/helper"
require "models/person"
require "models/topic"
@@ -35,7 +37,7 @@ class CoreTest < ActiveRecord::TestCase
def test_pretty_print_new
topic = Topic.new
- actual = ""
+ actual = "".dup
PP.pp(topic, StringIO.new(actual))
expected = <<-PRETTY.strip_heredoc
#<Topic:0xXXXXXX
@@ -64,7 +66,7 @@ class CoreTest < ActiveRecord::TestCase
def test_pretty_print_persisted
topic = topics(:first)
- actual = ""
+ actual = "".dup
PP.pp(topic, StringIO.new(actual))
expected = <<-PRETTY.strip_heredoc
#<Topic:0x\\w+
@@ -92,7 +94,7 @@ class CoreTest < ActiveRecord::TestCase
def test_pretty_print_uninitialized
topic = Topic.allocate
- actual = ""
+ actual = "".dup
PP.pp(topic, StringIO.new(actual))
expected = "#<Topic:XXXXXX not initialized>\n"
assert actual.start_with?(expected.split("XXXXXX").first)
@@ -105,7 +107,7 @@ class CoreTest < ActiveRecord::TestCase
"inspecting topic"
end
end
- actual = ""
+ actual = "".dup
PP.pp(subtopic.new, StringIO.new(actual))
assert_equal "inspecting topic\n", actual
end
diff --git a/activerecord/test/cases/counter_cache_test.rb b/activerecord/test/cases/counter_cache_test.rb
index 46d7526cc0..e0948f90ac 100644
--- a/activerecord/test/cases/counter_cache_test.rb
+++ b/activerecord/test/cases/counter_cache_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "cases/helper"
require "models/topic"
require "models/car"
diff --git a/activerecord/test/cases/custom_locking_test.rb b/activerecord/test/cases/custom_locking_test.rb
index 15c8b684e4..f52b26e9ec 100644
--- a/activerecord/test/cases/custom_locking_test.rb
+++ b/activerecord/test/cases/custom_locking_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "cases/helper"
require "models/person"
diff --git a/activerecord/test/cases/database_statements_test.rb b/activerecord/test/cases/database_statements_test.rb
index 66035865be..1c934602ec 100644
--- a/activerecord/test/cases/database_statements_test.rb
+++ b/activerecord/test/cases/database_statements_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "cases/helper"
class DatabaseStatementsTest < ActiveRecord::TestCase
diff --git a/activerecord/test/cases/date_test.rb b/activerecord/test/cases/date_test.rb
index 2edc0415cd..9f412cdb63 100644
--- a/activerecord/test/cases/date_test.rb
+++ b/activerecord/test/cases/date_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "cases/helper"
require "models/topic"
diff --git a/activerecord/test/cases/date_time_precision_test.rb b/activerecord/test/cases/date_time_precision_test.rb
index e4a2f9ee17..51f6164138 100644
--- a/activerecord/test/cases/date_time_precision_test.rb
+++ b/activerecord/test/cases/date_time_precision_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "cases/helper"
require "support/schema_dumping_helper"
diff --git a/activerecord/test/cases/date_time_test.rb b/activerecord/test/cases/date_time_test.rb
index ad7da9de70..b5f35aff0e 100644
--- a/activerecord/test/cases/date_time_test.rb
+++ b/activerecord/test/cases/date_time_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "cases/helper"
require "models/topic"
require "models/task"
@@ -58,4 +60,17 @@ class DateTimeTest < ActiveRecord::TestCase
assert_equal now, task.starting
end
end
+
+ def test_date_time_with_string_value_with_subsecond_precision
+ skip unless subsecond_precision_supported?
+ string_value = "2017-07-04 14:19:00.5"
+ topic = Topic.create(written_on: string_value)
+ assert_equal topic, Topic.find_by(written_on: string_value)
+ end
+
+ def test_date_time_with_string_value_with_non_iso_format
+ string_value = "04/07/2017 2:19pm"
+ topic = Topic.create(written_on: string_value)
+ assert_equal topic, Topic.find_by(written_on: string_value)
+ end
end
diff --git a/activerecord/test/cases/defaults_test.rb b/activerecord/test/cases/defaults_test.rb
index 996d298689..3d11b573f1 100644
--- a/activerecord/test/cases/defaults_test.rb
+++ b/activerecord/test/cases/defaults_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "cases/helper"
require "support/schema_dumping_helper"
require "models/default"
@@ -51,7 +53,7 @@ class DefaultNumbersTest < ActiveRecord::TestCase
def test_default_decimal_number
record = DefaultNumber.new
- assert_equal BigDecimal.new("2.78"), record.decimal_number
+ assert_equal BigDecimal("2.78"), record.decimal_number
assert_equal "2.78", record.decimal_number_before_type_cast
end
end
diff --git a/activerecord/test/cases/dirty_test.rb b/activerecord/test/cases/dirty_test.rb
index f72e0d2ead..14c4e3cbd4 100644
--- a/activerecord/test/cases/dirty_test.rb
+++ b/activerecord/test/cases/dirty_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "cases/helper"
require "models/topic" # For booleans
require "models/pirate" # For timestamps
@@ -22,18 +24,18 @@ class DirtyTest < ActiveRecord::TestCase
# Change catchphrase.
pirate.catchphrase = "arrr"
- assert pirate.catchphrase_changed?
+ assert_predicate pirate, :catchphrase_changed?
assert_nil pirate.catchphrase_was
assert_equal [nil, "arrr"], pirate.catchphrase_change
# Saved - no changes.
pirate.save!
- assert !pirate.catchphrase_changed?
+ assert_not_predicate pirate, :catchphrase_changed?
assert_nil pirate.catchphrase_change
# Same value - no changes.
pirate.catchphrase = "arrr"
- assert !pirate.catchphrase_changed?
+ assert_not_predicate pirate, :catchphrase_changed?
assert_nil pirate.catchphrase_change
end
@@ -44,23 +46,23 @@ class DirtyTest < ActiveRecord::TestCase
# New record - no changes.
pirate = target.new
- assert !pirate.created_on_changed?
+ assert_not_predicate pirate, :created_on_changed?
assert_nil pirate.created_on_change
# Saved - no changes.
pirate.catchphrase = "arrrr, time zone!!"
pirate.save!
- assert !pirate.created_on_changed?
+ assert_not_predicate pirate, :created_on_changed?
assert_nil pirate.created_on_change
# Change created_on.
old_created_on = pirate.created_on
pirate.created_on = Time.now - 1.day
- assert pirate.created_on_changed?
+ assert_predicate pirate, :created_on_changed?
assert_kind_of ActiveSupport::TimeWithZone, pirate.created_on_was
assert_equal old_created_on, pirate.created_on_was
pirate.created_on = old_created_on
- assert !pirate.created_on_changed?
+ assert_not_predicate pirate, :created_on_changed?
end
end
@@ -71,7 +73,7 @@ class DirtyTest < ActiveRecord::TestCase
pirate = target.create!
pirate.created_on = pirate.created_on
- assert !pirate.created_on_changed?
+ assert_not_predicate pirate, :created_on_changed?
end
end
@@ -84,19 +86,19 @@ class DirtyTest < ActiveRecord::TestCase
# New record - no changes.
pirate = target.new
- assert !pirate.created_on_changed?
+ assert_not_predicate pirate, :created_on_changed?
assert_nil pirate.created_on_change
# Saved - no changes.
pirate.catchphrase = "arrrr, time zone!!"
pirate.save!
- assert !pirate.created_on_changed?
+ assert_not_predicate pirate, :created_on_changed?
assert_nil pirate.created_on_change
# Change created_on.
old_created_on = pirate.created_on
pirate.created_on = Time.now + 1.day
- assert pirate.created_on_changed?
+ assert_predicate pirate, :created_on_changed?
# kind_of does not work because
# ActiveSupport::TimeWithZone.name == 'Time'
assert_instance_of Time, pirate.created_on_was
@@ -111,19 +113,19 @@ class DirtyTest < ActiveRecord::TestCase
# New record - no changes.
pirate = target.new
- assert !pirate.created_on_changed?
+ assert_not_predicate pirate, :created_on_changed?
assert_nil pirate.created_on_change
# Saved - no changes.
pirate.catchphrase = "arrrr, time zone!!"
pirate.save!
- assert !pirate.created_on_changed?
+ assert_not_predicate pirate, :created_on_changed?
assert_nil pirate.created_on_change
# Change created_on.
old_created_on = pirate.created_on
pirate.created_on = Time.now + 1.day
- assert pirate.created_on_changed?
+ assert_predicate pirate, :created_on_changed?
# kind_of does not work because
# ActiveSupport::TimeWithZone.name == 'Time'
assert_instance_of Time, pirate.created_on_was
@@ -135,11 +137,11 @@ class DirtyTest < ActiveRecord::TestCase
# the actual attribute here is name, title is an
# alias setup via alias_attribute
parrot = Parrot.new
- assert !parrot.title_changed?
+ assert_not_predicate parrot, :title_changed?
assert_nil parrot.title_change
parrot.name = "Sam"
- assert parrot.title_changed?
+ assert_predicate parrot, :title_changed?
assert_nil parrot.title_was
assert_equal parrot.name_change, parrot.title_change
end
@@ -151,7 +153,7 @@ class DirtyTest < ActiveRecord::TestCase
pirate.restore_catchphrase!
assert_equal "Yar!", pirate.catchphrase
assert_equal Hash.new, pirate.changes
- assert !pirate.catchphrase_changed?
+ assert_not_predicate pirate, :catchphrase_changed?
end
def test_nullable_number_not_marked_as_changed_if_new_value_is_blank
@@ -159,7 +161,7 @@ class DirtyTest < ActiveRecord::TestCase
["", nil].each do |value|
pirate.parrot_id = value
- assert !pirate.parrot_id_changed?
+ assert_not_predicate pirate, :parrot_id_changed?
assert_nil pirate.parrot_id_change
end
end
@@ -169,7 +171,7 @@ class DirtyTest < ActiveRecord::TestCase
["", nil].each do |value|
numeric_data.bank_balance = value
- assert !numeric_data.bank_balance_changed?
+ assert_not_predicate numeric_data, :bank_balance_changed?
assert_nil numeric_data.bank_balance_change
end
end
@@ -179,7 +181,7 @@ class DirtyTest < ActiveRecord::TestCase
["", nil].each do |value|
numeric_data.temperature = value
- assert !numeric_data.temperature_changed?
+ assert_not_predicate numeric_data, :temperature_changed?
assert_nil numeric_data.temperature_change
end
end
@@ -195,7 +197,7 @@ class DirtyTest < ActiveRecord::TestCase
["", nil].each do |value|
topic.written_on = value
assert_nil topic.written_on
- assert !topic.written_on_changed?
+ assert_not_predicate topic, :written_on_changed?
end
end
end
@@ -206,10 +208,10 @@ class DirtyTest < ActiveRecord::TestCase
pirate.catchphrase = "arrr"
assert pirate.save!
- assert !pirate.changed?
+ assert_not_predicate pirate, :changed?
pirate.parrot_id = "0"
- assert !pirate.changed?
+ assert_not_predicate pirate, :changed?
end
def test_integer_zero_to_integer_zero_not_marked_as_changed
@@ -218,17 +220,17 @@ class DirtyTest < ActiveRecord::TestCase
pirate.catchphrase = "arrr"
assert pirate.save!
- assert !pirate.changed?
+ assert_not_predicate pirate, :changed?
pirate.parrot_id = 0
- assert !pirate.changed?
+ assert_not_predicate pirate, :changed?
end
def test_float_zero_to_string_zero_not_marked_as_changed
data = NumericData.new temperature: 0.0
data.save!
- assert_not data.changed?
+ assert_not_predicate data, :changed?
data.temperature = "0"
assert_empty data.changes
@@ -249,38 +251,38 @@ class DirtyTest < ActiveRecord::TestCase
# check the change from 1 to ''
pirate = Pirate.find_by_catchphrase("Yarrrr, me hearties")
pirate.parrot_id = ""
- assert pirate.parrot_id_changed?
+ assert_predicate pirate, :parrot_id_changed?
assert_equal([1, nil], pirate.parrot_id_change)
pirate.save
# check the change from nil to 0
pirate = Pirate.find_by_catchphrase("Yarrrr, me hearties")
pirate.parrot_id = 0
- assert pirate.parrot_id_changed?
+ assert_predicate pirate, :parrot_id_changed?
assert_equal([nil, 0], pirate.parrot_id_change)
pirate.save
# check the change from 0 to ''
pirate = Pirate.find_by_catchphrase("Yarrrr, me hearties")
pirate.parrot_id = ""
- assert pirate.parrot_id_changed?
+ assert_predicate pirate, :parrot_id_changed?
assert_equal([0, nil], pirate.parrot_id_change)
end
def test_object_should_be_changed_if_any_attribute_is_changed
pirate = Pirate.new
- assert !pirate.changed?
+ assert_not_predicate pirate, :changed?
assert_equal [], pirate.changed
assert_equal Hash.new, pirate.changes
pirate.catchphrase = "arrr"
- assert pirate.changed?
+ assert_predicate pirate, :changed?
assert_nil pirate.catchphrase_was
assert_equal %w(catchphrase), pirate.changed
assert_equal({ "catchphrase" => [nil, "arrr"] }, pirate.changes)
pirate.save
- assert !pirate.changed?
+ assert_not_predicate pirate, :changed?
assert_equal [], pirate.changed
assert_equal Hash.new, pirate.changes
end
@@ -288,42 +290,40 @@ class DirtyTest < ActiveRecord::TestCase
def test_attribute_will_change!
pirate = Pirate.create!(catchphrase: "arr")
- assert !pirate.catchphrase_changed?
+ assert_not_predicate pirate, :catchphrase_changed?
assert pirate.catchphrase_will_change!
- assert pirate.catchphrase_changed?
+ assert_predicate pirate, :catchphrase_changed?
assert_equal ["arr", "arr"], pirate.catchphrase_change
pirate.catchphrase << " matey!"
- assert pirate.catchphrase_changed?
+ assert_predicate pirate, :catchphrase_changed?
assert_equal ["arr", "arr matey!"], pirate.catchphrase_change
end
def test_virtual_attribute_will_change
- assert_deprecated do
- parrot = Parrot.create!(name: "Ruby")
- parrot.send(:attribute_will_change!, :cancel_save_from_callback)
- assert parrot.has_changes_to_save?
- end
+ parrot = Parrot.create!(name: "Ruby")
+ parrot.send(:attribute_will_change!, :cancel_save_from_callback)
+ assert_predicate parrot, :has_changes_to_save?
end
def test_association_assignment_changes_foreign_key
pirate = Pirate.create!(catchphrase: "jarl")
pirate.parrot = Parrot.create!(name: "Lorre")
- assert pirate.changed?
+ assert_predicate pirate, :changed?
assert_equal %w(parrot_id), pirate.changed
end
def test_attribute_should_be_compared_with_type_cast
topic = Topic.new
- assert topic.approved?
- assert !topic.approved_changed?
+ assert_predicate topic, :approved?
+ assert_not_predicate topic, :approved_changed?
# Coming from web form.
params = { topic: { approved: 1 } }
# In the controller.
topic.attributes = params[:topic]
- assert topic.approved?
- assert !topic.approved_changed?
+ assert_predicate topic, :approved?
+ assert_not_predicate topic, :approved_changed?
end
def test_partial_update
@@ -378,9 +378,9 @@ class DirtyTest < ActiveRecord::TestCase
def test_reload_should_clear_changed_attributes
pirate = Pirate.create!(catchphrase: "shiver me timbers")
pirate.catchphrase = "*hic*"
- assert pirate.changed?
+ assert_predicate pirate, :changed?
pirate.reload
- assert !pirate.changed?
+ assert_not_predicate pirate, :changed?
end
def test_dup_objects_should_not_copy_dirty_flag_from_creator
@@ -388,17 +388,17 @@ class DirtyTest < ActiveRecord::TestCase
pirate_dup = pirate.dup
pirate_dup.restore_catchphrase!
pirate.catchphrase = "I love Rum"
- assert pirate.catchphrase_changed?
- assert !pirate_dup.catchphrase_changed?
+ assert_predicate pirate, :catchphrase_changed?
+ assert_not_predicate pirate_dup, :catchphrase_changed?
end
def test_reverted_changes_are_not_dirty
phrase = "shiver me timbers"
pirate = Pirate.create!(catchphrase: phrase)
pirate.catchphrase = "*hic*"
- assert pirate.changed?
+ assert_predicate pirate, :changed?
pirate.catchphrase = phrase
- assert !pirate.changed?
+ assert_not_predicate pirate, :changed?
end
def test_reverted_changes_are_not_dirty_after_multiple_changes
@@ -406,40 +406,40 @@ class DirtyTest < ActiveRecord::TestCase
pirate = Pirate.create!(catchphrase: phrase)
10.times do |i|
pirate.catchphrase = "*hic*" * i
- assert pirate.changed?
+ assert_predicate pirate, :changed?
end
- assert pirate.changed?
+ assert_predicate pirate, :changed?
pirate.catchphrase = phrase
- assert !pirate.changed?
+ assert_not_predicate pirate, :changed?
end
def test_reverted_changes_are_not_dirty_going_from_nil_to_value_and_back
pirate = Pirate.create!(catchphrase: "Yar!")
pirate.parrot_id = 1
- assert pirate.changed?
- assert pirate.parrot_id_changed?
- assert !pirate.catchphrase_changed?
+ assert_predicate pirate, :changed?
+ assert_predicate pirate, :parrot_id_changed?
+ assert_not_predicate pirate, :catchphrase_changed?
pirate.parrot_id = nil
- assert !pirate.changed?
- assert !pirate.parrot_id_changed?
- assert !pirate.catchphrase_changed?
+ assert_not_predicate pirate, :changed?
+ assert_not_predicate pirate, :parrot_id_changed?
+ assert_not_predicate pirate, :catchphrase_changed?
end
def test_save_should_store_serialized_attributes_even_with_partial_writes
with_partial_writes(Topic) do
topic = Topic.create!(content: { a: "a" })
- assert_not topic.changed?
+ assert_not_predicate topic, :changed?
topic.content[:b] = "b"
- assert topic.changed?
+ assert_predicate topic, :changed?
topic.save!
- assert_not topic.changed?
+ assert_not_predicate topic, :changed?
assert_equal "b", topic.content[:b]
topic.reload
@@ -466,11 +466,10 @@ class DirtyTest < ActiveRecord::TestCase
def test_save_should_not_save_serialized_attribute_with_partial_writes_if_not_present
with_partial_writes(Topic) do
- Topic.create!(author_name: "Bill", content: { a: "a" })
- topic = Topic.select("id, author_name").first
+ topic = Topic.create!(author_name: "Bill", content: { a: "a" })
+ topic = Topic.select("id, author_name").find(topic.id)
topic.update_columns author_name: "John"
- topic = Topic.first
- assert_not_nil topic.content
+ assert_not_nil topic.reload.content
end
end
@@ -597,7 +596,7 @@ class DirtyTest < ActiveRecord::TestCase
pirate = Pirate.create!(catchphrase: "rrrr", created_on: time_in_paris)
pirate.created_on = pirate.created_on.in_time_zone("Tokyo").to_s
- assert !pirate.created_on_changed?
+ assert_not_predicate pirate, :created_on_changed?
end
test "partial insert" do
@@ -628,7 +627,7 @@ class DirtyTest < ActiveRecord::TestCase
pirate = Pirate.create!(catchphrase: "arrrr")
pirate.catchphrase << " matey!"
- assert pirate.catchphrase_changed?
+ assert_predicate pirate, :catchphrase_changed?
expected_changes = {
"catchphrase" => ["arrrr", "arrrr matey!"]
}
@@ -642,7 +641,7 @@ class DirtyTest < ActiveRecord::TestCase
pirate.reload
assert_equal "arrrr matey!", pirate.catchphrase
- assert_not pirate.changed?
+ assert_not_predicate pirate, :changed?
end
test "in place mutation for binary" do
@@ -653,19 +652,19 @@ class DirtyTest < ActiveRecord::TestCase
binary = klass.create!(data: "\\\\foo")
- assert_not binary.changed?
+ assert_not_predicate binary, :changed?
binary.data = binary.data.dup
- assert_not binary.changed?
+ assert_not_predicate binary, :changed?
binary = klass.last
- assert_not binary.changed?
+ assert_not_predicate binary, :changed?
binary.data << "bar"
- assert binary.changed?
+ assert_predicate binary, :changed?
end
test "changes is correct for subclass" do
@@ -680,7 +679,7 @@ class DirtyTest < ActiveRecord::TestCase
new_catchphrase = "arrrr matey!"
pirate.catchphrase = new_catchphrase
- assert pirate.catchphrase_changed?
+ assert_predicate pirate, :catchphrase_changed?
expected_changes = {
"catchphrase" => ["arrrr", new_catchphrase]
@@ -699,7 +698,7 @@ class DirtyTest < ActiveRecord::TestCase
new_catchphrase = "arrrr matey!"
pirate.catchphrase = new_catchphrase
- assert pirate.catchphrase_changed?
+ assert_predicate pirate, :catchphrase_changed?
expected_changes = {
"catchphrase" => ["arrrr", new_catchphrase]
@@ -721,7 +720,7 @@ class DirtyTest < ActiveRecord::TestCase
end
model = klass.new(first_name: "Jim")
- assert model.first_name_changed?
+ assert_predicate model, :first_name_changed?
end
test "attribute_will_change! doesn't try to save non-persistable attributes" do
@@ -733,7 +732,7 @@ class DirtyTest < ActiveRecord::TestCase
record = klass.new(first_name: "Sean")
record.non_persisted_attribute_will_change!
- assert record.non_persisted_attribute_changed?
+ assert_predicate record, :non_persisted_attribute_changed?
assert record.save
end
@@ -763,26 +762,33 @@ class DirtyTest < ActiveRecord::TestCase
test "attributes assigned but not selected are dirty" do
person = Person.select(:id).first
- refute person.changed?
+ assert_not_predicate person, :changed?
person.first_name = "Sean"
- assert person.changed?
+ assert_predicate person, :changed?
person.first_name = nil
- assert person.changed?
+ assert_predicate person, :changed?
+ end
+
+ test "attributes not selected are still missing after save" do
+ person = Person.select(:id).first
+ assert_raises(ActiveModel::MissingAttributeError) { person.first_name }
+ assert person.save # calls forget_attribute_assignments
+ assert_raises(ActiveModel::MissingAttributeError) { person.first_name }
end
test "saved_change_to_attribute? returns whether a change occurred in the last save" do
person = Person.create!(first_name: "Sean")
- assert person.saved_change_to_first_name?
- refute person.saved_change_to_gender?
+ assert_predicate person, :saved_change_to_first_name?
+ assert_not_predicate person, :saved_change_to_gender?
assert person.saved_change_to_first_name?(from: nil, to: "Sean")
assert person.saved_change_to_first_name?(from: nil)
assert person.saved_change_to_first_name?(to: "Sean")
- refute person.saved_change_to_first_name?(from: "Jim", to: "Sean")
- refute person.saved_change_to_first_name?(from: "Jim")
- refute person.saved_change_to_first_name?(to: "Jim")
+ assert_not person.saved_change_to_first_name?(from: "Jim", to: "Sean")
+ assert_not person.saved_change_to_first_name?(from: "Jim")
+ assert_not person.saved_change_to_first_name?(to: "Jim")
end
test "saved_change_to_attribute returns the change that occurred in the last save" do
@@ -817,11 +823,11 @@ class DirtyTest < ActiveRecord::TestCase
test "saved_changes? returns whether the last call to save changed anything" do
person = Person.create!(first_name: "Sean")
- assert person.saved_changes?
+ assert_predicate person, :saved_changes?
person.save
- refute person.saved_changes?
+ assert_not_predicate person, :saved_changes?
end
test "saved_changes returns a hash of all the changes that occurred" do
@@ -839,20 +845,19 @@ class DirtyTest < ActiveRecord::TestCase
assert_equal %w(first_name lock_version updated_at).sort, person.saved_changes.keys.sort
end
- test "changed? in after callbacks returns true but is deprecated" do
+ test "changed? in after callbacks returns false" do
klass = Class.new(ActiveRecord::Base) do
self.table_name = "people"
after_save do
- ActiveSupport::Deprecation.silence do
- raise "changed? should be true" unless changed?
- end
+ raise "changed? should be false" if changed?
raise "has_changes_to_save? should be false" if has_changes_to_save?
+ raise "saved_changes? should be true" unless saved_changes?
end
end
person = klass.create!(first_name: "Sean")
- refute person.changed?
+ assert_not_predicate person, :changed?
end
private
@@ -865,8 +870,8 @@ class DirtyTest < ActiveRecord::TestCase
end
def check_pirate_after_save_failure(pirate)
- assert pirate.changed?
- assert pirate.parrot_id_changed?
+ assert_predicate pirate, :changed?
+ assert_predicate pirate, :parrot_id_changed?
assert_equal %w(parrot_id), pirate.changed
assert_nil pirate.parrot_id_was
end
diff --git a/activerecord/test/cases/disconnected_test.rb b/activerecord/test/cases/disconnected_test.rb
index c25089a420..533665d0f4 100644
--- a/activerecord/test/cases/disconnected_test.rb
+++ b/activerecord/test/cases/disconnected_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "cases/helper"
class TestRecord < ActiveRecord::Base
diff --git a/activerecord/test/cases/dup_test.rb b/activerecord/test/cases/dup_test.rb
index 000ed27efb..9e33c3110c 100644
--- a/activerecord/test/cases/dup_test.rb
+++ b/activerecord/test/cases/dup_test.rb
@@ -1,13 +1,16 @@
+# frozen_string_literal: true
+
require "cases/helper"
require "models/reply"
require "models/topic"
+require "models/movie"
module ActiveRecord
class DupTest < ActiveRecord::TestCase
fixtures :topics
def test_dup
- assert !Topic.new.freeze.dup.frozen?
+ assert_not_predicate Topic.new.freeze.dup, :frozen?
end
def test_not_readonly
@@ -38,7 +41,7 @@ module ActiveRecord
topic.destroy
duped = topic.dup
- assert_not duped.destroyed?
+ assert_not_predicate duped, :destroyed?
end
def test_dup_has_no_id
@@ -60,10 +63,10 @@ module ActiveRecord
topic.attributes = dbtopic.attributes.except("id")
- #duped has no timestamp values
+ # duped has no timestamp values
duped = dbtopic.dup
- #clear topic timestamp values
+ # clear topic timestamp values
topic.send(:clear_timestamp_attributes)
assert_equal topic.changes, duped.changes
@@ -98,7 +101,7 @@ module ActiveRecord
# temporary change to the topic object
topic.updated_at -= 3.days
- #dup should not preserve the timestamps if present
+ # dup should not preserve the timestamps if present
new_topic = topic.dup
assert_nil new_topic.updated_at
assert_nil new_topic.created_at
@@ -124,12 +127,12 @@ module ActiveRecord
duped = topic.dup
duped.title = nil
- assert duped.invalid?
+ assert_predicate duped, :invalid?
topic.title = nil
duped.title = "Mathematics"
- assert topic.invalid?
- assert duped.valid?
+ assert_predicate topic, :invalid?
+ assert_predicate duped, :valid?
end
end
@@ -155,5 +158,20 @@ module ActiveRecord
record.dup
end
end
+
+ def test_dup_record_not_persisted_after_rollback_transaction
+ movie = Movie.new(name: "test")
+
+ assert_raises(ActiveRecord::RecordInvalid) do
+ Movie.transaction do
+ movie.save!
+ duped = movie.dup
+ duped.name = nil
+ duped.save!
+ end
+ end
+
+ assert !movie.persisted?
+ end
end
end
diff --git a/activerecord/test/cases/enum_test.rb b/activerecord/test/cases/enum_test.rb
index 4ef9a125e6..d5a1d11e12 100644
--- a/activerecord/test/cases/enum_test.rb
+++ b/activerecord/test/cases/enum_test.rb
@@ -1,25 +1,27 @@
+# frozen_string_literal: true
+
require "cases/helper"
require "models/author"
require "models/book"
class EnumTest < ActiveRecord::TestCase
- fixtures :books, :authors
+ fixtures :books, :authors, :author_addresses
setup do
@book = books(:awdr)
end
test "query state by predicate" do
- assert @book.published?
- assert_not @book.written?
- assert_not @book.proposed?
+ assert_predicate @book, :published?
+ assert_not_predicate @book, :written?
+ assert_not_predicate @book, :proposed?
- assert @book.read?
- assert @book.in_english?
- assert @book.author_visibility_visible?
- assert @book.illustrator_visibility_visible?
- assert @book.with_medium_font_size?
- assert @book.medium_to_read?
+ assert_predicate @book, :read?
+ assert_predicate @book, :in_english?
+ assert_predicate @book, :author_visibility_visible?
+ assert_predicate @book, :illustrator_visibility_visible?
+ assert_predicate @book, :with_medium_font_size?
+ assert_predicate @book, :medium_to_read?
end
test "query state with strings" do
@@ -74,46 +76,46 @@ class EnumTest < ActiveRecord::TestCase
end
test "build from scope" do
- assert Book.written.build.written?
- assert_not Book.written.build.proposed?
+ assert_predicate Book.written.build, :written?
+ assert_not_predicate Book.written.build, :proposed?
end
test "build from where" do
- assert Book.where(status: Book.statuses[:written]).build.written?
- assert_not Book.where(status: Book.statuses[:written]).build.proposed?
- assert Book.where(status: :written).build.written?
- assert_not Book.where(status: :written).build.proposed?
- assert Book.where(status: "written").build.written?
- assert_not Book.where(status: "written").build.proposed?
+ assert_predicate Book.where(status: Book.statuses[:written]).build, :written?
+ assert_not_predicate Book.where(status: Book.statuses[:written]).build, :proposed?
+ assert_predicate Book.where(status: :written).build, :written?
+ assert_not_predicate Book.where(status: :written).build, :proposed?
+ assert_predicate Book.where(status: "written").build, :written?
+ assert_not_predicate Book.where(status: "written").build, :proposed?
end
test "update by declaration" do
@book.written!
- assert @book.written?
+ assert_predicate @book, :written?
@book.in_english!
- assert @book.in_english?
+ assert_predicate @book, :in_english?
@book.author_visibility_visible!
- assert @book.author_visibility_visible?
+ assert_predicate @book, :author_visibility_visible?
end
test "update by setter" do
@book.update! status: :written
- assert @book.written?
+ assert_predicate @book, :written?
end
test "enum methods are overwritable" do
assert_equal "do publish work...", @book.published!
- assert @book.published?
+ assert_predicate @book, :published?
end
test "direct assignment" do
@book.status = :written
- assert @book.written?
+ assert_predicate @book, :written?
end
test "assign string value" do
@book.status = "written"
- assert @book.written?
+ assert_predicate @book, :written?
end
test "enum changed attributes" do
@@ -240,25 +242,27 @@ class EnumTest < ActiveRecord::TestCase
end
test "building new objects with enum scopes" do
- assert Book.written.build.written?
- assert Book.read.build.read?
- assert Book.in_spanish.build.in_spanish?
- assert Book.illustrator_visibility_invisible.build.illustrator_visibility_invisible?
+ assert_predicate Book.written.build, :written?
+ assert_predicate Book.read.build, :read?
+ assert_predicate Book.in_spanish.build, :in_spanish?
+ assert_predicate Book.illustrator_visibility_invisible.build, :illustrator_visibility_invisible?
end
test "creating new objects with enum scopes" do
- assert Book.written.create.written?
- assert Book.read.create.read?
- assert Book.in_spanish.create.in_spanish?
- assert Book.illustrator_visibility_invisible.create.illustrator_visibility_invisible?
+ assert_predicate Book.written.create, :written?
+ assert_predicate Book.read.create, :read?
+ assert_predicate Book.in_spanish.create, :in_spanish?
+ assert_predicate Book.illustrator_visibility_invisible.create, :illustrator_visibility_invisible?
end
- test "_before_type_cast returns the enum label (required for form fields)" do
- if @book.status_came_from_user?
- assert_equal "published", @book.status_before_type_cast
- else
- assert_equal "published", @book.status
- end
+ test "_before_type_cast" do
+ assert_equal 2, @book.status_before_type_cast
+ assert_equal "published", @book.status
+
+ @book.status = "published"
+
+ assert_equal "published", @book.status_before_type_cast
+ assert_equal "published", @book.status
end
test "reserved enum names" do
@@ -304,6 +308,24 @@ class EnumTest < ActiveRecord::TestCase
end
end
+ test "reserved enum values for relation" do
+ relation_method_samples = [
+ :records,
+ :to_ary,
+ :scope_for_create
+ ]
+
+ relation_method_samples.each do |value|
+ e = assert_raises(ArgumentError, "enum value `#{value}` should not be allowed") do
+ Class.new(ActiveRecord::Base) do
+ self.table_name = "books"
+ enum category: [:other, value]
+ end
+ end
+ assert_match(/You tried to define an enum named .* on the model/, e.message)
+ end
+ end
+
test "overriding enum method should not raise" do
assert_nothing_raised do
Class.new(ActiveRecord::Base) do
@@ -333,9 +355,9 @@ class EnumTest < ActiveRecord::TestCase
klass.delete_all
klass.create!(status: "proposed")
book = klass.new(status: "written")
- assert book.valid?
+ assert_predicate book, :valid?
book.status = "proposed"
- assert_not book.valid?
+ assert_not_predicate book, :valid?
end
test "validate inclusion of value in array" do
@@ -346,9 +368,9 @@ class EnumTest < ActiveRecord::TestCase
end
klass.delete_all
invalid_book = klass.new(status: "proposed")
- assert_not invalid_book.valid?
+ assert_not_predicate invalid_book, :valid?
valid_book = klass.new(status: "written")
- assert valid_book.valid?
+ assert_predicate valid_book, :valid?
end
test "enums are distinct per class" do
@@ -395,10 +417,10 @@ class EnumTest < ActiveRecord::TestCase
end
book1 = klass.proposed.create!
- assert book1.proposed?
+ assert_predicate book1, :proposed?
book2 = klass.single.create!
- assert book2.single?
+ assert_predicate book2, :single?
end
test "enum with alias_attribute" do
@@ -409,62 +431,62 @@ class EnumTest < ActiveRecord::TestCase
end
book = klass.proposed.create!
- assert book.proposed?
+ assert_predicate book, :proposed?
assert_equal "proposed", book.aliased_status
book = klass.find(book.id)
- assert book.proposed?
+ assert_predicate book, :proposed?
assert_equal "proposed", book.aliased_status
end
test "query state by predicate with prefix" do
- assert @book.author_visibility_visible?
- assert_not @book.author_visibility_invisible?
- assert @book.illustrator_visibility_visible?
- assert_not @book.illustrator_visibility_invisible?
+ assert_predicate @book, :author_visibility_visible?
+ assert_not_predicate @book, :author_visibility_invisible?
+ assert_predicate @book, :illustrator_visibility_visible?
+ assert_not_predicate @book, :illustrator_visibility_invisible?
end
test "query state by predicate with custom prefix" do
- assert @book.in_english?
- assert_not @book.in_spanish?
- assert_not @book.in_french?
+ assert_predicate @book, :in_english?
+ assert_not_predicate @book, :in_spanish?
+ assert_not_predicate @book, :in_french?
end
test "query state by predicate with custom suffix" do
- assert @book.medium_to_read?
- assert_not @book.easy_to_read?
- assert_not @book.hard_to_read?
+ assert_predicate @book, :medium_to_read?
+ assert_not_predicate @book, :easy_to_read?
+ assert_not_predicate @book, :hard_to_read?
end
test "enum methods with custom suffix defined" do
- assert @book.class.respond_to?(:easy_to_read)
- assert @book.class.respond_to?(:medium_to_read)
- assert @book.class.respond_to?(:hard_to_read)
+ assert_respond_to @book.class, :easy_to_read
+ assert_respond_to @book.class, :medium_to_read
+ assert_respond_to @book.class, :hard_to_read
- assert @book.respond_to?(:easy_to_read?)
- assert @book.respond_to?(:medium_to_read?)
- assert @book.respond_to?(:hard_to_read?)
+ assert_respond_to @book, :easy_to_read?
+ assert_respond_to @book, :medium_to_read?
+ assert_respond_to @book, :hard_to_read?
- assert @book.respond_to?(:easy_to_read!)
- assert @book.respond_to?(:medium_to_read!)
- assert @book.respond_to?(:hard_to_read!)
+ assert_respond_to @book, :easy_to_read!
+ assert_respond_to @book, :medium_to_read!
+ assert_respond_to @book, :hard_to_read!
end
test "update enum attributes with custom suffix" do
@book.medium_to_read!
- assert_not @book.easy_to_read?
- assert @book.medium_to_read?
- assert_not @book.hard_to_read?
+ assert_not_predicate @book, :easy_to_read?
+ assert_predicate @book, :medium_to_read?
+ assert_not_predicate @book, :hard_to_read?
@book.easy_to_read!
- assert @book.easy_to_read?
- assert_not @book.medium_to_read?
- assert_not @book.hard_to_read?
+ assert_predicate @book, :easy_to_read?
+ assert_not_predicate @book, :medium_to_read?
+ assert_not_predicate @book, :hard_to_read?
@book.hard_to_read!
- assert_not @book.easy_to_read?
- assert_not @book.medium_to_read?
- assert @book.hard_to_read?
+ assert_not_predicate @book, :easy_to_read?
+ assert_not_predicate @book, :medium_to_read?
+ assert_predicate @book, :hard_to_read?
end
test "uses default status when no status is provided in fixtures" do
@@ -475,12 +497,12 @@ class EnumTest < ActiveRecord::TestCase
test "uses default value from database on initialization" do
book = Book.new
- assert book.proposed?
+ assert_predicate book, :proposed?
end
test "uses default value from database on initialization when using custom mapping" do
book = Book.new
- assert book.hard?
+ assert_predicate book, :hard?
end
test "data type of Enum type" do
diff --git a/activerecord/test/cases/errors_test.rb b/activerecord/test/cases/errors_test.rb
index e90669e0c7..b90e6a66c5 100644
--- a/activerecord/test/cases/errors_test.rb
+++ b/activerecord/test/cases/errors_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "cases/helper"
class ErrorsTest < ActiveRecord::TestCase
diff --git a/activerecord/test/cases/explain_subscriber_test.rb b/activerecord/test/cases/explain_subscriber_test.rb
index ca87e04012..82cc891970 100644
--- a/activerecord/test/cases/explain_subscriber_test.rb
+++ b/activerecord/test/cases/explain_subscriber_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "cases/helper"
require "active_record/explain_subscriber"
require "active_record/explain_registry"
@@ -13,20 +15,20 @@ if ActiveRecord::Base.connection.supports_explain?
def test_collects_nothing_if_the_payload_has_an_exception
SUBSCRIBER.finish(nil, nil, exception: Exception.new)
- assert queries.empty?
+ assert_empty queries
end
def test_collects_nothing_for_ignored_payloads
ActiveRecord::ExplainSubscriber::IGNORED_PAYLOADS.each do |ip|
SUBSCRIBER.finish(nil, nil, name: ip)
end
- assert queries.empty?
+ assert_empty queries
end
def test_collects_nothing_if_collect_is_false
ActiveRecord::ExplainRegistry.collect = false
SUBSCRIBER.finish(nil, nil, name: "SQL", sql: "select 1 from users", binds: [1, 2])
- assert queries.empty?
+ assert_empty queries
end
def test_collects_pairs_of_queries_and_binds
@@ -40,12 +42,12 @@ if ActiveRecord::Base.connection.supports_explain?
def test_collects_nothing_if_the_statement_is_not_whitelisted
SUBSCRIBER.finish(nil, nil, name: "SQL", sql: "SHOW max_identifier_length")
- assert queries.empty?
+ assert_empty queries
end
def test_collects_nothing_if_the_statement_is_only_partially_matched
SUBSCRIBER.finish(nil, nil, name: "SQL", sql: "select_db yo_mama")
- assert queries.empty?
+ assert_empty queries
end
def test_collects_cte_queries
diff --git a/activerecord/test/cases/explain_test.rb b/activerecord/test/cases/explain_test.rb
index 4f6bd9327c..17654027a9 100644
--- a/activerecord/test/cases/explain_test.rb
+++ b/activerecord/test/cases/explain_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "cases/helper"
require "models/car"
require "active_support/core_ext/string/strip"
@@ -47,7 +49,7 @@ if ActiveRecord::Base.connection.supports_explain?
def test_exec_explain_with_binds
sqls = %w(foo bar)
- binds = [[bind_attribute("wadus", 1)], [bind_attribute("chaflan", 2)]]
+ binds = [[bind_param("wadus", 1)], [bind_param("chaflan", 2)]]
queries = sqls.zip(binds)
stub_explain_for_query_plans(["query plan foo\n", "query plan bar\n"]) do
@@ -79,5 +81,9 @@ if ActiveRecord::Base.connection.supports_explain?
yield
end
end
+
+ def bind_param(name, value)
+ ActiveRecord::Relation::QueryAttribute.new(name, value, ActiveRecord::Type::Value.new)
+ end
end
end
diff --git a/activerecord/test/cases/finder_respond_to_test.rb b/activerecord/test/cases/finder_respond_to_test.rb
index 3eaa993d45..59af4e6961 100644
--- a/activerecord/test/cases/finder_respond_to_test.rb
+++ b/activerecord/test/cases/finder_respond_to_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "cases/helper"
require "models/topic"
@@ -6,7 +8,7 @@ class FinderRespondToTest < ActiveRecord::TestCase
def test_should_preserve_normal_respond_to_behaviour_on_base
assert_respond_to ActiveRecord::Base, :new
- assert !ActiveRecord::Base.respond_to?(:find_by_something)
+ assert_not_respond_to ActiveRecord::Base, :find_by_something
end
def test_should_preserve_normal_respond_to_behaviour_and_respond_to_newly_added_method
@@ -41,14 +43,14 @@ class FinderRespondToTest < ActiveRecord::TestCase
end
def test_should_not_respond_to_find_by_one_missing_attribute
- assert !Topic.respond_to?(:find_by_undertitle)
+ assert_not_respond_to Topic, :find_by_undertitle
end
def test_should_not_respond_to_find_by_invalid_method_syntax
- assert !Topic.respond_to?(:fail_to_find_by_title)
- assert !Topic.respond_to?(:find_by_title?)
- assert !Topic.respond_to?(:fail_to_find_or_create_by_title)
- assert !Topic.respond_to?(:find_or_create_by_title?)
+ assert_not_respond_to Topic, :fail_to_find_by_title
+ assert_not_respond_to Topic, :find_by_title?
+ assert_not_respond_to Topic, :fail_to_find_or_create_by_title
+ assert_not_respond_to Topic, :find_or_create_by_title?
end
private
diff --git a/activerecord/test/cases/finder_test.rb b/activerecord/test/cases/finder_test.rb
index 420f552ef6..b413212e26 100644
--- a/activerecord/test/cases/finder_test.rb
+++ b/activerecord/test/cases/finder_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "cases/helper"
require "models/post"
require "models/author"
@@ -7,6 +9,7 @@ require "models/company"
require "models/tagging"
require "models/topic"
require "models/reply"
+require "models/rating"
require "models/entrant"
require "models/project"
require "models/developer"
@@ -117,6 +120,21 @@ class FinderTest < ActiveRecord::TestCase
assert_equal "The Fourth Topic of the day", records[2].title
end
+ def test_find_with_ids_with_no_id_passed
+ exception = assert_raises(ActiveRecord::RecordNotFound) { Topic.find }
+ assert_equal exception.model, "Topic"
+ assert_equal exception.primary_key, "id"
+ end
+
+ def test_find_with_ids_with_id_out_of_range
+ exception = assert_raises(ActiveRecord::RecordNotFound) do
+ Topic.find("9999999999999999999999999999999")
+ end
+
+ assert_equal exception.model, "Topic"
+ assert_equal exception.primary_key, "id"
+ end
+
def test_find_passing_active_record_object_is_not_permitted
assert_raises(ArgumentError) do
Topic.find(Topic.last)
@@ -154,6 +172,32 @@ class FinderTest < ActiveRecord::TestCase
assert_raise(NoMethodError) { Topic.exists?([1, 2]) }
end
+ def test_exists_with_scope
+ davids = Author.where(name: "David")
+ assert_equal true, davids.exists?
+ assert_equal true, davids.exists?(authors(:david).id)
+ assert_equal false, davids.exists?(authors(:mary).id)
+ assert_equal false, davids.exists?("42")
+ assert_equal false, davids.exists?(42)
+ assert_equal false, davids.exists?(davids.new.id)
+
+ fake = Author.where(name: "fake author")
+ assert_equal false, fake.exists?
+ assert_equal false, fake.exists?(authors(:david).id)
+ end
+
+ def test_exists_uses_existing_scope
+ post = authors(:david).posts.first
+ authors = Author.includes(:posts).where(name: "David", posts: { id: post.id })
+ assert_equal true, authors.exists?(authors(:david).id)
+ end
+
+ def test_any_with_scope_on_hash_includes
+ post = authors(:david).posts.first
+ categories = Categorization.includes(author: :posts).where(posts: { id: post.id })
+ assert_equal true, categories.exists?
+ end
+
def test_exists_with_polymorphic_relation
post = Post.create!(title: "Post", body: "default", taggings: [Tagging.new(comment: "tagging comment")])
relation = Post.tagged_with_comment("tagging comment")
@@ -210,7 +254,7 @@ class FinderTest < ActiveRecord::TestCase
# Ensure +exists?+ runs without an error by excluding order value.
def test_exists_with_order
- assert_equal true, Topic.order("invalid sql here").exists?
+ assert_equal true, Topic.order(Arel.sql("invalid sql here")).exists?
end
def test_exists_with_joins
@@ -226,20 +270,27 @@ class FinderTest < ActiveRecord::TestCase
end
def test_exists_with_includes_limit_and_empty_result
- assert_equal false, Topic.includes(:replies).limit(0).exists?
- assert_equal false, Topic.includes(:replies).limit(1).where("0 = 1").exists?
+ assert_no_queries { assert_equal false, Topic.includes(:replies).limit(0).exists? }
+ assert_queries(1) { assert_equal false, Topic.includes(:replies).limit(1).where("0 = 1").exists? }
end
def test_exists_with_distinct_association_includes_and_limit
author = Author.first
- assert_equal false, author.unique_categorized_posts.includes(:special_comments).limit(0).exists?
- assert_equal true, author.unique_categorized_posts.includes(:special_comments).limit(1).exists?
+ unique_categorized_posts = author.unique_categorized_posts.includes(:special_comments)
+ assert_no_queries { assert_equal false, unique_categorized_posts.limit(0).exists? }
+ assert_queries(1) { assert_equal true, unique_categorized_posts.limit(1).exists? }
end
def test_exists_with_distinct_association_includes_limit_and_order
author = Author.first
- assert_equal false, author.unique_categorized_posts.includes(:special_comments).order("comments.tags_count DESC").limit(0).exists?
- assert_equal true, author.unique_categorized_posts.includes(:special_comments).order("comments.tags_count DESC").limit(1).exists?
+ unique_categorized_posts = author.unique_categorized_posts.includes(:special_comments).order("comments.tags_count DESC")
+ assert_no_queries { assert_equal false, unique_categorized_posts.limit(0).exists? }
+ assert_queries(1) { assert_equal true, unique_categorized_posts.limit(1).exists? }
+ end
+
+ def test_exists_should_reference_correct_aliases_while_joining_tables_of_has_many_through_association
+ ratings = developers(:david).ratings.includes(comment: :post).where(posts: { id: 1 })
+ assert_queries(1) { assert_not_predicate ratings.limit(1), :exists? }
end
def test_exists_with_empty_table_and_no_args_given
@@ -514,7 +565,7 @@ class FinderTest < ActiveRecord::TestCase
assert_nil Topic.offset(4).second_to_last
assert_nil Topic.offset(5).second_to_last
- #test with limit
+ # test with limit
assert_nil Topic.limit(1).second
assert_nil Topic.limit(1).second_to_last
end
@@ -597,13 +648,13 @@ class FinderTest < ActiveRecord::TestCase
def test_last_with_integer_and_order_should_use_sql_limit
relation = Topic.order("title")
assert_queries(1) { relation.last(5) }
- assert !relation.loaded?
+ assert_not_predicate relation, :loaded?
end
def test_last_with_integer_and_reorder_should_use_sql_limit
relation = Topic.reorder("title")
assert_queries(1) { relation.last(5) }
- assert !relation.loaded?
+ assert_not_predicate relation, :loaded?
end
def test_last_on_loaded_relation_should_not_use_sql
@@ -616,7 +667,7 @@ class FinderTest < ActiveRecord::TestCase
def test_last_with_irreversible_order
assert_raises(ActiveRecord::IrreversibleOrderError) do
- Topic.order("coalesce(author_name, title)").last
+ Topic.order(Arel.sql("coalesce(author_name, title)")).last
end
end
@@ -628,12 +679,34 @@ class FinderTest < ActiveRecord::TestCase
assert_equal comments.limit(2).to_a.last(2), comments.limit(2).last(2)
assert_equal comments.limit(2).to_a.last(3), comments.limit(2).last(3)
+ assert_equal comments.offset(2).to_a.last, comments.offset(2).last
+ assert_equal comments.offset(2).to_a.last(2), comments.offset(2).last(2)
+ assert_equal comments.offset(2).to_a.last(3), comments.offset(2).last(3)
+
comments = comments.offset(1)
assert_equal comments.limit(2).to_a.last, comments.limit(2).last
assert_equal comments.limit(2).to_a.last(2), comments.limit(2).last(2)
assert_equal comments.limit(2).to_a.last(3), comments.limit(2).last(3)
end
+ def test_first_on_relation_with_limit_and_offset
+ post = posts("sti_comments")
+
+ comments = post.comments.order(id: :asc)
+ assert_equal comments.limit(2).to_a.first, comments.limit(2).first
+ assert_equal comments.limit(2).to_a.first(2), comments.limit(2).first(2)
+ assert_equal comments.limit(2).to_a.first(3), comments.limit(2).first(3)
+
+ assert_equal comments.offset(2).to_a.first, comments.offset(2).first
+ assert_equal comments.offset(2).to_a.first(2), comments.offset(2).first(2)
+ assert_equal comments.offset(2).to_a.first(3), comments.offset(2).first(3)
+
+ comments = comments.offset(1)
+ assert_equal comments.limit(2).to_a.first, comments.limit(2).first
+ assert_equal comments.limit(2).to_a.first(2), comments.limit(2).first(2)
+ assert_equal comments.limit(2).to_a.first(3), comments.limit(2).first(3)
+ end
+
def test_take_and_first_and_last_with_integer_should_return_an_array
assert_kind_of Array, Topic.take(5)
assert_kind_of Array, Topic.first(5)
@@ -795,6 +868,25 @@ class FinderTest < ActiveRecord::TestCase
assert_equal customers(:david), found_customer
end
+ def test_hash_condition_find_with_aggregate_having_three_mappings_array
+ david_address = customers(:david).address
+ zaphod_address = customers(:zaphod).address
+ barney_address = customers(:barney).address
+ assert_kind_of Address, david_address
+ assert_kind_of Address, zaphod_address
+ found_customers = Customer.where(address: [david_address, zaphod_address, barney_address])
+ assert_equal [customers(:david), customers(:zaphod), customers(:barney)], found_customers.sort_by(&:id)
+ end
+
+ def test_hash_condition_find_with_aggregate_having_one_mapping_array
+ david_balance = customers(:david).balance
+ zaphod_balance = customers(:zaphod).balance
+ assert_kind_of Money, david_balance
+ assert_kind_of Money, zaphod_balance
+ found_customers = Customer.where(balance: [david_balance, zaphod_balance])
+ assert_equal [customers(:david), customers(:zaphod)], found_customers.sort_by(&:id)
+ end
+
def test_hash_condition_find_with_aggregate_attribute_having_same_name_as_field_and_key_value_being_aggregate
gps_location = customers(:david).gps_location
assert_kind_of GpsLocation, gps_location
@@ -1000,14 +1092,6 @@ class FinderTest < ActiveRecord::TestCase
assert_raise(ArgumentError) { Topic.find_by_title_and_author_name("The First Topic") }
end
- def test_find_last_with_offset
- devs = Developer.order("id")
-
- assert_equal devs[2], Developer.offset(2).first
- assert_equal devs[-3], Developer.offset(2).last
- assert_equal devs[-3], Developer.offset(2).order("id DESC").first
- end
-
def test_find_by_nil_attribute
topic = Topic.find_by_last_read nil
assert_not_nil topic
@@ -1151,7 +1235,7 @@ class FinderTest < ActiveRecord::TestCase
e = assert_raises(ActiveRecord::RecordNotFound) do
model.find "Hello", "World!"
end
- assert_equal "Couldn't find all MercedesCars with 'name': (Hello, World!) (found 0 results, but was looking for 2)", e.message
+ assert_equal "Couldn't find all MercedesCars with 'name': (Hello, World!) (found 0 results, but was looking for 2).", e.message
end
end
@@ -1177,13 +1261,17 @@ class FinderTest < ActiveRecord::TestCase
assert_equal posts(:eager_other), Post.find_by("id = ?", posts(:eager_other).id)
end
+ test "find_by with range conditions returns the first matching record" do
+ assert_equal posts(:eager_other), Post.find_by(id: posts(:eager_other).id...posts(:misc_by_bob).id)
+ end
+
test "find_by returns nil if the record is missing" do
assert_nil Post.find_by("1 = 0")
end
test "find_by with associations" do
assert_equal authors(:david), Post.find_by(author: authors(:david)).author
- assert_equal authors(:mary) , Post.find_by(author: authors(:mary)).author
+ assert_equal authors(:mary), Post.find_by(author: authors(:mary)).author
end
test "find_by doesn't have implicit ordering" do
@@ -1232,6 +1320,34 @@ class FinderTest < ActiveRecord::TestCase
assert_equal tyre2, zyke.tyres.custom_find_by(id: tyre2.id)
end
+ test "#skip_query_cache! for #exists?" do
+ Topic.cache do
+ assert_queries(1) do
+ Topic.exists?
+ Topic.exists?
+ end
+
+ assert_queries(2) do
+ Topic.all.skip_query_cache!.exists?
+ Topic.all.skip_query_cache!.exists?
+ end
+ end
+ end
+
+ test "#skip_query_cache! for #exists? with a limited eager load" do
+ Topic.cache do
+ assert_queries(1) do
+ Topic.eager_load(:replies).limit(1).exists?
+ Topic.eager_load(:replies).limit(1).exists?
+ end
+
+ assert_queries(2) do
+ Topic.eager_load(:replies).limit(1).skip_query_cache!.exists?
+ Topic.eager_load(:replies).limit(1).skip_query_cache!.exists?
+ end
+ end
+ end
+
private
def table_with_custom_primary_key
yield(Class.new(Toy) do
diff --git a/activerecord/test/cases/fixture_set/file_test.rb b/activerecord/test/cases/fixture_set/file_test.rb
index 533edcc2e0..ff99988cb5 100644
--- a/activerecord/test/cases/fixture_set/file_test.rb
+++ b/activerecord/test/cases/fixture_set/file_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "cases/helper"
require "tempfile"
diff --git a/activerecord/test/cases/fixtures_test.rb b/activerecord/test/cases/fixtures_test.rb
index b499e60922..c88b90c8eb 100644
--- a/activerecord/test/cases/fixtures_test.rb
+++ b/activerecord/test/cases/fixtures_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "cases/helper"
require "models/admin"
require "models/admin/account"
@@ -77,6 +79,151 @@ class FixturesTest < ActiveRecord::TestCase
ActiveSupport::Notifications.unsubscribe(subscription)
end
end
+
+ def test_bulk_insert_multiple_table_with_a_multi_statement_query
+ subscriber = InsertQuerySubscriber.new
+ subscription = ActiveSupport::Notifications.subscribe("sql.active_record", subscriber)
+
+ create_fixtures("bulbs", "authors", "computers")
+
+ expected_sql = <<-EOS.strip_heredoc.chop
+ INSERT INTO #{ActiveRecord::Base.connection.quote_table_name("bulbs")} .*
+ INSERT INTO #{ActiveRecord::Base.connection.quote_table_name("authors")} .*
+ INSERT INTO #{ActiveRecord::Base.connection.quote_table_name("computers")} .*
+ EOS
+ assert_equal 1, subscriber.events.size
+ assert_match(/#{expected_sql}/, subscriber.events.first)
+ ensure
+ ActiveSupport::Notifications.unsubscribe(subscription)
+ end
+
+ def test_bulk_insert_with_a_multi_statement_query_raises_an_exception_when_any_insert_fails
+ require "models/aircraft"
+
+ assert_equal false, Aircraft.columns_hash["wheels_count"].null
+ fixtures = {
+ "aircraft" => [
+ { "name" => "working_aircrafts", "wheels_count" => 2 },
+ { "name" => "broken_aircrafts", "wheels_count" => nil },
+ ]
+ }
+
+ assert_no_difference "Aircraft.count" do
+ assert_raises(ActiveRecord::NotNullViolation) do
+ ActiveRecord::Base.connection.insert_fixtures_set(fixtures)
+ end
+ end
+ end
+ end
+
+ if current_adapter?(:Mysql2Adapter)
+ def test_insert_fixtures_set_raises_an_error_when_max_allowed_packet_is_smaller_than_fixtures_set_size
+ conn = ActiveRecord::Base.connection
+ mysql_margin = 2
+ packet_size = 1024
+ bytes_needed_to_have_a_1024_bytes_fixture = 858
+ fixtures = {
+ "traffic_lights" => [
+ { "location" => "US", "state" => ["NY"], "long_state" => ["a" * bytes_needed_to_have_a_1024_bytes_fixture] },
+ ]
+ }
+
+ conn.stubs(:max_allowed_packet).returns(packet_size - mysql_margin)
+
+ error = assert_raises(ActiveRecord::ActiveRecordError) { conn.insert_fixtures_set(fixtures) }
+ assert_match(/Fixtures set is too large #{packet_size}\./, error.message)
+ end
+
+ def test_insert_fixture_set_when_max_allowed_packet_is_bigger_than_fixtures_set_size
+ conn = ActiveRecord::Base.connection
+ packet_size = 1024
+ fixtures = {
+ "traffic_lights" => [
+ { "location" => "US", "state" => ["NY"], "long_state" => ["a" * 51] },
+ ]
+ }
+
+ conn.stubs(:max_allowed_packet).returns(packet_size)
+
+ assert_difference "TrafficLight.count" do
+ conn.insert_fixtures_set(fixtures)
+ end
+ end
+
+ def test_insert_fixtures_set_split_the_total_sql_into_two_chunks_smaller_than_max_allowed_packet
+ subscriber = InsertQuerySubscriber.new
+ subscription = ActiveSupport::Notifications.subscribe("sql.active_record", subscriber)
+ conn = ActiveRecord::Base.connection
+ packet_size = 1024
+ fixtures = {
+ "traffic_lights" => [
+ { "location" => "US", "state" => ["NY"], "long_state" => ["a" * 450] },
+ ],
+ "comments" => [
+ { "post_id" => 1, "body" => "a" * 450 },
+ ]
+ }
+
+ conn.stubs(:max_allowed_packet).returns(packet_size)
+
+ conn.insert_fixtures_set(fixtures)
+ assert_equal 2, subscriber.events.size
+ assert_operator subscriber.events.first.bytesize, :<, packet_size
+ assert_operator subscriber.events.second.bytesize, :<, packet_size
+ ensure
+ ActiveSupport::Notifications.unsubscribe(subscription)
+ end
+
+ def test_insert_fixtures_set_concat_total_sql_into_a_single_packet_smaller_than_max_allowed_packet
+ subscriber = InsertQuerySubscriber.new
+ subscription = ActiveSupport::Notifications.subscribe("sql.active_record", subscriber)
+ conn = ActiveRecord::Base.connection
+ packet_size = 1024
+ fixtures = {
+ "traffic_lights" => [
+ { "location" => "US", "state" => ["NY"], "long_state" => ["a" * 200] },
+ ],
+ "comments" => [
+ { "post_id" => 1, "body" => "a" * 200 },
+ ]
+ }
+
+ conn.stubs(:max_allowed_packet).returns(packet_size)
+
+ assert_difference ["TrafficLight.count", "Comment.count"], +1 do
+ conn.insert_fixtures_set(fixtures)
+ end
+ assert_equal 1, subscriber.events.size
+ ensure
+ ActiveSupport::Notifications.unsubscribe(subscription)
+ end
+ end
+
+ def test_auto_value_on_primary_key
+ fixtures = [
+ { "name" => "first", "wheels_count" => 2 },
+ { "name" => "second", "wheels_count" => 3 }
+ ]
+ conn = ActiveRecord::Base.connection
+ assert_nothing_raised do
+ conn.insert_fixtures_set({ "aircraft" => fixtures }, ["aircraft"])
+ end
+ result = conn.select_all("SELECT name, wheels_count FROM aircraft ORDER BY id")
+ assert_equal fixtures, result.to_a
+ end
+
+ def test_deprecated_insert_fixtures
+ fixtures = [
+ { "name" => "first", "wheels_count" => 2 },
+ { "name" => "second", "wheels_count" => 3 }
+ ]
+ conn = ActiveRecord::Base.connection
+ conn.delete("DELETE FROM aircraft")
+ assert_deprecated do
+ conn.insert_fixtures(fixtures, "aircraft")
+ end
+ result = conn.select_all("SELECT name, wheels_count FROM aircraft ORDER BY id")
+ assert_equal fixtures, result.to_a
end
def test_broken_yaml_exception
@@ -130,7 +277,7 @@ class FixturesTest < ActiveRecord::TestCase
def test_no_args_record_returns_all_without_array
all_binaries = binaries
assert_kind_of(Array, all_binaries)
- assert_equal 1, binaries.length
+ assert_equal 2, binaries.length
end
def test_nil_raises
@@ -245,8 +392,8 @@ class FixturesTest < ActiveRecord::TestCase
def test_nonexistent_fixture_file
nonexistent_fixture_path = FIXTURES_ROOT + "/imnothere"
- #sanity check to make sure that this file never exists
- assert Dir[nonexistent_fixture_path + "*"].empty?
+ # sanity check to make sure that this file never exists
+ assert_empty Dir[nonexistent_fixture_path + "*"]
assert_raise(Errno::ENOENT) do
ActiveRecord::FixtureSet.new(Account.connection, "companies", Company, nonexistent_fixture_path)
@@ -311,6 +458,7 @@ class FixturesTest < ActiveRecord::TestCase
data.force_encoding("ASCII-8BIT")
data.freeze
assert_equal data, @flowers.data
+ assert_equal data, @binary_helper.data
end
def test_serialized_fixtures
@@ -395,6 +543,7 @@ if Account.connection.respond_to?(:reset_pk_sequence!)
class FixturesResetPkSequenceTest < ActiveRecord::TestCase
fixtures :accounts
fixtures :companies
+ self.use_transactional_tests = false
def setup
@instances = [Account.new(credit_limit: 50), Company.new(name: "RoR Consulting"), Course.new(name: "Test")]
@@ -839,6 +988,8 @@ class FasterFixturesTest < ActiveRecord::TestCase
end
class FoxyFixturesTest < ActiveRecord::TestCase
+ # Set to false to blow away fixtures cache and ensure our fixtures are loaded
+ self.use_transactional_tests = false
fixtures :parrots, :parrots_pirates, :pirates, :treasures, :mateys, :ships, :computers,
:developers, :"admin/accounts", :"admin/users", :live_parrots, :dead_parrots, :books
@@ -992,10 +1143,10 @@ class FoxyFixturesTest < ActiveRecord::TestCase
end
def test_resolves_enums
- assert books(:awdr).published?
- assert books(:awdr).read?
- assert books(:rfr).proposed?
- assert books(:ddd).published?
+ assert_predicate books(:awdr), :published?
+ assert_predicate books(:awdr), :read?
+ assert_predicate books(:rfr), :proposed?
+ assert_predicate books(:ddd), :published?
end
end
diff --git a/activerecord/test/cases/forbidden_attributes_protection_test.rb b/activerecord/test/cases/forbidden_attributes_protection_test.rb
index ffa3f63e0d..101fa118c8 100644
--- a/activerecord/test/cases/forbidden_attributes_protection_test.rb
+++ b/activerecord/test/cases/forbidden_attributes_protection_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "cases/helper"
require "active_support/core_ext/hash/indifferent_access"
diff --git a/activerecord/test/cases/habtm_destroy_order_test.rb b/activerecord/test/cases/habtm_destroy_order_test.rb
index 365d4576dd..b15e1b48c4 100644
--- a/activerecord/test/cases/habtm_destroy_order_test.rb
+++ b/activerecord/test/cases/habtm_destroy_order_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "cases/helper"
require "models/lesson"
require "models/student"
@@ -13,7 +15,7 @@ class HabtmDestroyOrderTest < ActiveRecord::TestCase
sicp.destroy
end
end
- assert !sicp.destroyed?
+ assert_not_predicate sicp, :destroyed?
end
test "should not raise error if have foreign key in the join table" do
@@ -40,7 +42,7 @@ class HabtmDestroyOrderTest < ActiveRecord::TestCase
ben.lessons << sicp
ben.save!
ben.destroy
- assert !ben.reload.lessons.empty?
+ assert_not_empty ben.reload.lessons
ensure
# get rid of it so Student is still like it was
Student.reset_callbacks(:destroy)
@@ -56,6 +58,6 @@ class HabtmDestroyOrderTest < ActiveRecord::TestCase
assert_raises LessonError do
sicp.destroy
end
- assert !sicp.reload.students.empty?
+ assert_not_empty sicp.reload.students
end
end
diff --git a/activerecord/test/cases/helper.rb b/activerecord/test/cases/helper.rb
index 5a3b8e3fb5..6ea02ac191 100644
--- a/activerecord/test/cases/helper.rb
+++ b/activerecord/test/cases/helper.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "config"
require "stringio"
@@ -141,6 +143,8 @@ def load_schema
if File.exist?(adapter_specific_schema_file)
load adapter_specific_schema_file
end
+
+ ActiveRecord::FixtureSet.reset_cache
ensure
$stdout = original_stdout
end
diff --git a/activerecord/test/cases/hot_compatibility_test.rb b/activerecord/test/cases/hot_compatibility_test.rb
index e107ff2362..e7778af55b 100644
--- a/activerecord/test/cases/hot_compatibility_test.rb
+++ b/activerecord/test/cases/hot_compatibility_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "cases/helper"
require "support/connection_helper"
diff --git a/activerecord/test/cases/i18n_test.rb b/activerecord/test/cases/i18n_test.rb
index 7f03c5b23d..22981c142a 100644
--- a/activerecord/test/cases/i18n_test.rb
+++ b/activerecord/test/cases/i18n_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "cases/helper"
require "models/topic"
require "models/reply"
diff --git a/activerecord/test/cases/inheritance_test.rb b/activerecord/test/cases/inheritance_test.rb
index fb5a7bcc31..e3ca79af99 100644
--- a/activerecord/test/cases/inheritance_test.rb
+++ b/activerecord/test/cases/inheritance_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "cases/helper"
require "models/author"
require "models/company"
@@ -128,49 +130,54 @@ class InheritanceTest < ActiveRecord::TestCase
end
def test_descends_from_active_record
- assert !ActiveRecord::Base.descends_from_active_record?
+ assert_not_predicate ActiveRecord::Base, :descends_from_active_record?
# Abstract subclass of AR::Base.
- assert LoosePerson.descends_from_active_record?
+ assert_predicate LoosePerson, :descends_from_active_record?
# Concrete subclass of an abstract class.
- assert LooseDescendant.descends_from_active_record?
+ assert_predicate LooseDescendant, :descends_from_active_record?
# Concrete subclass of AR::Base.
- assert TightPerson.descends_from_active_record?
+ assert_predicate TightPerson, :descends_from_active_record?
# Concrete subclass of a concrete class but has no type column.
- assert TightDescendant.descends_from_active_record?
+ assert_predicate TightDescendant, :descends_from_active_record?
# Concrete subclass of AR::Base.
- assert Post.descends_from_active_record?
+ assert_predicate Post, :descends_from_active_record?
+
+ # Concrete subclasses of a concrete class which has a type column.
+ assert_not_predicate StiPost, :descends_from_active_record?
+ assert_not_predicate SubStiPost, :descends_from_active_record?
# Abstract subclass of a concrete class which has a type column.
# This is pathological, as you'll never have Sub < Abstract < Concrete.
- assert !StiPost.descends_from_active_record?
+ assert_not_predicate AbstractStiPost, :descends_from_active_record?
- # Concrete subclasses an abstract class which has a type column.
- assert !SubStiPost.descends_from_active_record?
+ # Concrete subclass of an abstract class which has a type column.
+ assert_not_predicate SubAbstractStiPost, :descends_from_active_record?
end
def test_company_descends_from_active_record
- assert !ActiveRecord::Base.descends_from_active_record?
+ assert_not_predicate ActiveRecord::Base, :descends_from_active_record?
assert AbstractCompany.descends_from_active_record?, "AbstractCompany should descend from ActiveRecord::Base"
assert Company.descends_from_active_record?, "Company should descend from ActiveRecord::Base"
assert !Class.new(Company).descends_from_active_record?, "Company subclass should not descend from ActiveRecord::Base"
end
def test_abstract_class
- assert !ActiveRecord::Base.abstract_class?
- assert LoosePerson.abstract_class?
- assert !LooseDescendant.abstract_class?
+ assert_not_predicate ActiveRecord::Base, :abstract_class?
+ assert_predicate LoosePerson, :abstract_class?
+ assert_not_predicate LooseDescendant, :abstract_class?
end
def test_inheritance_base_class
assert_equal Post, Post.base_class
assert_equal Post, SpecialPost.base_class
assert_equal Post, StiPost.base_class
- assert_equal SubStiPost, SubStiPost.base_class
+ assert_equal Post, SubStiPost.base_class
+ assert_equal SubAbstractStiPost, SubAbstractStiPost.base_class
end
def test_abstract_inheritance_base_class
@@ -273,6 +280,21 @@ class InheritanceTest < ActiveRecord::TestCase
assert_equal Firm, firm.class
end
+ def test_where_new_with_subclass
+ firm = Company.where(type: "Firm").new
+ assert_equal Firm, firm.class
+ end
+
+ def test_where_create_with_subclass
+ firm = Company.where(type: "Firm").create(name: "Basecamp")
+ assert_equal Firm, firm.class
+ end
+
+ def test_where_create_bang_with_subclass
+ firm = Company.where(type: "Firm").create!(name: "Basecamp")
+ assert_equal Firm, firm.class
+ end
+
def test_new_with_abstract_class
e = assert_raises(NotImplementedError) do
AbstractCompany.new
@@ -295,6 +317,30 @@ class InheritanceTest < ActiveRecord::TestCase
assert_raise(ActiveRecord::SubclassNotFound) { Company.new(type: "Account") }
end
+ def test_where_new_with_invalid_type
+ assert_raise(ActiveRecord::SubclassNotFound) { Company.where(type: "InvalidType").new }
+ end
+
+ def test_where_new_with_unrelated_type
+ assert_raise(ActiveRecord::SubclassNotFound) { Company.where(type: "Account").new }
+ end
+
+ def test_where_create_with_invalid_type
+ assert_raise(ActiveRecord::SubclassNotFound) { Company.where(type: "InvalidType").create }
+ end
+
+ def test_where_create_with_unrelated_type
+ assert_raise(ActiveRecord::SubclassNotFound) { Company.where(type: "Account").create }
+ end
+
+ def test_where_create_bang_with_invalid_type
+ assert_raise(ActiveRecord::SubclassNotFound) { Company.where(type: "InvalidType").create! }
+ end
+
+ def test_where_create_bang_with_unrelated_type
+ assert_raise(ActiveRecord::SubclassNotFound) { Company.where(type: "Account").create! }
+ end
+
def test_new_with_unrelated_namespaced_type
without_store_full_sti_class do
e = assert_raises ActiveRecord::SubclassNotFound do
@@ -418,7 +464,8 @@ class InheritanceTest < ActiveRecord::TestCase
def test_eager_load_belongs_to_primary_key_quoting
con = Account.connection
- assert_sql(/#{con.quote_table_name('companies')}\.#{con.quote_column_name('id')} = 1/) do
+ bind_param = Arel::Nodes::BindParam.new(nil)
+ assert_sql(/#{con.quote_table_name('companies')}\.#{con.quote_column_name('id')} = (?:#{Regexp.quote(bind_param.to_sql)}|1)/) do
Account.all.merge!(includes: :firm).find(1)
end
end
diff --git a/activerecord/test/cases/instrumentation_test.rb b/activerecord/test/cases/instrumentation_test.rb
new file mode 100644
index 0000000000..e6e8468757
--- /dev/null
+++ b/activerecord/test/cases/instrumentation_test.rb
@@ -0,0 +1,72 @@
+# frozen_string_literal: true
+
+require "cases/helper"
+require "models/book"
+
+module ActiveRecord
+ class InstrumentationTest < ActiveRecord::TestCase
+ def test_payload_name_on_load
+ Book.create(name: "test book")
+ subscriber = ActiveSupport::Notifications.subscribe("sql.active_record") do |*args|
+ event = ActiveSupport::Notifications::Event.new(*args)
+ if event.payload[:sql].match "SELECT"
+ assert_equal "Book Load", event.payload[:name]
+ end
+ end
+ Book.first
+ ensure
+ ActiveSupport::Notifications.unsubscribe(subscriber) if subscriber
+ end
+
+ def test_payload_name_on_create
+ subscriber = ActiveSupport::Notifications.subscribe("sql.active_record") do |*args|
+ event = ActiveSupport::Notifications::Event.new(*args)
+ if event.payload[:sql].match "INSERT"
+ assert_equal "Book Create", event.payload[:name]
+ end
+ end
+ Book.create(name: "test book")
+ ensure
+ ActiveSupport::Notifications.unsubscribe(subscriber) if subscriber
+ end
+
+ def test_payload_name_on_update
+ subscriber = ActiveSupport::Notifications.subscribe("sql.active_record") do |*args|
+ event = ActiveSupport::Notifications::Event.new(*args)
+ if event.payload[:sql].match "UPDATE"
+ assert_equal "Book Update", event.payload[:name]
+ end
+ end
+ book = Book.create(name: "test book")
+ book.update_attribute(:name, "new name")
+ ensure
+ ActiveSupport::Notifications.unsubscribe(subscriber) if subscriber
+ end
+
+ def test_payload_name_on_update_all
+ subscriber = ActiveSupport::Notifications.subscribe("sql.active_record") do |*args|
+ event = ActiveSupport::Notifications::Event.new(*args)
+ if event.payload[:sql].match "UPDATE"
+ assert_equal "Book Update All", event.payload[:name]
+ end
+ end
+ Book.create(name: "test book")
+ Book.update_all(name: "new name")
+ ensure
+ ActiveSupport::Notifications.unsubscribe(subscriber) if subscriber
+ end
+
+ def test_payload_name_on_destroy
+ subscriber = ActiveSupport::Notifications.subscribe("sql.active_record") do |*args|
+ event = ActiveSupport::Notifications::Event.new(*args)
+ if event.payload[:sql].match "DELETE"
+ assert_equal "Book Destroy", event.payload[:name]
+ end
+ end
+ book = Book.create(name: "test book")
+ book.destroy
+ ensure
+ ActiveSupport::Notifications.unsubscribe(subscriber) if subscriber
+ end
+ end
+end
diff --git a/activerecord/test/cases/integration_test.rb b/activerecord/test/cases/integration_test.rb
index 9104976126..36cd63c4d4 100644
--- a/activerecord/test/cases/integration_test.rb
+++ b/activerecord/test/cases/integration_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "cases/helper"
require "models/company"
require "models/developer"
diff --git a/activerecord/test/cases/invalid_connection_test.rb b/activerecord/test/cases/invalid_connection_test.rb
index 1367af2859..a1be9c2780 100644
--- a/activerecord/test/cases/invalid_connection_test.rb
+++ b/activerecord/test/cases/invalid_connection_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "cases/helper"
if current_adapter?(:Mysql2Adapter)
diff --git a/activerecord/test/cases/invertible_migration_test.rb b/activerecord/test/cases/invertible_migration_test.rb
index cc3951e2ba..ebe0b0aa87 100644
--- a/activerecord/test/cases/invertible_migration_test.rb
+++ b/activerecord/test/cases/invertible_migration_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "cases/helper"
class Horse < ActiveRecord::Base
@@ -159,6 +161,15 @@ module ActiveRecord
end
end
+ class UpOnlyMigration < SilentMigration
+ def change
+ add_column :horses, :oldie, :integer, default: 0
+ up_only { execute "update horses set oldie = 1" }
+ end
+ end
+
+ self.use_transactional_tests = false
+
setup do
@verbose_was, ActiveRecord::Migration.verbose = ActiveRecord::Migration.verbose, false
end
@@ -293,6 +304,8 @@ module ActiveRecord
migration2.migrate(:down)
assert_equal false, Horse.connection.extension_enabled?("hstore")
+ ensure
+ enable_extension!("hstore", ActiveRecord::Base.connection)
end
end
@@ -374,5 +387,23 @@ module ActiveRecord
"horses_index_named index should not exist"
end
end
+
+ def test_up_only
+ InvertibleMigration.new.migrate(:up)
+ horse1 = Horse.create
+ # populates existing horses with oldie = 1 but new ones have default 0
+ UpOnlyMigration.new.migrate(:up)
+ Horse.reset_column_information
+ horse1.reload
+ horse2 = Horse.create
+
+ assert 1, horse1.oldie # created before migration
+ assert 0, horse2.oldie # created after migration
+
+ UpOnlyMigration.new.migrate(:down) # should be no error
+ connection = ActiveRecord::Base.connection
+ assert !connection.column_exists?(:horses, :oldie)
+ Horse.reset_column_information
+ end
end
end
diff --git a/activerecord/test/cases/json_attribute_test.rb b/activerecord/test/cases/json_attribute_test.rb
index e5848b45f8..afc39d0420 100644
--- a/activerecord/test/cases/json_attribute_test.rb
+++ b/activerecord/test/cases/json_attribute_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "cases/helper"
require "cases/json_shared_test_cases"
@@ -17,14 +19,14 @@ class JsonAttributeTest < ActiveRecord::TestCase
def setup
super
@connection.create_table("json_data_type") do |t|
- t.text "payload"
- t.text "settings"
+ t.string "payload"
+ t.string "settings"
end
end
private
def column_type
- :text
+ :string
end
def klass
diff --git a/activerecord/test/cases/json_serialization_test.rb b/activerecord/test/cases/json_serialization_test.rb
index 9b4b61b16e..82cf281cff 100644
--- a/activerecord/test/cases/json_serialization_test.rb
+++ b/activerecord/test/cases/json_serialization_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "cases/helper"
require "models/contact"
require "models/post"
@@ -160,10 +162,8 @@ class JsonSerializationTest < ActiveRecord::TestCase
end
def test_serializable_hash_should_not_modify_options_in_argument
- options = { only: :name }
- @contact.serializable_hash(options)
-
- assert_nil options[:except]
+ options = { only: :name }.freeze
+ assert_nothing_raised { @contact.serializable_hash(options) }
end
end
@@ -252,7 +252,7 @@ class DatabaseConnectedJsonEncodingTest < ActiveRecord::TestCase
def @david.favorite_quote; "Constraints are liberating"; end
json = @david.to_json(include: :posts, methods: :favorite_quote)
- assert !@david.posts.first.respond_to?(: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
diff --git a/activerecord/test/cases/json_shared_test_cases.rb b/activerecord/test/cases/json_shared_test_cases.rb
index 9a1c1c3f3f..c60a276850 100644
--- a/activerecord/test/cases/json_shared_test_cases.rb
+++ b/activerecord/test/cases/json_shared_test_cases.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "support/schema_dumping_helper"
module JSONSharedTestCases
@@ -21,10 +23,10 @@ module JSONSharedTestCases
def test_column
column = klass.columns_hash["payload"]
assert_equal column_type, column.type
- assert_equal column_type.to_s, column.sql_type
+ assert_type_match column_type, column.sql_type
type = klass.type_for_attribute("payload")
- assert_not type.binary?
+ assert_not_predicate type, :binary?
end
def test_change_table_supports_json
@@ -34,7 +36,7 @@ module JSONSharedTestCases
klass.reset_column_information
column = klass.columns_hash["users"]
assert_equal column_type, column.type
- assert_equal column_type.to_s, column.sql_type
+ assert_type_match column_type, column.sql_type
end
def test_schema_dumping
@@ -64,26 +66,26 @@ module JSONSharedTestCases
end
def test_rewrite
- @connection.execute(%q|insert into json_data_type (payload) VALUES ('{"k":"v"}')|)
+ @connection.execute(insert_statement_per_database('{"k":"v"}'))
x = klass.first
x.payload = { '"a\'' => "b" }
assert x.save!
end
def test_select
- @connection.execute(%q|insert into json_data_type (payload) VALUES ('{"k":"v"}')|)
+ @connection.execute(insert_statement_per_database('{"k":"v"}'))
x = klass.first
assert_equal({ "k" => "v" }, x.payload)
end
def test_select_multikey
- @connection.execute(%q|insert into json_data_type (payload) VALUES ('{"k1":"v1", "k2":"v2", "k3":[1,2,3]}')|)
+ @connection.execute(insert_statement_per_database('{"k1":"v1", "k2":"v2", "k3":[1,2,3]}'))
x = klass.first
assert_equal({ "k1" => "v1", "k2" => "v2", "k3" => [1, 2, 3] }, x.payload)
end
def test_null_json
- @connection.execute("insert into json_data_type (payload) VALUES(null)")
+ @connection.execute(insert_statement_per_database("null"))
x = klass.first
assert_nil(x.payload)
end
@@ -105,13 +107,13 @@ module JSONSharedTestCases
end
def test_select_array_json_value
- @connection.execute(%q|insert into json_data_type (payload) VALUES ('["v0",{"k1":"v1"}]')|)
+ @connection.execute(insert_statement_per_database('["v0",{"k1":"v1"}]'))
x = klass.first
assert_equal(["v0", { "k1" => "v1" }], x.payload)
end
def test_rewrite_array_json_value
- @connection.execute(%q|insert into json_data_type (payload) VALUES ('["v0",{"k1":"v1"}]')|)
+ @connection.execute(insert_statement_per_database('["v0",{"k1":"v1"}]'))
x = klass.first
x.payload = ["v1", { "k2" => "v2" }, "v3"]
assert x.save!
@@ -150,42 +152,42 @@ module JSONSharedTestCases
def test_changes_in_place
json = klass.new
- assert_not json.changed?
+ assert_not_predicate json, :changed?
json.payload = { "one" => "two" }
- assert json.changed?
- assert json.payload_changed?
+ assert_predicate json, :changed?
+ assert_predicate json, :payload_changed?
json.save!
- assert_not json.changed?
+ assert_not_predicate json, :changed?
json.payload["three"] = "four"
- assert json.payload_changed?
+ assert_predicate json, :payload_changed?
json.save!
json.reload
assert_equal({ "one" => "two", "three" => "four" }, json.payload)
- assert_not json.changed?
+ assert_not_predicate json, :changed?
end
def test_changes_in_place_ignores_key_order
json = klass.new
- assert_not json.changed?
+ assert_not_predicate json, :changed?
json.payload = { "three" => "four", "one" => "two" }
json.save!
json.reload
json.payload = { "three" => "four", "one" => "two" }
- assert_not json.changed?
+ assert_not_predicate json, :changed?
json.payload = [{ "three" => "four", "one" => "two" }, { "seven" => "eight", "five" => "six" }]
json.save!
json.reload
json.payload = [{ "three" => "four", "one" => "two" }, { "seven" => "eight", "five" => "six" }]
- assert_not json.changed?
+ assert_not_predicate json, :changed?
end
def test_changes_in_place_with_ruby_object
@@ -193,10 +195,10 @@ module JSONSharedTestCases
json = klass.create!(payload: time)
json.reload
- assert_not json.changed?
+ assert_not_predicate json, :changed?
json.payload = time
- assert_not json.changed?
+ assert_not_predicate json, :changed?
end
def test_assigning_string_literal
@@ -214,8 +216,54 @@ module JSONSharedTestCases
assert_equal true, json.payload
end
+ def test_not_compatible_with_serialize_json
+ new_klass = Class.new(klass) do
+ serialize :payload, JSON
+ end
+ assert_raises(ActiveRecord::AttributeMethods::Serialization::ColumnNotSerializableError) do
+ new_klass.new
+ end
+ end
+
+ class MySettings
+ def initialize(hash); @hash = hash end
+ def to_hash; @hash end
+ def self.load(hash); new(hash) end
+ def self.dump(object); object.to_hash end
+ end
+
+ def test_json_with_serialized_attributes
+ new_klass = Class.new(klass) do
+ serialize :settings, MySettings
+ end
+
+ new_klass.create!(settings: MySettings.new("one" => "two"))
+ record = new_klass.first
+
+ assert_instance_of MySettings, record.settings
+ assert_equal({ "one" => "two" }, record.settings.to_hash)
+
+ record.settings = MySettings.new("three" => "four")
+ record.save!
+
+ assert_equal({ "three" => "four" }, record.reload.settings.to_hash)
+ end
+
private
def klass
JsonDataType
end
+
+ def assert_type_match(type, sql_type)
+ native_type = ActiveRecord::Base.connection.native_database_types[type][:name]
+ assert_match %r(\A#{native_type}\b), sql_type
+ end
+
+ def insert_statement_per_database(values)
+ if current_adapter?(:OracleAdapter)
+ "insert into json_data_type (id, payload) VALUES (json_data_type_seq.nextval, '#{values}')"
+ else
+ "insert into json_data_type (payload) VALUES ('#{values}')"
+ end
+ end
end
diff --git a/activerecord/test/cases/locking_test.rb b/activerecord/test/cases/locking_test.rb
index 2fc52393f2..bb76137ef5 100644
--- a/activerecord/test/cases/locking_test.rb
+++ b/activerecord/test/cases/locking_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "thread"
require "cases/helper"
require "models/person"
@@ -67,8 +69,8 @@ class OptimisticLockingTest < ActiveRecord::TestCase
assert_raise(ActiveRecord::StaleObjectError) { s2.destroy }
assert s1.destroy
- assert s1.frozen?
- assert s1.destroyed?
+ assert_predicate s1, :frozen?
+ assert_predicate s1, :destroyed?
assert_raises(ActiveRecord::RecordNotFound) { StringKeyObject.find("record1") }
end
@@ -102,8 +104,8 @@ class OptimisticLockingTest < ActiveRecord::TestCase
assert_raises(ActiveRecord::StaleObjectError) { p2.destroy }
assert p1.destroy
- assert p1.frozen?
- assert p1.destroyed?
+ assert_predicate p1, :frozen?
+ assert_predicate p1, :destroyed?
assert_raises(ActiveRecord::RecordNotFound) { Person.find(1) }
end
@@ -199,7 +201,7 @@ class OptimisticLockingTest < ActiveRecord::TestCase
person.first_name = "Douglas Adams"
person.lock_version = 42
- assert person.lock_version_changed?
+ assert_predicate person, :lock_version_changed?
person.save
end
@@ -397,25 +399,57 @@ class OptimisticLockingTest < ActiveRecord::TestCase
end
end
+ def test_counter_cache_with_touch_and_lock_version
+ car = Car.create!
+
+ assert_equal 0, car.wheels_count
+ assert_equal 0, car.lock_version
+
+ previously_car_updated_at = car.updated_at
+ travel(2.second) do
+ Wheel.create!(wheelable: car)
+ end
+
+ assert_equal 1, car.reload.wheels_count
+ assert_not_equal previously_car_updated_at, car.updated_at
+ assert_equal 1, car.lock_version
+
+ previously_car_updated_at = car.updated_at
+ car.wheels.first.update(size: 42)
+
+ assert_equal 1, car.reload.wheels_count
+ assert_not_equal previously_car_updated_at, car.updated_at
+ assert_equal 2, car.lock_version
+
+ previously_car_updated_at = car.updated_at
+ travel(2.second) do
+ car.wheels.first.destroy!
+ end
+
+ assert_equal 0, car.reload.wheels_count
+ assert_not_equal previously_car_updated_at, car.updated_at
+ assert_equal 3, car.lock_version
+ end
+
def test_polymorphic_destroy_with_dependencies_and_lock_version
car = Car.create!
assert_difference "car.wheels.count" do
- car.wheels << Wheel.create!
+ car.wheels.create
end
assert_difference "car.wheels.count", -1 do
car.reload.destroy
end
- assert car.destroyed?
+ assert_predicate car, :destroyed?
end
def test_removing_has_and_belongs_to_many_associations_upon_destroy
p = RichPerson.create! first_name: "Jon"
p.treasures.create!
- assert !p.treasures.empty?
+ assert_not_empty p.treasures
p.destroy
- assert p.treasures.empty?
- assert RichPerson.connection.select_all("SELECT * FROM peoples_treasures WHERE rich_person_id = 1").empty?
+ assert_empty p.treasures
+ assert_empty RichPerson.connection.select_all("SELECT * FROM peoples_treasures WHERE rich_person_id = 1")
end
def test_yaml_dumping_with_lock_column
@@ -485,7 +519,7 @@ class OptimisticLockingWithSchemaChangeTest < ActiveRecord::TestCase
t1.destroy
- assert t1.destroyed?
+ assert_predicate t1, :destroyed?
end
def test_destroy_stale_object
@@ -498,7 +532,7 @@ class OptimisticLockingWithSchemaChangeTest < ActiveRecord::TestCase
stale_object.destroy!
end
- refute stale_object.destroyed?
+ assert_not_predicate stale_object, :destroyed?
end
private
@@ -563,18 +597,18 @@ unless in_memory_db?
end
end
- # Locking a record reloads it.
- def test_sane_lock_method
+ def test_lock_does_not_raise_when_the_object_is_not_dirty
+ person = Person.find 1
assert_nothing_raised do
- Person.transaction do
- person = Person.find 1
- old, person.first_name = person.first_name, "fooman"
- # Locking a dirty record is deprecated
- assert_deprecated do
- person.lock!
- end
- assert_equal old, person.first_name
- end
+ person.lock!
+ end
+ end
+
+ def test_lock_raises_when_the_record_is_dirty
+ person = Person.find 1
+ person.first_name = "fooman"
+ assert_raises(RuntimeError) do
+ person.lock!
end
end
@@ -609,14 +643,12 @@ unless in_memory_db?
end
end
- if current_adapter?(:PostgreSQLAdapter, :OracleAdapter)
- def test_no_locks_no_wait
- first, second = duel { Person.find 1 }
- assert first.end > second.end
- end
-
- private
+ def test_no_locks_no_wait
+ first, second = duel { Person.find 1 }
+ assert first.end > second.end
+ end
+ private
def duel(zzz = 5)
t0, t1, t2, t3 = nil, nil, nil, nil
@@ -644,6 +676,5 @@ unless in_memory_db?
assert t3 > t2
[t0.to_f..t1.to_f, t2.to_f..t3.to_f]
end
- end
end
end
diff --git a/activerecord/test/cases/log_subscriber_test.rb b/activerecord/test/cases/log_subscriber_test.rb
index b80257962c..e2742ed33e 100644
--- a/activerecord/test/cases/log_subscriber_test.rb
+++ b/activerecord/test/cases/log_subscriber_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "cases/helper"
require "models/binary"
require "models/developer"
@@ -31,8 +33,9 @@ class LogSubscriberTest < ActiveRecord::TestCase
super
end
- def debug(message)
- @debugs << message
+ def debug(progname = nil, &block)
+ @debugs << progname
+ super
end
end
@@ -169,6 +172,22 @@ class LogSubscriberTest < ActiveRecord::TestCase
assert_match(/SELECT .*?FROM .?developers.?/i, @logger.logged(:debug).last)
end
+ def test_vebose_query_logs
+ ActiveRecord::Base.verbose_query_logs = true
+
+ logger = TestDebugLogSubscriber.new
+ logger.sql(Event.new(0, sql: "hi mom!"))
+ assert_match(/↳/, @logger.logged(:debug).last)
+ ensure
+ ActiveRecord::Base.verbose_query_logs = false
+ end
+
+ def test_verbose_query_logs_disabled_by_default
+ logger = TestDebugLogSubscriber.new
+ logger.sql(Event.new(0, sql: "hi mom!"))
+ assert_no_match(/↳/, @logger.logged(:debug).last)
+ end
+
def test_cached_queries
ActiveRecord::Base.cache do
Developer.all.load
diff --git a/activerecord/test/cases/migration/change_schema_test.rb b/activerecord/test/cases/migration/change_schema_test.rb
index 1d305fa11f..1494027182 100644
--- a/activerecord/test/cases/migration/change_schema_test.rb
+++ b/activerecord/test/cases/migration/change_schema_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "cases/helper"
module ActiveRecord
@@ -82,7 +84,7 @@ module ActiveRecord
columns = connection.columns(:testings)
array_column = columns.detect { |c| c.name == "foo" }
- assert array_column.array?
+ assert_predicate array_column, :array?
end
def test_create_table_with_array_column
@@ -93,7 +95,7 @@ module ActiveRecord
columns = connection.columns(:testings)
array_column = columns.detect { |c| c.name == "foo" }
- assert array_column.array?
+ assert_predicate array_column, :array?
end
end
@@ -262,17 +264,18 @@ module ActiveRecord
t.column :foo, :timestamp
end
- klass = Class.new(ActiveRecord::Base)
- klass.table_name = "testings"
+ column = connection.columns(:testings).find { |c| c.name == "foo" }
- assert_equal :datetime, klass.columns_hash["foo"].type
+ assert_equal :datetime, column.type
if current_adapter?(:PostgreSQLAdapter)
- assert_equal "timestamp without time zone", klass.columns_hash["foo"].sql_type
+ assert_equal "timestamp without time zone", column.sql_type
elsif current_adapter?(:Mysql2Adapter)
- assert_equal "timestamp", klass.columns_hash["foo"].sql_type
+ assert_equal "timestamp", column.sql_type
+ elsif current_adapter?(:OracleAdapter)
+ assert_equal "TIMESTAMP(6)", column.sql_type
else
- assert_equal klass.connection.type_to_sql("datetime"), klass.columns_hash["foo"].sql_type
+ assert_equal connection.type_to_sql("datetime"), column.sql_type
end
end
diff --git a/activerecord/test/cases/migration/change_table_test.rb b/activerecord/test/cases/migration/change_table_test.rb
index ec817a579b..034bf32165 100644
--- a/activerecord/test/cases/migration/change_table_test.rb
+++ b/activerecord/test/cases/migration/change_table_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "cases/migration/helper"
module ActiveRecord
diff --git a/activerecord/test/cases/migration/column_attributes_test.rb b/activerecord/test/cases/migration/column_attributes_test.rb
index 48df931543..3022121f4c 100644
--- a/activerecord/test/cases/migration/column_attributes_test.rb
+++ b/activerecord/test/cases/migration/column_attributes_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "cases/migration/helper"
module ActiveRecord
@@ -43,11 +45,11 @@ module ActiveRecord
assert_nil TestModel.columns_hash["description"].limit
end
- if current_adapter?(:Mysql2Adapter)
+ if current_adapter?(:Mysql2Adapter, :PostgreSQLAdapter)
def test_unabstracted_database_dependent_types
- add_column :test_models, :intelligence_quotient, :tinyint
+ add_column :test_models, :intelligence_quotient, :smallint
TestModel.reset_column_information
- assert_match(/tinyint/, TestModel.columns_hash["intelligence_quotient"].sql_type)
+ assert_match(/smallint/, TestModel.columns_hash["intelligence_quotient"].sql_type)
end
end
@@ -78,7 +80,7 @@ module ActiveRecord
TestModel.delete_all
# Now use the Rails insertion
- TestModel.create wealth: BigDecimal.new("12345678901234567890.0123456789")
+ TestModel.create wealth: BigDecimal("12345678901234567890.0123456789")
# SELECT
row = TestModel.first
@@ -97,7 +99,21 @@ module ActiveRecord
assert_equal 7, wealth_column.scale
end
+ # Test SQLite3 adapter specifically for decimal types with precision and scale
+ # attributes, since these need to be maintained in schema but aren't actually
+ # used in SQLite3 itself
if current_adapter?(:SQLite3Adapter)
+ def test_change_column_with_new_precision_and_scale
+ connection.add_column "test_models", "wealth", :decimal, precision: 9, scale: 7
+
+ connection.change_column "test_models", "wealth", :decimal, precision: 12, scale: 8
+ TestModel.reset_column_information
+
+ wealth_column = TestModel.columns_hash["wealth"]
+ assert_equal 12, wealth_column.precision
+ assert_equal 8, wealth_column.scale
+ end
+
def test_change_column_preserve_other_column_precision_and_scale
connection.add_column "test_models", "last_name", :string
connection.add_column "test_models", "wealth", :decimal, precision: 9, scale: 7
@@ -130,7 +146,7 @@ module ActiveRecord
TestModel.create first_name: "bob", last_name: "bobsen",
bio: "I was born ....", age: 18, height: 1.78,
- wealth: BigDecimal.new("12345678901234567890.0123456789"),
+ wealth: BigDecimal("12345678901234567890.0123456789"),
birthday: 18.years.ago, favorite_day: 10.days.ago,
moment_of_truth: "1782-10-10 21:40:18", male: true
@@ -143,7 +159,7 @@ module ActiveRecord
# Test for 30 significant digits (beyond the 16 of float), 10 of them
# after the decimal place.
- assert_equal BigDecimal.new("0012345678901234567890.0123456789"), bob.wealth
+ assert_equal BigDecimal("0012345678901234567890.0123456789"), bob.wealth
assert_equal true, bob.male?
diff --git a/activerecord/test/cases/migration/column_positioning_test.rb b/activerecord/test/cases/migration/column_positioning_test.rb
index f2162d91b1..1c62a68cf9 100644
--- a/activerecord/test/cases/migration/column_positioning_test.rb
+++ b/activerecord/test/cases/migration/column_positioning_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "cases/helper"
module ActiveRecord
@@ -50,6 +52,16 @@ module ActiveRecord
conn.change_column :testings, :second, :integer, after: :third
assert_equal %w(first third second), conn.columns(:testings).map(&:name)
end
+
+ def test_add_reference_with_positioning_first
+ conn.add_reference :testings, :new, polymorphic: true, first: true
+ assert_equal %w(new_id new_type first second third), conn.columns(:testings).map(&:name)
+ end
+
+ def test_add_reference_with_positioning_after
+ conn.add_reference :testings, :new, polymorphic: true, after: :first
+ assert_equal %w(first new_id new_type second third), conn.columns(:testings).map(&:name)
+ end
end
end
end
diff --git a/activerecord/test/cases/migration/columns_test.rb b/activerecord/test/cases/migration/columns_test.rb
index 2329888345..cedd9c44e3 100644
--- a/activerecord/test/cases/migration/columns_test.rb
+++ b/activerecord/test/cases/migration/columns_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "cases/migration/helper"
module ActiveRecord
@@ -65,7 +67,7 @@ module ActiveRecord
if current_adapter?(:Mysql2Adapter)
def test_mysql_rename_column_preserves_auto_increment
rename_column "test_models", "id", "id_test"
- assert connection.columns("test_models").find { |c| c.name == "id_test" }.auto_increment?
+ assert_predicate connection.columns("test_models").find { |c| c.name == "id_test" }, :auto_increment?
TestModel.reset_column_information
ensure
rename_column "test_models", "id_test", "id"
@@ -140,6 +142,10 @@ module ActiveRecord
end
def test_remove_column_with_multi_column_index
+ # MariaDB starting with 10.2.8
+ # Dropping a column that is part of a multi-column UNIQUE constraint is not permitted.
+ skip if current_adapter?(:Mysql2Adapter) && connection.mariadb? && connection.version >= "10.2.8"
+
add_column "test_models", :hat_size, :integer
add_column "test_models", :hat_style, :string, limit: 100
add_index "test_models", ["hat_style", "hat_size"], unique: true
@@ -217,31 +223,31 @@ module ActiveRecord
def test_change_column_with_nil_default
add_column "test_models", "contributor", :boolean, default: true
- assert TestModel.new.contributor?
+ assert_predicate TestModel.new, :contributor?
change_column "test_models", "contributor", :boolean, default: nil
TestModel.reset_column_information
- assert_not TestModel.new.contributor?
+ assert_not_predicate TestModel.new, :contributor?
assert_nil TestModel.new.contributor
end
def test_change_column_to_drop_default_with_null_false
add_column "test_models", "contributor", :boolean, default: true, null: false
- assert TestModel.new.contributor?
+ assert_predicate TestModel.new, :contributor?
change_column "test_models", "contributor", :boolean, default: nil, null: false
TestModel.reset_column_information
- assert_not TestModel.new.contributor?
+ assert_not_predicate TestModel.new, :contributor?
assert_nil TestModel.new.contributor
end
def test_change_column_with_new_default
add_column "test_models", "administrator", :boolean, default: true
- assert TestModel.new.administrator?
+ assert_predicate TestModel.new, :administrator?
change_column "test_models", "administrator", :boolean, default: false
TestModel.reset_column_information
- assert_not TestModel.new.administrator?
+ assert_not_predicate TestModel.new, :administrator?
end
def test_change_column_with_custom_index_name
diff --git a/activerecord/test/cases/migration/command_recorder_test.rb b/activerecord/test/cases/migration/command_recorder_test.rb
index 007926f1b9..3a11bb081b 100644
--- a/activerecord/test/cases/migration/command_recorder_test.rb
+++ b/activerecord/test/cases/migration/command_recorder_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "cases/helper"
module ActiveRecord
@@ -12,7 +14,7 @@ module ActiveRecord
recorder = CommandRecorder.new(Class.new {
def america; end
}.new)
- assert recorder.respond_to?(:america)
+ assert_respond_to recorder, :america
end
def test_send_calls_super
@@ -25,7 +27,7 @@ module ActiveRecord
recorder = CommandRecorder.new(Class.new {
def create_table(name); end
}.new)
- assert recorder.respond_to?(:create_table), "respond_to? create_table"
+ assert_respond_to recorder, :create_table
recorder.send(:create_table, :horses)
assert_equal [[:create_table, [:horses], nil]], recorder.commands
end
@@ -211,6 +213,11 @@ module ActiveRecord
assert_equal [:remove_index, [:table, { name: "new_index" }]], remove
end
+ def test_invert_add_index_with_algorithm_option
+ remove = @recorder.inverse_of :add_index, [:table, :one, algorithm: :concurrently]
+ assert_equal [:remove_index, [:table, { column: :one, algorithm: :concurrently }]], remove
+ end
+
def test_invert_remove_index
add = @recorder.inverse_of :remove_index, [:table, :one]
assert_equal [:add_index, [:table, :one]], add
diff --git a/activerecord/test/cases/migration/compatibility_test.rb b/activerecord/test/cases/migration/compatibility_test.rb
index 596a21dcbc..69a50674af 100644
--- a/activerecord/test/cases/migration/compatibility_test.rb
+++ b/activerecord/test/cases/migration/compatibility_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "cases/helper"
require "support/schema_dumping_helper"
@@ -14,7 +16,7 @@ module ActiveRecord
ActiveRecord::Migration.verbose = false
connection.create_table :testings do |t|
- t.column :foo, :string, limit: 100
+ t.column :foo, :string, limit: 5
t.column :bar, :string, limit: 100
end
end
@@ -124,15 +126,32 @@ module ActiveRecord
end
assert_match(/LegacyMigration < ActiveRecord::Migration\[4\.2\]/, e.message)
end
+
+ if current_adapter?(:PostgreSQLAdapter)
+ class Testing < ActiveRecord::Base
+ end
+
+ def test_legacy_change_column_with_null_executes_update
+ migration = Class.new(ActiveRecord::Migration[5.1]) {
+ def migrate(x)
+ change_column :testings, :foo, :string, limit: 10, null: false, default: "foobar"
+ end
+ }.new
+
+ Testing.create!
+ ActiveRecord::Migrator.new(:up, [migration]).migrate
+ assert_equal ["foobar"], Testing.all.map(&:foo)
+ ensure
+ ActiveRecord::Base.clear_cache!
+ end
+ end
end
end
end
-class LegacyPrimaryKeyTest < ActiveRecord::TestCase
+module LegacyPrimaryKeyTestCases
include SchemaDumpingHelper
- self.use_transactional_tests = false
-
class LegacyPrimaryKey < ActiveRecord::Base
end
@@ -150,7 +169,7 @@ class LegacyPrimaryKeyTest < ActiveRecord::TestCase
end
def test_legacy_primary_key_should_be_auto_incremented
- @migration = Class.new(ActiveRecord::Migration[5.0]) {
+ @migration = Class.new(migration_class) {
def change
create_table :legacy_primary_keys do |t|
t.references :legacy_ref
@@ -160,12 +179,10 @@ class LegacyPrimaryKeyTest < ActiveRecord::TestCase
@migration.migrate(:up)
- legacy_pk = LegacyPrimaryKey.columns_hash["id"]
- assert_not legacy_pk.bigint?
- assert_not legacy_pk.null
+ assert_legacy_primary_key
legacy_ref = LegacyPrimaryKey.columns_hash["legacy_ref_id"]
- assert_not legacy_ref.bigint?
+ assert_not_predicate legacy_ref, :bigint?
record1 = LegacyPrimaryKey.create!
assert_not_nil record1.id
@@ -180,7 +197,7 @@ class LegacyPrimaryKeyTest < ActiveRecord::TestCase
def test_legacy_integer_primary_key_should_not_be_auto_incremented
skip if current_adapter?(:SQLite3Adapter)
- @migration = Class.new(ActiveRecord::Migration[5.0]) {
+ @migration = Class.new(migration_class) {
def change
create_table :legacy_primary_keys, id: :integer do |t|
end
@@ -197,9 +214,85 @@ class LegacyPrimaryKeyTest < ActiveRecord::TestCase
assert_match %r{create_table "legacy_primary_keys", id: :integer, default: nil}, schema
end
+ def test_legacy_primary_key_in_create_table_should_be_integer
+ @migration = Class.new(migration_class) {
+ def change
+ create_table :legacy_primary_keys, id: false do |t|
+ t.primary_key :id
+ end
+ end
+ }.new
+
+ @migration.migrate(:up)
+
+ assert_legacy_primary_key
+ end
+
+ def test_legacy_primary_key_in_change_table_should_be_integer
+ @migration = Class.new(migration_class) {
+ def change
+ create_table :legacy_primary_keys, id: false do |t|
+ t.integer :dummy
+ end
+ change_table :legacy_primary_keys do |t|
+ t.primary_key :id
+ end
+ end
+ }.new
+
+ @migration.migrate(:up)
+
+ assert_legacy_primary_key
+ end
+
+ def test_add_column_with_legacy_primary_key_should_be_integer
+ @migration = Class.new(migration_class) {
+ def change
+ create_table :legacy_primary_keys, id: false do |t|
+ t.integer :dummy
+ end
+ add_column :legacy_primary_keys, :id, :primary_key
+ end
+ }.new
+
+ @migration.migrate(:up)
+
+ assert_legacy_primary_key
+ end
+
+ def test_legacy_join_table_foreign_keys_should_be_integer
+ @migration = Class.new(migration_class) {
+ def change
+ create_join_table :apples, :bananas do |t|
+ end
+ end
+ }.new
+
+ @migration.migrate(:up)
+
+ schema = dump_table_schema "apples_bananas"
+ assert_match %r{integer "apple_id", null: false}, schema
+ assert_match %r{integer "banana_id", null: false}, schema
+ end
+
+ def test_legacy_join_table_column_options_should_be_overwritten
+ @migration = Class.new(migration_class) {
+ def change
+ create_join_table :apples, :bananas, column_options: { type: :bigint } do |t|
+ end
+ end
+ }.new
+
+ @migration.migrate(:up)
+
+ schema = dump_table_schema "apples_bananas"
+ assert_match %r{bigint "apple_id", null: false}, schema
+ assert_match %r{bigint "banana_id", null: false}, schema
+ end
+
if current_adapter?(:Mysql2Adapter)
def test_legacy_bigint_primary_key_should_be_auto_incremented
- @migration = Class.new(ActiveRecord::Migration[5.0]) {
+ @migration = Class.new(migration_class) {
def change
create_table :legacy_primary_keys, id: :bigint
end
@@ -208,15 +301,15 @@ class LegacyPrimaryKeyTest < ActiveRecord::TestCase
@migration.migrate(:up)
legacy_pk = LegacyPrimaryKey.columns_hash["id"]
- assert legacy_pk.bigint?
- assert legacy_pk.auto_increment?
+ assert_predicate legacy_pk, :bigint?
+ assert_predicate legacy_pk, :auto_increment?
schema = dump_table_schema "legacy_primary_keys"
assert_match %r{create_table "legacy_primary_keys", (?!id: :bigint, default: nil)}, schema
end
else
def test_legacy_bigint_primary_key_should_not_be_auto_incremented
- @migration = Class.new(ActiveRecord::Migration[5.0]) {
+ @migration = Class.new(migration_class) {
def change
create_table :legacy_primary_keys, id: :bigint do |t|
end
@@ -233,4 +326,44 @@ class LegacyPrimaryKeyTest < ActiveRecord::TestCase
assert_match %r{create_table "legacy_primary_keys", id: :bigint, default: nil}, schema
end
end
+
+ private
+ def assert_legacy_primary_key
+ assert_equal "id", LegacyPrimaryKey.primary_key
+
+ legacy_pk = LegacyPrimaryKey.columns_hash["id"]
+
+ assert_equal :integer, legacy_pk.type
+ assert_not_predicate legacy_pk, :bigint?
+ assert_not legacy_pk.null
+
+ if current_adapter?(:Mysql2Adapter, :PostgreSQLAdapter)
+ schema = dump_table_schema "legacy_primary_keys"
+ assert_match %r{create_table "legacy_primary_keys", id: :(?:integer|serial), (?!default: nil)}, schema
+ end
+ end
+end
+
+module LegacyPrimaryKeyTest
+ class V5_0 < ActiveRecord::TestCase
+ include LegacyPrimaryKeyTestCases
+
+ self.use_transactional_tests = false
+
+ private
+ def migration_class
+ ActiveRecord::Migration[5.0]
+ end
+ end
+
+ class V4_2 < ActiveRecord::TestCase
+ include LegacyPrimaryKeyTestCases
+
+ self.use_transactional_tests = false
+
+ private
+ def migration_class
+ ActiveRecord::Migration[4.2]
+ end
+ end
end
diff --git a/activerecord/test/cases/migration/create_join_table_test.rb b/activerecord/test/cases/migration/create_join_table_test.rb
index c4896f3d6e..83fb4f9385 100644
--- a/activerecord/test/cases/migration/create_join_table_test.rb
+++ b/activerecord/test/cases/migration/create_join_table_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "cases/helper"
module ActiveRecord
@@ -67,7 +69,7 @@ module ActiveRecord
def test_create_join_table_without_indexes
connection.create_join_table :artists, :musics
- assert connection.indexes(:artists_musics).blank?
+ assert_predicate connection.indexes(:artists_musics), :blank?
end
def test_create_join_table_with_index
diff --git a/activerecord/test/cases/migration/foreign_key_test.rb b/activerecord/test/cases/migration/foreign_key_test.rb
index 7762d37915..de37215e80 100644
--- a/activerecord/test/cases/migration/foreign_key_test.rb
+++ b/activerecord/test/cases/migration/foreign_key_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "cases/helper"
require "support/schema_dumping_helper"
@@ -225,6 +227,74 @@ if ActiveRecord::Base.connection.supports_foreign_keys?
end
end
+ if ActiveRecord::Base.connection.supports_validate_constraints?
+ def test_add_invalid_foreign_key
+ @connection.add_foreign_key :astronauts, :rockets, column: "rocket_id", validate: false
+
+ foreign_keys = @connection.foreign_keys("astronauts")
+ assert_equal 1, foreign_keys.size
+
+ fk = foreign_keys.first
+ assert_not_predicate fk, :validated?
+ end
+
+ def test_validate_foreign_key_infers_column
+ @connection.add_foreign_key :astronauts, :rockets, validate: false
+ assert_not_predicate @connection.foreign_keys("astronauts").first, :validated?
+
+ @connection.validate_foreign_key :astronauts, :rockets
+ assert_predicate @connection.foreign_keys("astronauts").first, :validated?
+ end
+
+ def test_validate_foreign_key_by_column
+ @connection.add_foreign_key :astronauts, :rockets, column: "rocket_id", validate: false
+ assert_not_predicate @connection.foreign_keys("astronauts").first, :validated?
+
+ @connection.validate_foreign_key :astronauts, column: "rocket_id"
+ assert_predicate @connection.foreign_keys("astronauts").first, :validated?
+ end
+
+ def test_validate_foreign_key_by_symbol_column
+ @connection.add_foreign_key :astronauts, :rockets, column: :rocket_id, validate: false
+ assert_not_predicate @connection.foreign_keys("astronauts").first, :validated?
+
+ @connection.validate_foreign_key :astronauts, column: :rocket_id
+ assert_predicate @connection.foreign_keys("astronauts").first, :validated?
+ end
+
+ def test_validate_foreign_key_by_name
+ @connection.add_foreign_key :astronauts, :rockets, column: "rocket_id", name: "fancy_named_fk", validate: false
+ assert_not_predicate @connection.foreign_keys("astronauts").first, :validated?
+
+ @connection.validate_foreign_key :astronauts, name: "fancy_named_fk"
+ assert_predicate @connection.foreign_keys("astronauts").first, :validated?
+ end
+
+ def test_validate_foreign_non_existing_foreign_key_raises
+ assert_raises ArgumentError do
+ @connection.validate_foreign_key :astronauts, :rockets
+ end
+ end
+
+ def test_validate_constraint_by_name
+ @connection.add_foreign_key :astronauts, :rockets, column: "rocket_id", name: "fancy_named_fk", validate: false
+
+ @connection.validate_constraint :astronauts, "fancy_named_fk"
+ assert_predicate @connection.foreign_keys("astronauts").first, :validated?
+ end
+ else
+ # Foreign key should still be created, but should not be invalid
+ def test_add_invalid_foreign_key
+ @connection.add_foreign_key :astronauts, :rockets, column: "rocket_id", validate: false
+
+ foreign_keys = @connection.foreign_keys("astronauts")
+ assert_equal 1, foreign_keys.size
+
+ fk = foreign_keys.first
+ assert_predicate fk, :validated?
+ end
+ end
+
def test_schema_dumping
@connection.add_foreign_key :astronauts, :rockets
output = dump_table_schema "astronauts"
diff --git a/activerecord/test/cases/migration/helper.rb b/activerecord/test/cases/migration/helper.rb
index 9c0fa7339d..c056199140 100644
--- a/activerecord/test/cases/migration/helper.rb
+++ b/activerecord/test/cases/migration/helper.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "cases/helper"
module ActiveRecord
diff --git a/activerecord/test/cases/migration/index_test.rb b/activerecord/test/cases/migration/index_test.rb
index f10fcf1398..b25c6d84bc 100644
--- a/activerecord/test/cases/migration/index_test.rb
+++ b/activerecord/test/cases/migration/index_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "cases/helper"
module ActiveRecord
@@ -31,10 +33,8 @@ module ActiveRecord
connection.add_index(table_name, [:foo], name: "old_idx")
connection.rename_index(table_name, "old_idx", "new_idx")
- assert_deprecated do
- assert_not connection.index_name_exists?(table_name, "old_idx", false)
- assert connection.index_name_exists?(table_name, "new_idx", true)
- end
+ assert_not connection.index_name_exists?(table_name, "old_idx")
+ assert connection.index_name_exists?(table_name, "new_idx")
end
def test_rename_index_too_long
diff --git a/activerecord/test/cases/migration/logger_test.rb b/activerecord/test/cases/migration/logger_test.rb
index 3d7c7ad469..28f4cc124b 100644
--- a/activerecord/test/cases/migration/logger_test.rb
+++ b/activerecord/test/cases/migration/logger_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "cases/helper"
module ActiveRecord
diff --git a/activerecord/test/cases/migration/pending_migrations_test.rb b/activerecord/test/cases/migration/pending_migrations_test.rb
index 6970fdcc87..dedb5ea502 100644
--- a/activerecord/test/cases/migration/pending_migrations_test.rb
+++ b/activerecord/test/cases/migration/pending_migrations_test.rb
@@ -1,38 +1,40 @@
+# frozen_string_literal: true
+
require "cases/helper"
module ActiveRecord
class Migration
- class PendingMigrationsTest < ActiveRecord::TestCase
- def setup
- super
- @connection = Minitest::Mock.new
- @app = Minitest::Mock.new
- conn = @connection
- @pending = Class.new(CheckPending) {
- define_method(:connection) { conn }
- }.new(@app)
- @pending.instance_variable_set :@last_check, -1 # Force checking
- end
+ if current_adapter?(:SQLite3Adapter) && !in_memory_db?
+ class PendingMigrationsTest < ActiveRecord::TestCase
+ setup do
+ file = ActiveRecord::Base.connection.raw_connection.filename
+ @conn = ActiveRecord::Base.establish_connection adapter: "sqlite3", database: ":memory:", migrations_paths: MIGRATIONS_ROOT + "/valid"
+ source_db = SQLite3::Database.new file
+ dest_db = ActiveRecord::Base.connection.raw_connection
+ backup = SQLite3::Backup.new(dest_db, "main", source_db, "main")
+ backup.step(-1)
+ backup.finish
+ end
- def teardown
- assert @connection.verify
- assert @app.verify
- super
- end
+ teardown do
+ @conn.release_connection if @conn
+ ActiveRecord::Base.establish_connection :arunit
+ end
+
+ def test_errors_if_pending
+ ActiveRecord::Base.connection.drop_table "schema_migrations", if_exists: true
- def test_errors_if_pending
- ActiveRecord::Migrator.stub :needs_migration?, true do
- assert_raise ActiveRecord::PendingMigrationError do
- @pending.call(nil)
+ assert_raises ActiveRecord::PendingMigrationError do
+ CheckPending.new(Proc.new {}).call({})
end
end
- end
- def test_checks_if_supported
- @app.expect :call, nil, [:foo]
+ def test_checks_if_supported
+ ActiveRecord::SchemaMigration.create_table
+ migrator = Base.connection.migration_context
+ capture(:stdout) { migrator.migrate }
- ActiveRecord::Migrator.stub :needs_migration?, false do
- @pending.call(:foo)
+ assert_nil CheckPending.new(Proc.new {}).call({})
end
end
end
diff --git a/activerecord/test/cases/migration/references_foreign_key_test.rb b/activerecord/test/cases/migration/references_foreign_key_test.rb
index 718b9a0613..7a092103c7 100644
--- a/activerecord/test/cases/migration/references_foreign_key_test.rb
+++ b/activerecord/test/cases/migration/references_foreign_key_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "cases/helper"
if ActiveRecord::Base.connection.supports_foreign_keys_in_create?
diff --git a/activerecord/test/cases/migration/references_index_test.rb b/activerecord/test/cases/migration/references_index_test.rb
index 2866cabab6..e41377d817 100644
--- a/activerecord/test/cases/migration/references_index_test.rb
+++ b/activerecord/test/cases/migration/references_index_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "cases/helper"
module ActiveRecord
diff --git a/activerecord/test/cases/migration/references_statements_test.rb b/activerecord/test/cases/migration/references_statements_test.rb
index e9eb9968cb..769241ba12 100644
--- a/activerecord/test/cases/migration/references_statements_test.rb
+++ b/activerecord/test/cases/migration/references_statements_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "cases/migration/helper"
module ActiveRecord
diff --git a/activerecord/test/cases/migration/rename_table_test.rb b/activerecord/test/cases/migration/rename_table_test.rb
index 5da3ad33a3..8514ccd55b 100644
--- a/activerecord/test/cases/migration/rename_table_test.rb
+++ b/activerecord/test/cases/migration/rename_table_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "cases/migration/helper"
module ActiveRecord
@@ -84,7 +86,7 @@ module ActiveRecord
rename_table :cats, :felines
assert connection.table_exists? :felines
- refute connection.table_exists? :cats
+ assert_not connection.table_exists? :cats
primary_key_name = connection.select_values(<<-SQL.strip_heredoc, "SCHEMA")[0]
SELECT c.relname
@@ -105,7 +107,7 @@ module ActiveRecord
connection.create_table :cats, id: :uuid, default: "uuid_generate_v4()"
assert_nothing_raised { rename_table :cats, :felines }
assert connection.table_exists? :felines
- refute connection.table_exists? :cats
+ assert_not connection.table_exists? :cats
ensure
connection.drop_table :cats, if_exists: true
connection.drop_table :felines, if_exists: true
diff --git a/activerecord/test/cases/migration_test.rb b/activerecord/test/cases/migration_test.rb
index 3a49a41580..a4b8664a85 100644
--- a/activerecord/test/cases/migration_test.rb
+++ b/activerecord/test/cases/migration_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "cases/helper"
require "cases/migration/helper"
require "bigdecimal/util"
@@ -69,6 +71,16 @@ class MigrationTest < ActiveRecord::TestCase
ActiveRecord::Migration.verbose = @verbose_was
end
+ def test_migrator_migrations_path_is_deprecated
+ assert_deprecated do
+ ActiveRecord::Migrator.migrations_path = "/whatever"
+ end
+ ensure
+ assert_deprecated do
+ ActiveRecord::Migrator.migrations_path = "db/migrate"
+ end
+ end
+
def test_migration_version_matches_component_version
assert_equal ActiveRecord::VERSION::STRING.to_f, ActiveRecord::Migration.current_version
end
@@ -76,20 +88,20 @@ class MigrationTest < ActiveRecord::TestCase
def test_migrator_versions
migrations_path = MIGRATIONS_ROOT + "/valid"
old_path = ActiveRecord::Migrator.migrations_paths
- ActiveRecord::Migrator.migrations_paths = migrations_path
+ migrator = ActiveRecord::MigrationContext.new(migrations_path)
- ActiveRecord::Migrator.up(migrations_path)
- assert_equal 3, ActiveRecord::Migrator.current_version
- assert_equal false, ActiveRecord::Migrator.needs_migration?
+ migrator.up
+ assert_equal 3, migrator.current_version
+ assert_equal false, migrator.needs_migration?
- ActiveRecord::Migrator.down(MIGRATIONS_ROOT + "/valid")
- assert_equal 0, ActiveRecord::Migrator.current_version
- assert_equal true, ActiveRecord::Migrator.needs_migration?
+ migrator.down
+ assert_equal 0, migrator.current_version
+ assert_equal true, migrator.needs_migration?
ActiveRecord::SchemaMigration.create!(version: 3)
- assert_equal true, ActiveRecord::Migrator.needs_migration?
+ assert_equal true, migrator.needs_migration?
ensure
- ActiveRecord::Migrator.migrations_paths = old_path
+ ActiveRecord::MigrationContext.new(old_path)
end
def test_migration_detection_without_schema_migration_table
@@ -97,28 +109,31 @@ class MigrationTest < ActiveRecord::TestCase
migrations_path = MIGRATIONS_ROOT + "/valid"
old_path = ActiveRecord::Migrator.migrations_paths
- ActiveRecord::Migrator.migrations_paths = migrations_path
+ migrator = ActiveRecord::MigrationContext.new(migrations_path)
- assert_equal true, ActiveRecord::Migrator.needs_migration?
+ assert_equal true, migrator.needs_migration?
ensure
- ActiveRecord::Migrator.migrations_paths = old_path
+ ActiveRecord::MigrationContext.new(old_path)
end
def test_any_migrations
old_path = ActiveRecord::Migrator.migrations_paths
- ActiveRecord::Migrator.migrations_paths = MIGRATIONS_ROOT + "/valid"
+ migrator = ActiveRecord::MigrationContext.new(MIGRATIONS_ROOT + "/valid")
- assert ActiveRecord::Migrator.any_migrations?
+ assert_predicate migrator, :any_migrations?
- ActiveRecord::Migrator.migrations_paths = MIGRATIONS_ROOT + "/empty"
+ migrator_empty = ActiveRecord::MigrationContext.new(MIGRATIONS_ROOT + "/empty")
- assert_not ActiveRecord::Migrator.any_migrations?
+ assert_not_predicate migrator_empty, :any_migrations?
ensure
- ActiveRecord::Migrator.migrations_paths = old_path
+ ActiveRecord::MigrationContext.new(old_path)
end
def test_migration_version
- assert_nothing_raised { ActiveRecord::Migrator.run(:up, MIGRATIONS_ROOT + "/version_check", 20131219224947) }
+ migrator = ActiveRecord::MigrationContext.new(MIGRATIONS_ROOT + "/version_check")
+ assert_equal 0, migrator.current_version
+ migrator.up(20131219224947)
+ assert_equal 20131219224947, migrator.current_version
end
def test_create_table_with_force_true_does_not_drop_nonexisting_table
@@ -155,7 +170,7 @@ class MigrationTest < ActiveRecord::TestCase
def test_add_table_with_decimals
Person.connection.drop_table :big_numbers rescue nil
- assert !BigNumber.table_exists?
+ assert_not_predicate BigNumber, :table_exists?
GiveMeBigNumbers.up
BigNumber.reset_column_information
@@ -214,15 +229,16 @@ class MigrationTest < ActiveRecord::TestCase
def test_filtering_migrations
assert_no_column Person, :last_name
- assert !Reminder.table_exists?
+ assert_not_predicate Reminder, :table_exists?
name_filter = lambda { |migration| migration.name == "ValidPeopleHaveLastNames" }
- ActiveRecord::Migrator.up(MIGRATIONS_ROOT + "/valid", &name_filter)
+ migrator = ActiveRecord::MigrationContext.new(MIGRATIONS_ROOT + "/valid")
+ migrator.up(&name_filter)
assert_column Person, :last_name
assert_raise(ActiveRecord::StatementInvalid) { Reminder.first }
- ActiveRecord::Migrator.down(MIGRATIONS_ROOT + "/valid", &name_filter)
+ migrator.down(&name_filter)
assert_no_column Person, :last_name
assert_raise(ActiveRecord::StatementInvalid) { Reminder.first }
@@ -380,9 +396,9 @@ class MigrationTest < ActiveRecord::TestCase
current_env = ActiveRecord::ConnectionHandling::DEFAULT_ENV.call
migrations_path = MIGRATIONS_ROOT + "/valid"
old_path = ActiveRecord::Migrator.migrations_paths
- ActiveRecord::Migrator.migrations_paths = migrations_path
+ migrator = ActiveRecord::MigrationContext.new(migrations_path)
- ActiveRecord::Migrator.up(migrations_path)
+ migrator.up
assert_equal current_env, ActiveRecord::InternalMetadata[:environment]
original_rails_env = ENV["RAILS_ENV"]
@@ -393,13 +409,13 @@ class MigrationTest < ActiveRecord::TestCase
refute_equal current_env, new_env
sleep 1 # mysql by default does not store fractional seconds in the database
- ActiveRecord::Migrator.up(migrations_path)
+ migrator.up
assert_equal new_env, ActiveRecord::InternalMetadata[:environment]
ensure
- ActiveRecord::Migrator.migrations_paths = old_path
+ migrator = ActiveRecord::MigrationContext.new(old_path)
ENV["RAILS_ENV"] = original_rails_env
ENV["RACK_ENV"] = original_rack_env
- ActiveRecord::Migrator.up(migrations_path)
+ migrator.up
end
def test_internal_metadata_stores_environment_when_other_data_exists
@@ -409,14 +425,15 @@ class MigrationTest < ActiveRecord::TestCase
current_env = ActiveRecord::ConnectionHandling::DEFAULT_ENV.call
migrations_path = MIGRATIONS_ROOT + "/valid"
old_path = ActiveRecord::Migrator.migrations_paths
- ActiveRecord::Migrator.migrations_paths = migrations_path
current_env = ActiveRecord::ConnectionHandling::DEFAULT_ENV.call
- ActiveRecord::Migrator.up(migrations_path)
+ migrator = ActiveRecord::MigrationContext.new(migrations_path)
+ migrator.up
assert_equal current_env, ActiveRecord::InternalMetadata[:environment]
assert_equal "bar", ActiveRecord::InternalMetadata[:foo]
ensure
- ActiveRecord::Migrator.migrations_paths = old_path
+ migrator = ActiveRecord::MigrationContext.new(old_path)
+ migrator.up
end
def test_proper_table_name_on_migration
@@ -448,7 +465,7 @@ class MigrationTest < ActiveRecord::TestCase
end
def test_rename_table_with_prefix_and_suffix
- assert !Thing.table_exists?
+ assert_not_predicate Thing, :table_exists?
ActiveRecord::Base.table_name_prefix = "p_"
ActiveRecord::Base.table_name_suffix = "_s"
Thing.reset_table_name
@@ -469,7 +486,7 @@ class MigrationTest < ActiveRecord::TestCase
end
def test_add_drop_table_with_prefix_and_suffix
- assert !Reminder.table_exists?
+ assert_not_predicate Reminder, :table_exists?
ActiveRecord::Base.table_name_prefix = "prefix_"
ActiveRecord::Base.table_name_suffix = "_suffix"
Reminder.reset_table_name
@@ -799,8 +816,15 @@ if ActiveRecord::Base.connection.supports_bulk_alter?
t.integer :age
end
- # Adding an index fires a query every time to check if an index already exists or not
- assert_queries(3) do
+ classname = ActiveRecord::Base.connection.class.name[/[^:]*$/]
+ expected_query_count = {
+ "Mysql2Adapter" => 3, # Adding an index fires a query every time to check if an index already exists or not
+ "PostgreSQLAdapter" => 2,
+ }.fetch(classname) {
+ raise "need an expected query count for #{classname}"
+ }
+
+ assert_queries(expected_query_count) do
with_bulk_change_table do |t|
t.index :username, unique: true, name: :awesome_username_index
t.index [:name, :age]
@@ -824,7 +848,15 @@ if ActiveRecord::Base.connection.supports_bulk_alter?
assert index(:index_delete_me_on_name)
- assert_queries(3) do
+ classname = ActiveRecord::Base.connection.class.name[/[^:]*$/]
+ expected_query_count = {
+ "Mysql2Adapter" => 3, # Adding an index fires a query every time to check if an index already exists or not
+ "PostgreSQLAdapter" => 2,
+ }.fetch(classname) {
+ raise "need an expected query count for #{classname}"
+ }
+
+ assert_queries(expected_query_count) do
with_bulk_change_table do |t|
t.remove_index :name
t.index :name, name: :new_name_index, unique: true
@@ -846,18 +878,24 @@ if ActiveRecord::Base.connection.supports_bulk_alter?
assert ! column(:name).default
assert_equal :date, column(:birthdate).type
- # One query for columns (delete_me table)
- # One query for primary key (delete_me table)
- # One query to do the bulk change
- assert_queries(3, ignore_none: true) do
+ classname = ActiveRecord::Base.connection.class.name[/[^:]*$/]
+ expected_query_count = {
+ "Mysql2Adapter" => 3, # one query for columns, one query for primary key, one query to do the bulk change
+ "PostgreSQLAdapter" => 3, # one query for columns, one for bulk change, one for comment
+ }.fetch(classname) {
+ raise "need an expected query count for #{classname}"
+ }
+
+ assert_queries(expected_query_count, ignore_none: true) do
with_bulk_change_table do |t|
t.change :name, :string, default: "NONAME"
- t.change :birthdate, :datetime
+ t.change :birthdate, :datetime, comment: "This is a comment"
end
end
assert_equal "NONAME", column(:name).default
assert_equal :datetime, column(:birthdate).type
+ assert_equal "This is a comment", column(:birthdate).comment
end
private
@@ -912,12 +950,12 @@ class CopyMigrationsTest < ActiveRecord::TestCase
assert_equal [@migrations_path + "/4_people_have_hobbies.bukkits.rb", @migrations_path + "/5_people_have_descriptions.bukkits.rb"], copied.map(&:filename)
expected = "# This migration comes from bukkits (originally 1)"
- assert_equal expected, IO.readlines(@migrations_path + "/4_people_have_hobbies.bukkits.rb")[0].chomp
+ assert_equal expected, IO.readlines(@migrations_path + "/4_people_have_hobbies.bukkits.rb")[1].chomp
files_count = Dir[@migrations_path + "/*.rb"].length
copied = ActiveRecord::Migration.copy(@migrations_path, bukkits: MIGRATIONS_ROOT + "/to_copy")
assert_equal files_count, Dir[@migrations_path + "/*.rb"].length
- assert copied.empty?
+ assert_empty copied
ensure
clear
end
@@ -958,7 +996,7 @@ class CopyMigrationsTest < ActiveRecord::TestCase
files_count = Dir[@migrations_path + "/*.rb"].length
copied = ActiveRecord::Migration.copy(@migrations_path, bukkits: MIGRATIONS_ROOT + "/to_copy_with_timestamps")
assert_equal files_count, Dir[@migrations_path + "/*.rb"].length
- assert copied.empty?
+ assert_empty copied
end
ensure
clear
@@ -1000,7 +1038,7 @@ class CopyMigrationsTest < ActiveRecord::TestCase
files_count = Dir[@migrations_path + "/*.rb"].length
copied = ActiveRecord::Migration.copy(@migrations_path, bukkits: MIGRATIONS_ROOT + "/to_copy_with_timestamps")
assert_equal files_count, Dir[@migrations_path + "/*.rb"].length
- assert copied.empty?
+ assert_empty copied
end
ensure
clear
@@ -1015,13 +1053,13 @@ class CopyMigrationsTest < ActiveRecord::TestCase
assert File.exist?(@migrations_path + "/4_currencies_have_symbols.bukkits.rb")
assert_equal [@migrations_path + "/4_currencies_have_symbols.bukkits.rb"], copied.map(&:filename)
- expected = "# coding: ISO-8859-15\n# This migration comes from bukkits (originally 1)"
- assert_equal expected, IO.readlines(@migrations_path + "/4_currencies_have_symbols.bukkits.rb")[0..1].join.chomp
+ expected = "# frozen_string_literal: true\n# coding: ISO-8859-15\n# This migration comes from bukkits (originally 1)"
+ assert_equal expected, IO.readlines(@migrations_path + "/4_currencies_have_symbols.bukkits.rb")[0..2].join.chomp
files_count = Dir[@migrations_path + "/*.rb"].length
copied = ActiveRecord::Migration.copy(@migrations_path, bukkits: MIGRATIONS_ROOT + "/magic")
assert_equal files_count, Dir[@migrations_path + "/*.rb"].length
- assert copied.empty?
+ assert_empty copied
ensure
clear
end
@@ -1104,21 +1142,4 @@ class CopyMigrationsTest < ActiveRecord::TestCase
def test_unknown_migration_version_should_raise_an_argument_error
assert_raise(ArgumentError) { ActiveRecord::Migration[1.0] }
end
-
- def test_deprecate_initialize_internal_tables
- assert_deprecated { ActiveRecord::Base.connection.initialize_schema_migrations_table }
- assert_deprecated { ActiveRecord::Base.connection.initialize_internal_metadata_table }
- end
-
- def test_deprecate_migration_keys
- assert_deprecated { ActiveRecord::Base.connection.migration_keys }
- end
-
- def test_deprecate_supports_migrations
- assert_deprecated { ActiveRecord::Base.connection.supports_migrations? }
- end
-
- def test_deprecate_schema_migrations_table_name
- assert_deprecated { ActiveRecord::Migrator.schema_migrations_table_name }
- end
end
diff --git a/activerecord/test/cases/migrator_test.rb b/activerecord/test/cases/migrator_test.rb
index 2e4b454a86..873455cf67 100644
--- a/activerecord/test/cases/migrator_test.rb
+++ b/activerecord/test/cases/migrator_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "cases/helper"
require "cases/migration/helper"
@@ -64,10 +66,30 @@ class MigratorTest < ActiveRecord::TestCase
list = [ActiveRecord::Migration.new("Foo", 1), ActiveRecord::Migration.new("Bar", 2)]
ActiveRecord::Migrator.new(:up, list, 3).run
end
+
+ assert_raises(ActiveRecord::UnknownMigrationVersionError) do
+ list = [ActiveRecord::Migration.new("Foo", 1), ActiveRecord::Migration.new("Bar", 2)]
+ ActiveRecord::Migrator.new(:up, list, -1).run
+ end
+
+ assert_raises(ActiveRecord::UnknownMigrationVersionError) do
+ list = [ActiveRecord::Migration.new("Foo", 1), ActiveRecord::Migration.new("Bar", 2)]
+ ActiveRecord::Migrator.new(:up, list, 0).run
+ end
+
+ assert_raises(ActiveRecord::UnknownMigrationVersionError) do
+ list = [ActiveRecord::Migration.new("Foo", 1), ActiveRecord::Migration.new("Bar", 2)]
+ ActiveRecord::Migrator.new(:up, list, 3).migrate
+ end
+
+ assert_raises(ActiveRecord::UnknownMigrationVersionError) do
+ list = [ActiveRecord::Migration.new("Foo", 1), ActiveRecord::Migration.new("Bar", 2)]
+ ActiveRecord::Migrator.new(:up, list, -1).migrate
+ end
end
def test_finds_migrations
- migrations = ActiveRecord::Migrator.migrations(MIGRATIONS_ROOT + "/valid")
+ migrations = ActiveRecord::MigrationContext.new(MIGRATIONS_ROOT + "/valid").migrations
[[1, "ValidPeopleHaveLastNames"], [2, "WeNeedReminders"], [3, "InnocentJointable"]].each_with_index do |pair, i|
assert_equal migrations[i].version, pair.first
@@ -76,7 +98,8 @@ class MigratorTest < ActiveRecord::TestCase
end
def test_finds_migrations_in_subdirectories
- migrations = ActiveRecord::Migrator.migrations(MIGRATIONS_ROOT + "/valid_with_subdirectories")
+ migrations = ActiveRecord::MigrationContext.new(MIGRATIONS_ROOT + "/valid_with_subdirectories").migrations
+
[[1, "ValidPeopleHaveLastNames"], [2, "WeNeedReminders"], [3, "InnocentJointable"]].each_with_index do |pair, i|
assert_equal migrations[i].version, pair.first
@@ -86,7 +109,7 @@ class MigratorTest < ActiveRecord::TestCase
def test_finds_migrations_from_two_directories
directories = [MIGRATIONS_ROOT + "/valid_with_timestamps", MIGRATIONS_ROOT + "/to_copy_with_timestamps"]
- migrations = ActiveRecord::Migrator.migrations directories
+ migrations = ActiveRecord::MigrationContext.new(directories).migrations
[[20090101010101, "PeopleHaveHobbies"],
[20090101010202, "PeopleHaveDescriptions"],
@@ -99,14 +122,14 @@ class MigratorTest < ActiveRecord::TestCase
end
def test_finds_migrations_in_numbered_directory
- migrations = ActiveRecord::Migrator.migrations [MIGRATIONS_ROOT + "/10_urban"]
+ migrations = ActiveRecord::MigrationContext.new(MIGRATIONS_ROOT + "/10_urban").migrations
assert_equal 9, migrations[0].version
assert_equal "AddExpressions", migrations[0].name
end
def test_relative_migrations
list = Dir.chdir(MIGRATIONS_ROOT) do
- ActiveRecord::Migrator.migrations("valid")
+ ActiveRecord::MigrationContext.new("valid").migrations
end
migration_proxy = list.find { |item|
@@ -135,7 +158,7 @@ class MigratorTest < ActiveRecord::TestCase
["up", "002", "We need reminders"],
["down", "003", "Innocent jointable"],
["up", "010", "********** NO FILE **********"],
- ], ActiveRecord::Migrator.migrations_status(path)
+ ], ActiveRecord::MigrationContext.new(path).migrations_status
end
def test_migrations_status_in_subdirectories
@@ -149,7 +172,7 @@ class MigratorTest < ActiveRecord::TestCase
["up", "002", "We need reminders"],
["down", "003", "Innocent jointable"],
["up", "010", "********** NO FILE **********"],
- ], ActiveRecord::Migrator.migrations_status(path)
+ ], ActiveRecord::MigrationContext.new(path).migrations_status
end
def test_migrations_status_with_schema_define_in_subdirectories
@@ -164,7 +187,7 @@ class MigratorTest < ActiveRecord::TestCase
["up", "001", "Valid people have last names"],
["up", "002", "We need reminders"],
["up", "003", "Innocent jointable"],
- ], ActiveRecord::Migrator.migrations_status(path)
+ ], ActiveRecord::MigrationContext.new(path).migrations_status
ensure
ActiveRecord::Migrator.migrations_paths = prev_paths
end
@@ -182,7 +205,7 @@ class MigratorTest < ActiveRecord::TestCase
["down", "20100201010101", "Valid with timestamps we need reminders"],
["down", "20100301010101", "Valid with timestamps innocent jointable"],
["up", "20160528010101", "********** NO FILE **********"],
- ], ActiveRecord::Migrator.migrations_status(paths)
+ ], ActiveRecord::MigrationContext.new(paths).migrations_status
end
def test_migrator_interleaved_migrations
@@ -210,25 +233,28 @@ class MigratorTest < ActiveRecord::TestCase
def test_up_calls_up
migrations = [Sensor.new(nil, 0), Sensor.new(nil, 1), Sensor.new(nil, 2)]
- ActiveRecord::Migrator.new(:up, migrations).migrate
+ migrator = ActiveRecord::Migrator.new(:up, migrations)
+ migrator.migrate
assert migrations.all?(&:went_up)
assert migrations.all? { |m| !m.went_down }
- assert_equal 2, ActiveRecord::Migrator.current_version
+ assert_equal 2, migrator.current_version
end
def test_down_calls_down
test_up_calls_up
migrations = [Sensor.new(nil, 0), Sensor.new(nil, 1), Sensor.new(nil, 2)]
- ActiveRecord::Migrator.new(:down, migrations).migrate
+ migrator = ActiveRecord::Migrator.new(:down, migrations)
+ migrator.migrate
assert migrations.all? { |m| !m.went_up }
assert migrations.all?(&:went_down)
- assert_equal 0, ActiveRecord::Migrator.current_version
+ assert_equal 0, migrator.current_version
end
def test_current_version
ActiveRecord::SchemaMigration.create!(version: "1000")
- assert_equal 1000, ActiveRecord::Migrator.current_version
+ migrator = ActiveRecord::MigrationContext.new("db/migrate")
+ assert_equal 1000, migrator.current_version
end
def test_migrator_one_up
@@ -267,33 +293,36 @@ class MigratorTest < ActiveRecord::TestCase
def test_migrator_double_up
calls, migrations = sensors(3)
- assert_equal(0, ActiveRecord::Migrator.current_version)
+ migrator = ActiveRecord::Migrator.new(:up, migrations, 1)
+ assert_equal(0, migrator.current_version)
- ActiveRecord::Migrator.new(:up, migrations, 1).migrate
+ migrator.migrate
assert_equal [[:up, 1]], calls
calls.clear
- ActiveRecord::Migrator.new(:up, migrations, 1).migrate
+ migrator.migrate
assert_equal [], calls
end
def test_migrator_double_down
calls, migrations = sensors(3)
+ migrator = ActiveRecord::Migrator.new(:up, migrations, 1)
- assert_equal(0, ActiveRecord::Migrator.current_version)
+ assert_equal 0, migrator.current_version
- ActiveRecord::Migrator.new(:up, migrations, 1).run
+ migrator.run
assert_equal [[:up, 1]], calls
calls.clear
- ActiveRecord::Migrator.new(:down, migrations, 1).run
+ migrator = ActiveRecord::Migrator.new(:down, migrations, 1)
+ migrator.run
assert_equal [[:down, 1]], calls
calls.clear
- ActiveRecord::Migrator.new(:down, migrations, 1).run
+ migrator.run
assert_equal [], calls
- assert_equal(0, ActiveRecord::Migrator.current_version)
+ assert_equal 0, migrator.current_version
end
def test_migrator_verbosity
@@ -339,78 +368,85 @@ class MigratorTest < ActiveRecord::TestCase
def test_migrator_going_down_due_to_version_target
calls, migrator = migrator_class(3)
+ migrator = migrator.new("valid")
- migrator.up("valid", 1)
+ migrator.up(1)
assert_equal [[:up, 1]], calls
calls.clear
- migrator.migrate("valid", 0)
+ migrator.migrate(0)
assert_equal [[:down, 1]], calls
calls.clear
- migrator.migrate("valid")
+ migrator.migrate
assert_equal [[:up, 1], [:up, 2], [:up, 3]], calls
end
def test_migrator_output_when_running_multiple_migrations
_, migrator = migrator_class(3)
+ migrator = migrator.new("valid")
- result = migrator.migrate("valid")
+ result = migrator.migrate
assert_equal(3, result.count)
# Nothing migrated from duplicate run
- result = migrator.migrate("valid")
+ result = migrator.migrate
assert_equal(0, result.count)
- result = migrator.rollback("valid")
+ result = migrator.rollback
assert_equal(1, result.count)
end
def test_migrator_output_when_running_single_migration
_, migrator = migrator_class(1)
- result = migrator.run(:up, "valid", 1)
+ migrator = migrator.new("valid")
+
+ result = migrator.run(:up, 1)
assert_equal(1, result.version)
end
def test_migrator_rollback
_, migrator = migrator_class(3)
+ migrator = migrator.new("valid")
- migrator.migrate("valid")
- assert_equal(3, ActiveRecord::Migrator.current_version)
+ migrator.migrate
+ assert_equal(3, migrator.current_version)
- migrator.rollback("valid")
- assert_equal(2, ActiveRecord::Migrator.current_version)
+ migrator.rollback
+ assert_equal(2, migrator.current_version)
- migrator.rollback("valid")
- assert_equal(1, ActiveRecord::Migrator.current_version)
+ migrator.rollback
+ assert_equal(1, migrator.current_version)
- migrator.rollback("valid")
- assert_equal(0, ActiveRecord::Migrator.current_version)
+ migrator.rollback
+ assert_equal(0, migrator.current_version)
- migrator.rollback("valid")
- assert_equal(0, ActiveRecord::Migrator.current_version)
+ migrator.rollback
+ assert_equal(0, migrator.current_version)
end
def test_migrator_db_has_no_schema_migrations_table
_, migrator = migrator_class(3)
+ migrator = migrator.new("valid")
ActiveRecord::Base.connection.drop_table "schema_migrations", if_exists: true
assert_not ActiveRecord::Base.connection.table_exists?("schema_migrations")
- migrator.migrate("valid", 1)
+ migrator.migrate(1)
assert ActiveRecord::Base.connection.table_exists?("schema_migrations")
end
def test_migrator_forward
_, migrator = migrator_class(3)
- migrator.migrate("/valid", 1)
- assert_equal(1, ActiveRecord::Migrator.current_version)
+ migrator = migrator.new("/valid")
+ migrator.migrate(1)
+ assert_equal(1, migrator.current_version)
- migrator.forward("/valid", 2)
- assert_equal(3, ActiveRecord::Migrator.current_version)
+ migrator.forward(2)
+ assert_equal(3, migrator.current_version)
- migrator.forward("/valid")
- assert_equal(3, ActiveRecord::Migrator.current_version)
+ migrator.forward
+ assert_equal(3, migrator.current_version)
end
def test_only_loads_pending_migrations
@@ -418,25 +454,27 @@ class MigratorTest < ActiveRecord::TestCase
ActiveRecord::SchemaMigration.create!(version: "1")
calls, migrator = migrator_class(3)
- migrator.migrate("valid", nil)
+ migrator = migrator.new("valid")
+ migrator.migrate
assert_equal [[:up, 2], [:up, 3]], calls
end
def test_get_all_versions
_, migrator = migrator_class(3)
+ migrator = migrator.new("valid")
- migrator.migrate("valid")
- assert_equal([1, 2, 3], ActiveRecord::Migrator.get_all_versions)
+ migrator.migrate
+ assert_equal([1, 2, 3], migrator.get_all_versions)
- migrator.rollback("valid")
- assert_equal([1, 2], ActiveRecord::Migrator.get_all_versions)
+ migrator.rollback
+ assert_equal([1, 2], migrator.get_all_versions)
- migrator.rollback("valid")
- assert_equal([1], ActiveRecord::Migrator.get_all_versions)
+ migrator.rollback
+ assert_equal([1], migrator.get_all_versions)
- migrator.rollback("valid")
- assert_equal([], ActiveRecord::Migrator.get_all_versions)
+ migrator.rollback
+ assert_equal([], migrator.get_all_versions)
end
private
@@ -461,11 +499,11 @@ class MigratorTest < ActiveRecord::TestCase
def migrator_class(count)
calls, migrations = sensors(count)
- migrator = Class.new(ActiveRecord::Migrator).extend(Module.new {
- define_method(:migrations) { |paths|
+ migrator = Class.new(ActiveRecord::MigrationContext) {
+ define_method(:migrations) { |*|
migrations
}
- })
+ }
[calls, migrator]
end
end
diff --git a/activerecord/test/cases/mixin_test.rb b/activerecord/test/cases/mixin_test.rb
index a8af8e30f7..fdb8ac6ab3 100644
--- a/activerecord/test/cases/mixin_test.rb
+++ b/activerecord/test/cases/mixin_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "cases/helper"
class Mixin < ActiveRecord::Base
@@ -10,10 +12,6 @@ class TouchTest < ActiveRecord::TestCase
travel_to Time.now
end
- teardown do
- travel_back
- end
-
def test_update
stamped = Mixin.new
diff --git a/activerecord/test/cases/modules_test.rb b/activerecord/test/cases/modules_test.rb
index f8a7bab35f..060d555607 100644
--- a/activerecord/test/cases/modules_test.rb
+++ b/activerecord/test/cases/modules_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "cases/helper"
require "models/company_in_module"
require "models/shop"
diff --git a/activerecord/test/cases/multiparameter_attributes_test.rb b/activerecord/test/cases/multiparameter_attributes_test.rb
index ceb5724377..a24b173cf5 100644
--- a/activerecord/test/cases/multiparameter_attributes_test.rb
+++ b/activerecord/test/cases/multiparameter_attributes_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "cases/helper"
require "models/topic"
require "models/customer"
@@ -225,7 +227,7 @@ class MultiParameterAttributeTest < ActiveRecord::TestCase
topic = Topic.find(1)
topic.attributes = attributes
assert_equal Time.local(2004, 6, 24, 16, 24, 0), topic.written_on
- assert_equal false, topic.written_on.respond_to?(:time_zone)
+ assert_not_respond_to topic.written_on, :time_zone
end
end
@@ -240,7 +242,7 @@ class MultiParameterAttributeTest < ActiveRecord::TestCase
topic = Topic.find(1)
topic.attributes = attributes
assert_equal Time.utc(2004, 6, 24, 16, 24, 0), topic.written_on
- assert_equal false, topic.written_on.respond_to?(:time_zone)
+ assert_not_respond_to topic.written_on, :time_zone
end
ensure
Topic.skip_time_zone_conversion_for_attributes = []
@@ -259,7 +261,7 @@ class MultiParameterAttributeTest < ActiveRecord::TestCase
topic = Topic.find(1)
topic.attributes = attributes
assert_equal Time.zone.local(2000, 1, 1, 16, 24, 0), topic.bonus_time
- assert_not topic.bonus_time.utc?
+ assert_not_predicate topic.bonus_time, :utc?
attributes = {
"written_on(1i)" => "2000", "written_on(2i)" => "", "written_on(3i)" => "",
@@ -271,6 +273,12 @@ class MultiParameterAttributeTest < ActiveRecord::TestCase
ensure
Topic.reset_column_information
end
+
+ def test_multiparameter_attributes_setting_time_attribute
+ topic = Topic.new("bonus_time(4i)" => "01", "bonus_time(5i)" => "05")
+ assert_equal 1, topic.bonus_time.hour
+ assert_equal 5, topic.bonus_time.min
+ end
end
def test_multiparameter_attributes_on_time_with_empty_seconds
@@ -285,14 +293,6 @@ class MultiParameterAttributeTest < ActiveRecord::TestCase
end
end
- unless current_adapter? :OracleAdapter
- def test_multiparameter_attributes_setting_time_attribute
- topic = Topic.new("bonus_time(4i)" => "01", "bonus_time(5i)" => "05")
- assert_equal 1, topic.bonus_time.hour
- assert_equal 5, topic.bonus_time.min
- end
- end
-
def test_multiparameter_attributes_setting_date_attribute
topic = Topic.new("written_on(1i)" => "1952", "written_on(2i)" => "3", "written_on(3i)" => "11")
assert_equal 1952, topic.written_on.year
@@ -300,13 +300,34 @@ class MultiParameterAttributeTest < ActiveRecord::TestCase
assert_equal 11, topic.written_on.day
end
+ def test_create_with_multiparameter_attributes_setting_date_attribute
+ topic = Topic.create_with("written_on(1i)" => "1952", "written_on(2i)" => "3", "written_on(3i)" => "11").new
+ assert_equal 1952, topic.written_on.year
+ assert_equal 3, topic.written_on.month
+ assert_equal 11, topic.written_on.day
+ end
+
def test_multiparameter_attributes_setting_date_and_time_attribute
topic = Topic.new(
- "written_on(1i)" => "1952",
- "written_on(2i)" => "3",
- "written_on(3i)" => "11",
- "written_on(4i)" => "13",
- "written_on(5i)" => "55")
+ "written_on(1i)" => "1952",
+ "written_on(2i)" => "3",
+ "written_on(3i)" => "11",
+ "written_on(4i)" => "13",
+ "written_on(5i)" => "55")
+ assert_equal 1952, topic.written_on.year
+ assert_equal 3, topic.written_on.month
+ assert_equal 11, topic.written_on.day
+ assert_equal 13, topic.written_on.hour
+ assert_equal 55, topic.written_on.min
+ end
+
+ def test_create_with_multiparameter_attributes_setting_date_and_time_attribute
+ topic = Topic.create_with(
+ "written_on(1i)" => "1952",
+ "written_on(2i)" => "3",
+ "written_on(3i)" => "11",
+ "written_on(4i)" => "13",
+ "written_on(5i)" => "55").new
assert_equal 1952, topic.written_on.year
assert_equal 3, topic.written_on.month
assert_equal 11, topic.written_on.day
@@ -364,4 +385,15 @@ class MultiParameterAttributeTest < ActiveRecord::TestCase
assert_equal("address", ex.errors[0].attribute)
end
+
+ def test_multiparameter_assigned_attributes_did_not_come_from_user
+ topic = Topic.new(
+ "written_on(1i)" => "1952",
+ "written_on(2i)" => "3",
+ "written_on(3i)" => "11",
+ "written_on(4i)" => "13",
+ "written_on(5i)" => "55",
+ )
+ refute_predicate topic, :written_on_came_from_user?
+ end
end
diff --git a/activerecord/test/cases/multiple_db_test.rb b/activerecord/test/cases/multiple_db_test.rb
index e3bb51bd77..192d2f5251 100644
--- a/activerecord/test/cases/multiple_db_test.rb
+++ b/activerecord/test/cases/multiple_db_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "cases/helper"
require "models/entrant"
require "models/bird"
@@ -90,14 +92,9 @@ class MultipleDbTest < ActiveRecord::TestCase
assert_equal "Ruby Developer", Entrant.find(1).name
end
- def test_arel_table_engines
- assert_not_equal Entrant.arel_engine, Bird.arel_engine
- assert_not_equal Entrant.arel_engine, Course.arel_engine
- end
-
def test_connection
- assert_equal Entrant.arel_engine.connection.object_id, Bird.arel_engine.connection.object_id
- assert_not_equal Entrant.arel_engine.connection.object_id, Course.arel_engine.connection.object_id
+ assert_same Entrant.connection, Bird.connection
+ assert_not_same Entrant.connection, Course.connection
end
unless in_memory_db?
diff --git a/activerecord/test/cases/nested_attributes_test.rb b/activerecord/test/cases/nested_attributes_test.rb
index 154faa56aa..1ed3a61bbb 100644
--- a/activerecord/test/cases/nested_attributes_test.rb
+++ b/activerecord/test/cases/nested_attributes_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "cases/helper"
require "models/pirate"
require "models/ship"
@@ -34,7 +36,7 @@ class TestNestedAttributesInGeneral < ActiveRecord::TestCase
pirate.birds_with_reject_all_blank_attributes = [{ name: "", color: "", _destroy: "0" }]
pirate.save!
- assert pirate.birds_with_reject_all_blank.empty?
+ assert_empty pirate.birds_with_reject_all_blank
end
def test_should_not_build_a_new_record_if_reject_all_blank_returns_false
@@ -42,7 +44,7 @@ class TestNestedAttributesInGeneral < ActiveRecord::TestCase
pirate.birds_with_reject_all_blank_attributes = [{ name: "", color: "" }]
pirate.save!
- assert pirate.birds_with_reject_all_blank.empty?
+ assert_empty pirate.birds_with_reject_all_blank
end
def test_should_build_a_new_record_if_reject_all_blank_does_not_return_false
@@ -150,7 +152,7 @@ class TestNestedAttributesInGeneral < ActiveRecord::TestCase
man = Man.create(name: "Jon")
interest = man.interests.create(topic: "the ladies")
man.update(interests_attributes: { _destroy: "1", id: interest.id })
- assert man.reload.interests.empty?
+ assert_empty man.reload.interests
end
def test_reject_if_is_not_short_circuited_if_allow_destroy_is_false
@@ -238,7 +240,7 @@ class TestNestedAttributesOnAHasOneAssociation < ActiveRecord::TestCase
@ship.destroy
@pirate.reload.ship_attributes = { name: "Davy Jones Gold Dagger" }
- assert !@pirate.ship.persisted?
+ assert_not_predicate @pirate.ship, :persisted?
assert_equal "Davy Jones Gold Dagger", @pirate.ship.name
end
@@ -259,7 +261,7 @@ class TestNestedAttributesOnAHasOneAssociation < ActiveRecord::TestCase
def test_should_replace_an_existing_record_if_there_is_no_id
@pirate.reload.ship_attributes = { name: "Davy Jones Gold Dagger" }
- assert !@pirate.ship.persisted?
+ assert_not_predicate @pirate.ship, :persisted?
assert_equal "Davy Jones Gold Dagger", @pirate.ship.name
assert_equal "Nights Dirty Lightning", @ship.name
end
@@ -333,7 +335,7 @@ class TestNestedAttributesOnAHasOneAssociation < ActiveRecord::TestCase
def test_should_also_work_with_a_HashWithIndifferentAccess
@pirate.ship_attributes = ActiveSupport::HashWithIndifferentAccess.new(id: @ship.id, name: "Davy Jones Gold Dagger")
- assert @pirate.ship.persisted?
+ assert_predicate @pirate.ship, :persisted?
assert_equal "Davy Jones Gold Dagger", @pirate.ship.name
end
@@ -348,12 +350,12 @@ class TestNestedAttributesOnAHasOneAssociation < ActiveRecord::TestCase
def test_should_not_destroy_the_associated_model_until_the_parent_is_saved
@pirate.attributes = { ship_attributes: { id: @ship.id, _destroy: "1" } }
- assert !@pirate.ship.destroyed?
- assert @pirate.ship.marked_for_destruction?
+ assert_not_predicate @pirate.ship, :destroyed?
+ assert_predicate @pirate.ship, :marked_for_destruction?
@pirate.save
- assert @pirate.ship.destroyed?
+ assert_predicate @pirate.ship, :destroyed?
assert_nil @pirate.reload.ship
end
@@ -422,7 +424,7 @@ class TestNestedAttributesOnABelongsToAssociation < ActiveRecord::TestCase
@pirate.destroy
@ship.reload.pirate_attributes = { catchphrase: "Arr" }
- assert !@ship.pirate.persisted?
+ assert_not_predicate @ship.pirate, :persisted?
assert_equal "Arr", @ship.pirate.catchphrase
end
@@ -443,7 +445,7 @@ class TestNestedAttributesOnABelongsToAssociation < ActiveRecord::TestCase
def test_should_replace_an_existing_record_if_there_is_no_id
@ship.reload.pirate_attributes = { catchphrase: "Arr" }
- assert !@ship.pirate.persisted?
+ assert_not_predicate @ship.pirate, :persisted?
assert_equal "Arr", @ship.pirate.catchphrase
assert_equal "Aye", @pirate.catchphrase
end
@@ -548,7 +550,7 @@ class TestNestedAttributesOnABelongsToAssociation < ActiveRecord::TestCase
@pirate.delete
@ship.reload.attributes = { update_only_pirate_attributes: { catchphrase: "Arr" } }
- assert !@ship.update_only_pirate.persisted?
+ assert_not_predicate @ship.update_only_pirate, :persisted?
end
def test_should_update_existing_when_update_only_is_true_and_no_id_is_given
@@ -630,10 +632,10 @@ module NestedAttributesOnACollectionAssociationTests
def test_should_not_load_association_when_updating_existing_records
@pirate.reload
@pirate.send(association_setter, [{ id: @child_1.id, name: "Grace OMalley" }])
- assert ! @pirate.send(@association_name).loaded?
+ assert_not_predicate @pirate.send(@association_name), :loaded?
@pirate.save
- assert ! @pirate.send(@association_name).loaded?
+ assert_not_predicate @pirate.send(@association_name), :loaded?
assert_equal "Grace OMalley", @child_1.reload.name
end
@@ -661,7 +663,7 @@ module NestedAttributesOnACollectionAssociationTests
def test_should_not_remove_scheduled_destroys_when_loading_association
@pirate.reload
@pirate.send(association_setter, [{ id: @child_1.id, _destroy: "1" }])
- assert @pirate.send(@association_name).load_target.find { |r| r.id == @child_1.id }.marked_for_destruction?
+ assert_predicate @pirate.send(@association_name).load_target.find { |r| r.id == @child_1.id }, :marked_for_destruction?
end
def test_should_take_a_hash_with_composite_id_keys_and_assign_the_attributes_to_the_associated_models
@@ -703,10 +705,10 @@ module NestedAttributesOnACollectionAssociationTests
association_getter => { "foo" => { name: "Grace OMalley" }, "bar" => { name: "Privateers Greed" } }
}
- assert !@pirate.send(@association_name).first.persisted?
+ assert_not_predicate @pirate.send(@association_name).first, :persisted?
assert_equal "Grace OMalley", @pirate.send(@association_name).first.name
- assert !@pirate.send(@association_name).last.persisted?
+ assert_not_predicate @pirate.send(@association_name).last, :persisted?
assert_equal "Privateers Greed", @pirate.send(@association_name).last.name
end
@@ -1089,7 +1091,7 @@ class TestHasManyAutosaveAssociationWhichItselfHasAutosaveAssociations < ActiveR
test "nested singular associations are validated" do
part = ShipPart.new(name: "Stern", ship_attributes: { name: nil })
- assert_not part.valid?
+ assert_not_predicate part, :valid?
assert_equal ["Ship name can't be blank"], part.errors.full_messages
end
end
diff --git a/activerecord/test/cases/nested_attributes_with_callbacks_test.rb b/activerecord/test/cases/nested_attributes_with_callbacks_test.rb
index b9d2acbed2..1d26057fdc 100644
--- a/activerecord/test/cases/nested_attributes_with_callbacks_test.rb
+++ b/activerecord/test/cases/nested_attributes_with_callbacks_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "cases/helper"
require "models/pirate"
require "models/bird"
@@ -61,7 +63,7 @@ class NestedAttributesWithCallbacksTest < ActiveRecord::TestCase
# Characterizing when :before_add callback is called
test ":before_add called for new bird when not loaded" do
- assert_not @pirate.birds_with_add.loaded?
+ assert_not_predicate @pirate.birds_with_add, :loaded?
@pirate.birds_with_add_attributes = new_bird_attributes
assert_new_bird_with_callback_called
end
@@ -78,7 +80,7 @@ class NestedAttributesWithCallbacksTest < ActiveRecord::TestCase
end
test ":before_add not called for identical assignment when not loaded" do
- assert_not @pirate.birds_with_add.loaded?
+ assert_not_predicate @pirate.birds_with_add, :loaded?
@pirate.birds_with_add_attributes = existing_birds_attributes
assert_callbacks_not_called
end
@@ -90,7 +92,7 @@ class NestedAttributesWithCallbacksTest < ActiveRecord::TestCase
end
test ":before_add not called for destroy assignment when not loaded" do
- assert_not @pirate.birds_with_add.loaded?
+ assert_not_predicate @pirate.birds_with_add, :loaded?
@pirate.birds_with_add_attributes = destroy_bird_attributes
assert_callbacks_not_called
end
@@ -109,7 +111,7 @@ class NestedAttributesWithCallbacksTest < ActiveRecord::TestCase
# Ensuring that the records in the association target are updated,
# whether the association is loaded before or not
test "Assignment updates records in target when not loaded" do
- assert_not @pirate.birds_with_add.loaded?
+ assert_not_predicate @pirate.birds_with_add, :loaded?
@pirate.birds_with_add_attributes = update_new_and_destroy_bird_attributes
assert_assignment_affects_records_in_target(:birds_with_add)
end
@@ -122,7 +124,7 @@ class NestedAttributesWithCallbacksTest < ActiveRecord::TestCase
test("Assignment updates records in target when not loaded" \
" and callback loads target") do
- assert_not @pirate.birds_with_add_load.loaded?
+ assert_not_predicate @pirate.birds_with_add_load, :loaded?
@pirate.birds_with_add_load_attributes = update_new_and_destroy_bird_attributes
assert_assignment_affects_records_in_target(:birds_with_add_load)
end
diff --git a/activerecord/test/cases/null_relation_test.rb b/activerecord/test/cases/null_relation_test.rb
new file mode 100644
index 0000000000..17527568f8
--- /dev/null
+++ b/activerecord/test/cases/null_relation_test.rb
@@ -0,0 +1,84 @@
+# frozen_string_literal: true
+
+require "cases/helper"
+require "models/developer"
+require "models/comment"
+require "models/post"
+require "models/topic"
+
+class NullRelationTest < ActiveRecord::TestCase
+ fixtures :posts, :comments
+
+ def test_none
+ assert_no_queries(ignore_none: false) do
+ assert_equal [], Developer.none
+ assert_equal [], Developer.all.none
+ end
+ end
+
+ def test_none_chainable
+ assert_no_queries(ignore_none: false) do
+ assert_equal [], Developer.none.where(name: "David")
+ end
+ end
+
+ def test_none_chainable_to_existing_scope_extension_method
+ assert_no_queries(ignore_none: false) do
+ assert_equal 1, Topic.anonymous_extension.none.one
+ end
+ end
+
+ def test_none_chained_to_methods_firing_queries_straight_to_db
+ assert_no_queries(ignore_none: false) do
+ assert_equal [], Developer.none.pluck(:id, :name)
+ assert_equal 0, Developer.none.delete_all
+ assert_equal 0, Developer.none.update_all(name: "David")
+ assert_equal 0, Developer.none.delete(1)
+ assert_equal false, Developer.none.exists?(1)
+ end
+ end
+
+ def test_null_relation_content_size_methods
+ assert_no_queries(ignore_none: false) do
+ assert_equal 0, Developer.none.size
+ assert_equal 0, Developer.none.count
+ assert_equal true, Developer.none.empty?
+ assert_equal true, Developer.none.none?
+ assert_equal false, Developer.none.any?
+ assert_equal false, Developer.none.one?
+ assert_equal false, Developer.none.many?
+ end
+ end
+
+ def test_null_relation_metadata_methods
+ assert_equal "", Developer.none.to_sql
+ assert_equal({}, Developer.none.where_values_hash)
+ end
+
+ def test_null_relation_where_values_hash
+ assert_equal({ "salary" => 100_000 }, Developer.none.where(salary: 100_000).where_values_hash)
+ end
+
+ [:count, :sum].each do |method|
+ define_method "test_null_relation_#{method}" do
+ assert_no_queries(ignore_none: false) do
+ assert_equal 0, Comment.none.public_send(method, :id)
+ assert_equal Hash.new, Comment.none.group(:post_id).public_send(method, :id)
+ end
+ end
+ end
+
+ [:average, :minimum, :maximum].each do |method|
+ define_method "test_null_relation_#{method}" do
+ assert_no_queries(ignore_none: false) do
+ assert_nil Comment.none.public_send(method, :id)
+ assert_equal Hash.new, Comment.none.group(:post_id).public_send(method, :id)
+ end
+ end
+ end
+
+ def test_null_relation_in_where_condition
+ assert_operator Comment.count, :>, 0 # precondition, make sure there are comments.
+ assert_equal 0, Comment.where(post_id: Post.none).count
+ end
+end
diff --git a/activerecord/test/cases/numeric_data_test.rb b/activerecord/test/cases/numeric_data_test.rb
index 76b97033af..f917c8f727 100644
--- a/activerecord/test/cases/numeric_data_test.rb
+++ b/activerecord/test/cases/numeric_data_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "cases/helper"
require "models/numeric_data"
diff --git a/activerecord/test/cases/persistence_test.rb b/activerecord/test/cases/persistence_test.rb
index 5895c51714..d4a4cd564a 100644
--- a/activerecord/test/cases/persistence_test.rb
+++ b/activerecord/test/cases/persistence_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "cases/helper"
require "models/aircraft"
require "models/post"
@@ -57,13 +59,42 @@ class PersistenceTest < ActiveRecord::TestCase
def test_update_all_with_order_and_limit_updates_subset_only
author = authors(:david)
- assert_nothing_raised do
- assert_equal 1, author.posts_sorted_by_id_limited.size
- assert_equal 2, author.posts_sorted_by_id_limited.limit(2).to_a.size
- assert_equal 1, author.posts_sorted_by_id_limited.update_all([ "body = ?", "bulk update!" ])
- assert_equal "bulk update!", posts(:welcome).body
- assert_not_equal "bulk update!", posts(:thinking).body
- end
+ limited_posts = author.posts_sorted_by_id_limited
+ assert_equal 1, limited_posts.size
+ assert_equal 2, limited_posts.limit(2).size
+ assert_equal 1, limited_posts.update_all([ "body = ?", "bulk update!" ])
+ assert_equal "bulk update!", posts(:welcome).body
+ assert_not_equal "bulk update!", posts(:thinking).body
+ end
+
+ def test_update_all_with_order_and_limit_and_offset_updates_subset_only
+ author = authors(:david)
+ limited_posts = author.posts_sorted_by_id_limited.offset(1)
+ assert_equal 1, limited_posts.size
+ assert_equal 2, limited_posts.limit(2).size
+ assert_equal 1, limited_posts.update_all([ "body = ?", "bulk update!" ])
+ assert_equal "bulk update!", posts(:thinking).body
+ assert_not_equal "bulk update!", posts(:welcome).body
+ end
+
+ def test_delete_all_with_order_and_limit_deletes_subset_only
+ author = authors(:david)
+ limited_posts = Post.where(author: author).order(:id).limit(1)
+ assert_equal 1, limited_posts.size
+ assert_equal 2, limited_posts.limit(2).size
+ assert_equal 1, limited_posts.delete_all
+ assert_raise(ActiveRecord::RecordNotFound) { posts(:welcome) }
+ assert posts(:thinking)
+ end
+
+ def test_delete_all_with_order_and_limit_and_offset_deletes_subset_only
+ author = authors(:david)
+ limited_posts = Post.where(author: author).order(:id).limit(1).offset(1)
+ assert_equal 1, limited_posts.size
+ assert_equal 2, limited_posts.limit(2).size
+ assert_equal 1, limited_posts.delete_all
+ assert_raise(ActiveRecord::RecordNotFound) { posts(:thinking) }
+ assert posts(:welcome)
end
end
@@ -71,11 +102,43 @@ class PersistenceTest < ActiveRecord::TestCase
topic_data = { 1 => { "content" => "1 updated" }, 2 => { "content" => "2 updated" } }
updated = Topic.update(topic_data.keys, topic_data.values)
- assert_equal 2, updated.size
+ assert_equal [1, 2], updated.map(&:id)
assert_equal "1 updated", Topic.find(1).content
assert_equal "2 updated", Topic.find(2).content
end
+ def test_update_many_with_duplicated_ids
+ updated = Topic.update([1, 1, 2], [
+ { "content" => "1 duplicated" }, { "content" => "1 updated" }, { "content" => "2 updated" }
+ ])
+
+ assert_equal [1, 1, 2], updated.map(&:id)
+ assert_equal "1 updated", Topic.find(1).content
+ assert_equal "2 updated", Topic.find(2).content
+ end
+
+ def test_update_many_with_invalid_id
+ topic_data = { 1 => { "content" => "1 updated" }, 2 => { "content" => "2 updated" }, 99999 => {} }
+
+ assert_raise(ActiveRecord::RecordNotFound) do
+ Topic.update(topic_data.keys, topic_data.values)
+ end
+
+ assert_not_equal "1 updated", Topic.find(1).content
+ assert_not_equal "2 updated", Topic.find(2).content
+ end
+
+ def test_class_level_update_is_affected_by_scoping
+ topic_data = { 1 => { "content" => "1 updated" }, 2 => { "content" => "2 updated" } }
+
+ assert_raise(ActiveRecord::RecordNotFound) do
+ Topic.where("1=0").scoping { Topic.update(topic_data.keys, topic_data.values) }
+ end
+
+ assert_not_equal "1 updated", Topic.find(1).content
+ assert_not_equal "2 updated", Topic.find(2).content
+ end
+
def test_delete_all
assert Topic.count > 0
@@ -83,27 +146,31 @@ class PersistenceTest < ActiveRecord::TestCase
end
def test_delete_all_with_joins_and_where_part_is_hash
- where_args = { toys: { name: "Bone" } }
- count = Pet.joins(:toys).where(where_args).count
+ pets = Pet.joins(:toys).where(toys: { name: "Bone" })
+
+ assert_equal true, pets.exists?
+ assert_equal pets.count, pets.delete_all
+ end
- assert_equal count, 1
- assert_equal count, Pet.joins(:toys).where(where_args).delete_all
+ def test_delete_all_with_joins_and_where_part_is_not_hash
+ pets = Pet.joins(:toys).where("toys.name = ?", "Bone")
+
+ assert_equal true, pets.exists?
+ assert_equal pets.count, pets.delete_all
end
def test_delete_all_with_left_joins
- where_args = { toys: { name: "Bone" } }
- count = Pet.left_joins(:toys).where(where_args).count
+ pets = Pet.left_joins(:toys).where(toys: { name: "Bone" })
- assert_equal count, 1
- assert_equal count, Pet.left_joins(:toys).where(where_args).delete_all
+ assert_equal true, pets.exists?
+ assert_equal pets.count, pets.delete_all
end
- def test_delete_all_with_joins_and_where_part_is_not_hash
- where_args = ["toys.name = ?", "Bone"]
- count = Pet.joins(:toys).where(where_args).count
+ def test_delete_all_with_includes
+ pets = Pet.includes(:toys).where(toys: { name: "Bone" })
- assert_equal count, 1
- assert_equal count, Pet.joins(:toys).where(where_args).delete_all
+ assert_equal true, pets.exists?
+ assert_equal pets.count, pets.delete_all
end
def test_increment_attribute
@@ -150,7 +217,7 @@ class PersistenceTest < ActiveRecord::TestCase
def test_destroy_all
conditions = "author_name = 'Mary'"
topics_by_mary = Topic.all.merge!(where: conditions, order: "id").to_a
- assert ! topics_by_mary.empty?
+ assert_not_empty topics_by_mary
assert_difference("Topic.count", -topics_by_mary.size) do
destroyed = Topic.where(conditions).destroy_all.sort_by(&:id)
@@ -160,23 +227,40 @@ class PersistenceTest < ActiveRecord::TestCase
end
def test_destroy_many
- clients = Client.all.merge!(order: "id").find([2, 3])
+ clients = Client.find([2, 3])
assert_difference("Client.count", -2) do
- destroyed = Client.destroy([2, 3]).sort_by(&:id)
+ destroyed = Client.destroy([2, 3])
assert_equal clients, destroyed
assert destroyed.all?(&:frozen?), "destroyed clients should be frozen"
end
end
+ def test_destroy_many_with_invalid_id
+ clients = Client.find([2, 3])
+
+ assert_raise(ActiveRecord::RecordNotFound) do
+ Client.destroy([2, 3, 99999])
+ end
+
+ assert_equal clients, Client.find([2, 3])
+ end
+
def test_becomes
assert_kind_of Reply, topics(:first).becomes(Reply)
assert_equal "The First Topic", topics(:first).becomes(Reply).title
end
+ def test_becomes_after_reload_schema_from_cache
+ Reply.define_attribute_methods
+ Reply.serialize(:content) # invoke reload_schema_from_cache
+ assert_kind_of Reply, topics(:first).becomes(Reply)
+ assert_equal "The First Topic", topics(:first).becomes(Reply).title
+ end
+
def test_becomes_includes_errors
company = Company.new(name: nil)
- assert !company.valid?
+ assert_not_predicate company, :valid?
original_errors = company.errors
client = company.becomes(Client)
assert_equal original_errors.keys, client.errors.keys
@@ -293,8 +377,8 @@ class PersistenceTest < ActiveRecord::TestCase
developer.destroy
new_developer = developer.dup
new_developer.save
- assert new_developer.persisted?
- assert_not new_developer.destroyed?
+ assert_predicate new_developer, :persisted?
+ assert_not_predicate new_developer, :destroyed?
end
def test_create_many
@@ -305,12 +389,13 @@ class PersistenceTest < ActiveRecord::TestCase
def test_create_columns_not_equal_attributes
topic = Topic.instantiate(
- "attributes" => {
- "title" => "Another New Topic",
- "does_not_exist" => "test"
- }
+ "title" => "Another New Topic",
+ "does_not_exist" => "test"
)
+ topic = topic.dup # reset @new_record
assert_nothing_raised { topic.save }
+ assert_predicate topic, :persisted?
+ assert_equal "Another New Topic", topic.reload.title
end
def test_create_through_factory_with_block
@@ -357,6 +442,8 @@ class PersistenceTest < ActiveRecord::TestCase
topic_reloaded = Topic.instantiate(topic.attributes.merge("does_not_exist" => "test"))
topic_reloaded.title = "A New Topic"
assert_nothing_raised { topic_reloaded.save }
+ assert_predicate topic_reloaded, :persisted?
+ assert_equal "A New Topic", topic_reloaded.reload.title
end
def test_update_for_record_with_only_primary_key
@@ -393,6 +480,22 @@ class PersistenceTest < ActiveRecord::TestCase
assert_instance_of Reply, Reply.find(reply.id)
end
+ def test_becomes_default_sti_subclass
+ original_type = Topic.columns_hash["type"].default
+ ActiveRecord::Base.connection.change_column_default :topics, :type, "Reply"
+ Topic.reset_column_information
+
+ reply = topics(:second)
+ assert_instance_of Reply, reply
+
+ topic = reply.becomes(Topic)
+ assert_instance_of Topic, topic
+
+ ensure
+ ActiveRecord::Base.connection.change_column_default :topics, :type, original_type
+ Topic.reset_column_information
+ end
+
def test_update_after_create
klass = Class.new(Topic) do
def self.name; "Topic"; end
@@ -437,6 +540,13 @@ class PersistenceTest < ActiveRecord::TestCase
assert_not_nil Topic.find(2)
end
+ def test_delete_isnt_affected_by_scoping
+ topic = Topic.find(1)
+ assert_difference("Topic.count", -1) do
+ Topic.where("1=0").scoping { topic.delete }
+ end
+ end
+
def test_destroy
topic = Topic.find(1)
assert_equal topic, topic.destroy, "topic.destroy did not return self"
@@ -451,10 +561,18 @@ class PersistenceTest < ActiveRecord::TestCase
assert_raise(ActiveRecord::RecordNotFound) { Topic.find(topic.id) }
end
- def test_record_not_found_exception
+ def test_find_raises_record_not_found_exception
assert_raise(ActiveRecord::RecordNotFound) { Topic.find(99999) }
end
+ def test_update_raises_record_not_found_exception
+ assert_raise(ActiveRecord::RecordNotFound) { Topic.update(99999, approved: true) }
+ end
+
+ def test_destroy_raises_record_not_found_exception
+ assert_raise(ActiveRecord::RecordNotFound) { Topic.destroy(99999) }
+ end
+
def test_update_all
assert_equal Topic.count, Topic.update_all("content = 'bulk updated!'")
assert_equal "bulk updated!", Topic.find(1).content
@@ -478,17 +596,24 @@ class PersistenceTest < ActiveRecord::TestCase
end
def test_update_all_with_joins
- where_args = { toys: { name: "Bone" } }
- count = Pet.left_joins(:toys).where(where_args).count
+ pets = Pet.joins(:toys).where(toys: { name: "Bone" })
- assert_equal count, Pet.joins(:toys).where(where_args).update_all(name: "Bob")
+ assert_equal true, pets.exists?
+ assert_equal pets.count, pets.update_all(name: "Bob")
end
def test_update_all_with_left_joins
- where_args = { toys: { name: "Bone" } }
- count = Pet.left_joins(:toys).where(where_args).count
+ pets = Pet.left_joins(:toys).where(toys: { name: "Bone" })
+
+ assert_equal true, pets.exists?
+ assert_equal pets.count, pets.update_all(name: "Bob")
+ end
+
+ def test_update_all_with_includes
+ pets = Pet.includes(:toys).where(toys: { name: "Bone" })
- assert_equal count, Pet.left_joins(:toys).where(where_args).update_all(name: "Bob")
+ assert_equal true, pets.exists?
+ assert_equal pets.count, pets.update_all(name: "Bob")
end
def test_update_all_with_non_standard_table_name
@@ -497,40 +622,65 @@ class PersistenceTest < ActiveRecord::TestCase
end
def test_delete_new_record
- client = Client.new
+ client = Client.new(name: "37signals")
client.delete
- assert client.frozen?
+ assert_predicate client, :frozen?
+
+ assert_not client.save
+ assert_raise(ActiveRecord::RecordNotSaved) { client.save! }
+
+ assert_predicate client, :frozen?
+ assert_raise(RuntimeError) { client.name = "something else" }
end
def test_delete_record_with_associations
client = Client.find(3)
client.delete
- assert client.frozen?
+ assert_predicate client, :frozen?
assert_kind_of Firm, client.firm
+
+ assert_not client.save
+ assert_raise(ActiveRecord::RecordNotSaved) { client.save! }
+
+ assert_predicate client, :frozen?
assert_raise(RuntimeError) { client.name = "something else" }
end
def test_destroy_new_record
- client = Client.new
+ client = Client.new(name: "37signals")
client.destroy
- assert client.frozen?
+ assert_predicate client, :frozen?
+
+ assert_not client.save
+ assert_raise(ActiveRecord::RecordNotSaved) { client.save! }
+
+ assert_predicate client, :frozen?
+ assert_raise(RuntimeError) { client.name = "something else" }
end
def test_destroy_record_with_associations
client = Client.find(3)
client.destroy
- assert client.frozen?
+ assert_predicate client, :frozen?
assert_kind_of Firm, client.firm
+
+ assert_not client.save
+ assert_raise(ActiveRecord::RecordNotSaved) { client.save! }
+
+ assert_predicate client, :frozen?
assert_raise(RuntimeError) { client.name = "something else" }
end
def test_update_attribute
- assert !Topic.find(1).approved?
+ assert_not_predicate Topic.find(1), :approved?
Topic.find(1).update_attribute("approved", true)
- assert Topic.find(1).approved?
+ assert_predicate Topic.find(1), :approved?
Topic.find(1).update_attribute(:approved, false)
- assert !Topic.find(1).approved?
+ assert_not_predicate Topic.find(1), :approved?
+
+ Topic.find(1).update_attribute(:change_approved_before_save, true)
+ assert_predicate Topic.find(1), :approved?
end
def test_update_attribute_for_readonly_attribute
@@ -567,14 +717,14 @@ class PersistenceTest < ActiveRecord::TestCase
def test_update_column
topic = Topic.find(1)
topic.update_column("approved", true)
- assert topic.approved?
+ assert_predicate topic, :approved?
topic.reload
- assert topic.approved?
+ assert_predicate topic, :approved?
topic.update_column(:approved, false)
- assert !topic.approved?
+ assert_not_predicate topic, :approved?
topic.reload
- assert !topic.approved?
+ assert_not_predicate topic, :approved?
end
def test_update_column_should_not_use_setter_method
@@ -661,10 +811,10 @@ class PersistenceTest < ActiveRecord::TestCase
def test_update_columns
topic = Topic.find(1)
topic.update_columns("approved" => true, title: "Sebastian Topic")
- assert topic.approved?
+ assert_predicate topic, :approved?
assert_equal "Sebastian Topic", topic.title
topic.reload
- assert topic.approved?
+ assert_predicate topic, :approved?
assert_equal "Sebastian Topic", topic.title
end
@@ -772,33 +922,33 @@ class PersistenceTest < ActiveRecord::TestCase
def test_update
topic = Topic.find(1)
- assert !topic.approved?
+ assert_not_predicate topic, :approved?
assert_equal "The First Topic", topic.title
topic.update("approved" => true, "title" => "The First Topic Updated")
topic.reload
- assert topic.approved?
+ assert_predicate topic, :approved?
assert_equal "The First Topic Updated", topic.title
topic.update(approved: false, title: "The First Topic")
topic.reload
- assert !topic.approved?
+ assert_not_predicate topic, :approved?
assert_equal "The First Topic", topic.title
end
def test_update_attributes
topic = Topic.find(1)
- assert !topic.approved?
+ assert_not_predicate topic, :approved?
assert_equal "The First Topic", topic.title
topic.update_attributes("approved" => true, "title" => "The First Topic Updated")
topic.reload
- assert topic.approved?
+ assert_predicate topic, :approved?
assert_equal "The First Topic Updated", topic.title
topic.update_attributes(approved: false, title: "The First Topic")
topic.reload
- assert !topic.approved?
+ assert_not_predicate topic, :approved?
assert_equal "The First Topic", topic.title
error = assert_raise(ActiveRecord::RecordNotUnique, ActiveRecord::StatementInvalid) do
@@ -898,18 +1048,41 @@ class PersistenceTest < ActiveRecord::TestCase
should_be_destroyed_reply = Reply.create("title" => "hello", "content" => "world")
Topic.find(1).replies << should_be_destroyed_reply
- Topic.destroy(1)
+ topic = Topic.destroy(1)
+ assert_predicate topic, :destroyed?
+
assert_raise(ActiveRecord::RecordNotFound) { Topic.find(1) }
assert_raise(ActiveRecord::RecordNotFound) { Reply.find(should_be_destroyed_reply.id) }
end
+ def test_class_level_destroy_is_affected_by_scoping
+ should_not_be_destroyed_reply = Reply.create("title" => "hello", "content" => "world")
+ Topic.find(1).replies << should_not_be_destroyed_reply
+
+ assert_raise(ActiveRecord::RecordNotFound) do
+ Topic.where("1=0").scoping { Topic.destroy(1) }
+ end
+
+ assert_nothing_raised { Topic.find(1) }
+ assert_nothing_raised { Reply.find(should_not_be_destroyed_reply.id) }
+ end
+
def test_class_level_delete
- should_be_destroyed_reply = Reply.create("title" => "hello", "content" => "world")
- Topic.find(1).replies << should_be_destroyed_reply
+ should_not_be_destroyed_reply = Reply.create("title" => "hello", "content" => "world")
+ Topic.find(1).replies << should_not_be_destroyed_reply
Topic.delete(1)
assert_raise(ActiveRecord::RecordNotFound) { Topic.find(1) }
- assert_nothing_raised { Reply.find(should_be_destroyed_reply.id) }
+ assert_nothing_raised { Reply.find(should_not_be_destroyed_reply.id) }
+ end
+
+ def test_class_level_delete_is_affected_by_scoping
+ should_not_be_destroyed_reply = Reply.create("title" => "hello", "content" => "world")
+ Topic.find(1).replies << should_not_be_destroyed_reply
+
+ Topic.where("1=0").scoping { Topic.delete(1) }
+ assert_nothing_raised { Topic.find(1) }
+ assert_nothing_raised { Reply.find(should_not_be_destroyed_reply.id) }
end
def test_create_with_custom_timestamps
@@ -956,13 +1129,13 @@ class PersistenceTest < ActiveRecord::TestCase
def test_find_via_reload
post = Post.new
- assert post.new_record?
+ assert_predicate post, :new_record?
post.id = 1
post.reload
assert_equal "Welcome to the weblog", post.title
- assert_not post.new_record?
+ assert_not_predicate post, :new_record?
end
def test_reload_via_querycache
@@ -993,44 +1166,35 @@ class PersistenceTest < ActiveRecord::TestCase
end
class SaveTest < ActiveRecord::TestCase
- self.use_transactional_tests = false
-
def test_save_touch_false
- widget = Class.new(ActiveRecord::Base) do
- connection.create_table :widgets, force: true do |t|
- t.string :name
- t.timestamps null: false
- end
-
- self.table_name = :widgets
- end
-
- instance = widget.create!(
+ pet = Pet.create!(
name: "Bob",
created_at: 1.day.ago,
updated_at: 1.day.ago)
- created_at = instance.created_at
- updated_at = instance.updated_at
+ created_at = pet.created_at
+ updated_at = pet.updated_at
- instance.name = "Barb"
- instance.save!(touch: false)
- assert_equal instance.created_at, created_at
- assert_equal instance.updated_at, updated_at
- ensure
- ActiveRecord::Base.connection.drop_table widget.table_name
- widget.reset_column_information
+ pet.name = "Barb"
+ pet.save!(touch: false)
+ assert_equal pet.created_at, created_at
+ assert_equal pet.updated_at, updated_at
end
end
def test_reset_column_information_resets_children
- child = Class.new(Topic)
- child.new # force schema to load
+ child_class = Class.new(Topic)
+ child_class.new # force schema to load
ActiveRecord::Base.connection.add_column(:topics, :foo, :string)
Topic.reset_column_information
- assert_equal "bar", child.new(foo: :bar).foo
+ # this should redefine attribute methods
+ child_class.new
+
+ assert child_class.instance_methods.include?(:foo)
+ assert child_class.instance_methods.include?(:foo_changed?)
+ assert_equal "bar", child_class.new(foo: :bar).foo
ensure
ActiveRecord::Base.connection.remove_column(:topics, :foo)
Topic.reset_column_information
diff --git a/activerecord/test/cases/pooled_connections_test.rb b/activerecord/test/cases/pooled_connections_test.rb
index f1b0d08765..fa7f759e51 100644
--- a/activerecord/test/cases/pooled_connections_test.rb
+++ b/activerecord/test/cases/pooled_connections_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "cases/helper"
require "models/project"
require "timeout"
diff --git a/activerecord/test/cases/primary_keys_test.rb b/activerecord/test/cases/primary_keys_test.rb
index 56229b70bc..60dac91ec9 100644
--- a/activerecord/test/cases/primary_keys_test.rb
+++ b/activerecord/test/cases/primary_keys_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "cases/helper"
require "support/schema_dumping_helper"
require "models/topic"
@@ -154,10 +156,6 @@ class PrimaryKeysTest < ActiveRecord::TestCase
assert_nothing_raised { MixedCaseMonkey.find(1).destroy }
end
- def test_deprecate_supports_primary_key
- assert_deprecated { ActiveRecord::Base.connection.supports_primary_key? }
- end
-
def test_primary_key_returns_value_if_it_exists
klass = Class.new(ActiveRecord::Base) do
self.table_name = "developers"
@@ -209,13 +207,13 @@ class PrimaryKeysTest < ActiveRecord::TestCase
def test_serial_with_quoted_sequence_name
column = MixedCaseMonkey.columns_hash[MixedCaseMonkey.primary_key]
assert_equal "nextval('\"mixed_case_monkeys_monkeyID_seq\"'::regclass)", column.default_function
- assert column.serial?
+ assert_predicate column, :serial?
end
def test_serial_with_unquoted_sequence_name
column = Topic.columns_hash[Topic.primary_key]
assert_equal "nextval('topics_id_seq'::regclass)", column.default_function
- assert column.serial?
+ assert_predicate column, :serial?
end
end
end
@@ -300,6 +298,8 @@ class PrimaryKeyAnyTypeTest < ActiveRecord::TestCase
assert_not column.null
assert_equal :string, column.type
assert_equal 42, column.limit
+ ensure
+ Barcode.reset_column_information
end
test "schema dump primary key includes type and options" do
@@ -324,7 +324,7 @@ class CompositePrimaryKeyTest < ActiveRecord::TestCase
def setup
@connection = ActiveRecord::Base.connection
@connection.schema_cache.clear!
- @connection.create_table(:barcodes, primary_key: ["region", "code"], force: true) do |t|
+ @connection.create_table(:uber_barcodes, primary_key: ["region", "code"], force: true) do |t|
t.string :region
t.integer :code
end
@@ -332,14 +332,24 @@ class CompositePrimaryKeyTest < ActiveRecord::TestCase
t.string :region
t.integer :code
end
+ @connection.create_table(:travels, primary_key: ["from", "to"], force: true) do |t|
+ t.string :from
+ t.string :to
+ end
end
def teardown
- @connection.drop_table(:barcodes, if_exists: true)
+ @connection.drop_table :uber_barcodes, if_exists: true
+ @connection.drop_table :barcodes_reverse, if_exists: true
+ @connection.drop_table :travels, if_exists: true
end
def test_composite_primary_key
- assert_equal ["region", "code"], @connection.primary_keys("barcodes")
+ assert_equal ["region", "code"], @connection.primary_keys("uber_barcodes")
+ end
+
+ def test_composite_primary_key_with_reserved_words
+ assert_equal ["from", "to"], @connection.primary_keys("travels")
end
def test_composite_primary_key_out_of_order
@@ -350,7 +360,7 @@ class CompositePrimaryKeyTest < ActiveRecord::TestCase
def test_primary_key_issues_warning
model = Class.new(ActiveRecord::Base) do
def self.table_name
- "barcodes"
+ "uber_barcodes"
end
end
warning = capture(:stderr) do
@@ -359,9 +369,9 @@ class CompositePrimaryKeyTest < ActiveRecord::TestCase
assert_match(/WARNING: Active Record does not support composite primary key\./, warning)
end
- def test_dumping_composite_primary_key
- schema = dump_table_schema "barcodes"
- assert_match %r{create_table "barcodes", primary_key: \["region", "code"\]}, schema
+ def test_collectly_dump_composite_primary_key
+ schema = dump_table_schema "uber_barcodes"
+ assert_match %r{create_table "uber_barcodes", primary_key: \["region", "code"\]}, schema
end
def test_dumping_composite_primary_key_out_of_order
@@ -420,7 +430,7 @@ if current_adapter?(:PostgreSQLAdapter, :Mysql2Adapter)
@connection.create_table(:widgets, id: @pk_type, force: true)
column = @connection.columns(:widgets).find { |c| c.name == "id" }
assert_equal :integer, column.type
- assert_not column.bigint?
+ assert_not_predicate column, :bigint?
end
test "primary key with serial/integer are automatically numbered" do
@@ -432,32 +442,32 @@ if current_adapter?(:PostgreSQLAdapter, :Mysql2Adapter)
test "schema dump primary key with serial/integer" do
@connection.create_table(:widgets, id: @pk_type, force: true)
schema = dump_table_schema "widgets"
- assert_match %r{create_table "widgets", id: :#{@pk_type}, force: :cascade}, schema
+ assert_match %r{create_table "widgets", id: :#{@pk_type}, }, schema
end
if current_adapter?(:Mysql2Adapter)
test "primary key column type with options" do
@connection.create_table(:widgets, id: :primary_key, limit: 4, unsigned: true, force: true)
column = @connection.columns(:widgets).find { |c| c.name == "id" }
- assert column.auto_increment?
+ assert_predicate column, :auto_increment?
assert_equal :integer, column.type
- assert_not column.bigint?
- assert column.unsigned?
+ assert_not_predicate column, :bigint?
+ assert_predicate column, :unsigned?
schema = dump_table_schema "widgets"
- assert_match %r{create_table "widgets", id: :integer, unsigned: true, force: :cascade}, schema
+ assert_match %r{create_table "widgets", id: :integer, unsigned: true, }, schema
end
test "bigint primary key with unsigned" do
@connection.create_table(:widgets, id: :bigint, unsigned: true, force: true)
column = @connection.columns(:widgets).find { |c| c.name == "id" }
- assert column.auto_increment?
+ assert_predicate column, :auto_increment?
assert_equal :integer, column.type
- assert column.bigint?
- assert column.unsigned?
+ assert_predicate column, :bigint?
+ assert_predicate column, :unsigned?
schema = dump_table_schema "widgets"
- assert_match %r{create_table "widgets", id: :bigint, unsigned: true, force: :cascade}, schema
+ assert_match %r{create_table "widgets", id: :bigint, unsigned: true, }, schema
end
end
end
diff --git a/activerecord/test/cases/query_cache_test.rb b/activerecord/test/cases/query_cache_test.rb
index 9b741545d7..8d37f5ea95 100644
--- a/activerecord/test/cases/query_cache_test.rb
+++ b/activerecord/test/cases/query_cache_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "cases/helper"
require "models/topic"
require "models/task"
@@ -42,7 +44,8 @@ class QueryCacheTest < ActiveRecord::TestCase
mw = middleware { |env|
Task.find 1
Task.find 1
- assert_equal 1, ActiveRecord::Base.connection.query_cache.length
+ query_cache = ActiveRecord::Base.connection.query_cache
+ assert_equal 1, query_cache.length, query_cache.keys
raise "lol borked"
}
assert_raises(RuntimeError) { mw.call({}) }
@@ -79,7 +82,7 @@ class QueryCacheTest < ActiveRecord::TestCase
assert_cache :off, conn
end
- assert !ActiveRecord::Base.connection.nil?
+ assert_not_predicate ActiveRecord::Base.connection, :nil?
assert_cache :off
middleware {
@@ -130,7 +133,7 @@ class QueryCacheTest < ActiveRecord::TestCase
assert_cache :off, conn
end
ensure
- ActiveRecord::Base.clear_all_connections!
+ ActiveRecord::Base.connection_pool.disconnect!
end
end
end
@@ -149,7 +152,8 @@ class QueryCacheTest < ActiveRecord::TestCase
mw = middleware { |env|
Task.find 1
Task.find 1
- assert_equal 1, ActiveRecord::Base.connection.query_cache.length
+ query_cache = ActiveRecord::Base.connection.query_cache
+ assert_equal 1, query_cache.length, query_cache.keys
[200, {}, nil]
}
mw.call({})
@@ -279,7 +283,7 @@ class QueryCacheTest < ActiveRecord::TestCase
payload[:sql].downcase!
end
- assert_raises RuntimeError do
+ assert_raises frozen_error_class do
ActiveRecord::Base.cache do
assert_queries(1) { Task.find(1); Task.find(1) }
end
@@ -298,14 +302,10 @@ class QueryCacheTest < ActiveRecord::TestCase
end
end
- def test_cache_does_not_wrap_string_results_in_arrays
+ def test_cache_does_not_wrap_results_in_arrays
Task.cache do
- # Oracle adapter returns count() as Integer or Float
- if current_adapter?(:OracleAdapter)
- assert_kind_of Numeric, Task.connection.select_value("SELECT count(*) AS count_all FROM tasks")
- elsif current_adapter?(:SQLite3Adapter, :Mysql2Adapter, :PostgreSQLAdapter)
- # Future versions of the sqlite3 adapter will return numeric
- assert_instance_of 0.class, Task.connection.select_value("SELECT count(*) AS count_all FROM tasks")
+ if current_adapter?(:SQLite3Adapter, :Mysql2Adapter, :PostgreSQLAdapter, :OracleAdapter)
+ assert_equal 2, Task.connection.select_value("SELECT count(*) AS count_all FROM tasks")
else
assert_instance_of String, Task.connection.select_value("SELECT count(*) AS count_all FROM tasks")
end
@@ -320,36 +320,18 @@ class QueryCacheTest < ActiveRecord::TestCase
end
end
- def test_cache_is_available_when_connection_is_connected
- conf = ActiveRecord::Base.configurations
-
- ActiveRecord::Base.configurations = {}
- Task.cache do
- assert_queries(1) { Task.find(1); Task.find(1) }
- end
- ensure
- ActiveRecord::Base.configurations = conf
- end
-
- def test_cache_is_not_available_when_using_a_not_connected_connection
+ def test_cache_is_available_when_using_a_not_connected_connection
+ skip "In-Memory DB can't test for using a not connected connection" if in_memory_db?
with_temporary_connection_pool do
spec_name = Task.connection_specification_name
conf = ActiveRecord::Base.configurations["arunit"].merge("name" => "test2")
ActiveRecord::Base.connection_handler.establish_connection(conf)
Task.connection_specification_name = "test2"
- refute Task.connected?
+ assert_not_predicate Task, :connected?
Task.cache do
begin
- if in_memory_db?
- Task.connection.create_table :tasks do |t|
- t.datetime :starting
- t.datetime :ending
- end
- ActiveRecord::FixtureSet.create_fixtures(self.class.fixture_path, ["tasks"], {}, ActiveRecord::Base)
- end
- Task.connection # warmup postgresql connection setup queries
- assert_queries(2) { Task.find(1); Task.find(1) }
+ assert_queries(1) { Task.find(1); Task.find(1) }
ensure
ActiveRecord::Base.connection_handler.remove_connection(Task.connection_specification_name)
Task.connection_specification_name = spec_name
@@ -416,10 +398,8 @@ class QueryCacheTest < ActiveRecord::TestCase
# Warm the cache
Task.find(1)
- Task.connection.type_map.clear
-
# Preload the type cache again (so we don't have those queries issued during our assertions)
- Task.connection.send(:initialize_type_map, Task.connection.type_map)
+ Task.connection.send(:reload_type_map)
# Clear places where type information is cached
Task.reset_column_information
@@ -434,20 +414,20 @@ class QueryCacheTest < ActiveRecord::TestCase
def test_query_cache_does_not_establish_connection_if_unconnected
with_temporary_connection_pool do
ActiveRecord::Base.clear_active_connections!
- refute ActiveRecord::Base.connection_handler.active_connections? # sanity check
+ assert_not ActiveRecord::Base.connection_handler.active_connections? # sanity check
middleware {
- refute ActiveRecord::Base.connection_handler.active_connections?, "QueryCache forced ActiveRecord::Base to establish a connection in setup"
+ assert_not ActiveRecord::Base.connection_handler.active_connections?, "QueryCache forced ActiveRecord::Base to establish a connection in setup"
}.call({})
- refute ActiveRecord::Base.connection_handler.active_connections?, "QueryCache forced ActiveRecord::Base to establish a connection in cleanup"
+ assert_not ActiveRecord::Base.connection_handler.active_connections?, "QueryCache forced ActiveRecord::Base to establish a connection in cleanup"
end
end
def test_query_cache_is_enabled_on_connections_established_after_middleware_runs
with_temporary_connection_pool do
ActiveRecord::Base.clear_active_connections!
- refute ActiveRecord::Base.connection_handler.active_connections? # sanity check
+ assert_not ActiveRecord::Base.connection_handler.active_connections? # sanity check
middleware {
assert ActiveRecord::Base.connection.query_cache_enabled, "QueryCache did not get lazily enabled"
@@ -464,14 +444,23 @@ class QueryCacheTest < ActiveRecord::TestCase
assert ActiveRecord::Base.connection.query_cache_enabled
Thread.new {
- refute ActiveRecord::Base.connection_pool.query_cache_enabled
- refute ActiveRecord::Base.connection.query_cache_enabled
+ assert_not ActiveRecord::Base.connection_pool.query_cache_enabled
+ assert_not ActiveRecord::Base.connection.query_cache_enabled
}.join
}.call({})
end
end
+ def test_query_cache_is_enabled_on_all_connection_pools
+ middleware {
+ ActiveRecord::Base.connection_handler.connection_pool_list.each do |pool|
+ assert pool.query_cache_enabled
+ assert pool.connection.query_cache_enabled
+ end
+ }.call({})
+ end
+
private
def middleware(&app)
executor = Class.new(ActiveSupport::Executor)
@@ -573,7 +562,7 @@ class QueryCacheExpiryTest < ActiveRecord::TestCase
assert_called(ActiveRecord::Base.connection, :clear_query_cache, times: 2) do
ActiveRecord::Base.cache do
p = Post.find(1)
- assert p.categories.any?
+ assert_predicate p.categories, :any?
p.categories.delete_all
end
end
diff --git a/activerecord/test/cases/quoting_test.rb b/activerecord/test/cases/quoting_test.rb
index 0819776fbf..6534770c57 100644
--- a/activerecord/test/cases/quoting_test.rb
+++ b/activerecord/test/cases/quoting_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "cases/helper"
module ActiveRecord
@@ -8,11 +10,11 @@ module ActiveRecord
end
def test_quoted_true
- assert_equal "'t'", @quoter.quoted_true
+ assert_equal "TRUE", @quoter.quoted_true
end
def test_quoted_false
- assert_equal "'f'", @quoter.quoted_false
+ assert_equal "FALSE", @quoter.quoted_false
end
def test_quote_column_name
@@ -81,23 +83,6 @@ module ActiveRecord
end
end
- class QuotedOne
- def quoted_id
- 1
- end
- end
- class SubQuotedOne < QuotedOne
- end
- def test_quote_with_quoted_id
- assert_deprecated(/defined on \S+::QuotedOne at .*quoting_test\.rb:[0-9]/) do
- assert_equal 1, @quoter.quote(QuotedOne.new)
- end
-
- assert_deprecated(/defined on \S+::SubQuotedOne\(\S+::QuotedOne\) at .*quoting_test\.rb:[0-9]/) do
- assert_equal 1, @quoter.quote(SubQuotedOne.new)
- end
- end
-
def test_quote_nil
assert_equal "NULL", @quoter.quote(nil)
end
@@ -126,7 +111,7 @@ module ActiveRecord
end
def test_quote_bigdecimal
- bigdec = BigDecimal.new((1 << 100).to_s)
+ bigdec = BigDecimal((1 << 100).to_s)
assert_equal bigdec.to_s("F"), @quoter.quote(bigdec)
end
@@ -174,13 +159,21 @@ module ActiveRecord
def test_type_cast_date
date = Date.today
- expected = @conn.quoted_date(date)
+ if current_adapter?(:Mysql2Adapter)
+ expected = date
+ else
+ expected = @conn.quoted_date(date)
+ end
assert_equal expected, @conn.type_cast(date)
end
def test_type_cast_time
time = Time.now
- expected = @conn.quoted_date(time)
+ if current_adapter?(:Mysql2Adapter)
+ expected = time
+ else
+ expected = @conn.quoted_date(time)
+ end
assert_equal expected, @conn.type_cast(time)
end
@@ -197,26 +190,6 @@ module ActiveRecord
obj = Class.new.new
assert_raise(TypeError) { @conn.type_cast(obj) }
end
-
- def test_type_cast_object_which_responds_to_quoted_id
- quoted_id_obj = Class.new {
- def quoted_id
- "'zomg'"
- end
-
- def id
- 10
- end
- }.new
- assert_equal 10, @conn.type_cast(quoted_id_obj)
-
- quoted_id_obj = Class.new {
- def quoted_id
- "'zomg'"
- end
- }.new
- assert_raise(TypeError) { @conn.type_cast(quoted_id_obj) }
- end
end
class QuoteBooleanTest < ActiveRecord::TestCase
@@ -257,7 +230,7 @@ module ActiveRecord
def test_type_cast_ar_object
value = DatetimePrimaryKey.new(id: @time)
- assert_equal "2017-02-14 12:34:56.789000", @connection.type_cast(value)
+ assert_equal @connection.type_cast(value.id), @connection.type_cast(value)
end
end
end
diff --git a/activerecord/test/cases/readonly_test.rb b/activerecord/test/cases/readonly_test.rb
index 24b678310d..383e43ed55 100644
--- a/activerecord/test/cases/readonly_test.rb
+++ b/activerecord/test/cases/readonly_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "cases/helper"
require "models/author"
require "models/post"
@@ -14,10 +16,10 @@ class ReadOnlyTest < ActiveRecord::TestCase
def test_cant_save_readonly_record
dev = Developer.find(1)
- assert !dev.readonly?
+ assert_not_predicate dev, :readonly?
dev.readonly!
- assert dev.readonly?
+ assert_predicate dev, :readonly?
assert_nothing_raised do
dev.name = "Luscious forbidden fruit."
@@ -52,7 +54,7 @@ class ReadOnlyTest < ActiveRecord::TestCase
def test_has_many_find_readonly
post = Post.find(1)
- assert !post.comments.empty?
+ assert_not_empty post.comments
assert !post.comments.any?(&:readonly?)
assert !post.comments.to_a.any?(&:readonly?)
assert post.comments.readonly(true).all?(&:readonly?)
@@ -64,44 +66,44 @@ class ReadOnlyTest < ActiveRecord::TestCase
end
def test_has_many_with_through_is_not_implicitly_marked_readonly_while_finding_by_id
- assert !posts(:welcome).people.find(1).readonly?
+ assert_not_predicate posts(:welcome).people.find(1), :readonly?
end
def test_has_many_with_through_is_not_implicitly_marked_readonly_while_finding_first
- assert !posts(:welcome).people.first.readonly?
+ assert_not_predicate posts(:welcome).people.first, :readonly?
end
def test_has_many_with_through_is_not_implicitly_marked_readonly_while_finding_last
- assert !posts(:welcome).people.last.readonly?
+ assert_not_predicate posts(:welcome).people.last, :readonly?
end
def test_readonly_scoping
Post.where("1=1").scoping do
- assert !Post.find(1).readonly?
- assert Post.readonly(true).find(1).readonly?
- assert !Post.readonly(false).find(1).readonly?
+ assert_not_predicate Post.find(1), :readonly?
+ assert_predicate Post.readonly(true).find(1), :readonly?
+ assert_not_predicate Post.readonly(false).find(1), :readonly?
end
Post.joins(" ").scoping do
- assert !Post.find(1).readonly?
- assert Post.readonly.find(1).readonly?
- assert !Post.readonly(false).find(1).readonly?
+ assert_not_predicate Post.find(1), :readonly?
+ assert_predicate Post.readonly.find(1), :readonly?
+ assert_not_predicate Post.readonly(false).find(1), :readonly?
end
# Oracle barfs on this because the join includes unqualified and
# conflicting column names
unless current_adapter?(:OracleAdapter)
Post.joins(", developers").scoping do
- assert_not Post.find(1).readonly?
- assert Post.readonly.find(1).readonly?
- assert !Post.readonly(false).find(1).readonly?
+ assert_not_predicate Post.find(1), :readonly?
+ assert_predicate Post.readonly.find(1), :readonly?
+ assert_not_predicate Post.readonly(false).find(1), :readonly?
end
end
Post.readonly(true).scoping do
- assert Post.find(1).readonly?
- assert Post.readonly.find(1).readonly?
- assert !Post.readonly(false).find(1).readonly?
+ assert_predicate Post.find(1), :readonly?
+ assert_predicate Post.readonly.find(1), :readonly?
+ assert_not_predicate Post.readonly(false).find(1), :readonly?
end
end
@@ -109,10 +111,10 @@ class ReadOnlyTest < ActiveRecord::TestCase
developer = Developer.find(1)
project = Post.find(1)
- assert !developer.projects.all_as_method.first.readonly?
- assert !developer.projects.all_as_scope.first.readonly?
+ assert_not_predicate developer.projects.all_as_method.first, :readonly?
+ assert_not_predicate developer.projects.all_as_scope.first, :readonly?
- assert !project.comments.all_as_method.first.readonly?
- assert !project.comments.all_as_scope.first.readonly?
+ assert_not_predicate project.comments.all_as_method.first, :readonly?
+ assert_not_predicate project.comments.all_as_scope.first, :readonly?
end
end
diff --git a/activerecord/test/cases/reaper_test.rb b/activerecord/test/cases/reaper_test.rb
index 249878b67d..61cb0f130d 100644
--- a/activerecord/test/cases/reaper_test.rb
+++ b/activerecord/test/cases/reaper_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "cases/helper"
module ActiveRecord
@@ -16,6 +18,7 @@ module ActiveRecord
class FakePool
attr_reader :reaped
+ attr_reader :flushed
def initialize
@reaped = false
@@ -24,6 +27,10 @@ module ActiveRecord
def reap
@reaped = true
end
+
+ def flush
+ @flushed = true
+ end
end
# A reaper with nil time should never reap connections
@@ -45,6 +52,7 @@ module ActiveRecord
Thread.pass
end
assert fp.reaped
+ assert fp.flushed
end
def test_pool_has_reaper
@@ -71,14 +79,14 @@ module ActiveRecord
end
Thread.pass while conn.nil?
- assert conn.in_use?
+ assert_predicate conn, :in_use?
child.terminate
while conn.in_use?
Thread.pass
end
- assert !conn.in_use?
+ assert_not_predicate conn, :in_use?
end
end
end
diff --git a/activerecord/test/cases/reflection_test.rb b/activerecord/test/cases/reflection_test.rb
index c1c2efb9c8..ed19192ad9 100644
--- a/activerecord/test/cases/reflection_test.rb
+++ b/activerecord/test/cases/reflection_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "cases/helper"
require "models/topic"
require "models/customer"
@@ -23,7 +25,6 @@ require "models/chef"
require "models/department"
require "models/cake_designer"
require "models/drink_designer"
-require "models/mocktail_designer"
require "models/recipe"
class ReflectionTest < ActiveRecord::TestCase
@@ -65,6 +66,9 @@ class ReflectionTest < ActiveRecord::TestCase
def test_column_string_type_and_limit
assert_equal :string, @first.column_for_attribute("title").type
+ assert_equal :string, @first.column_for_attribute(:title).type
+ assert_equal :string, @first.type_for_attribute("title").type
+ assert_equal :string, @first.type_for_attribute(:title).type
assert_equal 250, @first.column_for_attribute("title").limit
end
@@ -80,6 +84,9 @@ class ReflectionTest < ActiveRecord::TestCase
def test_integer_columns
assert_equal :integer, @first.column_for_attribute("id").type
+ assert_equal :integer, @first.column_for_attribute(:id).type
+ assert_equal :integer, @first.type_for_attribute("id").type
+ assert_equal :integer, @first.type_for_attribute(:id).type
end
def test_non_existent_columns_return_null_object
@@ -88,6 +95,9 @@ class ReflectionTest < ActiveRecord::TestCase
assert_equal "attribute_that_doesnt_exist", column.name
assert_nil column.sql_type
assert_nil column.type
+
+ column = @first.column_for_attribute(:attribute_that_doesnt_exist)
+ assert_instance_of ActiveRecord::ConnectionAdapters::NullColumn, column
end
def test_non_existent_types_are_identity_types
@@ -97,6 +107,11 @@ class ReflectionTest < ActiveRecord::TestCase
assert_equal object, type.deserialize(object)
assert_equal object, type.cast(object)
assert_equal object, type.serialize(object)
+
+ type = @first.type_for_attribute(:attribute_that_doesnt_exist)
+ assert_equal object, type.deserialize(object)
+ assert_equal object, type.cast(object)
+ assert_equal object, type.serialize(object)
end
def test_reflection_klass_for_nested_class_name
@@ -148,7 +163,7 @@ class ReflectionTest < ActiveRecord::TestCase
expected = Pirate.reflect_on_all_associations.select { |r| r.options[:autosave] }
received = Pirate.reflect_on_all_autosave_associations
- assert !received.empty?
+ assert_not_empty received
assert_not_equal Pirate.reflect_on_all_associations.length, received.length
assert_equal expected, received
end
@@ -252,50 +267,35 @@ class ReflectionTest < ActiveRecord::TestCase
assert_equal expected, actual
end
- def test_scope_chain
- expected = [
- [Tagging.reflect_on_association(:tag).scope, Post.reflect_on_association(:first_blue_tags).scope],
- [Post.reflect_on_association(:first_taggings).scope],
- [Author.reflect_on_association(:misc_posts).scope]
- ]
- actual = assert_deprecated do
- Author.reflect_on_association(:misc_post_first_blue_tags).scope_chain
- end
- assert_equal expected, actual
-
- expected = [
- [
- Tagging.reflect_on_association(:blue_tag).scope,
- Post.reflect_on_association(:first_blue_tags_2).scope,
- Author.reflect_on_association(:misc_post_first_blue_tags_2).scope
- ],
- [],
- []
- ]
- actual = assert_deprecated do
- Author.reflect_on_association(:misc_post_first_blue_tags_2).scope_chain
- end
- assert_equal expected, actual
- end
-
def test_scope_chain_does_not_interfere_with_hmt_with_polymorphic_case
- @hotel = Hotel.create!
- @department = @hotel.departments.create!
- @department.chefs.create!(employable: CakeDesigner.create!)
- @department.chefs.create!(employable: DrinkDesigner.create!)
+ hotel = Hotel.create!
+ department = hotel.departments.create!
+ department.chefs.create!(employable: CakeDesigner.create!)
+ department.chefs.create!(employable: DrinkDesigner.create!)
- assert_equal 1, @hotel.cake_designers.size
- assert_equal 1, @hotel.drink_designers.size
- assert_equal 2, @hotel.chefs.size
+ assert_equal 1, hotel.cake_designers.size
+ assert_equal 1, hotel.cake_designers.count
+ assert_equal 1, hotel.drink_designers.size
+ assert_equal 1, hotel.drink_designers.count
+ assert_equal 2, hotel.chefs.size
+ assert_equal 2, hotel.chefs.count
end
def test_scope_chain_does_not_interfere_with_hmt_with_polymorphic_case_and_sti
- @hotel = Hotel.create!
- @hotel.mocktail_designers << MocktailDesigner.create!
+ hotel = Hotel.create!
+ hotel.mocktail_designers << MocktailDesigner.create!
+
+ assert_equal 1, hotel.mocktail_designers.size
+ assert_equal 1, hotel.mocktail_designers.count
+ assert_equal 1, hotel.chef_lists.size
+ assert_equal 1, hotel.chef_lists.count
- assert_equal 1, @hotel.mocktail_designers.size
- assert_equal 1, @hotel.mocktail_designers.count
- assert_equal 1, @hotel.chef_lists.size
+ hotel.mocktail_designers = []
+
+ assert_equal 0, hotel.mocktail_designers.size
+ assert_equal 0, hotel.mocktail_designers.count
+ assert_equal 0, hotel.chef_lists.size
+ assert_equal 0, hotel.chef_lists.count
end
def test_scope_chain_of_polymorphic_association_does_not_leak_into_other_hmt_associations
@@ -315,12 +315,12 @@ class ReflectionTest < ActiveRecord::TestCase
end
def test_nested?
- assert !Author.reflect_on_association(:comments).nested?
- assert Author.reflect_on_association(:tags).nested?
+ assert_not_predicate Author.reflect_on_association(:comments), :nested?
+ assert_predicate Author.reflect_on_association(:tags), :nested?
# Only goes :through once, but the through_reflection is a has_and_belongs_to_many, so this is
# a nested through association
- assert Category.reflect_on_association(:post_comments).nested?
+ assert_predicate Category.reflect_on_association(:post_comments), :nested?
end
def test_association_primary_key
@@ -335,15 +335,6 @@ class ReflectionTest < ActiveRecord::TestCase
assert_equal "custom_primary_key", Author.reflect_on_association(:tags_with_primary_key).association_primary_key.to_s # nested
end
- def test_association_primary_key_type
- # Normal Association
- assert_equal :integer, Author.reflect_on_association(:posts).association_primary_key_type.type
- assert_equal :string, Author.reflect_on_association(:essay).association_primary_key_type.type
-
- # Through Association
- assert_equal :string, Author.reflect_on_association(:essay_category).association_primary_key_type.type
- end
-
def test_association_primary_key_raises_when_missing_primary_key
reflection = ActiveRecord::Reflection.create(:has_many, :edge, nil, {}, Author)
assert_raises(ActiveRecord::UnknownPrimaryKey) { reflection.association_primary_key }
@@ -364,42 +355,49 @@ class ReflectionTest < ActiveRecord::TestCase
assert_raises(ActiveRecord::UnknownPrimaryKey) { reflection.active_record_primary_key }
end
+ def test_type
+ assert_equal "taggable_type", Post.reflect_on_association(:taggings).type.to_s
+ assert_equal "imageable_class", Post.reflect_on_association(:images).type.to_s
+ assert_nil Post.reflect_on_association(:readers).type
+ end
+
def test_foreign_type
assert_equal "sponsorable_type", Sponsor.reflect_on_association(:sponsorable).foreign_type.to_s
assert_equal "sponsorable_type", Sponsor.reflect_on_association(:thing).foreign_type.to_s
+ assert_nil Sponsor.reflect_on_association(:sponsor_club).foreign_type
end
def test_collection_association
- assert Pirate.reflect_on_association(:birds).collection?
- assert Pirate.reflect_on_association(:parrots).collection?
+ assert_predicate Pirate.reflect_on_association(:birds), :collection?
+ assert_predicate Pirate.reflect_on_association(:parrots), :collection?
- assert !Pirate.reflect_on_association(:ship).collection?
- assert !Ship.reflect_on_association(:pirate).collection?
+ assert_not_predicate Pirate.reflect_on_association(:ship), :collection?
+ assert_not_predicate Ship.reflect_on_association(:pirate), :collection?
end
def test_default_association_validation
- assert ActiveRecord::Reflection.create(:has_many, :clients, nil, {}, Firm).validate?
+ assert_predicate ActiveRecord::Reflection.create(:has_many, :clients, nil, {}, Firm), :validate?
- assert !ActiveRecord::Reflection.create(:has_one, :client, nil, {}, Firm).validate?
- assert !ActiveRecord::Reflection.create(:belongs_to, :client, nil, {}, Firm).validate?
+ assert_not_predicate ActiveRecord::Reflection.create(:has_one, :client, nil, {}, Firm), :validate?
+ assert_not_predicate ActiveRecord::Reflection.create(:belongs_to, :client, nil, {}, Firm), :validate?
end
def test_always_validate_association_if_explicit
- assert ActiveRecord::Reflection.create(:has_one, :client, nil, { validate: true }, Firm).validate?
- assert ActiveRecord::Reflection.create(:belongs_to, :client, nil, { validate: true }, Firm).validate?
- assert ActiveRecord::Reflection.create(:has_many, :clients, nil, { validate: true }, Firm).validate?
+ assert_predicate ActiveRecord::Reflection.create(:has_one, :client, nil, { validate: true }, Firm), :validate?
+ assert_predicate ActiveRecord::Reflection.create(:belongs_to, :client, nil, { validate: true }, Firm), :validate?
+ assert_predicate ActiveRecord::Reflection.create(:has_many, :clients, nil, { validate: true }, Firm), :validate?
end
def test_validate_association_if_autosave
- assert ActiveRecord::Reflection.create(:has_one, :client, nil, { autosave: true }, Firm).validate?
- assert ActiveRecord::Reflection.create(:belongs_to, :client, nil, { autosave: true }, Firm).validate?
- assert ActiveRecord::Reflection.create(:has_many, :clients, nil, { autosave: true }, Firm).validate?
+ assert_predicate ActiveRecord::Reflection.create(:has_one, :client, nil, { autosave: true }, Firm), :validate?
+ assert_predicate ActiveRecord::Reflection.create(:belongs_to, :client, nil, { autosave: true }, Firm), :validate?
+ assert_predicate ActiveRecord::Reflection.create(:has_many, :clients, nil, { autosave: true }, Firm), :validate?
end
def test_never_validate_association_if_explicit
- assert !ActiveRecord::Reflection.create(:has_one, :client, nil, { autosave: true, validate: false }, Firm).validate?
- assert !ActiveRecord::Reflection.create(:belongs_to, :client, nil, { autosave: true, validate: false }, Firm).validate?
- assert !ActiveRecord::Reflection.create(:has_many, :clients, nil, { autosave: true, validate: false }, Firm).validate?
+ assert_not_predicate ActiveRecord::Reflection.create(:has_one, :client, nil, { autosave: true, validate: false }, Firm), :validate?
+ assert_not_predicate ActiveRecord::Reflection.create(:belongs_to, :client, nil, { autosave: true, validate: false }, Firm), :validate?
+ assert_not_predicate ActiveRecord::Reflection.create(:has_many, :clients, nil, { autosave: true, validate: false }, Firm), :validate?
end
def test_foreign_key
@@ -407,26 +405,15 @@ class ReflectionTest < ActiveRecord::TestCase
assert_equal "category_id", Post.reflect_on_association(:categorizations).foreign_key.to_s
end
- def test_through_reflection_scope_chain_does_not_modify_other_reflections
- orig_conds = assert_deprecated do
- Post.reflect_on_association(:first_blue_tags_2).scope_chain
- end.inspect
- assert_deprecated do
- Author.reflect_on_association(:misc_post_first_blue_tags_2).scope_chain
- end
- assert_equal orig_conds, assert_deprecated {
- Post.reflect_on_association(:first_blue_tags_2).scope_chain
- }.inspect
- end
-
def test_symbol_for_class_name
assert_equal Client, Firm.reflect_on_association(:unsorted_clients_with_symbol).klass
end
def test_class_for_class_name
- assert_deprecated do
- assert_predicate ActiveRecord::Reflection.create(:has_many, :clients, nil, { class_name: Client }, Firm), :validate?
+ error = assert_raises(ArgumentError) do
+ ActiveRecord::Reflection.create(:has_many, :clients, nil, { class_name: Client }, Firm)
end
+ assert_equal "A class was passed to `:class_name` but we are expecting a string.", error.message
end
def test_join_table
diff --git a/activerecord/test/cases/relation/delegation_test.rb b/activerecord/test/cases/relation/delegation_test.rb
index cb6e4d76d3..2696d1bb00 100644
--- a/activerecord/test/cases/relation/delegation_test.rb
+++ b/activerecord/test/cases/relation/delegation_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "cases/helper"
require "models/post"
require "models/comment"
@@ -8,8 +10,8 @@ module ActiveRecord
:+, :-, :|, :&, :[], :shuffle,
:all?, :collect, :compact, :detect, :each, :each_cons, :each_with_index,
:exclude?, :find_all, :flat_map, :group_by, :include?, :length,
- :map, :none?, :one?, :partition, :reject, :reverse,
- :sample, :second, :sort, :sort_by, :third,
+ :map, :none?, :one?, :partition, :reject, :reverse, :rotate,
+ :sample, :second, :sort, :sort_by, :slice, :third, :index, :rindex,
:to_ary, :to_set, :to_xml, :to_yaml, :join,
:in_groups, :in_groups_of, :to_sentence, :to_formatted_s, :as_json
]
@@ -21,20 +23,32 @@ module ActiveRecord
end
end
+ module DeprecatedArelDelegationTests
+ AREL_METHODS = [
+ :with, :orders, :froms, :project, :projections, :taken, :constraints, :exists, :locked, :where_sql,
+ :ast, :source, :join_sources, :to_dot, :create_insert, :create_true, :create_false
+ ]
+
+ def test_deprecate_arel_delegation
+ AREL_METHODS.each do |method|
+ assert_deprecated { target.public_send(method) }
+ assert_deprecated { target.public_send(method) }
+ end
+ end
+ end
+
class DelegationAssociationTest < ActiveRecord::TestCase
include DelegationWhitelistTests
-
- fixtures :posts
+ include DeprecatedArelDelegationTests
def target
- Post.first.comments
+ Post.new.comments
end
end
class DelegationRelationTest < ActiveRecord::TestCase
include DelegationWhitelistTests
-
- fixtures :comments
+ include DeprecatedArelDelegationTests
def target
Comment.all
diff --git a/activerecord/test/cases/relation/merging_test.rb b/activerecord/test/cases/relation/merging_test.rb
index 3901824aac..074ce9454f 100644
--- a/activerecord/test/cases/relation/merging_test.rb
+++ b/activerecord/test/cases/relation/merging_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "cases/helper"
require "models/author"
require "models/comment"
@@ -56,7 +58,7 @@ class RelationMergingTest < ActiveRecord::TestCase
def test_relation_merging_with_locks
devs = Developer.lock.where("salary >= 80000").order("id DESC").merge(Developer.limit(2))
- assert devs.locked?
+ assert_predicate devs, :locked?
end
def test_relation_merging_with_preload
@@ -70,6 +72,12 @@ class RelationMergingTest < ActiveRecord::TestCase
assert_equal 1, comments.count
end
+ def test_relation_merging_with_left_outer_joins
+ comments = Comment.joins(:post).where(body: "Thank you for the welcome").merge(Post.left_outer_joins(:author).where(body: "Such a lovely day"))
+
+ assert_equal 1, comments.count
+ end
+
def test_relation_merging_with_association
assert_queries(2) do # one for loading post, and another one merged query
post = Post.where(body: "Such a lovely day").first
@@ -79,13 +87,11 @@ class RelationMergingTest < ActiveRecord::TestCase
end
test "merge collapses wheres from the LHS only" do
- left = Post.where(title: "omg").where(comments_count: 1)
+ left = Post.where(title: "omg").where(comments_count: 1)
right = Post.where(title: "wtf").where(title: "bbq")
- expected = [left.bound_attributes[1]] + right.bound_attributes
- merged = left.merge(right)
+ merged = left.merge(right)
- assert_equal expected, merged.bound_attributes
assert_not_includes merged.to_sql, "omg"
assert_includes merged.to_sql, "wtf"
assert_includes merged.to_sql, "bbq"
@@ -107,9 +113,9 @@ class RelationMergingTest < ActiveRecord::TestCase
def test_merging_with_from_clause
relation = Post.all
- assert relation.from_clause.empty?
+ assert_empty relation.from_clause
relation = relation.merge(Post.from("posts"))
- refute relation.from_clause.empty?
+ assert_not_empty relation.from_clause
end
end
diff --git a/activerecord/test/cases/relation/mutation_test.rb b/activerecord/test/cases/relation/mutation_test.rb
index dea787c07f..1428b3e132 100644
--- a/activerecord/test/cases/relation/mutation_test.rb
+++ b/activerecord/test/cases/relation/mutation_test.rb
@@ -1,41 +1,10 @@
+# frozen_string_literal: true
+
require "cases/helper"
require "models/post"
module ActiveRecord
class RelationMutationTest < ActiveRecord::TestCase
- FakeKlass = Struct.new(:table_name, :name) do
- extend ActiveRecord::Delegation::DelegateCache
- inherited self
-
- def connection
- Post.connection
- end
-
- def relation_delegate_class(klass)
- self.class.relation_delegate_class(klass)
- end
-
- def attribute_alias?(name)
- false
- end
-
- def sanitize_sql(sql)
- sql
- end
-
- def sanitize_sql_for_order(sql)
- sql
- end
-
- def arel_attribute(name, table)
- table[name]
- end
- end
-
- def relation
- @relation ||= Relation.new FakeKlass.new("posts"), Post.arel_table, Post.predicate_builder
- end
-
(Relation::MULTI_VALUE_METHODS - [:references, :extending, :order, :unscope, :select]).each do |method|
test "##{method}!" do
assert relation.public_send("#{method}!", :foo).equal?(relation)
@@ -56,7 +25,7 @@ module ActiveRecord
test "#order! with symbol prepends the table name" do
assert relation.order!(:name).equal?(relation)
node = relation.order_values.first
- assert node.ascending?
+ assert_predicate node, :ascending?
assert_equal :name, node.expr.name
assert_equal "posts", node.expr.relation.name
end
@@ -90,7 +59,7 @@ module ActiveRecord
assert_equal [], relation.extending_values
end
- (Relation::SINGLE_VALUE_METHODS - [:lock, :reordering, :reverse_order, :create_with]).each do |method|
+ (Relation::SINGLE_VALUE_METHODS - [:lock, :reordering, :reverse_order, :create_with, :skip_query_cache]).each do |method|
test "##{method}!" do
assert relation.public_send("#{method}!", :foo).equal?(relation)
assert_equal :foo, relation.public_send("#{method}_value")
@@ -119,7 +88,7 @@ module ActiveRecord
assert relation.reorder!(:name).equal?(relation)
node = relation.order_values.first
- assert node.ascending?
+ assert_predicate node, :ascending?
assert_equal :name, node.expr.name
assert_equal "posts", node.expr.relation.name
end
@@ -162,5 +131,15 @@ module ActiveRecord
relation.distinct! :foo
assert_equal :foo, relation.distinct_value
end
+
+ test "skip_query_cache!" do
+ relation.skip_query_cache!
+ assert relation.skip_query_cache_value
+ end
+
+ private
+ def relation
+ @relation ||= Relation.new(FakeKlass)
+ end
end
end
diff --git a/activerecord/test/cases/relation/or_test.rb b/activerecord/test/cases/relation/or_test.rb
index 61b6601580..7e418f9c7d 100644
--- a/activerecord/test/cases/relation/or_test.rb
+++ b/activerecord/test/cases/relation/or_test.rb
@@ -1,9 +1,14 @@
+# frozen_string_literal: true
+
require "cases/helper"
+require "models/author"
+require "models/categorization"
require "models/post"
module ActiveRecord
class OrTest < ActiveRecord::TestCase
fixtures :posts
+ fixtures :authors, :author_addresses
def test_or_with_relation
expected = Post.where("id = 1 or id = 2").to_a
@@ -26,7 +31,7 @@ module ActiveRecord
end
def test_or_with_bind_params
- assert_equal Post.find([1, 2]), Post.where(id: 1).or(Post.where(id: 2)).to_a
+ assert_equal Post.find([1, 2]).sort_by(&:id), Post.where(id: 1).or(Post.where(id: 2)).sort_by(&:id)
end
def test_or_with_null_both
@@ -113,5 +118,13 @@ module ActiveRecord
Post.where(id: [1, 2, 3]).or(title: "Rails")
end
end
+
+ def test_or_with_references_inequality
+ joined = Post.includes(:author)
+ actual = joined.where(authors: { id: 1 })
+ .or(joined.where(title: "I don't have any comments"))
+ expected = Author.find(1).posts + Post.where(title: "I don't have any comments")
+ assert_equal expected.sort_by(&:id), actual.sort_by(&:id)
+ end
end
end
diff --git a/activerecord/test/cases/relation/predicate_builder_test.rb b/activerecord/test/cases/relation/predicate_builder_test.rb
index 48758dc148..b432330deb 100644
--- a/activerecord/test/cases/relation/predicate_builder_test.rb
+++ b/activerecord/test/cases/relation/predicate_builder_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "cases/helper"
require "models/topic"
diff --git a/activerecord/test/cases/relation/record_fetch_warning_test.rb b/activerecord/test/cases/relation/record_fetch_warning_test.rb
index 62ca051431..22d32d75bc 100644
--- a/activerecord/test/cases/relation/record_fetch_warning_test.rb
+++ b/activerecord/test/cases/relation/record_fetch_warning_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "cases/helper"
require "models/post"
require "active_record/relation/record_fetch_warning"
diff --git a/activerecord/test/cases/relation/where_chain_test.rb b/activerecord/test/cases/relation/where_chain_test.rb
index 86e150ed79..a68eb2b446 100644
--- a/activerecord/test/cases/relation/where_chain_test.rb
+++ b/activerecord/test/cases/relation/where_chain_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "cases/helper"
require "models/post"
require "models/comment"
@@ -25,7 +27,7 @@ module ActiveRecord
end
def test_association_not_eq
- expected = Arel::Nodes::Grouping.new(Comment.arel_table[@name].not_eq(bind_param))
+ expected = Comment.arel_table[@name].not_eq(Arel::Nodes::BindParam.new(1))
relation = Post.joins(:comments).where.not(comments: { title: "hello" })
assert_equal(expected.to_sql, relation.where_clause.ast.to_sql)
end
diff --git a/activerecord/test/cases/relation/where_clause_test.rb b/activerecord/test/cases/relation/where_clause_test.rb
index f8eb0dee91..8703d238a0 100644
--- a/activerecord/test/cases/relation/where_clause_test.rb
+++ b/activerecord/test/cases/relation/where_clause_test.rb
@@ -1,78 +1,86 @@
+# frozen_string_literal: true
+
require "cases/helper"
class ActiveRecord::Relation
class WhereClauseTest < ActiveRecord::TestCase
test "+ combines two where clauses" do
- first_clause = WhereClause.new([table["id"].eq(bind_param)], [["id", 1]])
- second_clause = WhereClause.new([table["name"].eq(bind_param)], [["name", "Sean"]])
+ first_clause = WhereClause.new([table["id"].eq(bind_param(1))])
+ second_clause = WhereClause.new([table["name"].eq(bind_param("Sean"))])
combined = WhereClause.new(
- [table["id"].eq(bind_param), table["name"].eq(bind_param)],
- [["id", 1], ["name", "Sean"]],
+ [table["id"].eq(bind_param(1)), table["name"].eq(bind_param("Sean"))],
)
assert_equal combined, first_clause + second_clause
end
test "+ is associative, but not commutative" do
- a = WhereClause.new(["a"], ["bind a"])
- b = WhereClause.new(["b"], ["bind b"])
- c = WhereClause.new(["c"], ["bind c"])
+ a = WhereClause.new(["a"])
+ b = WhereClause.new(["b"])
+ c = WhereClause.new(["c"])
assert_equal a + (b + c), (a + b) + c
assert_not_equal a + b, b + a
end
test "an empty where clause is the identity value for +" do
- clause = WhereClause.new([table["id"].eq(bind_param)], [["id", 1]])
+ clause = WhereClause.new([table["id"].eq(bind_param(1))])
assert_equal clause, clause + WhereClause.empty
end
test "merge combines two where clauses" do
- a = WhereClause.new([table["id"].eq(1)], [])
- b = WhereClause.new([table["name"].eq("Sean")], [])
- expected = WhereClause.new([table["id"].eq(1), table["name"].eq("Sean")], [])
+ a = WhereClause.new([table["id"].eq(1)])
+ b = WhereClause.new([table["name"].eq("Sean")])
+ expected = WhereClause.new([table["id"].eq(1), table["name"].eq("Sean")])
assert_equal expected, a.merge(b)
end
test "merge keeps the right side, when two equality clauses reference the same column" do
- a = WhereClause.new([table["id"].eq(1), table["name"].eq("Sean")], [])
- b = WhereClause.new([table["name"].eq("Jim")], [])
- expected = WhereClause.new([table["id"].eq(1), table["name"].eq("Jim")], [])
+ a = WhereClause.new([table["id"].eq(1), table["name"].eq("Sean")])
+ b = WhereClause.new([table["name"].eq("Jim")])
+ expected = WhereClause.new([table["id"].eq(1), table["name"].eq("Jim")])
assert_equal expected, a.merge(b)
end
test "merge removes bind parameters matching overlapping equality clauses" do
a = WhereClause.new(
- [table["id"].eq(bind_param), table["name"].eq(bind_param)],
- [bind_attribute("id", 1), bind_attribute("name", "Sean")],
+ [table["id"].eq(bind_param(1)), table["name"].eq(bind_param("Sean"))],
)
b = WhereClause.new(
- [table["name"].eq(bind_param)],
- [bind_attribute("name", "Jim")]
+ [table["name"].eq(bind_param("Jim"))],
)
expected = WhereClause.new(
- [table["id"].eq(bind_param), table["name"].eq(bind_param)],
- [bind_attribute("id", 1), bind_attribute("name", "Jim")],
+ [table["id"].eq(bind_param(1)), table["name"].eq(bind_param("Jim"))],
)
assert_equal expected, a.merge(b)
end
test "merge allows for columns with the same name from different tables" do
- skip "This is not possible as of 4.2, and the binds do not yet contain sufficient information for this to happen"
- # We might be able to change the implementation to remove conflicts by index, rather than column name
+ table2 = Arel::Table.new("table2")
+ a = WhereClause.new(
+ [table["id"].eq(bind_param(1)), table2["id"].eq(bind_param(2))],
+ )
+ b = WhereClause.new(
+ [table["id"].eq(bind_param(3))],
+ )
+ expected = WhereClause.new(
+ [table2["id"].eq(bind_param(2)), table["id"].eq(bind_param(3))],
+ )
+
+ assert_equal expected, a.merge(b)
end
test "a clause knows if it is empty" do
- assert WhereClause.empty.empty?
- assert_not WhereClause.new(["anything"], []).empty?
+ assert_empty WhereClause.empty
+ assert_not_empty WhereClause.new(["anything"])
end
test "invert cannot handle nil" do
- where_clause = WhereClause.new([nil], [])
+ where_clause = WhereClause.new([nil])
assert_raises ArgumentError do
where_clause.invert
@@ -86,37 +94,47 @@ class ActiveRecord::Relation
table["id"].eq(1),
"sql literal",
random_object
- ], [])
+ ])
expected = WhereClause.new([
table["id"].not_in([1, 2, 3]),
table["id"].not_eq(1),
Arel::Nodes::Not.new(Arel::Nodes::SqlLiteral.new("sql literal")),
Arel::Nodes::Not.new(random_object)
- ], [])
+ ])
assert_equal expected, original.invert
end
- test "accept removes binary predicates referencing a given column" do
+ test "except removes binary predicates referencing a given column" do
where_clause = WhereClause.new([
table["id"].in([1, 2, 3]),
- table["name"].eq(bind_param),
- table["age"].gteq(bind_param),
- ], [
- bind_attribute("name", "Sean"),
- bind_attribute("age", 30),
+ table["name"].eq(bind_param("Sean")),
+ table["age"].gteq(bind_param(30)),
])
- expected = WhereClause.new([table["age"].gteq(bind_param)], [bind_attribute("age", 30)])
+ expected = WhereClause.new([table["age"].gteq(bind_param(30))])
assert_equal expected, where_clause.except("id", "name")
end
+ test "except jumps over unhandled binds (like with OR) correctly" do
+ wcs = (0..9).map do |i|
+ WhereClause.new([table["id#{i}"].eq(bind_param(i))])
+ end
+
+ wc = wcs[0] + wcs[1] + wcs[2].or(wcs[3]) + wcs[4] + wcs[5] + wcs[6].or(wcs[7]) + wcs[8] + wcs[9]
+
+ expected = wcs[0] + wcs[2].or(wcs[3]) + wcs[5] + wcs[6].or(wcs[7]) + wcs[9]
+ actual = wc.except("id1", "id2", "id4", "id7", "id8")
+
+ assert_equal expected, actual
+ end
+
test "ast groups its predicates with AND" do
predicates = [
table["id"].in([1, 2, 3]),
- table["name"].eq(bind_param),
+ table["name"].eq(bind_param(nil)),
]
- where_clause = WhereClause.new(predicates, [])
+ where_clause = WhereClause.new(predicates)
expected = Arel::Nodes::And.new(predicates)
assert_equal expected, where_clause.ast
@@ -128,47 +146,96 @@ class ActiveRecord::Relation
table["id"].in([1, 2, 3]),
"foo = bar",
random_object,
- ], [])
+ ])
expected = Arel::Nodes::And.new([
table["id"].in([1, 2, 3]),
Arel::Nodes::Grouping.new(Arel.sql("foo = bar")),
- Arel::Nodes::Grouping.new(random_object),
+ random_object,
])
assert_equal expected, where_clause.ast
end
test "ast removes any empty strings" do
- where_clause = WhereClause.new([table["id"].in([1, 2, 3])], [])
- where_clause_with_empty = WhereClause.new([table["id"].in([1, 2, 3]), ""], [])
+ where_clause = WhereClause.new([table["id"].in([1, 2, 3])])
+ where_clause_with_empty = WhereClause.new([table["id"].in([1, 2, 3]), ""])
assert_equal where_clause.ast, where_clause_with_empty.ast
end
test "or joins the two clauses using OR" do
- where_clause = WhereClause.new([table["id"].eq(bind_param)], [bind_attribute("id", 1)])
- other_clause = WhereClause.new([table["name"].eq(bind_param)], [bind_attribute("name", "Sean")])
+ where_clause = WhereClause.new([table["id"].eq(bind_param(1))])
+ other_clause = WhereClause.new([table["name"].eq(bind_param("Sean"))])
expected_ast =
Arel::Nodes::Grouping.new(
- Arel::Nodes::Or.new(table["id"].eq(bind_param), table["name"].eq(bind_param))
+ Arel::Nodes::Or.new(table["id"].eq(bind_param(1)), table["name"].eq(bind_param("Sean")))
)
- expected_binds = where_clause.binds + other_clause.binds
assert_equal expected_ast.to_sql, where_clause.or(other_clause).ast.to_sql
- assert_equal expected_binds, where_clause.or(other_clause).binds
end
test "or returns an empty where clause when either side is empty" do
- where_clause = WhereClause.new([table["id"].eq(bind_param)], [bind_attribute("id", 1)])
+ where_clause = WhereClause.new([table["id"].eq(bind_param(1))])
assert_equal WhereClause.empty, where_clause.or(WhereClause.empty)
assert_equal WhereClause.empty, WhereClause.empty.or(where_clause)
end
+ test "or places common conditions before the OR" do
+ a = WhereClause.new(
+ [table["id"].eq(bind_param(1)), table["name"].eq(bind_param("Sean"))],
+ )
+ b = WhereClause.new(
+ [table["id"].eq(bind_param(1)), table["hair_color"].eq(bind_param("black"))],
+ )
+
+ common = WhereClause.new(
+ [table["id"].eq(bind_param(1))],
+ )
+
+ or_clause = WhereClause.new([table["name"].eq(bind_param("Sean"))])
+ .or(WhereClause.new([table["hair_color"].eq(bind_param("black"))]))
+
+ assert_equal common + or_clause, a.or(b)
+ end
+
+ test "or can detect identical or as being a common condition" do
+ common_or = WhereClause.new([table["name"].eq(bind_param("Sean"))])
+ .or(WhereClause.new([table["hair_color"].eq(bind_param("black"))]))
+
+ a = common_or + WhereClause.new([table["id"].eq(bind_param(1))])
+ b = common_or + WhereClause.new([table["foo"].eq(bind_param("bar"))])
+
+ new_or = WhereClause.new([table["id"].eq(bind_param(1))])
+ .or(WhereClause.new([table["foo"].eq(bind_param("bar"))]))
+
+ assert_equal common_or + new_or, a.or(b)
+ end
+
+ test "or will use only common conditions if one side only has common conditions" do
+ only_common = WhereClause.new([
+ table["id"].eq(bind_param(1)),
+ "foo = bar",
+ ])
+
+ common_with_extra = WhereClause.new([
+ table["id"].eq(bind_param(1)),
+ "foo = bar",
+ table["extra"].eq(bind_param("pluto")),
+ ])
+
+ assert_equal only_common, only_common.or(common_with_extra)
+ assert_equal only_common, common_with_extra.or(only_common)
+ end
+
private
def table
Arel::Table.new("table")
end
+
+ def bind_param(value)
+ Arel::Nodes::BindParam.new(value)
+ end
end
end
diff --git a/activerecord/test/cases/relation/where_test.rb b/activerecord/test/cases/relation/where_test.rb
index 42dae4d569..99797528b2 100644
--- a/activerecord/test/cases/relation/where_test.rb
+++ b/activerecord/test/cases/relation/where_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "cases/helper"
require "models/author"
require "models/binary"
@@ -107,6 +109,15 @@ module ActiveRecord
assert_equal expected.to_sql, actual.to_sql
end
+ def test_polymorphic_shallow_where_not
+ treasure = treasures(:sapphire)
+
+ expected = [price_estimates(:diamond), price_estimates(:honda)]
+ actual = PriceEstimate.where.not(estimate_of: treasure)
+
+ assert_equal expected.sort_by(&:id), actual.sort_by(&:id)
+ end
+
def test_polymorphic_nested_array_where
treasure = Treasure.new
treasure.id = 1
@@ -119,13 +130,23 @@ module ActiveRecord
assert_equal expected.to_sql, actual.to_sql
end
+ def test_polymorphic_nested_array_where_not
+ treasure = treasures(:diamond)
+ car = cars(:honda)
+
+ expected = [price_estimates(:sapphire_1), price_estimates(:sapphire_2)]
+ actual = PriceEstimate.where.not(estimate_of: [treasure, car])
+
+ assert_equal expected.sort_by(&:id), actual.sort_by(&:id)
+ end
+
def test_polymorphic_array_where_multiple_types
treasure_1 = treasures(:diamond)
treasure_2 = treasures(:sapphire)
car = cars(:honda)
expected = [price_estimates(:diamond), price_estimates(:sapphire_1), price_estimates(:sapphire_2), price_estimates(:honda)].sort
- actual = PriceEstimate.where(estimate_of: [treasure_1, treasure_2, car]).to_a.sort
+ actual = PriceEstimate.where(estimate_of: [treasure_1, treasure_2, car]).to_a.sort
assert_equal expected, actual
end
@@ -244,7 +265,7 @@ module ActiveRecord
end
def test_where_with_decimal_for_string_column
- count = Post.where(title: BigDecimal.new(0)).count
+ count = Post.where(title: BigDecimal(0)).count
assert_equal 0, count
end
@@ -293,6 +314,20 @@ module ActiveRecord
assert_equal essays(:david_modest_proposal), essay
end
+ def test_where_with_relation_on_has_many_association
+ essay = essays(:david_modest_proposal)
+ author = Author.where(essays: Essay.where(id: essay.id)).first
+
+ assert_equal authors(:david), author
+ end
+
+ def test_where_with_relation_on_has_one_association
+ author = authors(:david)
+ author_address = AuthorAddress.where(author: Author.where(id: author.id)).first
+ assert_equal author_addresses(:david_address), author_address
+ end
+
+
def test_where_on_association_with_select_relation
essay = Essay.where(author: Author.where(name: "David").select(:name)).take
assert_equal essays(:david_modest_proposal), essay
diff --git a/activerecord/test/cases/relation_test.rb b/activerecord/test/cases/relation_test.rb
index a403824f1a..4e75371147 100644
--- a/activerecord/test/cases/relation_test.rb
+++ b/activerecord/test/cases/relation_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "cases/helper"
require "models/post"
require "models/comment"
@@ -8,49 +10,30 @@ module ActiveRecord
class RelationTest < ActiveRecord::TestCase
fixtures :posts, :comments, :authors, :author_addresses, :ratings
- FakeKlass = Struct.new(:table_name, :name) do
- extend ActiveRecord::Delegation::DelegateCache
-
- inherited self
-
- def self.connection
- Post.connection
- end
-
- def self.table_name
- "fake_table"
- end
-
- def self.sanitize_sql_for_order(sql)
- sql
- end
- end
-
def test_construction
- relation = Relation.new(FakeKlass, :b, nil)
+ relation = Relation.new(FakeKlass, table: :b)
assert_equal FakeKlass, relation.klass
assert_equal :b, relation.table
assert !relation.loaded, "relation is not loaded"
end
def test_responds_to_model_and_returns_klass
- relation = Relation.new(FakeKlass, :b, nil)
+ relation = Relation.new(FakeKlass)
assert_equal FakeKlass, relation.model
end
def test_initialize_single_values
- relation = Relation.new(FakeKlass, :b, nil)
- (Relation::SINGLE_VALUE_METHODS - [:create_with, :readonly]).each do |method|
+ relation = Relation.new(FakeKlass)
+ (Relation::SINGLE_VALUE_METHODS - [:create_with]).each do |method|
assert_nil relation.send("#{method}_value"), method.to_s
end
- assert_equal false, relation.readonly_value
value = relation.create_with_value
assert_equal({}, value)
assert_predicate value, :frozen?
end
def test_multi_value_initialize
- relation = Relation.new(FakeKlass, :b, nil)
+ relation = Relation.new(FakeKlass)
Relation::MULTI_VALUE_METHODS.each do |method|
values = relation.send("#{method}_values")
assert_equal [], values, method.to_s
@@ -59,70 +42,64 @@ module ActiveRecord
end
def test_extensions
- relation = Relation.new(FakeKlass, :b, nil)
+ relation = Relation.new(FakeKlass)
assert_equal [], relation.extensions
end
def test_empty_where_values_hash
- relation = Relation.new(FakeKlass, :b, nil)
+ relation = Relation.new(FakeKlass)
assert_equal({}, relation.where_values_hash)
end
def test_has_values
- relation = Relation.new(Post, Post.arel_table, Post.predicate_builder)
- relation.where! relation.table[:id].eq(10)
- assert_equal({ id: 10 }, relation.where_values_hash)
+ relation = Relation.new(Post)
+ relation.where!(id: 10)
+ assert_equal({ "id" => 10 }, relation.where_values_hash)
end
def test_values_wrong_table
- relation = Relation.new(Post, Post.arel_table, Post.predicate_builder)
+ relation = Relation.new(Post)
relation.where! Comment.arel_table[:id].eq(10)
assert_equal({}, relation.where_values_hash)
end
def test_tree_is_not_traversed
- relation = Relation.new(Post, Post.arel_table, Post.predicate_builder)
+ relation = Relation.new(Post)
left = relation.table[:id].eq(10)
right = relation.table[:id].eq(10)
- combine = left.and right
+ combine = left.or(right)
relation.where! combine
assert_equal({}, relation.where_values_hash)
end
- def test_table_name_delegates_to_klass
- relation = Relation.new(FakeKlass.new("posts"), :b, Post.predicate_builder)
- assert_equal "posts", relation.table_name
- end
-
def test_scope_for_create
- relation = Relation.new(FakeKlass, :b, nil)
+ relation = Relation.new(FakeKlass)
assert_equal({}, relation.scope_for_create)
end
def test_create_with_value
- relation = Relation.new(Post, Post.arel_table, Post.predicate_builder)
- hash = { hello: "world" }
- relation.create_with_value = hash
- assert_equal hash, relation.scope_for_create
- end
-
- def test_create_with_value_with_wheres
- relation = Relation.new(Post, Post.arel_table, Post.predicate_builder)
- relation.where! relation.table[:id].eq(10)
+ relation = Relation.new(Post)
relation.create_with_value = { hello: "world" }
- assert_equal({ hello: "world", id: 10 }, relation.scope_for_create)
+ assert_equal({ "hello" => "world" }, relation.scope_for_create)
end
- # FIXME: is this really wanted or expected behavior?
- def test_scope_for_create_is_cached
- relation = Relation.new(Post, Post.arel_table, Post.predicate_builder)
+ def test_create_with_value_with_wheres
+ relation = Relation.new(Post)
assert_equal({}, relation.scope_for_create)
- relation.where! relation.table[:id].eq(10)
- assert_equal({}, relation.scope_for_create)
+ relation.where!(id: 10)
+ assert_equal({ "id" => 10 }, relation.scope_for_create)
relation.create_with_value = { hello: "world" }
- assert_equal({}, relation.scope_for_create)
+ assert_equal({ "hello" => "world", "id" => 10 }, relation.scope_for_create)
+ end
+
+ def test_empty_scope
+ relation = Relation.new(Post)
+ assert_predicate relation, :empty_scope?
+
+ relation.merge!(relation)
+ assert_predicate relation, :empty_scope?
end
def test_bad_constants_raise_errors
@@ -132,31 +109,31 @@ module ActiveRecord
end
def test_empty_eager_loading?
- relation = Relation.new(FakeKlass, :b, nil)
- assert !relation.eager_loading?
+ relation = Relation.new(FakeKlass)
+ assert_not_predicate relation, :eager_loading?
end
def test_eager_load_values
- relation = Relation.new(FakeKlass, :b, nil)
+ relation = Relation.new(FakeKlass)
relation.eager_load! :b
- assert relation.eager_loading?
+ assert_predicate relation, :eager_loading?
end
def test_references_values
- relation = Relation.new(FakeKlass, :b, nil)
+ relation = Relation.new(FakeKlass)
assert_equal [], relation.references_values
relation = relation.references(:foo).references(:omg, :lol)
assert_equal ["foo", "omg", "lol"], relation.references_values
end
def test_references_values_dont_duplicate
- relation = Relation.new(FakeKlass, :b, nil)
+ relation = Relation.new(FakeKlass)
relation = relation.references(:foo).references(:foo)
assert_equal ["foo"], relation.references_values
end
test "merging a hash into a relation" do
- relation = Relation.new(Post, Post.arel_table, Post.predicate_builder)
+ relation = Relation.new(Post)
relation = relation.merge where: { name: :lol }, readonly: true
assert_equal({ "name" => :lol }, relation.where_clause.to_h)
@@ -164,7 +141,7 @@ module ActiveRecord
end
test "merging an empty hash into a relation" do
- assert_equal Relation::WhereClause.empty, Relation.new(FakeKlass, :b, nil).merge({}).where_clause
+ assert_equal Relation::WhereClause.empty, Relation.new(FakeKlass).merge({}).where_clause
end
test "merging a hash with unknown keys raises" do
@@ -172,7 +149,7 @@ module ActiveRecord
end
test "merging nil or false raises" do
- relation = Relation.new(FakeKlass, :b, nil)
+ relation = Relation.new(FakeKlass)
e = assert_raises(ArgumentError) do
relation = relation.merge nil
@@ -188,7 +165,7 @@ module ActiveRecord
end
test "#values returns a dup of the values" do
- relation = Relation.new(Post, Post.arel_table, Post.predicate_builder).where!(name: :foo)
+ relation = Relation.new(Post).where!(name: :foo)
values = relation.values
values[:where] = nil
@@ -196,7 +173,7 @@ module ActiveRecord
end
test "relations can be created with a values hash" do
- relation = Relation.new(FakeKlass, :b, nil, select: [:foo])
+ relation = Relation.new(FakeKlass, values: { select: [:foo] })
assert_equal [:foo], relation.select_values
end
@@ -208,13 +185,13 @@ module ActiveRecord
end
end
- relation = Relation.new(klass, :b, nil)
+ relation = Relation.new(klass)
relation.merge!(where: ["foo = ?", "bar"])
- assert_equal Relation::WhereClause.new(["foo = bar"], []), relation.where_clause
+ assert_equal Relation::WhereClause.new(["foo = bar"]), relation.where_clause
end
def test_merging_readonly_false
- relation = Relation.new(FakeKlass, :b, nil)
+ relation = Relation.new(FakeKlass)
readonly_false_relation = relation.readonly(false)
# test merging in both directions
assert_equal false, relation.merge(readonly_false_relation).readonly_value
@@ -258,17 +235,17 @@ module ActiveRecord
def test_merge_raises_with_invalid_argument
assert_raises ArgumentError do
- relation = Relation.new(FakeKlass, :b, nil)
+ relation = Relation.new(FakeKlass)
relation.merge(true)
end
end
def test_respond_to_for_non_selected_element
post = Post.select(:title).first
- assert_equal false, post.respond_to?(:body), "post should not respond_to?(:body) since invoking it raises exception"
+ assert_not_respond_to post, :body, "post should not respond_to?(:body) since invoking it raises exception"
silence_warnings { post = Post.select("'title' as post_title").first }
- assert_equal false, post.respond_to?(:title), "post should not respond_to?(:body) since invoking it raises exception"
+ assert_not_respond_to post, :title, "post should not respond_to?(:body) since invoking it raises exception"
end
def test_select_quotes_when_using_from_clause
@@ -296,18 +273,32 @@ module ActiveRecord
assert_equal({ 2 => 1, 4 => 3, 5 => 1 }, authors(:david).posts.merge(posts_with_special_comments_with_ratings).count)
end
+ def test_relation_merging_keeps_joining_order
+ authors = Author.where(id: 1)
+ posts = Post.joins(:author).merge(authors)
+ comments = Comment.joins(:post).merge(posts)
+ ratings = Rating.joins(:comment).merge(comments)
+
+ assert_equal 3, ratings.count
+ end
+
class EnsureRoundTripTypeCasting < ActiveRecord::Type::Value
def type
:string
end
+ def cast(value)
+ raise value unless value == "value from user"
+ "cast value"
+ end
+
def deserialize(value)
raise value unless value == "type cast for database"
"type cast from database"
end
def serialize(value)
- raise value unless value == "value from user"
+ raise value unless value == "cast value"
"type cast for database"
end
end
diff --git a/activerecord/test/cases/relations_test.rb b/activerecord/test/cases/relations_test.rb
index 86f3b0b962..bc7c54dbe0 100644
--- a/activerecord/test/cases/relations_test.rb
+++ b/activerecord/test/cases/relations_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "cases/helper"
require "models/tag"
require "models/tagging"
@@ -15,15 +17,14 @@ require "models/car"
require "models/engine"
require "models/tyre"
require "models/minivan"
-require "models/aircraft"
require "models/possession"
require "models/reader"
+require "models/category"
require "models/categorization"
require "models/edge"
class RelationTest < ActiveRecord::TestCase
- fixtures :authors, :author_addresses, :topics, :entrants, :developers, :companies, :developers_projects, :accounts, :categories, :categorizations, :posts, :comments,
- :tags, :taggings, :cars, :minivans
+ fixtures :authors, :author_addresses, :topics, :entrants, :developers, :companies, :developers_projects, :accounts, :categories, :categorizations, :categories_posts, :posts, :comments, :tags, :taggings, :cars, :minivans
class TopicWithCallbacks < ActiveRecord::Base
self.table_name = :topics
@@ -55,7 +56,7 @@ class RelationTest < ActiveRecord::TestCase
def test_dynamic_finder
x = Post.where("author_id = ?", 1)
- assert x.klass.respond_to?(:find_by_id), "@klass should handle dynamic finders"
+ assert_respond_to x.klass, :find_by_id
end
def test_multivalue_where
@@ -97,7 +98,7 @@ class RelationTest < ActiveRecord::TestCase
2.times { assert_equal 5, topics.to_a.size }
end
- assert topics.loaded?
+ assert_predicate topics, :loaded?
end
def test_scoped_first
@@ -107,7 +108,7 @@ class RelationTest < ActiveRecord::TestCase
2.times { assert_equal "The First Topic", topics.first.title }
end
- assert ! topics.loaded?
+ assert_not_predicate topics, :loaded?
end
def test_loaded_first
@@ -118,7 +119,7 @@ class RelationTest < ActiveRecord::TestCase
assert_equal "The First Topic", topics.first.title
end
- assert topics.loaded?
+ assert_predicate topics, :loaded?
end
def test_loaded_first_with_limit
@@ -130,7 +131,7 @@ class RelationTest < ActiveRecord::TestCase
"The Second Topic of the day"], topics.first(2).map(&:title)
end
- assert topics.loaded?
+ assert_predicate topics, :loaded?
end
def test_first_get_more_than_available
@@ -151,14 +152,14 @@ class RelationTest < ActiveRecord::TestCase
2.times { topics.to_a }
end
- assert topics.loaded?
+ assert_predicate topics, :loaded?
original_size = topics.to_a.size
Topic.create! title: "fake"
assert_queries(1) { topics.reload }
assert_equal original_size + 1, topics.size
- assert topics.loaded?
+ assert_predicate topics, :loaded?
end
def test_finding_with_subquery
@@ -194,6 +195,18 @@ class RelationTest < ActiveRecord::TestCase
assert_equal(relation.map(&:post_count).sort, subquery.values.sort)
end
+ def test_finding_with_subquery_with_eager_loading_in_from
+ relation = Comment.includes(:post).where("posts.type": "Post")
+ assert_equal relation.to_a, Comment.select("*").from(relation).to_a
+ assert_equal relation.to_a, Comment.select("subquery.*").from(relation).to_a
+ assert_equal relation.to_a, Comment.select("a.*").from(relation, :a).to_a
+ end
+
+ def test_finding_with_subquery_with_eager_loading_in_where
+ relation = Comment.includes(:post).where("posts.type": "Post")
+ assert_equal relation.sort_by(&:id), Comment.where(id: relation).sort_by(&:id)
+ end
+
def test_finding_with_conditions
assert_equal ["David"], Author.where(name: "David").map(&:name)
assert_equal ["Mary"], Author.where(["name = ?", "Mary"]).map(&:name)
@@ -237,7 +250,7 @@ class RelationTest < ActiveRecord::TestCase
end
def test_reverse_order_with_function
- topics = Topic.order("length(title)").reverse_order
+ topics = Topic.order(Arel.sql("length(title)")).reverse_order
assert_equal topics(:second).title, topics.first.title
end
@@ -247,24 +260,24 @@ class RelationTest < ActiveRecord::TestCase
end
def test_reverse_order_with_function_other_predicates
- topics = Topic.order("author_name, length(title), id").reverse_order
+ topics = Topic.order(Arel.sql("author_name, length(title), id")).reverse_order
assert_equal topics(:second).title, topics.first.title
- topics = Topic.order("length(author_name), id, length(title)").reverse_order
+ topics = Topic.order(Arel.sql("length(author_name), id, length(title)")).reverse_order
assert_equal topics(:fifth).title, topics.first.title
end
def test_reverse_order_with_multiargument_function
assert_raises(ActiveRecord::IrreversibleOrderError) do
- Topic.order("concat(author_name, title)").reverse_order
+ Topic.order(Arel.sql("concat(author_name, title)")).reverse_order
end
assert_raises(ActiveRecord::IrreversibleOrderError) do
- Topic.order("concat(lower(author_name), title)").reverse_order
+ Topic.order(Arel.sql("concat(lower(author_name), title)")).reverse_order
end
assert_raises(ActiveRecord::IrreversibleOrderError) do
- Topic.order("concat(author_name, lower(title))").reverse_order
+ Topic.order(Arel.sql("concat(author_name, lower(title))")).reverse_order
end
assert_raises(ActiveRecord::IrreversibleOrderError) do
- Topic.order("concat(lower(author_name), title, length(title)").reverse_order
+ Topic.order(Arel.sql("concat(lower(author_name), title, length(title)")).reverse_order
end
end
@@ -276,10 +289,10 @@ class RelationTest < ActiveRecord::TestCase
def test_reverse_order_with_nulls_first_or_last
assert_raises(ActiveRecord::IrreversibleOrderError) do
- Topic.order("title NULLS FIRST").reverse_order
+ Topic.order(Arel.sql("title NULLS FIRST")).reverse_order
end
assert_raises(ActiveRecord::IrreversibleOrderError) do
- Topic.order("title nulls last").reverse_order
+ Topic.order(Arel.sql("title nulls last")).reverse_order
end
end
@@ -372,29 +385,29 @@ class RelationTest < ActiveRecord::TestCase
def test_finding_with_cross_table_order_and_limit
tags = Tag.includes(:taggings).
- order("tags.name asc", "taggings.taggable_id asc", "REPLACE('abc', taggings.taggable_type, taggings.taggable_type)").
+ order("tags.name asc", "taggings.taggable_id asc", Arel.sql("REPLACE('abc', taggings.taggable_type, taggings.taggable_type)")).
limit(1).to_a
assert_equal 1, tags.length
end
def test_finding_with_complex_order_and_limit
- tags = Tag.includes(:taggings).references(:taggings).order("REPLACE('abc', taggings.taggable_type, taggings.taggable_type)").limit(1).to_a
+ tags = Tag.includes(:taggings).references(:taggings).order(Arel.sql("REPLACE('abc', taggings.taggable_type, taggings.taggable_type)")).limit(1).to_a
assert_equal 1, tags.length
end
def test_finding_with_complex_order
- tags = Tag.includes(:taggings).references(:taggings).order("REPLACE('abc', taggings.taggable_type, taggings.taggable_type)").to_a
+ tags = Tag.includes(:taggings).references(:taggings).order(Arel.sql("REPLACE('abc', taggings.taggable_type, taggings.taggable_type)")).to_a
assert_equal 3, tags.length
end
def test_finding_with_sanitized_order
- query = Tag.order(["field(id, ?)", [1, 3, 2]]).to_sql
+ query = Tag.order([Arel.sql("field(id, ?)"), [1, 3, 2]]).to_sql
assert_match(/field\(id, 1,3,2\)/, query)
- query = Tag.order(["field(id, ?)", []]).to_sql
+ query = Tag.order([Arel.sql("field(id, ?)"), []]).to_sql
assert_match(/field\(id, NULL\)/, query)
- query = Tag.order(["field(id, ?)", nil]).to_sql
+ query = Tag.order([Arel.sql("field(id, ?)"), nil]).to_sql
assert_match(/field\(id, NULL\)/, query)
end
@@ -420,123 +433,6 @@ class RelationTest < ActiveRecord::TestCase
assert_equal [2, 4, 6, 8, 10], even_ids.sort
end
- def test_none
- assert_no_queries(ignore_none: false) do
- assert_equal [], Developer.none
- assert_equal [], Developer.all.none
- end
- end
-
- def test_none_chainable
- assert_no_queries(ignore_none: false) do
- assert_equal [], Developer.none.where(name: "David")
- end
- end
-
- def test_none_chainable_to_existing_scope_extension_method
- assert_no_queries(ignore_none: false) do
- assert_equal 1, Topic.anonymous_extension.none.one
- end
- end
-
- def test_none_chained_to_methods_firing_queries_straight_to_db
- assert_no_queries(ignore_none: false) do
- assert_equal [], Developer.none.pluck(:id, :name)
- assert_equal 0, Developer.none.delete_all
- assert_equal 0, Developer.none.update_all(name: "David")
- assert_equal 0, Developer.none.delete(1)
- assert_equal false, Developer.none.exists?(1)
- end
- end
-
- def test_null_relation_content_size_methods
- assert_no_queries(ignore_none: false) do
- assert_equal 0, Developer.none.size
- assert_equal 0, Developer.none.count
- assert_equal true, Developer.none.empty?
- assert_equal true, Developer.none.none?
- assert_equal false, Developer.none.any?
- assert_equal false, Developer.none.one?
- assert_equal false, Developer.none.many?
- end
- end
-
- def test_null_relation_calculations_methods
- assert_no_queries(ignore_none: false) do
- assert_equal 0, Developer.none.count
- assert_equal 0, Developer.none.calculate(:count, nil)
- assert_nil Developer.none.calculate(:average, "salary")
- end
- end
-
- def test_null_relation_metadata_methods
- assert_equal "", Developer.none.to_sql
- assert_equal({}, Developer.none.where_values_hash)
- end
-
- def test_null_relation_where_values_hash
- assert_equal({ "salary" => 100_000 }, Developer.none.where(salary: 100_000).where_values_hash)
- end
-
- def test_null_relation_sum
- ac = Aircraft.new
- assert_equal Hash.new, ac.engines.group(:id).sum(:id)
- assert_equal 0, ac.engines.count
- ac.save
- assert_equal Hash.new, ac.engines.group(:id).sum(:id)
- assert_equal 0, ac.engines.count
- end
-
- def test_null_relation_count
- ac = Aircraft.new
- assert_equal Hash.new, ac.engines.group(:id).count
- assert_equal 0, ac.engines.count
- ac.save
- assert_equal Hash.new, ac.engines.group(:id).count
- assert_equal 0, ac.engines.count
- end
-
- def test_null_relation_size
- ac = Aircraft.new
- assert_equal Hash.new, ac.engines.group(:id).size
- assert_equal 0, ac.engines.size
- ac.save
- assert_equal Hash.new, ac.engines.group(:id).size
- assert_equal 0, ac.engines.size
- end
-
- def test_null_relation_average
- ac = Aircraft.new
- assert_equal Hash.new, ac.engines.group(:car_id).average(:id)
- assert_nil ac.engines.average(:id)
- ac.save
- assert_equal Hash.new, ac.engines.group(:car_id).average(:id)
- assert_nil ac.engines.average(:id)
- end
-
- def test_null_relation_minimum
- ac = Aircraft.new
- assert_equal Hash.new, ac.engines.group(:car_id).minimum(:id)
- assert_nil ac.engines.minimum(:id)
- ac.save
- assert_equal Hash.new, ac.engines.group(:car_id).minimum(:id)
- assert_nil ac.engines.minimum(:id)
- end
-
- def test_null_relation_maximum
- ac = Aircraft.new
- assert_equal Hash.new, ac.engines.group(:car_id).maximum(:id)
- assert_nil ac.engines.maximum(:id)
- ac.save
- assert_equal Hash.new, ac.engines.group(:car_id).maximum(:id)
- assert_nil ac.engines.maximum(:id)
- end
-
- def test_null_relation_in_where_condition
- assert_operator Comment.count, :>, 0 # precondition, make sure there are comments.
- assert_equal 0, Comment.where(post_id: Post.none).to_a.size
- end
-
def test_joins_with_nil_argument
assert_nothing_raised { DependentFirm.joins(nil).first }
end
@@ -586,14 +482,6 @@ class RelationTest < ActiveRecord::TestCase
assert_nothing_raised { Topic.reorder([]) }
end
- def test_scoped_responds_to_delegated_methods
- relation = Topic.all
-
- ["map", "uniq", "sort", "insert", "delete", "update"].each do |method|
- assert_respond_to relation, method, "Topic.all should respond to #{method.inspect}"
- end
- end
-
def test_respond_to_delegates_to_arel
relation = Topic.all
fake_arel = Struct.new(:responds) {
@@ -613,12 +501,12 @@ class RelationTest < ActiveRecord::TestCase
relation = Topic.all
["find_by_title", "find_by_title_and_author_name"].each do |method|
- assert_respond_to relation, method, "Topic.all should respond to #{method.inspect}"
+ assert_respond_to relation, method
end
end
def test_respond_to_class_methods_and_scopes
- assert Topic.all.respond_to?(:by_lifo)
+ assert_respond_to Topic.all, :by_lifo
end
def test_find_with_readonly_option
@@ -713,7 +601,7 @@ class RelationTest < ActiveRecord::TestCase
reader = Reader.create! post_id: post.id, person_id: 1
comment = Comment.create! post_id: post.id, body: "body"
- assert !comment.respond_to?(:readers)
+ assert_not_respond_to comment, :readers
post_rel = Post.preload(:readers).joins(:readers).where(title: "Uhuu")
result_comment = Comment.joins(:post).merge(post_rel).to_a.first
@@ -834,7 +722,7 @@ class RelationTest < ActiveRecord::TestCase
def test_find_in_empty_array
authors = Author.all.where(id: [])
- assert authors.to_a.blank?
+ assert_predicate authors.to_a, :blank?
end
def test_where_with_ar_object
@@ -892,8 +780,6 @@ class RelationTest < ActiveRecord::TestCase
def test_find_all_using_where_with_relation
david = authors(:david)
- # switching the lines below would succeed in current rails
- # assert_queries(2) {
assert_queries(1) {
relation = Author.where(id: Author.where(id: david.id))
assert_equal [david], relation.to_a
@@ -932,8 +818,6 @@ class RelationTest < ActiveRecord::TestCase
def test_find_all_using_where_with_relation_and_alternate_primary_key
cool_first = minivans(:cool_first)
- # switching the lines below would succeed in current rails
- # assert_queries(2) {
assert_queries(1) {
relation = Minivan.where(minivan_id: Minivan.where(name: cool_first.name))
assert_equal [cool_first], relation.to_a
@@ -969,32 +853,6 @@ class RelationTest < ActiveRecord::TestCase
}
end
- def test_exists
- davids = Author.where(name: "David")
- assert davids.exists?
- assert davids.exists?(authors(:david).id)
- assert ! davids.exists?(authors(:mary).id)
- assert ! davids.exists?("42")
- assert ! davids.exists?(42)
- assert ! davids.exists?(davids.new.id)
-
- fake = Author.where(name: "fake author")
- assert ! fake.exists?
- assert ! fake.exists?(authors(:david).id)
- end
-
- def test_exists_uses_existing_scope
- post = authors(:david).posts.first
- authors = Author.includes(:posts).where(name: "David", posts: { id: post.id })
- assert authors.exists?(authors(:david).id)
- end
-
- def test_any_with_scope_on_hash_includes
- post = authors(:david).posts.first
- categories = Categorization.includes(author: :posts).where(posts: { id: post.id })
- assert categories.exists?
- end
-
def test_last
authors = Author.all
assert_equal authors(:bob), authors.last
@@ -1005,19 +863,19 @@ class RelationTest < ActiveRecord::TestCase
# Force load
assert_equal [authors(:david)], davids.to_a
- assert davids.loaded?
+ assert_predicate davids, :loaded?
assert_difference("Author.count", -1) { davids.destroy_all }
assert_equal [], davids.to_a
- assert davids.loaded?
+ assert_predicate davids, :loaded?
end
def test_delete_all
davids = Author.where(name: "David")
assert_difference("Author.count", -1) { davids.delete_all }
- assert ! davids.loaded?
+ assert_not_predicate davids, :loaded?
end
def test_delete_all_loaded
@@ -1025,20 +883,18 @@ class RelationTest < ActiveRecord::TestCase
# Force load
assert_equal [authors(:david)], davids.to_a
- assert davids.loaded?
+ assert_predicate davids, :loaded?
assert_difference("Author.count", -1) { davids.delete_all }
assert_equal [], davids.to_a
- assert davids.loaded?
+ assert_predicate davids, :loaded?
end
def test_delete_all_with_unpermitted_relation_raises_error
- assert_raises(ActiveRecord::ActiveRecordError) { Author.limit(10).delete_all }
assert_raises(ActiveRecord::ActiveRecordError) { Author.distinct.delete_all }
assert_raises(ActiveRecord::ActiveRecordError) { Author.group(:name).delete_all }
assert_raises(ActiveRecord::ActiveRecordError) { Author.having("SUM(id) < 3").delete_all }
- assert_raises(ActiveRecord::ActiveRecordError) { Author.offset(10).delete_all }
end
def test_select_with_aggregates
@@ -1046,9 +902,9 @@ class RelationTest < ActiveRecord::TestCase
assert_equal 11, posts.count(:all)
assert_equal 11, posts.size
- assert posts.any?
- assert posts.many?
- assert_not posts.empty?
+ assert_predicate posts, :any?
+ assert_predicate posts, :many?
+ assert_not_empty posts
end
def test_select_takes_a_variable_list_of_args
@@ -1077,13 +933,13 @@ class RelationTest < ActiveRecord::TestCase
assert_equal 11, posts.count(:all)
assert_equal 11, posts.count(:id)
- assert_equal 1, posts.where("comments_count > 1").count
- assert_equal 9, posts.where(comments_count: 0).count
+ assert_equal 3, posts.where("comments_count > 1").count
+ assert_equal 6, posts.where(comments_count: 0).count
end
def test_count_with_block
posts = Post.all
- assert_equal 10, posts.count { |p| p.comments_count.even? }
+ assert_equal 8, posts.count { |p| p.comments_count.even? }
end
def test_count_on_association_relation
@@ -1094,19 +950,31 @@ class RelationTest < ActiveRecord::TestCase
assert_equal author.posts.where(author_id: author.id).size, posts.count
assert_equal 0, author.posts.where(author_id: another_author.id).size
- assert author.posts.where(author_id: another_author.id).empty?
+ assert_empty author.posts.where(author_id: another_author.id)
end
def test_count_with_distinct
posts = Post.all
- assert_equal 3, posts.distinct(true).count(:comments_count)
+ assert_equal 4, posts.distinct(true).count(:comments_count)
assert_equal 11, posts.distinct(false).count(:comments_count)
- assert_equal 3, posts.distinct(true).select(:comments_count).count
+ assert_equal 4, posts.distinct(true).select(:comments_count).count
assert_equal 11, posts.distinct(false).select(:comments_count).count
end
+ def test_size_with_distinct
+ posts = Post.distinct.select(:author_id, :comments_count)
+ assert_queries(1) { assert_equal 8, posts.size }
+ assert_queries(1) { assert_equal 8, posts.load.size }
+ end
+
+ def test_size_with_eager_loading_and_custom_order
+ posts = Post.includes(:comments).order("comments.id")
+ assert_queries(1) { assert_equal 11, posts.size }
+ assert_queries(1) { assert_equal 11, posts.load.size }
+ end
+
def test_update_all_with_scope
tag = Tag.first
Post.tagged_with(tag.id).update_all title: "rofl"
@@ -1138,29 +1006,29 @@ class RelationTest < ActiveRecord::TestCase
posts = Post.all
assert_queries(1) { assert_equal 11, posts.size }
- assert ! posts.loaded?
+ assert_not_predicate posts, :loaded?
best_posts = posts.where(comments_count: 0)
best_posts.load # force load
- assert_no_queries { assert_equal 9, best_posts.size }
+ assert_no_queries { assert_equal 6, best_posts.size }
end
def test_size_with_limit
posts = Post.limit(10)
assert_queries(1) { assert_equal 10, posts.size }
- assert ! posts.loaded?
+ assert_not_predicate posts, :loaded?
best_posts = posts.where(comments_count: 0)
best_posts.load # force load
- assert_no_queries { assert_equal 9, best_posts.size }
+ assert_no_queries { assert_equal 6, best_posts.size }
end
def test_size_with_zero_limit
posts = Post.limit(0)
assert_no_queries { assert_equal 0, posts.size }
- assert ! posts.loaded?
+ assert_not_predicate posts, :loaded?
posts.load # force load
assert_no_queries { assert_equal 0, posts.size }
@@ -1170,13 +1038,13 @@ class RelationTest < ActiveRecord::TestCase
posts = Post.limit(0)
assert_no_queries { assert_equal true, posts.empty? }
- assert ! posts.loaded?
+ assert_not_predicate posts, :loaded?
end
def test_count_complex_chained_relations
posts = Post.select("comments_count").where("id is not null").group("author_id").where("comments_count > 0")
- expected = { 1 => 2 }
+ expected = { 1 => 4, 2 => 1 }
assert_equal expected, posts.count
end
@@ -1184,11 +1052,11 @@ class RelationTest < ActiveRecord::TestCase
posts = Post.all
assert_queries(1) { assert_equal false, posts.empty? }
- assert ! posts.loaded?
+ assert_not_predicate posts, :loaded?
no_posts = posts.where(title: "")
assert_queries(1) { assert_equal true, no_posts.empty? }
- assert ! no_posts.loaded?
+ assert_not_predicate no_posts, :loaded?
best_posts = posts.where(comments_count: 0)
best_posts.load # force load
@@ -1199,11 +1067,11 @@ class RelationTest < ActiveRecord::TestCase
posts = Post.select("comments_count").where("id is not null").group("author_id").where("comments_count > 0")
assert_queries(1) { assert_equal false, posts.empty? }
- assert ! posts.loaded?
+ assert_not_predicate posts, :loaded?
no_posts = posts.where(title: "")
assert_queries(1) { assert_equal true, no_posts.empty? }
- assert ! no_posts.loaded?
+ assert_not_predicate no_posts, :loaded?
end
def test_any
@@ -1219,13 +1087,13 @@ class RelationTest < ActiveRecord::TestCase
assert_queries(3) do
assert posts.any? # Uses COUNT()
- assert ! posts.where(id: nil).any?
+ assert_not_predicate posts.where(id: nil), :any?
assert posts.any? { |p| p.id > 0 }
assert ! posts.any? { |p| p.id <= 0 }
end
- assert posts.loaded?
+ assert_predicate posts, :loaded?
end
def test_many
@@ -1237,14 +1105,14 @@ class RelationTest < ActiveRecord::TestCase
assert ! posts.many? { |p| p.id < 2 }
end
- assert posts.loaded?
+ assert_predicate posts, :loaded?
end
def test_many_with_limits
posts = Post.all
- assert posts.many?
- assert ! posts.limit(1).many?
+ assert_predicate posts, :many?
+ assert_not_predicate posts.limit(1), :many?
end
def test_none?
@@ -1253,14 +1121,14 @@ class RelationTest < ActiveRecord::TestCase
assert ! posts.none? # Uses COUNT()
end
- assert ! posts.loaded?
+ assert_not_predicate posts, :loaded?
assert_queries(1) do
assert posts.none? { |p| p.id < 0 }
assert ! posts.none? { |p| p.id == 1 }
end
- assert posts.loaded?
+ assert_predicate posts, :loaded?
end
def test_one
@@ -1269,14 +1137,14 @@ class RelationTest < ActiveRecord::TestCase
assert ! posts.one? # Uses COUNT()
end
- assert ! posts.loaded?
+ assert_not_predicate posts, :loaded?
assert_queries(1) do
assert ! posts.one? { |p| p.id < 3 }
assert posts.one? { |p| p.id == 1 }
end
- assert posts.loaded?
+ assert_predicate posts, :loaded?
end
def test_to_a_should_dup_target
@@ -1309,10 +1177,10 @@ class RelationTest < ActiveRecord::TestCase
sparrow = birds.create
assert_kind_of Bird, sparrow
- assert !sparrow.persisted?
+ assert_not_predicate sparrow, :persisted?
hen = birds.where(name: "hen").create
- assert hen.persisted?
+ assert_predicate hen, :persisted?
assert_equal "hen", hen.name
end
@@ -1323,34 +1191,43 @@ class RelationTest < ActiveRecord::TestCase
hen = birds.where(name: "hen").create!
assert_kind_of Bird, hen
- assert hen.persisted?
+ assert_predicate hen, :persisted?
assert_equal "hen", hen.name
end
+ def test_create_with_polymorphic_association
+ author = authors(:david)
+ post = posts(:welcome)
+ comment = Comment.where(post: post, author: author).create!(body: "hello")
+
+ assert_equal author, comment.author
+ assert_equal post, comment.post
+ end
+
def test_first_or_create
parrot = Bird.where(color: "green").first_or_create(name: "parrot")
assert_kind_of Bird, parrot
- assert parrot.persisted?
+ assert_predicate parrot, :persisted?
assert_equal "parrot", parrot.name
assert_equal "green", parrot.color
same_parrot = Bird.where(color: "green").first_or_create(name: "parakeet")
assert_kind_of Bird, same_parrot
- assert same_parrot.persisted?
+ assert_predicate same_parrot, :persisted?
assert_equal parrot, same_parrot
end
def test_first_or_create_with_no_parameters
parrot = Bird.where(color: "green").first_or_create
assert_kind_of Bird, parrot
- assert !parrot.persisted?
+ assert_not_predicate parrot, :persisted?
assert_equal "green", parrot.color
end
def test_first_or_create_with_block
parrot = Bird.where(color: "green").first_or_create { |bird| bird.name = "parrot" }
assert_kind_of Bird, parrot
- assert parrot.persisted?
+ assert_predicate parrot, :persisted?
assert_equal "green", parrot.color
assert_equal "parrot", parrot.name
@@ -1371,13 +1248,13 @@ class RelationTest < ActiveRecord::TestCase
def test_first_or_create_bang_with_valid_options
parrot = Bird.where(color: "green").first_or_create!(name: "parrot")
assert_kind_of Bird, parrot
- assert parrot.persisted?
+ assert_predicate parrot, :persisted?
assert_equal "parrot", parrot.name
assert_equal "green", parrot.color
same_parrot = Bird.where(color: "green").first_or_create!(name: "parakeet")
assert_kind_of Bird, same_parrot
- assert same_parrot.persisted?
+ assert_predicate same_parrot, :persisted?
assert_equal parrot, same_parrot
end
@@ -1392,7 +1269,7 @@ class RelationTest < ActiveRecord::TestCase
def test_first_or_create_bang_with_valid_block
parrot = Bird.where(color: "green").first_or_create! { |bird| bird.name = "parrot" }
assert_kind_of Bird, parrot
- assert parrot.persisted?
+ assert_predicate parrot, :persisted?
assert_equal "green", parrot.color
assert_equal "parrot", parrot.name
@@ -1423,9 +1300,9 @@ class RelationTest < ActiveRecord::TestCase
def test_first_or_initialize
parrot = Bird.where(color: "green").first_or_initialize(name: "parrot")
assert_kind_of Bird, parrot
- assert !parrot.persisted?
- assert parrot.valid?
- assert parrot.new_record?
+ assert_not_predicate parrot, :persisted?
+ assert_predicate parrot, :valid?
+ assert_predicate parrot, :new_record?
assert_equal "parrot", parrot.name
assert_equal "green", parrot.color
end
@@ -1433,18 +1310,18 @@ class RelationTest < ActiveRecord::TestCase
def test_first_or_initialize_with_no_parameters
parrot = Bird.where(color: "green").first_or_initialize
assert_kind_of Bird, parrot
- assert !parrot.persisted?
- assert !parrot.valid?
- assert parrot.new_record?
+ assert_not_predicate parrot, :persisted?
+ assert_not_predicate parrot, :valid?
+ assert_predicate parrot, :new_record?
assert_equal "green", parrot.color
end
def test_first_or_initialize_with_block
parrot = Bird.where(color: "green").first_or_initialize { |bird| bird.name = "parrot" }
assert_kind_of Bird, parrot
- assert !parrot.persisted?
- assert parrot.valid?
- assert parrot.new_record?
+ assert_not_predicate parrot, :persisted?
+ assert_predicate parrot, :valid?
+ assert_predicate parrot, :new_record?
assert_equal "green", parrot.color
assert_equal "parrot", parrot.name
end
@@ -1453,7 +1330,7 @@ class RelationTest < ActiveRecord::TestCase
assert_nil Bird.find_by(name: "bob")
bird = Bird.find_or_create_by(name: "bob")
- assert bird.persisted?
+ assert_predicate bird, :persisted?
assert_equal bird, Bird.find_or_create_by(name: "bob")
end
@@ -1462,7 +1339,7 @@ class RelationTest < ActiveRecord::TestCase
assert_nil Bird.find_by(name: "bob")
bird = Bird.create_with(color: "green").find_or_create_by(name: "bob")
- assert bird.persisted?
+ assert_predicate bird, :persisted?
assert_equal "green", bird.color
assert_equal bird, Bird.create_with(color: "blue").find_or_create_by(name: "bob")
@@ -1476,13 +1353,13 @@ class RelationTest < ActiveRecord::TestCase
assert_nil Bird.find_by(name: "bob")
bird = Bird.find_or_initialize_by(name: "bob")
- assert bird.new_record?
+ assert_predicate bird, :new_record?
bird.save!
assert_equal bird, Bird.find_or_initialize_by(name: "bob")
end
- def test_explicit_create_scope
+ def test_explicit_create_with
hens = Bird.where(name: "hen")
assert_equal "hen", hens.new.name
@@ -1651,10 +1528,10 @@ class RelationTest < ActiveRecord::TestCase
def test_doesnt_add_having_values_if_options_are_blank
scope = Post.having("")
- assert scope.having_clause.empty?
+ assert_empty scope.having_clause
scope = Post.having([])
- assert scope.having_clause.empty?
+ assert_empty scope.having_clause
end
def test_having_with_binds_for_both_where_and_having
@@ -1680,13 +1557,13 @@ class RelationTest < ActiveRecord::TestCase
def test_references_triggers_eager_loading
scope = Post.includes(:comments)
- assert !scope.eager_loading?
- assert scope.references(:comments).eager_loading?
+ assert_not_predicate scope, :eager_loading?
+ assert_predicate scope.references(:comments), :eager_loading?
end
def test_references_doesnt_trigger_eager_loading_if_reference_not_included
scope = Post.references(:comments)
- assert !scope.eager_loading?
+ assert_not_predicate scope, :eager_loading?
end
def test_automatically_added_where_references
@@ -1717,6 +1594,13 @@ class RelationTest < ActiveRecord::TestCase
scope = Post.order("comments.body")
assert_equal ["comments"], scope.references_values
+ scope = Post.order(Arel.sql("#{Comment.quoted_table_name}.#{Comment.quoted_primary_key}"))
+ if current_adapter?(:OracleAdapter)
+ assert_equal ["COMMENTS"], scope.references_values
+ else
+ assert_equal ["comments"], scope.references_values
+ end
+
scope = Post.order("comments.body", "yaks.body")
assert_equal ["comments", "yaks"], scope.references_values
@@ -1727,7 +1611,7 @@ class RelationTest < ActiveRecord::TestCase
scope = Post.order("comments.body asc")
assert_equal ["comments"], scope.references_values
- scope = Post.order("foo(comments.body)")
+ scope = Post.order(Arel.sql("foo(comments.body)"))
assert_equal [], scope.references_values
end
@@ -1735,6 +1619,13 @@ class RelationTest < ActiveRecord::TestCase
scope = Post.reorder("comments.body")
assert_equal %w(comments), scope.references_values
+ scope = Post.reorder(Arel.sql("#{Comment.quoted_table_name}.#{Comment.quoted_primary_key}"))
+ if current_adapter?(:OracleAdapter)
+ assert_equal ["COMMENTS"], scope.references_values
+ else
+ assert_equal ["comments"], scope.references_values
+ end
+
scope = Post.reorder("comments.body", "yaks.body")
assert_equal %w(comments yaks), scope.references_values
@@ -1745,7 +1636,7 @@ class RelationTest < ActiveRecord::TestCase
scope = Post.reorder("comments.body asc")
assert_equal %w(comments), scope.references_values
- scope = Post.reorder("foo(comments.body)")
+ scope = Post.reorder(Arel.sql("foo(comments.body)"))
assert_equal [], scope.references_values
end
@@ -1780,7 +1671,7 @@ class RelationTest < ActiveRecord::TestCase
# count always trigger the COUNT query.
assert_queries(1) { topics.count }
- assert topics.loaded?
+ assert_predicate topics, :loaded?
end
test "find_by with hash conditions returns the first matching record" do
@@ -1871,7 +1762,7 @@ class RelationTest < ActiveRecord::TestCase
test "relations with cached arel can't be mutated [internal API]" do
relation = Post.all
- relation.count
+ relation.arel
assert_raises(ActiveRecord::ImmutableRelation) { relation.limit!(5) }
assert_raises(ActiveRecord::ImmutableRelation) { relation.where!("1 = 2") }
@@ -1905,15 +1796,23 @@ class RelationTest < ActiveRecord::TestCase
end
test "using a custom table affects the wheres" do
- table_alias = Post.arel_table.alias("omg_posts")
+ post = posts(:welcome)
- table_metadata = ActiveRecord::TableMetadata.new(Post, table_alias)
- predicate_builder = ActiveRecord::PredicateBuilder.new(table_metadata)
- relation = ActiveRecord::Relation.new(Post, table_alias, predicate_builder)
- relation.where!(foo: "bar")
+ assert_equal post, custom_post_relation.where!(title: post.title).take
+ end
+
+ test "using a custom table with joins affects the joins" do
+ post = posts(:welcome)
+
+ assert_equal post, custom_post_relation.joins(:author).where!(title: post.title).take
+ end
+
+ test "arel_attribute respects a custom table" do
+ assert_equal [posts(:sti_comments)], custom_post_relation.ranked_by_comments.limit_by(1).to_a
+ end
- node = relation.arel.constraints.first.grep(Arel::Attributes::Attribute).first
- assert_equal table_alias, node.relation
+ test "alias_tracker respects a custom table" do
+ assert_equal posts(:welcome), custom_post_relation("categories_posts").joins(:categories).first
end
test "#load" do
@@ -1947,7 +1846,7 @@ class RelationTest < ActiveRecord::TestCase
test "delegations do not leak to other classes" do
Topic.all.by_lifo
assert Topic.all.class.method_defined?(:by_lifo)
- assert !Post.all.respond_to?(:by_lifo)
+ assert_not_respond_to Post.all, :by_lifo
end
def test_unscope_with_subquery
@@ -1970,56 +1869,81 @@ class RelationTest < ActiveRecord::TestCase
assert_equal 1, posts.unscope(where: :body).count
end
- def test_unscope_removes_binds
- left = Post.where(id: 20)
-
- binds = [bind_attribute("id", 20, Post.type_for_attribute("id"))]
- assert_equal binds, left.bound_attributes
-
- relation = left.unscope(where: :id)
- assert_equal [], relation.bound_attributes
+ def test_locked_should_not_build_arel
+ posts = Post.locked
+ assert_predicate posts, :locked?
+ assert_nothing_raised { posts.lock!(false) }
end
- def test_merging_removes_rhs_binds
- left = Post.where(id: 20)
- right = Post.where(id: [1, 2, 3, 4])
+ def test_relation_join_method
+ assert_equal "Thank you for the welcome,Thank you again for the welcome", Post.first.comments.join(",")
+ end
- binds = [bind_attribute("id", 20, Post.type_for_attribute("id"))]
- assert_equal binds, left.bound_attributes
+ test "#skip_query_cache!" do
+ Post.cache do
+ assert_queries(1) do
+ Post.all.load
+ Post.all.load
+ end
- merged = left.merge(right)
- assert_equal [], merged.bound_attributes
+ assert_queries(2) do
+ Post.all.skip_query_cache!.load
+ Post.all.skip_query_cache!.load
+ end
+ end
end
- def test_merging_keeps_lhs_binds
- binds = [bind_attribute("id", 20, Post.type_for_attribute("id"))]
+ test "#skip_query_cache! with an eager load" do
+ Post.cache do
+ assert_queries(1) do
+ Post.eager_load(:comments).load
+ Post.eager_load(:comments).load
+ end
+
+ assert_queries(2) do
+ Post.eager_load(:comments).skip_query_cache!.load
+ Post.eager_load(:comments).skip_query_cache!.load
+ end
+ end
+ end
- right = Post.where(id: 20)
- left = Post.where(id: 10)
+ test "#skip_query_cache! with a preload" do
+ Post.cache do
+ assert_queries(2) do
+ Post.preload(:comments).load
+ Post.preload(:comments).load
+ end
- merged = left.merge(right)
- assert_equal binds, merged.bound_attributes
+ assert_queries(4) do
+ Post.preload(:comments).skip_query_cache!.load
+ Post.preload(:comments).skip_query_cache!.load
+ end
+ end
end
- def test_relation_join_method
- assert_equal "Thank you for the welcome,Thank you again for the welcome", Post.first.comments.join(",")
+ test "#where with set" do
+ david = authors(:david)
+ mary = authors(:mary)
+
+ authors = Author.where(name: ["David", "Mary"].to_set)
+ assert_equal [david, mary], authors
end
- def test_connection_adapters_can_reorder_binds
- posts = Post.limit(1).offset(2)
+ test "#where with empty set" do
+ authors = Author.where(name: Set.new)
+ assert_empty authors
+ end
- stubbed_connection = Post.connection.dup
- def stubbed_connection.combine_bind_parameters(**kwargs)
- offset = kwargs[:offset]
- kwargs[:offset] = kwargs[:limit]
- kwargs[:limit] = offset
- super(**kwargs)
- end
+ private
+ def custom_post_relation(alias_name = "omg_posts")
+ table_alias = Post.arel_table.alias(alias_name)
+ table_metadata = ActiveRecord::TableMetadata.new(Post, table_alias)
+ predicate_builder = ActiveRecord::PredicateBuilder.new(table_metadata)
- posts.define_singleton_method(:connection) do
- stubbed_connection
+ ActiveRecord::Relation.create(
+ Post,
+ table: table_alias,
+ predicate_builder: predicate_builder
+ )
end
-
- assert_equal 2, posts.to_a.length
- end
end
diff --git a/activerecord/test/cases/reload_models_test.rb b/activerecord/test/cases/reload_models_test.rb
index 3f4c0c03e3..72f4bfaf6d 100644
--- a/activerecord/test/cases/reload_models_test.rb
+++ b/activerecord/test/cases/reload_models_test.rb
@@ -1,8 +1,12 @@
+# frozen_string_literal: true
+
require "cases/helper"
require "models/owner"
require "models/pet"
class ReloadModelsTest < ActiveRecord::TestCase
+ include ActiveSupport::Testing::Isolation
+
fixtures :pets, :owners
def test_has_one_with_reload
@@ -19,4 +23,4 @@ class ReloadModelsTest < ActiveRecord::TestCase
pet.owner = Owner.find_by_name("ashley")
assert_equal pet.owner, Owner.find_by_name("ashley")
end
-end
+end unless in_memory_db?
diff --git a/activerecord/test/cases/reserved_word_test.rb b/activerecord/test/cases/reserved_word_test.rb
index f3019a5326..e32605fd11 100644
--- a/activerecord/test/cases/reserved_word_test.rb
+++ b/activerecord/test/cases/reserved_word_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "cases/helper"
class ReservedWordTest < ActiveRecord::TestCase
@@ -37,7 +39,7 @@ class ReservedWordTest < ActiveRecord::TestCase
t.string :order
t.belongs_to :select
end
- @connection.create_table :values, force: true do |t|
+ @connection.create_table :values, primary_key: :as, force: true do |t|
t.belongs_to :group
end
end
@@ -86,6 +88,13 @@ class ReservedWordTest < ActiveRecord::TestCase
assert_equal x, Group.find(x.id)
end
+ def test_delete_all_with_subselect
+ create_test_fixtures :values
+ assert_equal 1, Values.order(:as).limit(1).offset(1).delete_all
+ assert_raise(ActiveRecord::RecordNotFound) { Values.find(2) }
+ assert Values.find(1)
+ end
+
def test_has_one_associations
create_test_fixtures :group, :values
v = Group.find(1).values
@@ -107,7 +116,7 @@ class ReservedWordTest < ActiveRecord::TestCase
end
def test_activerecord_introspection
- assert Group.table_exists?
+ assert_predicate Group, :table_exists?
assert_equal ["id", "order", "select_id"], Group.columns.map(&:name).sort
end
diff --git a/activerecord/test/cases/result_test.rb b/activerecord/test/cases/result_test.rb
index 1a0b7c6ca7..db52c108ac 100644
--- a/activerecord/test/cases/result_test.rb
+++ b/activerecord/test/cases/result_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "cases/helper"
module ActiveRecord
diff --git a/activerecord/test/cases/sanitize_test.rb b/activerecord/test/cases/sanitize_test.rb
index 72f09186e2..778cf86ac3 100644
--- a/activerecord/test/cases/sanitize_test.rb
+++ b/activerecord/test/cases/sanitize_test.rb
@@ -1,7 +1,10 @@
+# frozen_string_literal: true
+
require "cases/helper"
require "models/binary"
require "models/author"
require "models/post"
+require "models/customer"
class SanitizeTest < ActiveRecord::TestCase
def setup
@@ -9,30 +12,30 @@ class SanitizeTest < ActiveRecord::TestCase
def test_sanitize_sql_array_handles_string_interpolation
quoted_bambi = ActiveRecord::Base.connection.quote_string("Bambi")
- assert_equal "name='#{quoted_bambi}'", Binary.send(:sanitize_sql_array, ["name='%s'", "Bambi"])
- assert_equal "name='#{quoted_bambi}'", Binary.send(:sanitize_sql_array, ["name='%s'", "Bambi".mb_chars])
+ assert_equal "name='#{quoted_bambi}'", Binary.sanitize_sql_array(["name='%s'", "Bambi"])
+ assert_equal "name='#{quoted_bambi}'", Binary.sanitize_sql_array(["name='%s'", "Bambi".mb_chars])
quoted_bambi_and_thumper = ActiveRecord::Base.connection.quote_string("Bambi\nand\nThumper")
- assert_equal "name='#{quoted_bambi_and_thumper}'", Binary.send(:sanitize_sql_array, ["name='%s'", "Bambi\nand\nThumper"])
- assert_equal "name='#{quoted_bambi_and_thumper}'", Binary.send(:sanitize_sql_array, ["name='%s'", "Bambi\nand\nThumper".mb_chars])
+ assert_equal "name='#{quoted_bambi_and_thumper}'", Binary.sanitize_sql_array(["name='%s'", "Bambi\nand\nThumper"])
+ assert_equal "name='#{quoted_bambi_and_thumper}'", Binary.sanitize_sql_array(["name='%s'", "Bambi\nand\nThumper".mb_chars])
end
def test_sanitize_sql_array_handles_bind_variables
quoted_bambi = ActiveRecord::Base.connection.quote("Bambi")
- assert_equal "name=#{quoted_bambi}", Binary.send(:sanitize_sql_array, ["name=?", "Bambi"])
- assert_equal "name=#{quoted_bambi}", Binary.send(:sanitize_sql_array, ["name=?", "Bambi".mb_chars])
+ assert_equal "name=#{quoted_bambi}", Binary.sanitize_sql_array(["name=?", "Bambi"])
+ assert_equal "name=#{quoted_bambi}", Binary.sanitize_sql_array(["name=?", "Bambi".mb_chars])
quoted_bambi_and_thumper = ActiveRecord::Base.connection.quote("Bambi\nand\nThumper")
- assert_equal "name=#{quoted_bambi_and_thumper}", Binary.send(:sanitize_sql_array, ["name=?", "Bambi\nand\nThumper"])
- assert_equal "name=#{quoted_bambi_and_thumper}", Binary.send(:sanitize_sql_array, ["name=?", "Bambi\nand\nThumper".mb_chars])
+ assert_equal "name=#{quoted_bambi_and_thumper}", Binary.sanitize_sql_array(["name=?", "Bambi\nand\nThumper"])
+ assert_equal "name=#{quoted_bambi_and_thumper}", Binary.sanitize_sql_array(["name=?", "Bambi\nand\nThumper".mb_chars])
end
def test_sanitize_sql_array_handles_named_bind_variables
quoted_bambi = ActiveRecord::Base.connection.quote("Bambi")
- assert_equal "name=#{quoted_bambi}", Binary.send(:sanitize_sql_array, ["name=:name", name: "Bambi"])
- assert_equal "name=#{quoted_bambi} AND id=1", Binary.send(:sanitize_sql_array, ["name=:name AND id=:id", name: "Bambi", id: 1])
+ assert_equal "name=#{quoted_bambi}", Binary.sanitize_sql_array(["name=:name", name: "Bambi"])
+ assert_equal "name=#{quoted_bambi} AND id=1", Binary.sanitize_sql_array(["name=:name AND id=:id", name: "Bambi", id: 1])
quoted_bambi_and_thumper = ActiveRecord::Base.connection.quote("Bambi\nand\nThumper")
- assert_equal "name=#{quoted_bambi_and_thumper}", Binary.send(:sanitize_sql_array, ["name=:name", name: "Bambi\nand\nThumper"])
- assert_equal "name=#{quoted_bambi_and_thumper} AND name2=#{quoted_bambi_and_thumper}", Binary.send(:sanitize_sql_array, ["name=:name AND name2=:name", name: "Bambi\nand\nThumper"])
+ assert_equal "name=#{quoted_bambi_and_thumper}", Binary.sanitize_sql_array(["name=:name", name: "Bambi\nand\nThumper"])
+ assert_equal "name=#{quoted_bambi_and_thumper} AND name2=#{quoted_bambi_and_thumper}", Binary.sanitize_sql_array(["name=:name AND name2=:name", name: "Bambi\nand\nThumper"])
end
def test_sanitize_sql_array_handles_relations
@@ -41,42 +44,50 @@ class SanitizeTest < ActiveRecord::TestCase
sub_query_pattern = /\(\bselect\b.*?\bwhere\b.*?\)/i
- select_author_sql = Post.send(:sanitize_sql_array, ["id in (?)", david_posts])
+ select_author_sql = Post.sanitize_sql_array(["id in (?)", david_posts])
assert_match(sub_query_pattern, select_author_sql, "should sanitize `Relation` as subquery for bind variables")
- select_author_sql = Post.send(:sanitize_sql_array, ["id in (:post_ids)", post_ids: david_posts])
+ select_author_sql = Post.sanitize_sql_array(["id in (:post_ids)", post_ids: david_posts])
assert_match(sub_query_pattern, select_author_sql, "should sanitize `Relation` as subquery for named bind variables")
end
def test_sanitize_sql_array_handles_empty_statement
- select_author_sql = Post.send(:sanitize_sql_array, [""])
+ select_author_sql = Post.sanitize_sql_array([""])
assert_equal("", select_author_sql)
end
def test_sanitize_sql_like
- assert_equal '100\%', Binary.send(:sanitize_sql_like, "100%")
- assert_equal 'snake\_cased\_string', Binary.send(:sanitize_sql_like, "snake_cased_string")
- assert_equal 'C:\\\\Programs\\\\MsPaint', Binary.send(:sanitize_sql_like, 'C:\\Programs\\MsPaint')
- assert_equal "normal string 42", Binary.send(:sanitize_sql_like, "normal string 42")
+ assert_equal '100\%', Binary.sanitize_sql_like("100%")
+ assert_equal 'snake\_cased\_string', Binary.sanitize_sql_like("snake_cased_string")
+ assert_equal 'C:\\\\Programs\\\\MsPaint', Binary.sanitize_sql_like('C:\\Programs\\MsPaint')
+ assert_equal "normal string 42", Binary.sanitize_sql_like("normal string 42")
end
def test_sanitize_sql_like_with_custom_escape_character
- assert_equal "100!%", Binary.send(:sanitize_sql_like, "100%", "!")
- assert_equal "snake!_cased!_string", Binary.send(:sanitize_sql_like, "snake_cased_string", "!")
- assert_equal "great!!", Binary.send(:sanitize_sql_like, "great!", "!")
- assert_equal 'C:\\Programs\\MsPaint', Binary.send(:sanitize_sql_like, 'C:\\Programs\\MsPaint', "!")
- assert_equal "normal string 42", Binary.send(:sanitize_sql_like, "normal string 42", "!")
+ assert_equal "100!%", Binary.sanitize_sql_like("100%", "!")
+ assert_equal "snake!_cased!_string", Binary.sanitize_sql_like("snake_cased_string", "!")
+ assert_equal "great!!", Binary.sanitize_sql_like("great!", "!")
+ assert_equal 'C:\\Programs\\MsPaint', Binary.sanitize_sql_like('C:\\Programs\\MsPaint', "!")
+ assert_equal "normal string 42", Binary.sanitize_sql_like("normal string 42", "!")
end
def test_sanitize_sql_like_example_use_case
searchable_post = Class.new(Post) do
- def self.search(term)
+ def self.search_as_method(term)
where("title LIKE ?", sanitize_sql_like(term, "!"))
end
+
+ scope :search_as_scope, -> (term) {
+ where("title LIKE ?", sanitize_sql_like(term, "!"))
+ }
end
assert_sql(/LIKE '20!% !_reduction!_!!'/) do
- searchable_post.search("20% _reduction_!").to_a
+ searchable_post.search_as_method("20% _reduction_!").to_a
+ end
+
+ assert_sql(/LIKE '20!% !_reduction!_!!'/) do
+ searchable_post.search_as_scope("20% _reduction_!").to_a
end
end
@@ -151,24 +162,18 @@ class SanitizeTest < ActiveRecord::TestCase
assert_equal "name=#{quoted_bambi_and_thumper}", bind("name=?", "Bambi\nand\nThumper".mb_chars)
end
- def test_bind_record
- o = Class.new {
- def quoted_id
- 1
- end
- }.new
- assert_deprecated { assert_equal "1", bind("?", o) }
-
- os = [o] * 3
- assert_deprecated { assert_equal "1,1,1", bind("?", os) }
- end
-
def test_named_bind_with_postgresql_type_casts
l = Proc.new { bind(":a::integer '2009-01-01'::date", a: "10") }
assert_nothing_raised(&l)
assert_equal "#{ActiveRecord::Base.connection.quote('10')}::integer '2009-01-01'::date", l.call
end
+ def test_deprecated_expand_hash_conditions_for_aggregates
+ assert_deprecated do
+ assert_equal({ "balance" => 50 }, Customer.send(:expand_hash_conditions_for_aggregates, balance: Money.new(50)))
+ end
+ end
+
private
def bind(statement, *vars)
if vars.first.is_a?(Hash)
diff --git a/activerecord/test/cases/schema_dumper_test.rb b/activerecord/test/cases/schema_dumper_test.rb
index 4c81e825fa..a612ce9bb2 100644
--- a/activerecord/test/cases/schema_dumper_test.rb
+++ b/activerecord/test/cases/schema_dumper_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "cases/helper"
require "support/schema_dumping_helper"
@@ -64,7 +66,7 @@ class SchemaDumperTest < ActiveRecord::TestCase
def test_schema_dump_uses_force_cascade_on_create_table
output = dump_table_schema "authors"
- assert_match %r{create_table "authors", force: :cascade}, output
+ assert_match %r{create_table "authors",.* force: :cascade}, output
end
def test_schema_dump_excludes_sqlite_sequence
@@ -175,14 +177,14 @@ class SchemaDumperTest < ActiveRecord::TestCase
def test_schema_dumps_index_columns_in_right_order
index_definition = dump_table_schema("companies").split(/\n/).grep(/t\.index.*company_index/).first.strip
- if current_adapter?(:PostgreSQLAdapter)
- assert_equal 't.index ["firm_id", "type", "rating"], name: "company_index", order: { rating: :desc }', index_definition
- elsif current_adapter?(:Mysql2Adapter)
+ if current_adapter?(:Mysql2Adapter)
if ActiveRecord::Base.connection.supports_index_sort_order?
assert_equal 't.index ["firm_id", "type", "rating"], name: "company_index", length: { type: 10 }, order: { rating: :desc }', index_definition
else
assert_equal 't.index ["firm_id", "type", "rating"], name: "company_index", length: { type: 10 }', index_definition
end
+ elsif ActiveRecord::Base.connection.supports_index_sort_order?
+ assert_equal 't.index ["firm_id", "type", "rating"], name: "company_index", order: { rating: :desc }', index_definition
else
assert_equal 't.index ["firm_id", "type", "rating"], name: "company_index"', index_definition
end
@@ -197,6 +199,24 @@ class SchemaDumperTest < ActiveRecord::TestCase
end
end
+ def test_schema_dumps_index_sort_order
+ index_definition = dump_table_schema("companies").split(/\n/).grep(/t\.index.*_name_and_rating/).first.strip
+ if ActiveRecord::Base.connection.supports_index_sort_order?
+ assert_equal 't.index ["name", "rating"], name: "index_companies_on_name_and_rating", order: :desc', index_definition
+ else
+ assert_equal 't.index ["name", "rating"], name: "index_companies_on_name_and_rating"', index_definition
+ end
+ end
+
+ def test_schema_dumps_index_length
+ index_definition = dump_table_schema("companies").split(/\n/).grep(/t\.index.*_name_and_description/).first.strip
+ if current_adapter?(:Mysql2Adapter)
+ assert_equal 't.index ["name", "description"], name: "index_companies_on_name_and_description", length: 10', index_definition
+ else
+ assert_equal 't.index ["name", "description"], name: "index_companies_on_name_and_description"', index_definition
+ end
+ end
+
def test_schema_dump_should_honor_nonstandard_primary_keys
output = standard_dump
match = output.match(%r{create_table "movies"(.*)do})
@@ -205,20 +225,25 @@ class SchemaDumperTest < ActiveRecord::TestCase
end
def test_schema_dump_should_use_false_as_default
- output = standard_dump
+ output = dump_table_schema "booleans"
assert_match %r{t\.boolean\s+"has_fun",.+default: false}, output
end
def test_schema_dump_does_not_include_limit_for_text_field
- output = standard_dump
+ output = dump_table_schema "admin_users"
assert_match %r{t\.text\s+"params"$}, output
end
def test_schema_dump_does_not_include_limit_for_binary_field
- output = standard_dump
+ output = dump_table_schema "binaries"
assert_match %r{t\.binary\s+"data"$}, output
end
+ def test_schema_dump_does_not_include_limit_for_float_field
+ output = dump_table_schema "numeric_data"
+ assert_match %r{t\.float\s+"temperature"$}, output
+ end
+
if current_adapter?(:Mysql2Adapter)
def test_schema_dump_includes_length_for_mysql_binary_fields
output = standard_dump
@@ -287,20 +312,32 @@ class SchemaDumperTest < ActiveRecord::TestCase
assert_match %r{t\.oid\s+"obj_id"$}, output
end
- if ActiveRecord::Base.connection.supports_extensions?
- def test_schema_dump_includes_extensions
- connection = ActiveRecord::Base.connection
+ def test_schema_dump_includes_extensions
+ connection = ActiveRecord::Base.connection
- connection.stubs(:extensions).returns(["hstore"])
- output = perform_schema_dump
- assert_match "# These are extensions that must be enabled", output
- assert_match %r{enable_extension "hstore"}, output
+ connection.stubs(:extensions).returns(["hstore"])
+ output = perform_schema_dump
+ assert_match "# These are extensions that must be enabled", output
+ assert_match %r{enable_extension "hstore"}, output
- connection.stubs(:extensions).returns([])
- output = perform_schema_dump
- assert_no_match "# These are extensions that must be enabled", output
- assert_no_match %r{enable_extension}, output
- end
+ connection.stubs(:extensions).returns([])
+ output = perform_schema_dump
+ assert_no_match "# These are extensions that must be enabled", output
+ assert_no_match %r{enable_extension}, output
+ end
+
+ def test_schema_dump_includes_extensions_in_alphabetic_order
+ connection = ActiveRecord::Base.connection
+
+ connection.stubs(:extensions).returns(["hstore", "uuid-ossp", "xml2"])
+ output = perform_schema_dump
+ enabled_extensions = output.scan(%r{enable_extension "(.+)"}).flatten
+ assert_equal ["hstore", "uuid-ossp", "xml2"], enabled_extensions
+
+ connection.stubs(:extensions).returns(["uuid-ossp", "xml2", "hstore"])
+ output = perform_schema_dump
+ enabled_extensions = output.scan(%r{enable_extension "(.+)"}).flatten
+ assert_equal ["hstore", "uuid-ossp", "xml2"], enabled_extensions
end
end
@@ -384,6 +421,31 @@ class SchemaDumperTest < ActiveRecord::TestCase
$stdout = original
end
+ def test_schema_dump_with_table_name_prefix_and_suffix_regexp_escape
+ original, $stdout = $stdout, StringIO.new
+ ActiveRecord::Base.table_name_prefix = "foo$"
+ ActiveRecord::Base.table_name_suffix = "$bar"
+
+ migration = CreateDogMigration.new
+ migration.migrate(:up)
+
+ output = perform_schema_dump
+ assert_no_match %r{create_table "foo\$.+\$bar"}, output
+ assert_no_match %r{add_index "foo\$.+\$bar"}, output
+ assert_no_match %r{create_table "schema_migrations"}, output
+ assert_no_match %r{create_table "ar_internal_metadata"}, output
+
+ if ActiveRecord::Base.connection.supports_foreign_keys?
+ assert_no_match %r{add_foreign_key "foo\$.+\$bar"}, output
+ assert_no_match %r{add_foreign_key "[^"]+", "foo\$.+\$bar"}, output
+ end
+ ensure
+ migration.migrate(:down)
+
+ ActiveRecord::Base.table_name_suffix = ActiveRecord::Base.table_name_prefix = ""
+ $stdout = original
+ end
+
def test_schema_dump_with_table_name_prefix_and_ignoring_tables
original, $stdout = $stdout, StringIO.new
diff --git a/activerecord/test/cases/schema_loading_test.rb b/activerecord/test/cases/schema_loading_test.rb
index 362370ac61..f539156466 100644
--- a/activerecord/test/cases/schema_loading_test.rb
+++ b/activerecord/test/cases/schema_loading_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "cases/helper"
module SchemaLoadCounter
diff --git a/activerecord/test/cases/scoping/default_scoping_test.rb b/activerecord/test/cases/scoping/default_scoping_test.rb
index 89fb434b27..0804de1fb3 100644
--- a/activerecord/test/cases/scoping/default_scoping_test.rb
+++ b/activerecord/test/cases/scoping/default_scoping_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "cases/helper"
require "models/post"
require "models/comment"
@@ -118,49 +120,49 @@ class DefaultScopingTest < ActiveRecord::TestCase
def test_unscope_with_where_attributes
expected = Developer.order("salary DESC").collect(&:name)
received = DeveloperOrderedBySalary.where(name: "David").unscope(where: :name).collect(&:name)
- assert_equal expected, received
+ assert_equal expected.sort, received.sort
expected_2 = Developer.order("salary DESC").collect(&:name)
received_2 = DeveloperOrderedBySalary.select("id").where("name" => "Jamis").unscope({ where: :name }, :select).collect(&:name)
- assert_equal expected_2, received_2
+ assert_equal expected_2.sort, received_2.sort
expected_3 = Developer.order("salary DESC").collect(&:name)
received_3 = DeveloperOrderedBySalary.select("id").where("name" => "Jamis").unscope(:select, :where).collect(&:name)
- assert_equal expected_3, received_3
+ assert_equal expected_3.sort, received_3.sort
expected_4 = Developer.order("salary DESC").collect(&:name)
received_4 = DeveloperOrderedBySalary.where.not("name" => "Jamis").unscope(where: :name).collect(&:name)
- assert_equal expected_4, received_4
+ assert_equal expected_4.sort, received_4.sort
expected_5 = Developer.order("salary DESC").collect(&:name)
received_5 = DeveloperOrderedBySalary.where.not("name" => ["Jamis", "David"]).unscope(where: :name).collect(&:name)
- assert_equal expected_5, received_5
+ assert_equal expected_5.sort, received_5.sort
expected_6 = Developer.order("salary DESC").collect(&:name)
received_6 = DeveloperOrderedBySalary.where(Developer.arel_table["name"].eq("David")).unscope(where: :name).collect(&:name)
- assert_equal expected_6, received_6
+ assert_equal expected_6.sort, received_6.sort
expected_7 = Developer.order("salary DESC").collect(&:name)
received_7 = DeveloperOrderedBySalary.where(Developer.arel_table[:name].eq("David")).unscope(where: :name).collect(&:name)
- assert_equal expected_7, received_7
+ assert_equal expected_7.sort, received_7.sort
end
def test_unscope_comparison_where_clauses
# unscoped for WHERE (`developers`.`id` <= 2)
expected = Developer.order("salary DESC").collect(&:name)
received = DeveloperOrderedBySalary.where(id: -Float::INFINITY..2).unscope(where: :id).collect { |dev| dev.name }
- assert_equal expected, received
+ assert_equal expected.sort, received.sort
# unscoped for WHERE (`developers`.`id` < 2)
expected = Developer.order("salary DESC").collect(&:name)
received = DeveloperOrderedBySalary.where(id: -Float::INFINITY...2).unscope(where: :id).collect { |dev| dev.name }
- assert_equal expected, received
+ assert_equal expected.sort, received.sort
end
def test_unscope_multiple_where_clauses
expected = Developer.order("salary DESC").collect(&:name)
received = DeveloperOrderedBySalary.where(name: "Jamis").where(id: 1).unscope(where: [:name, :id]).collect(&:name)
- assert_equal expected, received
+ assert_equal expected.sort, received.sort
end
def test_unscope_string_where_clauses_involved
@@ -170,23 +172,23 @@ class DefaultScopingTest < ActiveRecord::TestCase
dev_ordered_relation = DeveloperOrderedBySalary.where(name: "Jamis").where("created_at > ?", 1.year.ago)
received = dev_ordered_relation.unscope(where: [:name]).collect(&:name)
- assert_equal expected, received
+ assert_equal expected.sort, received.sort
end
def test_unscope_with_grouping_attributes
expected = Developer.order("salary DESC").collect(&:name)
received = DeveloperOrderedBySalary.group(:name).unscope(:group).collect(&:name)
- assert_equal expected, received
+ assert_equal expected.sort, received.sort
expected_2 = Developer.order("salary DESC").collect(&:name)
received_2 = DeveloperOrderedBySalary.group("name").unscope(:group).collect(&:name)
- assert_equal expected_2, received_2
+ assert_equal expected_2.sort, received_2.sort
end
def test_unscope_with_limit_in_query
expected = Developer.order("salary DESC").collect(&:name)
received = DeveloperOrderedBySalary.limit(1).unscope(:limit).collect(&:name)
- assert_equal expected, received
+ assert_equal expected.sort, received.sort
end
def test_order_to_unscope_reordering
@@ -222,6 +224,18 @@ class DefaultScopingTest < ActiveRecord::TestCase
assert_equal expected, received
end
+ def test_unscope_left_outer_joins
+ expected = Developer.all.collect(&:name)
+ received = Developer.left_outer_joins(:projects).select(:id).unscope(:left_outer_joins, :select).collect(&:name)
+ assert_equal expected, received
+ end
+
+ def test_unscope_left_joins
+ expected = Developer.all.collect(&:name)
+ received = Developer.left_joins(:projects).select(:id).unscope(:left_joins, :select).collect(&:name)
+ assert_equal expected, received
+ end
+
def test_unscope_includes
expected = Developer.all.collect(&:name)
received = Developer.includes(:projects).select(:id).unscope(:includes, :select).collect(&:name)
@@ -288,8 +302,8 @@ class DefaultScopingTest < ActiveRecord::TestCase
def test_unscope_merging
merged = Developer.where(name: "Jamis").merge(Developer.unscope(:where))
- assert merged.where_clause.empty?
- assert !merged.where(name: "Jon").where_clause.empty?
+ assert_empty merged.where_clause
+ assert_not_empty merged.where(name: "Jon").where_clause
end
def test_order_in_default_scope_should_not_prevail
@@ -332,7 +346,7 @@ class DefaultScopingTest < ActiveRecord::TestCase
def test_create_with_merge
aaron = PoorDeveloperCalledJamis.create_with(name: "foo", salary: 20).merge(
- PoorDeveloperCalledJamis.create_with(name: "Aaron")).new
+ PoorDeveloperCalledJamis.create_with(name: "Aaron")).new
assert_equal 20, aaron.salary
assert_equal "Aaron", aaron.name
@@ -342,6 +356,11 @@ class DefaultScopingTest < ActiveRecord::TestCase
assert_equal "Aaron", aaron.name
end
+ def test_create_with_using_both_string_and_symbol
+ jamis = PoorDeveloperCalledJamis.create_with(name: "foo").create_with("name" => "Aaron").new
+ assert_equal "Aaron", jamis.name
+ end
+
def test_create_with_reset
jamis = PoorDeveloperCalledJamis.create_with(name: "Aaron").create_with(nil).new
assert_equal "Jamis", jamis.name
@@ -372,11 +391,39 @@ class DefaultScopingTest < ActiveRecord::TestCase
Comment.joins(:post).count
end
+ def test_joins_not_affected_by_scope_other_than_default_or_unscoped
+ without_scope_on_post = Comment.joins(:post).to_a
+ with_scope_on_post = nil
+ Post.where(id: [1, 5, 6]).scoping do
+ with_scope_on_post = Comment.joins(:post).to_a
+ end
+
+ assert_equal with_scope_on_post, without_scope_on_post
+ end
+
def test_unscoped_with_joins_should_not_have_default_scope
assert_equal SpecialPostWithDefaultScope.unscoped { Comment.joins(:special_post_with_default_scope).to_a },
Comment.joins(:post).to_a
end
+ def test_sti_association_with_unscoped_not_affected_by_default_scope
+ post = posts(:thinking)
+ comments = [comments(:does_it_hurt)]
+
+ post.special_comments.update_all(deleted_at: Time.now)
+
+ assert_raises(ActiveRecord::RecordNotFound) { Post.joins(:special_comments).find(post.id) }
+ assert_equal [], post.special_comments
+
+ SpecialComment.unscoped do
+ assert_equal post, Post.joins(:special_comments).find(post.id)
+ assert_equal comments, Post.joins(:special_comments).find(post.id).special_comments
+ assert_equal comments, Post.eager_load(:special_comments).find(post.id).special_comments
+ assert_equal comments, Post.includes(:special_comments).find(post.id).special_comments
+ assert_equal comments, Post.preload(:special_comments).find(post.id).special_comments
+ end
+ end
+
def test_default_scope_select_ignored_by_aggregations
assert_equal DeveloperWithSelect.all.to_a.count, DeveloperWithSelect.count
end
@@ -437,7 +484,7 @@ class DefaultScopingTest < ActiveRecord::TestCase
test "a scope can remove the condition from the default scope" do
scope = DeveloperCalledJamis.david2
assert_equal 1, scope.where_clause.ast.children.length
- assert_equal Developer.where(name: "David"), scope
+ assert_equal Developer.where(name: "David").map(&:id), scope.map(&:id)
end
def test_with_abstract_class_where_clause_should_not_be_duplicated
@@ -486,6 +533,8 @@ class DefaultScopingWithThreadTest < ActiveRecord::TestCase
end
def test_default_scope_is_threadsafe
+ 2.times { ThreadsafeDeveloper.unscoped.create! }
+
threads = []
assert_not_equal 1, ThreadsafeDeveloper.unscoped.count
@@ -504,5 +553,7 @@ class DefaultScopingWithThreadTest < ActiveRecord::TestCase
ThreadsafeDeveloper.connection.close
end
threads.each(&:join)
+ ensure
+ ThreadsafeDeveloper.unscoped.destroy_all
end
end unless in_memory_db?
diff --git a/activerecord/test/cases/scoping/named_scoping_test.rb b/activerecord/test/cases/scoping/named_scoping_test.rb
index 483ea7128d..ea71a5ce28 100644
--- a/activerecord/test/cases/scoping/named_scoping_test.rb
+++ b/activerecord/test/cases/scoping/named_scoping_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "cases/helper"
require "models/post"
require "models/topic"
@@ -11,7 +13,7 @@ class NamedScopingTest < ActiveRecord::TestCase
fixtures :posts, :authors, :topics, :comments, :author_addresses
def test_implements_enumerable
- assert !Topic.all.empty?
+ assert_not_empty Topic.all
assert_equal Topic.all.to_a, Topic.base
assert_equal Topic.all.to_a, Topic.base.to_a
@@ -38,7 +40,7 @@ class NamedScopingTest < ActiveRecord::TestCase
end
def test_delegates_finds_and_calculations_to_the_base_class
- assert !Topic.all.empty?
+ assert_not_empty Topic.all
assert_equal Topic.all.to_a, Topic.base.to_a
assert_equal Topic.first, Topic.base.first
@@ -63,13 +65,13 @@ class NamedScopingTest < ActiveRecord::TestCase
end
def test_scope_should_respond_to_own_methods_and_methods_of_the_proxy
- assert Topic.approved.respond_to?(:limit)
- assert Topic.approved.respond_to?(:count)
- assert Topic.approved.respond_to?(:length)
+ assert_respond_to Topic.approved, :limit
+ assert_respond_to Topic.approved, :count
+ assert_respond_to Topic.approved, :length
end
def test_scopes_with_options_limit_finds_to_those_matching_the_criteria_specified
- assert !Topic.all.merge!(where: { approved: true }).to_a.empty?
+ assert_not_empty Topic.all.merge!(where: { approved: true }).to_a
assert_equal Topic.all.merge!(where: { approved: true }).to_a, Topic.approved
assert_equal Topic.where(approved: true).count, Topic.approved.count
@@ -85,7 +87,7 @@ class NamedScopingTest < ActiveRecord::TestCase
assert_equal((approved = Topic.all.merge!(where: { approved: true }).to_a), Topic.approved)
assert_equal((replied = Topic.all.merge!(where: "replies_count > 0").to_a), Topic.replied)
assert !(approved == replied)
- assert !(approved & replied).empty?
+ assert_not_empty (approved & replied)
assert_equal approved & replied, Topic.approved.replied
end
@@ -113,9 +115,10 @@ class NamedScopingTest < ActiveRecord::TestCase
def test_has_many_associations_have_access_to_scopes
assert_not_equal Post.containing_the_letter_a, authors(:david).posts
- assert !Post.containing_the_letter_a.empty?
+ assert_not_empty Post.containing_the_letter_a
- assert_equal authors(:david).posts & Post.containing_the_letter_a, authors(:david).posts.containing_the_letter_a
+ expected = authors(:david).posts & Post.containing_the_letter_a
+ assert_equal expected.sort_by(&:id), authors(:david).posts.containing_the_letter_a.sort_by(&:id)
end
def test_scope_with_STI
@@ -125,14 +128,15 @@ class NamedScopingTest < ActiveRecord::TestCase
def test_has_many_through_associations_have_access_to_scopes
assert_not_equal Comment.containing_the_letter_e, authors(:david).comments
- assert !Comment.containing_the_letter_e.empty?
+ assert_not_empty Comment.containing_the_letter_e
- assert_equal authors(:david).comments & Comment.containing_the_letter_e, authors(:david).comments.containing_the_letter_e
+ expected = authors(:david).comments & Comment.containing_the_letter_e
+ assert_equal expected.sort_by(&:id), authors(:david).comments.containing_the_letter_e.sort_by(&:id)
end
def test_scopes_honor_current_scopes_from_when_defined
- assert !Post.ranked_by_comments.limit_by(5).empty?
- assert !authors(:david).posts.ranked_by_comments.limit_by(5).empty?
+ assert_not_empty Post.ranked_by_comments.limit_by(5)
+ assert_not_empty authors(:david).posts.ranked_by_comments.limit_by(5)
assert_not_equal Post.ranked_by_comments.limit_by(5), authors(:david).posts.ranked_by_comments.limit_by(5)
assert_not_equal Post.top(5), authors(:david).posts.top(5)
# Oracle sometimes sorts differently if WHERE condition is changed
@@ -147,15 +151,31 @@ class NamedScopingTest < ActiveRecord::TestCase
assert_equal "The scope body needs to be callable.", e.message
end
+ def test_scopes_name_is_relation_method
+ conflicts = [
+ :records,
+ :to_ary,
+ :to_sql,
+ :explain
+ ]
+
+ conflicts.each do |name|
+ e = assert_raises ArgumentError do
+ Class.new(Post).class_eval { scope name, -> { where(approved: true) } }
+ end
+ assert_match(/You tried to define a scope named \"#{name}\" on the model/, e.message)
+ end
+ end
+
def test_active_records_have_scope_named__all__
- assert !Topic.all.empty?
+ assert_not_empty Topic.all
assert_equal Topic.all.to_a, Topic.base
end
def test_active_records_have_scope_named__scoped__
scope = Topic.where("content LIKE '%Have%'")
- assert !scope.empty?
+ assert_not_empty scope
assert_equal scope, Topic.all.merge!(where: "content LIKE '%Have%'")
end
@@ -208,9 +228,9 @@ class NamedScopingTest < ActiveRecord::TestCase
end
def test_model_class_should_respond_to_any
- assert Topic.any?
+ assert_predicate Topic, :any?
Topic.delete_all
- assert !Topic.any?
+ assert_not_predicate Topic, :any?
end
def test_many_should_not_load_results
@@ -239,22 +259,22 @@ class NamedScopingTest < ActiveRecord::TestCase
def test_many_should_return_false_if_none_or_one
topics = Topic.base.where(id: 0)
- assert !topics.many?
+ assert_not_predicate topics, :many?
topics = Topic.base.where(id: 1)
- assert !topics.many?
+ assert_not_predicate topics, :many?
end
def test_many_should_return_true_if_more_than_one
- assert Topic.base.many?
+ assert_predicate Topic.base, :many?
end
def test_model_class_should_respond_to_many
Topic.delete_all
- assert !Topic.many?
+ assert_not_predicate Topic, :many?
Topic.create!
- assert !Topic.many?
+ assert_not_predicate Topic, :many?
Topic.create!
- assert Topic.many?
+ assert_predicate Topic, :many?
end
def test_should_build_on_top_of_scope
@@ -403,16 +423,16 @@ class NamedScopingTest < ActiveRecord::TestCase
def test_chaining_applies_last_conditions_when_creating
post = Topic.rejected.new
- assert !post.approved?
+ assert_not_predicate post, :approved?
post = Topic.rejected.approved.new
- assert post.approved?
+ assert_predicate post, :approved?
post = Topic.approved.rejected.new
- assert !post.approved?
+ assert_not_predicate post, :approved?
post = Topic.approved.rejected.approved.new
- assert post.approved?
+ assert_predicate post, :approved?
end
def test_chaining_combines_conditions_when_searching
@@ -478,7 +498,7 @@ class NamedScopingTest < ActiveRecord::TestCase
def test_index_on_scope
approved = Topic.approved.order("id ASC")
assert_equal topics(:second), approved[0]
- assert approved.loaded?
+ assert_predicate approved, :loaded?
end
def test_nested_scopes_queries_size
@@ -558,16 +578,16 @@ class NamedScopingTest < ActiveRecord::TestCase
end
def test_model_class_should_respond_to_none
- assert !Topic.none?
+ assert_not_predicate Topic, :none?
Topic.delete_all
- assert Topic.none?
+ assert_predicate Topic, :none?
end
def test_model_class_should_respond_to_one
- assert !Topic.one?
+ assert_not_predicate Topic, :one?
Topic.delete_all
- assert !Topic.one?
+ assert_not_predicate Topic, :one?
Topic.create!
- assert Topic.one?
+ assert_predicate Topic, :one?
end
end
diff --git a/activerecord/test/cases/scoping/relation_scoping_test.rb b/activerecord/test/cases/scoping/relation_scoping_test.rb
index 8535be8402..5c86bc892d 100644
--- a/activerecord/test/cases/scoping/relation_scoping_test.rb
+++ b/activerecord/test/cases/scoping/relation_scoping_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "cases/helper"
require "models/post"
require "models/author"
@@ -211,21 +213,44 @@ class RelationScopingTest < ActiveRecord::TestCase
def test_current_scope_does_not_pollute_sibling_subclasses
Comment.none.scoping do
- assert_not SpecialComment.all.any?
- assert_not VerySpecialComment.all.any?
- assert_not SubSpecialComment.all.any?
+ assert_not_predicate SpecialComment.all, :any?
+ assert_not_predicate VerySpecialComment.all, :any?
+ assert_not_predicate SubSpecialComment.all, :any?
end
SpecialComment.none.scoping do
- assert Comment.all.any?
- assert VerySpecialComment.all.any?
- assert_not SubSpecialComment.all.any?
+ assert_predicate Comment.all, :any?
+ assert_predicate VerySpecialComment.all, :any?
+ assert_not_predicate SubSpecialComment.all, :any?
end
SubSpecialComment.none.scoping do
- assert Comment.all.any?
- assert VerySpecialComment.all.any?
- assert SpecialComment.all.any?
+ assert_predicate Comment.all, :any?
+ assert_predicate VerySpecialComment.all, :any?
+ assert_predicate SpecialComment.all, :any?
+ end
+ end
+
+ def test_scoping_is_correctly_restored
+ Comment.unscoped do
+ SpecialComment.unscoped.created
+ end
+
+ assert_nil Comment.current_scope
+ assert_nil SpecialComment.current_scope
+ end
+
+ def test_scoping_respects_current_class
+ Comment.unscoped do
+ assert_equal "a comment...", Comment.all.what_are_you
+ assert_equal "a special comment...", SpecialComment.all.what_are_you
+ end
+ end
+
+ def test_scoping_respects_sti_constraint
+ Comment.unscoped do
+ assert_equal comments(:greetings), Comment.find(1)
+ assert_raises(ActiveRecord::RecordNotFound) { SpecialComment.find(1) }
end
end
@@ -309,7 +334,7 @@ class NestedRelationScopingTest < ActiveRecord::TestCase
def test_nested_exclusive_scope_for_create
comment = Comment.create_with(body: "Hey guys, nested scopes are broken. Please fix!").scoping do
Comment.unscoped.create_with(post_id: 1).scoping do
- assert Comment.new.body.blank?
+ assert_predicate Comment.new.body, :blank?
Comment.create body: "Hey guys"
end
end
diff --git a/activerecord/test/cases/secure_token_test.rb b/activerecord/test/cases/secure_token_test.rb
index 7b9cbee40a..f5fa6aa302 100644
--- a/activerecord/test/cases/secure_token_test.rb
+++ b/activerecord/test/cases/secure_token_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "cases/helper"
require "models/user"
diff --git a/activerecord/test/cases/serialization_test.rb b/activerecord/test/cases/serialization_test.rb
index ec33ad38f2..2d829ad4ba 100644
--- a/activerecord/test/cases/serialization_test.rb
+++ b/activerecord/test/cases/serialization_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "cases/helper"
require "models/contact"
require "models/topic"
diff --git a/activerecord/test/cases/serialized_attribute_test.rb b/activerecord/test/cases/serialized_attribute_test.rb
index e1bdaab5cf..7de5429cbb 100644
--- a/activerecord/test/cases/serialized_attribute_test.rb
+++ b/activerecord/test/cases/serialized_attribute_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "cases/helper"
require "models/topic"
require "models/reply"
@@ -277,7 +279,7 @@ class SerializedAttributeTest < ActiveRecord::TestCase
topic = Topic.new(content: nil)
- assert_not topic.content_changed?
+ assert_not_predicate topic, :content_changed?
end
def test_classes_without_no_arg_constructors_are_not_supported
@@ -347,7 +349,7 @@ class SerializedAttributeTest < ActiveRecord::TestCase
topic = model.create!(foo: "bar")
topic.foo
- refute topic.changed?
+ assert_not_predicate topic, :changed?
end
def test_serialized_attribute_works_under_concurrent_initial_access
diff --git a/activerecord/test/cases/statement_cache_test.rb b/activerecord/test/cases/statement_cache_test.rb
index fab3648564..ad6cd198e2 100644
--- a/activerecord/test/cases/statement_cache_test.rb
+++ b/activerecord/test/cases/statement_cache_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "cases/helper"
require "models/book"
require "models/liquid"
@@ -10,7 +12,6 @@ module ActiveRecord
@connection = ActiveRecord::Base.connection
end
- #Cache v 1.1 tests
def test_statement_cache
Book.create(name: "my book")
Book.create(name: "my other book")
@@ -19,9 +20,9 @@ module ActiveRecord
Book.where(name: params.bind)
end
- b = cache.execute([ "my book" ], Book, Book.connection)
+ b = cache.execute([ "my book" ], Book.connection)
assert_equal "my book", b[0].name
- b = cache.execute([ "my other book" ], Book, Book.connection)
+ b = cache.execute([ "my other book" ], Book.connection)
assert_equal "my other book", b[0].name
end
@@ -33,9 +34,9 @@ module ActiveRecord
Book.where(id: params.bind)
end
- b = cache.execute([ b1.id ], Book, Book.connection)
+ b = cache.execute([ b1.id ], Book.connection)
assert_equal b1.name, b[0].name
- b = cache.execute([ b2.id ], Book, Book.connection)
+ b = cache.execute([ b2.id ], Book.connection)
assert_equal b2.name, b[0].name
end
@@ -49,8 +50,6 @@ module ActiveRecord
assert_equal("my other book", b.name)
end
- #End
-
def test_statement_cache_with_simple_statement
cache = ActiveRecord::StatementCache.create(Book.connection) do |params|
Book.where(name: "my book").where("author_id > 3")
@@ -58,7 +57,7 @@ module ActiveRecord
Book.create(name: "my book", author_id: 4)
- books = cache.execute([], Book, Book.connection)
+ books = cache.execute([], Book.connection)
assert_equal "my book", books[0].name
end
@@ -71,7 +70,7 @@ module ActiveRecord
molecule = salty.molecules.create(name: "dioxane")
molecule.electrons.create(name: "lepton")
- liquids = cache.execute([], Book, Book.connection)
+ liquids = cache.execute([], Book.connection)
assert_equal "salty", liquids[0].name
end
@@ -84,13 +83,13 @@ module ActiveRecord
Book.create(name: "my book")
end
- first_books = cache.execute([], Book, Book.connection)
+ first_books = cache.execute([], Book.connection)
3.times do
Book.create(name: "my book")
end
- additional_books = cache.execute([], Book, Book.connection)
+ additional_books = cache.execute([], Book.connection)
assert first_books != additional_books
end
diff --git a/activerecord/test/cases/store_test.rb b/activerecord/test/cases/store_test.rb
index 633a8a0ebc..a30d13632a 100644
--- a/activerecord/test/cases/store_test.rb
+++ b/activerecord/test/cases/store_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "cases/helper"
require "models/admin"
require "models/admin/user"
@@ -43,7 +45,7 @@ class StoreTest < ActiveRecord::TestCase
test "updating the store will mark it as changed" do
@john.color = "red"
- assert @john.settings_changed?
+ assert_predicate @john, :settings_changed?
end
test "updating the store populates the changed array correctly" do
@@ -54,7 +56,7 @@ class StoreTest < ActiveRecord::TestCase
test "updating the store won't mark it as changed if an attribute isn't changed" do
@john.color = @john.color
- assert !@john.settings_changed?
+ assert_not_predicate @john, :settings_changed?
end
test "object initialization with not nullable column" do
@@ -135,7 +137,7 @@ class StoreTest < ActiveRecord::TestCase
test "updating the store will mark it as changed encoded with JSON" do
@john.height = "short"
- assert @john.json_data_changed?
+ assert_predicate @john, :json_data_changed?
end
test "object initialization with not nullable column encoded with JSON" do
diff --git a/activerecord/test/cases/suppressor_test.rb b/activerecord/test/cases/suppressor_test.rb
index a7d16b7cdb..b68f0033d9 100644
--- a/activerecord/test/cases/suppressor_test.rb
+++ b/activerecord/test/cases/suppressor_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "cases/helper"
require "models/notification"
require "models/user"
diff --git a/activerecord/test/cases/tasks/database_tasks_test.rb b/activerecord/test/cases/tasks/database_tasks_test.rb
index c47c97e9d9..21226352ff 100644
--- a/activerecord/test/cases/tasks/database_tasks_test.rb
+++ b/activerecord/test/cases/tasks/database_tasks_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "cases/helper"
require "active_record/tasks/database_tasks"
@@ -26,15 +28,32 @@ module ActiveRecord
class DatabaseTasksUtilsTask < ActiveRecord::TestCase
def test_raises_an_error_when_called_with_protected_environment
- ActiveRecord::Migrator.stubs(:current_version).returns(1)
+ ActiveRecord::MigrationContext.any_instance.stubs(:current_version).returns(1)
- protected_environments = ActiveRecord::Base.protected_environments.dup
- current_env = ActiveRecord::Migrator.current_environment
+ protected_environments = ActiveRecord::Base.protected_environments
+ current_env = ActiveRecord::Base.connection.migration_context.current_environment
assert_not_includes protected_environments, current_env
# Assert no error
ActiveRecord::Tasks::DatabaseTasks.check_protected_environments!
- ActiveRecord::Base.protected_environments << current_env
+ ActiveRecord::Base.protected_environments = [current_env]
+ assert_raise(ActiveRecord::ProtectedEnvironmentError) do
+ ActiveRecord::Tasks::DatabaseTasks.check_protected_environments!
+ end
+ ensure
+ ActiveRecord::Base.protected_environments = protected_environments
+ end
+
+ def test_raises_an_error_when_called_with_protected_environment_which_name_is_a_symbol
+ ActiveRecord::MigrationContext.any_instance.stubs(:current_version).returns(1)
+
+ protected_environments = ActiveRecord::Base.protected_environments
+ current_env = ActiveRecord::Base.connection.migration_context.current_environment
+ assert_not_includes protected_environments, current_env
+ # Assert no error
+ ActiveRecord::Tasks::DatabaseTasks.check_protected_environments!
+
+ ActiveRecord::Base.protected_environments = [current_env.to_sym]
assert_raise(ActiveRecord::ProtectedEnvironmentError) do
ActiveRecord::Tasks::DatabaseTasks.check_protected_environments!
end
@@ -44,7 +63,7 @@ module ActiveRecord
def test_raises_an_error_if_no_migrations_have_been_made
ActiveRecord::InternalMetadata.stubs(:table_exists?).returns(false)
- ActiveRecord::Migrator.stubs(:current_version).returns(1)
+ ActiveRecord::MigrationContext.any_instance.stubs(:current_version).returns(1)
assert_raise(ActiveRecord::NoEnvironmentInSchemaError) do
ActiveRecord::Tasks::DatabaseTasks.check_protected_environments!
@@ -91,6 +110,7 @@ module ActiveRecord
ActiveRecord::Tasks::DatabaseTasks.dump_schema_cache(ActiveRecord::Base.connection, path)
assert File.file?(path)
ensure
+ ActiveRecord::Base.clear_cache!
FileUtils.rm_rf(path)
end
end
@@ -327,53 +347,134 @@ module ActiveRecord
end
end
- class DatabaseTasksMigrateTest < ActiveRecord::TestCase
- self.use_transactional_tests = false
+ if current_adapter?(:SQLite3Adapter) && !in_memory_db?
+ class DatabaseTasksMigrateTest < ActiveRecord::TestCase
+ self.use_transactional_tests = false
+
+ # Use a memory db here to avoid having to rollback at the end
+ setup do
+ migrations_path = MIGRATIONS_ROOT + "/valid"
+ file = ActiveRecord::Base.connection.raw_connection.filename
+ @conn = ActiveRecord::Base.establish_connection adapter: "sqlite3",
+ database: ":memory:", migrations_paths: migrations_path
+ source_db = SQLite3::Database.new file
+ dest_db = ActiveRecord::Base.connection.raw_connection
+ backup = SQLite3::Backup.new(dest_db, "main", source_db, "main")
+ backup.step(-1)
+ backup.finish
+ end
- def setup
- ActiveRecord::Tasks::DatabaseTasks.migrations_paths = "custom/path"
- end
+ teardown do
+ @conn.release_connection if @conn
+ ActiveRecord::Base.establish_connection :arunit
+ end
- def teardown
- ActiveRecord::Tasks::DatabaseTasks.migrations_paths = nil
- end
+ def test_migrate_set_and_unset_verbose_and_version_env_vars
+ verbose, version = ENV["VERBOSE"], ENV["VERSION"]
+ ENV["VERSION"] = "2"
+ ENV["VERBOSE"] = "false"
- def test_migrate_receives_correct_env_vars
- verbose, version = ENV["VERBOSE"], ENV["VERSION"]
+ # run down migration because it was already run on copied db
+ assert_empty capture_migration_output
- ENV["VERBOSE"] = "false"
- ENV["VERSION"] = "4"
- ActiveRecord::Migrator.expects(:migrate).with("custom/path", 4)
- ActiveRecord::Migration.expects(:verbose=).with(false)
- ActiveRecord::Migration.expects(:verbose=).with(ActiveRecord::Migration.verbose)
- ActiveRecord::Tasks::DatabaseTasks.migrate
+ ENV.delete("VERSION")
+ ENV.delete("VERBOSE")
- ENV.delete("VERBOSE")
- ENV.delete("VERSION")
- ActiveRecord::Migrator.expects(:migrate).with("custom/path", nil)
- ActiveRecord::Migration.expects(:verbose=).with(true)
- ActiveRecord::Migration.expects(:verbose=).with(ActiveRecord::Migration.verbose)
- ActiveRecord::Tasks::DatabaseTasks.migrate
+ # re-run up migration
+ assert_includes capture_migration_output, "migrating"
+ ensure
+ ENV["VERBOSE"], ENV["VERSION"] = verbose, version
+ end
- ENV["VERBOSE"] = "yes"
- ENV["VERSION"] = "unknown"
- ActiveRecord::Migrator.expects(:migrate).with("custom/path", 0)
- ActiveRecord::Migration.expects(:verbose=).with(true)
- ActiveRecord::Migration.expects(:verbose=).with(ActiveRecord::Migration.verbose)
- ActiveRecord::Tasks::DatabaseTasks.migrate
- ensure
- ENV["VERBOSE"], ENV["VERSION"] = verbose, version
+ def test_migrate_set_and_unset_empty_values_for_verbose_and_version_env_vars
+ verbose, version = ENV["VERBOSE"], ENV["VERSION"]
+
+ ENV["VERSION"] = "2"
+ ENV["VERBOSE"] = "false"
+
+ # run down migration because it was already run on copied db
+ assert_empty capture_migration_output
+
+ ENV["VERBOSE"] = ""
+ ENV["VERSION"] = ""
+
+ # re-run up migration
+ assert_includes capture_migration_output, "migrating"
+ ensure
+ ENV["VERBOSE"], ENV["VERSION"] = verbose, version
+ end
+
+ def test_migrate_set_and_unset_nonsense_values_for_verbose_and_version_env_vars
+ verbose, version = ENV["VERBOSE"], ENV["VERSION"]
+
+ # run down migration because it was already run on copied db
+ ENV["VERSION"] = "2"
+ ENV["VERBOSE"] = "false"
+
+ assert_empty capture_migration_output
+
+ ENV["VERBOSE"] = "yes"
+ ENV["VERSION"] = "2"
+
+ # run no migration because 2 was already run
+ assert_empty capture_migration_output
+ ensure
+ ENV["VERBOSE"], ENV["VERSION"] = verbose, version
+ end
+
+ private
+ def capture_migration_output
+ capture(:stdout) do
+ ActiveRecord::Tasks::DatabaseTasks.migrate
+ end
+ end
end
+ end
- def test_migrate_raise_error_on_empty_version
+ class DatabaseTasksMigrateErrorTest < ActiveRecord::TestCase
+ self.use_transactional_tests = false
+
+ def test_migrate_raise_error_on_invalid_version_format
version = ENV["VERSION"]
- ENV["VERSION"] = ""
+
+ ENV["VERSION"] = "unknown"
+ e = assert_raise(RuntimeError) { ActiveRecord::Tasks::DatabaseTasks.migrate }
+ assert_match(/Invalid format of target version/, e.message)
+
+ ENV["VERSION"] = "0.1.11"
+ e = assert_raise(RuntimeError) { ActiveRecord::Tasks::DatabaseTasks.migrate }
+ assert_match(/Invalid format of target version/, e.message)
+
+ ENV["VERSION"] = "1.1.11"
+ e = assert_raise(RuntimeError) { ActiveRecord::Tasks::DatabaseTasks.migrate }
+ assert_match(/Invalid format of target version/, e.message)
+
+ ENV["VERSION"] = "0 "
e = assert_raise(RuntimeError) { ActiveRecord::Tasks::DatabaseTasks.migrate }
- assert_equal "Empty VERSION provided", e.message
+ assert_match(/Invalid format of target version/, e.message)
+
+ ENV["VERSION"] = "1."
+ e = assert_raise(RuntimeError) { ActiveRecord::Tasks::DatabaseTasks.migrate }
+ assert_match(/Invalid format of target version/, e.message)
+
+ ENV["VERSION"] = "1_"
+ e = assert_raise(RuntimeError) { ActiveRecord::Tasks::DatabaseTasks.migrate }
+ assert_match(/Invalid format of target version/, e.message)
+
+ ENV["VERSION"] = "1_name"
+ e = assert_raise(RuntimeError) { ActiveRecord::Tasks::DatabaseTasks.migrate }
+ assert_match(/Invalid format of target version/, e.message)
ensure
ENV["VERSION"] = version
end
+ def test_migrate_raise_error_on_failed_check_target_version
+ ActiveRecord::Tasks::DatabaseTasks.stubs(:check_target_version).raises("foo")
+
+ e = assert_raise(RuntimeError) { ActiveRecord::Tasks::DatabaseTasks.migrate }
+ assert_equal "foo", e.message
+ end
+
def test_migrate_clears_schema_cache_afterward
ActiveRecord::Base.expects(:clear_cache!)
ActiveRecord::Tasks::DatabaseTasks.migrate
@@ -442,6 +543,108 @@ module ActiveRecord
end
end
+ class DatabaseTaskTargetVersionTest < ActiveRecord::TestCase
+ def test_target_version_returns_nil_if_version_does_not_exist
+ version = ENV.delete("VERSION")
+ assert_nil ActiveRecord::Tasks::DatabaseTasks.target_version
+ ensure
+ ENV["VERSION"] = version
+ end
+
+ def test_target_version_returns_nil_if_version_is_empty
+ version = ENV["VERSION"]
+
+ ENV["VERSION"] = ""
+ assert_nil ActiveRecord::Tasks::DatabaseTasks.target_version
+ ensure
+ ENV["VERSION"] = version
+ end
+
+ def test_target_version_returns_converted_to_integer_env_version_if_version_exists
+ version = ENV["VERSION"]
+
+ ENV["VERSION"] = "0"
+ assert_equal ENV["VERSION"].to_i, ActiveRecord::Tasks::DatabaseTasks.target_version
+
+ ENV["VERSION"] = "42"
+ assert_equal ENV["VERSION"].to_i, ActiveRecord::Tasks::DatabaseTasks.target_version
+
+ ENV["VERSION"] = "042"
+ assert_equal ENV["VERSION"].to_i, ActiveRecord::Tasks::DatabaseTasks.target_version
+ ensure
+ ENV["VERSION"] = version
+ end
+ end
+
+ class DatabaseTaskCheckTargetVersionTest < ActiveRecord::TestCase
+ def test_check_target_version_does_not_raise_error_on_empty_version
+ version = ENV["VERSION"]
+ ENV["VERSION"] = ""
+ assert_nothing_raised { ActiveRecord::Tasks::DatabaseTasks.check_target_version }
+ ensure
+ ENV["VERSION"] = version
+ end
+
+ def test_check_target_version_does_not_raise_error_if_version_is_not_setted
+ version = ENV.delete("VERSION")
+ assert_nothing_raised { ActiveRecord::Tasks::DatabaseTasks.check_target_version }
+ ensure
+ ENV["VERSION"] = version
+ end
+
+ def test_check_target_version_raises_error_on_invalid_version_format
+ version = ENV["VERSION"]
+
+ ENV["VERSION"] = "unknown"
+ e = assert_raise(RuntimeError) { ActiveRecord::Tasks::DatabaseTasks.check_target_version }
+ assert_match(/Invalid format of target version/, e.message)
+
+ ENV["VERSION"] = "0.1.11"
+ e = assert_raise(RuntimeError) { ActiveRecord::Tasks::DatabaseTasks.check_target_version }
+ assert_match(/Invalid format of target version/, e.message)
+
+ ENV["VERSION"] = "1.1.11"
+ e = assert_raise(RuntimeError) { ActiveRecord::Tasks::DatabaseTasks.check_target_version }
+ assert_match(/Invalid format of target version/, e.message)
+
+ ENV["VERSION"] = "0 "
+ e = assert_raise(RuntimeError) { ActiveRecord::Tasks::DatabaseTasks.check_target_version }
+ assert_match(/Invalid format of target version/, e.message)
+
+ ENV["VERSION"] = "1."
+ e = assert_raise(RuntimeError) { ActiveRecord::Tasks::DatabaseTasks.check_target_version }
+ assert_match(/Invalid format of target version/, e.message)
+
+ ENV["VERSION"] = "1_"
+ e = assert_raise(RuntimeError) { ActiveRecord::Tasks::DatabaseTasks.check_target_version }
+ assert_match(/Invalid format of target version/, e.message)
+
+ ENV["VERSION"] = "1_name"
+ e = assert_raise(RuntimeError) { ActiveRecord::Tasks::DatabaseTasks.check_target_version }
+ assert_match(/Invalid format of target version/, e.message)
+ ensure
+ ENV["VERSION"] = version
+ end
+
+ def test_check_target_version_does_not_raise_error_on_valid_version_format
+ version = ENV["VERSION"]
+
+ ENV["VERSION"] = "0"
+ assert_nothing_raised { ActiveRecord::Tasks::DatabaseTasks.check_target_version }
+
+ ENV["VERSION"] = "1"
+ assert_nothing_raised { ActiveRecord::Tasks::DatabaseTasks.check_target_version }
+
+ ENV["VERSION"] = "001"
+ assert_nothing_raised { ActiveRecord::Tasks::DatabaseTasks.check_target_version }
+
+ ENV["VERSION"] = "001_name.rb"
+ assert_nothing_raised { ActiveRecord::Tasks::DatabaseTasks.check_target_version }
+ ensure
+ ENV["VERSION"] = version
+ end
+ end
+
class DatabaseTasksStructureDumpTest < ActiveRecord::TestCase
include DatabaseTasksSetupper
diff --git a/activerecord/test/cases/tasks/mysql_rake_test.rb b/activerecord/test/cases/tasks/mysql_rake_test.rb
index c22d974536..047153e7cc 100644
--- a/activerecord/test/cases/tasks/mysql_rake_test.rb
+++ b/activerecord/test/cases/tasks/mysql_rake_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "cases/helper"
require "active_record/tasks/database_tasks"
@@ -73,7 +75,7 @@ if current_adapter?(:Mysql2Adapter)
end
end
- class MysqlDBCreateAsRootTest < ActiveRecord::TestCase
+ class MysqlDBCreateWithInvalidPermissionsTest < ActiveRecord::TestCase
def setup
@connection = stub("Connection", create_database: true)
@error = Mysql2::Error.new("Invalid permissions")
@@ -84,13 +86,8 @@ if current_adapter?(:Mysql2Adapter)
"password" => "wossname"
}
- $stdin.stubs(:gets).returns("secret\n")
- $stdout.stubs(:print).returns(nil)
- @error.stubs(:errno).returns(1045)
ActiveRecord::Base.stubs(:connection).returns(@connection)
- ActiveRecord::Base.stubs(:establish_connection).
- raises(@error).
- then.returns(true)
+ ActiveRecord::Base.stubs(:establish_connection).raises(@error)
$stdout, @original_stdout = StringIO.new, $stdout
$stderr, @original_stderr = StringIO.new, $stderr
@@ -100,75 +97,11 @@ if current_adapter?(:Mysql2Adapter)
$stdout, $stderr = @original_stdout, @original_stderr
end
- def test_root_password_is_requested
- assert_permissions_granted_for("pat")
- $stdin.expects(:gets).returns("secret\n")
-
- ActiveRecord::Tasks::DatabaseTasks.create @configuration
- end
-
- def test_connection_established_as_root
- assert_permissions_granted_for("pat")
- ActiveRecord::Base.expects(:establish_connection).with(
- "adapter" => "mysql2",
- "database" => nil,
- "username" => "root",
- "password" => "secret"
- )
-
- ActiveRecord::Tasks::DatabaseTasks.create @configuration
- end
-
- def test_database_created_by_root
- assert_permissions_granted_for("pat")
- @connection.expects(:create_database).
- with("my-app-db", {})
-
- ActiveRecord::Tasks::DatabaseTasks.create @configuration
- end
-
- def test_grant_privileges_for_normal_user
- assert_permissions_granted_for("pat")
- ActiveRecord::Tasks::DatabaseTasks.create @configuration
- end
-
- def test_do_not_grant_privileges_for_root_user
- @configuration["username"] = "root"
- @configuration["password"] = ""
- ActiveRecord::Tasks::DatabaseTasks.create @configuration
- end
-
- def test_connection_established_as_normal_user
- assert_permissions_granted_for("pat")
- ActiveRecord::Base.expects(:establish_connection).returns do
- ActiveRecord::Base.expects(:establish_connection).with(
- "adapter" => "mysql2",
- "database" => "my-app-db",
- "username" => "pat",
- "password" => "secret"
- )
-
- raise @error
+ def test_raises_error
+ assert_raises(Mysql2::Error) do
+ ActiveRecord::Tasks::DatabaseTasks.create @configuration
end
-
- ActiveRecord::Tasks::DatabaseTasks.create @configuration
end
-
- def test_sends_output_to_stderr_when_other_errors
- @error.stubs(:errno).returns(42)
-
- $stderr.expects(:puts).at_least_once.returns(nil)
-
- ActiveRecord::Tasks::DatabaseTasks.create @configuration
- end
-
- private
-
- def assert_permissions_granted_for(db_user)
- db_name = @configuration["database"]
- db_password = @configuration["password"]
- @connection.expects(:execute).with("GRANT ALL PRIVILEGES ON `#{db_name}`.* TO '#{db_user}'@'localhost' IDENTIFIED BY '#{db_password}' WITH GRANT OPTION;")
- end
end
class MySQLDBDropTest < ActiveRecord::TestCase
@@ -296,7 +229,7 @@ if current_adapter?(:Mysql2Adapter)
def test_structure_dump_with_extra_flags
filename = "awesome-file.sql"
- expected_command = ["mysqldump", "--result-file", filename, "--no-data", "--routines", "--skip-comments", "--noop", "test-db"]
+ expected_command = ["mysqldump", "--noop", "--result-file", filename, "--no-data", "--routines", "--skip-comments", "test-db"]
assert_called_with(Kernel, :system, expected_command, returns: true) do
with_structure_dump_flags(["--noop"]) do
@@ -364,7 +297,7 @@ if current_adapter?(:Mysql2Adapter)
def test_structure_load
filename = "awesome-file.sql"
- expected_command = ["mysql", "--execute", %{SET FOREIGN_KEY_CHECKS = 0; SOURCE #{filename}; SET FOREIGN_KEY_CHECKS = 1}, "--database", "test-db", "--noop"]
+ expected_command = ["mysql", "--noop", "--execute", %{SET FOREIGN_KEY_CHECKS = 0; SOURCE #{filename}; SET FOREIGN_KEY_CHECKS = 1}, "--database", "test-db"]
assert_called_with(Kernel, :system, expected_command, returns: true) do
with_structure_load_flags(["--noop"]) do
diff --git a/activerecord/test/cases/tasks/postgresql_rake_test.rb b/activerecord/test/cases/tasks/postgresql_rake_test.rb
index a2e968aedf..ca1defa332 100644
--- a/activerecord/test/cases/tasks/postgresql_rake_test.rb
+++ b/activerecord/test/cases/tasks/postgresql_rake_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "cases/helper"
require "active_record/tasks/database_tasks"
@@ -227,7 +229,6 @@ if current_adapter?(:PostgreSQLAdapter)
ActiveRecord::Base.stubs(:connection).returns(@connection)
ActiveRecord::Base.stubs(:establish_connection).returns(true)
- Kernel.stubs(:system)
end
def teardown
@@ -293,6 +294,16 @@ if current_adapter?(:PostgreSQLAdapter)
end
end
+ def test_structure_dump_execution_fails
+ filename = "awesome-file.sql"
+ Kernel.expects(:system).with("pg_dump", "-s", "-x", "-O", "-f", filename, "my-app-db").returns(nil)
+
+ e = assert_raise(RuntimeError) do
+ ActiveRecord::Tasks::DatabaseTasks.structure_dump(@configuration, filename)
+ end
+ assert_match("failed to execute:", e.message)
+ end
+
private
def with_dump_schemas(value, &block)
old_dump_schemas = ActiveRecord::Base.dump_schemas
@@ -321,7 +332,6 @@ if current_adapter?(:PostgreSQLAdapter)
ActiveRecord::Base.stubs(:connection).returns(@connection)
ActiveRecord::Base.stubs(:establish_connection).returns(true)
- Kernel.stubs(:system)
end
def test_structure_load
diff --git a/activerecord/test/cases/tasks/sqlite_rake_test.rb b/activerecord/test/cases/tasks/sqlite_rake_test.rb
index ccb3834fee..d7e3caa2ff 100644
--- a/activerecord/test/cases/tasks/sqlite_rake_test.rb
+++ b/activerecord/test/cases/tasks/sqlite_rake_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "cases/helper"
require "active_record/tasks/database_tasks"
require "pathname"
@@ -213,6 +215,31 @@ if current_adapter?(:SQLite3Adapter)
FileUtils.rm_f(filename)
FileUtils.rm_f(dbfile)
end
+
+ def test_structure_dump_execution_fails
+ dbfile = @database
+ filename = "awesome-file.sql"
+ Kernel.expects(:system).with("sqlite3", "--noop", "db_create.sqlite3", ".schema", out: "awesome-file.sql").returns(nil)
+
+ e = assert_raise(RuntimeError) do
+ with_structure_dump_flags(["--noop"]) do
+ quietly { ActiveRecord::Tasks::DatabaseTasks.structure_dump(@configuration, filename, "/rails/root") }
+ end
+ end
+ assert_match("failed to execute:", e.message)
+ ensure
+ FileUtils.rm_f(filename)
+ FileUtils.rm_f(dbfile)
+ end
+
+ private
+ def with_structure_dump_flags(flags)
+ old = ActiveRecord::Tasks::DatabaseTasks.structure_dump_flags
+ ActiveRecord::Tasks::DatabaseTasks.structure_dump_flags = flags
+ yield
+ ensure
+ ActiveRecord::Tasks::DatabaseTasks.structure_dump_flags = old
+ end
end
class SqliteStructureLoadTest < ActiveRecord::TestCase
diff --git a/activerecord/test/cases/test_case.rb b/activerecord/test/cases/test_case.rb
index 9f594fef85..024b5bd8a1 100644
--- a/activerecord/test/cases/test_case.rb
+++ b/activerecord/test/cases/test_case.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "active_support/test_case"
require "active_support/testing/autorun"
require "active_support/testing/method_call_assertions"
@@ -76,12 +78,8 @@ module ActiveRecord
model.column_names.include?(column_name.to_s)
end
- def bind_param
- Arel::Nodes::BindParam.new
- end
-
- def bind_attribute(name, value, type = ActiveRecord::Type.default_value)
- ActiveRecord::Relation::QueryAttribute.new(name, value, type)
+ def frozen_error_class
+ Object.const_defined?(:FrozenError) ? FrozenError : RuntimeError
end
end
@@ -116,9 +114,9 @@ module ActiveRecord
# FIXME: this needs to be refactored so specific database can add their own
# ignored SQL, or better yet, use a different notification for the queries
# instead examining the SQL content.
- oracle_ignored = [/^select .*nextval/i, /^SAVEPOINT/, /^ROLLBACK TO/, /^\s*select .* from all_triggers/im, /^\s*select .* from all_constraints/im, /^\s*select .* from all_tab_cols/im]
+ oracle_ignored = [/^select .*nextval/i, /^SAVEPOINT/, /^ROLLBACK TO/, /^\s*select .* from all_triggers/im, /^\s*select .* from all_constraints/im, /^\s*select .* from all_tab_cols/im, /^\s*select .* from all_sequences/im]
mysql_ignored = [/^SHOW FULL TABLES/i, /^SHOW FULL FIELDS/, /^SHOW CREATE TABLE /i, /^SHOW VARIABLES /, /^\s*SELECT (?:column_name|table_name)\b.*\bFROM information_schema\.(?:key_column_usage|tables)\b/im]
- postgresql_ignored = [/^\s*select\b.*\bfrom\b.*pg_namespace\b/im, /^\s*select tablename\b.*from pg_tables\b/im, /^\s*select\b.*\battname\b.*\bfrom\b.*\bpg_attribute\b/im, /^SHOW search_path/i]
+ postgresql_ignored = [/^\s*select\b.*\bfrom\b.*pg_namespace\b/im, /^\s*select tablename\b.*from pg_tables\b/im, /^\s*select\b.*\battname\b.*\bfrom\b.*\bpg_attribute\b/im, /^SHOW search_path/i, /^\s*SELECT\b.*::regtype::oid\b/im]
sqlite3_ignored = [/^\s*SELECT name\b.*\bFROM sqlite_master/im, /^\s*SELECT sql\b.*\bFROM sqlite_master/im]
[oracle_ignored, mysql_ignored, postgresql_ignored, sqlite3_ignored].each do |db_ignored_sql|
diff --git a/activerecord/test/cases/test_fixtures_test.rb b/activerecord/test/cases/test_fixtures_test.rb
index 58d3bea3a2..4411410eda 100644
--- a/activerecord/test/cases/test_fixtures_test.rb
+++ b/activerecord/test/cases/test_fixtures_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "cases/helper"
class TestFixturesTest < ActiveRecord::TestCase
diff --git a/activerecord/test/cases/time_precision_test.rb b/activerecord/test/cases/time_precision_test.rb
index 09c585167e..41455637bb 100644
--- a/activerecord/test/cases/time_precision_test.rb
+++ b/activerecord/test/cases/time_precision_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "cases/helper"
require "support/schema_dumping_helper"
diff --git a/activerecord/test/cases/timestamp_test.rb b/activerecord/test/cases/timestamp_test.rb
index 39b40e3411..e95446c0a7 100644
--- a/activerecord/test/cases/timestamp_test.rb
+++ b/activerecord/test/cases/timestamp_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "cases/helper"
require "support/ddl_helper"
require "models/developer"
@@ -88,7 +90,7 @@ class TimestampTest < ActiveRecord::TestCase
@developer.touch(:created_at)
end
- assert !@developer.created_at_changed? , "created_at should not be changed"
+ assert !@developer.created_at_changed?, "created_at should not be changed"
assert !@developer.changed?, "record should not be changed"
assert_not_equal previously_created_at, @developer.created_at
assert_not_equal @previously_updated_at, @developer.updated_at
@@ -137,13 +139,13 @@ class TimestampTest < ActiveRecord::TestCase
def test_touching_a_no_touching_object
Developer.no_touching do
- assert @developer.no_touching?
- assert !@owner.no_touching?
+ assert_predicate @developer, :no_touching?
+ assert_not_predicate @owner, :no_touching?
@developer.touch
end
- assert !@developer.no_touching?
- assert !@owner.no_touching?
+ assert_not_predicate @developer, :no_touching?
+ assert_not_predicate @owner, :no_touching?
assert_equal @previously_updated_at, @developer.updated_at
end
@@ -160,26 +162,26 @@ class TimestampTest < ActiveRecord::TestCase
def test_global_no_touching
ActiveRecord::Base.no_touching do
- assert @developer.no_touching?
- assert @owner.no_touching?
+ assert_predicate @developer, :no_touching?
+ assert_predicate @owner, :no_touching?
@developer.touch
end
- assert !@developer.no_touching?
- assert !@owner.no_touching?
+ assert_not_predicate @developer, :no_touching?
+ assert_not_predicate @owner, :no_touching?
assert_equal @previously_updated_at, @developer.updated_at
end
def test_no_touching_threadsafe
Thread.new do
Developer.no_touching do
- assert @developer.no_touching?
+ assert_predicate @developer, :no_touching?
sleep(1)
end
end
- assert !@developer.no_touching?
+ assert_not_predicate @developer, :no_touching?
end
def test_no_touching_with_callbacks
@@ -235,7 +237,7 @@ class TimestampTest < ActiveRecord::TestCase
pet = Pet.new(owner: klass.new)
pet.save!
- assert pet.owner.new_record?
+ assert_predicate pet.owner, :new_record?
end
def test_saving_a_record_with_a_belongs_to_that_specifies_touching_a_specific_attribute_the_parent_should_update_that_attribute
@@ -444,17 +446,6 @@ class TimestampTest < ActiveRecord::TestCase
toy = Toy.first
assert_equal ["created_at", "updated_at"], toy.send(:all_timestamp_attributes_in_model)
end
-
- def test_index_is_created_for_both_timestamps
- ActiveRecord::Base.connection.create_table(:foos, force: true) do |t|
- t.timestamps null: true, index: true
- end
-
- indexes = ActiveRecord::Base.connection.indexes("foos")
- assert_equal ["created_at", "updated_at"], indexes.flat_map(&:columns).sort
- ensure
- ActiveRecord::Base.connection.drop_table(:foos)
- end
end
class TimestampsWithoutTransactionTest < ActiveRecord::TestCase
@@ -473,4 +464,15 @@ class TimestampsWithoutTransactionTest < ActiveRecord::TestCase
assert_nil post.updated_at
end
end
+
+ def test_index_is_created_for_both_timestamps
+ ActiveRecord::Base.connection.create_table(:foos, force: true) do |t|
+ t.timestamps null: true, index: true
+ end
+
+ indexes = ActiveRecord::Base.connection.indexes("foos")
+ assert_equal ["created_at", "updated_at"], indexes.flat_map(&:columns).sort
+ ensure
+ ActiveRecord::Base.connection.drop_table(:foos)
+ end
end
diff --git a/activerecord/test/cases/touch_later_test.rb b/activerecord/test/cases/touch_later_test.rb
index d1e8c649d9..925a4609a2 100644
--- a/activerecord/test/cases/touch_later_test.rb
+++ b/activerecord/test/cases/touch_later_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "cases/helper"
require "models/invoice"
require "models/line_item"
@@ -11,7 +13,7 @@ class TouchLaterTest < ActiveRecord::TestCase
def test_touch_laster_raise_if_non_persisted
invoice = Invoice.new
Invoice.transaction do
- assert_not invoice.persisted?
+ assert_not_predicate invoice, :persisted?
assert_raises(ActiveRecord::ActiveRecordError) do
invoice.touch_later
end
@@ -21,7 +23,7 @@ class TouchLaterTest < ActiveRecord::TestCase
def test_touch_later_dont_set_dirty_attributes
invoice = Invoice.create!
invoice.touch_later
- assert_not invoice.changed?
+ assert_not_predicate invoice, :changed?
end
def test_touch_later_respects_no_touching_policy
diff --git a/activerecord/test/cases/transaction_callbacks_test.rb b/activerecord/test/cases/transaction_callbacks_test.rb
index eaa4dd09a9..1c7cec4dad 100644
--- a/activerecord/test/cases/transaction_callbacks_test.rb
+++ b/activerecord/test/cases/transaction_callbacks_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "cases/helper"
require "models/owner"
require "models/pet"
@@ -156,13 +158,13 @@ class TransactionCallbacksTest < ActiveRecord::TestCase
def test_only_call_after_commit_on_top_level_transactions
@first.after_commit_block { |r| r.history << :after_commit }
- assert @first.history.empty?
+ assert_empty @first.history
@first.transaction do
@first.transaction(requires_new: true) do
@first.touch
end
- assert @first.history.empty?
+ assert_empty @first.history
end
assert_equal [:after_commit], @first.history
end
@@ -516,7 +518,7 @@ class TransactionEnrollmentCallbacksTest < ActiveRecord::TestCase
@topic.content = "foo"
@topic.save!
end
- assert @topic.history.empty?
+ assert_empty @topic.history
end
def test_commit_run_transactions_callbacks_with_explicit_enrollment
@@ -536,7 +538,7 @@ class TransactionEnrollmentCallbacksTest < ActiveRecord::TestCase
@topic.save!
raise ActiveRecord::Rollback
end
- assert @topic.history.empty?
+ assert_empty @topic.history
end
def test_rollback_run_transactions_callbacks_with_explicit_enrollment
diff --git a/activerecord/test/cases/transaction_isolation_test.rb b/activerecord/test/cases/transaction_isolation_test.rb
index 58abfadaf4..eaafd13360 100644
--- a/activerecord/test/cases/transaction_isolation_test.rb
+++ b/activerecord/test/cases/transaction_isolation_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "cases/helper"
unless ActiveRecord::Base.connection.supports_transaction_isolation?
@@ -13,9 +15,7 @@ unless ActiveRecord::Base.connection.supports_transaction_isolation?
end
end
end
-end
-
-if ActiveRecord::Base.connection.supports_transaction_isolation?
+else
class TransactionIsolationTest < ActiveRecord::TestCase
self.use_transactional_tests = false
diff --git a/activerecord/test/cases/transactions_test.rb b/activerecord/test/cases/transactions_test.rb
index 79ba306ef5..c70286d52a 100644
--- a/activerecord/test/cases/transactions_test.rb
+++ b/activerecord/test/cases/transactions_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "cases/helper"
require "models/topic"
require "models/reply"
@@ -18,22 +20,22 @@ class TransactionTest < ActiveRecord::TestCase
def test_persisted_in_a_model_with_custom_primary_key_after_failed_save
movie = Movie.create
- assert !movie.persisted?
+ assert_not_predicate movie, :persisted?
end
def test_raise_after_destroy
- assert_not @first.frozen?
+ assert_not_predicate @first, :frozen?
assert_raises(RuntimeError) {
Topic.transaction do
@first.destroy
- assert @first.frozen?
+ assert_predicate @first, :frozen?
raise
end
}
assert @first.reload
- assert_not @first.frozen?
+ assert_not_predicate @first, :frozen?
end
def test_successful
@@ -150,7 +152,7 @@ class TransactionTest < ActiveRecord::TestCase
@first.approved = true
e = assert_raises(RuntimeError) { @first.save }
assert_equal "Make the transaction rollback", e.message
- assert !Topic.find(1).approved?
+ assert_not_predicate Topic.find(1), :approved?
end
def test_rolling_back_in_a_callback_rollbacks_before_save
@@ -184,7 +186,7 @@ class TransactionTest < ActiveRecord::TestCase
author = Author.create! name: "foo"
author.name = nil
assert_not author.save
- assert_not author.new_record?
+ assert_not_predicate author, :new_record?
end
def test_update_should_rollback_on_failure
@@ -304,6 +306,76 @@ class TransactionTest < ActiveRecord::TestCase
assert !Topic.find(2).approved?, "Second should have been unapproved"
end
+ def test_nested_transaction_with_new_transaction_applies_parent_state_on_rollback
+ topic_one = Topic.new(title: "A new topic")
+ topic_two = Topic.new(title: "Another new topic")
+
+ Topic.transaction do
+ topic_one.save
+
+ Topic.transaction(requires_new: true) do
+ topic_two.save
+
+ assert_predicate topic_one, :persisted?
+ assert_predicate topic_two, :persisted?
+ end
+
+ raise ActiveRecord::Rollback
+ end
+
+ refute_predicate topic_one, :persisted?
+ refute_predicate topic_two, :persisted?
+ end
+
+ def test_nested_transaction_without_new_transaction_applies_parent_state_on_rollback
+ topic_one = Topic.new(title: "A new topic")
+ topic_two = Topic.new(title: "Another new topic")
+
+ Topic.transaction do
+ topic_one.save
+
+ Topic.transaction do
+ topic_two.save
+
+ assert_predicate topic_one, :persisted?
+ assert_predicate topic_two, :persisted?
+ end
+
+ raise ActiveRecord::Rollback
+ end
+
+ refute_predicate topic_one, :persisted?
+ refute_predicate topic_two, :persisted?
+ end
+
+ def test_double_nested_transaction_applies_parent_state_on_rollback
+ topic_one = Topic.new(title: "A new topic")
+ topic_two = Topic.new(title: "Another new topic")
+ topic_three = Topic.new(title: "Another new topic of course")
+
+ Topic.transaction do
+ topic_one.save
+
+ Topic.transaction do
+ topic_two.save
+
+ Topic.transaction do
+ topic_three.save
+ end
+ end
+
+ assert_predicate topic_one, :persisted?
+ assert_predicate topic_two, :persisted?
+ assert_predicate topic_three, :persisted?
+
+ raise ActiveRecord::Rollback
+ end
+
+ refute_predicate topic_one, :persisted?
+ refute_predicate topic_two, :persisted?
+ refute_predicate topic_three, :persisted?
+ end
+
def test_manually_rolling_back_a_transaction
Topic.transaction do
@first.approved = true
@@ -345,8 +417,8 @@ class TransactionTest < ActiveRecord::TestCase
end
end
- assert @first.reload.approved?
- assert !@second.reload.approved?
+ assert_predicate @first.reload, :approved?
+ assert_not_predicate @second.reload, :approved?
end if Topic.connection.supports_savepoints?
def test_force_savepoint_on_instance
@@ -366,8 +438,8 @@ class TransactionTest < ActiveRecord::TestCase
end
end
- assert @first.reload.approved?
- assert !@second.reload.approved?
+ assert_predicate @first.reload, :approved?
+ assert_not_predicate @second.reload, :approved?
end if Topic.connection.supports_savepoints?
def test_no_savepoint_in_nested_transaction_without_force
@@ -387,8 +459,8 @@ class TransactionTest < ActiveRecord::TestCase
end
end
- assert !@first.reload.approved?
- assert !@second.reload.approved?
+ assert_not_predicate @first.reload, :approved?
+ assert_not_predicate @second.reload, :approved?
end if Topic.connection.supports_savepoints?
def test_many_savepoints
@@ -444,12 +516,12 @@ class TransactionTest < ActiveRecord::TestCase
@first.approved = false
@first.save!
Topic.connection.rollback_to_savepoint("first")
- assert @first.reload.approved?
+ assert_predicate @first.reload, :approved?
@first.approved = false
@first.save!
Topic.connection.release_savepoint("first")
- assert_not @first.reload.approved?
+ assert_not_predicate @first.reload, :approved?
end
end if Topic.connection.supports_savepoints?
@@ -504,7 +576,7 @@ class TransactionTest < ActiveRecord::TestCase
def test_rollback_when_saving_a_frozen_record
topic = Topic.new(title: "test")
topic.freeze
- e = assert_raise(RuntimeError) { topic.save }
+ e = assert_raise(frozen_error_class) { topic.save }
# Not good enough, but we can't do much
# about it since there is no specific error
# for frozen objects.
@@ -591,8 +663,8 @@ class TransactionTest < ActiveRecord::TestCase
raise ActiveRecord::Rollback
end
- assert_not reply.frozen?
- assert_not topic.frozen?
+ assert_not_predicate reply, :frozen?
+ assert_not_predicate topic, :frozen?
end
def test_restore_id_after_rollback
@@ -614,7 +686,7 @@ class TransactionTest < ActiveRecord::TestCase
raise ActiveRecord::Rollback
end
- assert_nil movie.id
+ assert_nil movie.movieid
end
def test_assign_id_after_rollback
@@ -637,8 +709,54 @@ class TransactionTest < ActiveRecord::TestCase
raise ActiveRecord::Rollback
end
- movie.id = nil
- assert_nil movie.id
+ movie.movieid = nil
+ assert_nil movie.movieid
+ end
+
+ def test_read_attribute_after_rollback
+ topic = Topic.new
+
+ Topic.transaction do
+ topic.save!
+ raise ActiveRecord::Rollback
+ end
+
+ assert_nil topic.read_attribute(:id)
+ end
+
+ def test_read_attribute_with_custom_primary_key_after_rollback
+ movie = Movie.new(name: "foo")
+
+ Movie.transaction do
+ movie.save!
+ raise ActiveRecord::Rollback
+ end
+
+ assert_nil movie.read_attribute(:movieid)
+ end
+
+ def test_write_attribute_after_rollback
+ topic = Topic.create!
+
+ Topic.transaction do
+ topic.save!
+ raise ActiveRecord::Rollback
+ end
+
+ topic.write_attribute(:id, nil)
+ assert_nil topic.id
+ end
+
+ def test_write_attribute_with_custom_primary_key_after_rollback
+ movie = Movie.create!(name: "foo")
+
+ Movie.transaction do
+ movie.save!
+ raise ActiveRecord::Rollback
+ end
+
+ movie.write_attribute(:movieid, nil)
+ assert_nil movie.movieid
end
def test_rollback_of_frozen_records
@@ -701,28 +819,66 @@ class TransactionTest < ActiveRecord::TestCase
connection = Topic.connection
transaction = ActiveRecord::ConnectionAdapters::TransactionManager.new(connection).begin_transaction
- assert transaction.open?
- assert !transaction.state.rolledback?
- assert !transaction.state.committed?
+ assert_predicate transaction, :open?
+ assert_not_predicate transaction.state, :rolledback?
+ assert_not_predicate transaction.state, :committed?
transaction.rollback
- assert transaction.state.rolledback?
- assert !transaction.state.committed?
+ assert_predicate transaction.state, :rolledback?
+ assert_not_predicate transaction.state, :committed?
end
def test_transactions_state_from_commit
connection = Topic.connection
transaction = ActiveRecord::ConnectionAdapters::TransactionManager.new(connection).begin_transaction
- assert transaction.open?
- assert !transaction.state.rolledback?
- assert !transaction.state.committed?
+ assert_predicate transaction, :open?
+ assert_not_predicate transaction.state, :rolledback?
+ assert_not_predicate transaction.state, :committed?
+
+ transaction.commit
+
+ assert_not_predicate transaction.state, :rolledback?
+ assert_predicate transaction.state, :committed?
+ end
+
+ def test_set_state_method_is_deprecated
+ connection = Topic.connection
+ transaction = ActiveRecord::ConnectionAdapters::TransactionManager.new(connection).begin_transaction
+
+ transaction.commit
+
+ assert_deprecated do
+ transaction.state.set_state(:rolledback)
+ end
+ end
+
+ def test_mark_transaction_state_as_committed
+ connection = Topic.connection
+ transaction = ActiveRecord::ConnectionAdapters::TransactionManager.new(connection).begin_transaction
+
+ transaction.rollback
+
+ assert_equal :committed, transaction.state.commit!
+ end
+
+ def test_mark_transaction_state_as_rolledback
+ connection = Topic.connection
+ transaction = ActiveRecord::ConnectionAdapters::TransactionManager.new(connection).begin_transaction
+
+ transaction.commit
+
+ assert_equal :rolledback, transaction.state.rollback!
+ end
+
+ def test_mark_transaction_state_as_nil
+ connection = Topic.connection
+ transaction = ActiveRecord::ConnectionAdapters::TransactionManager.new(connection).begin_transaction
transaction.commit
- assert !transaction.state.rolledback?
- assert transaction.state.committed?
+ assert_nil transaction.state.nullify!
end
def test_transaction_rollback_with_primarykeyless_tables
@@ -773,7 +929,7 @@ class TransactionsWithTransactionalFixturesTest < ActiveRecord::TestCase
raise
end
rescue
- assert !@first.reload.approved?
+ assert_not_predicate @first.reload, :approved?
end
end
@@ -794,31 +950,29 @@ class TransactionsWithTransactionalFixturesTest < ActiveRecord::TestCase
end
end
- assert !@first.reload.approved?
+ assert_not_predicate @first.reload, :approved?
end
end if Topic.connection.supports_savepoints?
-if current_adapter?(:PostgreSQLAdapter)
+if ActiveRecord::Base.connection.supports_transaction_isolation?
class ConcurrentTransactionTest < TransactionTest
# This will cause transactions to overlap and fail unless they are performed on
# separate database connections.
- unless in_memory_db?
- def test_transaction_per_thread
- threads = 3.times.map do
- Thread.new do
- Topic.transaction do
- topic = Topic.find(1)
- topic.approved = !topic.approved?
- assert topic.save!
- topic.approved = !topic.approved?
- assert topic.save!
- end
- Topic.connection.close
+ def test_transaction_per_thread
+ threads = 3.times.map do
+ Thread.new do
+ Topic.transaction do
+ topic = Topic.find(1)
+ topic.approved = !topic.approved?
+ assert topic.save!
+ topic.approved = !topic.approved?
+ assert topic.save!
end
+ Topic.connection.close
end
-
- threads.each(&:join)
end
+
+ threads.each(&:join)
end
# Test for dirty reads among simultaneous transactions.
diff --git a/activerecord/test/cases/type/adapter_specific_registry_test.rb b/activerecord/test/cases/type/adapter_specific_registry_test.rb
index 8b836b4793..b58bdd5549 100644
--- a/activerecord/test/cases/type/adapter_specific_registry_test.rb
+++ b/activerecord/test/cases/type/adapter_specific_registry_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "cases/helper"
module ActiveRecord
diff --git a/activerecord/test/cases/type/date_time_test.rb b/activerecord/test/cases/type/date_time_test.rb
index 6848619ece..c9558e25b5 100644
--- a/activerecord/test/cases/type/date_time_test.rb
+++ b/activerecord/test/cases/type/date_time_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "cases/helper"
require "models/task"
diff --git a/activerecord/test/cases/type/integer_test.rb b/activerecord/test/cases/type/integer_test.rb
index 368b6d7199..15d1a675a1 100644
--- a/activerecord/test/cases/type/integer_test.rb
+++ b/activerecord/test/cases/type/integer_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "cases/helper"
require "models/company"
diff --git a/activerecord/test/cases/type/string_test.rb b/activerecord/test/cases/type/string_test.rb
index a95da864fa..9e7810a6a5 100644
--- a/activerecord/test/cases/type/string_test.rb
+++ b/activerecord/test/cases/type/string_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "cases/helper"
module ActiveRecord
@@ -7,16 +9,16 @@ module ActiveRecord
klass.table_name = "authors"
author = klass.create!(name: "Sean")
- assert_not author.changed?
+ assert_not_predicate author, :changed?
author.name << " Griffin"
- assert author.name_changed?
+ assert_predicate author, :name_changed?
author.save!
author.reload
assert_equal "Sean Griffin", author.name
- assert_not author.changed?
+ assert_not_predicate author, :changed?
end
end
end
diff --git a/activerecord/test/cases/type/type_map_test.rb b/activerecord/test/cases/type/type_map_test.rb
index 2959d36466..f3699c11a2 100644
--- a/activerecord/test/cases/type/type_map_test.rb
+++ b/activerecord/test/cases/type/type_map_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "cases/helper"
module ActiveRecord
diff --git a/activerecord/test/cases/type/unsigned_integer_test.rb b/activerecord/test/cases/type/unsigned_integer_test.rb
index 1cd4dbc2c5..dd05cf3fff 100644
--- a/activerecord/test/cases/type/unsigned_integer_test.rb
+++ b/activerecord/test/cases/type/unsigned_integer_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "cases/helper"
module ActiveRecord
diff --git a/activerecord/test/cases/type_test.rb b/activerecord/test/cases/type_test.rb
index d45a9b3141..93ae563c8b 100644
--- a/activerecord/test/cases/type_test.rb
+++ b/activerecord/test/cases/type_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "cases/helper"
class TypeTest < ActiveRecord::TestCase
diff --git a/activerecord/test/cases/types_test.rb b/activerecord/test/cases/types_test.rb
index 11476ea0ef..3f7fb0a604 100644
--- a/activerecord/test/cases/types_test.rb
+++ b/activerecord/test/cases/types_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "cases/helper"
module ActiveRecord
diff --git a/activerecord/test/cases/unconnected_test.rb b/activerecord/test/cases/unconnected_test.rb
index b210584644..f4d8be5897 100644
--- a/activerecord/test/cases/unconnected_test.rb
+++ b/activerecord/test/cases/unconnected_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "cases/helper"
class TestRecord < ActiveRecord::Base
diff --git a/activerecord/test/cases/unsafe_raw_sql_test.rb b/activerecord/test/cases/unsafe_raw_sql_test.rb
new file mode 100644
index 0000000000..72d4997d0b
--- /dev/null
+++ b/activerecord/test/cases/unsafe_raw_sql_test.rb
@@ -0,0 +1,299 @@
+# frozen_string_literal: true
+
+require "cases/helper"
+require "models/post"
+require "models/comment"
+
+class UnsafeRawSqlTest < ActiveRecord::TestCase
+ fixtures :posts, :comments
+
+ test "order: allows string column name" do
+ ids_expected = Post.order(Arel.sql("title")).pluck(:id)
+
+ ids_depr = with_unsafe_raw_sql_deprecated { Post.order("title").pluck(:id) }
+ ids_disabled = with_unsafe_raw_sql_disabled { Post.order("title").pluck(:id) }
+
+ assert_equal ids_expected, ids_depr
+ assert_equal ids_expected, ids_disabled
+ end
+
+ test "order: allows symbol column name" do
+ ids_expected = Post.order(Arel.sql("title")).pluck(:id)
+
+ ids_depr = with_unsafe_raw_sql_deprecated { Post.order(:title).pluck(:id) }
+ ids_disabled = with_unsafe_raw_sql_disabled { Post.order(:title).pluck(:id) }
+
+ assert_equal ids_expected, ids_depr
+ assert_equal ids_expected, ids_disabled
+ end
+
+ test "order: allows downcase symbol direction" do
+ ids_expected = Post.order(Arel.sql("title") => Arel.sql("asc")).pluck(:id)
+
+ ids_depr = with_unsafe_raw_sql_deprecated { Post.order(title: :asc).pluck(:id) }
+ ids_disabled = with_unsafe_raw_sql_disabled { Post.order(title: :asc).pluck(:id) }
+
+ assert_equal ids_expected, ids_depr
+ assert_equal ids_expected, ids_disabled
+ end
+
+ test "order: allows upcase symbol direction" do
+ ids_expected = Post.order(Arel.sql("title") => Arel.sql("ASC")).pluck(:id)
+
+ ids_depr = with_unsafe_raw_sql_deprecated { Post.order(title: :ASC).pluck(:id) }
+ ids_disabled = with_unsafe_raw_sql_disabled { Post.order(title: :ASC).pluck(:id) }
+
+ assert_equal ids_expected, ids_depr
+ assert_equal ids_expected, ids_disabled
+ end
+
+ test "order: allows string direction" do
+ ids_expected = Post.order(Arel.sql("title") => Arel.sql("asc")).pluck(:id)
+
+ ids_depr = with_unsafe_raw_sql_deprecated { Post.order(title: "asc").pluck(:id) }
+ ids_disabled = with_unsafe_raw_sql_disabled { Post.order(title: "asc").pluck(:id) }
+
+ assert_equal ids_expected, ids_depr
+ assert_equal ids_expected, ids_disabled
+ end
+
+ test "order: allows multiple columns" do
+ ids_expected = Post.order(Arel.sql("author_id"), Arel.sql("title")).pluck(:id)
+
+ ids_depr = with_unsafe_raw_sql_deprecated { Post.order(:author_id, :title).pluck(:id) }
+ ids_disabled = with_unsafe_raw_sql_disabled { Post.order(:author_id, :title).pluck(:id) }
+
+ assert_equal ids_expected, ids_depr
+ assert_equal ids_expected, ids_disabled
+ end
+
+ test "order: allows mixed" do
+ ids_expected = Post.order(Arel.sql("author_id"), Arel.sql("title") => Arel.sql("asc")).pluck(:id)
+
+ ids_depr = with_unsafe_raw_sql_deprecated { Post.order(:author_id, title: :asc).pluck(:id) }
+ ids_disabled = with_unsafe_raw_sql_disabled { Post.order(:author_id, title: :asc).pluck(:id) }
+
+ assert_equal ids_expected, ids_depr
+ assert_equal ids_expected, ids_disabled
+ end
+
+ test "order: allows table and column name" do
+ ids_expected = Post.order(Arel.sql("title")).pluck(:id)
+
+ ids_depr = with_unsafe_raw_sql_deprecated { Post.order("posts.title").pluck(:id) }
+ ids_disabled = with_unsafe_raw_sql_disabled { Post.order("posts.title").pluck(:id) }
+
+ assert_equal ids_expected, ids_depr
+ assert_equal ids_expected, ids_disabled
+ end
+
+ test "order: allows column name and direction in string" do
+ ids_expected = Post.order(Arel.sql("title desc")).pluck(:id)
+
+ ids_depr = with_unsafe_raw_sql_deprecated { Post.order("title desc").pluck(:id) }
+ ids_disabled = with_unsafe_raw_sql_disabled { Post.order("title desc").pluck(:id) }
+
+ assert_equal ids_expected, ids_depr
+ assert_equal ids_expected, ids_disabled
+ end
+
+ test "order: allows table name, column name and direction in string" do
+ ids_expected = Post.order(Arel.sql("title desc")).pluck(:id)
+
+ ids_depr = with_unsafe_raw_sql_deprecated { Post.order("posts.title desc").pluck(:id) }
+ ids_disabled = with_unsafe_raw_sql_disabled { Post.order("posts.title desc").pluck(:id) }
+
+ assert_equal ids_expected, ids_depr
+ assert_equal ids_expected, ids_disabled
+ end
+
+ test "order: disallows invalid column name" do
+ with_unsafe_raw_sql_disabled do
+ assert_raises(ActiveRecord::UnknownAttributeReference) do
+ Post.order("len(title) asc").pluck(:id)
+ end
+ end
+ end
+
+ test "order: disallows invalid direction" do
+ with_unsafe_raw_sql_disabled do
+ assert_raises(ArgumentError) do
+ Post.order(title: :foo).pluck(:id)
+ end
+ end
+ end
+
+ test "order: disallows invalid column with direction" do
+ with_unsafe_raw_sql_disabled do
+ assert_raises(ActiveRecord::UnknownAttributeReference) do
+ Post.order("len(title)" => :asc).pluck(:id)
+ end
+ end
+ end
+
+ test "order: always allows Arel" do
+ ids_depr = with_unsafe_raw_sql_deprecated { Post.order(Arel.sql("length(title)")).pluck(:title) }
+ ids_disabled = with_unsafe_raw_sql_disabled { Post.order(Arel.sql("length(title)")).pluck(:title) }
+
+ assert_equal ids_depr, ids_disabled
+ end
+
+ test "order: allows Arel.sql with binds" do
+ ids_expected = Post.order(Arel.sql("REPLACE(title, 'misc', 'zzzz'), id")).pluck(:id)
+
+ ids_depr = with_unsafe_raw_sql_deprecated { Post.order([Arel.sql("REPLACE(title, ?, ?), id"), "misc", "zzzz"]).pluck(:id) }
+ ids_disabled = with_unsafe_raw_sql_disabled { Post.order([Arel.sql("REPLACE(title, ?, ?), id"), "misc", "zzzz"]).pluck(:id) }
+
+ assert_equal ids_expected, ids_depr
+ assert_equal ids_expected, ids_disabled
+ end
+
+ test "order: disallows invalid bind statement" do
+ with_unsafe_raw_sql_disabled do
+ assert_raises(ActiveRecord::UnknownAttributeReference) do
+ Post.order(["REPLACE(title, ?, ?), id", "misc", "zzzz"]).pluck(:id)
+ end
+ end
+ end
+
+ test "order: disallows invalid Array arguments" do
+ with_unsafe_raw_sql_disabled do
+ assert_raises(ActiveRecord::UnknownAttributeReference) do
+ Post.order(["author_id", "length(title)"]).pluck(:id)
+ end
+ end
+ end
+
+ test "order: allows valid Array arguments" do
+ ids_expected = Post.order(Arel.sql("author_id, length(title)")).pluck(:id)
+
+ ids_depr = with_unsafe_raw_sql_deprecated { Post.order(["author_id", Arel.sql("length(title)")]).pluck(:id) }
+ ids_disabled = with_unsafe_raw_sql_disabled { Post.order(["author_id", Arel.sql("length(title)")]).pluck(:id) }
+
+ assert_equal ids_expected, ids_depr
+ assert_equal ids_expected, ids_disabled
+ end
+
+ test "order: logs deprecation warning for unrecognized column" do
+ with_unsafe_raw_sql_deprecated do
+ assert_deprecated(/Dangerous query method/) do
+ Post.order("length(title)")
+ end
+ end
+ end
+
+ test "pluck: allows string column name" do
+ titles_expected = Post.pluck(Arel.sql("title"))
+
+ titles_depr = with_unsafe_raw_sql_deprecated { Post.pluck("title") }
+ titles_disabled = with_unsafe_raw_sql_disabled { Post.pluck("title") }
+
+ assert_equal titles_expected, titles_depr
+ assert_equal titles_expected, titles_disabled
+ end
+
+ test "pluck: allows symbol column name" do
+ titles_expected = Post.pluck(Arel.sql("title"))
+
+ titles_depr = with_unsafe_raw_sql_deprecated { Post.pluck(:title) }
+ titles_disabled = with_unsafe_raw_sql_disabled { Post.pluck(:title) }
+
+ assert_equal titles_expected, titles_depr
+ assert_equal titles_expected, titles_disabled
+ end
+
+ test "pluck: allows multiple column names" do
+ values_expected = Post.pluck(Arel.sql("title"), Arel.sql("id"))
+
+ values_depr = with_unsafe_raw_sql_deprecated { Post.pluck(:title, :id) }
+ values_disabled = with_unsafe_raw_sql_disabled { Post.pluck(:title, :id) }
+
+ assert_equal values_expected, values_depr
+ assert_equal values_expected, values_disabled
+ end
+
+ test "pluck: allows column names with includes" do
+ values_expected = Post.includes(:comments).pluck(Arel.sql("title"), Arel.sql("id"))
+
+ values_depr = with_unsafe_raw_sql_deprecated { Post.includes(:comments).pluck(:title, :id) }
+ values_disabled = with_unsafe_raw_sql_disabled { Post.includes(:comments).pluck(:title, :id) }
+
+ assert_equal values_expected, values_depr
+ assert_equal values_expected, values_disabled
+ end
+
+ test "pluck: allows auto-generated attributes" do
+ values_expected = Post.pluck(Arel.sql("tags_count"))
+
+ values_depr = with_unsafe_raw_sql_deprecated { Post.pluck(:tags_count) }
+ values_disabled = with_unsafe_raw_sql_disabled { Post.pluck(:tags_count) }
+
+ assert_equal values_expected, values_depr
+ assert_equal values_expected, values_disabled
+ end
+
+ test "pluck: allows table and column names" do
+ titles_expected = Post.pluck(Arel.sql("title"))
+
+ titles_depr = with_unsafe_raw_sql_deprecated { Post.pluck("posts.title") }
+ titles_disabled = with_unsafe_raw_sql_disabled { Post.pluck("posts.title") }
+
+ assert_equal titles_expected, titles_depr
+ assert_equal titles_expected, titles_disabled
+ end
+
+ test "pluck: disallows invalid column name" do
+ with_unsafe_raw_sql_disabled do
+ assert_raises(ActiveRecord::UnknownAttributeReference) do
+ Post.pluck("length(title)")
+ end
+ end
+ end
+
+ test "pluck: disallows invalid column name amongst valid names" do
+ with_unsafe_raw_sql_disabled do
+ assert_raises(ActiveRecord::UnknownAttributeReference) do
+ Post.pluck(:title, "length(title)")
+ end
+ end
+ end
+
+ test "pluck: disallows invalid column names with includes" do
+ with_unsafe_raw_sql_disabled do
+ assert_raises(ActiveRecord::UnknownAttributeReference) do
+ Post.includes(:comments).pluck(:title, "length(title)")
+ end
+ end
+ end
+
+ test "pluck: always allows Arel" do
+ values_depr = with_unsafe_raw_sql_deprecated { Post.includes(:comments).pluck(:title, Arel.sql("length(title)")) }
+ values_disabled = with_unsafe_raw_sql_disabled { Post.includes(:comments).pluck(:title, Arel.sql("length(title)")) }
+
+ assert_equal values_depr, values_disabled
+ end
+
+ test "pluck: logs deprecation warning" do
+ with_unsafe_raw_sql_deprecated do
+ assert_deprecated(/Dangerous query method/) do
+ Post.includes(:comments).pluck(:title, "length(title)")
+ end
+ end
+ end
+
+ def with_unsafe_raw_sql_disabled(&blk)
+ with_config(:disabled, &blk)
+ end
+
+ def with_unsafe_raw_sql_deprecated(&blk)
+ with_config(:deprecated, &blk)
+ end
+
+ def with_config(new_value, &blk)
+ old_value = ActiveRecord::Base.allow_unsafe_raw_sql
+ ActiveRecord::Base.allow_unsafe_raw_sql = new_value
+ blk.call
+ ensure
+ ActiveRecord::Base.allow_unsafe_raw_sql = old_value
+ end
+end
diff --git a/activerecord/test/cases/validations/absence_validation_test.rb b/activerecord/test/cases/validations/absence_validation_test.rb
index 870619e4e7..8235a54d8a 100644
--- a/activerecord/test/cases/validations/absence_validation_test.rb
+++ b/activerecord/test/cases/validations/absence_validation_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "cases/helper"
require "models/face"
require "models/interest"
@@ -11,8 +13,8 @@ class AbsenceValidationTest < ActiveRecord::TestCase
validates_absence_of :name
end
- assert boy_klass.new.valid?
- assert_not boy_klass.new(name: "Alex").valid?
+ assert_predicate boy_klass.new, :valid?
+ assert_not_predicate boy_klass.new(name: "Alex"), :valid?
end
def test_has_one_marked_for_destruction
@@ -42,7 +44,7 @@ class AbsenceValidationTest < ActiveRecord::TestCase
assert_not boy.valid?, "should not be valid if has_many association is present"
i2.mark_for_destruction
- assert boy.valid?
+ assert_predicate boy, :valid?
end
def test_does_not_call_to_a_on_associations
@@ -63,11 +65,11 @@ class AbsenceValidationTest < ActiveRecord::TestCase
Interest.validates_absence_of(:token)
interest = Interest.create!(topic: "Thought Leadering")
- assert interest.valid?
+ assert_predicate interest, :valid?
interest.token = "tl"
- assert interest.invalid?
+ assert_predicate interest, :invalid?
end
end
end
diff --git a/activerecord/test/cases/validations/association_validation_test.rb b/activerecord/test/cases/validations/association_validation_test.rb
index f5ceb27d97..ce6d42b34b 100644
--- a/activerecord/test/cases/validations/association_validation_test.rb
+++ b/activerecord/test/cases/validations/association_validation_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "cases/helper"
require "models/topic"
require "models/reply"
@@ -14,14 +16,14 @@ class AssociationValidationTest < ActiveRecord::TestCase
Reply.validates_presence_of(:content)
t = Topic.create("title" => "uhohuhoh", "content" => "whatever")
t.replies << [r = Reply.new("title" => "A reply"), r2 = Reply.new("title" => "Another reply", "content" => "non-empty"), r3 = Reply.new("title" => "Yet another reply"), r4 = Reply.new("title" => "The last reply", "content" => "non-empty")]
- assert !t.valid?
- assert t.errors[:replies].any?
+ assert_not_predicate t, :valid?
+ assert_predicate t.errors[:replies], :any?
assert_equal 1, r.errors.count # make sure all associated objects have been validated
assert_equal 0, r2.errors.count
assert_equal 1, r3.errors.count
assert_equal 0, r4.errors.count
r.content = r3.content = "non-empty"
- assert t.valid?
+ assert_predicate t, :valid?
end
def test_validates_associated_one
@@ -29,10 +31,10 @@ class AssociationValidationTest < ActiveRecord::TestCase
Topic.validates_presence_of(:content)
r = Reply.new("title" => "A reply", "content" => "with content!")
r.topic = Topic.create("title" => "uhohuhoh")
- assert !r.valid?
- assert r.errors[:topic].any?
+ assert_not_predicate r, :valid?
+ assert_predicate r.errors[:topic], :any?
r.topic.content = "non-empty"
- assert r.valid?
+ assert_predicate r, :valid?
end
def test_validates_associated_marked_for_destruction
@@ -40,9 +42,9 @@ class AssociationValidationTest < ActiveRecord::TestCase
Reply.validates_presence_of(:content)
t = Topic.new
t.replies << Reply.new
- assert t.invalid?
+ assert_predicate t, :invalid?
t.replies.first.mark_for_destruction
- assert t.valid?
+ assert_predicate t, :valid?
end
def test_validates_associated_without_marked_for_destruction
@@ -54,7 +56,7 @@ class AssociationValidationTest < ActiveRecord::TestCase
Topic.validates_associated(:replies)
t = Topic.new
t.define_singleton_method(:replies) { [reply.new] }
- assert t.valid?
+ assert_predicate t, :valid?
end
def test_validates_associated_with_custom_message_using_quotes
@@ -69,11 +71,11 @@ class AssociationValidationTest < ActiveRecord::TestCase
def test_validates_associated_missing
Reply.validates_presence_of(:topic)
r = Reply.create("title" => "A reply", "content" => "with content!")
- assert !r.valid?
- assert r.errors[:topic].any?
+ assert_not_predicate r, :valid?
+ assert_predicate r.errors[:topic], :any?
r.topic = Topic.first
- assert r.valid?
+ assert_predicate r, :valid?
end
def test_validates_presence_of_belongs_to_association__parent_is_new_record
diff --git a/activerecord/test/cases/validations/i18n_generate_message_validation_test.rb b/activerecord/test/cases/validations/i18n_generate_message_validation_test.rb
index a57065ba75..703c24b340 100644
--- a/activerecord/test/cases/validations/i18n_generate_message_validation_test.rb
+++ b/activerecord/test/cases/validations/i18n_generate_message_validation_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "cases/helper"
require "models/topic"
diff --git a/activerecord/test/cases/validations/i18n_validation_test.rb b/activerecord/test/cases/validations/i18n_validation_test.rb
index fd88a3ea67..b7c52ea18c 100644
--- a/activerecord/test/cases/validations/i18n_validation_test.rb
+++ b/activerecord/test/cases/validations/i18n_validation_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "cases/helper"
require "models/topic"
require "models/reply"
diff --git a/activerecord/test/cases/validations/length_validation_test.rb b/activerecord/test/cases/validations/length_validation_test.rb
index ba45c6dcc1..73422a31cd 100644
--- a/activerecord/test/cases/validations/length_validation_test.rb
+++ b/activerecord/test/cases/validations/length_validation_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "cases/helper"
require "models/owner"
require "models/pet"
@@ -16,47 +18,47 @@ class LengthValidationTest < ActiveRecord::TestCase
assert_nothing_raised { @owner.validates_size_of :pets, minimum: 1 }
o = @owner.new("name" => "nopets")
assert !o.save
- assert o.errors[:pets].any?
+ assert_predicate o.errors[:pets], :any?
o.pets.build("name" => "apet")
- assert o.valid?
+ assert_predicate o, :valid?
end
def test_validates_size_of_association_using_within
assert_nothing_raised { @owner.validates_size_of :pets, within: 1..2 }
o = @owner.new("name" => "nopets")
assert !o.save
- assert o.errors[:pets].any?
+ assert_predicate o.errors[:pets], :any?
o.pets.build("name" => "apet")
- assert o.valid?
+ assert_predicate o, :valid?
2.times { o.pets.build("name" => "apet") }
assert !o.save
- assert o.errors[:pets].any?
+ assert_predicate o.errors[:pets], :any?
end
def test_validates_size_of_association_utf8
@owner.validates_size_of :pets, minimum: 1
o = @owner.new("name" => "あいうえおかきくけこ")
assert !o.save
- assert o.errors[:pets].any?
+ assert_predicate o.errors[:pets], :any?
o.pets.build("name" => "あいうえおかきくけこ")
- assert o.valid?
+ assert_predicate o, :valid?
end
def test_validates_size_of_respects_records_marked_for_destruction
@owner.validates_size_of :pets, minimum: 1
owner = @owner.new
assert_not owner.save
- assert owner.errors[:pets].any?
+ assert_predicate owner.errors[:pets], :any?
pet = owner.pets.build
- assert owner.valid?
+ assert_predicate owner, :valid?
assert owner.save
pet_count = Pet.count
assert_not owner.update_attributes pets_attributes: [ { _destroy: 1, id: pet.id } ]
- assert_not owner.valid?
- assert owner.errors[:pets].any?
+ assert_not_predicate owner, :valid?
+ assert_predicate owner.errors[:pets], :any?
assert_equal pet_count, Pet.count
end
@@ -68,11 +70,11 @@ class LengthValidationTest < ActiveRecord::TestCase
pet = Pet.create!(name: "Fancy Pants", nickname: "Fancy")
- assert pet.valid?
+ assert_predicate pet, :valid?
pet.nickname = ""
- assert pet.invalid?
+ assert_predicate pet, :invalid?
end
end
end
diff --git a/activerecord/test/cases/validations/presence_validation_test.rb b/activerecord/test/cases/validations/presence_validation_test.rb
index 13956e26ec..63c3f67da2 100644
--- a/activerecord/test/cases/validations/presence_validation_test.rb
+++ b/activerecord/test/cases/validations/presence_validation_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "cases/helper"
require "models/man"
require "models/face"
@@ -13,10 +15,10 @@ class PresenceValidationTest < ActiveRecord::TestCase
def test_validates_presence_of_non_association
Boy.validates_presence_of(:name)
b = Boy.new
- assert b.invalid?
+ assert_predicate b, :invalid?
b.name = "Alex"
- assert b.valid?
+ assert_predicate b, :valid?
end
def test_validates_presence_of_has_one
@@ -31,23 +33,23 @@ class PresenceValidationTest < ActiveRecord::TestCase
b = Boy.new
f = Face.new
b.face = f
- assert b.valid?
+ assert_predicate b, :valid?
f.mark_for_destruction
- assert b.invalid?
+ assert_predicate b, :invalid?
end
def test_validates_presence_of_has_many_marked_for_destruction
Boy.validates_presence_of(:interests)
b = Boy.new
b.interests << [i1 = Interest.new, i2 = Interest.new]
- assert b.valid?
+ assert_predicate b, :valid?
i1.mark_for_destruction
- assert b.valid?
+ assert_predicate b, :valid?
i2.mark_for_destruction
- assert b.invalid?
+ assert_predicate b, :invalid?
end
def test_validates_presence_doesnt_convert_to_array
@@ -72,11 +74,11 @@ class PresenceValidationTest < ActiveRecord::TestCase
Interest.validates_presence_of(:abbreviation)
interest = Interest.create!(topic: "Thought Leadering", abbreviation: "tl")
- assert interest.valid?
+ assert_predicate interest, :valid?
interest.abbreviation = ""
- assert interest.invalid?
+ assert_predicate interest, :invalid?
end
end
diff --git a/activerecord/test/cases/validations/uniqueness_validation_test.rb b/activerecord/test/cases/validations/uniqueness_validation_test.rb
index 28605d2f8e..1fa94a5b75 100644
--- a/activerecord/test/cases/validations/uniqueness_validation_test.rb
+++ b/activerecord/test/cases/validations/uniqueness_validation_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "cases/helper"
require "models/topic"
require "models/reply"
@@ -94,7 +96,7 @@ class UniquenessValidationTest < ActiveRecord::TestCase
Topic.validates_uniqueness_of(:new_title)
topic = Topic.new(new_title: "abc")
- assert topic.valid?
+ assert_predicate topic, :valid?
end
def test_validates_uniqueness_with_nil_value
@@ -114,7 +116,7 @@ class UniquenessValidationTest < ActiveRecord::TestCase
Topic.create!("title" => "abc")
t2 = Topic.new("title" => "abc")
- assert !t2.valid?
+ assert_not_predicate t2, :valid?
assert t2.errors[:title]
end
@@ -154,6 +156,13 @@ class UniquenessValidationTest < ActiveRecord::TestCase
assert r3.valid?, "Saving r3"
end
+ def test_validate_uniqueness_with_scope_invalid_syntax
+ error = assert_raises(ArgumentError) do
+ Reply.validates_uniqueness_of(:content, scope: { parent_id: false })
+ end
+ assert_match(/Pass a symbol or an array of symbols instead/, error.to_s)
+ end
+
def test_validate_uniqueness_with_object_scope
Reply.validates_uniqueness_of(:content, scope: :topic)
@@ -250,15 +259,15 @@ class UniquenessValidationTest < ActiveRecord::TestCase
t2 = Topic.new("title" => "I'm UNIQUE!", :parent_id => 1)
assert !t2.valid?, "Shouldn't be valid"
assert !t2.save, "Shouldn't save t2 as unique"
- assert t2.errors[:title].any?
- assert t2.errors[:parent_id].any?
+ assert_predicate t2.errors[:title], :any?
+ assert_predicate t2.errors[:parent_id], :any?
assert_equal ["has already been taken"], t2.errors[:title]
t2.title = "I'm truly UNIQUE!"
assert !t2.valid?, "Shouldn't be valid"
assert !t2.save, "Shouldn't save t2 as unique"
- assert t2.errors[:title].empty?
- assert t2.errors[:parent_id].any?
+ assert_empty t2.errors[:title]
+ assert_predicate t2.errors[:parent_id], :any?
t2.parent_id = 4
assert t2.save, "Should now save t2 as unique"
@@ -317,15 +326,15 @@ class UniquenessValidationTest < ActiveRecord::TestCase
t2 = Topic.new("title" => "I'M UNIQUE!")
assert t2.valid?, "Should be valid"
assert t2.save, "Should save t2 as unique"
- assert t2.errors[:title].empty?
- assert t2.errors[:parent_id].empty?
+ assert_empty t2.errors[:title]
+ assert_empty t2.errors[:parent_id]
assert_not_equal ["has already been taken"], t2.errors[:title]
t3 = Topic.new("title" => "I'M uNiQUe!")
assert t3.valid?, "Should be valid"
assert t3.save, "Should save t2 as unique"
- assert t3.errors[:title].empty?
- assert t3.errors[:parent_id].empty?
+ assert_empty t3.errors[:title]
+ assert_empty t3.errors[:parent_id]
assert_not_equal ["has already been taken"], t3.errors[:title]
end
@@ -334,7 +343,7 @@ class UniquenessValidationTest < ActiveRecord::TestCase
Topic.create!("title" => 101)
t2 = Topic.new("title" => 101)
- assert !t2.valid?
+ assert_not_predicate t2, :valid?
assert t2.errors[:title]
end
@@ -351,7 +360,7 @@ class UniquenessValidationTest < ActiveRecord::TestCase
t1 = Topic.new("title" => "I'm unique!", "author_name" => "Mary")
assert t1.save
t2 = Topic.new("title" => "I'm unique!", "author_name" => "David")
- assert !t2.valid?
+ assert_not_predicate t2, :valid?
end
end
@@ -451,16 +460,16 @@ class UniquenessValidationTest < ActiveRecord::TestCase
def test_validate_uniqueness_on_existing_relation
event = Event.create
- assert TopicWithUniqEvent.create(event: event).valid?
+ assert_predicate TopicWithUniqEvent.create(event: event), :valid?
topic = TopicWithUniqEvent.new(event: event)
- assert_not topic.valid?
+ assert_not_predicate topic, :valid?
assert_equal ["has already been taken"], topic.errors[:event]
end
def test_validate_uniqueness_on_empty_relation
topic = TopicWithUniqEvent.new
- assert topic.valid?
+ assert_predicate topic, :valid?
end
def test_validate_uniqueness_of_custom_primary_key
@@ -479,7 +488,7 @@ class UniquenessValidationTest < ActiveRecord::TestCase
key2 = klass.create!(key_number: 11)
key2.key_number = 10
- assert_not key2.valid?
+ assert_not_predicate key2, :valid?
end
def test_validate_uniqueness_without_primary_key
@@ -492,8 +501,8 @@ class UniquenessValidationTest < ActiveRecord::TestCase
end
abc = klass.create!(dashboard_id: "abc")
- assert klass.new(dashboard_id: "xyz").valid?
- assert_not klass.new(dashboard_id: "abc").valid?
+ assert_predicate klass.new(dashboard_id: "xyz"), :valid?
+ assert_not_predicate klass.new(dashboard_id: "abc"), :valid?
abc.dashboard_id = "def"
@@ -521,7 +530,7 @@ class UniquenessValidationTest < ActiveRecord::TestCase
assert topic.author_name.start_with?("Title1")
topic2 = TopicWithAfterCreate.new(title: "Title1")
- refute topic2.valid?
+ assert_not_predicate topic2, :valid?
assert_equal(["has already been taken"], topic2.errors[:title])
end
@@ -541,7 +550,7 @@ class UniquenessValidationTest < ActiveRecord::TestCase
assert_empty item.errors
item2 = CoolTopic.new(id: item.id, title: "MyItem2")
- refute item2.valid?
+ assert_not_predicate item2, :valid?
assert_equal(["has already been taken"], item2.errors[:id])
end
diff --git a/activerecord/test/cases/validations_repair_helper.rb b/activerecord/test/cases/validations_repair_helper.rb
index b30666d876..6dc3b64b2b 100644
--- a/activerecord/test/cases/validations_repair_helper.rb
+++ b/activerecord/test/cases/validations_repair_helper.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module ActiveRecord
module ValidationsRepairHelper
extend ActiveSupport::Concern
diff --git a/activerecord/test/cases/validations_test.rb b/activerecord/test/cases/validations_test.rb
index a305aa295a..6c83bbd15c 100644
--- a/activerecord/test/cases/validations_test.rb
+++ b/activerecord/test/cases/validations_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "cases/helper"
require "models/topic"
require "models/reply"
@@ -17,7 +19,7 @@ class ValidationsTest < ActiveRecord::TestCase
def test_valid_uses_create_context_when_new
r = WrongReply.new
r.title = "Wrong Create"
- assert_not r.valid?
+ assert_not_predicate r, :valid?
assert r.errors[:title].any?, "A reply with a bad title should mark that attribute as invalid"
assert_equal ["is Wrong Create"], r.errors[:title], "A reply with a bad content should contain an error"
end
@@ -137,7 +139,7 @@ class ValidationsTest < ActiveRecord::TestCase
def test_throw_away_typing
d = Developer.new("name" => "David", "salary" => "100,000")
- assert !d.valid?
+ assert_not_predicate d, :valid?
assert_equal 100, d.salary
assert_equal "100,000", d.salary_before_type_cast
end
@@ -164,7 +166,7 @@ class ValidationsTest < ActiveRecord::TestCase
topic = klass.new(wibble: "123-4567")
topic.wibble.gsub!("-", "")
- assert topic.valid?
+ assert_predicate topic, :valid?
end
def test_numericality_validation_checks_against_raw_value
@@ -173,12 +175,12 @@ class ValidationsTest < ActiveRecord::TestCase
ActiveModel::Name.new(self, nil, "Topic")
end
attribute :wibble, :decimal, scale: 2, precision: 9
- validates_numericality_of :wibble, greater_than_or_equal_to: BigDecimal.new("97.18")
+ validates_numericality_of :wibble, greater_than_or_equal_to: BigDecimal("97.18")
end
- assert_not klass.new(wibble: "97.179").valid?
- assert_not klass.new(wibble: 97.179).valid?
- assert_not klass.new(wibble: BigDecimal.new("97.179")).valid?
+ assert_not_predicate klass.new(wibble: "97.179"), :valid?
+ assert_not_predicate klass.new(wibble: 97.179), :valid?
+ assert_not_predicate klass.new(wibble: BigDecimal("97.179")), :valid?
end
def test_acceptance_validator_doesnt_require_db_connection
diff --git a/activerecord/test/cases/view_test.rb b/activerecord/test/cases/view_test.rb
index 1d21a2454f..7e2d66c62a 100644
--- a/activerecord/test/cases/view_test.rb
+++ b/activerecord/test/cases/view_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "cases/helper"
require "models/book"
require "support/schema_dumping_helper"
diff --git a/activerecord/test/cases/yaml_serialization_test.rb b/activerecord/test/cases/yaml_serialization_test.rb
index bfc13d683d..60ebdce178 100644
--- a/activerecord/test/cases/yaml_serialization_test.rb
+++ b/activerecord/test/cases/yaml_serialization_test.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "cases/helper"
require "models/topic"
require "models/reply"
@@ -94,7 +96,7 @@ class YamlSerializationTest < ActiveRecord::TestCase
def test_deserializing_rails_41_yaml
topic = YAML.load(yaml_fixture("rails_4_1"))
- assert topic.new_record?
+ assert_predicate topic, :new_record?
assert_nil topic.id
assert_equal "The First Topic", topic.title
assert_equal({ omg: :lol }, topic.content)
@@ -103,7 +105,7 @@ class YamlSerializationTest < ActiveRecord::TestCase
def test_deserializing_rails_4_2_0_yaml
topic = YAML.load(yaml_fixture("rails_4_2_0"))
- assert_not topic.new_record?
+ assert_not_predicate topic, :new_record?
assert_equal 1, topic.id
assert_equal "The First Topic", topic.title
assert_equal("Have a nice day", topic.content)
@@ -119,6 +121,14 @@ class YamlSerializationTest < ActiveRecord::TestCase
assert_equal author.changes, dumped.changes
end
+ def test_yaml_encoding_keeps_false_values
+ topic = Topic.first
+ topic.approved = false
+ dumped = YAML.load(YAML.dump(topic))
+
+ assert_equal false, dumped.approved
+ end
+
private
def yaml_fixture(file_name)
diff --git a/activerecord/test/config.rb b/activerecord/test/config.rb
index a65e6ff776..72cdfb16ef 100644
--- a/activerecord/test/config.rb
+++ b/activerecord/test/config.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
TEST_ROOT = __dir__
ASSETS_ROOT = TEST_ROOT + "/assets"
FIXTURES_ROOT = TEST_ROOT + "/fixtures"
diff --git a/activerecord/test/fixtures/binaries.yml b/activerecord/test/fixtures/binaries.yml
index ec8f2facdc..53b7883369 100644
--- a/activerecord/test/fixtures/binaries.yml
+++ b/activerecord/test/fixtures/binaries.yml
@@ -131,3 +131,7 @@ flowers:
SgCUASgCUASgCUASgAC74PbXOTvE5/En7jpSoLE8/wBn7uPJjKyj46T9D/NT
pKsXyQzxNpdNP0/akB5484WkMKh4RfXG4UafNmH7b0UxWMrb7Nxg6rl9Z/Im
w+vWq0iscQwxQroiUIvkKsRZQBKAJQBKAJQB/9k=
+
+binary_helper:
+ id: 2
+ data: <%= binary(ASSETS_ROOT + "/flowers.jpg") %>
diff --git a/activerecord/test/fixtures/customers.yml b/activerecord/test/fixtures/customers.yml
index 0399ff83b9..7d6c1366d0 100644
--- a/activerecord/test/fixtures/customers.yml
+++ b/activerecord/test/fixtures/customers.yml
@@ -23,4 +23,13 @@ barney:
address_street: Quiet Road
address_city: Peaceful Town
address_country: Tranquil Land
- gps_location: NULL \ No newline at end of file
+ gps_location: NULL
+
+mary:
+ id: 4
+ name: Mary
+ balance: 1
+ address_street: Funny Street
+ address_city: Peaceful Town
+ address_country: Nation Land
+ gps_location: NULL
diff --git a/activerecord/test/fixtures/minimalistics.yml b/activerecord/test/fixtures/minimalistics.yml
index c3ec546209..83df0551bc 100644
--- a/activerecord/test/fixtures/minimalistics.yml
+++ b/activerecord/test/fixtures/minimalistics.yml
@@ -1,2 +1,5 @@
+zero:
+ id: 0
+
first:
id: 1
diff --git a/activerecord/test/fixtures/other_posts.yml b/activerecord/test/fixtures/other_posts.yml
index 39ff763547..3e11a33802 100644
--- a/activerecord/test/fixtures/other_posts.yml
+++ b/activerecord/test/fixtures/other_posts.yml
@@ -5,3 +5,4 @@ second_welcome:
author_id: 1
title: Welcome to the another weblog
body: It's really nice today
+ comments_count: 1
diff --git a/activerecord/test/fixtures/posts.yml b/activerecord/test/fixtures/posts.yml
index 86d46f753a..8d7e1e0ae7 100644
--- a/activerecord/test/fixtures/posts.yml
+++ b/activerecord/test/fixtures/posts.yml
@@ -28,6 +28,7 @@ sti_comments:
author_id: 1
title: sti comments
body: hello
+ comments_count: 5
type: Post
sti_post_and_comments:
@@ -35,6 +36,7 @@ sti_post_and_comments:
author_id: 1
title: sti me
body: hello
+ comments_count: 2
type: StiPost
sti_habtm:
@@ -50,6 +52,8 @@ eager_other:
title: eager loading with OR'd conditions
body: hello
type: Post
+ comments_count: 1
+ tags_count: 3
misc_by_bob:
id: 8
@@ -57,6 +61,7 @@ misc_by_bob:
title: misc post by bob
body: hello
type: Post
+ tags_count: 1
misc_by_mary:
id: 9
@@ -64,6 +69,7 @@ misc_by_mary:
title: misc post by mary
body: hello
type: Post
+ tags_count: 1
other_by_bob:
id: 10
@@ -71,6 +77,7 @@ other_by_bob:
title: other post by bob
body: hello
type: Post
+ tags_count: 1
other_by_mary:
id: 11
@@ -78,3 +85,4 @@ other_by_mary:
title: other post by mary
body: hello
type: Post
+ tags_count: 1
diff --git a/activerecord/test/fixtures/reserved_words/values.yml b/activerecord/test/fixtures/reserved_words/values.yml
index 7d109609ab..9ed9e5edc5 100644
--- a/activerecord/test/fixtures/reserved_words/values.yml
+++ b/activerecord/test/fixtures/reserved_words/values.yml
@@ -1,7 +1,7 @@
values1:
- id: 1
+ as: 1
group_id: 2
values2:
- id: 2
+ as: 2
group_id: 1
diff --git a/activerecord/test/fixtures/teapots.yml b/activerecord/test/fixtures/teapots.yml
deleted file mode 100644
index ff515beb45..0000000000
--- a/activerecord/test/fixtures/teapots.yml
+++ /dev/null
@@ -1,3 +0,0 @@
-bob:
- id: 1
- name: Bob
diff --git a/activerecord/test/migrations/10_urban/9_add_expressions.rb b/activerecord/test/migrations/10_urban/9_add_expressions.rb
index e908c9eabc..4b0d5fb6fa 100644
--- a/activerecord/test/migrations/10_urban/9_add_expressions.rb
+++ b/activerecord/test/migrations/10_urban/9_add_expressions.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class AddExpressions < ActiveRecord::Migration::Current
def self.up
create_table("expressions") do |t|
diff --git a/activerecord/test/migrations/decimal/1_give_me_big_numbers.rb b/activerecord/test/migrations/decimal/1_give_me_big_numbers.rb
index 43c79bc20b..b892f50e41 100644
--- a/activerecord/test/migrations/decimal/1_give_me_big_numbers.rb
+++ b/activerecord/test/migrations/decimal/1_give_me_big_numbers.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class GiveMeBigNumbers < ActiveRecord::Migration::Current
def self.up
create_table :big_numbers do |table|
diff --git a/activerecord/test/migrations/empty/.gitkeep b/activerecord/test/migrations/empty/.keep
index e69de29bb2..e69de29bb2 100644
--- a/activerecord/test/migrations/empty/.gitkeep
+++ b/activerecord/test/migrations/empty/.keep
diff --git a/activerecord/test/migrations/magic/1_currencies_have_symbols.rb b/activerecord/test/migrations/magic/1_currencies_have_symbols.rb
index d4b0e6cd95..2ba2875751 100644
--- a/activerecord/test/migrations/magic/1_currencies_have_symbols.rb
+++ b/activerecord/test/migrations/magic/1_currencies_have_symbols.rb
@@ -1,3 +1,4 @@
+# frozen_string_literal: true
# coding: ISO-8859-15
class CurrenciesHaveSymbols < ActiveRecord::Migration::Current
diff --git a/activerecord/test/migrations/missing/1000_people_have_middle_names.rb b/activerecord/test/migrations/missing/1000_people_have_middle_names.rb
index e046944e31..d3c9b127fb 100644
--- a/activerecord/test/migrations/missing/1000_people_have_middle_names.rb
+++ b/activerecord/test/migrations/missing/1000_people_have_middle_names.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class PeopleHaveMiddleNames < ActiveRecord::Migration::Current
def self.up
add_column "people", "middle_name", :string
diff --git a/activerecord/test/migrations/missing/1_people_have_last_names.rb b/activerecord/test/migrations/missing/1_people_have_last_names.rb
index 50fe2a9c8e..bd5f5ea11e 100644
--- a/activerecord/test/migrations/missing/1_people_have_last_names.rb
+++ b/activerecord/test/migrations/missing/1_people_have_last_names.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class PeopleHaveLastNames < ActiveRecord::Migration::Current
def self.up
add_column "people", "last_name", :string
diff --git a/activerecord/test/migrations/missing/3_we_need_reminders.rb b/activerecord/test/migrations/missing/3_we_need_reminders.rb
index d7c63ac892..4647268c6e 100644
--- a/activerecord/test/migrations/missing/3_we_need_reminders.rb
+++ b/activerecord/test/migrations/missing/3_we_need_reminders.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class WeNeedReminders < ActiveRecord::Migration::Current
def self.up
create_table("reminders") do |t|
diff --git a/activerecord/test/migrations/missing/4_innocent_jointable.rb b/activerecord/test/migrations/missing/4_innocent_jointable.rb
index bd3bf21576..8063bc0558 100644
--- a/activerecord/test/migrations/missing/4_innocent_jointable.rb
+++ b/activerecord/test/migrations/missing/4_innocent_jointable.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class InnocentJointable < ActiveRecord::Migration::Current
def self.up
create_table("people_reminders", id: false) do |t|
diff --git a/activerecord/test/migrations/rename/1_we_need_things.rb b/activerecord/test/migrations/rename/1_we_need_things.rb
index 9dce01acfd..8e71a1d996 100644
--- a/activerecord/test/migrations/rename/1_we_need_things.rb
+++ b/activerecord/test/migrations/rename/1_we_need_things.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class WeNeedThings < ActiveRecord::Migration::Current
def self.up
create_table("things") do |t|
diff --git a/activerecord/test/migrations/rename/2_rename_things.rb b/activerecord/test/migrations/rename/2_rename_things.rb
index cb8484e7dc..110fe3f0fa 100644
--- a/activerecord/test/migrations/rename/2_rename_things.rb
+++ b/activerecord/test/migrations/rename/2_rename_things.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class RenameThings < ActiveRecord::Migration::Current
def self.up
rename_table "things", "awesome_things"
diff --git a/activerecord/test/migrations/to_copy/1_people_have_hobbies.rb b/activerecord/test/migrations/to_copy/1_people_have_hobbies.rb
index 76734bcd7d..badccf65cc 100644
--- a/activerecord/test/migrations/to_copy/1_people_have_hobbies.rb
+++ b/activerecord/test/migrations/to_copy/1_people_have_hobbies.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class PeopleHaveHobbies < ActiveRecord::Migration::Current
def self.up
add_column "people", "hobbies", :text
diff --git a/activerecord/test/migrations/to_copy/2_people_have_descriptions.rb b/activerecord/test/migrations/to_copy/2_people_have_descriptions.rb
index 7f883dbb45..1d19d5d6f4 100644
--- a/activerecord/test/migrations/to_copy/2_people_have_descriptions.rb
+++ b/activerecord/test/migrations/to_copy/2_people_have_descriptions.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class PeopleHaveDescriptions < ActiveRecord::Migration::Current
def self.up
add_column "people", "description", :text
diff --git a/activerecord/test/migrations/to_copy2/1_create_articles.rb b/activerecord/test/migrations/to_copy2/1_create_articles.rb
index 2e9f5ec6bc..85c166b319 100644
--- a/activerecord/test/migrations/to_copy2/1_create_articles.rb
+++ b/activerecord/test/migrations/to_copy2/1_create_articles.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class CreateArticles < ActiveRecord::Migration::Current
def self.up
end
diff --git a/activerecord/test/migrations/to_copy2/2_create_comments.rb b/activerecord/test/migrations/to_copy2/2_create_comments.rb
index d361847d4b..1d213a1705 100644
--- a/activerecord/test/migrations/to_copy2/2_create_comments.rb
+++ b/activerecord/test/migrations/to_copy2/2_create_comments.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class CreateComments < ActiveRecord::Migration::Current
def self.up
end
diff --git a/activerecord/test/migrations/to_copy_with_name_collision/1_people_have_hobbies.rb b/activerecord/test/migrations/to_copy_with_name_collision/1_people_have_hobbies.rb
index 1a863367dd..d9fef596f5 100644
--- a/activerecord/test/migrations/to_copy_with_name_collision/1_people_have_hobbies.rb
+++ b/activerecord/test/migrations/to_copy_with_name_collision/1_people_have_hobbies.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class PeopleHaveHobbies < ActiveRecord::Migration::Current
def self.up
add_column "people", "hobbies", :string
diff --git a/activerecord/test/migrations/to_copy_with_timestamps/20090101010101_people_have_hobbies.rb b/activerecord/test/migrations/to_copy_with_timestamps/20090101010101_people_have_hobbies.rb
index 76734bcd7d..badccf65cc 100644
--- a/activerecord/test/migrations/to_copy_with_timestamps/20090101010101_people_have_hobbies.rb
+++ b/activerecord/test/migrations/to_copy_with_timestamps/20090101010101_people_have_hobbies.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class PeopleHaveHobbies < ActiveRecord::Migration::Current
def self.up
add_column "people", "hobbies", :text
diff --git a/activerecord/test/migrations/to_copy_with_timestamps/20090101010202_people_have_descriptions.rb b/activerecord/test/migrations/to_copy_with_timestamps/20090101010202_people_have_descriptions.rb
index 7f883dbb45..1d19d5d6f4 100644
--- a/activerecord/test/migrations/to_copy_with_timestamps/20090101010202_people_have_descriptions.rb
+++ b/activerecord/test/migrations/to_copy_with_timestamps/20090101010202_people_have_descriptions.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class PeopleHaveDescriptions < ActiveRecord::Migration::Current
def self.up
add_column "people", "description", :text
diff --git a/activerecord/test/migrations/to_copy_with_timestamps2/20090101010101_create_articles.rb b/activerecord/test/migrations/to_copy_with_timestamps2/20090101010101_create_articles.rb
index 2e9f5ec6bc..85c166b319 100644
--- a/activerecord/test/migrations/to_copy_with_timestamps2/20090101010101_create_articles.rb
+++ b/activerecord/test/migrations/to_copy_with_timestamps2/20090101010101_create_articles.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class CreateArticles < ActiveRecord::Migration::Current
def self.up
end
diff --git a/activerecord/test/migrations/to_copy_with_timestamps2/20090101010202_create_comments.rb b/activerecord/test/migrations/to_copy_with_timestamps2/20090101010202_create_comments.rb
index d361847d4b..1d213a1705 100644
--- a/activerecord/test/migrations/to_copy_with_timestamps2/20090101010202_create_comments.rb
+++ b/activerecord/test/migrations/to_copy_with_timestamps2/20090101010202_create_comments.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class CreateComments < ActiveRecord::Migration::Current
def self.up
end
diff --git a/activerecord/test/migrations/valid/1_valid_people_have_last_names.rb b/activerecord/test/migrations/valid/1_valid_people_have_last_names.rb
index c450211d8c..3bedcdcdf0 100644
--- a/activerecord/test/migrations/valid/1_valid_people_have_last_names.rb
+++ b/activerecord/test/migrations/valid/1_valid_people_have_last_names.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class ValidPeopleHaveLastNames < ActiveRecord::Migration::Current
def self.up
add_column "people", "last_name", :string
diff --git a/activerecord/test/migrations/valid/2_we_need_reminders.rb b/activerecord/test/migrations/valid/2_we_need_reminders.rb
index d7c63ac892..4647268c6e 100644
--- a/activerecord/test/migrations/valid/2_we_need_reminders.rb
+++ b/activerecord/test/migrations/valid/2_we_need_reminders.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class WeNeedReminders < ActiveRecord::Migration::Current
def self.up
create_table("reminders") do |t|
diff --git a/activerecord/test/migrations/valid/3_innocent_jointable.rb b/activerecord/test/migrations/valid/3_innocent_jointable.rb
index bd3bf21576..8063bc0558 100644
--- a/activerecord/test/migrations/valid/3_innocent_jointable.rb
+++ b/activerecord/test/migrations/valid/3_innocent_jointable.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class InnocentJointable < ActiveRecord::Migration::Current
def self.up
create_table("people_reminders", id: false) do |t|
diff --git a/activerecord/test/migrations/valid_with_subdirectories/1_valid_people_have_last_names.rb b/activerecord/test/migrations/valid_with_subdirectories/1_valid_people_have_last_names.rb
index c450211d8c..3bedcdcdf0 100644
--- a/activerecord/test/migrations/valid_with_subdirectories/1_valid_people_have_last_names.rb
+++ b/activerecord/test/migrations/valid_with_subdirectories/1_valid_people_have_last_names.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class ValidPeopleHaveLastNames < ActiveRecord::Migration::Current
def self.up
add_column "people", "last_name", :string
diff --git a/activerecord/test/migrations/valid_with_subdirectories/sub/2_we_need_reminders.rb b/activerecord/test/migrations/valid_with_subdirectories/sub/2_we_need_reminders.rb
index d7c63ac892..4647268c6e 100644
--- a/activerecord/test/migrations/valid_with_subdirectories/sub/2_we_need_reminders.rb
+++ b/activerecord/test/migrations/valid_with_subdirectories/sub/2_we_need_reminders.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class WeNeedReminders < ActiveRecord::Migration::Current
def self.up
create_table("reminders") do |t|
diff --git a/activerecord/test/migrations/valid_with_subdirectories/sub1/3_innocent_jointable.rb b/activerecord/test/migrations/valid_with_subdirectories/sub1/3_innocent_jointable.rb
index bd3bf21576..8063bc0558 100644
--- a/activerecord/test/migrations/valid_with_subdirectories/sub1/3_innocent_jointable.rb
+++ b/activerecord/test/migrations/valid_with_subdirectories/sub1/3_innocent_jointable.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class InnocentJointable < ActiveRecord::Migration::Current
def self.up
create_table("people_reminders", id: false) do |t|
diff --git a/activerecord/test/migrations/valid_with_timestamps/20100101010101_valid_with_timestamps_people_have_last_names.rb b/activerecord/test/migrations/valid_with_timestamps/20100101010101_valid_with_timestamps_people_have_last_names.rb
index 9fd27593f0..b938847170 100644
--- a/activerecord/test/migrations/valid_with_timestamps/20100101010101_valid_with_timestamps_people_have_last_names.rb
+++ b/activerecord/test/migrations/valid_with_timestamps/20100101010101_valid_with_timestamps_people_have_last_names.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class ValidWithTimestampsPeopleHaveLastNames < ActiveRecord::Migration::Current
def self.up
add_column "people", "last_name", :string
diff --git a/activerecord/test/migrations/valid_with_timestamps/20100201010101_valid_with_timestamps_we_need_reminders.rb b/activerecord/test/migrations/valid_with_timestamps/20100201010101_valid_with_timestamps_we_need_reminders.rb
index 4a59921136..94551e8208 100644
--- a/activerecord/test/migrations/valid_with_timestamps/20100201010101_valid_with_timestamps_we_need_reminders.rb
+++ b/activerecord/test/migrations/valid_with_timestamps/20100201010101_valid_with_timestamps_we_need_reminders.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class ValidWithTimestampsWeNeedReminders < ActiveRecord::Migration::Current
def self.up
create_table("reminders") do |t|
diff --git a/activerecord/test/migrations/valid_with_timestamps/20100301010101_valid_with_timestamps_innocent_jointable.rb b/activerecord/test/migrations/valid_with_timestamps/20100301010101_valid_with_timestamps_innocent_jointable.rb
index be24de6d70..672edc5253 100644
--- a/activerecord/test/migrations/valid_with_timestamps/20100301010101_valid_with_timestamps_innocent_jointable.rb
+++ b/activerecord/test/migrations/valid_with_timestamps/20100301010101_valid_with_timestamps_innocent_jointable.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class ValidWithTimestampsInnocentJointable < ActiveRecord::Migration::Current
def self.up
create_table("people_reminders", id: false) do |t|
diff --git a/activerecord/test/migrations/version_check/20131219224947_migration_version_check.rb b/activerecord/test/migrations/version_check/20131219224947_migration_version_check.rb
index 6f314c881c..91bfbbdfd1 100644
--- a/activerecord/test/migrations/version_check/20131219224947_migration_version_check.rb
+++ b/activerecord/test/migrations/version_check/20131219224947_migration_version_check.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class MigrationVersionCheck < ActiveRecord::Migration::Current
def self.up
raise "incorrect migration version" unless version == 20131219224947
diff --git a/activerecord/test/models/account.rb b/activerecord/test/models/account.rb
new file mode 100644
index 0000000000..0c3cd45a81
--- /dev/null
+++ b/activerecord/test/models/account.rb
@@ -0,0 +1,34 @@
+# frozen_string_literal: true
+
+class Account < ActiveRecord::Base
+ belongs_to :firm, class_name: "Company"
+ belongs_to :unautosaved_firm, foreign_key: "firm_id", class_name: "Firm", autosave: false
+
+ alias_attribute :available_credit, :credit_limit
+
+ def self.destroyed_account_ids
+ @destroyed_account_ids ||= Hash.new { |h, k| h[k] = [] }
+ end
+
+ # Test private kernel method through collection proxy using has_many.
+ def self.open
+ where("firm_name = ?", "37signals")
+ end
+
+ before_destroy do |account|
+ if account.firm
+ Account.destroyed_account_ids[account.firm.id] << account.id
+ end
+ end
+
+ validate :check_empty_credit_limit
+
+ private
+ def check_empty_credit_limit
+ errors.add("credit_limit", :blank) if credit_limit.blank?
+ end
+
+ def private_method
+ "Sir, yes sir!"
+ end
+end
diff --git a/activerecord/test/models/admin.rb b/activerecord/test/models/admin.rb
index bc3ce23447..a40b5a33b2 100644
--- a/activerecord/test/models/admin.rb
+++ b/activerecord/test/models/admin.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Admin
def self.table_name_prefix
"admin_"
diff --git a/activerecord/test/models/admin/account.rb b/activerecord/test/models/admin/account.rb
index bd23192d20..41fe2d782b 100644
--- a/activerecord/test/models/admin/account.rb
+++ b/activerecord/test/models/admin/account.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class Admin::Account < ActiveRecord::Base
has_many :users
end
diff --git a/activerecord/test/models/admin/randomly_named_c1.rb b/activerecord/test/models/admin/randomly_named_c1.rb
index b64ae7fc41..d89b8dd293 100644
--- a/activerecord/test/models/admin/randomly_named_c1.rb
+++ b/activerecord/test/models/admin/randomly_named_c1.rb
@@ -1,7 +1,9 @@
-class Admin::ClassNameThatDoesNotFollowCONVENTIONS1 < ActiveRecord::Base
- self.table_name = :randomly_named_table2
-end
-
-class Admin::ClassNameThatDoesNotFollowCONVENTIONS2 < ActiveRecord::Base
- self.table_name = :randomly_named_table3
-end
+# frozen_string_literal: true
+
+class Admin::ClassNameThatDoesNotFollowCONVENTIONS1 < ActiveRecord::Base
+ self.table_name = :randomly_named_table2
+end
+
+class Admin::ClassNameThatDoesNotFollowCONVENTIONS2 < ActiveRecord::Base
+ self.table_name = :randomly_named_table3
+end
diff --git a/activerecord/test/models/admin/user.rb b/activerecord/test/models/admin/user.rb
index a76e4b6795..abb5cb28e7 100644
--- a/activerecord/test/models/admin/user.rb
+++ b/activerecord/test/models/admin/user.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class Admin::User < ActiveRecord::Base
class Coder
def initialize(default = {})
diff --git a/activerecord/test/models/aircraft.rb b/activerecord/test/models/aircraft.rb
index ebd42ff824..4fdea46cf7 100644
--- a/activerecord/test/models/aircraft.rb
+++ b/activerecord/test/models/aircraft.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class Aircraft < ActiveRecord::Base
self.pluralize_table_names = false
has_many :engines, foreign_key: "car_id"
diff --git a/activerecord/test/models/arunit2_model.rb b/activerecord/test/models/arunit2_model.rb
index 04b8b15d3d..5b0da8a249 100644
--- a/activerecord/test/models/arunit2_model.rb
+++ b/activerecord/test/models/arunit2_model.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class ARUnit2Model < ActiveRecord::Base
self.abstract_class = true
end
diff --git a/activerecord/test/models/author.rb b/activerecord/test/models/author.rb
index 2d9cba77e0..27da886e1c 100644
--- a/activerecord/test/models/author.rb
+++ b/activerecord/test/models/author.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class Author < ActiveRecord::Base
has_many :posts
has_many :serialized_posts
@@ -19,7 +21,8 @@ class Author < ActiveRecord::Base
end
has_many :comments_containing_the_letter_e, through: :posts, source: :comments
has_many :comments_with_order_and_conditions, -> { order("comments.body").where("comments.body like 'Thank%'") }, through: :posts, source: :comments
- has_many :comments_with_include, -> { includes(:post) }, through: :posts, source: :comments
+ has_many :comments_with_include, -> { includes(:post).where(posts: { type: "Post" }) }, through: :posts, source: :comments
+ has_many :comments_for_first_author, -> { for_first_author }, through: :posts, source: :comments
has_many :first_posts
has_many :comments_on_first_posts, -> { order("posts.id desc, comments.id asc") }, through: :first_posts, source: :comments
@@ -38,6 +41,7 @@ class Author < ActiveRecord::Base
class_name: "Post"
has_many :comments_desc, -> { order("comments.id DESC") }, through: :posts, source: :comments
+ has_many :unordered_comments, -> { unscope(:order).distinct }, through: :posts_sorted_by_id_limited, source: :comments
has_many :funky_comments, through: :posts, source: :comments
has_many :ordered_uniq_comments, -> { distinct.order("comments.id") }, through: :posts, source: :comments
has_many :ordered_uniq_comments_desc, -> { distinct.order("comments.id DESC") }, through: :posts, source: :comments
@@ -76,7 +80,7 @@ class Author < ActiveRecord::Base
after_add: [:log_after_adding, Proc.new { |o, r| o.post_log << "after_adding_proc#{r.id || '<new>'}" }]
has_many :unchangeable_posts, class_name: "Post", before_add: :raise_exception, after_add: :log_after_adding
- has_many :categorizations
+ has_many :categorizations, -> {}
has_many :categories, through: :categorizations
has_many :named_categories, through: :categorizations
@@ -84,6 +88,9 @@ class Author < ActiveRecord::Base
has_many :special_categories, through: :special_categorizations, source: :category
has_one :special_category, through: :special_categorizations, source: :category
+ has_many :special_categories_with_conditions, -> { where(categorizations: { special: true }) }, through: :categorizations, source: :category
+ has_many :nonspecial_categories_with_conditions, -> { where(categorizations: { special: false }) }, through: :categorizations, source: :category
+
has_many :categories_like_general, -> { where(name: "General") }, through: :categorizations, source: :category, class_name: "Category"
has_many :categorized_posts, through: :categorizations, source: :post
@@ -97,10 +104,12 @@ class Author < ActiveRecord::Base
has_many :taggings, through: :posts, source: :taggings
has_many :taggings_2, through: :posts, source: :tagging
has_many :tags, through: :posts
+ has_many :ordered_tags, through: :posts
has_many :post_categories, through: :posts, source: :categories
has_many :tagging_tags, through: :taggings, source: :tag
has_many :similar_posts, -> { distinct }, through: :tags, source: :tagged_posts
+ has_many :ordered_posts, -> { distinct }, through: :ordered_tags, source: :tagged_posts
has_many :distinct_tags, -> { select("DISTINCT tags.*").order("tags.name") }, through: :posts, source: :tags
has_many :tags_with_primary_key, through: :posts
diff --git a/activerecord/test/models/auto_id.rb b/activerecord/test/models/auto_id.rb
index 82c6544bd5..fd672603bb 100644
--- a/activerecord/test/models/auto_id.rb
+++ b/activerecord/test/models/auto_id.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class AutoId < ActiveRecord::Base
self.table_name = "auto_id_tests"
self.primary_key = "auto_id"
diff --git a/activerecord/test/models/autoloadable/extra_firm.rb b/activerecord/test/models/autoloadable/extra_firm.rb
index 5578ba0d9b..c46e34c101 100644
--- a/activerecord/test/models/autoloadable/extra_firm.rb
+++ b/activerecord/test/models/autoloadable/extra_firm.rb
@@ -1,2 +1,4 @@
+# frozen_string_literal: true
+
class ExtraFirm < Company
end
diff --git a/activerecord/test/models/binary.rb b/activerecord/test/models/binary.rb
index 39b2f5090a..b93f87519f 100644
--- a/activerecord/test/models/binary.rb
+++ b/activerecord/test/models/binary.rb
@@ -1,2 +1,4 @@
+# frozen_string_literal: true
+
class Binary < ActiveRecord::Base
end
diff --git a/activerecord/test/models/bird.rb b/activerecord/test/models/bird.rb
index 24b839135d..be08636ac6 100644
--- a/activerecord/test/models/bird.rb
+++ b/activerecord/test/models/bird.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class Bird < ActiveRecord::Base
belongs_to :pirate
validates_presence_of :name
diff --git a/activerecord/test/models/book.rb b/activerecord/test/models/book.rb
index 6466e1b341..afdda1a81e 100644
--- a/activerecord/test/models/book.rb
+++ b/activerecord/test/models/book.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class Book < ActiveRecord::Base
belongs_to :author
diff --git a/activerecord/test/models/boolean.rb b/activerecord/test/models/boolean.rb
index 0da228aac2..bee757fb9c 100644
--- a/activerecord/test/models/boolean.rb
+++ b/activerecord/test/models/boolean.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class Boolean < ActiveRecord::Base
def has_fun
super
diff --git a/activerecord/test/models/bulb.rb b/activerecord/test/models/bulb.rb
index 113d21cb84..ab92f7025d 100644
--- a/activerecord/test/models/bulb.rb
+++ b/activerecord/test/models/bulb.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class Bulb < ActiveRecord::Base
default_scope { where(name: "defaulty") }
belongs_to :car, touch: true
diff --git a/activerecord/test/models/cake_designer.rb b/activerecord/test/models/cake_designer.rb
index 9c57ef573a..0b2a00edfd 100644
--- a/activerecord/test/models/cake_designer.rb
+++ b/activerecord/test/models/cake_designer.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class CakeDesigner < ActiveRecord::Base
has_one :chef, as: :employable
end
diff --git a/activerecord/test/models/car.rb b/activerecord/test/models/car.rb
index 92bff7ff96..3d6a7a96c2 100644
--- a/activerecord/test/models/car.rb
+++ b/activerecord/test/models/car.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class Car < ActiveRecord::Base
has_many :bulbs
has_many :all_bulbs, -> { unscope where: :name }, class_name: "Bulb"
diff --git a/activerecord/test/models/carrier.rb b/activerecord/test/models/carrier.rb
index 230be118c3..995a9d3bef 100644
--- a/activerecord/test/models/carrier.rb
+++ b/activerecord/test/models/carrier.rb
@@ -1,2 +1,4 @@
+# frozen_string_literal: true
+
class Carrier < ActiveRecord::Base
end
diff --git a/activerecord/test/models/cat.rb b/activerecord/test/models/cat.rb
index dfdde18641..43013964b6 100644
--- a/activerecord/test/models/cat.rb
+++ b/activerecord/test/models/cat.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class Cat < ActiveRecord::Base
self.abstract_class = true
diff --git a/activerecord/test/models/categorization.rb b/activerecord/test/models/categorization.rb
index b99383d0b1..68b0ea90d3 100644
--- a/activerecord/test/models/categorization.rb
+++ b/activerecord/test/models/categorization.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class Categorization < ActiveRecord::Base
belongs_to :post
belongs_to :category, counter_cache: true
diff --git a/activerecord/test/models/category.rb b/activerecord/test/models/category.rb
index 4b2840c653..2ccc00bed9 100644
--- a/activerecord/test/models/category.rb
+++ b/activerecord/test/models/category.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class Category < ActiveRecord::Base
has_and_belongs_to_many :posts
has_and_belongs_to_many :special_posts, class_name: "Post"
diff --git a/activerecord/test/models/chef.rb b/activerecord/test/models/chef.rb
index 9d3dd01016..ff528644bc 100644
--- a/activerecord/test/models/chef.rb
+++ b/activerecord/test/models/chef.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class Chef < ActiveRecord::Base
belongs_to :employable, polymorphic: true
has_many :recipes
diff --git a/activerecord/test/models/citation.rb b/activerecord/test/models/citation.rb
index 7d06387f56..3d786f27eb 100644
--- a/activerecord/test/models/citation.rb
+++ b/activerecord/test/models/citation.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class Citation < ActiveRecord::Base
belongs_to :reference_of, class_name: "Book", foreign_key: :book2_id
end
diff --git a/activerecord/test/models/club.rb b/activerecord/test/models/club.rb
index 49d7b24a3b..2006e05fcf 100644
--- a/activerecord/test/models/club.rb
+++ b/activerecord/test/models/club.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class Club < ActiveRecord::Base
has_one :membership
has_many :memberships, inverse_of: false
@@ -8,6 +10,8 @@ class Club < ActiveRecord::Base
has_many :favourites, -> { where(memberships: { favourite: true }) }, through: :memberships, source: :member
+ scope :general, -> { left_joins(:category).where(categories: { name: "General" }) }
+
private
def private_method
diff --git a/activerecord/test/models/college.rb b/activerecord/test/models/college.rb
index c9dbe1ecc2..52017dda42 100644
--- a/activerecord/test/models/college.rb
+++ b/activerecord/test/models/college.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require_dependency "models/arunit2_model"
require "active_support/core_ext/object/with_options"
diff --git a/activerecord/test/models/column.rb b/activerecord/test/models/column.rb
index 499358b4cf..d3cd419a00 100644
--- a/activerecord/test/models/column.rb
+++ b/activerecord/test/models/column.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class Column < ActiveRecord::Base
belongs_to :record
end
diff --git a/activerecord/test/models/column_name.rb b/activerecord/test/models/column_name.rb
index 460eb4fe20..c6047c507b 100644
--- a/activerecord/test/models/column_name.rb
+++ b/activerecord/test/models/column_name.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class ColumnName < ActiveRecord::Base
self.table_name = "colnametests"
end
diff --git a/activerecord/test/models/comment.rb b/activerecord/test/models/comment.rb
index eecf923046..5ab433f2d9 100644
--- a/activerecord/test/models/comment.rb
+++ b/activerecord/test/models/comment.rb
@@ -1,3 +1,8 @@
+# frozen_string_literal: true
+
+# `counter_cache` requires association class before `attr_readonly`.
+class Post < ActiveRecord::Base; end
+
class Comment < ActiveRecord::Base
scope :limit_by, lambda { |l| limit(l) }
scope :containing_the_letter_e, -> { where("comments.body LIKE '%e%'") }
@@ -54,6 +59,11 @@ class Comment < ActiveRecord::Base
end
class SpecialComment < Comment
+ default_scope { where(deleted_at: nil) }
+
+ def self.what_are_you
+ "a special comment..."
+ end
end
class SubSpecialComment < SpecialComment
@@ -74,3 +84,9 @@ class CommentWithDefaultScopeReferencesAssociation < Comment
default_scope -> { includes(:developer).order("developers.name").references(:developer) }
belongs_to :developer
end
+
+class CommentWithAfterCreateUpdate < Comment
+ after_create do
+ update_attributes(body: "bar")
+ end
+end
diff --git a/activerecord/test/models/company.rb b/activerecord/test/models/company.rb
index c6a5bf1c92..fc6488f729 100644
--- a/activerecord/test/models/company.rb
+++ b/activerecord/test/models/company.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class AbstractCompany < ActiveRecord::Base
self.abstract_class = true
end
@@ -85,6 +87,8 @@ class Firm < Company
has_many :association_with_references, -> { references(:foo) }, class_name: "Client"
+ has_many :developers_with_select, -> { select("id, name, first_name") }, class_name: "Developer"
+
has_one :lead_developer, class_name: "Developer"
has_many :projects
@@ -185,37 +189,4 @@ end
class VerySpecialClient < SpecialClient
end
-class Account < ActiveRecord::Base
- belongs_to :firm, class_name: "Company"
- belongs_to :unautosaved_firm, foreign_key: "firm_id", class_name: "Firm", autosave: false
-
- alias_attribute :available_credit, :credit_limit
-
- def self.destroyed_account_ids
- @destroyed_account_ids ||= Hash.new { |h, k| h[k] = [] }
- end
-
- # Test private kernel method through collection proxy using has_many.
- def self.open
- where("firm_name = ?", "37signals")
- end
-
- before_destroy do |account|
- if account.firm
- Account.destroyed_account_ids[account.firm.id] << account.id
- end
- true
- end
-
- validate :check_empty_credit_limit
-
- private
-
- def check_empty_credit_limit
- errors.add("credit_limit", :blank) if credit_limit.blank?
- end
-
- def private_method
- "Sir, yes sir!"
- end
-end
+require "models/account"
diff --git a/activerecord/test/models/company_in_module.rb b/activerecord/test/models/company_in_module.rb
index 0782c1eff4..52b7e06a63 100644
--- a/activerecord/test/models/company_in_module.rb
+++ b/activerecord/test/models/company_in_module.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "active_support/core_ext/object/with_options"
module MyApplication
diff --git a/activerecord/test/models/computer.rb b/activerecord/test/models/computer.rb
index 1c9856e1af..582b4a38b5 100644
--- a/activerecord/test/models/computer.rb
+++ b/activerecord/test/models/computer.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class Computer < ActiveRecord::Base
belongs_to :developer, foreign_key: "developer"
end
diff --git a/activerecord/test/models/contact.rb b/activerecord/test/models/contact.rb
index 47bbbbfd8b..6e02ff199b 100644
--- a/activerecord/test/models/contact.rb
+++ b/activerecord/test/models/contact.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module ContactFakeColumns
def self.extended(base)
base.class_eval do
diff --git a/activerecord/test/models/content.rb b/activerecord/test/models/content.rb
index 68db2127d8..14bbee53d8 100644
--- a/activerecord/test/models/content.rb
+++ b/activerecord/test/models/content.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class Content < ActiveRecord::Base
self.table_name = "content"
has_one :content_position, dependent: :destroy
diff --git a/activerecord/test/models/contract.rb b/activerecord/test/models/contract.rb
index 32bd581377..f273badd85 100644
--- a/activerecord/test/models/contract.rb
+++ b/activerecord/test/models/contract.rb
@@ -1,6 +1,8 @@
+# frozen_string_literal: true
+
class Contract < ActiveRecord::Base
belongs_to :company
- belongs_to :developer
+ belongs_to :developer, primary_key: :id
belongs_to :firm, foreign_key: "company_id"
before_save :hi
diff --git a/activerecord/test/models/country.rb b/activerecord/test/models/country.rb
index 7912719ddd..0c84a40de2 100644
--- a/activerecord/test/models/country.rb
+++ b/activerecord/test/models/country.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class Country < ActiveRecord::Base
self.primary_key = :country_id
diff --git a/activerecord/test/models/course.rb b/activerecord/test/models/course.rb
index 348f2bf1e0..4f346124ea 100644
--- a/activerecord/test/models/course.rb
+++ b/activerecord/test/models/course.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require_dependency "models/arunit2_model"
class Course < ARUnit2Model
diff --git a/activerecord/test/models/customer.rb b/activerecord/test/models/customer.rb
index 3d40cb1ace..bc501a5ce0 100644
--- a/activerecord/test/models/customer.rb
+++ b/activerecord/test/models/customer.rb
@@ -1,8 +1,10 @@
+# frozen_string_literal: true
+
class Customer < ActiveRecord::Base
cattr_accessor :gps_conversion_was_run
composed_of :address, mapping: [ %w(address_street street), %w(address_city city), %w(address_country country) ], allow_nil: true
- composed_of :balance, class_name: "Money", mapping: %w(balance amount), converter: Proc.new(&:to_money)
+ composed_of :balance, class_name: "Money", mapping: %w(balance amount)
composed_of :gps_location, allow_nil: true
composed_of :non_blank_gps_location, class_name: "GpsLocation", allow_nil: true, mapping: %w(gps_location gps_location),
converter: lambda { |gps| self.gps_conversion_was_run = true; gps.blank? ? nil : GpsLocation.new(gps) }
diff --git a/activerecord/test/models/customer_carrier.rb b/activerecord/test/models/customer_carrier.rb
index 37186903ff..6cb9d5239d 100644
--- a/activerecord/test/models/customer_carrier.rb
+++ b/activerecord/test/models/customer_carrier.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class CustomerCarrier < ActiveRecord::Base
cattr_accessor :current_customer
diff --git a/activerecord/test/models/dashboard.rb b/activerecord/test/models/dashboard.rb
index 1b3b54545f..d25ceeafb1 100644
--- a/activerecord/test/models/dashboard.rb
+++ b/activerecord/test/models/dashboard.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class Dashboard < ActiveRecord::Base
self.primary_key = :dashboard_id
end
diff --git a/activerecord/test/models/default.rb b/activerecord/test/models/default.rb
index 887e9cc999..90f1046d87 100644
--- a/activerecord/test/models/default.rb
+++ b/activerecord/test/models/default.rb
@@ -1,2 +1,4 @@
+# frozen_string_literal: true
+
class Default < ActiveRecord::Base
end
diff --git a/activerecord/test/models/department.rb b/activerecord/test/models/department.rb
index 08004a0ed3..868b9bf4bf 100644
--- a/activerecord/test/models/department.rb
+++ b/activerecord/test/models/department.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class Department < ActiveRecord::Base
has_many :chefs
belongs_to :hotel
diff --git a/activerecord/test/models/developer.rb b/activerecord/test/models/developer.rb
index 654830ba11..8881c69368 100644
--- a/activerecord/test/models/developer.rb
+++ b/activerecord/test/models/developer.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "ostruct"
module DeveloperProjectsAssociationExtension2
@@ -85,6 +87,17 @@ class Developer < ActiveRecord::Base
private :track_instance_count
end
+class SubDeveloper < Developer
+end
+
+class SymbolIgnoredDeveloper < ActiveRecord::Base
+ self.table_name = "developers"
+ self.ignored_columns = [:first_name, :last_name]
+
+ attr_accessor :last_name
+ define_attribute_method "last_name"
+end
+
class AuditLog < ActiveRecord::Base
belongs_to :developer, validate: true
belongs_to :unvalidated_developer, class_name: "Developer"
diff --git a/activerecord/test/models/dog.rb b/activerecord/test/models/dog.rb
index b02b8447b8..75d284ac25 100644
--- a/activerecord/test/models/dog.rb
+++ b/activerecord/test/models/dog.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class Dog < ActiveRecord::Base
belongs_to :breeder, class_name: "DogLover", counter_cache: :bred_dogs_count
belongs_to :trainer, class_name: "DogLover", counter_cache: :trained_dogs_count
diff --git a/activerecord/test/models/dog_lover.rb b/activerecord/test/models/dog_lover.rb
index 2c5be94aea..aabe914f77 100644
--- a/activerecord/test/models/dog_lover.rb
+++ b/activerecord/test/models/dog_lover.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class DogLover < ActiveRecord::Base
has_many :trained_dogs, class_name: "Dog", foreign_key: :trainer_id, dependent: :destroy
has_many :bred_dogs, class_name: "Dog", foreign_key: :breeder_id
diff --git a/activerecord/test/models/doubloon.rb b/activerecord/test/models/doubloon.rb
index 7272504666..febadc3a5a 100644
--- a/activerecord/test/models/doubloon.rb
+++ b/activerecord/test/models/doubloon.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class AbstractDoubloon < ActiveRecord::Base
# This has functionality that might be shared by multiple classes.
diff --git a/activerecord/test/models/drink_designer.rb b/activerecord/test/models/drink_designer.rb
index 2db968ef11..eb6701b84e 100644
--- a/activerecord/test/models/drink_designer.rb
+++ b/activerecord/test/models/drink_designer.rb
@@ -1,3 +1,8 @@
+# frozen_string_literal: true
+
class DrinkDesigner < ActiveRecord::Base
has_one :chef, as: :employable
end
+
+class MocktailDesigner < DrinkDesigner
+end
diff --git a/activerecord/test/models/edge.rb b/activerecord/test/models/edge.rb
index e61d25c9bc..a04ab103de 100644
--- a/activerecord/test/models/edge.rb
+++ b/activerecord/test/models/edge.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# This class models an edge in a directed graph.
class Edge < ActiveRecord::Base
belongs_to :source, class_name: "Vertex", foreign_key: "source_id"
diff --git a/activerecord/test/models/electron.rb b/activerecord/test/models/electron.rb
index 6fc270673f..902006b314 100644
--- a/activerecord/test/models/electron.rb
+++ b/activerecord/test/models/electron.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class Electron < ActiveRecord::Base
belongs_to :molecule
diff --git a/activerecord/test/models/engine.rb b/activerecord/test/models/engine.rb
index eada171f6a..396a52b3b9 100644
--- a/activerecord/test/models/engine.rb
+++ b/activerecord/test/models/engine.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class Engine < ActiveRecord::Base
belongs_to :my_car, class_name: "Car", foreign_key: "car_id", counter_cache: :engines_count
end
diff --git a/activerecord/test/models/entrant.rb b/activerecord/test/models/entrant.rb
index 4682ce48c8..2c086e451f 100644
--- a/activerecord/test/models/entrant.rb
+++ b/activerecord/test/models/entrant.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class Entrant < ActiveRecord::Base
belongs_to :course
end
diff --git a/activerecord/test/models/essay.rb b/activerecord/test/models/essay.rb
index 1f9772870e..e59db4d877 100644
--- a/activerecord/test/models/essay.rb
+++ b/activerecord/test/models/essay.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class Essay < ActiveRecord::Base
belongs_to :author
belongs_to :writer, primary_key: :name, polymorphic: true
diff --git a/activerecord/test/models/event.rb b/activerecord/test/models/event.rb
index 365ab32b0b..a7cdc39e5c 100644
--- a/activerecord/test/models/event.rb
+++ b/activerecord/test/models/event.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class Event < ActiveRecord::Base
validates_uniqueness_of :title
end
diff --git a/activerecord/test/models/eye.rb b/activerecord/test/models/eye.rb
index f53c34e4b1..f3608b62ef 100644
--- a/activerecord/test/models/eye.rb
+++ b/activerecord/test/models/eye.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class Eye < ActiveRecord::Base
attr_reader :after_create_callbacks_stack
attr_reader :after_update_callbacks_stack
diff --git a/activerecord/test/models/face.rb b/activerecord/test/models/face.rb
index 5913bfa969..948435136d 100644
--- a/activerecord/test/models/face.rb
+++ b/activerecord/test/models/face.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class Face < ActiveRecord::Base
belongs_to :man, inverse_of: :face
belongs_to :polymorphic_man, polymorphic: true, inverse_of: :polymorphic_face
@@ -6,4 +8,8 @@ class Face < ActiveRecord::Base
# These is a "broken" inverse_of for the purposes of testing
belongs_to :horrible_man, class_name: "Man", inverse_of: :horrible_face
belongs_to :horrible_polymorphic_man, polymorphic: true, inverse_of: :horrible_polymorphic_face
+
+ validate do
+ man
+ end
end
diff --git a/activerecord/test/models/family.rb b/activerecord/test/models/family.rb
index 5ae5a78c95..0713dba1a6 100644
--- a/activerecord/test/models/family.rb
+++ b/activerecord/test/models/family.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class Family < ActiveRecord::Base
has_many :family_trees, -> { where(token: nil) }
has_many :members, through: :family_trees
diff --git a/activerecord/test/models/family_tree.rb b/activerecord/test/models/family_tree.rb
index cd9829fedd..a8ea907c05 100644
--- a/activerecord/test/models/family_tree.rb
+++ b/activerecord/test/models/family_tree.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class FamilyTree < ActiveRecord::Base
belongs_to :member, class_name: "User", foreign_key: "member_id"
belongs_to :family
diff --git a/activerecord/test/models/friendship.rb b/activerecord/test/models/friendship.rb
index 578382b494..9f1712a8ec 100644
--- a/activerecord/test/models/friendship.rb
+++ b/activerecord/test/models/friendship.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class Friendship < ActiveRecord::Base
belongs_to :friend, class_name: "Person"
# friend_too exists to test a bug, and probably shouldn't be used elsewhere
diff --git a/activerecord/test/models/guid.rb b/activerecord/test/models/guid.rb
index 05653ba498..ec71c37690 100644
--- a/activerecord/test/models/guid.rb
+++ b/activerecord/test/models/guid.rb
@@ -1,2 +1,4 @@
+# frozen_string_literal: true
+
class Guid < ActiveRecord::Base
end
diff --git a/activerecord/test/models/guitar.rb b/activerecord/test/models/guitar.rb
index cd068ff53d..649b998665 100644
--- a/activerecord/test/models/guitar.rb
+++ b/activerecord/test/models/guitar.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class Guitar < ActiveRecord::Base
has_many :tuning_pegs, index_errors: true
accepts_nested_attributes_for :tuning_pegs
diff --git a/activerecord/test/models/hotel.rb b/activerecord/test/models/hotel.rb
index 7bc717c891..1a433c3cab 100644
--- a/activerecord/test/models/hotel.rb
+++ b/activerecord/test/models/hotel.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class Hotel < ActiveRecord::Base
has_many :departments
has_many :chefs, through: :departments
diff --git a/activerecord/test/models/image.rb b/activerecord/test/models/image.rb
index 7ae8e4a7f6..b4808293cc 100644
--- a/activerecord/test/models/image.rb
+++ b/activerecord/test/models/image.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class Image < ActiveRecord::Base
belongs_to :imageable, foreign_key: :imageable_identifier, foreign_type: :imageable_class
end
diff --git a/activerecord/test/models/interest.rb b/activerecord/test/models/interest.rb
index ec79416ee7..899b8f9b9d 100644
--- a/activerecord/test/models/interest.rb
+++ b/activerecord/test/models/interest.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class Interest < ActiveRecord::Base
belongs_to :man, inverse_of: :interests
belongs_to :polymorphic_man, polymorphic: true, inverse_of: :polymorphic_interests
diff --git a/activerecord/test/models/invoice.rb b/activerecord/test/models/invoice.rb
index 4be5a00193..1851792ed5 100644
--- a/activerecord/test/models/invoice.rb
+++ b/activerecord/test/models/invoice.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class Invoice < ActiveRecord::Base
has_many :line_items, autosave: true
before_save { |record| record.balance = record.line_items.map(&:amount).sum }
diff --git a/activerecord/test/models/item.rb b/activerecord/test/models/item.rb
index 336fb1769a..8d079d56e6 100644
--- a/activerecord/test/models/item.rb
+++ b/activerecord/test/models/item.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class AbstractItem < ActiveRecord::Base
self.abstract_class = true
has_one :tagging, as: :taggable
diff --git a/activerecord/test/models/job.rb b/activerecord/test/models/job.rb
index bbaef2792c..52817a8435 100644
--- a/activerecord/test/models/job.rb
+++ b/activerecord/test/models/job.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class Job < ActiveRecord::Base
has_many :references
has_many :people, through: :references
diff --git a/activerecord/test/models/joke.rb b/activerecord/test/models/joke.rb
index eeb5818a1f..436ffb6471 100644
--- a/activerecord/test/models/joke.rb
+++ b/activerecord/test/models/joke.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class Joke < ActiveRecord::Base
self.table_name = "funny_jokes"
end
diff --git a/activerecord/test/models/keyboard.rb b/activerecord/test/models/keyboard.rb
index bcede53ec9..d200e0fb56 100644
--- a/activerecord/test/models/keyboard.rb
+++ b/activerecord/test/models/keyboard.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class Keyboard < ActiveRecord::Base
self.primary_key = "key_number"
end
diff --git a/activerecord/test/models/legacy_thing.rb b/activerecord/test/models/legacy_thing.rb
index eead181a0e..e0210c8922 100644
--- a/activerecord/test/models/legacy_thing.rb
+++ b/activerecord/test/models/legacy_thing.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class LegacyThing < ActiveRecord::Base
self.locking_column = :version
end
diff --git a/activerecord/test/models/lesson.rb b/activerecord/test/models/lesson.rb
index 4c88153068..e546339689 100644
--- a/activerecord/test/models/lesson.rb
+++ b/activerecord/test/models/lesson.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class LessonError < Exception
end
diff --git a/activerecord/test/models/line_item.rb b/activerecord/test/models/line_item.rb
index 93f7cceb13..3a51cf03b2 100644
--- a/activerecord/test/models/line_item.rb
+++ b/activerecord/test/models/line_item.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class LineItem < ActiveRecord::Base
belongs_to :invoice, touch: true
end
diff --git a/activerecord/test/models/liquid.rb b/activerecord/test/models/liquid.rb
index 69d4d7df1a..b2fd305d66 100644
--- a/activerecord/test/models/liquid.rb
+++ b/activerecord/test/models/liquid.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class Liquid < ActiveRecord::Base
self.table_name = :liquid
has_many :molecules, -> { distinct }
diff --git a/activerecord/test/models/man.rb b/activerecord/test/models/man.rb
index d2436a735c..3acd89a48e 100644
--- a/activerecord/test/models/man.rb
+++ b/activerecord/test/models/man.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class Man < ActiveRecord::Base
has_one :face, inverse_of: :man
has_one :polymorphic_face, class_name: "Face", as: :polymorphic_man, inverse_of: :polymorphic_man
diff --git a/activerecord/test/models/matey.rb b/activerecord/test/models/matey.rb
index 80ee5f47c5..a77ac21e96 100644
--- a/activerecord/test/models/matey.rb
+++ b/activerecord/test/models/matey.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class Matey < ActiveRecord::Base
belongs_to :pirate
belongs_to :target, class_name: "Pirate"
diff --git a/activerecord/test/models/member.rb b/activerecord/test/models/member.rb
index 36f2461b84..4315ba1941 100644
--- a/activerecord/test/models/member.rb
+++ b/activerecord/test/models/member.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class Member < ActiveRecord::Base
has_one :current_membership
has_one :selected_membership
@@ -22,6 +24,7 @@ class Member < ActiveRecord::Base
has_many :organization_member_details_2, through: :organization, source: :member_details
has_one :club_category, through: :club, source: :category
+ has_one :general_club, -> { general }, through: :current_membership, source: :club
has_many :current_memberships, -> { where favourite: true }
has_many :clubs, through: :current_memberships
diff --git a/activerecord/test/models/member_detail.rb b/activerecord/test/models/member_detail.rb
index 157130986c..87f7aab9a2 100644
--- a/activerecord/test/models/member_detail.rb
+++ b/activerecord/test/models/member_detail.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class MemberDetail < ActiveRecord::Base
belongs_to :member, inverse_of: false
belongs_to :organization
diff --git a/activerecord/test/models/member_type.rb b/activerecord/test/models/member_type.rb
index a13561c72a..b49b168d03 100644
--- a/activerecord/test/models/member_type.rb
+++ b/activerecord/test/models/member_type.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class MemberType < ActiveRecord::Base
has_many :members
end
diff --git a/activerecord/test/models/membership.rb b/activerecord/test/models/membership.rb
index 2c3ad230a7..09ee7544b3 100644
--- a/activerecord/test/models/membership.rb
+++ b/activerecord/test/models/membership.rb
@@ -1,4 +1,7 @@
+# frozen_string_literal: true
+
class Membership < ActiveRecord::Base
+ enum type: %i(Membership CurrentMembership SuperMembership SelectedMembership TenantMembership)
belongs_to :member
belongs_to :club
end
diff --git a/activerecord/test/models/mentor.rb b/activerecord/test/models/mentor.rb
index 66504b4e91..2fbb62c435 100644
--- a/activerecord/test/models/mentor.rb
+++ b/activerecord/test/models/mentor.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class Mentor < ActiveRecord::Base
has_many :developers
end
diff --git a/activerecord/test/models/minimalistic.rb b/activerecord/test/models/minimalistic.rb
index 2e3f8e081a..c67b086853 100644
--- a/activerecord/test/models/minimalistic.rb
+++ b/activerecord/test/models/minimalistic.rb
@@ -1,2 +1,4 @@
+# frozen_string_literal: true
+
class Minimalistic < ActiveRecord::Base
end
diff --git a/activerecord/test/models/minivan.rb b/activerecord/test/models/minivan.rb
index e9b05dadf2..d9d331798a 100644
--- a/activerecord/test/models/minivan.rb
+++ b/activerecord/test/models/minivan.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class Minivan < ActiveRecord::Base
self.primary_key = :minivan_id
diff --git a/activerecord/test/models/mixed_case_monkey.rb b/activerecord/test/models/mixed_case_monkey.rb
index 1c35006665..8e92f68817 100644
--- a/activerecord/test/models/mixed_case_monkey.rb
+++ b/activerecord/test/models/mixed_case_monkey.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class MixedCaseMonkey < ActiveRecord::Base
belongs_to :man
end
diff --git a/activerecord/test/models/mocktail_designer.rb b/activerecord/test/models/mocktail_designer.rb
deleted file mode 100644
index 77b44651a3..0000000000
--- a/activerecord/test/models/mocktail_designer.rb
+++ /dev/null
@@ -1,2 +0,0 @@
-class MocktailDesigner < DrinkDesigner
-end
diff --git a/activerecord/test/models/molecule.rb b/activerecord/test/models/molecule.rb
index 26870c8f88..7da08a85c4 100644
--- a/activerecord/test/models/molecule.rb
+++ b/activerecord/test/models/molecule.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class Molecule < ActiveRecord::Base
belongs_to :liquid
has_many :electrons
diff --git a/activerecord/test/models/movie.rb b/activerecord/test/models/movie.rb
index 0302abad1e..fa2ea900c7 100644
--- a/activerecord/test/models/movie.rb
+++ b/activerecord/test/models/movie.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class Movie < ActiveRecord::Base
self.primary_key = "movieid"
diff --git a/activerecord/test/models/node.rb b/activerecord/test/models/node.rb
index 459ea8cf95..ae46c76b46 100644
--- a/activerecord/test/models/node.rb
+++ b/activerecord/test/models/node.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class Node < ActiveRecord::Base
belongs_to :tree, touch: true
belongs_to :parent, class_name: "Node", touch: true, optional: true
diff --git a/activerecord/test/models/non_primary_key.rb b/activerecord/test/models/non_primary_key.rb
index 1cafb09608..e954375989 100644
--- a/activerecord/test/models/non_primary_key.rb
+++ b/activerecord/test/models/non_primary_key.rb
@@ -1,2 +1,4 @@
+# frozen_string_literal: true
+
class NonPrimaryKey < ActiveRecord::Base
end
diff --git a/activerecord/test/models/notification.rb b/activerecord/test/models/notification.rb
index 82edc64b68..3f8728af5e 100644
--- a/activerecord/test/models/notification.rb
+++ b/activerecord/test/models/notification.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class Notification < ActiveRecord::Base
validates_presence_of :message
end
diff --git a/activerecord/test/models/numeric_data.rb b/activerecord/test/models/numeric_data.rb
index c6e025a9ce..666e1a5778 100644
--- a/activerecord/test/models/numeric_data.rb
+++ b/activerecord/test/models/numeric_data.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class NumericData < ActiveRecord::Base
self.table_name = "numeric_data"
# Decimal columns with 0 scale being automatically treated as integers
diff --git a/activerecord/test/models/order.rb b/activerecord/test/models/order.rb
index 699be53959..36866b398f 100644
--- a/activerecord/test/models/order.rb
+++ b/activerecord/test/models/order.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class Order < ActiveRecord::Base
belongs_to :billing, class_name: "Customer", foreign_key: "billing_customer_id"
belongs_to :shipping, class_name: "Customer", foreign_key: "shipping_customer_id"
diff --git a/activerecord/test/models/organization.rb b/activerecord/test/models/organization.rb
index 462830dadc..099e7e38e0 100644
--- a/activerecord/test/models/organization.rb
+++ b/activerecord/test/models/organization.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class Organization < ActiveRecord::Base
has_many :member_details
has_many :members, through: :member_details
diff --git a/activerecord/test/models/other_dog.rb b/activerecord/test/models/other_dog.rb
index 418caf34be..a0fda5ae1b 100644
--- a/activerecord/test/models/other_dog.rb
+++ b/activerecord/test/models/other_dog.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require_dependency "models/arunit2_model"
class OtherDog < ARUnit2Model
diff --git a/activerecord/test/models/owner.rb b/activerecord/test/models/owner.rb
index 21fc9b6eb8..5fa50d9918 100644
--- a/activerecord/test/models/owner.rb
+++ b/activerecord/test/models/owner.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class Owner < ActiveRecord::Base
self.primary_key = :owner_id
has_many :pets, -> { order "pets.name desc" }
diff --git a/activerecord/test/models/parrot.rb b/activerecord/test/models/parrot.rb
index 1e5f9285a8..ba9ddb8c6a 100644
--- a/activerecord/test/models/parrot.rb
+++ b/activerecord/test/models/parrot.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class Parrot < ActiveRecord::Base
self.inheritance_column = :parrot_sti_class
@@ -8,7 +10,7 @@ class Parrot < ActiveRecord::Base
validates_presence_of :name
- attr_accessor :cancel_save_from_callback
+ attribute :cancel_save_from_callback
before_save :cancel_save_callback_method, if: :cancel_save_from_callback
def cancel_save_callback_method
throw(:abort)
diff --git a/activerecord/test/models/person.rb b/activerecord/test/models/person.rb
index 18994d6f18..5cba1e440e 100644
--- a/activerecord/test/models/person.rb
+++ b/activerecord/test/models/person.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class Person < ActiveRecord::Base
has_many :readers
has_many :secure_readers
diff --git a/activerecord/test/models/personal_legacy_thing.rb b/activerecord/test/models/personal_legacy_thing.rb
index adde7a504a..ed8b70cfcc 100644
--- a/activerecord/test/models/personal_legacy_thing.rb
+++ b/activerecord/test/models/personal_legacy_thing.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class PersonalLegacyThing < ActiveRecord::Base
self.locking_column = :version
belongs_to :person, counter_cache: true
diff --git a/activerecord/test/models/pet.rb b/activerecord/test/models/pet.rb
index 51a3e42815..9bda2109e6 100644
--- a/activerecord/test/models/pet.rb
+++ b/activerecord/test/models/pet.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class Pet < ActiveRecord::Base
attr_accessor :current_user
diff --git a/activerecord/test/models/pet_treasure.rb b/activerecord/test/models/pet_treasure.rb
index 1fe7807ffe..47b9f57fad 100644
--- a/activerecord/test/models/pet_treasure.rb
+++ b/activerecord/test/models/pet_treasure.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class PetTreasure < ActiveRecord::Base
self.table_name = "pets_treasures"
diff --git a/activerecord/test/models/pirate.rb b/activerecord/test/models/pirate.rb
index c532ab426e..c8617d1cfe 100644
--- a/activerecord/test/models/pirate.rb
+++ b/activerecord/test/models/pirate.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class Pirate < ActiveRecord::Base
belongs_to :parrot, validate: true
belongs_to :non_validated_parrot, class_name: "Parrot"
diff --git a/activerecord/test/models/possession.rb b/activerecord/test/models/possession.rb
index 0226336c16..9b843e1525 100644
--- a/activerecord/test/models/possession.rb
+++ b/activerecord/test/models/possession.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class Possession < ActiveRecord::Base
self.table_name = "having"
end
diff --git a/activerecord/test/models/post.rb b/activerecord/test/models/post.rb
index 4c913b3b72..54eb5e6783 100644
--- a/activerecord/test/models/post.rb
+++ b/activerecord/test/models/post.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class Post < ActiveRecord::Base
class CategoryPost < ActiveRecord::Base
self.table_name = "categories_posts"
@@ -19,9 +21,10 @@ class Post < ActiveRecord::Base
scope :containing_the_letter_a, -> { where("body LIKE '%a%'") }
scope :titled_with_an_apostrophe, -> { where("title LIKE '%''%'") }
- scope :ranked_by_comments, -> { order("comments_count DESC") }
+ scope :ranked_by_comments, -> { order(arel_attribute(:comments_count).desc) }
scope :limit_by, lambda { |l| limit(l) }
+ scope :locked, -> { lock }
belongs_to :author
belongs_to :readonly_author, -> { readonly }, class_name: "Author", foreign_key: :author_id
@@ -103,6 +106,9 @@ class Post < ActiveRecord::Base
end
end
+ has_many :indestructible_taggings, as: :taggable, counter_cache: :indestructible_tags_count
+ has_many :indestructible_tags, through: :indestructible_taggings, source: :tag
+
has_many :taggings_with_delete_all, class_name: "Tagging", as: :taggable, dependent: :delete_all, counter_cache: :taggings_with_delete_all_count
has_many :taggings_with_destroy, class_name: "Tagging", as: :taggable, dependent: :destroy, counter_cache: :taggings_with_destroy_count
@@ -112,6 +118,7 @@ class Post < ActiveRecord::Base
has_many :misc_tags, -> { where tags: { name: "Misc" } }, through: :taggings, source: :tag
has_many :funky_tags, through: :taggings, source: :tag
has_many :super_tags, through: :taggings
+ has_many :ordered_tags, through: :taggings
has_many :tags_with_primary_key, through: :taggings, source: :tag_with_primary_key
has_one :tagging, as: :taggable
@@ -181,14 +188,19 @@ end
class SpecialPost < Post; end
class StiPost < Post
- self.abstract_class = true
has_one :special_comment, class_name: "SpecialComment"
end
+class AbstractStiPost < Post
+ self.abstract_class = true
+end
+
class SubStiPost < StiPost
self.table_name = Post.table_name
end
+class SubAbstractStiPost < AbstractStiPost; end
+
class FirstPost < ActiveRecord::Base
self.inheritance_column = :disabled
self.table_name = "posts"
@@ -198,6 +210,11 @@ class FirstPost < ActiveRecord::Base
has_one :comment, foreign_key: :post_id
end
+class TaggedPost < Post
+ has_many :taggings, -> { rewhere(taggable_type: "TaggedPost") }, as: :taggable
+ has_many :tags, through: :taggings
+end
+
class PostWithDefaultInclude < ActiveRecord::Base
self.inheritance_column = :disabled
self.table_name = "posts"
@@ -275,3 +292,47 @@ end
class SubConditionalStiPost < ConditionalStiPost
end
+
+class FakeKlass
+ extend ActiveRecord::Delegation::DelegateCache
+
+ inherited self
+
+ class << self
+ def connection
+ Post.connection
+ end
+
+ def table_name
+ "posts"
+ end
+
+ def attribute_alias?(name)
+ false
+ end
+
+ def sanitize_sql(sql)
+ sql
+ end
+
+ def sanitize_sql_for_order(sql)
+ sql
+ end
+
+ def arel_attribute(name, table)
+ table[name]
+ end
+
+ def enforce_raw_sql_whitelist(*args)
+ # noop
+ end
+
+ def arel_table
+ Post.arel_table
+ end
+
+ def predicate_builder
+ Post.predicate_builder
+ end
+ end
+end
diff --git a/activerecord/test/models/price_estimate.rb b/activerecord/test/models/price_estimate.rb
index ce086e40a3..f1f88d8d8d 100644
--- a/activerecord/test/models/price_estimate.rb
+++ b/activerecord/test/models/price_estimate.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class PriceEstimate < ActiveRecord::Base
belongs_to :estimate_of, polymorphic: true
belongs_to :thing, polymorphic: true
diff --git a/activerecord/test/models/professor.rb b/activerecord/test/models/professor.rb
index 4dfb1a9602..abc23f40ff 100644
--- a/activerecord/test/models/professor.rb
+++ b/activerecord/test/models/professor.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require_dependency "models/arunit2_model"
class Professor < ARUnit2Model
diff --git a/activerecord/test/models/project.rb b/activerecord/test/models/project.rb
index 4fbd986e40..846cef625b 100644
--- a/activerecord/test/models/project.rb
+++ b/activerecord/test/models/project.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class Project < ActiveRecord::Base
belongs_to :mentor
has_and_belongs_to_many :developers, -> { distinct.order "developers.name desc, developers.id desc" }
diff --git a/activerecord/test/models/publisher.rb b/activerecord/test/models/publisher.rb
index 0d4a7f9235..53677197c4 100644
--- a/activerecord/test/models/publisher.rb
+++ b/activerecord/test/models/publisher.rb
@@ -1,2 +1,4 @@
+# frozen_string_literal: true
+
module Publisher
end
diff --git a/activerecord/test/models/publisher/article.rb b/activerecord/test/models/publisher/article.rb
index d73a8eb936..355c22dcc5 100644
--- a/activerecord/test/models/publisher/article.rb
+++ b/activerecord/test/models/publisher/article.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class Publisher::Article < ActiveRecord::Base
has_and_belongs_to_many :magazines
has_and_belongs_to_many :tags
diff --git a/activerecord/test/models/publisher/magazine.rb b/activerecord/test/models/publisher/magazine.rb
index 82e1a14008..425ede8df2 100644
--- a/activerecord/test/models/publisher/magazine.rb
+++ b/activerecord/test/models/publisher/magazine.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class Publisher::Magazine < ActiveRecord::Base
has_and_belongs_to_many :articles
end
diff --git a/activerecord/test/models/randomly_named_c1.rb b/activerecord/test/models/randomly_named_c1.rb
index d4be1e13b4..f90a7b9336 100644
--- a/activerecord/test/models/randomly_named_c1.rb
+++ b/activerecord/test/models/randomly_named_c1.rb
@@ -1,3 +1,5 @@
-class ClassNameThatDoesNotFollowCONVENTIONS < ActiveRecord::Base
- self.table_name = :randomly_named_table1
-end
+# frozen_string_literal: true
+
+class ClassNameThatDoesNotFollowCONVENTIONS < ActiveRecord::Base
+ self.table_name = :randomly_named_table1
+end
diff --git a/activerecord/test/models/rating.rb b/activerecord/test/models/rating.rb
index 7420821db0..cf06bc6931 100644
--- a/activerecord/test/models/rating.rb
+++ b/activerecord/test/models/rating.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class Rating < ActiveRecord::Base
belongs_to :comment
has_many :taggings, as: :taggable
diff --git a/activerecord/test/models/reader.rb b/activerecord/test/models/reader.rb
index 7c5a159fe0..d25627e430 100644
--- a/activerecord/test/models/reader.rb
+++ b/activerecord/test/models/reader.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class Reader < ActiveRecord::Base
belongs_to :post
belongs_to :person, inverse_of: :readers
diff --git a/activerecord/test/models/recipe.rb b/activerecord/test/models/recipe.rb
index c387230603..e53f5c8fb1 100644
--- a/activerecord/test/models/recipe.rb
+++ b/activerecord/test/models/recipe.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class Recipe < ActiveRecord::Base
belongs_to :chef
end
diff --git a/activerecord/test/models/record.rb b/activerecord/test/models/record.rb
index f77ac9fc03..63143e296a 100644
--- a/activerecord/test/models/record.rb
+++ b/activerecord/test/models/record.rb
@@ -1,2 +1,4 @@
+# frozen_string_literal: true
+
class Record < ActiveRecord::Base
end
diff --git a/activerecord/test/models/reference.rb b/activerecord/test/models/reference.rb
index e2bb980fed..2a7a1e3b77 100644
--- a/activerecord/test/models/reference.rb
+++ b/activerecord/test/models/reference.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class Reference < ActiveRecord::Base
belongs_to :person
belongs_to :job
diff --git a/activerecord/test/models/reply.rb b/activerecord/test/models/reply.rb
index a2d169292a..bc829ec67f 100644
--- a/activerecord/test/models/reply.rb
+++ b/activerecord/test/models/reply.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "models/topic"
class Reply < Topic
diff --git a/activerecord/test/models/ship.rb b/activerecord/test/models/ship.rb
index 77a7b22315..7973219a79 100644
--- a/activerecord/test/models/ship.rb
+++ b/activerecord/test/models/ship.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class Ship < ActiveRecord::Base
self.record_timestamps = false
diff --git a/activerecord/test/models/ship_part.rb b/activerecord/test/models/ship_part.rb
index 1a633b8d77..f6d7a8ae5e 100644
--- a/activerecord/test/models/ship_part.rb
+++ b/activerecord/test/models/ship_part.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class ShipPart < ActiveRecord::Base
belongs_to :ship
has_many :trinkets, class_name: "Treasure", as: :looter
diff --git a/activerecord/test/models/shop.rb b/activerecord/test/models/shop.rb
index f9d23d13b0..92afe70b92 100644
--- a/activerecord/test/models/shop.rb
+++ b/activerecord/test/models/shop.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module Shop
class Collection < ActiveRecord::Base
has_many :products, dependent: :nullify
diff --git a/activerecord/test/models/shop_account.rb b/activerecord/test/models/shop_account.rb
index 1580e8b20c..97fb058331 100644
--- a/activerecord/test/models/shop_account.rb
+++ b/activerecord/test/models/shop_account.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class ShopAccount < ActiveRecord::Base
belongs_to :customer
belongs_to :customer_carrier
diff --git a/activerecord/test/models/speedometer.rb b/activerecord/test/models/speedometer.rb
index 497c3aba9a..e456907a22 100644
--- a/activerecord/test/models/speedometer.rb
+++ b/activerecord/test/models/speedometer.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class Speedometer < ActiveRecord::Base
self.primary_key = :speedometer_id
belongs_to :dashboard
diff --git a/activerecord/test/models/sponsor.rb b/activerecord/test/models/sponsor.rb
index 3f142b25fe..f190860fd1 100644
--- a/activerecord/test/models/sponsor.rb
+++ b/activerecord/test/models/sponsor.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class Sponsor < ActiveRecord::Base
belongs_to :sponsor_club, class_name: "Club", foreign_key: "club_id"
belongs_to :sponsorable, polymorphic: true
diff --git a/activerecord/test/models/string_key_object.rb b/activerecord/test/models/string_key_object.rb
index f084ec1bdc..473c145f4c 100644
--- a/activerecord/test/models/string_key_object.rb
+++ b/activerecord/test/models/string_key_object.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class StringKeyObject < ActiveRecord::Base
self.primary_key = :id
end
diff --git a/activerecord/test/models/student.rb b/activerecord/test/models/student.rb
index 28a0b6c99b..e750798f74 100644
--- a/activerecord/test/models/student.rb
+++ b/activerecord/test/models/student.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class Student < ActiveRecord::Base
has_and_belongs_to_many :lessons
belongs_to :college
diff --git a/activerecord/test/models/subject.rb b/activerecord/test/models/subject.rb
deleted file mode 100644
index 504f68a296..0000000000
--- a/activerecord/test/models/subject.rb
+++ /dev/null
@@ -1,14 +0,0 @@
-# used for OracleSynonymTest, see test/synonym_test_oracle.rb
-#
-class Subject < ActiveRecord::Base
- # added initialization of author_email_address in the same way as in Topic class
- # as otherwise synonym test was failing
- after_initialize :set_email_address
-
- private
- def set_email_address
- unless persisted?
- self.author_email_address = "test@test.com"
- end
- end
-end
diff --git a/activerecord/test/models/subscriber.rb b/activerecord/test/models/subscriber.rb
index a820329003..b21969ca2d 100644
--- a/activerecord/test/models/subscriber.rb
+++ b/activerecord/test/models/subscriber.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class Subscriber < ActiveRecord::Base
self.primary_key = "nick"
has_many :subscriptions
diff --git a/activerecord/test/models/subscription.rb b/activerecord/test/models/subscription.rb
index 1cedf6deae..d1d5d21621 100644
--- a/activerecord/test/models/subscription.rb
+++ b/activerecord/test/models/subscription.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class Subscription < ActiveRecord::Base
belongs_to :subscriber, counter_cache: :books_count
belongs_to :book
diff --git a/activerecord/test/models/tag.rb b/activerecord/test/models/tag.rb
index c907aea10f..c1a8890a8a 100644
--- a/activerecord/test/models/tag.rb
+++ b/activerecord/test/models/tag.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class Tag < ActiveRecord::Base
has_many :taggings
has_many :taggables, through: :taggings
@@ -9,5 +11,6 @@ end
class OrderedTag < Tag
self.table_name = "tags"
- has_many :taggings, -> { order("taggings.id DESC") }, foreign_key: "tag_id"
+ has_many :ordered_taggings, -> { order("taggings.id DESC") }, foreign_key: "tag_id", class_name: "Tagging"
+ has_many :tagged_posts, through: :ordered_taggings, source: "taggable", source_type: "Post"
end
diff --git a/activerecord/test/models/tagging.rb b/activerecord/test/models/tagging.rb
index f739b4a197..6d4230f6f4 100644
--- a/activerecord/test/models/tagging.rb
+++ b/activerecord/test/models/tagging.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# test that attr_readonly isn't called on the :taggable polymorphic association
module Taggable
end
@@ -6,8 +8,13 @@ class Tagging < ActiveRecord::Base
belongs_to :tag, -> { includes(:tagging) }
belongs_to :super_tag, class_name: "Tag", foreign_key: "super_tag_id"
belongs_to :invalid_tag, class_name: "Tag", foreign_key: "tag_id"
+ belongs_to :ordered_tag, class_name: "OrderedTag", foreign_key: "tag_id"
belongs_to :blue_tag, -> { where tags: { name: "Blue" } }, class_name: "Tag", foreign_key: :tag_id
belongs_to :tag_with_primary_key, class_name: "Tag", foreign_key: :tag_id, primary_key: :custom_primary_key
belongs_to :taggable, polymorphic: true, counter_cache: :tags_count
has_many :things, through: :taggable
end
+
+class IndestructibleTagging < Tagging
+ before_destroy { throw :abort }
+end
diff --git a/activerecord/test/models/task.rb b/activerecord/test/models/task.rb
index e36989dd56..dabe3ce06b 100644
--- a/activerecord/test/models/task.rb
+++ b/activerecord/test/models/task.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class Task < ActiveRecord::Base
def updated_at
ending
diff --git a/activerecord/test/models/topic.rb b/activerecord/test/models/topic.rb
index d9381ac9cf..8cd4dc352a 100644
--- a/activerecord/test/models/topic.rb
+++ b/activerecord/test/models/topic.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class Topic < ActiveRecord::Base
scope :base, -> { all }
scope :written_before, lambda { |time|
@@ -63,6 +65,9 @@ class Topic < ActiveRecord::Base
after_initialize :set_email_address
+ attr_accessor :change_approved_before_save
+ before_save :change_approved_callback
+
class_attribute :after_initialize_called
after_initialize do
self.class.after_initialize_called = true
@@ -94,6 +99,10 @@ class Topic < ActiveRecord::Base
def before_destroy_for_transaction; end
def after_save_for_transaction; end
def after_create_for_transaction; end
+
+ def change_approved_callback
+ self.approved = change_approved_before_save unless change_approved_before_save.nil?
+ end
end
class ImportantTopic < Topic
diff --git a/activerecord/test/models/toy.rb b/activerecord/test/models/toy.rb
index ddc7048a56..4a5697eeb1 100644
--- a/activerecord/test/models/toy.rb
+++ b/activerecord/test/models/toy.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class Toy < ActiveRecord::Base
self.primary_key = :toy_id
belongs_to :pet
diff --git a/activerecord/test/models/traffic_light.rb b/activerecord/test/models/traffic_light.rb
index a6b7edb882..0b88815cbd 100644
--- a/activerecord/test/models/traffic_light.rb
+++ b/activerecord/test/models/traffic_light.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class TrafficLight < ActiveRecord::Base
serialize :state, Array
serialize :long_state, Array
diff --git a/activerecord/test/models/treasure.rb b/activerecord/test/models/treasure.rb
index fb2a5d44e2..b51db56c37 100644
--- a/activerecord/test/models/treasure.rb
+++ b/activerecord/test/models/treasure.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class Treasure < ActiveRecord::Base
has_and_belongs_to_many :parrots
belongs_to :looter, polymorphic: true
diff --git a/activerecord/test/models/treaty.rb b/activerecord/test/models/treaty.rb
index 373cd48f71..5c1d75aa09 100644
--- a/activerecord/test/models/treaty.rb
+++ b/activerecord/test/models/treaty.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class Treaty < ActiveRecord::Base
self.primary_key = :treaty_id
diff --git a/activerecord/test/models/tree.rb b/activerecord/test/models/tree.rb
index dc29cccc9c..77050c5fff 100644
--- a/activerecord/test/models/tree.rb
+++ b/activerecord/test/models/tree.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class Tree < ActiveRecord::Base
has_many :nodes, dependent: :destroy
end
diff --git a/activerecord/test/models/tuning_peg.rb b/activerecord/test/models/tuning_peg.rb
index 1252d6dc1d..6e052e1d0f 100644
--- a/activerecord/test/models/tuning_peg.rb
+++ b/activerecord/test/models/tuning_peg.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class TuningPeg < ActiveRecord::Base
belongs_to :guitar
validates_numericality_of :pitch
diff --git a/activerecord/test/models/tyre.rb b/activerecord/test/models/tyre.rb
index e50a21ca68..d627026585 100644
--- a/activerecord/test/models/tyre.rb
+++ b/activerecord/test/models/tyre.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class Tyre < ActiveRecord::Base
belongs_to :car
diff --git a/activerecord/test/models/user.rb b/activerecord/test/models/user.rb
index 5089a795f4..3efbc45d2a 100644
--- a/activerecord/test/models/user.rb
+++ b/activerecord/test/models/user.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "models/job"
class User < ActiveRecord::Base
diff --git a/activerecord/test/models/uuid_child.rb b/activerecord/test/models/uuid_child.rb
index a3d0962ad6..9fce361cc8 100644
--- a/activerecord/test/models/uuid_child.rb
+++ b/activerecord/test/models/uuid_child.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class UuidChild < ActiveRecord::Base
belongs_to :uuid_parent
end
diff --git a/activerecord/test/models/uuid_item.rb b/activerecord/test/models/uuid_item.rb
index 2353e40213..41f68c4c18 100644
--- a/activerecord/test/models/uuid_item.rb
+++ b/activerecord/test/models/uuid_item.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class UuidItem < ActiveRecord::Base
end
diff --git a/activerecord/test/models/uuid_parent.rb b/activerecord/test/models/uuid_parent.rb
index 5634f22d0c..05db61855e 100644
--- a/activerecord/test/models/uuid_parent.rb
+++ b/activerecord/test/models/uuid_parent.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class UuidParent < ActiveRecord::Base
has_many :uuid_children
end
diff --git a/activerecord/test/models/vegetables.rb b/activerecord/test/models/vegetables.rb
index a4590d06e0..cfaab08ed1 100644
--- a/activerecord/test/models/vegetables.rb
+++ b/activerecord/test/models/vegetables.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class Vegetable < ActiveRecord::Base
validates_presence_of :name
diff --git a/activerecord/test/models/vehicle.rb b/activerecord/test/models/vehicle.rb
index 855bc4e325..c9b3338522 100644
--- a/activerecord/test/models/vehicle.rb
+++ b/activerecord/test/models/vehicle.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class Vehicle < ActiveRecord::Base
self.abstract_class = true
default_scope -> { where("tires_count IS NOT NULL") }
diff --git a/activerecord/test/models/vertex.rb b/activerecord/test/models/vertex.rb
index 3d19433b6f..0ad8114898 100644
--- a/activerecord/test/models/vertex.rb
+++ b/activerecord/test/models/vertex.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
# This class models a vertex in a directed graph.
class Vertex < ActiveRecord::Base
has_many :sink_edges, class_name: "Edge", foreign_key: "source_id"
diff --git a/activerecord/test/models/warehouse_thing.rb b/activerecord/test/models/warehouse_thing.rb
index f20bd1a245..099633af55 100644
--- a/activerecord/test/models/warehouse_thing.rb
+++ b/activerecord/test/models/warehouse_thing.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class WarehouseThing < ActiveRecord::Base
self.table_name = "warehouse-things"
diff --git a/activerecord/test/models/wheel.rb b/activerecord/test/models/wheel.rb
index cba2b3e518..8db57d181e 100644
--- a/activerecord/test/models/wheel.rb
+++ b/activerecord/test/models/wheel.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class Wheel < ActiveRecord::Base
- belongs_to :wheelable, polymorphic: true, counter_cache: true
+ belongs_to :wheelable, polymorphic: true, counter_cache: true, touch: true
end
diff --git a/activerecord/test/models/without_table.rb b/activerecord/test/models/without_table.rb
index 7c0fc286e1..fc0a52d6ad 100644
--- a/activerecord/test/models/without_table.rb
+++ b/activerecord/test/models/without_table.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class WithoutTable < ActiveRecord::Base
default_scope -> { where(published: true) }
end
diff --git a/activerecord/test/models/zine.rb b/activerecord/test/models/zine.rb
index 3f2b348b46..6f361665ef 100644
--- a/activerecord/test/models/zine.rb
+++ b/activerecord/test/models/zine.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
class Zine < ActiveRecord::Base
has_many :interests, inverse_of: :zine
end
diff --git a/activerecord/test/schema/mysql2_specific_schema.rb b/activerecord/test/schema/mysql2_specific_schema.rb
index 90a314c83c..e634e9e6b1 100644
--- a/activerecord/test/schema/mysql2_specific_schema.rb
+++ b/activerecord/test/schema/mysql2_specific_schema.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
ActiveRecord::Schema.define do
if ActiveRecord::Base.connection.version >= "5.6.0"
diff --git a/activerecord/test/schema/oracle_specific_schema.rb b/activerecord/test/schema/oracle_specific_schema.rb
index 264d9b8910..e236571caa 100644
--- a/activerecord/test/schema/oracle_specific_schema.rb
+++ b/activerecord/test/schema/oracle_specific_schema.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
ActiveRecord::Schema.define do
execute "drop table test_oracle_defaults" rescue nil
diff --git a/activerecord/test/schema/postgresql_specific_schema.rb b/activerecord/test/schema/postgresql_specific_schema.rb
index e56e8fa36a..f15178d695 100644
--- a/activerecord/test/schema/postgresql_specific_schema.rb
+++ b/activerecord/test/schema/postgresql_specific_schema.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
ActiveRecord::Schema.define do
enable_extension!("uuid-ossp", ActiveRecord::Base.connection)
diff --git a/activerecord/test/schema/schema.rb b/activerecord/test/schema/schema.rb
index 6da0ef26f6..7d008eecd5 100644
--- a/activerecord/test/schema/schema.rb
+++ b/activerecord/test/schema/schema.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
ActiveRecord::Schema.define do
# ------------------------------------------------------------------- #
# #
@@ -7,7 +9,7 @@ ActiveRecord::Schema.define do
# ------------------------------------------------------------------- #
create_table :accounts, force: true do |t|
- t.integer :firm_id
+ t.references :firm, index: false
t.string :firm_name
t.integer :credit_limit
end
@@ -107,7 +109,7 @@ ActiveRecord::Schema.define do
t.boolean :has_fun, null: false, default: false
end
- create_table :bulbs, force: true do |t|
+ create_table :bulbs, primary_key: "ID", force: true do |t|
t.integer :car_id
t.string :name
t.boolean :frickinawesome, default: false
@@ -121,7 +123,7 @@ ActiveRecord::Schema.define do
create_table :cars, force: true do |t|
t.string :name
t.integer :engines_count
- t.integer :wheels_count
+ t.integer :wheels_count, default: 0
t.column :lock_version, :integer, null: false, default: 0
t.timestamps null: false
end
@@ -189,17 +191,22 @@ ActiveRecord::Schema.define do
t.string :resource_id
t.string :resource_type
t.integer :developer_id
+ t.datetime :updated_at
+ t.datetime :deleted_at
+ t.integer :comments
end
create_table :companies, force: true do |t|
t.string :type
- t.integer :firm_id
+ t.references :firm, index: false
t.string :firm_name
t.string :name
- t.integer :client_of
- t.integer :rating, default: 1
+ t.bigint :client_of
+ t.bigint :rating, default: 1
t.integer :account_id
t.string :description, default: ""
+ t.index [:name, :rating], order: :desc
+ t.index [:name, :description], length: 10
t.index [:firm_id, :type, :rating], name: "company_index", length: { type: 10 }, order: { rating: :desc }
t.index [:firm_id, :type], name: "company_partial_index", where: "(rating > 10)"
t.index :name, name: "company_name_index", using: :btree
@@ -232,8 +239,8 @@ ActiveRecord::Schema.define do
end
create_table :contracts, force: true do |t|
- t.integer :developer_id
- t.integer :company_id
+ t.references :developer, index: false
+ t.references :company, index: false
end
create_table :customers, force: true do |t|
@@ -259,7 +266,7 @@ ActiveRecord::Schema.define do
t.string :name
t.string :first_name
t.integer :salary, default: 70000
- t.integer :firm_id
+ t.references :firm, index: false
t.integer :mentor_id
if subsecond_precision_supported?
t.datetime :created_at, precision: 6
@@ -491,7 +498,7 @@ ActiveRecord::Schema.define do
t.datetime :joined_on
t.integer :club_id, :member_id
t.boolean :favourite, default: false
- t.string :type
+ t.integer :type
end
create_table :member_types, force: true do |t|
@@ -683,6 +690,7 @@ ActiveRecord::Schema.define do
t.integer :taggings_with_delete_all_count, default: 0
t.integer :taggings_with_destroy_count, default: 0
t.integer :tags_count, default: 0
+ t.integer :indestructible_tags_count, default: 0
t.integer :tags_with_destroy_count, default: 0
t.integer :tags_with_nullify_count, default: 0
end
@@ -716,7 +724,7 @@ ActiveRecord::Schema.define do
create_table :projects, force: true do |t|
t.string :name
t.string :type
- t.integer :firm_id
+ t.references :firm, index: false
t.integer :mentor_id
end
@@ -805,8 +813,7 @@ ActiveRecord::Schema.define do
create_table :sponsors, force: true do |t|
t.integer :club_id
- t.integer :sponsorable_id
- t.string :sponsorable_type
+ t.references :sponsorable, polymorphic: true, index: false
end
create_table :string_key_objects, id: false, force: true do |t|
@@ -841,6 +848,7 @@ ActiveRecord::Schema.define do
t.column :taggable_type, :string
t.column :taggable_id, :integer
t.string :comment
+ t.string :type
end
create_table :tasks, force: true do |t|
@@ -958,6 +966,7 @@ ActiveRecord::Schema.define do
end
create_table :wheels, force: true do |t|
+ t.integer :size
t.references :wheelable, polymorphic: true
end
diff --git a/activerecord/test/support/config.rb b/activerecord/test/support/config.rb
index aaff408b41..bd6d5c339b 100644
--- a/activerecord/test/support/config.rb
+++ b/activerecord/test/support/config.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "yaml"
require "erb"
require "fileutils"
diff --git a/activerecord/test/support/connection.rb b/activerecord/test/support/connection.rb
index 1a609e13c3..2a4fa53460 100644
--- a/activerecord/test/support/connection.rb
+++ b/activerecord/test/support/connection.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require "active_support/logger"
require "models/college"
require "models/course"
diff --git a/activerecord/test/support/connection_helper.rb b/activerecord/test/support/connection_helper.rb
index 4a19e5df44..3bb1b370c1 100644
--- a/activerecord/test/support/connection_helper.rb
+++ b/activerecord/test/support/connection_helper.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module ConnectionHelper
def run_without_connection
original_connection = ActiveRecord::Base.remove_connection
diff --git a/activerecord/test/support/ddl_helper.rb b/activerecord/test/support/ddl_helper.rb
index 43cb235e01..a18bf5ea0a 100644
--- a/activerecord/test/support/ddl_helper.rb
+++ b/activerecord/test/support/ddl_helper.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module DdlHelper
def with_example_table(connection, table_name, definition = nil)
connection.execute("CREATE TABLE #{table_name}(#{definition})")
diff --git a/activerecord/test/support/schema_dumping_helper.rb b/activerecord/test/support/schema_dumping_helper.rb
index 666c1b6a14..777e6a7c1b 100644
--- a/activerecord/test/support/schema_dumping_helper.rb
+++ b/activerecord/test/support/schema_dumping_helper.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
module SchemaDumpingHelper
def dump_table_schema(table, connection = ActiveRecord::Base.connection)
old_ignore_tables = ActiveRecord::SchemaDumper.ignore_tables