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
@@ -1,5 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "pp"
3
4
  require "cases/helper"
4
5
  require "models/tag"
5
6
  require "models/tagging"
@@ -25,10 +26,12 @@ require "models/reader"
25
26
  require "models/category"
26
27
  require "models/categorization"
27
28
  require "models/edge"
29
+ require "models/wheel"
28
30
  require "models/subscriber"
31
+ require "models/cpk"
29
32
 
30
33
  class RelationTest < ActiveRecord::TestCase
31
- fixtures :authors, :author_addresses, :topics, :entrants, :developers, :people, :companies, :developers_projects, :accounts, :categories, :categorizations, :categories_posts, :posts, :comments, :tags, :taggings, :cars, :minivans
34
+ fixtures :authors, :author_addresses, :topics, :entrants, :developers, :people, :companies, :developers_projects, :accounts, :categories, :categorizations, :categories_posts, :posts, :comments, :tags, :taggings, :cars, :minivans, :cpk_orders
32
35
 
33
36
  def test_do_not_double_quote_string_id
34
37
  van = Minivan.last
@@ -55,7 +58,7 @@ class RelationTest < ActiveRecord::TestCase
55
58
 
56
59
  def test_dynamic_finder
57
60
  x = Post.where("author_id = ?", 1)
58
- assert_respond_to x.klass, :find_by_id
61
+ assert_respond_to x.model, :find_by_id
59
62
  end
60
63
 
61
64
  def test_multivalue_where
@@ -96,7 +99,7 @@ class RelationTest < ActiveRecord::TestCase
96
99
  assert_not_predicate topics, :loaded?
97
100
  assert_not_predicate topics, :loaded
98
101
 
99
- assert_queries(1) do
102
+ assert_queries_count(1) do
100
103
  2.times { assert_equal 5, topics.to_a.size }
101
104
  end
102
105
 
@@ -107,7 +110,7 @@ class RelationTest < ActiveRecord::TestCase
107
110
  def test_scoped_first
108
111
  topics = Topic.all.order("id ASC")
109
112
 
110
- assert_queries(1) do
113
+ assert_queries_count(1) do
111
114
  2.times { assert_equal "The First Topic", topics.first.title }
112
115
  end
113
116
 
@@ -151,7 +154,7 @@ class RelationTest < ActiveRecord::TestCase
151
154
  def test_reload
152
155
  topics = Topic.all
153
156
 
154
- assert_queries(1) do
157
+ assert_queries_count(1) do
155
158
  2.times { topics.to_a }
156
159
  end
157
160
 
@@ -160,7 +163,7 @@ class RelationTest < ActiveRecord::TestCase
160
163
  original_size = topics.to_a.size
161
164
  Topic.create! title: "fake"
162
165
 
163
- assert_queries(1) { topics.reload }
166
+ assert_queries_count(1) { topics.reload }
164
167
  assert_equal original_size + 1, topics.size
165
168
  assert_predicate topics, :loaded?
166
169
  end
@@ -180,6 +183,7 @@ class RelationTest < ActiveRecord::TestCase
180
183
  end
181
184
 
182
185
  def test_finding_with_subquery_without_select_does_not_change_the_select
186
+ skip "IBM_DB: Does not raise StatementInvalid for subquery without SELECT on all databases" if ActiveRecord::Base.connection.adapter_name.match?(/ibm/i)
183
187
  relation = Topic.where(approved: true)
184
188
  assert_raises(ActiveRecord::StatementInvalid) do
185
189
  Topic.from(relation).to_a
@@ -211,12 +215,9 @@ class RelationTest < ActiveRecord::TestCase
211
215
  end
212
216
 
213
217
  def test_select_with_subquery_in_from_uses_original_table_name
214
- puts "test_select_with_subquery_in_from_uses_original_table_name"
215
218
  relation = Comment.joins(:post).select(:id).order(:id)
216
- puts "After relation"
217
219
  # Avoid subquery flattening by adding distinct to work with SQLite < 3.20.0.
218
220
  subquery = Comment.from(Comment.all.distinct, Comment.quoted_table_name).joins(:post).select(:id).order(:id)
219
- puts "After subquery"
220
221
  assert_equal relation.map(&:id), subquery.map(&:id)
221
222
  end
222
223
 
@@ -239,13 +240,14 @@ class RelationTest < ActiveRecord::TestCase
239
240
  end
240
241
 
241
242
  def test_select_with_subquery_string_in_from_does_not_use_original_table_name
243
+ skip "IBM_DB: SQL0206N - COMMENTS.TYPE not available in subquery context" if ActiveRecord::Base.connection.adapter_name.match?(/ibm/i)
242
244
  relation = Comment.group(:type).select("COUNT(post_id) AS post_count, type")
243
245
  subquery = Comment.from("(#{relation.to_sql}) #{Comment.table_name}_grouped").select("type", "post_count")
244
246
  assert_equal(relation.map(&:post_count).sort, subquery.map(&:post_count).sort)
245
247
  end
246
248
 
247
249
  def test_group_with_subquery_string_in_from_does_not_use_original_table_name
248
- puts "test_group_with_subquery_string_in_from_does_not_use_original_table_name"
250
+ skip "IBM_DB: SQL0206N - COMMENTS.TYPE not available in subquery context" if ActiveRecord::Base.connection.adapter_name.match?(/ibm/i)
249
251
  relation = Comment.group(:type).select("COUNT(post_id) AS post_count,type")
250
252
  subquery = Comment.from("(#{relation.to_sql}) #{Comment.table_name}_grouped").group("type").average("post_count")
251
253
  assert_equal(relation.map(&:post_count).sort, subquery.values.sort)
@@ -364,7 +366,7 @@ class RelationTest < ActiveRecord::TestCase
364
366
  assert_raises(ActiveRecord::IrreversibleOrderError) do
365
367
  Topic.order("author_name, title nulls last").reverse_order
366
368
  end
367
- end if current_adapter?(:PostgreSQLAdapter, :OracleAdapter)
369
+ end if current_adapter?(:PostgreSQLAdapter)
368
370
 
369
371
  def test_default_reverse_order_on_table_without_primary_key
370
372
  assert_raises(ActiveRecord::IrreversibleOrderError) do
@@ -459,6 +461,7 @@ class RelationTest < ActiveRecord::TestCase
459
461
  end
460
462
 
461
463
  def test_finding_with_cross_table_order_and_limit
464
+ skip "IBM_DB: CLI0125E function sequence error with complex REPLACE query" if ActiveRecord::Base.connection.adapter_name.match?(/ibm/i)
462
465
  tags = Tag.includes(:taggings).
463
466
  order("tags.name asc", "taggings.taggable_id asc", Arel.sql("REPLACE('abc', taggings.taggable_type, taggings.taggable_type)")).
464
467
  limit(1).to_a
@@ -466,6 +469,7 @@ class RelationTest < ActiveRecord::TestCase
466
469
  end
467
470
 
468
471
  def test_finding_with_complex_order_and_limit
472
+ skip "IBM_DB: Complex REPLACE function with references returns wrong result count" if ActiveRecord::Base.connection.adapter_name.match?(/ibm/i)
469
473
  tags = Tag.includes(:taggings).references(:taggings).order(Arel.sql("REPLACE('abc', taggings.taggable_type, taggings.taggable_type)")).limit(1).to_a
470
474
  assert_equal 1, tags.length
471
475
  end
@@ -477,7 +481,11 @@ class RelationTest < ActiveRecord::TestCase
477
481
 
