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
@@ -0,0 +1,240 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "cases/encryption/helper"
4
+ require "models/author_encrypted"
5
+
6
+ class ActiveRecord::Encryption::EncryptionSchemesTest < ActiveRecord::EncryptionTestCase
7
+ test "can decrypt encrypted_value encrypted with a different encryption scheme" do
8
+ ActiveRecord::Encryption.config.support_unencrypted_data = false
9
+
10
+ author = create_author_with_name_encrypted_with_previous_scheme
11
+ assert_equal "dhh", author.reload.name
12
+ assert author.encrypted_attribute? :name
13
+ end
14
+
15
+ test "when defining previous encryption schemes, you still get Decryption errors when using invalid clear values" do
16
+ ActiveRecord::Encryption.config.support_unencrypted_data = false
17
+
18
+ author = ActiveRecord::Encryption.without_encryption { EncryptedAuthor.create!(name: "unencrypted author") }
19
+
20
+ assert_raises ActiveRecord::Encryption::Errors::Decryption do
21
+ author.reload.name
22
+ end
23
+ end
24
+
25
+ test "use a custom encryptor" do
26
+ author = EncryptedAuthor1.create name: "1"
27
+ assert_equal "1", author.name
28
+ assert author.encrypted_attribute? :name
29
+ end
30
+
31
+ test "support previous contexts" do
32
+ ActiveRecord::Encryption.config.support_unencrypted_data = true
33
+
34
+ author = EncryptedAuthor2.create name: "2"
35
+ assert_equal "2", author.name
36
+ assert_equal author, EncryptedAuthor2.find_by_name("2")
37
+ assert author.encrypted_attribute? :name
38
+
39
+ Author.find(author.id).update! name: "1"
40
+ assert_equal "1", author.reload.name
41
+ assert_equal author, EncryptedAuthor2.find_by_name("1")
42
+ assert_not author.encrypted_attribute? :name
43
+ end
44
+
45
+ test "use global previous schemes to decrypt data encrypted with previous schemes" do
46
+ ActiveRecord::Encryption.config.support_unencrypted_data = false
47
+ encrypted_author_class = declare_class_with_global_previous_encryption_schemes({ encryptor: TestEncryptor.new("0" => "1") }, { encryptor: TestEncryptor.new("1" => "2") })
48
+
49
+ assert_equal 2, encrypted_author_class.type_for_attribute(:name).previous_types.count
50
+ previous_type_1, previous_type_2 = encrypted_author_class.type_for_attribute(:name).previous_types
51
+
52
+ author = ActiveRecord::Encryption.without_encryption do
53
+ encrypted_author_class.create name: previous_type_1.serialize("1")
54
+ end
55
+ assert_equal "0", author.reload.name
56
+
57
+ author = ActiveRecord::Encryption.without_encryption do
58
+ encrypted_author_class.create name: previous_type_2.serialize("2")
59
+ end
60
+ assert_equal "1", author.reload.name
61
+ end
62
+
63
+ test "use global previous schemes to decrypt data encrypted with previous schemes with unencrypted data" do
64
+ ActiveRecord::Encryption.config.support_unencrypted_data = true
65
+ encrypted_author_class = declare_class_with_global_previous_encryption_schemes({ encryptor: TestEncryptor.new("0" => "1") }, { encryptor: TestEncryptor.new("1" => "2") })
66
+
67
+ assert_equal 3, encrypted_author_class.type_for_attribute(:name).previous_types.count
68
+ previous_type_1, previous_type_2 = encrypted_author_class.type_for_attribute(:name).previous_types
69
+
70
+ author = ActiveRecord::Encryption.without_encryption do
71
+ encrypted_author_class.create name: previous_type_1.serialize("1")
72
+ end
73
+ assert_equal "0", author.reload.name
74
+
75
+ author = ActiveRecord::Encryption.without_encryption do
76
+ encrypted_author_class.create name: previous_type_2.serialize("2")
77
+ end
78
+ assert_equal "1", author.reload.name
79
+ end
80
+
81
+ test "returns ciphertext all the previous schemes fail to decrypt and support for unencrypted data is on" do
82
+ ActiveRecord::Encryption.config.support_unencrypted_data = true
83
+ encrypted_author_class = declare_class_with_global_previous_encryption_schemes({ encryptor: TestEncryptor.new("0" => "1") }, { encryptor: TestEncryptor.new("1" => "2") })
84
+
85
+ author = ActiveRecord::Encryption.without_encryption do
86
+ encrypted_author_class.create name: "some ciphertext"
87
+ end
88
+
89
+ assert_equal "some ciphertext", author.reload.name
90
+ end
91
+
92
+ test "raise decryption error when all the previous schemes fail to decrypt" do
93
+ ActiveRecord::Encryption.config.support_unencrypted_data = false
94
+ encrypted_author_class = declare_class_with_global_previous_encryption_schemes({ encryptor: TestEncryptor.new("0" => "1") }, { encryptor: TestEncryptor.new("1" => "2") })
95
+
96
+ author = ActiveRecord::Encryption.without_encryption do
97
+ encrypted_author_class.create name: "some invalid ciphertext"
98
+ end
99
+
100
+ assert_raise ActiveRecord::Encryption::Errors::Decryption do
101
+ author.reload.name
102
+ end
103
+ end
104
+
105
+ test "deterministic encryption is fixed by default: it will always use the oldest scheme to encrypt data" do
106
+ ActiveRecord::Encryption.config.support_unencrypted_data = false
107
+ ActiveRecord::Encryption.config.deterministic_key = "12345"
108
+ ActiveRecord::Encryption.config.previous = [{ downcase: true, deterministic: true }, { downcase: false, deterministic: true }]
109
+
110
+ encrypted_author_class = Class.new(Author) do
111
+ self.table_name = "authors"
112
+
113
+ encrypts :name, deterministic: true, downcase: false
114
+ end
115
+
116
+ author = encrypted_author_class.create!(name: "STEPHEN KING")
117
+ assert_equal "stephen king", author.name
118
+ end
119
+
120
+ test "don't use global previous schemes with a different deterministic nature" do
121
+ ActiveRecord::Encryption.config.support_unencrypted_data = false
122
+ ActiveRecord::Encryption.config.deterministic_key = "12345"
123
+ ActiveRecord::Encryption.config.previous = [{ downcase: true, deterministic: false }, { downcase: false, deterministic: true }]
124
+
125
+ encrypted_author_class = Class.new(Author) do
126
+ self.table_name = "authors"
127
+
128
+ encrypts :name, deterministic: true, downcase: false
129
+ end
130
+
131
+ author = encrypted_author_class.create!(name: "STEPHEN KING")
132
+ assert_equal "STEPHEN KING", author.name
133
+ end
134
+
135
+ test "deterministic encryption will use the newest encryption scheme to encrypt data when setting it to { fixed: false }" do
136
+ ActiveRecord::Encryption.config.support_unencrypted_data = false
137
+ ActiveRecord::Encryption.config.deterministic_key = "12345"
138
+ ActiveRecord::Encryption.config.previous = [{ downcase: true, deterministic: true }, { downcase: false, deterministic: true }]
139
+
140
+ encrypted_author_class = Class.new(Author) do
141
+ self.table_name = "authors"
142
+
143
+ encrypts :name, deterministic: { fixed: false }, downcase: false
144
+ end
145
+
146
+ author = encrypted_author_class.create!(name: "STEPHEN KING")
147
+ assert_equal "STEPHEN KING", author.name
148
+ end
149
+
150
+ test "use global previous schemes when performing queries" do
151
+ ActiveRecord::Encryption.config.support_unencrypted_data = false
152
+ ActiveRecord::Encryption.config.deterministic_key = "12345"
153
+ ActiveRecord::Encryption.config.previous = [{ downcase: true, deterministic: true }, { downcase: false, deterministic: true }]
154
+
155
+ encrypted_author_class = Class.new(Author) do
156
+ self.table_name = "authors"
157
+
158
+ encrypts :name, deterministic: true, downcase: false
159
+ end
160
+
161
+ author = encrypted_author_class.create!(name: "STEPHEN KING")
162
+ assert_equal author, encrypted_author_class.find_by_name("STEPHEN KING")
163
+ assert_equal author, encrypted_author_class.find_by_name("stephen king")
164
+ end
165
+
166
+ test "don't use global previous schemes with a different deterministic nature when performing queries" do
167
+ ActiveRecord::Encryption.config.support_unencrypted_data = false
168
+ ActiveRecord::Encryption.config.deterministic_key = "12345"
169
+ ActiveRecord::Encryption.config.previous = [{ downcase: true, deterministic: false }, { downcase: false, deterministic: true }]
170
+
171
+ encrypted_author_class = Class.new(Author) do
172
+ self.table_name = "authors"
173
+
174
+ encrypts :name, deterministic: true, downcase: false
175
+ end
176
+
177
+ author = encrypted_author_class.create!(name: "STEPHEN KING")
178
+ assert_equal author, encrypted_author_class.find_by_name("STEPHEN KING")
179
+ assert_nil encrypted_author_class.find_by_name("stephen king")
180
+ end
181
+
182
+ private
183
+ class TestEncryptor
184
+ def initialize(ciphertexts_by_clear_value)
185
+ @ciphertexts_by_clear_value = ciphertexts_by_clear_value
186
+ end
187
+
188
+ def encrypt(clear_text, key_provider: nil, cipher_options: {})
189
+ @ciphertexts_by_clear_value[clear_text] || clear_text
190
+ end
191
+
192
+ def decrypt(encrypted_text, key_provider: nil, cipher_options: {})
193
+ @ciphertexts_by_clear_value.each { |clear_value, encrypted_value| return clear_value if encrypted_value == encrypted_text }
194
+ raise ActiveRecord::Encryption::Errors::Decryption, "Couldn't find a match for #{encrypted_text} (#{@ciphertexts_by_clear_value.inspect})"
195
+ end
196
+
197
+ def encrypted?(text)
198
+ decrypt(text)
199
+ rescue ActiveRecord::Encryption::Errors::Decryption
200
+ false
201
+ end
202
+
203
+ def binary?
204
+ false
205
+ end
206
+ end
207
+
208
+ class EncryptedAuthor1 < Author
209
+ self.table_name = "authors"
210
+
211
+ encrypts :name, encryptor: TestEncryptor.new("1" => "2")
212
+ end
213
+
214
+ class EncryptedAuthor2 < Author
215
+ self.table_name = "authors"
216
+
217
+ encrypts :name, encryptor: TestEncryptor.new("2" => "3"), previous: { encryptor: TestEncryptor.new("1" => "2") }
218
+ end
219
+
220
+ def create_author_with_name_encrypted_with_previous_scheme
221
+ author = EncryptedAuthor.create!(name: "david")
222
+ old_type = EncryptedAuthor.type_for_attribute(:name).previous_types.first
223
+ value_encrypted_with_old_type = old_type.serialize("dhh")
224
+ ActiveRecord::Encryption.without_encryption do
225
+ author.update!(name: value_encrypted_with_old_type)
226
+ end
227
+ author
228
+ end
229
+
230
+ def declare_class_with_global_previous_encryption_schemes(*previous_schemes)
231
+ ActiveRecord::Encryption.config.previous = previous_schemes
232
+
233
+ # We want to evaluate .encrypts *after* tweaking the config property
234
+ Class.new(Author) do
235
+ self.table_name = "authors"
236
+
237
+ encrypts :name
238
+ end.tap { |klass| klass.type_for_attribute(:name) }
239
+ end
240
+ end
@@ -0,0 +1,113 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "cases/encryption/helper"
4
+
5
+ class ActiveRecord::Encryption::EncryptorTest < ActiveRecord::EncryptionTestCase
6
+ setup do
7
+ @secret_key = "This is my secret 256 bits key!!"
8
+ @encryptor = ActiveRecord::Encryption::Encryptor.new
9
+ end
10
+
11
+ test "encrypt and decrypt a string" do
12
+ assert_encrypt_text("my secret text")
13
+ end
14
+
15
+ test "trying to decrypt something else than a string will raise a Decryption error" do
16
+ assert_raises(ActiveRecord::Encryption::Errors::Decryption) do
17
+ @encryptor.decrypt(:it_can_only_decrypt_strings)
18
+ end
19
+ end
20
+
21
+ test "decrypt an invalid string will raise a Decryption error" do
22
+ assert_raises(ActiveRecord::Encryption::Errors::Decryption) do
23
+ @encryptor.decrypt("some test that does not make sense")
24
+ end
25
+ end
26
+
27
+ test "decrypt an encrypted text with an invalid key will raise a Decryption error" do
28
+ assert_raises(ActiveRecord::Encryption::Errors::Decryption) do
29
+ encrypted_text = @encryptor.encrypt("Some text to encrypt")
30
+ @encryptor.decrypt(encrypted_text, key_provider: ActiveRecord::Encryption::DerivedSecretKeyProvider.new("some invalid key"))
31
+ end
32
+ end
33
+
34
+ test "if an encryption error happens when encrypting an encrypted text it should raise" do
35
+ assert_raises(ActiveRecord::Encryption::Errors::Encryption) do
36
+ key_provider_that_raises_an_encryption_error = ActiveRecord::Encryption::DerivedSecretKeyProvider.new("some key")
37
+ key_provider_that_raises_an_encryption_error.stub :encryption_key, -> { raise ActiveRecord::Encryption::Errors::Encryption } do
38
+ @encryptor.encrypt("Some text to encrypt", key_provider: key_provider_that_raises_an_encryption_error)
39
+ end
40
+ end
41
+ end
42
+
43
+ test "content is compressed" do
44
+ content = SecureRandom.hex(5.kilobytes)
45
+ cipher_text = @encryptor.encrypt(content)
46
+
47
+ assert_encrypt_text content
48
+ assert cipher_text.bytesize < content.bytesize
49
+ end
50
+
51
+ test "content is not compressed, when disabled" do
52
+ @encryptor = ActiveRecord::Encryption::Encryptor.new(compress: false)
53
+ content = SecureRandom.hex(5.kilobytes)
54
+ cipher_text = @encryptor.encrypt(content)
55
+
56
+ assert_encrypt_text content
57
+ assert cipher_text.bytesize > content.bytesize
58
+ end
59
+
60
+ test "trying to encrypt custom classes raises a ForbiddenClass exception" do
61
+ assert_raises ActiveRecord::Encryption::Errors::ForbiddenClass do
62
+ @encryptor.encrypt(Struct.new(:name).new("Jorge"))
63
+ end
64
+ end
65
+
66
+ test "store custom metadata with the encrypted data, accessible by the key provider" do
67
+ key = ActiveRecord::Encryption::Key.new(@secret_key)
68
+ key.public_tags[:key] = "my tag"
69
+ key_provider = ActiveRecord::Encryption::KeyProvider.new(key)
70
+ encryptor = ActiveRecord::Encryption::Encryptor.new
71
+
72
+ key_provider.stub :decryption_keys, ->(message) { [key] } do
73
+ decrypted_text = encryptor.decrypt encryptor.encrypt("some text", key_provider: key_provider), key_provider: key_provider
74
+ assert decrypted_text
75
+ end
76
+ end
77
+
78
+ test "encrypted? returns whether the passed text is encrypted" do
79
+ assert @encryptor.encrypted?(@encryptor.encrypt("clean text"))
80
+ assert_not @encryptor.encrypted?("clean text")
81
+ end
82
+
83
+ test "decrypt respects encoding even when compression is used" do
84
+ text = "The Starfleet is here #{'OMG! ' * 50}!".dup.force_encoding(Encoding::ISO_8859_1)
85
+ encrypted_text = @encryptor.encrypt(text)
86
+ decrypted_text = @encryptor.decrypt(encrypted_text)
87
+
88
+ assert_equal Encoding::ISO_8859_1, decrypted_text.encoding
89
+ end
90
+
91
+ test "accept a custom compressor" do
92
+ compressor = Module.new do
93
+ def self.deflate(data)
94
+ "compressed #{data}"
95
+ end
96
+
97
+ def self.inflate(data)
98
+ data.sub(/\Acompressed /, "")
99
+ end
100
+ end
101
+ @encryptor = ActiveRecord::Encryption::Encryptor.new(compressor: compressor)
102
+ content = SecureRandom.hex(5.kilobytes)
103
+
104
+ assert_encrypt_text content
105
+ end
106
+
107
+ private
108
+ def assert_encrypt_text(clean_text)
109
+ encrypted_text = @encryptor.encrypt(clean_text)
110
+ assert_not_equal encrypted_text, clean_text
111
+ assert_equal clean_text, @encryptor.decrypt(encrypted_text)
112
+ end
113
+ end
@@ -0,0 +1,49 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "cases/encryption/helper"
4
+
5
+ class ActiveRecord::Encryption::EnvelopeEncryptionKeyProviderTest < ActiveRecord::EncryptionTestCase
6
+ setup do
7
+ @key_provider = ActiveRecord::Encryption::EnvelopeEncryptionKeyProvider.new
8
+ end
9
+
10
+ test "encryption_key returns random encryption keys" do
11
+ keys = 5.times.collect { @key_provider.encryption_key }
12
+ assert_equal 5, keys.group_by(&:secret).length
13
+ end
14
+
15
+ test "generate_random_encryption_key generates keys of 32 bytes" do
16
+ assert_equal 32, @key_provider.encryption_key.secret.bytesize
17
+ end
18
+
19
+ test "generated random keys carry their secret encrypted with the primary key" do
20
+ key = @key_provider.encryption_key
21
+ encrypted_secret = key.public_tags.encrypted_data_key
22
+ assert_equal key.secret, ActiveRecord::Encryption.cipher.decrypt(encrypted_secret, key: @key_provider.active_primary_key.secret)
23
+ end
24
+
25
+ test "decryption_key_for returns the decryption key for a message that was encrypted with a generated encryption key" do
26
+ key = @key_provider.encryption_key
27
+ encrypted_encoded_message = ActiveRecord::Encryption.encryptor.encrypt("some message", key_provider: ActiveRecord::Encryption::KeyProvider.new(key))
28
+ encrypted_message = ActiveRecord::Encryption.message_serializer.load encrypted_encoded_message
29
+ assert_equal key.secret, @key_provider.decryption_keys(encrypted_message).first.secret
30
+ end
31
+
32
+ test "work with multiple keys when config.store_key_references is false" do
33
+ ActiveRecord::Encryption.config.primary_key = ["key 1", "key 2"]
34
+
35
+ assert_encryptor_works_with @key_provider
36
+ end
37
+
38
+ test "work with multiple keys when config.store_key_references is true" do
39
+ ActiveRecord::Encryption.config.primary_key = ["key 1", "key 2"]
40
+ ActiveRecord::Encryption.config.store_key_references = true
41
+
42
+ assert_encryptor_works_with @key_provider
43
+ end
44
+
45
+ private
46
+ def assert_multiple_primary_keys
47
+ assert Rails.application.credentials.dig(:active_record_encryption, :primary_key).length > 1
48
+ end
49
+ end
@@ -0,0 +1,81 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "cases/encryption/helper"
4
+ require "models/book_encrypted"
5
+
6
+ class ActiveRecord::Encryption::ExtendedDeterministicQueriesTest < ActiveRecord::EncryptionTestCase
7
+ setup do
8
+ ActiveRecord::Encryption.config.support_unencrypted_data = true
9
+ end
10
+
11
+ test "Finds records when data is unencrypted" do
12
+ UnencryptedBook.create!(name: "Dune")
13
+ assert EncryptedBook.find_by(name: "Dune") # core
14
+ assert EncryptedBook.where("id > 0").find_by(name: "Dune") # relation
15
+ end
16
+
17
+ test "Finds records when data is encrypted" do
18
+ EncryptedBook.create!(name: "Dune")
19
+ assert EncryptedBook.find_by(name: "Dune") # core
20
+ assert EncryptedBook.where("id > 0").find_by(name: "Dune") # relation
21
+ end
22
+
23
+ test "Works well with downcased attributes" do
24
+ EncryptedBookWithDowncaseName.create! name: "Dune"
25
+ assert EncryptedBookWithDowncaseName.find_by(name: "DUNE")
26
+ end
27
+
28
+ test "Works well with string attribute names" do
29
+ UnencryptedBook.create! "name" => "Dune"
30
+ assert EncryptedBook.find_by("name" => "Dune")
31
+ end
32
+
33
+ test "find_or_create_by works" do
34
+ EncryptedBook.find_or_create_by!(name: "Dune")
35
+ assert EncryptedBook.find_by(name: "Dune")
36
+
37
+ EncryptedBook.find_or_create_by!(name: "Dune")
38
+ assert EncryptedBook.find_by(name: "Dune")
39
+ end
40
+
41
+ test "does not mutate arguments" do
42
+ props = { name: "Dune" }
43
+
44
+ assert_equal "Dune", EncryptedBook.find_or_initialize_by(props).name
45
+ assert_equal "Dune", props[:name]
46
+ end
47
+
48
+ test "where(...).first_or_create works" do
49
+ EncryptedBook.where(name: "Dune").first_or_create
50
+ assert EncryptedBook.exists?(name: "Dune")
51
+ end
52
+
53
+ test "exists?(...) works" do
54
+ EncryptedBook.create! name: "Dune"
55
+ assert EncryptedBook.exists?(name: "Dune")
56
+ end
57
+
58
+ test "If support_unencrypted_data is opted out at the attribute level, cannot find unencrypted data" do
59
+ UnencryptedBook.create! name: "Dune"
60
+ assert_nil EncryptedBookWithUnencryptedDataOptedOut.find_by(name: "Dune") # core
61
+ assert_nil EncryptedBookWithUnencryptedDataOptedOut.where("id > 0").find_by(name: "Dune") # relation
62
+ end
63
+
64
+ test "If support_unencrypted_data is opted out at the attribute level, can find encrypted data" do
65
+ EncryptedBook.create! name: "Dune"
66
+ assert EncryptedBookWithUnencryptedDataOptedOut.find_by(name: "Dune") # core
67
+ assert EncryptedBookWithUnencryptedDataOptedOut.where("id > 0").find_by(name: "Dune") # relation
68
+ end
69
+
70
+ test "If support_unencrypted_data is opted in at the attribute level, can find unencrypted data" do
71
+ UnencryptedBook.create! name: "Dune"
72
+ assert EncryptedBookWithUnencryptedDataOptedIn.find_by(name: "Dune") # core
73
+ assert EncryptedBookWithUnencryptedDataOptedIn.where("id > 0").find_by(name: "Dune") # relation
74
+ end
75
+
76
+ test "If support_unencrypted_data is opted in at the attribute level, can find encrypted data" do
77
+ EncryptedBook.create! name: "Dune"
78
+ assert EncryptedBookWithUnencryptedDataOptedIn.find_by(name: "Dune") # core
79
+ assert EncryptedBookWithUnencryptedDataOptedIn.where("id > 0").find_by(name: "Dune") # relation
80
+ end
81
+ end
@@ -0,0 +1,152 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "cases/helper"
4
+
5
+ class ActiveRecord::Fixture
6
+ prepend ActiveRecord::Encryption::EncryptedFixtures
7
+ end
8
+
9
+ module ActiveRecord::Encryption
10
+ module EncryptionHelpers
11
+ def assert_ciphertext_decrypts_to(model, attribute_name, ciphertext)
12
+ assert_not_equal model.public_send(attribute_name), ciphertext
13
+ assert_not_equal model.read_attribute(attribute_name), ciphertext
14
+ cleartext = model.type_for_attribute(attribute_name).deserialize(ciphertext)
15
+ assert_equal model.read_attribute(attribute_name), cleartext
16
+ end
17
+
18
+ def assert_encrypted_attribute(model, attribute_name, expected_value)
19
+ assert_ciphertext_decrypts_to model, attribute_name, model.read_attribute_before_type_cast(attribute_name)
20
+ assert_equal expected_value, model.public_send(attribute_name)
21
+ unless model.new_record?
22
+ model.reload
23
+ assert_ciphertext_decrypts_to model, attribute_name, model.read_attribute_before_type_cast(attribute_name)
24
+ assert_equal expected_value, model.public_send(attribute_name)
25
+ end
26
+ end
27
+
28
+ def assert_invalid_key_cant_read_attribute(model, attribute_name)
29
+ if model.type_for_attribute(attribute_name).key_provider.respond_to?(:keys=)
30
+ assert_invalid_key_cant_read_attribute_with_custom_key_provider(model, attribute_name)
31
+ else
32
+ assert_invalid_key_cant_read_attribute_with_default_key_provider(model, attribute_name)
33
+ end
34
+ end
35
+
36
+ def assert_not_encrypted_attribute(model, attribute_name, expected_value)
37
+ assert_equal expected_value, model.send(attribute_name)
38
+ assert_equal expected_value, model.read_attribute_before_type_cast(attribute_name)
39
+ end
40
+
41
+ def assert_encrypted_record(model)
42
+ encrypted_attributes = model.class.encrypted_attributes.find_all { |attribute_name| model.send(attribute_name).present? }
43
+ assert_not encrypted_attributes.empty?, "The model has no encrypted attributes with content to check (they are all blank)"
44
+
45
+ encrypted_attributes.each do |attribute_name|
46
+ assert_encrypted_attribute model, attribute_name, model.send(attribute_name)
47
+ end
48
+ end
49
+
50
+ def assert_encryptor_works_with(key_provider)
51
+ encryptor = ActiveRecord::Encryption::Encryptor.new
52
+
53
+ encrypted_message = encryptor.encrypt("some text", key_provider: key_provider)
54
+ assert_equal "some text", encryptor.decrypt(encrypted_message, key_provider: key_provider)
55
+ end
56
+
57
+ private
58
+ def build_keys(count = 3)
59
+ count.times.collect do |index|
60
+ password = "some secret #{index}"
61
+ secret = ActiveRecord::Encryption.key_generator.derive_key_from(password)
62
+ ActiveRecord::Encryption::Key.new(secret)
63
+ end
64
+ end
65
+
66
+ def with_key_provider(key_provider, &block)
67
+ ActiveRecord::Encryption.with_encryption_context key_provider: key_provider, &block
68
+ end
69
+
70
+ def with_envelope_encryption(&block)
71
+ @envelope_encryption_key_provider ||= ActiveRecord::Encryption::EnvelopeEncryptionKeyProvider.new
72
+ with_key_provider @envelope_encryption_key_provider, &block
73
+ end
74
+
75
+ def create_unencrypted_book_ignoring_case(name:)
76
+ book = ActiveRecord::Encryption.without_encryption do
77
+ EncryptedBookThatIgnoresCase.create!(name: name)
78
+ end
79
+
80
+ # Skip type casting to simulate an upcase value. Not supported in AR without using private apis
81
+ EncryptedBookThatIgnoresCase.lease_connection.execute <<~SQL
82
+ UPDATE encrypted_books SET name = '#{name}' WHERE id = #{book.id};
83
+ SQL
84
+
85
+ book.reload
86
+ end
87
+
88
+ def assert_invalid_key_cant_read_attribute_with_default_key_provider(model, attribute_name)
89
+ model.reload
90
+
91
+ ActiveRecord::Encryption.with_encryption_context key_provider: ActiveRecord::Encryption::DerivedSecretKeyProvider.new("a different 256 bits key for now") do
92
+ assert_raises ActiveRecord::Encryption::Errors::Decryption do
93
+ model.public_send(attribute_name)
94
+ end
95
+ end
96
+ end
97
+
98
+ def assert_invalid_key_cant_read_attribute_with_custom_key_provider(model, attribute_name)
99
+ attribute_type = model.type_for_attribute(attribute_name)
100
+
101
+ model.reload
102
+
103
+ original_keys = attribute_type.key_provider.keys
104
+ attribute_type.key_provider.keys = [ ActiveRecord::Encryption::Key.derive_from("other custom attribute secret") ]
105
+
106
+ assert_raises ActiveRecord::Encryption::Errors::Decryption do
107
+ model.public_send(attribute_name)
108
+ end
109
+ ensure
110
+ attribute_type.key_provider.keys = original_keys
111
+ end
112
+ end
113
+ end
114
+
115
+ # We eager load encrypted attribute types as they are declared, so that they pick up the
116
+ # default encryption setup for tests. Because we load those lazily when used, this prevents
117
+ # side effects where some tests modify encryption config settings affecting others.
118
+ #
119
+ # Notice that we clear the declaration listeners when each test start, so this will only affect
120
+ # the classes loaded before tests starts, not those declared during tests.
121
+ ActiveRecord::Encryption.on_encrypted_attribute_declared do |klass, attribute_name|
122
+ klass.type_for_attribute(attribute_name)
123
+ end
124
+
125
+ class ActiveRecord::EncryptionTestCase < ActiveRecord::TestCase
126
+ include ActiveRecord::Encryption::EncryptionHelpers
127
+
128
+ ENCRYPTION_PROPERTIES_TO_RESET = {
129
+ config: %i[ primary_key deterministic_key key_derivation_salt store_key_references hash_digest_class
130
+ key_derivation_salt support_unencrypted_data encrypt_fixtures
131
+ forced_encoding_for_deterministic_encryption ],
132
+ context: %i[ key_provider ]
133
+ }
134
+
135
+ setup do
136
+ ENCRYPTION_PROPERTIES_TO_RESET.each do |key, properties|
137
+ properties.each do |property|
138
+ instance_variable_set "@_original_encryption_#{key}_#{property}", ActiveRecord::Encryption.public_send(key).public_send(property)
139
+ end
140
+ end
141
+ ActiveRecord::Encryption.config.previous_schemes.clear
142
+ ActiveRecord::Encryption.encrypted_attribute_declaration_listeners&.clear
143
+ end
144
+
145
+ teardown do
146
+ ENCRYPTION_PROPERTIES_TO_RESET.each do |key, properties|
147
+ properties.each do |property|
148
+ ActiveRecord::Encryption.public_send(key).public_send("#{property}=", instance_variable_get("@_original_encryption_#{key}_#{property}"))
149
+ end
150
+ end
151
+ end
152
+ end