ibm_db 5.6.1 → 5.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (592) hide show
  1. checksums.yaml +4 -4
  2. data/ext/Makefile +13 -9
  3. data/ext/extconf.rb +176 -110
  4. data/ext/ibm_db.c +60 -0
  5. data/ext/ibm_db.o +0 -0
  6. data/ext/ibm_db.so +0 -0
  7. data/ext/mkmf.log +6 -6
  8. data/ext/ruby_ibm_db_cli.c +14 -10
  9. data/ext/ruby_ibm_db_cli.h +4 -0
  10. data/ext/ruby_ibm_db_cli.o +0 -0
  11. data/lib/IBM_DB.rb +2 -2
  12. data/lib/active_record/connection_adapters/ibm_db_adapter.rb +1254 -277
  13. data/test/active_record/connection_adapters/fake_legacy_adapter.rb +17 -0
  14. data/test/active_record/connection_adapters/fake_misleading_legacy_adapter.rb +8 -0
  15. data/test/activejob/destroy_association_async_job_test.rb +62 -0
  16. data/test/activejob/destroy_association_async_test.rb +147 -11
  17. data/test/activejob/job_runtime_test.rb +41 -0
  18. data/test/activejob/unloadable_base_job.rb +3 -0
  19. data/test/cases/active_record_schema_test.rb +196 -0
  20. data/test/cases/active_record_test.rb +21 -0
  21. data/test/cases/adapter_prevent_writes_test.rb +26 -202
  22. data/test/cases/adapter_test.rb +592 -82
  23. data/test/cases/adapters/abstract_mysql_adapter/active_schema_test.rb +210 -0
  24. data/test/cases/adapters/abstract_mysql_adapter/adapter_prevent_writes_test.rb +111 -0
  25. data/test/cases/adapters/abstract_mysql_adapter/auto_increment_test.rb +48 -0
  26. data/test/cases/adapters/abstract_mysql_adapter/bind_parameter_test.rb +91 -0
  27. data/test/cases/adapters/abstract_mysql_adapter/case_sensitivity_test.rb +65 -0
  28. data/test/cases/adapters/abstract_mysql_adapter/charset_collation_test.rb +85 -0
  29. data/test/cases/adapters/abstract_mysql_adapter/connection_test.rb +267 -0
  30. data/test/cases/adapters/abstract_mysql_adapter/count_deleted_rows_with_lock_test.rb +29 -0
  31. data/test/cases/adapters/abstract_mysql_adapter/json_test.rb +24 -0
  32. data/test/cases/adapters/abstract_mysql_adapter/mysql_boolean_test.rb +102 -0
  33. data/test/cases/adapters/abstract_mysql_adapter/mysql_enum_test.rb +47 -0
  34. data/test/cases/adapters/abstract_mysql_adapter/mysql_explain_test.rb +71 -0
  35. data/test/cases/adapters/abstract_mysql_adapter/nested_deadlock_test.rb +191 -0
  36. data/test/cases/adapters/abstract_mysql_adapter/optimizer_hints_test.rb +69 -0
  37. data/test/cases/adapters/abstract_mysql_adapter/quoting_test.rb +49 -0
  38. data/test/cases/adapters/abstract_mysql_adapter/schema_migrations_test.rb +68 -0
  39. data/test/cases/adapters/abstract_mysql_adapter/schema_test.rb +130 -0
  40. data/test/cases/adapters/abstract_mysql_adapter/set_test.rb +32 -0
  41. data/test/cases/adapters/abstract_mysql_adapter/sp_test.rb +39 -0
  42. data/test/cases/adapters/abstract_mysql_adapter/sql_types_test.rb +16 -0
  43. data/test/cases/adapters/abstract_mysql_adapter/table_options_test.rb +125 -0
  44. data/test/cases/adapters/abstract_mysql_adapter/transaction_test.rb +204 -0
  45. data/test/cases/adapters/abstract_mysql_adapter/unsigned_type_test.rb +77 -0
  46. data/test/cases/adapters/abstract_mysql_adapter/virtual_column_test.rb +69 -0
  47. data/test/cases/adapters/abstract_mysql_adapter/warnings_test.rb +109 -0
  48. data/test/cases/adapters/mysql2/check_constraint_quoting_test.rb +35 -0
  49. data/test/cases/adapters/mysql2/dbconsole_test.rb +87 -0
  50. data/test/cases/adapters/mysql2/mysql2_adapter_test.rb +136 -10
  51. data/test/cases/adapters/mysql2/mysql2_rake_test.rb +433 -0
  52. data/test/cases/adapters/postgresql/active_schema_test.rb +8 -4
  53. data/test/cases/adapters/postgresql/array_test.rb +6 -6
  54. data/test/cases/adapters/postgresql/bind_parameter_test.rb +52 -0
  55. data/test/cases/adapters/postgresql/bit_string_test.rb +2 -2
  56. data/test/cases/adapters/postgresql/bytea_test.rb +3 -3
  57. data/test/cases/adapters/postgresql/case_insensitive_test.rb +1 -1
  58. data/test/cases/adapters/postgresql/change_schema_test.rb +32 -1
  59. data/test/cases/adapters/postgresql/cidr_test.rb +17 -0
  60. data/test/cases/adapters/postgresql/citext_test.rb +7 -1
  61. data/test/cases/adapters/postgresql/collation_test.rb +1 -1
  62. data/test/cases/adapters/postgresql/composite_test.rb +1 -1
  63. data/test/cases/adapters/postgresql/connection_test.rb +43 -36
  64. data/test/cases/adapters/postgresql/create_unlogged_tables_test.rb +3 -2
  65. data/test/cases/adapters/postgresql/datatype_test.rb +9 -4
  66. data/test/cases/adapters/postgresql/date_test.rb +2 -2
  67. data/test/cases/adapters/postgresql/dbconsole_test.rb +109 -0
  68. data/test/cases/adapters/postgresql/deferred_constraints_test.rb +55 -0
  69. data/test/cases/adapters/postgresql/domain_test.rb +1 -1
  70. data/test/cases/adapters/postgresql/enum_test.rb +208 -8
  71. data/test/cases/adapters/postgresql/explain_test.rb +25 -6
  72. data/test/cases/adapters/postgresql/extension_migration_test.rb +51 -9
  73. data/test/cases/adapters/postgresql/foreign_table_test.rb +3 -3
  74. data/test/cases/adapters/postgresql/full_text_test.rb +1 -1
  75. data/test/cases/adapters/postgresql/geometric_test.rb +13 -6
  76. data/test/cases/adapters/postgresql/hstore_test.rb +22 -32
  77. data/test/cases/adapters/postgresql/infinity_test.rb +13 -2
  78. data/test/cases/adapters/postgresql/integer_test.rb +1 -1
  79. data/test/cases/adapters/postgresql/interval_test.rb +9 -17
  80. data/test/cases/adapters/postgresql/invertible_migration_test.rb +139 -0
  81. data/test/cases/adapters/postgresql/json_test.rb +5 -0
  82. data/test/cases/adapters/postgresql/ltree_test.rb +1 -1
  83. data/test/cases/adapters/postgresql/money_test.rb +17 -9
  84. data/test/cases/adapters/postgresql/network_test.rb +13 -1
  85. data/test/cases/adapters/postgresql/numbers_test.rb +18 -2
  86. data/test/cases/adapters/postgresql/optimizer_hints_test.rb +9 -9
  87. data/test/cases/adapters/postgresql/partitions_test.rb +5 -2
  88. data/test/cases/adapters/postgresql/postgresql_adapter_prevent_writes_test.rb +7 -110
  89. data/test/cases/adapters/postgresql/postgresql_adapter_test.rb +403 -20
  90. data/test/cases/adapters/postgresql/postgresql_rake_test.rb +637 -0
  91. data/test/cases/adapters/postgresql/prepared_statements_disabled_test.rb +1 -1
  92. data/test/cases/adapters/postgresql/quoting_test.rb +60 -1
  93. data/test/cases/adapters/postgresql/range_test.rb +185 -21
  94. data/test/cases/adapters/postgresql/referential_integrity_test.rb +29 -5
  95. data/test/cases/adapters/postgresql/rename_table_test.rb +35 -8
  96. data/test/cases/adapters/postgresql/schema_authorization_test.rb +2 -2
  97. data/test/cases/adapters/postgresql/schema_test.rb +270 -20
  98. data/test/cases/adapters/postgresql/serial_test.rb +5 -5
  99. data/test/cases/adapters/postgresql/statement_pool_test.rb +6 -2
  100. data/test/cases/adapters/postgresql/timestamp_test.rb +122 -5
  101. data/test/cases/adapters/postgresql/transaction_nested_test.rb +130 -6
  102. data/test/cases/adapters/postgresql/transaction_test.rb +37 -11
  103. data/test/cases/adapters/postgresql/type_lookup_test.rb +1 -1
  104. data/test/cases/adapters/postgresql/uuid_test.rb +77 -9
  105. data/test/cases/adapters/postgresql/virtual_column_test.rb +114 -0
  106. data/test/cases/adapters/postgresql/xml_test.rb +1 -1
  107. data/test/cases/adapters/sqlite3/bind_parameter_test.rb +52 -0
  108. data/test/cases/adapters/sqlite3/collation_test.rb +1 -1
  109. data/test/cases/adapters/sqlite3/copy_table_test.rb +28 -7
  110. data/test/cases/adapters/sqlite3/dbconsole_test.rb +81 -0
  111. data/test/cases/adapters/sqlite3/explain_test.rb +3 -3
  112. data/test/cases/adapters/sqlite3/quoting_test.rb +34 -3
  113. data/test/cases/adapters/sqlite3/sqlite3_adapter_prevent_writes_test.rb +9 -102
  114. data/test/cases/adapters/sqlite3/sqlite3_adapter_test.rb +727 -72
  115. data/test/cases/adapters/sqlite3/sqlite3_create_folder_test.rb +6 -3
  116. data/test/cases/adapters/sqlite3/sqlite_rake_test.rb +264 -0
  117. data/test/cases/adapters/sqlite3/statement_pool_test.rb +1 -1
  118. data/test/cases/adapters/sqlite3/transaction_test.rb +6 -3
  119. data/test/cases/adapters/sqlite3/virtual_column_test.rb +138 -0
  120. data/test/cases/adapters/sqlite3/virtual_table_test.rb +51 -0
  121. data/test/cases/adapters/trilogy/dbconsole_test.rb +76 -0
  122. data/test/cases/adapters/trilogy/trilogy_adapter_test.rb +473 -0
  123. data/test/cases/adapters/trilogy/trilogy_rake_test.rb +433 -0
  124. data/test/cases/aggregations_test.rb +16 -0
  125. data/test/cases/annotate_test.rb +14 -12
  126. data/test/cases/arel/attributes/attribute_test.rb +86 -47
  127. data/test/cases/arel/collectors/bind_test.rb +1 -1
  128. data/test/cases/arel/collectors/composite_test.rb +11 -1
  129. data/test/cases/arel/collectors/sql_string_test.rb +1 -1
  130. data/test/cases/arel/collectors/substitute_bind_collector_test.rb +1 -1
  131. data/test/cases/arel/delete_manager_test.rb +0 -6
  132. data/test/cases/arel/factory_methods_test.rb +45 -0
  133. data/test/cases/arel/insert_manager_test.rb +1 -7
  134. data/test/cases/arel/nodes/as_test.rb +12 -0
  135. data/test/cases/arel/nodes/ascending_test.rb +1 -1
  136. data/test/cases/arel/nodes/bin_test.rb +1 -1
  137. data/test/cases/arel/nodes/bound_sql_literal_test.rb +31 -0
  138. data/test/cases/arel/nodes/cte_test.rb +46 -0
  139. data/test/cases/arel/nodes/descending_test.rb +1 -1
  140. data/test/cases/arel/nodes/equality_test.rb +3 -3
  141. data/test/cases/arel/nodes/filter_test.rb +37 -0
  142. data/test/cases/arel/nodes/fragments_test.rb +38 -0
  143. data/test/cases/arel/nodes/homogeneous_in_test.rb +52 -0
  144. data/test/cases/arel/nodes/infix_operation_test.rb +1 -1
  145. data/test/cases/arel/nodes/node_test.rb +1 -1
  146. data/test/cases/arel/nodes/or_test.rb +2 -2
  147. data/test/cases/arel/nodes/sql_literal_test.rb +18 -1
  148. data/test/cases/arel/nodes/table_alias_test.rb +12 -0
  149. data/test/cases/arel/nodes/unary_operation_test.rb +1 -1
  150. data/test/cases/arel/nodes/update_statement_test.rb +4 -0
  151. data/test/cases/arel/nodes_test.rb +3 -2
  152. data/test/cases/arel/select_manager_test.rb +12 -12
  153. data/test/cases/arel/support/fake_record.rb +22 -17
  154. data/test/cases/arel/table_test.rb +15 -25
  155. data/test/cases/arel/update_manager_test.rb +51 -6
  156. data/test/cases/arel/visitors/dispatch_contamination_test.rb +1 -1
  157. data/test/cases/arel/visitors/dot_test.rb +177 -2
  158. data/test/cases/arel/visitors/mysql_test.rb +49 -1
  159. data/test/cases/arel/visitors/postgres_test.rb +1 -31
  160. data/test/cases/arel/visitors/sqlite_test.rb +1 -1
  161. data/test/cases/arel/visitors/to_sql_test.rb +330 -20
  162. data/test/cases/assertions/query_assertions_test.rb +136 -0
  163. data/test/cases/associations/belongs_to_associations_test.rb +502 -41
  164. data/test/cases/associations/bidirectional_destroy_dependencies_test.rb +1 -1
  165. data/test/cases/associations/cascaded_eager_loading_test.rb +26 -15
  166. data/test/cases/associations/eager_load_nested_include_test.rb +1 -1
  167. data/test/cases/associations/eager_singularization_test.rb +1 -1
  168. data/test/cases/associations/eager_test.rb +255 -93
  169. data/test/cases/associations/has_and_belongs_to_many_associations_test.rb +25 -53
  170. data/test/cases/associations/has_many_associations_test.rb +282 -72
  171. data/test/cases/associations/has_many_through_associations_test.rb +172 -45
  172. data/test/cases/associations/has_many_through_disable_joins_associations_test.rb +197 -0
  173. data/test/cases/associations/has_one_associations_test.rb +160 -29
  174. data/test/cases/associations/has_one_through_associations_test.rb +64 -15
  175. data/test/cases/associations/has_one_through_disable_joins_associations_test.rb +82 -0
  176. data/test/cases/associations/inner_join_association_test.rb +36 -6
  177. data/test/cases/associations/inverse_associations_test.rb +177 -40
  178. data/test/cases/associations/join_model_test.rb +17 -12
  179. data/test/cases/associations/left_outer_join_association_test.rb +14 -3
  180. data/test/cases/associations/nested_error_test.rb +116 -0
  181. data/test/cases/associations/nested_through_associations_test.rb +54 -19
  182. data/test/cases/associations/required_test.rb +1 -1
  183. data/test/cases/associations_test.rb +1278 -21
  184. data/test/cases/asynchronous_queries_test.rb +310 -0
  185. data/test/cases/attribute_methods/read_test.rb +6 -0
  186. data/test/cases/attribute_methods/time_zone_converter_test.rb +21 -0
  187. data/test/cases/attribute_methods_test.rb +565 -46
  188. data/test/cases/attributes_test.rb +42 -1
  189. data/test/cases/autosave_association_test.rb +645 -92
  190. data/test/cases/base_prevent_writes_test.rb +28 -159
  191. data/test/cases/base_test.rb +434 -209
  192. data/test/cases/batches_test.rb +441 -57
  193. data/test/cases/binary_test.rb +31 -0
  194. data/test/cases/bind_parameter_test.rb +107 -16
  195. data/test/cases/cache_key_test.rb +7 -7
  196. data/test/cases/calculations_test.rb +541 -110
  197. data/test/cases/callbacks_test.rb +0 -1
  198. data/test/cases/clone_test.rb +5 -4
  199. data/test/cases/coders/yaml_column_test.rb +97 -2
  200. data/test/cases/collection_cache_key_test.rb +24 -16
  201. data/test/cases/column_alias_test.rb +2 -8
  202. data/test/cases/column_definition_test.rb +12 -3
  203. data/test/cases/comment_test.rb +23 -26
  204. data/test/cases/connection_adapters/adapter_leasing_test.rb +9 -9
  205. data/test/cases/connection_adapters/connection_handler_test.rb +145 -99
  206. data/test/cases/connection_adapters/connection_handlers_multi_db_test.rb +79 -55
  207. data/test/cases/connection_adapters/connection_handlers_multi_pool_config_test.rb +17 -20
  208. data/test/cases/connection_adapters/connection_handlers_sharding_db_test.rb +146 -55
  209. data/test/cases/connection_adapters/connection_swapping_nested_test.rb +136 -23
  210. data/test/cases/connection_adapters/merge_and_resolve_default_url_config_test.rb +96 -27
  211. data/test/cases/connection_adapters/mysql_type_lookup_test.rb +5 -5
  212. data/test/cases/connection_adapters/registration_test.rb +98 -0
  213. data/test/cases/connection_adapters/schema_cache_test.rb +288 -135
  214. data/test/cases/connection_adapters/standalone_connection_test.rb +38 -0
  215. data/test/cases/connection_adapters/statement_pool_test.rb +29 -0
  216. data/test/cases/connection_adapters/type_lookup_test.rb +12 -24
  217. data/test/cases/connection_handling_test.rb +210 -0
  218. data/test/cases/connection_management_test.rb +44 -7
  219. data/test/cases/connection_pool_test.rb +466 -99
  220. data/test/cases/core_test.rb +167 -9
  221. data/test/cases/counter_cache_test.rb +129 -2
  222. data/test/cases/custom_locking_test.rb +3 -3
  223. data/test/cases/database_configurations/hash_config_test.rb +149 -13
  224. data/test/cases/database_configurations/resolver_test.rb +14 -10
  225. data/test/cases/database_configurations/url_config_test.rb +53 -0
  226. data/test/cases/database_configurations_test.rb +86 -43
  227. data/test/cases/database_selector_test.rb +30 -8
  228. data/test/cases/database_statements_test.rb +10 -14
  229. data/test/cases/date_time_precision_test.rb +141 -11
  230. data/test/cases/date_time_test.rb +19 -1
  231. data/test/cases/defaults_test.rb +107 -41
  232. data/test/cases/delegated_type_test.rb +69 -9
  233. data/test/cases/dirty_test.rb +82 -25
  234. data/test/cases/disconnected_test.rb +10 -9
  235. data/test/cases/dup_test.rb +26 -6
  236. data/test/cases/encryption/cipher/aes256_gcm_test.rb +50 -0
  237. data/test/cases/encryption/cipher_test.rb +65 -0
  238. data/test/cases/encryption/concurrency_test.rb +27 -0
  239. data/test/cases/encryption/config_test.rb +21 -0
  240. data/test/cases/encryption/configurable_test.rb +93 -0
  241. data/test/cases/encryption/contexts_test.rb +105 -0
  242. data/test/cases/encryption/derived_secret_key_provider_test.rb +31 -0
  243. data/test/cases/encryption/deterministic_key_provider_test.rb +11 -0
  244. data/test/cases/encryption/encryptable_record_api_test.rb +175 -0
  245. data/test/cases/encryption/encryptable_record_message_pack_serialized_test.rb +42 -0
  246. data/test/cases/encryption/encryptable_record_test.rb +470 -0
  247. data/test/cases/encryption/encrypted_fixtures_test.rb +22 -0
  248. data/test/cases/encryption/encrypting_only_encryptor_test.rb +20 -0
  249. data/test/cases/encryption/encryption_schemes_test.rb +240 -0
  250. data/test/cases/encryption/encryptor_test.rb +113 -0
  251. data/test/cases/encryption/envelope_encryption_key_provider_test.rb +49 -0
  252. data/test/cases/encryption/extended_deterministic_queries_test.rb +81 -0
  253. data/test/cases/encryption/helper.rb +152 -0
  254. data/test/cases/encryption/key_generator_test.rb +55 -0
  255. data/test/cases/encryption/key_provider_test.rb +57 -0
  256. data/test/cases/encryption/key_test.rb +17 -0
  257. data/test/cases/encryption/message_pack_message_serializer_test.rb +70 -0
  258. data/test/cases/encryption/message_serializer_test.rb +82 -0
  259. data/test/cases/encryption/message_test.rb +43 -0
  260. data/test/cases/encryption/null_encryptor_test.rb +21 -0
  261. data/test/cases/encryption/properties_test.rb +69 -0
  262. data/test/cases/encryption/read_only_null_encryptor_test.rb +19 -0
  263. data/test/cases/encryption/scheme_test.rb +58 -0
  264. data/test/cases/encryption/unencrypted_attributes_test.rb +27 -0
  265. data/test/cases/encryption/uniqueness_validations_test.rb +65 -0
  266. data/test/cases/enum_test.rb +374 -72
  267. data/test/cases/errors_test.rb +7 -4
  268. data/test/cases/excluding_test.rb +116 -0
  269. data/test/cases/explain_subscriber_test.rb +6 -1
  270. data/test/cases/explain_test.rb +119 -10
  271. data/test/cases/filter_attributes_test.rb +7 -14
  272. data/test/cases/finder_respond_to_test.rb +3 -3
  273. data/test/cases/finder_test.rb +357 -72
  274. data/test/cases/fixture_set/file_test.rb +9 -0
  275. data/test/cases/fixtures_test.rb +426 -190
  276. data/test/cases/helper.rb +66 -192
  277. data/test/cases/hot_compatibility_test.rb +10 -10
  278. data/test/cases/inheritance_test.rb +51 -61
  279. data/test/cases/inherited_test.rb +40 -0
  280. data/test/cases/insert_all_test.rb +419 -9
  281. data/test/cases/instrumentation_test.rb +202 -30
  282. data/test/cases/integration_test.rb +25 -7
  283. data/test/cases/invalid_connection_test.rb +5 -5
  284. data/test/cases/invertible_migration_test.rb +108 -24
  285. data/test/cases/json_attribute_test.rb +1 -0
  286. data/test/cases/json_serialization_test.rb +5 -5
  287. data/test/cases/json_shared_test_cases.rb +5 -9
  288. data/test/cases/locking_test.rb +67 -31
  289. data/test/cases/log_subscriber_test.rb +17 -15
  290. data/test/cases/marshal_serialization_test.rb +78 -5
  291. data/test/cases/message_pack_test.rb +91 -0
  292. data/test/cases/migration/change_schema_test.rb +77 -46
  293. data/test/cases/migration/change_table_test.rb +129 -114
  294. data/test/cases/migration/check_constraint_test.rb +192 -10
  295. data/test/cases/migration/column_attributes_test.rb +4 -9
  296. data/test/cases/migration/column_positioning_test.rb +2 -2
  297. data/test/cases/migration/columns_test.rb +118 -4
  298. data/test/cases/migration/command_recorder_test.rb +181 -14
  299. data/test/cases/migration/compatibility_test.rb +883 -196
  300. data/test/cases/migration/create_join_table_test.rb +8 -1
  301. data/test/cases/migration/exclusion_constraint_test.rb +192 -0
  302. data/test/cases/migration/foreign_key_test.rb +356 -14
  303. data/test/cases/migration/helper.rb +4 -1
  304. data/test/cases/migration/index_test.rb +67 -2
  305. data/test/cases/migration/invalid_options_test.rb +129 -0
  306. data/test/cases/migration/logger_test.rb +7 -4
  307. data/test/cases/migration/pending_migrations_test.rb +103 -60
  308. data/test/cases/migration/references_foreign_key_test.rb +73 -4
  309. data/test/cases/migration/references_index_test.rb +25 -27
  310. data/test/cases/migration/references_statements_test.rb +27 -3
  311. data/test/cases/migration/rename_table_test.rb +39 -54
  312. data/test/cases/migration/schema_definitions_test.rb +109 -0
  313. data/test/cases/migration/unique_constraint_test.rb +248 -0
  314. data/test/cases/migration_test.rb +677 -220
  315. data/test/cases/migrator_test.rb +102 -75
  316. data/test/cases/multi_db_migrator_test.rb +55 -34
  317. data/test/cases/multiparameter_attributes_test.rb +26 -29
  318. data/test/cases/multiple_db_test.rb +42 -19
  319. data/test/cases/nested_attributes_test.rb +124 -6
  320. data/test/cases/nested_attributes_with_callbacks_test.rb +2 -2
  321. data/test/cases/normalized_attribute_test.rb +117 -0
  322. data/test/cases/null_relation_test.rb +35 -2
  323. data/test/cases/persistence/reload_association_cache_test.rb +20 -0
  324. data/test/cases/persistence_test.rb +629 -22
  325. data/test/cases/pooled_connections_test.rb +57 -56
  326. data/test/cases/prepared_statement_status_test.rb +8 -3
  327. data/test/cases/primary_class_test.rb +134 -0
  328. data/test/cases/primary_keys_test.rb +171 -55
  329. data/test/cases/query_cache_test.rb +558 -263
  330. data/test/cases/query_logs_test.rb +293 -0
  331. data/test/cases/quoting_test.rb +39 -72
  332. data/test/cases/readonly_test.rb +50 -12
  333. data/test/cases/reaper_test.rb +22 -6
  334. data/test/cases/reflection_test.rb +207 -9
  335. data/test/cases/relation/and_test.rb +43 -0
  336. data/test/cases/relation/delegation_test.rb +32 -5
  337. data/test/cases/relation/delete_all_test.rb +27 -6
  338. data/test/cases/relation/field_ordered_values_test.rb +128 -0
  339. data/test/cases/relation/load_async_test.rb +650 -0
  340. data/test/cases/relation/merging_test.rb +80 -109
  341. data/test/cases/relation/mutation_test.rb +12 -7
  342. data/test/cases/relation/or_test.rb +11 -8
  343. data/test/cases/relation/order_test.rb +90 -0
  344. data/test/cases/relation/predicate_builder_test.rb +21 -6
  345. data/test/cases/relation/select_test.rb +162 -3
  346. data/test/cases/relation/structural_compatibility_test.rb +38 -0
  347. data/test/cases/relation/update_all_test.rb +81 -27
  348. data/test/cases/relation/where_chain_test.rb +190 -3
  349. data/test/cases/relation/where_clause_test.rb +43 -1
  350. data/test/cases/relation/where_test.rb +95 -7
  351. data/test/cases/relation/with_test.rb +165 -0
  352. data/test/cases/relation_test.rb +42 -37
  353. data/test/cases/relations_test.rb +436 -138
  354. data/test/cases/reload_models_test.rb +18 -14
  355. data/test/cases/reserved_word_test.rb +2 -1
  356. data/test/cases/result_test.rb +14 -22
  357. data/test/cases/sanitize_test.rb +89 -27
  358. data/test/cases/schema_dumper_test.rb +535 -62
  359. data/test/cases/scoping/default_scoping_test.rb +219 -35
  360. data/test/cases/scoping/named_scoping_test.rb +25 -46
  361. data/test/cases/scoping/relation_scoping_test.rb +112 -4
  362. data/test/cases/secure_password_test.rb +108 -0
  363. data/test/cases/secure_token_test.rb +72 -0
  364. data/test/cases/serialization_test.rb +1 -1
  365. data/test/cases/serialized_attribute_test.rb +305 -60
  366. data/test/cases/shard_keys_test.rb +125 -0
  367. data/test/cases/shard_selector_test.rb +45 -0
  368. data/test/cases/signed_id_test.rb +57 -5
  369. data/test/cases/statement_cache_test.rb +31 -30
  370. data/test/cases/statement_invalid_test.rb +8 -4
  371. data/test/cases/store_test.rb +73 -9
  372. data/test/cases/strict_loading_test.rb +327 -13
  373. data/test/cases/table_metadata_test.rb +16 -0
  374. data/test/cases/tasks/database_tasks_test.rb +517 -244
  375. data/test/cases/test_case.rb +254 -60
  376. data/test/cases/test_case_test.rb +69 -0
  377. data/test/cases/test_databases_test.rb +7 -3
  378. data/test/cases/test_fixtures_test.rb +19 -40
  379. data/test/cases/time_precision_test.rb +17 -5
  380. data/test/cases/timestamp_test.rb +124 -16
  381. data/test/cases/token_for_test.rb +165 -0
  382. data/test/cases/touch_later_test.rb +43 -1
  383. data/test/cases/transaction_callbacks_test.rb +325 -8
  384. data/test/cases/transaction_instrumentation_test.rb +453 -0
  385. data/test/cases/transaction_isolation_test.rb +12 -8
  386. data/test/cases/transactions_test.rb +629 -112
  387. data/test/cases/type/date_time_test.rb +7 -1
  388. data/test/cases/type/time_test.rb +9 -0
  389. data/test/cases/type/type_map_test.rb +108 -73
  390. data/test/cases/type/unsigned_integer_test.rb +12 -0
  391. data/test/cases/type_caster/connection_test.rb +19 -0
  392. data/test/cases/type_test.rb +1 -1
  393. data/test/cases/unconnected_test.rb +7 -7
  394. data/test/cases/unsafe_raw_sql_test.rb +39 -8
  395. data/test/cases/validations/absence_validation_test.rb +1 -1
  396. data/test/cases/validations/association_validation_test.rb +25 -3
  397. data/test/cases/validations/numericality_validation_test.rb +3 -9
  398. data/test/cases/validations/presence_validation_test.rb +1 -1
  399. data/test/cases/validations/uniqueness_validation_test.rb +280 -40
  400. data/test/cases/validations_test.rb +2 -2
  401. data/test/cases/view_test.rb +33 -22
  402. data/test/cases/yaml_serialization_test.rb +23 -17
  403. data/test/config.example.yml +58 -56
  404. data/test/config.rb +1 -0
  405. data/test/fixtures/1_need_quoting.yml +2 -0
  406. data/test/fixtures/accounts.yml +5 -0
  407. data/test/fixtures/aircrafts.yml +7 -0
  408. data/test/fixtures/book_identifiers.yml +11 -0
  409. data/test/fixtures/books.yml +5 -4
  410. data/test/fixtures/cake_designers.yml +5 -0
  411. data/test/fixtures/cars.yml +6 -0
  412. data/test/fixtures/chefs.yml +5 -0
  413. data/test/fixtures/clothing_items.yml +23 -0
  414. data/test/fixtures/clubs.yml +1 -1
  415. data/test/fixtures/comments.yml +2 -1
  416. data/test/fixtures/cpk_authors.yml +8 -0
  417. data/test/fixtures/cpk_books.yml +21 -0
  418. data/test/fixtures/cpk_order_agreements.yml +13 -0
  419. data/test/fixtures/cpk_order_tags.yml +15 -0
  420. data/test/fixtures/cpk_orders.yml +11 -0
  421. data/test/fixtures/cpk_posts.yml +12 -0
  422. data/test/fixtures/cpk_reviews.yml +13 -0
  423. data/test/fixtures/cpk_tags.yml +11 -0
  424. data/test/fixtures/drink_designers.yml +5 -0
  425. data/test/fixtures/encrypted_book_that_ignores_cases.yml +4 -0
  426. data/test/fixtures/encrypted_books.yml +4 -0
  427. data/test/fixtures/fk_object_to_point_to.yml +2 -0
  428. data/test/fixtures/memberships.yml +15 -15
  429. data/test/fixtures/paragraphs.yml +5 -0
  430. data/test/fixtures/parrots.yml +4 -0
  431. data/test/fixtures/people.yml +2 -0
  432. data/test/fixtures/pirates.yml +2 -2
  433. data/test/fixtures/primary_key_error/primary_key_error.yml +6 -0
  434. data/test/fixtures/references.yml +3 -3
  435. data/test/fixtures/sharded_blog_posts.yml +14 -0
  436. data/test/fixtures/sharded_blog_posts_tags.yml +27 -0
  437. data/test/fixtures/sharded_blogs.yml +8 -0
  438. data/test/fixtures/sharded_comments.yml +22 -0
  439. data/test/fixtures/sharded_tags.yml +34 -0
  440. data/test/fixtures/sponsors.yml +2 -2
  441. data/test/fixtures/topics.yml +1 -0
  442. data/test/fixtures/treasures.yml +4 -0
  443. data/test/fixtures/virtual_columns.yml +5 -0
  444. data/test/fixtures/warehouse_things.yml +1 -1
  445. data/test/migrations/old_and_new_versions/20210716122844_add_people_description.rb +5 -0
  446. data/test/migrations/old_and_new_versions/20210716123013_add_people_number_of_legs.rb +5 -0
  447. data/test/migrations/old_and_new_versions/230_add_people_hobby.rb +5 -0
  448. data/test/migrations/old_and_new_versions/231_add_people_last_name.rb +5 -0
  449. data/test/migrations/scope/1_unscoped.rb +7 -0
  450. data/test/migrations/scope/2_mysql_only.mysql.rb +7 -0
  451. data/test/models/admin/user.rb +2 -0
  452. data/test/models/admin/user_json.rb +48 -0
  453. data/test/models/attachment.rb +7 -0
  454. data/test/models/author.rb +70 -3
  455. data/test/models/author_encrypted.rb +16 -0
  456. data/test/models/bird.rb +1 -1
  457. data/test/models/book.rb +18 -11
  458. data/test/models/book_destroy_async.rb +1 -1
  459. data/test/models/book_encrypted.rb +106 -0
  460. data/test/models/book_identifier.rb +5 -0
  461. data/test/models/branch.rb +11 -0
  462. data/test/models/bulb.rb +1 -1
  463. data/test/models/car.rb +8 -1
  464. data/test/models/cat.rb +1 -1
  465. data/test/models/category.rb +3 -1
  466. data/test/models/chat_message.rb +8 -0
  467. data/test/models/chef.rb +52 -0
  468. data/test/models/clothing_item.rb +12 -0
  469. data/test/models/club.rb +4 -1
  470. data/test/models/comment.rb +7 -4
  471. data/test/models/comment_overlapping_counter_cache.rb +15 -0
  472. data/test/models/company.rb +16 -2
  473. data/test/models/computer.rb +1 -0
  474. data/test/models/contact.rb +3 -3
  475. data/test/models/contract.rb +5 -1
  476. data/test/models/cpk/author.rb +9 -0
  477. data/test/models/cpk/book.rb +55 -0
  478. data/test/models/cpk/book_destroy_async.rb +9 -0
  479. data/test/models/cpk/car.rb +9 -0
  480. data/test/models/cpk/car_review.rb +9 -0
  481. data/test/models/cpk/chapter.rb +16 -0
  482. data/test/models/cpk/chapter_destroy_async.rb +10 -0
  483. data/test/models/cpk/comment.rb +9 -0
  484. data/test/models/cpk/order.rb +55 -0
  485. data/test/models/cpk/order_agreement.rb +10 -0
  486. data/test/models/cpk/order_tag.rb +10 -0
  487. data/test/models/cpk/post.rb +10 -0
  488. data/test/models/cpk/posts_tag.rb +10 -0
  489. data/test/models/cpk/review.rb +9 -0
  490. data/test/models/cpk/tag.rb +10 -0
  491. data/test/models/cpk.rb +15 -0
  492. data/test/models/customer.rb +3 -1
  493. data/test/models/destroy_async_parent.rb +1 -1
  494. data/test/models/developer.rb +44 -3
  495. data/test/models/discount.rb +4 -0
  496. data/test/models/drink_designer.rb +1 -0
  497. data/test/models/editor.rb +8 -0
  498. data/test/models/editorship.rb +6 -0
  499. data/test/models/entry.rb +5 -0
  500. data/test/models/essay.rb +6 -1
  501. data/test/models/essay_destroy_async.rb +1 -0
  502. data/test/models/eye.rb +87 -21
  503. data/test/models/face.rb +1 -1
  504. data/test/models/friendship.rb +3 -0
  505. data/test/models/hardback.rb +7 -0
  506. data/test/models/hotel.rb +2 -0
  507. data/test/models/human.rb +2 -2
  508. data/test/models/invoice.rb +2 -1
  509. data/test/models/invokes_an_undefined_method.rb +5 -0
  510. data/test/models/line_item.rb +6 -0
  511. data/test/models/member.rb +9 -4
  512. data/test/models/membership.rb +2 -2
  513. data/test/models/message.rb +2 -1
  514. data/test/models/need_quoting.rb +5 -0
  515. data/test/models/paragraph.rb +5 -0
  516. data/test/models/parrot.rb +6 -3
  517. data/test/models/person.rb +7 -4
  518. data/test/models/pirate.rb +4 -1
  519. data/test/models/pk_autopopulated_by_a_trigger_record.rb +5 -0
  520. data/test/models/post.rb +65 -2
  521. data/test/models/post_encrypted.rb +15 -0
  522. data/test/models/post_with_prefetched_pk.rb +15 -0
  523. data/test/models/project.rb +1 -0
  524. data/test/models/publication.rb +16 -0
  525. data/test/models/raises_argument_error.rb +5 -0
  526. data/test/models/raises_no_method_error.rb +5 -0
  527. data/test/models/recipient.rb +5 -0
  528. data/test/models/reference.rb +1 -1
  529. data/test/models/reply.rb +1 -1
  530. data/test/models/room.rb +3 -0
  531. data/test/models/sharded/blog.rb +12 -0
  532. data/test/models/sharded/blog_post.rb +17 -0
  533. data/test/models/sharded/blog_post_destroy_async.rb +14 -0
  534. data/test/models/sharded/blog_post_tag.rb +11 -0
  535. data/test/models/sharded/blog_post_with_revision.rb +11 -0
  536. data/test/models/sharded/comment.rb +12 -0
  537. data/test/models/sharded/comment_destroy_async.rb +12 -0
  538. data/test/models/sharded/tag.rb +11 -0
  539. data/test/models/sharded.rb +8 -0
  540. data/test/models/ship.rb +1 -1
  541. data/test/models/shipping_line.rb +11 -0
  542. data/test/models/subscriber.rb +1 -0
  543. data/test/models/subscription.rb +3 -1
  544. data/test/models/too_long_table_name.rb +5 -0
  545. data/test/models/topic.rb +3 -1
  546. data/test/models/traffic_light.rb +2 -2
  547. data/test/models/traffic_light_encrypted.rb +21 -0
  548. data/test/models/translation.rb +9 -0
  549. data/test/models/treasure.rb +1 -1
  550. data/test/models/tyre.rb +1 -1
  551. data/test/models/user.rb +16 -0
  552. data/test/models/user_with_invalid_relation.rb +28 -0
  553. data/test/models/uuid_comment.rb +5 -0
  554. data/test/models/uuid_entry.rb +5 -0
  555. data/test/models/uuid_message.rb +5 -0
  556. data/test/models/zine.rb +2 -1
  557. data/test/schema/mysql2_specific_schema.rb +26 -12
  558. data/test/schema/postgresql_specific_schema.rb +121 -13
  559. data/test/schema/schema.rb +361 -103
  560. data/test/schema/sqlite_specific_schema.rb +17 -6
  561. data/test/schema/trilogy_specific_schema.rb +96 -0
  562. data/test/support/adapter_helper.rb +104 -0
  563. data/test/support/async_helper.rb +15 -0
  564. data/test/support/config.rb +1 -1
  565. data/test/support/connection.rb +14 -2
  566. data/test/support/ddl_helper.rb +1 -1
  567. data/test/support/fake_adapter.rb +42 -0
  568. data/test/support/load_schema_helper.rb +22 -0
  569. data/test/support/marshal_compatibility_fixtures/IBM_DB/rails_6_1_topic.dump +0 -0
  570. data/test/support/marshal_compatibility_fixtures/IBM_DB/rails_6_1_topic_associations.dump +0 -0
  571. data/test/support/marshal_compatibility_fixtures/IBM_DB/rails_7_1_topic.dump +0 -0
  572. data/test/support/marshal_compatibility_fixtures/IBM_DB/rails_7_1_topic_associations.dump +0 -0
  573. data/test/support/marshal_compatibility_fixtures/Mysql2/rails_6_1_topic.dump +0 -0
  574. data/test/support/marshal_compatibility_fixtures/Mysql2/rails_6_1_topic_associations.dump +0 -0
  575. data/test/support/marshal_compatibility_fixtures/Mysql2/rails_7_1_topic.dump +0 -0
  576. data/test/support/marshal_compatibility_fixtures/Mysql2/rails_7_1_topic_associations.dump +0 -0
  577. data/test/support/marshal_compatibility_fixtures/PostgreSQL/rails_6_1_topic.dump +0 -0
  578. data/test/support/marshal_compatibility_fixtures/PostgreSQL/rails_6_1_topic_associations.dump +0 -0
  579. data/test/support/marshal_compatibility_fixtures/PostgreSQL/rails_7_1_topic.dump +0 -0
  580. data/test/support/marshal_compatibility_fixtures/PostgreSQL/rails_7_1_topic_associations.dump +0 -0
  581. data/test/support/marshal_compatibility_fixtures/SQLite/rails_6_1_topic.dump +0 -0
  582. data/test/support/marshal_compatibility_fixtures/SQLite/rails_6_1_topic_associations.dump +0 -0
  583. data/test/support/marshal_compatibility_fixtures/SQLite/rails_7_1_topic.dump +0 -0
  584. data/test/support/marshal_compatibility_fixtures/SQLite/rails_7_1_topic_associations.dump +0 -0
  585. data/test/support/marshal_compatibility_fixtures/Trilogy/rails_6_1_topic.dump +0 -0
  586. data/test/support/marshal_compatibility_fixtures/Trilogy/rails_6_1_topic_associations.dump +0 -0
  587. data/test/support/marshal_compatibility_fixtures/Trilogy/rails_7_1_topic.dump +0 -0
  588. data/test/support/marshal_compatibility_fixtures/Trilogy/rails_7_1_topic_associations.dump +0 -0
  589. data/test/support/schema_dumping_helper.rb +15 -9
  590. data/test/support/tools.rb +37 -0
  591. data/test/support/yaml_compatibility_fixtures/rails_4_1_no_symbol.yml +22 -0
  592. metadata +247 -15