478
482
  def test_finding_with_sanitized_order
479
483
  query = Tag.order([Arel.sql("field(id, ?)"), [1, 3, 2]]).to_sql
480
- assert_match(/field\(id, 1,3,2\)/, query)
484
+ if current_adapter?(:Mysql2Adapter, :TrilogyAdapter)
485
+ assert_match(/field\(id, '1',\s*'3',\s*'2'\)/, query)
486
+ else
487
+ assert_match(/field\(id, 1,\s*3,\s*2\)/, query)
488
+ end
481
489
 
482
490
  query = Tag.order([Arel.sql("field(id, ?)"), []]).to_sql
483
491
  assert_match(/field\(id, NULL\)/, query)
@@ -486,6 +494,21 @@ class RelationTest < ActiveRecord::TestCase
486
494
  assert_match(/field\(id, NULL\)/, query)
487
495
  end
488
496
 
497
+ def test_finding_with_arel_sql_order
498
+ query = Tag.order(Arel.sql("field(id, ?)", [1, 3, 2])).to_sql
499
+ if current_adapter?(:Mysql2Adapter, :TrilogyAdapter)
500
+ assert_match(/field\(id, '1', '3', '2'\)/, query)
501
+ else
502
+ assert_match(/field\(id, 1, 3, 2\)/, query)
503
+ end
504
+
505
+ query = Tag.order(Arel.sql("field(id, ?)", [])).to_sql
506
+ assert_match(/field\(id, NULL\)/, query)
507
+
508
+ query = Tag.order(Arel.sql("field(id, ?)", nil)).to_sql
509
+ assert_match(/field\(id, NULL\)/, query)
510
+ end
511
+
489
512
  def test_finding_with_order_limit_and_offset
490
513
  entrants = Entrant.order("id ASC").limit(2).offset(1)
491
514
 
@@ -533,7 +556,8 @@ class RelationTest < ActiveRecord::TestCase
533
556
  end
534
557
 
535
558
  def test_joins_with_string_array