@@ -19,6 +19,7 @@ require "models/order"
19
19
  require "models/parrot"
20
20
  require "models/pirate"
21
21
  require "models/project"
22
+ require "models/price_estimate"
22
23
  require "models/ship"
23
24
  require "models/ship_part"
24
25
  require "models/squeak"
@@ -34,6 +35,14 @@ require "models/organization"
34
35
  require "models/guitar"
35
36
  require "models/tuning_peg"
36
37
  require "models/reply"
38
+ require "models/attachment"
39
+ require "models/translation"
40
+ require "models/chef"
41
+ require "models/cake_designer"
42
+ require "models/drink_designer"
43
+ require "models/cpk"
44
+ require "models/human"
45
+ require "models/face"
37
46
 
38
47
  class TestAutosaveAssociationsInGeneral < ActiveRecord::TestCase
39
48
  def test_autosave_works_even_when_other_callbacks_update_the_parent_model
@@ -87,6 +96,56 @@ class TestAutosaveAssociationsInGeneral < ActiveRecord::TestCase
87
96
  assert_predicate r, :valid?
88
97
  end
89
98
 
99
+ def test_autosave_collection_association_callbacks_get_called_once
100
+ ship_with_saving_stack = Class.new(Ship) do
101
+ def save_collection_association(reflection)
102
+ @count ||= 0
103
+ @count += 1 if reflection.name == :parts
104
+ super
105
+ end
106
+ end
107
+
108
+ ship = ship_with_saving_stack.new(name: "Nights Dirty Lightning")
109
+ ship.parts.build(name: "part")
110
+ ship.save!
111
+ assert_equal 1, ship.instance_variable_get(:@count)
112
+ end
113
+
114
+ def test_autosave_has_one_association_callbacks_get_called_once
115
+ # a bidirectional autosave is required to trigger multiple calls to
116
+ # save_has_one_association
117
+ assert Ship.reflect_on_association(:pirate).options[:autosave]
118
+ assert Pirate.reflect_on_association(:ship).options[:autosave]
119
+
120
+ pirate_with_saving_stack = Class.new(Pirate) do
121
+ def save_has_one_association(reflection)
122
+ @count ||= 0
123
+ @count += 1 if reflection.name == :ship
124
+ super
125
+ end
126
+ end
127
+
128
+ pirate = pirate_with_saving_stack.new(catchphrase: "Aye")
129
+ pirate.build_ship(name: "Nights Dirty Lightning")
130
+ pirate.save!
131
+ assert_equal 1, pirate.instance_variable_get(:@count)
132
+ end
133
+
134
+ def test_autosave_belongs_to_association_callbacks_get_called_once
135
+ ship_with_saving_stack = Class.new(Ship) do
136
+ def save_belongs_to_association(reflection)
137
+ @count ||= 0
138
+ @count += 1 if reflection.name == :pirate
139
+ super
140
+ end
141
+ end
142
+
143
+ ship = ship_with_saving_stack.new(name: "Nights Dirty Lightning")
144
+ ship.build_pirate(catchphrase: "Aye")
145
+ ship.save!
146
+ assert_equal 1, ship.instance_variable_get(:@count)
147
+ end
148
+
90
149
  def test_should_not_add_the_same_callbacks_multiple_times_for_has_one
91
150
  assert_no_difference_when_adding_callbacks_twice_for Pirate, :ship
92
151
  end
@@ -114,6 +173,7 @@ class TestAutosaveAssociationsInGeneral < ActiveRecord::TestCase
114
173
  private
115
174
  def assert_no_difference_when_adding_callbacks_twice_for(model, association_name)
116
175
  reflection = model.reflect_on_association(association_name)
176
+ assert_not_nil reflection
117
177
  assert_no_difference "callbacks_for_model(#{model.name}).length" do
118
178
  model.send(:add_autosave_association_callbacks, reflection)
119
179
  end
@@ -213,19 +273,25 @@ class TestDefaultAutosaveAssociationOnAHasOneAssociation < ActiveRecord::TestCas
213
273
  def test_not_resaved_when_unchanged
214
274
  firm = Firm.all.merge!(includes: :account).first
215
275
  firm.name += "-changed"
216
- assert_queries(1) { firm.save! }
276
+ assert_queries_count(3) { firm.save! }
217
277
 
218
278
  firm = Firm.first
219
279
  firm.account = Account.first
220
- assert_queries(Firm.partial_writes? ? 0 : 1) { firm.save! }
280
+ assert_queries_count(Firm.partial_updates? ? 0 : 1) { firm.save! }
221
281
 