536
- person_with_reader_and_post = Post.joins([
559
+ person_with_reader_and_post = Post.joins(
560
+ [
537
561
  "INNER JOIN categorizations ON categorizations.post_id = posts.id",
538
562
  "INNER JOIN categories ON categories.id = categorizations.category_id AND categories.type = 'SpecialCategory'"
539
563
  ]
@@ -541,20 +565,14 @@ class RelationTest < ActiveRecord::TestCase
541
565
  assert_equal 1, person_with_reader_and_post.size
542
566
  end
543
567
 
544
- def test_no_arguments_to_query_methods_raise_errors
545
- assert_raises(ArgumentError) { Topic.references() }
546
- assert_raises(ArgumentError) { Topic.includes() }
547
- assert_raises(ArgumentError) { Topic.preload() }
548
- assert_raises(ArgumentError) { Topic.group() }
549
- assert_raises(ArgumentError) { Topic.reorder() }
550
- assert_raises(ArgumentError) { Topic.order() }
551
- assert_raises(ArgumentError) { Topic.eager_load() }
552
- assert_raises(ArgumentError) { Topic.reselect() }
553
- assert_raises(ArgumentError) { Topic.unscope() }
554
- assert_raises(ArgumentError) { Topic.joins() }
555
- assert_raises(ArgumentError) { Topic.left_joins() }
556
- assert_raises(ArgumentError) { Topic.optimizer_hints() }
557
- assert_raises(ArgumentError) { Topic.annotate() }
568
+ %w( references includes preload eager_load group order reorder reselect unscope
569
+ joins left_joins left_outer_joins optimizer_hints annotate regroup ).each do |method|
570
+ class_eval <<~RUBY
571
+ def test_no_arguments_to_#{method}_raise_errors
572
+ error = assert_raises(ArgumentError) { Topic.#{method}() }
573
+ assert_equal "The method .#{method}() must contain arguments.", error.message
574
+ end
575
+ RUBY
558
576
  end
559
577
 
560
578
  def test_blank_like_arguments_to_query_methods_dont_raise_errors
@@ -587,7 +605,7 @@ class RelationTest < ActiveRecord::TestCase
587
605
 
588
606
  def test_find_with_readonly_option
589
607
  Developer.all.each { |d| assert_not d.readonly? }
590
- Developer.all.readonly.each { |d| assert d.readonly? }
608
+ Developer.all.readonly.each { |d| assert_predicate d, :readonly? }
591
609
  end
592
610
 
593
611
  def test_eager_association_loading_of_stis_with_multiple_references
@@ -602,27 +620,27 @@ class RelationTest < ActiveRecord::TestCase
602
620
  end
603
621
 
604
622
  def test_find_with_preloaded_associations
605
- assert_queries(2) do
623
+ assert_queries_count(2) do
606
624
  posts = Post.preload(:comments).order("posts.id")
607
625
  assert posts.first.comments.first
608
626
  end
609
627
 
610
- assert_queries(2) do
628
+ assert_queries_count(2) do
611
629
  posts = Post.preload(:comments).order("posts.id")
612
630
  assert posts.first.comments.first
613
631
  end
614
632
 
615
- assert_queries(2) do
633
+ assert_queries_count(2) do
616
634
  posts = Post.preload(:author).order("posts.id")
617
635
  assert posts.first.author
618
636
  end
619
637
 
620
- assert_queries(2) do
638
+ assert_queries_count(2) do
621
639
  posts = Post.preload(:author).order("posts.id")
622
640
  assert posts.first.author
623
641
  end
624
642
 
625
- assert_queries(3) do
643
+ assert_queries_count(3) do
626
644
  posts = Post.preload(:author, :comments).order("posts.id")
627
645
  assert posts.first.author
628
646
  assert posts.first.comments.first
@@ -630,36 +648,36 @@ class RelationTest < ActiveRecord::TestCase
630
648
  end
631
649
 
632
650
  def test_preload_applies_to_all_chained_preloaded_scopes
633
- assert_queries(3) do
651
+ assert_queries_count(3) do
634
652
  post = Post.with_comments.with_tags.first
635
653
  assert post
636
654
  end
637
655
  end
638
656
 
639
657
  def test_extracted_association
640
- relation_authors = assert_queries(2) { Post.all.extract_associated(:author) }
641
- root_authors = assert_queries(2) { Post.extract_associated(:author) }
658
+ relation_authors = assert_queries_count(2) { Post.all.extract_associated(:author) }
659
+ root_authors = assert_queries_count(2) { Post.extract_associated(:author) }
642
660
  assert_equal relation_authors, root_authors
643
661
  assert_equal Post.all.collect(&:author), relation_authors
644
662
  end
645
663
 
646
664
  def test_find_with_included_associations
647
- assert_queries(2) do
665
+ assert_queries_count(2) do
648
666
  posts = Post.includes(:comments).order("posts.id")
649
667
  assert posts.first.comments.first
650
668
  end
651
669
 
652
- assert_queries(2) do
670
+ assert_queries_count(2) do
653
671
  posts = Post.all.includes(:comments).order("posts.id")
654
672
  assert posts.first.comments.first
655
673
  end
656
674
 
657
- assert_queries(2) do
675
+ assert_queries_count(2) do
658
676
  posts = Post.includes(:author).order("posts.id")
659
677
  assert posts.first.author
660
678
  end
661
679
 
662
- assert_queries(3) do
680
+ assert_queries_count(3) do
663
681
  posts = Post.includes(:author, :comments).order("posts.id")
664
682
  assert posts.first.author
665
683
  assert posts.first.comments.first
@@ -885,22 +903,22 @@ class RelationTest < ActiveRecord::TestCase
885
903
  ids = Author.pluck(:id)
886
904
  slugs = ids.map { |id| "#{id}-as-a-slug" }
887
905
 
888
- assert_equal Author.all.to_a, Author.where(id: slugs).to_a
906
+ assert_equal Author.where(id: ids).to_a, Author.where(id: slugs).to_a
889
907
  end
890
908
 
891
909
  def test_find_all_using_where_with_relation
892
910
  david = authors(:david)
893
- assert_queries(1) {
911
+ assert_queries_count(1) {
894
912
  relation = Author.where(id: Author.where(id: david.id))
895
913
  assert_equal [david], relation.to_a
896
914
  }
897
915
 
898
- assert_queries(1) {
916
+ assert_queries_count(1) {
899
917
  relation = Author.where("id in (?)", Author.where(id: david).select(:id))
900
918
  assert_equal [david], relation.to_a
901
919
  }
902
920
 
903
- assert_queries(1) do
921
+ assert_queries_count(1) do
904
922
  relation = Author.where("id in (:author_ids)", author_ids: Author.where(id: david).select(:id))
905
923
  assert_equal [david], relation.to_a
906
924
  end
@@ -910,17 +928,17 @@ class RelationTest < ActiveRecord::TestCase
910
928
  david = authors(:david)
911
929
  davids_posts = david.posts.order(:id).to_a
912
930
 
913
- assert_queries(1) do
931
+ assert_queries_count(1) do
914
932
  relation = Post.where(id: david.posts.select(:id))
915
933
  assert_equal davids_posts, relation.order(:id).to_a
916
934
  end
917
935
 
918
- assert_queries(1) do
936
+ assert_queries_count(1) do
919
937
  relation = Post.where("id in (?)", david.posts.select(:id))
920
938
  assert_equal davids_posts, relation.order(:id).to_a, "should process Relation as bind variables"
921
939
  end
922
940
 
923
- assert_queries(1) do
941
+ assert_queries_count(1) do
924
942
  relation = Post.where("id in (:post_ids)", post_ids: david.posts.select(:id))
925
943
  assert_equal davids_posts, relation.order(:id).to_a, "should process Relation as named bind variables"
926
944
  end
@@ -928,18 +946,33 @@ class RelationTest < ActiveRecord::TestCase
928
946
 
929
947
  def test_find_all_using_where_with_relation_and_alternate_primary_key
930
948
  cool_first = minivans(:cool_first)
931
- assert_queries(1) {
949
+ assert_queries_count(1) {
932
950
  relation = Minivan.where(minivan_id: Minivan.where(name: cool_first.name))
933
951
  assert_equal [cool_first], relation.to_a
934
952
  }
935
953
  end
936
954
 
955
+ def test_find_all_using_where_with_relation_with_no_selects_and_composite_primary_key_raises
956
+ order = cpk_orders(:cpk_groceries_order_1)
957
+ subquery = Cpk::Order.where(Cpk::Order.primary_key => [order.id])
958
+
959
+ assert_nothing_raised do
960
+ Cpk::Order.where(id: subquery.select(:id)).to_a
961
+ end
962
+
963
+ error = assert_raise(ArgumentError) do
964
+ Cpk::Order.where(id: subquery).to_a
965
+ end
966
+
967
+ assert_equal "Cannot map composite primary key [\"shop_id\", \"id\"] to id", error.message
968
+ end
969
+
937
970
  def test_find_all_using_where_with_relation_does_not_alter_select_values
938
971
  david = authors(:david)
939
972
 
940
973
  subquery = Author.where(id: david.id)
941
974
 
942
- assert_queries(1) {
975
+ assert_queries_count(1) {
943
976
  relation = Author.where(id: subquery)
944
977
  assert_equal [david], relation.to_a
945
978
  }
@@ -949,7 +982,7 @@ class RelationTest < ActiveRecord::TestCase
949
982
 
950
983
  def test_find_all_using_where_with_relation_with_joins
951
984
  david = authors(:david)
952
- assert_queries(1) {
985
+ assert_queries_count(1) {
953
986
  relation = Author.where(id: Author.joins(:posts).where(id: david.id))
954
987
  assert_equal [david], relation.to_a
955
988
  }
@@ -957,7 +990,7 @@ class RelationTest < ActiveRecord::TestCase
957
990
 
958
991
  def test_find_all_using_where_with_relation_with_select_to_build_subquery
959
992
  david = authors(:david)
960
- assert_queries(1) {
993
+ assert_queries_count(1) {
961
994
  relation = Author.where(name: Author.where(id: david.id).select(:name))
962
995
  assert_equal [david], relation.to_a
963
996
  }
@@ -993,14 +1026,6 @@ class RelationTest < ActiveRecord::TestCase
993
1026
  assert_equal first.heading, topic.heading
994
1027
  end
995
1028
 
996
- def test_select_argument_error
997
- assert_raises(ArgumentError) { Developer.select }
998
- end
999
-
1000
- def test_select_argument_error_with_block
1001
- assert_raises(ArgumentError) { Developer.select(:id) { |d| d.id % 2 == 0 } }
1002
- end
1003
-
1004
1029
  def test_count
1005
1030
  posts = Post.all
1006
1031
 
@@ -1040,33 +1065,33 @@ class RelationTest < ActiveRecord::TestCase
1040
1065
 
1041
1066
  def test_size_with_distinct
1042
1067
  posts = Post.distinct.select(:author_id, :comments_count)
1043
- assert_queries(1) { assert_equal 8, posts.size }
1044
- assert_queries(1) { assert_equal 8, posts.load.size }
1068
+ assert_queries_count(1) { assert_equal 8, posts.size }
1069
+ assert_queries_count(1) { assert_equal 8, posts.load.size }
1045
1070
  end
1046
1071
 
1047
1072
  def test_size_with_eager_loading_and_custom_order
1048
1073
  posts = Post.includes(:comments).order("comments.id")
1049
- assert_queries(1) { assert_equal 11, posts.size }
1050
- assert_queries(1) { assert_equal 11, posts.load.size }
1074
+ assert_queries_count(1) { assert_equal 11, posts.size }
1075
+ assert_queries_count(1) { assert_equal 11, posts.load.size }
1051
1076
  end
1052
1077
 
1053
1078
  def test_size_with_eager_loading_and_custom_select_and_order
1054
1079
  posts = Post.includes(:comments).order("comments.id").select(:type)
1055
- assert_queries(1) { assert_equal 11, posts.size }
1056
- assert_queries(1) { assert_equal 11, posts.load.size }
1080
+ assert_queries_count(1) { assert_equal 11, posts.size }
1081
+ assert_queries_count(1) { assert_equal 11, posts.load.size }
1057
1082
  end
1058
1083
 
1059
1084
  def test_size_with_eager_loading_and_custom_order_and_distinct
1060
1085
  posts = Post.includes(:comments).order("comments.id").distinct
1061
- assert_queries(1) { assert_equal 11, posts.size }
1062
- assert_queries(1) { assert_equal 11, posts.load.size }
1086
+ assert_queries_count(1) { assert_equal 11, posts.size }
1087
+ assert_queries_count(1) { assert_equal 11, posts.load.size }
1063
1088
  end
1064
1089
 
1065
1090
  def test_size_with_eager_loading_and_manual_distinct_select_and_custom_order
1066
1091
  accounts = Account.select("DISTINCT accounts.firm_id").order("accounts.firm_id")
1067
1092
 
1068
- assert_queries(1) { assert_equal 5, accounts.size }
1069
- assert_queries(1) { assert_equal 5, accounts.load.size }
1093
+ assert_queries_count(1) { assert_equal 5, accounts.size }
1094
+ assert_queries_count(1) { assert_equal 5, accounts.load.size }
1070
1095
  end
1071
1096
 
1072
1097
  def test_count_explicit_columns
@@ -1091,7 +1116,7 @@ class RelationTest < ActiveRecord::TestCase
1091
1116
  def test_size
1092
1117
  posts = Post.all
1093
1118
 
1094
- assert_queries(1) { assert_equal 11, posts.size }
1119
+ assert_queries_count(1) { assert_equal 11, posts.size }
1095
1120
  assert_not_predicate posts, :loaded?
1096
1121
 
1097
1122
  best_posts = posts.where(comments_count: 0)
@@ -1102,7 +1127,7 @@ class RelationTest < ActiveRecord::TestCase
1102
1127
  def test_size_with_limit
1103
1128
  posts = Post.limit(10)
1104
1129
 
1105
- assert_queries(1) { assert_equal 10, posts.size }
1130
+ assert_queries_count(1) { assert_equal 10, posts.size }
1106
1131
  assert_not_predicate posts, :loaded?
1107
1132
 
1108
1133
  best_posts = posts.where(comments_count: 0)
@@ -1137,11 +1162,11 @@ class RelationTest < ActiveRecord::TestCase
1137
1162
  def test_empty
1138
1163
  posts = Post.all
1139
1164
 
1140
- assert_queries(1) { assert_equal false, posts.empty? }
1165
+ assert_queries_count(1) { assert_equal false, posts.empty? }
1141
1166
  assert_not_predicate posts, :loaded?
1142
1167
 
1143
1168
  no_posts = posts.where(title: "")
1144
- assert_queries(1) { assert_equal true, no_posts.empty? }
1169
+ assert_queries_count(1) { assert_equal true, no_posts.empty? }
1145
1170
  assert_not_predicate no_posts, :loaded?
1146
1171
 
1147
1172
  best_posts = posts.where(comments_count: 0)
@@ -1152,23 +1177,26 @@ class RelationTest < ActiveRecord::TestCase
1152
1177
  def test_empty_complex_chained_relations
1153
1178
  posts = Post.select("comments_count").where("id is not null").group("author_id").where("legacy_comments_count > 0")
1154
1179
 
1155
- assert_queries(1) { assert_equal false, posts.empty? }
1180
+ assert_queries_count(1) { assert_equal false, posts.empty? }
1156
1181
  assert_not_predicate posts, :loaded?
1157
1182
 
1158
1183
  no_posts = posts.where(title: "")
1159
- assert_queries(1) { assert_equal true, no_posts.empty? }
1184
+ assert_queries_count(1) { assert_equal true, no_posts.empty? }
1160
1185
  assert_not_predicate no_posts, :loaded?
1161
1186
  end
1162
1187
 
1163
1188
  def test_any
1164
1189
  posts = Post.all
1165
1190
 
1166
- assert_queries(3) do
1167
- assert posts.any? # Uses COUNT()
1191
+ assert_queries_count(3) do
1192
+ assert_predicate posts, :any? # Uses COUNT()
1168
1193
  assert_not_predicate posts.where(id: nil), :any?
1169
1194
 
1170
1195
  assert posts.any? { |p| p.id > 0 }
1171
1196
  assert_not posts.any? { |p| p.id <= 0 }
1197
+
1198
+ assert posts.any?(Post)
1199
+ assert_not posts.any?(Comment)
1172
1200
  end
1173
1201
 
1174
1202
  assert_predicate posts, :loaded?
@@ -1177,8 +1205,8 @@ class RelationTest < ActiveRecord::TestCase
1177
1205
  def test_many
1178
1206
  posts = Post.all
1179
1207
 
1180
- assert_queries(2) do
1181
- assert posts.many? # Uses COUNT()
1208
+ assert_queries_count(2) do
1209
+ assert_predicate posts, :many? # Uses COUNT()
1182
1210
  assert posts.many? { |p| p.id > 0 }
1183
1211
  assert_not posts.many? { |p| p.id < 2 }
1184
1212
  end
@@ -1187,23 +1215,30 @@ class RelationTest < ActiveRecord::TestCase
1187
1215
  end
1188
1216
 
1189
1217
  def test_many_with_limits
1190
- posts = Post.all
1218
+ posts_with_limit = Post.limit(5)
1219
+ posts_with_limit_one = Post.limit(1)
1191
1220
 
1192
- assert_predicate posts, :many?
1193
- assert_not_predicate posts.limit(1), :many?
1221
+ assert_predicate posts_with_limit, :many?
1222
+ assert_not_predicate posts_with_limit, :loaded?
1223
+
1224
+ assert_not_predicate posts_with_limit_one, :many?
1225
+ assert_not_predicate posts_with_limit_one, :loaded?
1194
1226
  end
1195
1227
 
1196
1228
  def test_none?
1197
1229
  posts = Post.all
1198
- assert_queries(1) do
1230
+ assert_queries_count(1) do
1199
1231
  assert_not posts.none? # Uses COUNT()
1200
1232
  end
1201
1233
 
1202
1234
  assert_not_predicate posts, :loaded?
1203
1235
 
1204
- assert_queries(1) do
1236
+ assert_queries_count(1) do
1205
1237
  assert posts.none? { |p| p.id < 0 }
1206
1238
  assert_not posts.none? { |p| p.id == 1 }
1239
+
1240
+ assert posts.none?(Comment)
1241
+ assert_not posts.none?(Post)
1207
1242
  end
1208
1243
 
1209
1244
  assert_predicate posts, :loaded?
@@ -1211,20 +1246,35 @@ class RelationTest < ActiveRecord::TestCase
1211
1246
 
1212
1247
  def test_one
1213
1248
  posts = Post.all
1214
- assert_queries(1) do
1249
+ assert_queries_count(1) do
1215
1250
  assert_not posts.one? # Uses COUNT()
1216
1251
  end
1217
1252
 
1218
1253
  assert_not_predicate posts, :loaded?
1219
1254
 
1220
- assert_queries(1) do
1255
+ assert_queries_count(1) do
1221
1256
  assert_not posts.one? { |p| p.id < 3 }
1222
1257
  assert posts.one? { |p| p.id == 1 }
1258
+
1259
+ assert_not posts.one?(Post)
1260
+ assert_not posts.one?(Comment)
1223
1261
  end
1224
1262
 
1225
1263
  assert_predicate posts, :loaded?
1226
1264
  end
1227
1265
 
1266
+ def test_one_with_destroy
1267
+ posts = Post.all
1268
+ assert_queries_count(1) do
1269
+ assert_not posts.one?
1270
+ end
1271
+
1272
+ posts.where.not(id: Post.first).destroy_all
1273
+
1274
+ assert_equal 1, posts.size
1275
+ assert_predicate posts, :one?
1276
+ end
1277
+
1228
1278
  def test_to_a_should_dup_target
1229
1279
  posts = Post.all
1230
1280
 
@@ -1282,6 +1332,46 @@ class RelationTest < ActiveRecord::TestCase
1282
1332
  assert_equal post, comment.post
1283
1333
  end
1284
1334
 
1335
+ def test_new_with_array
1336
+ green_birds = Bird.where(color: "green").new([{ name: "parrot" }, { name: "canary" }])
1337
+ assert_equal ["parrot", "canary"], green_birds.map(&:name)
1338
+ assert_equal ["green", "green"], green_birds.map(&:color)
1339
+ green_birds.each { |bird| assert_not_predicate bird, :persisted? }
1340
+ end
1341
+
1342
+ def test_build_with_array
1343
+ green_birds = Bird.where(color: "green").build([{ name: "parrot" }, { name: "canary" }])
1344
+ assert_equal ["parrot", "canary"], green_birds.map(&:name)
1345
+ assert_equal ["green", "green"], green_birds.map(&:color)
1346
+ green_birds.each { |bird| assert_not_predicate bird, :persisted? }
1347
+ end
1348
+
1349
+ def test_create_with_array
1350
+ green_birds = Bird.where(color: "green").create([{ name: "parrot" }, { name: "canary" }])
1351
+ assert_equal ["parrot", "canary"], green_birds.map(&:name)
1352
+ assert_equal ["green", "green"], green_birds.map(&:color)
1353
+ green_birds.each { |bird| assert_predicate bird, :persisted? }
1354
+ end
1355
+
1356
+ def test_create_with_block
1357
+ sparrow = Bird.create do |bird|
1358
+ bird.name = "sparrow"
1359
+ bird.color = "grey"
1360
+ end
1361
+
1362
+ assert_kind_of Bird, sparrow
1363
+ assert_predicate sparrow, :persisted?
1364
+ assert_equal "sparrow", sparrow.name
1365
+ assert_equal "grey", sparrow.color
1366
+ end
1367
+
1368
+ def test_create_bang_with_array
1369
+ green_birds = Bird.where(color: "green").create!([{ name: "parrot" }, { name: "canary" }])
1370
+ assert_equal ["parrot", "canary"], green_birds.map(&:name)
1371
+ assert_equal ["green", "green"], green_birds.map(&:color)
1372
+ green_birds.each { |bird| assert_predicate bird, :persisted? }
1373
+ end
1374
+
1285
1375
  def test_first_or_create
1286
1376
  parrot = Bird.where(color: "green").first_or_create(name: "parrot")
1287
1377
  assert_kind_of Bird, parrot
@@ -1326,7 +1416,7 @@ class RelationTest < ActiveRecord::TestCase
1326
1416
  def test_first_or_create_with_array
1327
1417
  several_green_birds = Bird.where(color: "green").first_or_create([{ name: "parrot" }, { name: "parakeet" }])
1328
1418
  assert_kind_of Array, several_green_birds
1329
- several_green_birds.each { |bird| assert bird.persisted? }
1419
+ several_green_birds.each { |bird| assert_predicate bird, :persisted? }
1330
1420
 
1331
1421
  same_parrot = Bird.where(color: "green").first_or_create([{ name: "hummingbird" }, { name: "macaw" }])
1332
1422
  assert_kind_of Bird, same_parrot
@@ -1380,7 +1470,7 @@ class RelationTest < ActiveRecord::TestCase
1380
1470
  def test_first_or_create_bang_with_valid_array
1381
1471
  several_green_birds = Bird.where(color: "green").first_or_create!([{ name: "parrot" }, { name: "parakeet" }])
1382
1472
  assert_kind_of Array, several_green_birds
1383
- several_green_birds.each { |bird| assert bird.persisted? }
1473
+ several_green_birds.each { |bird| assert_predicate bird, :persisted? }
1384
1474
 
1385
1475
  same_parrot = Bird.where(color: "green").first_or_create!([{ name: "hummingbird" }, { name: "macaw" }])
1386
1476
  assert_kind_of Bird, same_parrot
@@ -1439,6 +1529,27 @@ class RelationTest < ActiveRecord::TestCase
1439
1529
  assert_equal bird, Bird.find_or_create_by(name: "bob")
1440
1530
  end
1441
1531
 
1532
+ def test_find_or_create_by_race_condition
1533
+ assert_nil Subscriber.find_by(nick: "bob")
1534
+
1535
+ bob = Subscriber.create!(nick: "bob")
1536
+ relation = Subscriber.all
1537
+
1538
+ results = [nil, bob]
1539
+ find_by_mock = -> (*) do
1540
+ assert_not_predicate results, :empty?
1541
+ results.shift
1542
+ end
1543
+
1544
+ relation.stub(:find_by, find_by_mock) do
1545
+ relation.stub(:find_by!, find_by_mock) do # create_or_find_by always call find_by! on retry
1546
+ assert_equal bob, relation.find_or_create_by(nick: "bob")
1547
+ end
1548
+ end
1549
+
1550
+ assert_predicate results, :empty?
1551
+ end
1552
+
1442
1553
  def test_find_or_create_by_with_create_with
1443
1554
  assert_nil Bird.find_by(name: "bob")
1444
1555
 
@@ -1449,6 +1560,19 @@ class RelationTest < ActiveRecord::TestCase
1449
1560
  assert_equal bird, Bird.create_with(color: "blue").find_or_create_by(name: "bob")
1450
1561
  end
1451
1562
 
1563
+ def test_find_or_create_by_with_block
1564
+ assert_nil Bird.find_by(name: "bob")
1565
+
1566
+ bird = Bird.find_or_create_by(name: "bob") do |record|
1567
+ record.color = "blue"
1568
+ end
1569
+ assert_predicate bird, :persisted?
1570
+ assert_equal "bob", bird.name
1571
+ assert_equal "blue", bird.color
1572
+
1573
+ assert_equal bird, Bird.find_or_create_by(name: "bob", color: "blue")
1574
+ end
1575
+
1452
1576
  def test_find_or_create_by!
1453
1577
  assert_raises(ActiveRecord::RecordInvalid) { Bird.find_or_create_by!(color: "green") }
1454
1578
  end
@@ -1462,6 +1586,49 @@ class RelationTest < ActiveRecord::TestCase
1462
1586
  assert_not_equal subscriber, Subscriber.create_or_find_by(nick: "cat")
1463
1587
  end
1464
1588
 
1589
+ def test_create_or_find_by_rollbacks_a_transaction
1590
+ skip "IBM_DB has different transaction rollback behavior on after_save failures" if ActiveRecord::Base.connection.adapter_name.match?(/ibm/i)
1591
+ assert_no_difference(-> { Car.count }) do
1592
+ car = BrokenCar.create_or_find_by(name: "Civic")
1593
+
1594
+ assert_instance_of(BrokenCar, car)
1595
+ assert_not_predicate(car, :persisted?)
1596
+ end
1597
+ end
1598
+
1599
+ def test_create_or_find_by_bang_rollbacks_a_transaction
1600
+ skip "IBM_DB has different transaction rollback behavior on after_save failures" if ActiveRecord::Base.connection.adapter_name.match?(/ibm/i)
1601
+ assert_no_difference(-> { Car.count }) do
1602
+ car = BrokenCar.create_or_find_by!(name: "Civic")
1603
+
1604
+ assert_instance_of(BrokenCar, car)
1605
+ assert_not_predicate(car, :persisted?)
1606
+ end
1607
+ end
1608
+
1609
+ def test_create_or_find_by_on_a_collections_rollbacks_a_transaction_when_owner_is_not_persisted
1610
+ car = BrokenCar.create(name: "Civic")
1611
+ assert_not_predicate(car, :persisted?)
1612
+
1613
+ assert_raises(ActiveRecord::RecordNotSaved) do
1614
+ car.wheels.create_or_find_by!(size: 1500)
1615
+ end
1616
+ end
1617
+
1618
+ def test_create_or_find_by_with_block
1619
+ assert_nil Subscriber.find_by(nick: "bob")
1620
+
1621
+ subscriber = Subscriber.create_or_find_by(nick: "bob") do |record|
1622
+ record.name = "the builder"
1623
+ end
1624
+
1625
+ assert_equal "bob", subscriber.nick
1626
+ assert_equal "the builder", subscriber.name
1627
+ assert_predicate subscriber, :persisted?
1628
+ assert_equal subscriber, Subscriber.create_or_find_by(nick: "bob")
1629
+ assert_not_equal subscriber, Subscriber.create_or_find_by(nick: "cat")
1630
+ end
1631
+
1465
1632
  def test_create_or_find_by_should_not_raise_due_to_validation_errors
1466
1633
  assert_nothing_raised do
1467
1634
  bird = Bird.create_or_find_by(color: "green")
@@ -1530,6 +1697,28 @@ class RelationTest < ActiveRecord::TestCase
1530
1697
  assert_equal bird, Bird.find_or_initialize_by(name: "bob")
1531
1698
  end
1532
1699
 
1700
+ def test_find_or_initialize_by_with_block
1701
+ assert_nil Bird.find_by(name: "bob")
1702
+
1703
+ bird = Bird.find_or_initialize_by(name: "bob") do |record|
1704
+ record.color = "blue"
1705
+ end
1706
+ assert_predicate bird, :new_record?
1707
+ assert_equal "bob", bird.name
1708
+ assert_equal "blue", bird.color
1709
+ bird.save!
1710
+
1711
+ assert_equal bird, Bird.find_or_initialize_by(name: "bob", color: "blue")
1712
+ end
1713
+
1714
+ def test_find_or_initialize_by_with_cpk_association
1715
+ order1 = Cpk::Order.create!(id: [1, 1])
1716
+ order2 = Cpk::Order.create!(id: [1, 2])
1717
+ Cpk::Book.create!(id: [2, 1], order: order1)
1718
+ book = Cpk::Book.find_or_initialize_by(order: order2)
1719
+ assert_equal order2, book.order
1720
+ end
1721
+
1533
1722
  def test_explicit_create_with
1534
1723
  hens = Bird.where(name: "hen")
1535
1724
  assert_equal "hen", hens.new.name
@@ -1640,10 +1829,10 @@ class RelationTest < ActiveRecord::TestCase
1640
1829
  query = Tag.select(:name).where(id: [tag1.id, tag2.id])
1641
1830
 
1642
1831
  assert_equal ["Foo", "Foo"], query.map(&:name)
1643
- assert_sql(/DISTINCT/) do
1832
+ assert_queries_match(/DISTINCT/) do
1644
1833
  assert_equal ["Foo"], query.distinct.map(&:name)
1645
1834
  end
1646
- assert_sql(/DISTINCT/) do
1835
+ assert_queries_match(/DISTINCT/) do
1647
1836
  assert_equal ["Foo"], query.distinct(true).map(&:name)
1648
1837
  end
1649
1838
  assert_equal ["Foo", "Foo"], query.distinct(true).distinct(false).map(&:name)
@@ -1667,7 +1856,6 @@ class RelationTest < ActiveRecord::TestCase
1667
1856
  end
1668
1857
 
1669
1858
  def test_multiple_where_and_having_clauses
1670
- puts "test_multiple_where_and_having_clauses"
1671
1859
  post = Post.first
1672
1860
  having_then_where = Post.having(id: post.id).where(title: post.title)
1673
1861
  .having(id: post.id).where(title: post.title).group(:id, :author_id, :title, :body, :type, :legacy_comments_count, :taggings_with_delete_all_count, :taggings_with_destroy_count, :tags_count, :indestructible_tags_count, :tags_with_destroy_count, :tags_with_nullify_count)
@@ -1690,6 +1878,36 @@ class RelationTest < ActiveRecord::TestCase
1690
1878
  assert_not_predicate scope, :eager_loading?
1691
1879
  end
1692
1880
 
1881
+ def test_order_triggers_eager_loading
1882
+ scope = Post.includes(:comments).order("comments.label ASC")
1883
+ assert_predicate scope, :eager_loading?
1884
+ end
1885
+
1886
+ def test_order_doesnt_trigger_eager_loading_when_ordering_using_the_owner_table
1887
+ scope = Post.includes(:comments).order("posts.title ASC")
1888
+ assert_not_predicate scope, :eager_loading?
1889
+ end
1890
+
1891
+ def test_order_triggers_eager_loading_when_ordering_using_symbols
1892
+ scope = Post.includes(:comments).order(:"comments.label")
1893
+ assert_predicate scope, :eager_loading?
1894
+ end
1895
+
1896
+ def test_order_doesnt_trigger_eager_loading_when_ordering_using_owner_table_and_symbols
1897
+ scope = Post.includes(:comments).order(:"posts.title")
1898
+ assert_not_predicate scope, :eager_loading?
1899
+ end
1900
+
1901
+ def test_order_triggers_eager_loading_when_ordering_using_hash_syntax
1902
+ scope = Post.includes(:comments).order({ "comments.label": :ASC })
1903
+ assert_predicate scope, :eager_loading?
1904
+ end
1905
+
1906
+ def test_order_doesnt_trigger_eager_loading_when_ordering_using_the_owner_table_and_hash_syntax
1907
+ scope = Post.includes(:comments).order({ "posts.title": :ASC })
1908
+ assert_not_predicate scope, :eager_loading?
1909
+ end
1910
+
1693
1911
  def test_automatically_added_where_references
1694
1912
  scope = Post.where(comments: { body: "Bla" })
1695
1913
  assert_equal ["comments"], scope.references_values
@@ -1719,11 +1937,8 @@ class RelationTest < ActiveRecord::TestCase
1719
1937
  assert_equal ["comments"], scope.references_values
1720
1938
 
1721
1939
  scope = Post.order("#{Comment.quoted_table_name}.#{Comment.quoted_primary_key}")
1722
- if current_adapter?(:OracleAdapter)
1723
- assert_equal ["COMMENTS"], scope.references_values
1724
- else
1725
- assert_equal ["comments"], scope.references_values
1726
- end
1940
+
1941
+ assert_equal ["comments"], scope.references_values
1727
1942
 
1728
1943
  scope = Post.order("comments.body", "yaks.body")
1729
1944
  assert_equal ["comments", "yaks"], scope.references_values
@@ -1744,11 +1959,8 @@ class RelationTest < ActiveRecord::TestCase
1744
1959
  assert_equal %w(comments), scope.references_values
1745
1960
 
1746
1961
  scope = Post.reorder("#{Comment.quoted_table_name}.#{Comment.quoted_primary_key}")
1747
- if current_adapter?(:OracleAdapter)
1748
- assert_equal ["COMMENTS"], scope.references_values
1749
- else
1750
- assert_equal ["comments"], scope.references_values
1751
- end
1962
+
1963
+ assert_equal ["comments"], scope.references_values
1752
1964
 
1753
1965
  scope = Post.reorder("comments.body", "yaks.body")
1754
1966
  assert_equal %w(comments yaks), scope.references_values
@@ -1777,17 +1989,14 @@ class RelationTest < ActiveRecord::TestCase
1777
1989
  end
1778
1990
 
1779
1991
  def test_reorder_with_first
1992
+ post = nil
1993
+
1780
1994
  sql_log = capture_sql do
1781
- message = <<~MSG.squish
1782
- `.reorder(nil)` with `.first` / `.first!` no longer
1783
- takes non-deterministic result in Rails 7.0.
1784
- To continue taking non-deterministic result, use `.take` / `.take!` instead.
1785
- MSG
1786
- assert_deprecated(message) do
1787
- assert Post.order(:title).reorder(nil).first
1788
- end
1995
+ post = Post.order(:title).reorder(nil).first
1789
1996
  end
1790
- assert sql_log.all? { |sql| !/order by/i.match?(sql) }, "ORDER BY was used in the query: #{sql_log}"
1997
+
1998
+ assert_equal posts(:welcome), post
1999
+ assert sql_log.any? { |sql| /order by/i.match?(sql) }, "ORDER BY was not used in the query: #{sql_log}"
1791
2000
  end
1792
2001
 
1793
2002
  def test_reorder_with_take
@@ -1801,11 +2010,11 @@ class RelationTest < ActiveRecord::TestCase
1801
2010
  topics = Topic.all
1802
2011
 
1803
2012
  # the first query is triggered because there are no topics yet.
1804
- assert_queries(1) { assert topics.present? }
2013
+ assert_queries_count(1) { assert_predicate topics, :present? }
1805
2014
 
1806
2015
  # checking if there are topics is used before you actually display them,
1807
2016
  # thus it shouldn't invoke an extra count query.
1808
- assert_no_queries { assert topics.present? }
2017
+ assert_no_queries { assert_predicate topics, :present? }
1809
2018
  assert_no_queries { assert_not topics.blank? }
1810
2019
 
1811
2020
  # shows count of topics and loops after loading the query should not trigger extra queries either.
@@ -1814,7 +2023,7 @@ class RelationTest < ActiveRecord::TestCase
1814
2023
  assert_no_queries { topics.each }
1815
2024
 
1816
2025
  # count always trigger the COUNT query.
1817
- assert_queries(1) { topics.count }
2026
+ assert_queries_count(1) { topics.count }
1818
2027
 
1819
2028
  assert_predicate topics, :loaded?
1820
2029
  end
@@ -1854,7 +2063,7 @@ class RelationTest < ActiveRecord::TestCase
1854
2063
  end
1855
2064
 
1856
2065
  test "find_by doesn't have implicit ordering" do
1857
- assert_sql(/^((?!ORDER).)*$/) { Post.all.find_by(author_id: 2) }
2066
+ assert_queries_match(/^((?!ORDER).)*$/) { Post.all.find_by(author_id: 2) }
1858
2067
  end
1859
2068
 
1860
2069
  test "find_by requires at least one argument" do
@@ -1874,7 +2083,7 @@ class RelationTest < ActiveRecord::TestCase
1874
2083
  end
1875
2084
 
1876
2085
  test "find_by! doesn't have implicit ordering" do
1877
- assert_sql(/^((?!ORDER).)*$/) { Post.all.find_by!(author_id: 2) }
2086
+ assert_queries_match(/^((?!ORDER).)*$/) { Post.all.find_by!(author_id: 2) }
1878
2087
  end
1879
2088
 
1880
2089
  test "find_by! raises RecordNotFound if the record is missing" do
@@ -1891,7 +2100,7 @@ class RelationTest < ActiveRecord::TestCase
1891
2100
  relation = Post.all
1892
2101
  relation.to_a
1893
2102
 
1894
- assert_raises(ActiveRecord::ImmutableRelation) do
2103
+ assert_raises(ActiveRecord::UnmodifiableRelation) do
1895
2104
  relation.where! "foo"
1896
2105
  end
1897
2106
  end
@@ -1900,7 +2109,7 @@ class RelationTest < ActiveRecord::TestCase
1900
2109
  relation = Post.all
1901
2110
  relation.to_a
1902
2111
 
1903
- assert_raises(ActiveRecord::ImmutableRelation) do
2112
+ assert_raises(ActiveRecord::UnmodifiableRelation) do
1904
2113
  relation.limit! 5
1905
2114
  end
1906
2115
  end
@@ -1909,7 +2118,7 @@ class RelationTest < ActiveRecord::TestCase
1909
2118
  relation = Post.all
1910
2119
  relation.to_a
1911
2120
 
1912
- assert_raises(ActiveRecord::ImmutableRelation) do
2121
+ assert_raises(ActiveRecord::UnmodifiableRelation) do
1913
2122
  relation.merge! where: "foo"
1914
2123
  end
1915
2124
  end
@@ -1918,7 +2127,7 @@ class RelationTest < ActiveRecord::TestCase
1918
2127
  relation = Post.all
1919
2128
  relation.to_a
1920
2129
 
1921
- assert_raises(ActiveRecord::ImmutableRelation) do
2130
+ assert_raises(ActiveRecord::UnmodifiableRelation) do
1922
2131
  relation.extending! Module.new
1923
2132
  end
1924
2133
  end
@@ -1927,8 +2136,8 @@ class RelationTest < ActiveRecord::TestCase
1927
2136
  relation = Post.all
1928
2137
  relation.arel
1929
2138
 
1930
- assert_raises(ActiveRecord::ImmutableRelation) { relation.limit!(5) }
1931
- assert_raises(ActiveRecord::ImmutableRelation) { relation.where!("1 = 2") }
2139
+ assert_raises(ActiveRecord::UnmodifiableRelation) { relation.limit!(5) }
2140
+ assert_raises(ActiveRecord::UnmodifiableRelation) { relation.where!("1 = 2") }
1932
2141
  end
1933
2142
 
1934
2143
  test "relations show the records in #inspect" do
@@ -1942,13 +2151,13 @@ class RelationTest < ActiveRecord::TestCase
1942
2151
  end
1943
2152
 
1944
2153
  test "relations don't load all records in #inspect" do
1945
- assert_sql(/LIMIT|ROWNUM <=|FETCH FIRST/) do
2154
+ assert_queries_match(/LIMIT|ROWNUM <=|FETCH FIRST/) do
1946
2155
  Post.all.inspect
1947
2156
  end
1948
2157
  end
1949
2158
 
1950
2159
  test "loading query is annotated in #inspect" do
1951
- assert_sql(%r(/\* loading for inspect \*/)) do
2160
+ assert_queries_match(%r(/\* loading for inspect \*/)) do
1952
2161
  Post.all.inspect
1953
2162
  end
1954
2163
  end
@@ -1964,6 +2173,35 @@ class RelationTest < ActiveRecord::TestCase
1964
2173
  end
1965
2174
  end
1966
2175
 
2176
+ test "relations limit the records in #pretty_print at 10" do
2177
+ relation = Post.limit(11)
2178
+ out = StringIO.new
2179
+ PP.pp(relation, out)
2180
+ assert_equal 10, out.string.scan(/#<\w*Post:/).size
2181
+ assert out.string.end_with?("\"...\"]\n"), "Did not end with an ellipsis."
2182
+ end
2183
+
2184
+ test "relations don't load all records in #pretty_print" do
2185
+ assert_queries_match(/LIMIT|ROWNUM <=|FETCH FIRST/) do
2186
+ PP.pp Post.all, StringIO.new # avoid outputting.
2187
+ end
2188
+ end
2189
+
2190
+ test "loading query is annotated in #pretty_print" do
2191
+ assert_queries_match(%r(/\* loading for pp \*/)) do
2192
+ PP.pp Post.all, StringIO.new # avoid outputting.
2193
+ end
2194
+ end
2195
+
2196
+ test "already-loaded relations don't perform a new query in #pretty_print" do
2197
+ relation = Post.limit(2)
2198
+ relation.to_a
2199
+
2200
+ assert_no_queries do
2201
+ PP.pp relation, StringIO.new # avoid outputting.
2202
+ end
2203
+ end
2204
+
1967
2205
  test "using a custom table affects the wheres" do
1968
2206
  post = posts(:welcome)
1969
2207
 
@@ -1986,7 +2224,7 @@ class RelationTest < ActiveRecord::TestCase
1986
2224
 
1987
2225
  test "#load" do
1988
2226
  relation = Post.all
1989
- assert_queries(1) do
2227
+ assert_queries_count(1) do
1990
2228
  assert_equal relation, relation.load
1991
2229
  end
1992
2230
  assert_no_queries { relation.to_a }
@@ -2021,6 +2259,8 @@ class RelationTest < ActiveRecord::TestCase
2021
2259
  test "joins with order by custom attribute" do
2022
2260
  companies = Company.create!([{ name: "test1" }, { name: "test2" }])
2023
2261
  companies.each { |company| company.contracts.create! }
2262
+ # In ordering by Contract#metadata, we rely on that JSON string to
2263
+ # be consistent
2024
2264
  assert_equal companies, Company.joins(:contracts).order(:metadata, :count)
2025
2265
  assert_equal companies.reverse, Company.joins(:contracts).order(metadata: :desc, count: :desc)
2026
2266
  end
@@ -2126,6 +2366,20 @@ class RelationTest < ActiveRecord::TestCase
2126
2366
  assert_equal Post.count, posts.unscope(where: :title).count
2127
2367
  end
2128
2368
 
2369
+ def test_unscope_with_double_dot_where
2370
+ posts = Post.where(id: 1..2)
2371
+
2372
+ assert_equal 2, posts.count
2373
+ assert_equal Post.count, posts.unscope(where: :id).count
2374
+ end
2375
+
2376
+ def test_unscope_with_triple_dot_where
2377
+ posts = Post.where(id: 1...3)
2378
+
2379
+ assert_equal 2, posts.count
2380
+ assert_equal Post.count, posts.unscope(where: :id).count
2381
+ end
2382
+
2129
2383
  def test_locked_should_not_build_arel
2130
2384
  posts = Post.locked
2131
2385
  assert_predicate posts, :locked?
@@ -2176,12 +2430,12 @@ class RelationTest < ActiveRecord::TestCase
2176
2430
 
2177
2431
  test "#skip_query_cache!" do
2178
2432
  Post.cache do
2179
- assert_queries(1) do
2433
+ assert_queries_count(1) do
2180
2434
  Post.all.load
2181
2435
  Post.all.load
2182
2436
  end
2183
2437
 
2184
- assert_queries(2) do
2438
+ assert_queries_count(2) do
2185
2439
  Post.all.skip_query_cache!.load
2186
2440
  Post.all.skip_query_cache!.load
2187
2441
  end
@@ -2190,12 +2444,12 @@ class RelationTest < ActiveRecord::TestCase
2190
2444
 
2191
2445
  test "#skip_query_cache! with an eager load" do
2192
2446
  Post.cache do
2193
- assert_queries(1) do
2447
+ assert_queries_count(1) do
2194
2448
  Post.eager_load(:comments).load
2195
2449
  Post.eager_load(:comments).load
2196
2450
  end
2197
2451
 
2198
- assert_queries(2) do
2452
+ assert_queries_count(2) do
2199
2453
  Post.eager_load(:comments).skip_query_cache!.load
2200
2454
  Post.eager_load(:comments).skip_query_cache!.load
2201
2455
  end
@@ -2204,12 +2458,12 @@ class RelationTest < ActiveRecord::TestCase
2204
2458
 
2205
2459
  test "#skip_query_cache! with a preload" do
2206
2460
  Post.cache do
2207
- assert_queries(2) do
2461
+ assert_queries_count(2) do
2208
2462
  Post.preload(:comments).load
2209
2463
  Post.preload(:comments).load
2210
2464
  end
2211
2465
 
2212
- assert_queries(4) do
2466
+ assert_queries_count(4) do
2213
2467
  Post.preload(:comments).skip_query_cache!.load
2214
2468
  Post.preload(:comments).skip_query_cache!.load
2215
2469
  end
@@ -2229,7 +2483,7 @@ class RelationTest < ActiveRecord::TestCase
2229
2483
  assert_empty authors
2230
2484
  end
2231
2485
 
2232
- (ActiveRecord::Relation::MULTI_VALUE_METHODS - [:extending]).each do |method|
2486
+ (ActiveRecord::Relation::MULTI_VALUE_METHODS - [:extending, :with]).each do |method|
2233
2487
  test "#{method} with blank value" do
2234
2488
  authors = Author.public_send(method, [""])
2235
2489
  assert_empty authors.public_send(:"#{method}_values")
@@ -2239,13 +2493,57 @@ class RelationTest < ActiveRecord::TestCase
2239
2493
  private
2240
2494
  def custom_post_relation(alias_name = "omg_posts")
2241
2495
  table_alias = Post.arel_table.alias(alias_name)
2242
- table_metadata = ActiveRecord::TableMetadata.new(Post, table_alias)
2243
- predicate_builder = ActiveRecord::PredicateBuilder.new(table_metadata)
2244
2496
 
2245
- ActiveRecord::Relation.create(
2246
- Post,
2247
- table: table_alias,
2248
- predicate_builder: predicate_builder
2249
- )
2497
+ ActiveRecord::Relation.create(Post, table: table_alias)
2498
+ end
2499
+ end
2500
+
2501
+ class CreateOrFindByWithinTransactions < ActiveRecord::TestCase
2502
+ unless current_adapter?(:SQLite3Adapter)
2503
+ self.use_transactional_tests = false
2504
+
2505
+ def teardown
2506
+ Subscriber.delete_all
2507
+ end
2508
+
2509
+ def test_multiple_find_or_create_by_within_transactions
2510
+ duel { Subscriber.find_or_create_by(nick: "bob") }
2511
+ end
2512
+
2513
+ def test_multiple_find_or_create_by_bang_within_transactions
2514
+ duel { Subscriber.find_or_create_by!(nick: "bob") }
2250
2515
  end
2516
+
2517
+ private
2518
+ def duel
2519
+ assert_nil Subscriber.find_by(nick: "bob")
2520
+
2521
+ a_wakeup = Concurrent::Event.new
2522
+ b_wakeup = Concurrent::Event.new
2523
+
2524
+ a = Thread.new do
2525
+ Subscriber.transaction do
2526
+ a_wakeup.wait
2527
+ yield
2528
+ b_wakeup.set
2529
+ end
2530
+ end
2531
+
2532
+ b = Thread.new do
2533
+ Subscriber.transaction do
2534
+ # Read the record prematurely for MySQL REPEATABLE READ to kick in
2535
+ Subscriber.find_by(nick: "bob")
2536
+
2537
+ a_wakeup.set
2538
+ b_wakeup.wait
2539
+ yield
2540
+ end
2541
+ end
2542
+
2543
+ a.join
2544
+ b.join
2545
+
2546
+ assert_equal 1, Subscriber.where(nick: "bob").count
2547
+ end
2548
+ end
2251
2549
  end