222
282
  firm = Firm.first.dup
223
283
  firm.account = Account.first
224
- assert_queries(2) { firm.save! }
284
+ assert_queries_count(4) { firm.save! }
225
285
 
226
286
  firm = Firm.first.dup
227
287
  firm.account = Account.first.dup
228
- assert_queries(2) { firm.save! }
288
+ assert_queries_count(4) { firm.save! }
289
+ end
290
+
291
+ def test_should_not_load_the_associated_model
292
+ firm = companies(:first_firm)
293
+ firm.reset_unvalidated_account
294
+ assert_no_queries { firm.save! }
229
295
  end
230
296
 
231
297
  def test_callbacks_firing_order_on_create
@@ -246,6 +312,75 @@ class TestDefaultAutosaveAssociationOnAHasOneAssociation < ActiveRecord::TestCas
246
312
  eye.update(iris_attributes: { color: "blue" })
247
313
  assert_equal [false, false, false, false], eye.after_save_callbacks_stack
248
314
  end
315
+
316
+ def test_callbacks_on_child_when_parent_autosaves_child
317
+ eye = Eye.create!(iris: Iris.new)
318
+ assert_equal 1, eye.iris.before_validation_callbacks_counter
319
+ assert_equal 1, eye.iris.before_create_callbacks_counter
320
+ assert_equal 1, eye.iris.before_save_callbacks_counter
321
+ assert_equal 1, eye.iris.after_validation_callbacks_counter
322
+ assert_equal 1, eye.iris.after_create_callbacks_counter
323
+ assert_equal 1, eye.iris.after_save_callbacks_counter
324
+ end
325
+
326
+ def test_callbacks_on_child_when_parent_autosaves_child_twice
327
+ eye = Eye.create!(iris: Iris.new)
328
+ eye.update!(iris: Iris.new)
329
+ assert_equal 1, eye.iris.before_validation_callbacks_counter
330
+ assert_equal 1, eye.iris.before_create_callbacks_counter
331
+ assert_equal 1, eye.iris.before_save_callbacks_counter
332
+ assert_equal 1, eye.iris.after_validation_callbacks_counter
333
+ assert_equal 1, eye.iris.after_create_callbacks_counter
334
+ assert_equal 1, eye.iris.after_save_callbacks_counter
335
+ end
336
+
337
+ def test_callbacks_on_child_when_parent_autosaves_polymorphic_child_with_inverse_of
338
+ drink_designer = DrinkDesigner.create!(chef: ChefWithPolymorphicInverseOf.new)
339
+ assert_equal 1, drink_designer.chef.before_validation_callbacks_counter
340
+ assert_equal 1, drink_designer.chef.before_create_callbacks_counter
341
+ assert_equal 1, drink_designer.chef.before_save_callbacks_counter
342
+ assert_equal 1, drink_designer.chef.after_validation_callbacks_counter
343
+ assert_equal 1, drink_designer.chef.after_create_callbacks_counter
344
+ assert_equal 1, drink_designer.chef.after_save_callbacks_counter
345
+ end
346
+
347
+ def test_callbacks_on_child_when_child_autosaves_parent
348
+ iris = Iris.create!(eye: Eye.new)
349
+ assert_equal 1, iris.before_validation_callbacks_counter
350
+ assert_equal 1, iris.before_create_callbacks_counter
351
+ assert_equal 1, iris.before_save_callbacks_counter
352
+ assert_equal 1, iris.after_validation_callbacks_counter
353
+ assert_equal 1, iris.after_create_callbacks_counter
354
+ assert_equal 1, iris.after_save_callbacks_counter
355
+ end
356
+
357
+ def test_callbacks_on_child_when_child_autosaves_parent_twice
358
+ iris = Iris.create!(eye: Eye.new)
359
+ iris.update!(eye: Eye.new)
360
+ assert_equal 2, iris.before_validation_callbacks_counter
361
+ assert_equal 1, iris.before_create_callbacks_counter
362
+ assert_equal 2, iris.before_save_callbacks_counter
363
+ assert_equal 2, iris.after_validation_callbacks_counter
364
+ assert_equal 1, iris.after_create_callbacks_counter
365
+ assert_equal 2, iris.after_save_callbacks_counter
366
+ end
367
+
368
+ def test_callbacks_on_child_when_polymorphic_child_with_inverse_of_autosaves_parent
369
+ chef = ChefWithPolymorphicInverseOf.create!(employable: DrinkDesigner.new)
370
+ assert_equal 1, chef.before_validation_callbacks_counter
371
+ assert_equal 1, chef.before_create_callbacks_counter
372
+ assert_equal 1, chef.before_save_callbacks_counter
373
+ assert_equal 1, chef.after_validation_callbacks_counter
374
+ assert_equal 1, chef.after_create_callbacks_counter
375
+ assert_equal 1, chef.after_save_callbacks_counter
376
+ end
377
+
378
+ def test_foreign_key_attribute_is_not_set_unless_changed
379
+ eye = Eye.create!(iris_with_read_only_foreign_key_attributes: { color: "honey" })
380
+ assert_nothing_raised do
381
+ eye.update!(override_iris_with_read_only_foreign_key_color: true)
382
+ end
383
+ end
249
384
  end
250
385
 
251
386
  class TestDefaultAutosaveAssociationOnABelongsToAssociation < ActiveRecord::TestCase
@@ -430,6 +565,18 @@ class TestDefaultAutosaveAssociationOnABelongsToAssociation < ActiveRecord::Test
430
565
  assert_equal true, squeak.mouse.present?
431
566
  assert_equal true, squeak.valid?
432
567
  end
568
+
569
+ test "composite primary key autosave" do
570
+ assert_nothing_raised do
571
+ Cpk::Order.create!(id: [1, 2], book: Cpk::Book.new(title: "Book", id: [3, 4]))
572
+ end
573
+ end
574
+
575
+ def test_should_not_load_the_associated_model
576
+ tagging = taggings(:welcome_general)
577
+ tagging.reset_tag
578
+ assert_no_queries { tagging.save! }
579
+ end
433
580
  end
434
581
 
435
582
  class TestDefaultAutosaveAssociationOnAHasManyAssociationWithAcceptsNestedAttributes < ActiveRecord::TestCase
@@ -462,8 +609,8 @@ class TestDefaultAutosaveAssociationOnAHasManyAssociationWithAcceptsNestedAttrib
462
609
  end
463
610
 
464
611
  def test_errors_should_be_indexed_when_global_flag_is_set
465
- old_attribute_config = ActiveRecord::Base.index_nested_attribute_errors
466
- ActiveRecord::Base.index_nested_attribute_errors = true
612
+ old_attribute_config = ActiveRecord.index_nested_attribute_errors
613
+ ActiveRecord.index_nested_attribute_errors = true
467
614
 
468
615
  molecule = Molecule.new
469
616
  valid_electron = Electron.new(name: "electron")
@@ -477,7 +624,7 @@ class TestDefaultAutosaveAssociationOnAHasManyAssociationWithAcceptsNestedAttrib
477
624
  assert_equal ["can't be blank"], molecule.errors["electrons[1].name"]
478
625
  assert_not_equal ["can't be blank"], molecule.errors["electrons.name"]
479
626
  ensure
480
- ActiveRecord::Base.index_nested_attribute_errors = old_attribute_config
627
+ ActiveRecord.index_nested_attribute_errors = old_attribute_config
481
628
  end
482
629
 
483
630
  def test_errors_details_should_be_set
@@ -508,9 +655,138 @@ class TestDefaultAutosaveAssociationOnAHasManyAssociationWithAcceptsNestedAttrib
508
655
  assert_equal [], guitar.errors.details[:"tuning_pegs.pitch"]
509
656
  end
510
657
 
658
+ def test_errors_details_with_error_on_base_should_be_indexed_when_passed_as_array
659
+ reference = Class.new(ActiveRecord::Base) do
660
+ self.table_name = "references"
661
+ def self.name; "Reference"; end
662
+
663
+ validate :should_be_favorite
664
+
665
+ private
666
+ def should_be_favorite
667
+ errors.add(:base, "should be favorite") unless favorite?
668
+ end
669
+ end
670
+
671
+ person = Class.new(ActiveRecord::Base) do
672
+ self.table_name = "people"
673
+ has_many :references, autosave: true, index_errors: true, anonymous_class: reference
674
+ def self.name; "Person"; end
675
+ end
676
+
677
+ p = person.new
678
+ reference_valid = reference.new(favorite: true)
679
+ reference_invalid = reference.new(favorite: false)
680
+ p.references = [reference_valid, reference_invalid]
681
+
682
+ assert_predicate reference_valid, :valid?
683
+ assert_not_predicate reference_invalid, :valid?
684
+ assert_not_predicate p, :valid?
685
+ assert_equal [{ error: "should be favorite" }], p.errors.details[:"references[1].base"]
686
+ assert_equal "should be favorite", p.errors[:"references[1].base"].first
687
+ assert_equal ["References[1] should be favorite"], p.errors.full_messages
688
+ end
689
+
690
+ def test_indexed_errors_should_be_properly_translated
691
+ old_i18n_customize_full_message = ActiveModel::Error.i18n_customize_full_message
692
+ ActiveModel::Error.i18n_customize_full_message = true
693
+ I18n.backend.store_translations(
694
+ :en,
695
+ activerecord: {
696
+ errors: {
697
+ models: {
698
+ "person/references": {
699
+ format: "%{message}"
700
+ }
701
+ }
702
+ }
703
+ }
704
+ )
705
+ reference = Class.new(ActiveRecord::Base) do
706
+ self.table_name = "references"
707
+ def self.name; "Reference"; end
708
+
709
+ validate :should_be_favorite
710
+ validates_presence_of :job_id
711
+
712
+ private
713
+ def should_be_favorite
714
+ errors.add(:base, "should be favorite") unless favorite?
715
+ end
716
+ end
717
+
718
+ person = Class.new(ActiveRecord::Base) do
719
+ self.table_name = "people"
720
+ has_many :references, autosave: true, index_errors: true, anonymous_class: reference
721
+ def self.name; "Person"; end
722
+ end
723
+
724
+ p = person.new
725
+ reference_valid = reference.new(favorite: true, job_id: 1)
726
+ reference_invalid = reference.new(favorite: false)
727
+ p.references = [reference_valid, reference_invalid]
728
+
729
+ assert_predicate reference_valid, :valid?
730
+ assert_not_predicate reference_invalid, :valid?
731
+ assert_not_predicate p, :valid?
732
+ assert_equal ["should be favorite", "can't be blank"], p.errors.full_messages
733
+ ensure
734
+ ActiveModel::Error.i18n_customize_full_message = old_i18n_customize_full_message
735
+ I18n.backend = I18n::Backend::Simple.new
736
+ end
737
+
738
+ def test_indexed_errors_on_base_attribute_should_be_properly_translated
739
+ I18n.backend.store_translations(
740
+ :en,
741
+ activerecord: {
742
+ attributes: {
743
+ person: {
744
+ reference: "Super reference",
745
+ },
746
+ reference: {
747
+ base: ""
748
+ }
749
+ }
750
+ }
751
+ )
752
+ reference = Class.new(ActiveRecord::Base) do
753
+ self.table_name = "references"
754
+ def self.name; "Reference"; end
755
+
756
+ validate :should_be_favorite
757
+ validates_presence_of :job_id
758
+
759
+ private
760
+ def should_be_favorite
761
+ errors.add(:base, "should be favorite") unless favorite?
762
+ end
763
+ end
764
+
765
+ person = Class.new(ActiveRecord::Base) do
766
+ self.table_name = "people"
767
+ def self.name; "Person"; end
768
+
769
+ has_one :reference, autosave: true, anonymous_class: reference
770
+ validates :reference, presence: true
771
+ end
772
+
773
+ p = person.new
774
+ assert_not_predicate p, :valid?
775
+ assert_equal ["Super reference can't be blank"], p.errors.full_messages
776
+
777
+ reference_invalid = reference.new(favorite: false)
778
+ p.reference = reference_invalid
779
+
780
+ assert_not_predicate reference_invalid, :valid?
781
+ assert_not_predicate p, :valid?
782
+ assert_equal [" should be favorite", "Reference job can't be blank"], p.errors.full_messages
783
+ ensure
784
+ I18n.backend = I18n::Backend::Simple.new
785
+ end
786
+
511
787
  def test_errors_details_should_be_indexed_when_global_flag_is_set
512
- old_attribute_config = ActiveRecord::Base.index_nested_attribute_errors
513
- ActiveRecord::Base.index_nested_attribute_errors = true
788
+ old_attribute_config = ActiveRecord.index_nested_attribute_errors
789
+ ActiveRecord.index_nested_attribute_errors = true
514
790
 
515
791
  molecule = Molecule.new
516
792
  valid_electron = Electron.new(name: "electron")
@@ -524,7 +800,7 @@ class TestDefaultAutosaveAssociationOnAHasManyAssociationWithAcceptsNestedAttrib
524
800
  assert_equal [{ error: :blank }], molecule.errors.details[:"electrons[1].name"]
525
801
  assert_equal [], molecule.errors.details[:"electrons.name"]
526
802
  ensure
527
- ActiveRecord::Base.index_nested_attribute_errors = old_attribute_config
803
+ ActiveRecord.index_nested_attribute_errors = old_attribute_config
528
804
  end
529
805
 
530
806
  def test_valid_adding_with_nested_attributes
@@ -541,7 +817,7 @@ class TestDefaultAutosaveAssociationOnAHasManyAssociationWithAcceptsNestedAttrib
541
817
  end
542
818
 
543
819
  class TestDefaultAutosaveAssociationOnAHasManyAssociation < ActiveRecord::TestCase
544
- fixtures :companies, :developers
820
+ fixtures :companies, :developers, :cpk_order_agreements, :cpk_orders, :cpk_books
545
821
 
546
822
  def test_invalid_adding
547
823
  firm = Firm.find(1)
@@ -603,6 +879,37 @@ class TestDefaultAutosaveAssociationOnAHasManyAssociation < ActiveRecord::TestCa
603
879
  assert_equal no_of_clients + 1, Client.count
604
880
  end
605
881
 
882
+ def test_circular_autosave_does_not_validate_children
883
+ skip "DB2 limitation: circular autosave self-reference can trigger unbounded memory growth" if current_adapter?(:IBM_DBAdapter)
884
+
885
+ person = Class.new(ActiveRecord::Base) {
886
+ self.table_name = "readers"
887
+ validate :should_be_funny
888
+ def self.name; "Reader"; end
889
+
890
+ attribute :catch_phrase, :string
891
+ attribute :reader_id
892
+
893
+ has_many :children, autosave: true, anonymous_class: self
894
+ belongs_to :parent, autosave: true, anonymous_class: self
895
+
896
+ private
897
+ def should_be_funny
898
+ unless catch_phrase == "funny"
899
+ errors.add :base, "not funny"
900
+ end
901
+ end
902
+ }
903
+ c = person.new(catch_phrase: "boring")
904
+ c.children << c
905
+ c.post_id = 0
906
+ c.person_id = 0
907
+ c.save
908
+
909
+ assert_not_predicate c, :persisted?
910
+ assert_not_predicate c, :valid?
911
+ end
912
+
606
913
  def test_parent_should_save_children_record_with_foreign_key_validation_set_in_before_save_callback
607
914
  company = NewlyContractedCompany.new(name: "test")
608
915
 
@@ -675,6 +982,44 @@ class TestDefaultAutosaveAssociationOnAHasManyAssociation < ActiveRecord::TestCa
675
982
  assert_includes firm.clients, companies(:second_client)
676
983
  end
677
984
 
985
+ def test_assign_ids_with_belongs_to_cpk_model
986
+ order_agreements = [cpk_order_agreements(:order_agreement_one).id, cpk_order_agreements(:order_agreement_two).id]
987
+ order = cpk_orders(:cpk_groceries_order_1)
988
+
989
+ assert_empty order.order_agreements
990
+
991
+ order.order_agreement_ids = order_agreements
992
+ order.save
993
+ order.reload
994
+
995
+ assert_equal order_agreements, order.order_agreement_ids
996
+ assert_equal 2, order.order_agreements.length
997
+ assert_includes order.order_agreements, cpk_order_agreements(:order_agreement_two)
998
+ end
999
+
1000
+ def test_assign_ids_with_cpk_for_two_models
1001
+ book_ids = [cpk_books(:cpk_great_author_first_book).id, cpk_books(:cpk_great_author_second_book).id]
1002
+ order = cpk_orders(:cpk_groceries_order_1)
1003
+
1004
+ assert_empty order.books
1005
+
1006
+ order.book_ids = book_ids
1007
+ order.save
1008
+ order.reload
1009
+
1010
+ assert_equal book_ids, order.book_ids
1011
+ assert_equal 2, order.books.length
1012
+ assert_includes order.books, cpk_books(:cpk_great_author_first_book)
1013
+ assert_includes order.books, cpk_books(:cpk_great_author_second_book)
1014
+ end
1015
+
1016
+ def test_has_one_cpk_has_one_autosave_with_id
1017
+ book = Cpk::Book.create!(id: [1, 3], shop_id: 2)
1018
+ order = Cpk::OrderWithPrimaryKeyAssociatedBook.create!(book: book, shop_id: 2)
1019
+
1020
+ assert_equal(book.order.id, order.id)
1021
+ end
1022
+
678
1023
  def test_assign_ids_for_through_a_belongs_to
679
1024
  firm = Firm.new("name" => "Apple")
680
1025
  firm.developer_ids = [developers(:david).id, developers(:jamis).id]
@@ -687,11 +1032,11 @@ class TestDefaultAutosaveAssociationOnAHasManyAssociation < ActiveRecord::TestCa
687
1032
  def test_build_before_save
688
1033
  company = companies(:first_firm)
689
1034
 
690
- new_client = assert_queries(0) { company.clients_of_firm.build("name" => "Another Client") }
1035
+ new_client = assert_queries_count(0) { company.clients_of_firm.build("name" => "Another Client") }
691
1036
  assert_not_predicate company.clients_of_firm, :loaded?
692
1037
 
693
1038
  company.name += "-changed"
694
- assert_queries(2) { assert company.save }
1039
+ assert_queries_count(4) { assert company.save }
695
1040
  assert_predicate new_client, :persisted?
696
1041
  assert_equal 3, company.clients_of_firm.reload.size
697
1042
  end
@@ -699,21 +1044,21 @@ class TestDefaultAutosaveAssociationOnAHasManyAssociation < ActiveRecord::TestCa
699
1044
  def test_build_many_before_save
700
1045
  company = companies(:first_firm)
701
1046
 
702
- assert_queries(0) { company.clients_of_firm.build([{ "name" => "Another Client" }, { "name" => "Another Client II" }]) }
1047
+ assert_queries_count(0) { company.clients_of_firm.build([{ "name" => "Another Client" }, { "name" => "Another Client II" }]) }
703
1048
 
704
1049
  company.name += "-changed"
705
- assert_queries(3) { assert company.save }
1050
+ assert_queries_count(5) { assert company.save }
706
1051
  assert_equal 4, company.clients_of_firm.reload.size
707
1052
  end
708
1053
 
709
1054
  def test_build_via_block_before_save
710
1055
  company = companies(:first_firm)
711
1056
 
712
- new_client = assert_queries(0) { company.clients_of_firm.build { |client| client.name = "Another Client" } }
1057
+ new_client = assert_queries_count(0) { company.clients_of_firm.build { |client| client.name = "Another Client" } }
713
1058
  assert_not_predicate company.clients_of_firm, :loaded?
714
1059
 
715
1060
  company.name += "-changed"
716
- assert_queries(2) { assert company.save }
1061
+ assert_queries_count(4) { assert company.save }
717
1062
  assert_predicate new_client, :persisted?
718
1063
  assert_equal 3, company.clients_of_firm.reload.size
719
1064
  end
@@ -721,14 +1066,14 @@ class TestDefaultAutosaveAssociationOnAHasManyAssociation < ActiveRecord::TestCa
721
1066
  def test_build_many_via_block_before_save
722
1067
  company = companies(:first_firm)
723
1068
 
724
- assert_queries(0) do
1069
+ assert_queries_count(0) do
725
1070
  company.clients_of_firm.build([{ "name" => "Another Client" }, { "name" => "Another Client II" }]) do |client|
726
1071
  client.name = "changed"
727
1072
  end
728
1073
  end
729
1074
 
730
1075
  company.name += "-changed"
731
- assert_queries(3) { assert company.save }
1076
+ assert_queries_count(5) { assert company.save }
732
1077
  assert_equal 4, company.clients_of_firm.reload.size
733
1078
  end
734
1079
 
@@ -749,6 +1094,12 @@ class TestDefaultAutosaveAssociationOnAHasManyAssociation < ActiveRecord::TestCa
749
1094
  assert_equal 2, firm.clients.length
750
1095
  assert_includes firm.clients, Client.find_by_name("New Client")
751
1096
  end
1097
+
1098
+ def test_should_not_load_the_associated_model
1099
+ firm = companies(:first_firm)
1100
+ firm.clients.reset
1101
+ assert_no_queries { firm.save! }
1102
+ end
752
1103
  end
753
1104
 
754
1105
  class TestDefaultAutosaveAssociationOnNewRecord < ActiveRecord::TestCase
@@ -846,6 +1197,8 @@ class TestDestroyAsPartOfAutosaveAssociation < ActiveRecord::TestCase
846
1197
  Parrot.delete_all
847
1198
  @ship.delete
848
1199
  @pirate.delete
1200
+ Cpk::Book.delete_all
1201
+ Cpk::Order.delete_all
849
1202
  end
850
1203
 
851
1204
  # reload
@@ -935,6 +1288,18 @@ class TestDestroyAsPartOfAutosaveAssociation < ActiveRecord::TestCase
935
1288
  assert_nil Pirate.find_by_id(id)
936
1289
  end
937
1290
 
1291
+ # belongs_to for CPK
1292
+ def test_autosave_cpk_association_should_destroy_parent_association_when_marked_for_destruction
1293
+ book = Cpk::Book.new(title: "Book", id: [1, 2])
1294
+ Cpk::Order.create!(id: [3, 4], book: book)
1295
+
1296
+ book.order.mark_for_destruction
1297
+
1298
+ assert book.save
1299
+ assert_nil book.reload.order
1300
+ assert_nil Cpk::Order.find_by(id: 4, shop_id: 3)
1301
+ end
1302
+
938
1303
  def test_should_skip_validation_on_a_parent_association_if_marked_for_destruction
939
1304
  @ship.pirate.catchphrase = ""
940
1305
  assert_not_predicate @ship, :valid?
@@ -1068,7 +1433,7 @@ class TestDestroyAsPartOfAutosaveAssociation < ActiveRecord::TestCase
1068
1433
  end
1069
1434
 
1070
1435
  def test_should_save_new_record_that_has_same_value_as_existing_record_marked_for_destruction_on_field_that_has_unique_index
1071
- Bird.connection.add_index :birds, :name, unique: true
1436
+ Bird.lease_connection.add_index :birds, :name, unique: true
1072
1437
 
1073
1438
  3.times { |i| @pirate.birds.create(name: "unique_birds_#{i}") }
1074
1439
 
@@ -1078,7 +1443,7 @@ class TestDestroyAsPartOfAutosaveAssociation < ActiveRecord::TestCase
1078
1443
 
1079
1444
  assert_equal 3, @pirate.birds.reload.length
1080
1445
  ensure
1081
- Bird.connection.remove_index :birds, column: :name
1446
+ Bird.lease_connection.remove_index :birds, column: :name
1082
1447
  end
1083
1448
 
1084
1449
  # Add and remove callbacks tests for association collections.
@@ -1128,7 +1493,7 @@ class TestDestroyAsPartOfAutosaveAssociation < ActiveRecord::TestCase
1128
1493
 
1129
1494
  assert_empty @pirate.reload.parrots
1130
1495
 
1131
- join_records = Pirate.connection.select_all("SELECT * FROM parrots_pirates WHERE pirate_id = #{@pirate.id}")
1496
+ join_records = Pirate.lease_connection.select_all("SELECT * FROM parrots_pirates WHERE pirate_id = #{@pirate.id}")
1132
1497
  assert_empty join_records
1133
1498
  end
1134
1499
 
@@ -1159,6 +1524,86 @@ class TestDestroyAsPartOfAutosaveAssociation < ActiveRecord::TestCase
1159
1524
  assert_predicate @pirate, :valid?
1160
1525
  end
1161
1526
 
1527
+ def test_should_be_valid_on_habtm_if_persisted_and_unchanged
1528
+ skip "DB2 limitation: HABTM autosave validates unchanged persisted associated records" if current_adapter?(:IBM_DBAdapter)
1529
+
1530
+ parrot = @pirate.parrots.create!(name: "parrots_1")
1531
+ parrot.update_column(:name, "")
1532
+ parrot.reload
1533
+ assert_not_predicate parrot, :valid?
1534
+
1535
+ new_pirate = Pirate.new(catchphrase: "Arr")
1536
+ new_pirate.parrots = @pirate.parrots
1537
+ new_pirate.save!
1538
+ end
1539
+
1540
+ def test_should_be_invalid_on_habtm_when_any_record_in_the_association_chain_is_invalid_and_was_changed
1541
+ treasure = @pirate.treasures.create!(name: "gold")
1542
+ estimate = treasure.price_estimates.create!(price: 1)
1543
+ estimate.update_columns(price: "not a number")
1544
+
1545
+ assert_not_predicate estimate, :valid?
1546
+
1547
+ treasures = @pirate.treasures.eager_load(:price_estimates).to_a
1548
+ treasures.first.price_estimates.first.price = "not a price"
1549
+ new_pirate = Pirate.new(
1550
+ catchphrase: "Arr",
1551
+ treasures: treasures,
1552
+ )
1553
+
1554
+ assert_raises(ActiveRecord::RecordInvalid) do
1555
+ new_pirate.save!
1556
+ end
1557
+ assert_equal(["Treasures is invalid"], new_pirate.errors.full_messages)
1558
+ end
1559
+
1560
+ def test_should_be_invalid_on_habtm_when_any_record_in_the_association_chain_is_invalid_and_was_changed_with_autosave
1561
+ super_pirate = Class.new(Pirate) do
1562
+ self.table_name = "pirates"
1563
+ has_many :great_treasures, class_name: "Treasure", foreign_key: "looter_id", autosave: true
1564
+
1565
+ def self.name
1566
+ "SuperPirate"
1567
+ end
1568
+ end
1569
+ @pirate = super_pirate.create(catchphrase: "Don' botharrr talkin' like one, savvy?")
1570
+ treasure = @pirate.great_treasures.create!(name: "gold")
1571
+ estimate = treasure.price_estimates.create!(price: 1)
1572
+ estimate.update_columns(price: "not a number")
1573
+
1574
+ assert_not_predicate estimate, :valid?
1575
+
1576
+ treasures = @pirate.great_treasures.eager_load(:price_estimates).to_a
1577
+ treasures.first.price_estimates.first.price = "not a price"
1578
+ new_pirate = super_pirate.new(
1579
+ catchphrase: "Arr",
1580
+ great_treasures: treasures,
1581
+ )
1582
+
1583
+ assert_raises(ActiveRecord::RecordInvalid) do
1584
+ new_pirate.save!
1585
+ end
1586
+ assert_equal(["Great treasures price estimates price is not a number"], new_pirate.errors.full_messages)
1587
+ end
1588
+
1589
+ def test_should_be_valid_on_habtm_when_any_record_in_the_association_chain_is_invalid_but_was_not_changed
1590
+ treasure = @pirate.treasures.create!(name: "gold")
1591
+ estimate = treasure.price_estimates.create!(price: 1)
1592
+ estimate.update_columns(price: "not a number")
1593
+
1594
+ assert_not_predicate estimate, :valid?
1595
+
1596
+ treasures = @pirate.treasures.eager_load(:price_estimates).to_a
1597
+ new_pirate = Pirate.new(
1598
+ catchphrase: "Arr",
1599
+ treasures: treasures,
1600
+ )
1601
+
1602
+ assert_nothing_raised do
1603
+ new_pirate.save!
1604
+ end
1605
+ end
1606
+
1162
1607
  def test_a_child_marked_for_destruction_should_not_be_destroyed_twice_while_saving_habtm
1163
1608
  @pirate.parrots.create!(name: "parrots_1")
1164
1609
 
@@ -1224,7 +1669,7 @@ class TestDestroyAsPartOfAutosaveAssociation < ActiveRecord::TestCase
1224
1669
  end
1225
1670
 
1226
1671
  class TestAutosaveAssociationOnAHasOneAssociation < ActiveRecord::TestCase
1227
- self.use_transactional_tests = false unless supports_savepoints?
1672
+ fixtures :chefs, :cake_designers, :drink_designers
1228
1673
 
1229
1674
  def setup
1230
1675
  super
@@ -1240,9 +1685,9 @@ class TestAutosaveAssociationOnAHasOneAssociation < ActiveRecord::TestCase
1240
1685
  end
1241
1686
 
1242
1687
  def test_should_automatically_save_the_associated_model
1243
- @pirate.ship.name = "The Vile Insanity"
1688
+ @pirate.ship.name = "The Vile Serpent"
1244
1689
  @pirate.save
1245
- assert_equal "The Vile Insanity", @pirate.reload.ship.name
1690
+ assert_equal "The Vile Serpent", @pirate.reload.ship.name
1246
1691
  end
1247
1692
 
1248
1693
  def test_changed_for_autosave_should_handle_cycles
@@ -1251,14 +1696,22 @@ class TestAutosaveAssociationOnAHasOneAssociation < ActiveRecord::TestCase
1251
1696
 
1252
1697
  @parrot = @pirate.parrots.create(name: "some_name")
1253
1698
  @parrot.name = "changed_name"
1254
- assert_queries(1) { @ship.save! }
1699
+ assert_queries_count(3) { @ship.save! }
1255
1700
  assert_no_queries { @ship.save! }
1256
1701
  end
1257
1702
 
1258
1703
  def test_should_automatically_save_bang_the_associated_model
1259
- @pirate.ship.name = "The Vile Insanity"
1704
+ @pirate.ship.name = "The Vile Serpent"
1260
1705
  @pirate.save!
1261
- assert_equal "The Vile Insanity", @pirate.reload.ship.name
1706
+ assert_equal "The Vile Serpent", @pirate.reload.ship.name
1707
+ end
1708
+
1709
+ def test_should_automatically_save_bang_the_associated_model_if_it_sets_the_inverse_record
1710
+ pirate = Pirate.new(catchphrase: "Savvy?")
1711
+ ship = Ship.new(name: "Black Pearl")
1712
+ ship.pirate = pirate
1713
+ pirate.save!
1714
+ assert_equal "Black Pearl", pirate.reload.ship.name
1262
1715
  end
1263
1716
 
1264
1717
  def test_should_automatically_validate_the_associated_model
@@ -1292,12 +1745,7 @@ class TestAutosaveAssociationOnAHasOneAssociation < ActiveRecord::TestCase
1292
1745
  @pirate.catchphrase = ""
1293
1746
  @pirate.ship.name = ""
1294
1747
  @pirate.save(validate: false)
1295
- # Oracle saves empty string as NULL
1296
- if current_adapter?(:OracleAdapter)
1297
- assert_equal [nil, nil], [@pirate.reload.catchphrase, @pirate.ship.name]
1298
- else
1299
- assert_equal ["", ""], [@pirate.reload.catchphrase, @pirate.ship.name]
1300
- end
1748
+ assert_equal ["", ""], [@pirate.reload.catchphrase, @pirate.ship.name]
1301
1749
  end
1302
1750
 
1303
1751
  def test_should_allow_to_bypass_validations_on_associated_models_at_any_depth
@@ -1309,12 +1757,7 @@ class TestAutosaveAssociationOnAHasOneAssociation < ActiveRecord::TestCase
1309
1757
  @pirate.save(validate: false)
1310
1758
 
1311
1759
  values = [@pirate.reload.catchphrase, @pirate.ship.name, *@pirate.ship.parts.map(&:name)]
1312
- # Oracle saves empty string as NULL
1313
- if current_adapter?(:OracleAdapter)
1314
- assert_equal [nil, nil, nil, nil], values
1315
- else
1316
- assert_equal ["", "", "", ""], values
1317
- end
1760
+ assert_equal ["", "", "", ""], values
1318
1761
  end
1319
1762
 
1320
1763
  def test_should_still_raise_an_ActiveRecordRecord_Invalid_exception_if_we_want_that
@@ -1326,7 +1769,7 @@ class TestAutosaveAssociationOnAHasOneAssociation < ActiveRecord::TestCase
1326
1769
 
1327
1770
  def test_should_not_save_and_return_false_if_a_callback_cancelled_saving
1328
1771
  pirate = Pirate.new(catchphrase: "Arr")
1329
- ship = pirate.build_ship(name: "The Vile Insanity")
1772
+ ship = pirate.build_ship(name: "The Vile Serpent")
1330
1773
  ship.cancel_save_from_callback = true
1331
1774
 
1332
1775
  assert_no_difference "Pirate.count" do
@@ -1340,7 +1783,7 @@ class TestAutosaveAssociationOnAHasOneAssociation < ActiveRecord::TestCase
1340
1783
  before = [@pirate.catchphrase, @pirate.ship.name]
1341
1784
 
1342
1785
  @pirate.catchphrase = "Arr"
1343
- @pirate.ship.name = "The Vile Insanity"
1786
+ @pirate.ship.name = "The Vile Serpent"
1344
1787
 
1345
1788
  # Stub the save method of the @pirate.ship instance to raise an exception
1346
1789
  class << @pirate.ship
@@ -1355,7 +1798,7 @@ class TestAutosaveAssociationOnAHasOneAssociation < ActiveRecord::TestCase
1355
1798
  end
1356
1799
 
1357
1800
  def test_should_not_load_the_associated_model
1358
- assert_queries(1) { @pirate.catchphrase = "Arr"; @pirate.save! }
1801
+ assert_queries_count(3) { @pirate.catchphrase = "Arr"; @pirate.save! }
1359
1802
  end
1360
1803
 
1361
1804
  def test_mark_for_destruction_is_ignored_without_autosave_true
@@ -1364,11 +1807,74 @@ class TestAutosaveAssociationOnAHasOneAssociation < ActiveRecord::TestCase
1364
1807
 
1365
1808
  assert_not_predicate ship, :valid?
1366
1809
  end
1810
+
1811
+ def test_should_not_saved_for_unchanged_sti_type_on_polymorphic_association
1812
+ skip "DB2 limitation: polymorphic autosave STI type update triggers an extra child save" if current_adapter?(:IBM_DBAdapter)
1813
+
1814
+ face = Class.new(Face) do
1815
+ def self.name; "Face"; end
1816
+
1817
+ after_save :count_saves
1818
+
1819
+ def count_saves
1820
+ @count ||= 0
1821
+ @count += 1
1822
+ end
1823
+ end
1824
+
1825
+ super_human = Class.new(SuperHuman) do
1826
+ self.table_name = "humans"
1827
+ def self.name; "SuperHuman"; end
1828
+
1829
+ attribute :name, :string
1830
+
1831
+ # Polymorphic has_one needs to be defined on the child class
1832
+ has_one :polymorphic_face, class_name: "Face", as: :polymorphic_human, inverse_of: :polymorphic_human
1833
+ end
1834
+
1835
+ face_record = face.create!
1836
+
1837
+ super_human_record = super_human.create!(name: "S. Human", polymorphic_face: face_record)
1838
+
1839
+ super_human_record.update!(name: "Super Human")
1840
+
1841
+ assert_equal "Human", face_record.polymorphic_human_type
1842
+ assert_equal super_human_record.id, face_record.polymorphic_human_id
1843
+
1844
+ # Saves on create of face and create of super human, but not update
1845
+ assert_equal 2, face_record.instance_variable_get(:@count)
1846
+ end
1847
+
1848
+ def test_recognizes_inverse_polymorphic_association_changes_with_same_foreign_key
1849
+ chef_a = chefs(:gordon_ramsay)
1850
+ chef_b = chefs(:marco_pierre_white)
1851
+
1852
+ cake_designer_a = cake_designers(:flora) # id: 1
1853
+ cake_designer_a.update!(chef: chef_a)
1854
+ cake_designer_b = cake_designers(:frosty) # id: 3
1855
+ cake_designer_b.update!(chef: chef_b)
1856
+
1857
+ drink_designer_a = drink_designers(:turner) # id: 1
1858
+ drink_designer_b = drink_designers(:sparrow) # id: 2
1859
+
1860
+ swap_chefs(cake_designer_b, drink_designer_b)
1861
+ assert_predicate cake_designer_b.reload.chef, :present?
1862
+ assert_not_predicate drink_designer_b.reload.chef, :present?
1863
+
1864
+ swap_chefs(cake_designer_a, drink_designer_a)
1865
+ assert_predicate cake_designer_a.reload.chef, :present?
1866
+ assert_not_predicate drink_designer_a.reload.chef, :present?
1867
+ end
1868
+
1869
+ private
1870
+ def swap_chefs(cake_designer, drink_designer)
1871
+ drink_designer.chef = cake_designer.chef
1872
+ drink_designer.save!
1873
+ cake_designer.save!
1874
+ end
1367
1875
  end
1368
1876
 
1369
1877
  class TestAutosaveAssociationOnAHasOneThroughAssociation < ActiveRecord::TestCase
1370
- self.use_transactional_tests = false unless supports_savepoints?
1371
-
1372
1878
  def create_member_with_organization
1373
1879
  organization = Organization.create
1374
1880
  member = Member.create
@@ -1412,8 +1918,6 @@ class TestAutosaveAssociationOnAHasOneThroughAssociation < ActiveRecord::TestCas
1412
1918
  end
1413
1919
 
1414
1920
  class TestAutosaveAssociationOnABelongsToAssociation < ActiveRecord::TestCase
1415
- self.use_transactional_tests = false unless supports_savepoints?
1416
-
1417
1921
  def setup
1418
1922
  super
1419
1923
  @ship = Ship.create(name: "Nights Dirty Lightning")
@@ -1422,9 +1926,9 @@ class TestAutosaveAssociationOnABelongsToAssociation < ActiveRecord::TestCase
1422
1926
 
1423
1927
  def test_should_still_work_without_an_associated_model
1424
1928
  @pirate.destroy
1425
- @ship.reload.name = "The Vile Insanity"
1929
+ @ship.reload.name = "The Vile Serpent"
1426
1930
  @ship.save
1427
- assert_equal "The Vile Insanity", @ship.reload.name
1931
+ assert_equal "The Vile Serpent", @ship.reload.name
1428
1932
  end
1429
1933
 
1430
1934
  def test_should_automatically_save_the_associated_model
@@ -1457,12 +1961,7 @@ class TestAutosaveAssociationOnABelongsToAssociation < ActiveRecord::TestCase
1457
1961
  @ship.pirate.catchphrase = ""
1458
1962
  @ship.name = ""
1459
1963
  @ship.save(validate: false)
1460
- # Oracle saves empty string as NULL
1461
- if current_adapter?(:OracleAdapter)
1462
- assert_equal [nil, nil], [@ship.reload.name, @ship.pirate.catchphrase]
1463
- else
1464
- assert_equal ["", ""], [@ship.reload.name, @ship.pirate.catchphrase]
1465
- end
1964
+ assert_equal ["", ""], [@ship.reload.name, @ship.pirate.catchphrase]
1466
1965
  end
1467
1966
 
1468
1967
  def test_should_still_raise_an_ActiveRecordRecord_Invalid_exception_if_we_want_that
@@ -1473,7 +1972,7 @@ class TestAutosaveAssociationOnABelongsToAssociation < ActiveRecord::TestCase
1473
1972
  end
1474
1973
 
1475
1974
  def test_should_not_save_and_return_false_if_a_callback_cancelled_saving
1476
- ship = Ship.new(name: "The Vile Insanity")
1975
+ ship = Ship.new(name: "The Vile Serpent")
1477
1976
  pirate = ship.build_pirate(catchphrase: "Arr")
1478
1977
  pirate.cancel_save_from_callback = true
1479
1978
 
@@ -1488,7 +1987,7 @@ class TestAutosaveAssociationOnABelongsToAssociation < ActiveRecord::TestCase
1488
1987
  before = [@ship.pirate.catchphrase, @ship.name]
1489
1988
 
1490
1989
  @ship.pirate.catchphrase = "Arr"
1491
- @ship.name = "The Vile Insanity"
1990
+ @ship.name = "The Vile Serpent"
1492
1991
 
1493
1992
  # Stub the save method of the @ship.pirate instance to raise an exception
1494
1993
  class << @ship.pirate
@@ -1503,7 +2002,20 @@ class TestAutosaveAssociationOnABelongsToAssociation < ActiveRecord::TestCase
1503
2002
  end
1504
2003
 
1505
2004
  def test_should_not_load_the_associated_model
1506
- assert_queries(1) { @ship.name = "The Vile Insanity"; @ship.save! }
2005
+ assert_queries_count(3) { @ship.name = "The Vile Serpent"; @ship.save! }
2006
+ end
2007
+
2008
+ def test_should_save_with_non_nullable_foreign_keys
2009
+ parent = Post.new title: "foo", body: "..."
2010
+ child = parent.comments.build body: "..."
2011
+ child.save!
2012
+ assert_equal child.reload.post, parent.reload
2013
+ end
2014
+
2015
+ def test_should_save_if_previously_saved
2016
+ ship = Ship.create(name: "Nights Dirty Lightning", pirate: Pirate.new(catchphrase: "Arrrr"))
2017
+ ship.create_pirate(catchphrase: "Savvy?")
2018
+ assert_equal "Savvy?", ship.reload.pirate.catchphrase
1507
2019
  end
1508
2020
  end
1509
2021
 
@@ -1587,20 +2099,11 @@ module AutosaveAssociationOnACollectionAssociationTests
1587
2099
  @pirate.public_send(@association_name).each { |child| child.name = "" }
1588
2100
 
1589
2101
  assert @pirate.save(validate: false)
1590
- # Oracle saves empty string as NULL
1591
- if current_adapter?(:OracleAdapter)
1592
- assert_equal [nil, nil, nil], [
1593
- @pirate.reload.catchphrase,
1594
- @pirate.public_send(@association_name).first.name,
1595
- @pirate.public_send(@association_name).last.name
1596
- ]
1597
- else
1598
- assert_equal ["", "", ""], [
1599
- @pirate.reload.catchphrase,
1600
- @pirate.public_send(@association_name).first.name,
1601
- @pirate.public_send(@association_name).last.name
1602
- ]
1603
- end
2102
+ assert_equal ["", "", ""], [
2103
+ @pirate.reload.catchphrase,
2104
+ @pirate.public_send(@association_name).first.name,
2105
+ @pirate.public_send(@association_name).last.name
2106
+ ]
1604
2107
  end
1605
2108
 
1606
2109
  def test_should_validation_the_associated_models_on_create
@@ -1664,11 +2167,11 @@ module AutosaveAssociationOnACollectionAssociationTests
1664
2167
  end
1665
2168
 
1666
2169
  def test_should_not_load_the_associated_models_if_they_were_not_loaded_yet
1667
- assert_queries(1) { @pirate.catchphrase = "Arr"; @pirate.save! }
2170
+ assert_queries_count(3) { @pirate.catchphrase = "Arr"; @pirate.save! }
1668
2171
 
1669
2172
  @pirate.public_send(@association_name).load_target
1670
2173
 
1671
- assert_queries(3) do
2174
+ assert_queries_count(5) do
1672
2175
  @pirate.catchphrase = "Yarr"
1673
2176
  new_names = ["Grace OMalley", "Privateers Greed"]
1674
2177
  @pirate.public_send(@association_name).each_with_index { |child, i| child.name = new_names[i] }
@@ -1678,8 +2181,6 @@ module AutosaveAssociationOnACollectionAssociationTests
1678
2181
  end
1679
2182
 
1680
2183
  class TestAutosaveAssociationOnAHasManyAssociation < ActiveRecord::TestCase
1681
- self.use_transactional_tests = false unless supports_savepoints?
1682
-
1683
2184
  def setup
1684
2185
  super
1685
2186
  @association_name = :birds
@@ -1694,8 +2195,6 @@ class TestAutosaveAssociationOnAHasManyAssociation < ActiveRecord::TestCase
1694
2195
  end
1695
2196
 
1696
2197
  class TestAutosaveAssociationOnAHasAndBelongsToManyAssociation < ActiveRecord::TestCase
1697
- self.use_transactional_tests = false unless supports_savepoints?
1698
-
1699
2198
  def setup
1700
2199
  super
1701
2200
  @association_name = :autosaved_parrots
@@ -1711,8 +2210,6 @@ class TestAutosaveAssociationOnAHasAndBelongsToManyAssociation < ActiveRecord::T
1711
2210
  end
1712
2211
 
1713
2212
  class TestAutosaveAssociationOnAHasAndBelongsToManyAssociationWithAcceptsNestedAttributes < ActiveRecord::TestCase
1714
- self.use_transactional_tests = false unless supports_savepoints?
1715
-
1716
2213
  def setup
1717
2214
  super
1718
2215
  @association_name = :parrots
@@ -1728,8 +2225,6 @@ class TestAutosaveAssociationOnAHasAndBelongsToManyAssociationWithAcceptsNestedA
1728
2225
  end
1729
2226
 
1730
2227
  class TestAutosaveAssociationValidationsOnAHasManyAssociation < ActiveRecord::TestCase
1731
- self.use_transactional_tests = false unless supports_savepoints?
1732
-
1733
2228
  def setup
1734
2229
  super
1735
2230
  @pirate = Pirate.create(catchphrase: "Don' botharrr talkin' like one, savvy?")
@@ -1787,14 +2282,12 @@ class TestAutosaveAssociationValidationsOnAHasManyAssociation < ActiveRecord::Te
1787
2282
  pirate = FamousPirate.create!(catchphrase: "Avast Ye!")
1788
2283
  pirate.famous_ships.create!
1789
2284
 
1790
- assert pirate.valid?
2285
+ assert_predicate pirate, :valid?
1791
2286
  assert_not pirate.valid?(:conference)
1792
2287
  end
1793
2288
  end
1794
2289
 
1795
2290
  class TestAutosaveAssociationValidationsOnAHasOneAssociation < ActiveRecord::TestCase
1796
- self.use_transactional_tests = false unless supports_savepoints?
1797
-
1798
2291
  def setup
1799
2292
  super
1800
2293
  @pirate = Pirate.create(catchphrase: "Don' botharrr talkin' like one, savvy?")
@@ -1816,8 +2309,6 @@ class TestAutosaveAssociationValidationsOnAHasOneAssociation < ActiveRecord::Tes
1816
2309
  end
1817
2310
 
1818
2311
  class TestAutosaveAssociationValidationsOnABelongsToAssociation < ActiveRecord::TestCase
1819
- self.use_transactional_tests = false unless supports_savepoints?
1820
-
1821
2312
  def setup
1822
2313
  super
1823
2314
  @pirate = Pirate.create(catchphrase: "Don' botharrr talkin' like one, savvy?")
@@ -1838,14 +2329,12 @@ class TestAutosaveAssociationValidationsOnABelongsToAssociation < ActiveRecord::
1838
2329
  def test_validations_still_fire_on_unchanged_association_with_custom_validation_context
1839
2330
  firm_with_low_credit = Firm.create!(name: "Something", account: Account.new(credit_limit: 50))
1840
2331
 
1841
- assert firm_with_low_credit.valid?
2332
+ assert_predicate firm_with_low_credit, :valid?
1842
2333
  assert_not firm_with_low_credit.valid?(:bank_loan)
1843
2334
  end
1844
2335
  end
1845
2336
 
1846
2337
  class TestAutosaveAssociationValidationsOnAHABTMAssociation < ActiveRecord::TestCase
1847
- self.use_transactional_tests = false unless supports_savepoints?
1848
-
1849
2338
  def setup
1850
2339
  super
1851
2340
  @pirate = Pirate.create(catchphrase: "Don' botharrr talkin' like one, savvy?")
@@ -1867,8 +2356,6 @@ class TestAutosaveAssociationValidationsOnAHABTMAssociation < ActiveRecord::Test
1867
2356
  end
1868
2357
 
1869
2358
  class TestAutosaveAssociationValidationMethodsGeneration < ActiveRecord::TestCase
1870
- self.use_transactional_tests = false unless supports_savepoints?
1871
-
1872
2359
  def setup
1873
2360
  super
1874
2361
  @pirate = Pirate.new
@@ -1951,3 +2438,69 @@ class TestAutosaveAssociationOnAHasManyAssociationDefinedInSubclassWithAcceptsNe
1951
2438
  assert_equal "Updated", valid_project.name
1952
2439
  end
1953
2440
  end
2441
+
2442
+ class TestAutosaveAssociationOnABelongsToAssociationDefinedAsRecord < ActiveRecord::TestCase
2443
+ def test_should_not_raise_error
2444
+ translation = Translation.create(locale: "fr", key: "bread", value: "Baguette 🥖")
2445
+ author = Author.create(name: "Dorian Marié")
2446
+ translation.build_attachment(record: author)
2447
+ assert_nothing_raised do
2448
+ translation.save!
2449
+ end
2450
+ end
2451
+ end
2452
+
2453
+ class TestAutosaveAssociationWithNestedAttributes < ActiveRecord::TestCase
2454
+ class Part < ActiveRecord::Base
2455
+ self.table_name = "ship_parts"
2456
+ end
2457
+
2458
+ class Ship < ActiveRecord::Base
2459
+ self.table_name = "ships"
2460
+ has_many :parts, class_name: Part.name
2461
+ accepts_nested_attributes_for :parts, allow_destroy: true
2462
+
2463
+ validate :has_at_least_two_parts
2464
+ def has_at_least_two_parts
2465
+ current_parts = parts.select { |p| !p.marked_for_destruction? }
2466
+ errors.add(:parts, "must have at least two parts") if current_parts.size < 2
2467
+ end
2468
+ end
2469
+
2470
+ class Pirate < ActiveRecord::Base
2471
+ self.table_name = "pirates"
2472
+ has_many :ships, class_name: Ship.name
2473
+ accepts_nested_attributes_for :ships, allow_destroy: true
2474
+ end
2475
+
2476
+ def test_should_be_invalid_when_nested_attributes_deletion_breaks_validation
2477
+ pirate = Pirate.create!
2478
+ ship = pirate.ships.new
2479
+ 2.times do |i|
2480
+ ship.parts.build
2481
+ end
2482
+ part = ship.parts.first
2483
+ ship.save!
2484
+
2485
+ deletion_params = {
2486
+ "ships_attributes" => {
2487
+ "0" => {
2488
+ "id" => ship.id,
2489
+ "parts_attributes" => {
2490
+ "0" => {
2491
+ "id" => part.id,
2492
+ "_destroy" => "1",
2493
+ },
2494
+ }
2495
+ }
2496
+ }
2497
+ }
2498
+
2499
+ assert_not pirate.update(deletion_params)
2500
+ assert_nothing_raised do
2501
+ part.reload
2502
+ end
2503
+ assert_includes pirate.errors[:"ships.parts"], "must have at least two parts"
2504
+ assert_includes ship.errors[:parts], "must have at least two parts"
2505
+ end
2506
+ end