ibm_db 3.0.0-x86-mingw32 → 3.0.1-x86-mingw32

Sign up to get free protection for your applications and to get access to all the features.
Files changed (465) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGES +4 -0
  3. data/MANIFEST +14 -14
  4. data/README +225 -225
  5. data/ext/Makefile.nt32 +181 -181
  6. data/ext/Makefile.nt32.191 +212 -212
  7. data/ext/OLD/extconf.rb +264 -0
  8. data/ext/{extconf_MacOS.rb → OLD/extconf_MacOS.rb} +269 -269
  9. data/ext/extconf.rb +291 -264
  10. data/ext/ibm_db.c +2 -2
  11. data/ext/ruby_ibm_db.h +241 -241
  12. data/ext/ruby_ibm_db_cli.h +500 -500
  13. data/init.rb +41 -41
  14. data/lib/IBM_DB.rb +27 -27
  15. data/lib/active_record/connection_adapters/ibm_db_adapter.rb +4 -4
  16. data/lib/active_record/connection_adapters/ibmdb_adapter.rb +1 -1
  17. data/lib/active_record/vendor/db2-i5-zOS.yaml +328 -328
  18. data/lib/mswin32/ibm_db.rb +115 -115
  19. data/test/active_record/connection_adapters/fake_adapter.rb +46 -0
  20. data/test/assets/example.log +1 -0
  21. data/test/assets/flowers.jpg +0 -0
  22. data/test/assets/test.txt +1 -0
  23. data/test/cases/adapter_test.rb +261 -207
  24. data/test/cases/aggregations_test.rb +158 -0
  25. data/test/cases/ar_schema_test.rb +161 -0
  26. data/test/cases/associations/association_scope_test.rb +21 -0
  27. data/test/cases/associations/belongs_to_associations_test.rb +1029 -711
  28. data/test/cases/associations/callbacks_test.rb +192 -0
  29. data/test/cases/associations/cascaded_eager_loading_test.rb +188 -181
  30. data/test/cases/associations/deprecated_counter_cache_on_has_many_through_test.rb +26 -0
  31. data/test/cases/associations/eager_load_includes_full_sti_class_test.rb +36 -0
  32. data/test/cases/associations/eager_load_nested_include_test.rb +128 -0
  33. data/test/cases/associations/eager_singularization_test.rb +148 -0
  34. data/test/cases/associations/eager_test.rb +1411 -0
  35. data/test/cases/associations/extension_test.rb +82 -0
  36. data/test/cases/associations/has_and_belongs_to_many_associations_test.rb +932 -851
  37. data/test/cases/associations/has_many_associations_test.rb +2162 -0
  38. data/test/cases/associations/has_many_through_associations_test.rb +1204 -0
  39. data/test/cases/associations/has_one_associations_test.rb +610 -0
  40. data/test/cases/associations/has_one_through_associations_test.rb +380 -0
  41. data/test/cases/associations/inner_join_association_test.rb +139 -0
  42. data/test/cases/associations/inverse_associations_test.rb +693 -0
  43. data/test/cases/associations/join_model_test.rb +754 -743
  44. data/test/cases/associations/nested_through_associations_test.rb +579 -0
  45. data/test/cases/associations/required_test.rb +82 -0
  46. data/test/cases/associations_test.rb +380 -0
  47. data/test/cases/attribute_decorators_test.rb +125 -0
  48. data/test/cases/attribute_methods/read_test.rb +60 -0
  49. data/test/cases/attribute_methods/serialization_test.rb +29 -0
  50. data/test/cases/attribute_methods_test.rb +952 -822
  51. data/test/cases/attribute_set_test.rb +200 -0
  52. data/test/cases/attribute_test.rb +180 -0
  53. data/test/cases/attributes_test.rb +136 -0
  54. data/test/cases/autosave_association_test.rb +1595 -0
  55. data/test/cases/base_test.rb +1638 -2133
  56. data/test/cases/batches_test.rb +212 -0
  57. data/test/cases/binary_test.rb +52 -0
  58. data/test/cases/bind_parameter_test.rb +100 -0
  59. data/test/cases/calculations_test.rb +646 -482
  60. data/test/cases/callbacks_test.rb +543 -0
  61. data/test/cases/clone_test.rb +40 -0
  62. data/test/cases/coders/yaml_column_test.rb +63 -0
  63. data/test/cases/column_alias_test.rb +17 -0
  64. data/test/cases/column_definition_test.rb +123 -0
  65. data/test/cases/connection_adapters/adapter_leasing_test.rb +54 -0
  66. data/test/cases/connection_adapters/connection_handler_test.rb +53 -0
  67. data/test/cases/connection_adapters/connection_specification_test.rb +12 -0
  68. data/test/cases/connection_adapters/merge_and_resolve_default_url_config_test.rb +293 -0
  69. data/test/cases/connection_adapters/mysql_type_lookup_test.rb +65 -0
  70. data/test/cases/connection_adapters/quoting_test.rb +13 -0
  71. data/test/cases/connection_adapters/schema_cache_test.rb +56 -0
  72. data/test/cases/connection_adapters/type_lookup_test.rb +110 -0
  73. data/test/cases/connection_management_test.rb +122 -0
  74. data/test/cases/connection_pool_test.rb +346 -0
  75. data/test/cases/connection_specification/resolver_test.rb +116 -0
  76. data/test/cases/core_test.rb +112 -0
  77. data/test/cases/counter_cache_test.rb +209 -0
  78. data/test/cases/custom_locking_test.rb +17 -0
  79. data/test/cases/database_statements_test.rb +19 -0
  80. data/test/cases/date_time_test.rb +61 -0
  81. data/test/cases/defaults_test.rb +223 -0
  82. data/test/cases/dirty_test.rb +775 -0
  83. data/test/cases/disconnected_test.rb +28 -0
  84. data/test/cases/dup_test.rb +157 -0
  85. data/test/cases/enum_test.rb +290 -0
  86. data/test/cases/explain_subscriber_test.rb +64 -0
  87. data/test/cases/explain_test.rb +76 -0
  88. data/test/cases/finder_respond_to_test.rb +60 -0
  89. data/test/cases/finder_test.rb +1166 -0
  90. data/test/cases/fixture_set/file_test.rb +138 -0
  91. data/test/cases/fixtures_test.rb +897 -0
  92. data/test/cases/forbidden_attributes_protection_test.rb +99 -0
  93. data/test/cases/habtm_destroy_order_test.rb +61 -0
  94. data/test/cases/helper.rb +210 -0
  95. data/test/cases/hot_compatibility_test.rb +54 -0
  96. data/test/cases/i18n_test.rb +45 -0
  97. data/test/cases/inheritance_test.rb +375 -0
  98. data/test/cases/integration_test.rb +139 -0
  99. data/test/cases/invalid_connection_test.rb +22 -0
  100. data/test/cases/invalid_date_test.rb +32 -0
  101. data/test/cases/invertible_migration_test.rb +295 -0
  102. data/test/cases/json_serialization_test.rb +302 -0
  103. data/test/cases/locking_test.rb +477 -0
  104. data/test/cases/log_subscriber_test.rb +136 -0
  105. data/test/cases/migration/change_schema_test - Copy.rb +448 -0
  106. data/test/cases/migration/change_schema_test.rb +472 -0
  107. data/test/cases/migration/change_table_test.rb +224 -0
  108. data/test/cases/migration/column_attributes_test.rb +192 -0
  109. data/test/cases/migration/column_positioning_test.rb +56 -0
  110. data/test/cases/migration/columns_test.rb +304 -0
  111. data/test/cases/migration/command_recorder_test.rb +305 -0
  112. data/test/cases/migration/create_join_table_test.rb +148 -0
  113. data/test/cases/migration/foreign_key_test - Changed.rb +325 -0
  114. data/test/cases/migration/foreign_key_test.rb +360 -0
  115. data/test/cases/migration/helper.rb +39 -0
  116. data/test/cases/migration/index_test.rb +216 -0
  117. data/test/cases/migration/logger_test.rb +36 -0
  118. data/test/cases/migration/pending_migrations_test.rb +53 -0
  119. data/test/cases/migration/references_foreign_key_test.rb +214 -0
  120. data/test/cases/migration/references_index_test.rb +101 -0
  121. data/test/cases/migration/references_statements_test.rb +116 -0
  122. data/test/cases/migration/rename_table_test.rb +93 -0
  123. data/test/cases/migration/table_and_index_test.rb +24 -0
  124. data/test/cases/migration_test.rb +959 -2408
  125. data/test/cases/migrator_test.rb +388 -0
  126. data/test/cases/mixin_test.rb +70 -0
  127. data/test/cases/modules_test.rb +173 -0
  128. data/test/cases/multiparameter_attributes_test.rb +350 -0
  129. data/test/cases/multiple_db_test.rb +115 -0
  130. data/test/cases/nested_attributes_test.rb +1057 -0
  131. data/test/cases/nested_attributes_with_callbacks_test.rb +144 -0
  132. data/test/cases/persistence_test.rb +909 -642
  133. data/test/cases/pooled_connections_test.rb +81 -0
  134. data/test/cases/primary_keys_test.rb +237 -0
  135. data/test/cases/query_cache_test.rb +326 -257
  136. data/test/cases/quoting_test.rb +156 -0
  137. data/test/cases/readonly_test.rb +118 -0
  138. data/test/cases/reaper_test.rb +85 -0
  139. data/test/cases/reflection_test.rb +454 -0
  140. data/test/cases/relation/delegation_test.rb +68 -0
  141. data/test/cases/relation/merging_test.rb +161 -0
  142. data/test/cases/relation/mutation_test.rb +165 -0
  143. data/test/cases/relation/predicate_builder_test.rb +14 -0
  144. data/test/cases/relation/where_chain_test.rb +181 -0
  145. data/test/cases/relation/where_test.rb +300 -0
  146. data/test/cases/relation/where_test2.rb +36 -0
  147. data/test/cases/relation_test.rb +297 -0
  148. data/test/cases/relations_test.rb +1815 -1182
  149. data/test/cases/reload_models_test.rb +22 -0
  150. data/test/cases/result_test.rb +80 -0
  151. data/test/cases/sanitize_test.rb +83 -0
  152. data/test/cases/schema_dumper_test.rb +463 -256
  153. data/test/cases/scoping/default_scoping_test.rb +454 -0
  154. data/test/cases/scoping/named_scoping_test.rb +524 -0
  155. data/test/cases/scoping/relation_scoping_test.rb +357 -0
  156. data/test/cases/serialization_test.rb +104 -0
  157. data/test/cases/serialized_attribute_test.rb +277 -0
  158. data/test/cases/statement_cache_test.rb +98 -0
  159. data/test/cases/store_test.rb +194 -0
  160. data/test/cases/tasks/database_tasks_test.rb +396 -0
  161. data/test/cases/tasks/mysql_rake_test.rb +311 -0
  162. data/test/cases/tasks/postgresql_rake_test.rb +245 -0
  163. data/test/cases/tasks/sqlite_rake_test.rb +193 -0
  164. data/test/cases/test_case.rb +123 -0
  165. data/test/cases/timestamp_test.rb +468 -0
  166. data/test/cases/transaction_callbacks_test.rb +452 -300
  167. data/test/cases/transaction_isolation_test.rb +106 -0
  168. data/test/cases/transactions_test.rb +817 -0
  169. data/test/cases/type/decimal_test.rb +51 -0
  170. data/test/cases/type/integer_test.rb +121 -0
  171. data/test/cases/type/string_test.rb +36 -0
  172. data/test/cases/type/type_map_test.rb +177 -0
  173. data/test/cases/type/unsigned_integer_test.rb +18 -0
  174. data/test/cases/types_test.rb +141 -0
  175. data/test/cases/unconnected_test.rb +33 -0
  176. data/test/cases/validations/association_validation_test.rb +86 -0
  177. data/test/cases/validations/i18n_generate_message_validation_test.rb +84 -0
  178. data/test/cases/validations/i18n_validation_test.rb +90 -0
  179. data/test/cases/validations/length_validation_test.rb +47 -0
  180. data/test/cases/validations/presence_validation_test.rb +68 -0
  181. data/test/cases/validations/uniqueness_validation_test.rb +434 -299
  182. data/test/cases/validations_repair_helper.rb +23 -0
  183. data/test/cases/validations_test.rb +165 -0
  184. data/test/cases/view_test.rb +113 -0
  185. data/test/cases/xml_serialization_test.rb +457 -408
  186. data/test/cases/yaml_serialization_test.rb +86 -0
  187. data/test/config.rb +5 -0
  188. data/test/config.yml +154 -154
  189. data/test/connections/native_ibm_db/connection.rb +43 -43
  190. data/test/fixtures/accounts.yml +29 -0
  191. data/test/fixtures/admin/accounts.yml +2 -0
  192. data/test/fixtures/admin/randomly_named_a9.yml +7 -0
  193. data/test/fixtures/admin/randomly_named_b0.yml +7 -0
  194. data/test/fixtures/admin/users.yml +10 -0
  195. data/test/fixtures/all/admin +1 -0
  196. data/test/fixtures/all/developers.yml +0 -0
  197. data/test/fixtures/all/people.yml +0 -0
  198. data/test/fixtures/all/tasks.yml +0 -0
  199. data/test/fixtures/author_addresses.yml +18 -0
  200. data/test/fixtures/author_favorites.yml +4 -0
  201. data/test/fixtures/authors.yml +23 -0
  202. data/test/fixtures/binaries.yml +133 -0
  203. data/test/fixtures/books.yml +11 -0
  204. data/test/fixtures/bulbs.yml +5 -0
  205. data/test/fixtures/cars.yml +9 -0
  206. data/test/fixtures/categories.yml +19 -0
  207. data/test/fixtures/categories/special_categories.yml +9 -0
  208. data/test/fixtures/categories/subsubdir/arbitrary_filename.yml +4 -0
  209. data/test/fixtures/categories_ordered.yml +7 -0
  210. data/test/fixtures/categories_posts.yml +31 -0
  211. data/test/fixtures/categorizations.yml +23 -0
  212. data/test/fixtures/clubs.yml +8 -0
  213. data/test/fixtures/collections.yml +3 -0
  214. data/test/fixtures/colleges.yml +3 -0
  215. data/test/fixtures/comments.yml +65 -0
  216. data/test/fixtures/companies.yml +67 -0
  217. data/test/fixtures/computers.yml +10 -0
  218. data/test/fixtures/courses.yml +8 -0
  219. data/test/fixtures/customers.yml +26 -0
  220. data/test/fixtures/dashboards.yml +6 -0
  221. data/test/fixtures/developers.yml +22 -0
  222. data/test/fixtures/developers_projects.yml +17 -0
  223. data/test/fixtures/dog_lovers.yml +7 -0
  224. data/test/fixtures/dogs.yml +4 -0
  225. data/test/fixtures/doubloons.yml +3 -0
  226. data/test/fixtures/edges.yml +5 -0
  227. data/test/fixtures/entrants.yml +14 -0
  228. data/test/fixtures/essays.yml +6 -0
  229. data/test/fixtures/faces.yml +11 -0
  230. data/test/fixtures/fk_test_has_fk.yml +3 -0
  231. data/test/fixtures/fk_test_has_pk.yml +2 -0
  232. data/test/fixtures/friendships.yml +4 -0
  233. data/test/fixtures/funny_jokes.yml +10 -0
  234. data/test/fixtures/interests.yml +33 -0
  235. data/test/fixtures/items.yml +3 -0
  236. data/test/fixtures/jobs.yml +7 -0
  237. data/test/fixtures/legacy_things.yml +3 -0
  238. data/test/fixtures/mateys.yml +4 -0
  239. data/test/fixtures/member_details.yml +8 -0
  240. data/test/fixtures/member_types.yml +6 -0
  241. data/test/fixtures/members.yml +11 -0
  242. data/test/fixtures/memberships.yml +34 -0
  243. data/test/fixtures/men.yml +5 -0
  244. data/test/fixtures/minimalistics.yml +2 -0
  245. data/test/fixtures/minivans.yml +5 -0
  246. data/test/fixtures/mixed_case_monkeys.yml +6 -0
  247. data/test/fixtures/mixins.yml +29 -0
  248. data/test/fixtures/movies.yml +7 -0
  249. data/test/fixtures/naked/csv/accounts.csv +1 -0
  250. data/test/fixtures/naked/yml/accounts.yml +1 -0
  251. data/test/fixtures/naked/yml/companies.yml +1 -0
  252. data/test/fixtures/naked/yml/courses.yml +1 -0
  253. data/test/fixtures/organizations.yml +5 -0
  254. data/test/fixtures/other_topics.yml +42 -0
  255. data/test/fixtures/owners.yml +9 -0
  256. data/test/fixtures/parrots.yml +27 -0
  257. data/test/fixtures/parrots_pirates.yml +7 -0
  258. data/test/fixtures/people.yml +24 -0
  259. data/test/fixtures/peoples_treasures.yml +3 -0
  260. data/test/fixtures/pets.yml +19 -0
  261. data/test/fixtures/pirates.yml +12 -0
  262. data/test/fixtures/posts.yml +80 -0
  263. data/test/fixtures/price_estimates.yml +7 -0
  264. data/test/fixtures/products.yml +4 -0
  265. data/test/fixtures/projects.yml +7 -0
  266. data/test/fixtures/randomly_named_a9.yml +7 -0
  267. data/test/fixtures/ratings.yml +14 -0
  268. data/test/fixtures/readers.yml +11 -0
  269. data/test/fixtures/references.yml +17 -0
  270. data/test/fixtures/reserved_words/distinct.yml +5 -0
  271. data/test/fixtures/reserved_words/distinct_select.yml +11 -0
  272. data/test/fixtures/reserved_words/group.yml +14 -0
  273. data/test/fixtures/reserved_words/select.yml +8 -0
  274. data/test/fixtures/reserved_words/values.yml +7 -0
  275. data/test/fixtures/ships.yml +6 -0
  276. data/test/fixtures/speedometers.yml +8 -0
  277. data/test/fixtures/sponsors.yml +12 -0
  278. data/test/fixtures/string_key_objects.yml +7 -0
  279. data/test/fixtures/subscribers.yml +11 -0
  280. data/test/fixtures/subscriptions.yml +12 -0
  281. data/test/fixtures/taggings.yml +78 -0
  282. data/test/fixtures/tags.yml +11 -0
  283. data/test/fixtures/tasks.yml +7 -0
  284. data/test/fixtures/teapots.yml +3 -0
  285. data/test/fixtures/to_be_linked/accounts.yml +2 -0
  286. data/test/fixtures/to_be_linked/users.yml +10 -0
  287. data/test/fixtures/topics.yml +49 -0
  288. data/test/fixtures/toys.yml +14 -0
  289. data/test/fixtures/traffic_lights.yml +10 -0
  290. data/test/fixtures/treasures.yml +10 -0
  291. data/test/fixtures/uuid_children.yml +3 -0
  292. data/test/fixtures/uuid_parents.yml +2 -0
  293. data/test/fixtures/variants.yml +4 -0
  294. data/test/fixtures/vegetables.yml +20 -0
  295. data/test/fixtures/vertices.yml +4 -0
  296. data/test/fixtures/warehouse_things.yml +3 -0
  297. data/test/fixtures/zines.yml +5 -0
  298. data/test/ibm_db_test.rb +24 -24
  299. data/test/migrations/10_urban/9_add_expressions.rb +11 -0
  300. data/test/migrations/decimal/1_give_me_big_numbers.rb +15 -0
  301. data/test/migrations/magic/1_currencies_have_symbols.rb +12 -0
  302. data/test/migrations/missing/1000_people_have_middle_names.rb +9 -0
  303. data/test/migrations/missing/1_people_have_last_names.rb +9 -0
  304. data/test/migrations/missing/3_we_need_reminders.rb +12 -0
  305. data/test/migrations/missing/4_innocent_jointable.rb +12 -0
  306. data/test/migrations/rename/1_we_need_things.rb +11 -0
  307. data/test/migrations/rename/2_rename_things.rb +9 -0
  308. data/test/migrations/to_copy/1_people_have_hobbies.rb +9 -0
  309. data/test/migrations/to_copy/2_people_have_descriptions.rb +9 -0
  310. data/test/migrations/to_copy2/1_create_articles.rb +7 -0
  311. data/test/migrations/to_copy2/2_create_comments.rb +7 -0
  312. data/test/migrations/to_copy_with_name_collision/1_people_have_hobbies.rb +9 -0
  313. data/test/migrations/to_copy_with_timestamps/20090101010101_people_have_hobbies.rb +9 -0
  314. data/test/migrations/to_copy_with_timestamps/20090101010202_people_have_descriptions.rb +9 -0
  315. data/test/migrations/to_copy_with_timestamps2/20090101010101_create_articles.rb +7 -0
  316. data/test/migrations/to_copy_with_timestamps2/20090101010202_create_comments.rb +7 -0
  317. data/test/migrations/valid/1_valid_people_have_last_names.rb +9 -0
  318. data/test/migrations/valid/2_we_need_reminders.rb +12 -0
  319. data/test/migrations/valid/3_innocent_jointable.rb +12 -0
  320. data/test/migrations/valid_with_subdirectories/1_valid_people_have_last_names.rb +9 -0
  321. data/test/migrations/valid_with_subdirectories/sub/2_we_need_reminders.rb +12 -0
  322. data/test/migrations/valid_with_subdirectories/sub1/3_innocent_jointable.rb +12 -0
  323. data/test/migrations/valid_with_timestamps/20100101010101_valid_with_timestamps_people_have_last_names.rb +9 -0
  324. data/test/migrations/valid_with_timestamps/20100201010101_valid_with_timestamps_we_need_reminders.rb +12 -0
  325. data/test/migrations/valid_with_timestamps/20100301010101_valid_with_timestamps_innocent_jointable.rb +12 -0
  326. data/test/migrations/version_check/20131219224947_migration_version_check.rb +8 -0
  327. data/test/models/admin.rb +5 -0
  328. data/test/models/admin/account.rb +3 -0
  329. data/test/models/admin/randomly_named_c1.rb +3 -0
  330. data/test/models/admin/user.rb +40 -0
  331. data/test/models/aircraft.rb +4 -0
  332. data/test/models/arunit2_model.rb +3 -0
  333. data/test/models/author.rb +212 -0
  334. data/test/models/auto_id.rb +4 -0
  335. data/test/models/autoloadable/extra_firm.rb +2 -0
  336. data/test/models/binary.rb +2 -0
  337. data/test/models/bird.rb +12 -0
  338. data/test/models/book.rb +18 -0
  339. data/test/models/boolean.rb +2 -0
  340. data/test/models/bulb.rb +51 -0
  341. data/test/models/cake_designer.rb +3 -0
  342. data/test/models/car.rb +26 -0
  343. data/test/models/carrier.rb +2 -0
  344. data/test/models/categorization.rb +19 -0
  345. data/test/models/category.rb +35 -0
  346. data/test/models/chef.rb +3 -0
  347. data/test/models/citation.rb +3 -0
  348. data/test/models/club.rb +23 -0
  349. data/test/models/college.rb +10 -0
  350. data/test/models/column.rb +3 -0
  351. data/test/models/column_name.rb +3 -0
  352. data/test/models/comment.rb +64 -0
  353. data/test/models/company.rb +225 -0
  354. data/test/models/company_in_module.rb +98 -0
  355. data/test/models/computer.rb +3 -0
  356. data/test/models/contact.rb +41 -0
  357. data/test/models/contract.rb +20 -0
  358. data/test/models/country.rb +7 -0
  359. data/test/models/course.rb +6 -0
  360. data/test/models/customer.rb +77 -0
  361. data/test/models/customer_carrier.rb +14 -0
  362. data/test/models/dashboard.rb +3 -0
  363. data/test/models/default.rb +2 -0
  364. data/test/models/department.rb +4 -0
  365. data/test/models/developer.rb +252 -0
  366. data/test/models/dog.rb +5 -0
  367. data/test/models/dog_lover.rb +5 -0
  368. data/test/models/doubloon.rb +12 -0
  369. data/test/models/drink_designer.rb +3 -0
  370. data/test/models/edge.rb +5 -0
  371. data/test/models/electron.rb +5 -0
  372. data/test/models/engine.rb +4 -0
  373. data/test/models/entrant.rb +3 -0
  374. data/test/models/essay.rb +5 -0
  375. data/test/models/event.rb +3 -0
  376. data/test/models/eye.rb +37 -0
  377. data/test/models/face.rb +9 -0
  378. data/test/models/friendship.rb +6 -0
  379. data/test/models/guid.rb +2 -0
  380. data/test/models/hotel.rb +6 -0
  381. data/test/models/image.rb +3 -0
  382. data/test/models/interest.rb +5 -0
  383. data/test/models/invoice.rb +4 -0
  384. data/test/models/item.rb +7 -0
  385. data/test/models/job.rb +7 -0
  386. data/test/models/joke.rb +7 -0
  387. data/test/models/keyboard.rb +3 -0
  388. data/test/models/legacy_thing.rb +3 -0
  389. data/test/models/lesson.rb +11 -0
  390. data/test/models/line_item.rb +3 -0
  391. data/test/models/liquid.rb +4 -0
  392. data/test/models/man.rb +11 -0
  393. data/test/models/matey.rb +4 -0
  394. data/test/models/member.rb +41 -0
  395. data/test/models/member_detail.rb +7 -0
  396. data/test/models/member_type.rb +3 -0
  397. data/test/models/membership.rb +35 -0
  398. data/test/models/minimalistic.rb +2 -0
  399. data/test/models/minivan.rb +9 -0
  400. data/test/models/mixed_case_monkey.rb +3 -0
  401. data/test/models/molecule.rb +6 -0
  402. data/test/models/movie.rb +5 -0
  403. data/test/models/order.rb +4 -0
  404. data/test/models/organization.rb +14 -0
  405. data/test/models/owner.rb +34 -0
  406. data/test/models/parrot.rb +29 -0
  407. data/test/models/person.rb +143 -0
  408. data/test/models/personal_legacy_thing.rb +4 -0
  409. data/test/models/pet.rb +15 -0
  410. data/test/models/pirate.rb +92 -0
  411. data/test/models/possession.rb +3 -0
  412. data/test/models/post.rb +264 -0
  413. data/test/models/price_estimate.rb +4 -0
  414. data/test/models/professor.rb +5 -0
  415. data/test/models/project.rb +29 -0
  416. data/test/models/publisher.rb +2 -0
  417. data/test/models/publisher/article.rb +4 -0
  418. data/test/models/publisher/magazine.rb +3 -0
  419. data/test/models/randomly_named_c1.rb +3 -0
  420. data/test/models/rating.rb +4 -0
  421. data/test/models/reader.rb +23 -0
  422. data/test/models/record.rb +2 -0
  423. data/test/models/reference.rb +22 -0
  424. data/test/models/reply.rb +61 -0
  425. data/test/models/ship.rb +33 -0
  426. data/test/models/ship_part.rb +8 -0
  427. data/test/models/shop.rb +17 -0
  428. data/test/models/shop_account.rb +6 -0
  429. data/test/models/speedometer.rb +6 -0
  430. data/test/models/sponsor.rb +7 -0
  431. data/test/models/string_key_object.rb +3 -0
  432. data/test/models/student.rb +4 -0
  433. data/test/models/subject.rb +16 -0
  434. data/test/models/subscriber.rb +8 -0
  435. data/test/models/subscription.rb +4 -0
  436. data/test/models/tag.rb +7 -0
  437. data/test/models/tagging.rb +13 -0
  438. data/test/models/task.rb +5 -0
  439. data/test/models/topic.rb +124 -0
  440. data/test/models/toy.rb +6 -0
  441. data/test/models/traffic_light.rb +4 -0
  442. data/test/models/treasure.rb +14 -0
  443. data/test/models/treaty.rb +7 -0
  444. data/test/models/tyre.rb +11 -0
  445. data/test/models/uuid_child.rb +3 -0
  446. data/test/models/uuid_parent.rb +3 -0
  447. data/test/models/vegetables.rb +24 -0
  448. data/test/models/vehicle.rb +7 -0
  449. data/test/models/vertex.rb +9 -0
  450. data/test/models/warehouse_thing.rb +5 -5
  451. data/test/models/wheel.rb +3 -0
  452. data/test/models/without_table.rb +3 -0
  453. data/test/models/zine.rb +3 -0
  454. data/test/schema/mysql2_specific_schema.rb +58 -0
  455. data/test/schema/mysql_specific_schema.rb +70 -0
  456. data/test/schema/oracle_specific_schema.rb +43 -0
  457. data/test/schema/postgresql_specific_schema.rb +202 -0
  458. data/test/schema/schema.rb +938 -751
  459. data/test/schema/sqlite_specific_schema.rb +22 -0
  460. data/test/support/config.rb +43 -0
  461. data/test/support/connection.rb +22 -0
  462. data/test/support/connection_helper.rb +14 -0
  463. data/test/support/ddl_helper.rb +8 -0
  464. data/test/support/schema_dumping_helper.rb +20 -0
  465. metadata +444 -18
@@ -1,2133 +1,1638 @@
1
- require "cases/helper"
2
- require 'models/post'
3
- require 'models/author'
4
- require 'models/topic'
5
- require 'models/reply'
6
- require 'models/category'
7
- require 'models/company'
8
- require 'models/customer'
9
- require 'models/developer'
10
- require 'models/project'
11
- require 'models/default'
12
- require 'models/auto_id'
13
- require 'models/boolean'
14
- require 'models/column_name'
15
- require 'models/subscriber'
16
- require 'models/keyboard'
17
- require 'models/comment'
18
- require 'models/minimalistic'
19
- require 'models/warehouse_thing'
20
- require 'models/parrot'
21
- require 'models/person'
22
- require 'models/edge'
23
- require 'models/joke'
24
- require 'models/bulb'
25
- require 'models/bird'
26
- require 'rexml/document'
27
- require 'active_support/core_ext/exception'
28
- require 'bcrypt'
29
-
30
- class FirstAbstractClass < ActiveRecord::Base
31
- self.abstract_class = true
32
- end
33
- class SecondAbstractClass < FirstAbstractClass
34
- self.abstract_class = true
35
- end
36
- class Photo < SecondAbstractClass; end
37
- class Category < ActiveRecord::Base; end
38
- class Categorization < ActiveRecord::Base; end
39
- class Smarts < ActiveRecord::Base; end
40
- class CreditCard < ActiveRecord::Base
41
- class PinNumber < ActiveRecord::Base
42
- class CvvCode < ActiveRecord::Base; end
43
- class SubCvvCode < CvvCode; end
44
- end
45
- class SubPinNumber < PinNumber; end
46
- class Brand < Category; end
47
- end
48
- class MasterCreditCard < ActiveRecord::Base; end
49
- class Post < ActiveRecord::Base; end
50
- class Computer < ActiveRecord::Base; end
51
- class NonExistentTable < ActiveRecord::Base; end
52
- class TestOracleDefault < ActiveRecord::Base; end
53
-
54
- class ReadonlyTitlePost < Post
55
- attr_readonly :title
56
- end
57
-
58
- class ProtectedTitlePost < Post
59
- attr_protected :title
60
- end
61
-
62
- class Weird < ActiveRecord::Base; end
63
-
64
- class Boolean < ActiveRecord::Base; end
65
-
66
- class LintTest < ActiveRecord::TestCase
67
- include ActiveModel::Lint::Tests
68
-
69
- class LintModel < ActiveRecord::Base; end
70
-
71
- def setup
72
- @model = LintModel.new
73
- end
74
- end
75
-
76
- class BasicsTest < ActiveRecord::TestCase
77
- fixtures :topics, :companies, :developers, :projects, :computers, :accounts, :minimalistics, 'warehouse_things', :authors, :categorizations, :categories, :posts
78
-
79
- def test_generated_methods_modules
80
- modules = Computer.ancestors
81
- assert modules.include?(Computer::GeneratedFeatureMethods)
82
- assert_equal(Computer::GeneratedFeatureMethods, Computer.generated_feature_methods)
83
- assert(modules.index(Computer.generated_attribute_methods) > modules.index(Computer.generated_feature_methods),
84
- "generated_attribute_methods must be higher in inheritance hierarchy than generated_feature_methods")
85
- assert_not_equal Computer.generated_feature_methods, Post.generated_feature_methods
86
- end
87
-
88
- def test_column_names_are_escaped
89
- conn = ActiveRecord::Base.connection
90
- classname = conn.class.name[/[^:]*$/]
91
- badchar = {
92
- 'SQLite3Adapter' => '"',
93
- 'MysqlAdapter' => '`',
94
- 'Mysql2Adapter' => '`',
95
- 'PostgreSQLAdapter' => '"',
96
- 'OracleAdapter' => '"',
97
- 'IBM_DBAdapter' => '"'
98
- }.fetch(classname) {
99
- raise "need a bad char for #{classname}"
100
- }
101
-
102
- quoted = conn.quote_column_name "foo#{badchar}bar"
103
- if current_adapter?(:OracleAdapter)
104
- # Oracle does not allow double quotes in table and column names at all
105
- # therefore quoting removes them
106
- assert_equal("#{badchar}foobar#{badchar}", quoted)
107
- elsif current_adapter?(:IBM_DBAdapter)
108
- assert_equal("foo#{badchar}bar", quoted)
109
- else
110
- assert_equal("#{badchar}foo#{badchar * 2}bar#{badchar}", quoted)
111
- end
112
- end
113
-
114
- def test_columns_should_obey_set_primary_key
115
- pk = Subscriber.columns.find { |x| x.name == 'nick' }
116
- assert pk.primary, 'nick should be primary key'
117
- end
118
-
119
- def test_primary_key_with_no_id
120
- assert_nil Edge.primary_key
121
- end
122
-
123
- unless current_adapter?(:PostgreSQLAdapter,:OracleAdapter,:SQLServerAdapter, :IBM_DBAdapter)
124
- def test_limit_with_comma
125
- assert_nothing_raised do
126
- Topic.limit("1,2").all
127
- end
128
- end
129
- end
130
-
131
- def test_limit_without_comma
132
- assert_nothing_raised do
133
- assert_equal 1, Topic.limit("1").all.length
134
- end
135
-
136
- assert_nothing_raised do
137
- assert_equal 1, Topic.limit(1).all.length
138
- end
139
- end
140
-
141
- def test_invalid_limit
142
- assert_raises(ArgumentError) do
143
- Topic.limit("asdfadf").all
144
- end
145
- end
146
-
147
- def test_limit_should_sanitize_sql_injection_for_limit_without_comas
148
- assert_raises(ArgumentError) do
149
- Topic.limit("1 select * from schema").all
150
- end
151
- end
152
-
153
- def test_limit_should_sanitize_sql_injection_for_limit_with_comas
154
- assert_raises(ArgumentError) do
155
- Topic.limit("1, 7 procedure help()").all
156
- end
157
- end
158
-
159
- unless current_adapter?(:MysqlAdapter) || current_adapter?(:Mysql2Adapter) || current_adapter?(:IBM_DBAdapter)
160
- def test_limit_should_allow_sql_literal
161
- assert_equal 1, Topic.limit(Arel.sql('2-1')).all.length
162
- end
163
- end
164
-
165
- def test_select_symbol
166
- topic_ids = Topic.select(:id).map(&:id).sort
167
- assert_equal Topic.all.map(&:id).sort, topic_ids
168
- end
169
-
170
- def test_table_exists
171
- assert !NonExistentTable.table_exists?
172
- assert Topic.table_exists?
173
- end
174
-
175
- def test_preserving_date_objects
176
- if current_adapter?(:SybaseAdapter)
177
- # Sybase ctlib does not (yet?) support the date type; use datetime instead.
178
- assert_kind_of(
179
- Time, Topic.find(1).last_read,
180
- "The last_read attribute should be of the Time class"
181
- )
182
- else
183
- # Oracle enhanced adapter allows to define Date attributes in model class (see topic.rb)
184
- assert_kind_of(
185
- Date, Topic.find(1).last_read,
186
- "The last_read attribute should be of the Date class"
187
- )
188
- end
189
- end
190
-
191
- def test_preserving_time_objects
192
- assert_kind_of(
193
- Time, Topic.find(1).bonus_time,
194
- "The bonus_time attribute should be of the Time class"
195
- )
196
-
197
- assert_kind_of(
198
- Time, Topic.find(1).written_on,
199
- "The written_on attribute should be of the Time class"
200
- )
201
-
202
- # For adapters which support microsecond resolution.
203
- if current_adapter?(:PostgreSQLAdapter) || current_adapter?(:SQLiteAdapter)
204
- assert_equal 11, Topic.find(1).written_on.sec
205
- assert_equal 223300, Topic.find(1).written_on.usec
206
- assert_equal 9900, Topic.find(2).written_on.usec
207
- end
208
- end
209
-
210
- def test_preserving_time_objects_with_local_time_conversion_to_default_timezone_utc
211
- with_env_tz 'America/New_York' do
212
- with_active_record_default_timezone :utc do
213
- time = Time.local(2000)
214
- topic = Topic.create('written_on' => time)
215
- saved_time = Topic.find(topic.id).reload.written_on
216
- assert_equal time, saved_time
217
- assert_equal [0, 0, 0, 1, 1, 2000, 6, 1, false, "EST"], time.to_a
218
- assert_equal [0, 0, 5, 1, 1, 2000, 6, 1, false, "UTC"], saved_time.to_a
219
- end
220
- end
221
- end
222
-
223
- def test_preserving_time_objects_with_time_with_zone_conversion_to_default_timezone_utc
224
- with_env_tz 'America/New_York' do
225
- with_active_record_default_timezone :utc do
226
- Time.use_zone 'Central Time (US & Canada)' do
227
- time = Time.zone.local(2000)
228
- topic = Topic.create('written_on' => time)
229
- saved_time = Topic.find(topic.id).reload.written_on
230
- assert_equal time, saved_time
231
- assert_equal [0, 0, 0, 1, 1, 2000, 6, 1, false, "CST"], time.to_a
232
- assert_equal [0, 0, 6, 1, 1, 2000, 6, 1, false, "UTC"], saved_time.to_a
233
- end
234
- end
235
- end
236
- end
237
-
238
- def test_preserving_time_objects_with_utc_time_conversion_to_default_timezone_local
239
- with_env_tz 'America/New_York' do
240
- time = Time.utc(2000)
241
- topic = Topic.create('written_on' => time)
242
- saved_time = Topic.find(topic.id).reload.written_on
243
- assert_equal time, saved_time
244
- assert_equal [0, 0, 0, 1, 1, 2000, 6, 1, false, "UTC"], time.to_a
245
- assert_equal [0, 0, 19, 31, 12, 1999, 5, 365, false, "EST"], saved_time.to_a
246
- end
247
- end
248
-
249
- def test_preserving_time_objects_with_time_with_zone_conversion_to_default_timezone_local
250
- with_env_tz 'America/New_York' do
251
- with_active_record_default_timezone :local do
252
- Time.use_zone 'Central Time (US & Canada)' do
253
- time = Time.zone.local(2000)
254
- topic = Topic.create('written_on' => time)
255
- saved_time = Topic.find(topic.id).reload.written_on
256
- assert_equal time, saved_time
257
- assert_equal [0, 0, 0, 1, 1, 2000, 6, 1, false, "CST"], time.to_a
258
- assert_equal [0, 0, 1, 1, 1, 2000, 6, 1, false, "EST"], saved_time.to_a
259
- end
260
- end
261
- end
262
- end
263
-
264
- def test_custom_mutator
265
- topic = Topic.find(1)
266
- # This mutator is protected in the class definition
267
- topic.send(:approved=, true)
268
- assert topic.instance_variable_get("@custom_approved")
269
- end
270
-
271
- def test_initialize_with_attributes
272
- topic = Topic.new({
273
- "title" => "initialized from attributes", "written_on" => "2003-12-12 23:23"
274
- })
275
-
276
- assert_equal("initialized from attributes", topic.title)
277
- end
278
-
279
- def test_initialize_with_invalid_attribute
280
- begin
281
- Topic.new({ "title" => "test",
282
- "last_read(1i)" => "2005", "last_read(2i)" => "2", "last_read(3i)" => "31"})
283
- rescue ActiveRecord::MultiparameterAssignmentErrors => ex
284
- assert_equal(1, ex.errors.size)
285
- assert_equal("last_read", ex.errors[0].attribute)
286
- end
287
- end
288
-
289
- def test_create_after_initialize_without_block
290
- cb = CustomBulb.create(:name => 'Dude')
291
- assert_equal('Dude', cb.name)
292
- assert_equal(true, cb.frickinawesome)
293
- end
294
-
295
- def test_create_after_initialize_with_block
296
- cb = CustomBulb.create {|c| c.name = 'Dude' }
297
- assert_equal('Dude', cb.name)
298
- assert_equal(true, cb.frickinawesome)
299
- end
300
-
301
- def test_first_or_create
302
- parrot = Bird.first_or_create(:color => 'green', :name => 'parrot')
303
- assert parrot.persisted?
304
- the_same_parrot = Bird.first_or_create(:color => 'yellow', :name => 'macaw')
305
- assert_equal parrot, the_same_parrot
306
- end
307
-
308
- def test_first_or_create_bang
309
- assert_raises(ActiveRecord::RecordInvalid) { Bird.first_or_create! }
310
- parrot = Bird.first_or_create!(:color => 'green', :name => 'parrot')
311
- assert parrot.persisted?
312
- the_same_parrot = Bird.first_or_create!(:color => 'yellow', :name => 'macaw')
313
- assert_equal parrot, the_same_parrot
314
- end
315
-
316
- def test_first_or_initialize
317
- parrot = Bird.first_or_initialize(:color => 'green', :name => 'parrot')
318
- assert_kind_of Bird, parrot
319
- assert !parrot.persisted?
320
- assert parrot.new_record?
321
- assert parrot.valid?
322
- end
323
-
324
- def test_load
325
- topics = Topic.find(:all, :order => 'id')
326
- assert_equal(4, topics.size)
327
- assert_equal(topics(:first).title, topics.first.title)
328
- end
329
-
330
- def test_load_with_condition
331
- topics = Topic.find(:all, :conditions => "author_name = 'Mary'")
332
-
333
- assert_equal(1, topics.size)
334
- assert_equal(topics(:second).title, topics.first.title)
335
- end
336
-
337
- GUESSED_CLASSES = [Category, Smarts, CreditCard, CreditCard::PinNumber, CreditCard::PinNumber::CvvCode, CreditCard::SubPinNumber, CreditCard::Brand, MasterCreditCard]
338
-
339
- def test_table_name_guesses
340
- assert_equal "topics", Topic.table_name
341
-
342
- assert_equal "categories", Category.table_name
343
- assert_equal "smarts", Smarts.table_name
344
- assert_equal "credit_cards", CreditCard.table_name
345
- assert_equal "credit_card_pin_numbers", CreditCard::PinNumber.table_name
346
- assert_equal "credit_card_pin_number_cvv_codes", CreditCard::PinNumber::CvvCode.table_name
347
- assert_equal "credit_card_pin_numbers", CreditCard::SubPinNumber.table_name
348
- assert_equal "categories", CreditCard::Brand.table_name
349
- assert_equal "master_credit_cards", MasterCreditCard.table_name
350
- ensure
351
- GUESSED_CLASSES.each(&:reset_table_name)
352
- end
353
-
354
- def test_singular_table_name_guesses
355
- ActiveRecord::Base.pluralize_table_names = false
356
- GUESSED_CLASSES.each(&:reset_table_name)
357
-
358
- assert_equal "category", Category.table_name
359
- assert_equal "smarts", Smarts.table_name
360
- assert_equal "credit_card", CreditCard.table_name
361
- assert_equal "credit_card_pin_number", CreditCard::PinNumber.table_name
362
- assert_equal "credit_card_pin_number_cvv_code", CreditCard::PinNumber::CvvCode.table_name
363
- assert_equal "credit_card_pin_number", CreditCard::SubPinNumber.table_name
364
- assert_equal "category", CreditCard::Brand.table_name
365
- assert_equal "master_credit_card", MasterCreditCard.table_name
366
- ensure
367
- ActiveRecord::Base.pluralize_table_names = true
368
- GUESSED_CLASSES.each(&:reset_table_name)
369
- end
370
-
371
- def test_table_name_guesses_with_prefixes_and_suffixes
372
- ActiveRecord::Base.table_name_prefix = "test_"
373
- Category.reset_table_name
374
- assert_equal "test_categories", Category.table_name
375
- ActiveRecord::Base.table_name_suffix = "_test"
376
- Category.reset_table_name
377
- assert_equal "test_categories_test", Category.table_name
378
- ActiveRecord::Base.table_name_prefix = ""
379
- Category.reset_table_name
380
- assert_equal "categories_test", Category.table_name
381
- ActiveRecord::Base.table_name_suffix = ""
382
- Category.reset_table_name
383
- assert_equal "categories", Category.table_name
384
- ensure
385
- ActiveRecord::Base.table_name_prefix = ""
386
- ActiveRecord::Base.table_name_suffix = ""
387
- GUESSED_CLASSES.each(&:reset_table_name)
388
- end
389
-
390
- def test_singular_table_name_guesses_with_prefixes_and_suffixes
391
- ActiveRecord::Base.pluralize_table_names = false
392
-
393
- ActiveRecord::Base.table_name_prefix = "test_"
394
- Category.reset_table_name
395
- assert_equal "test_category", Category.table_name
396
- ActiveRecord::Base.table_name_suffix = "_test"
397
- Category.reset_table_name
398
- assert_equal "test_category_test", Category.table_name
399
- ActiveRecord::Base.table_name_prefix = ""
400
- Category.reset_table_name
401
- assert_equal "category_test", Category.table_name
402
- ActiveRecord::Base.table_name_suffix = ""
403
- Category.reset_table_name
404
- assert_equal "category", Category.table_name
405
- ensure
406
- ActiveRecord::Base.pluralize_table_names = true
407
- ActiveRecord::Base.table_name_prefix = ""
408
- ActiveRecord::Base.table_name_suffix = ""
409
- GUESSED_CLASSES.each(&:reset_table_name)
410
- end
411
-
412
- def test_table_name_guesses_with_inherited_prefixes_and_suffixes
413
- GUESSED_CLASSES.each(&:reset_table_name)
414
-
415
- CreditCard.table_name_prefix = "test_"
416
- CreditCard.reset_table_name
417
- Category.reset_table_name
418
- assert_equal "test_credit_cards", CreditCard.table_name
419
- assert_equal "categories", Category.table_name
420
- CreditCard.table_name_suffix = "_test"
421
- CreditCard.reset_table_name
422
- Category.reset_table_name
423
- assert_equal "test_credit_cards_test", CreditCard.table_name
424
- assert_equal "categories", Category.table_name
425
- CreditCard.table_name_prefix = ""
426
- CreditCard.reset_table_name
427
- Category.reset_table_name
428
- assert_equal "credit_cards_test", CreditCard.table_name
429
- assert_equal "categories", Category.table_name
430
- CreditCard.table_name_suffix = ""
431
- CreditCard.reset_table_name
432
- Category.reset_table_name
433
- assert_equal "credit_cards", CreditCard.table_name
434
- assert_equal "categories", Category.table_name
435
- ensure
436
- CreditCard.table_name_prefix = ""
437
- CreditCard.table_name_suffix = ""
438
- GUESSED_CLASSES.each(&:reset_table_name)
439
- end
440
-
441
- def test_singular_table_name_guesses_for_individual_table
442
- CreditCard.pluralize_table_names = false
443
- CreditCard.reset_table_name
444
- assert_equal "credit_card", CreditCard.table_name
445
- assert_equal "categories", Category.table_name
446
- ensure
447
- CreditCard.pluralize_table_names = true
448
- CreditCard.reset_table_name
449
- end
450
-
451
- if current_adapter?(:MysqlAdapter) or current_adapter?(:Mysql2Adapter)
452
- def test_update_all_with_order_and_limit
453
- assert_equal 1, Topic.update_all("content = 'bulk updated!'", nil, :limit => 1, :order => 'id DESC')
454
- end
455
- end
456
-
457
- def test_null_fields
458
- assert_nil Topic.find(1).parent_id
459
- assert_nil Topic.create("title" => "Hey you").parent_id
460
- end
461
-
462
- def test_default_values
463
- topic = Topic.new
464
- assert topic.approved?
465
- assert_nil topic.written_on
466
- assert_nil topic.bonus_time
467
- assert_nil topic.last_read
468
-
469
- topic.save
470
-
471
- topic = Topic.find(topic.id)
472
- assert topic.approved?
473
- assert_nil topic.last_read
474
-
475
- # Oracle has some funky default handling, so it requires a bit of
476
- # extra testing. See ticket #2788.
477
- if current_adapter?(:OracleAdapter)
478
- test = TestOracleDefault.new
479
- assert_equal "X", test.test_char
480
- assert_equal "hello", test.test_string
481
- assert_equal 3, test.test_int
482
- end
483
- end
484
-
485
- # Oracle, and Sybase do not have a TIME datatype.
486
- unless current_adapter?(:OracleAdapter, :SybaseAdapter)
487
- def test_utc_as_time_zone
488
- Topic.default_timezone = :utc
489
- attributes = { "bonus_time" => "5:42:00AM" }
490
- topic = Topic.find(1)
491
- topic.attributes = attributes
492
- assert_equal Time.utc(2000, 1, 1, 5, 42, 0), topic.bonus_time
493
- Topic.default_timezone = :local
494
- end
495
-
496
- def test_utc_as_time_zone_and_new
497
- Topic.default_timezone = :utc
498
- attributes = { "bonus_time(1i)"=>"2000",
499
- "bonus_time(2i)"=>"1",
500
- "bonus_time(3i)"=>"1",
501
- "bonus_time(4i)"=>"10",
502
- "bonus_time(5i)"=>"35",
503
- "bonus_time(6i)"=>"50" }
504
- topic = Topic.new(attributes)
505
- assert_equal Time.utc(2000, 1, 1, 10, 35, 50), topic.bonus_time
506
- Topic.default_timezone = :local
507
- end
508
- end
509
-
510
- def test_default_values_on_empty_strings
511
- topic = Topic.new
512
- topic.approved = nil
513
- topic.last_read = nil
514
-
515
- topic.save
516
-
517
- topic = Topic.find(topic.id)
518
- assert_nil topic.last_read
519
-
520
- # Sybase adapter does not allow nulls in boolean columns
521
- if current_adapter?(:SybaseAdapter)
522
- assert topic.approved == false
523
- else
524
- assert_nil topic.approved
525
- end
526
- end
527
-
528
- def test_equality
529
- assert_equal Topic.find(1), Topic.find(2).topic
530
- end
531
-
532
- def test_find_by_slug
533
- assert_equal Topic.find('1-meowmeow'), Topic.find(1)
534
- end
535
-
536
- def test_equality_of_new_records
537
- assert_not_equal Topic.new, Topic.new
538
- end
539
-
540
- def test_equality_of_destroyed_records
541
- topic_1 = Topic.new(:title => 'test_1')
542
- topic_1.save
543
- topic_2 = Topic.find(topic_1.id)
544
- topic_1.destroy
545
- assert_equal topic_1, topic_2
546
- assert_equal topic_2, topic_1
547
- end
548
-
549
- def test_hashing
550
- assert_equal [ Topic.find(1) ], [ Topic.find(2).topic ] & [ Topic.find(1) ]
551
- end
552
-
553
- def test_comparison
554
- topic_1 = Topic.create!
555
- topic_2 = Topic.create!
556
-
557
- assert_equal [topic_2, topic_1].sort, [topic_1, topic_2]
558
- end
559
-
560
- def test_comparison_with_different_objects
561
- topic = Topic.create
562
- category = Category.create(:name => "comparison")
563
- assert_nil topic <=> category
564
- end
565
-
566
- def test_readonly_attributes
567
- assert_equal Set.new([ 'title' , 'comments_count' ]), ReadonlyTitlePost.readonly_attributes
568
-
569
- post = ReadonlyTitlePost.create(:title => "cannot change this", :body => "changeable")
570
- post.reload
571
- assert_equal "cannot change this", post.title
572
-
573
- post.update_attributes(:title => "try to change", :body => "changed")
574
- post.reload
575
- assert_equal "cannot change this", post.title
576
- assert_equal "changed", post.body
577
- end
578
-
579
- def test_non_valid_identifier_column_name
580
- weird = Weird.create('a$b' => 'value')
581
- weird.reload
582
- assert_equal 'value', weird.send('a$b')
583
- assert_equal 'value', weird.read_attribute('a$b')
584
-
585
- weird.update_column('a$b', 'value2')
586
- weird.reload
587
- assert_equal 'value2', weird.send('a$b')
588
- assert_equal 'value2', weird.read_attribute('a$b')
589
- end
590
-
591
- def test_multiparameter_attributes_on_date
592
- attributes = { "last_read(1i)" => "2004", "last_read(2i)" => "6", "last_read(3i)" => "24" }
593
- topic = Topic.find(1)
594
- topic.attributes = attributes
595
- # note that extra #to_date call allows test to pass for Oracle, which
596
- # treats dates/times the same
597
- assert_date_from_db Date.new(2004, 6, 24), topic.last_read.to_date
598
- end
599
-
600
- def test_multiparameter_attributes_on_date_with_empty_year
601
- attributes = { "last_read(1i)" => "", "last_read(2i)" => "6", "last_read(3i)" => "24" }
602
- topic = Topic.find(1)
603
- topic.attributes = attributes
604
- # note that extra #to_date call allows test to pass for Oracle, which
605
- # treats dates/times the same
606
- assert_nil topic.last_read
607
- end
608
-
609
- def test_multiparameter_attributes_on_date_with_empty_month
610
- attributes = { "last_read(1i)" => "2004", "last_read(2i)" => "", "last_read(3i)" => "24" }
611
- topic = Topic.find(1)
612
- topic.attributes = attributes
613
- # note that extra #to_date call allows test to pass for Oracle, which
614
- # treats dates/times the same
615
- assert_nil topic.last_read
616
- end
617
-
618
- def test_multiparameter_attributes_on_date_with_empty_day
619
- attributes = { "last_read(1i)" => "2004", "last_read(2i)" => "6", "last_read(3i)" => "" }
620
- topic = Topic.find(1)
621
- topic.attributes = attributes
622
- # note that extra #to_date call allows test to pass for Oracle, which
623
- # treats dates/times the same
624
- assert_nil topic.last_read
625
- end
626
-
627
- def test_multiparameter_attributes_on_date_with_empty_day_and_year
628
- attributes = { "last_read(1i)" => "", "last_read(2i)" => "6", "last_read(3i)" => "" }
629
- topic = Topic.find(1)
630
- topic.attributes = attributes
631
- # note that extra #to_date call allows test to pass for Oracle, which
632
- # treats dates/times the same
633
- assert_nil topic.last_read
634
- end
635
-
636
- def test_multiparameter_attributes_on_date_with_empty_day_and_month
637
- attributes = { "last_read(1i)" => "2004", "last_read(2i)" => "", "last_read(3i)" => "" }
638
- topic = Topic.find(1)
639
- topic.attributes = attributes
640
- # note that extra #to_date call allows test to pass for Oracle, which
641
- # treats dates/times the same
642
- assert_nil topic.last_read
643
- end
644
-
645
- def test_multiparameter_attributes_on_date_with_empty_year_and_month
646
- attributes = { "last_read(1i)" => "", "last_read(2i)" => "", "last_read(3i)" => "24" }
647
- topic = Topic.find(1)
648
- topic.attributes = attributes
649
- # note that extra #to_date call allows test to pass for Oracle, which
650
- # treats dates/times the same
651
- assert_nil topic.last_read
652
- end
653
-
654
- def test_multiparameter_attributes_on_date_with_all_empty
655
- attributes = { "last_read(1i)" => "", "last_read(2i)" => "", "last_read(3i)" => "" }
656
- topic = Topic.find(1)
657
- topic.attributes = attributes
658
- assert_nil topic.last_read
659
- end
660
-
661
- def test_multiparameter_attributes_on_time
662
- attributes = {
663
- "written_on(1i)" => "2004", "written_on(2i)" => "6", "written_on(3i)" => "24",
664
- "written_on(4i)" => "16", "written_on(5i)" => "24", "written_on(6i)" => "00"
665
- }
666
- topic = Topic.find(1)
667
- topic.attributes = attributes
668
- assert_equal Time.local(2004, 6, 24, 16, 24, 0), topic.written_on
669
- end
670
-
671
- def test_multiparameter_attributes_on_time_with_no_date
672
- ex = assert_raise(ActiveRecord::MultiparameterAssignmentErrors) do
673
- attributes = {
674
- "written_on(4i)" => "16", "written_on(5i)" => "24", "written_on(6i)" => "00"
675
- }
676
- topic = Topic.find(1)
677
- topic.attributes = attributes
678
- end
679
- assert_equal("written_on", ex.errors[0].attribute)
680
- end
681
-
682
- def test_multiparameter_attributes_on_time_with_invalid_time_params
683
- ex = assert_raise(ActiveRecord::MultiparameterAssignmentErrors) do
684
- attributes = {
685
- "written_on(1i)" => "2004", "written_on(2i)" => "6", "written_on(3i)" => "24",
686
- "written_on(4i)" => "2004", "written_on(5i)" => "36", "written_on(6i)" => "64",
687
- }
688
- topic = Topic.find(1)
689
- topic.attributes = attributes
690
- end
691
- assert_equal("written_on", ex.errors[0].attribute)
692
- end
693
-
694
- def test_multiparameter_attributes_on_time_with_old_date
695
- attributes = {
696
- "written_on(1i)" => "1850", "written_on(2i)" => "6", "written_on(3i)" => "24",
697
- "written_on(4i)" => "16", "written_on(5i)" => "24", "written_on(6i)" => "00"
698
- }
699
- topic = Topic.find(1)
700
- topic.attributes = attributes
701
- # testing against to_s(:db) representation because either a Time or a DateTime might be returned, depending on platform
702
- assert_equal "1850-06-24 16:24:00", topic.written_on.to_s(:db)
703
- end
704
-
705
- def test_multiparameter_attributes_on_time_will_raise_on_big_time_if_missing_date_parts
706
- ex = assert_raise(ActiveRecord::MultiparameterAssignmentErrors) do
707
- attributes = {
708
- "written_on(4i)" => "16", "written_on(5i)" => "24"
709
- }
710
- topic = Topic.find(1)
711
- topic.attributes = attributes
712
- end
713
- assert_equal("written_on", ex.errors[0].attribute)
714
- end
715
-
716
- def test_multiparameter_attributes_on_time_with_raise_on_small_time_if_missing_date_parts
717
- ex = assert_raise(ActiveRecord::MultiparameterAssignmentErrors) do
718
- attributes = {
719
- "written_on(4i)" => "16", "written_on(5i)" => "12", "written_on(6i)" => "02"
720
- }
721
- topic = Topic.find(1)
722
- topic.attributes = attributes
723
- end
724
- assert_equal("written_on", ex.errors[0].attribute)
725
- end
726
-
727
- def test_multiparameter_attributes_on_time_will_ignore_hour_if_missing
728
- attributes = {
729
- "written_on(1i)" => "2004", "written_on(2i)" => "12", "written_on(3i)" => "12",
730
- "written_on(5i)" => "12", "written_on(6i)" => "02"
731
- }
732
- topic = Topic.find(1)
733
- topic.attributes = attributes
734
- assert_equal Time.local(2004, 12, 12, 0, 12, 2), topic.written_on
735
- end
736
-
737
- def test_multiparameter_attributes_on_time_will_ignore_hour_if_blank
738
- attributes = {
739
- "written_on(1i)" => "", "written_on(2i)" => "", "written_on(3i)" => "",
740
- "written_on(4i)" => "", "written_on(5i)" => "12", "written_on(6i)" => "02"
741
- }
742
- topic = Topic.find(1)
743
- topic.attributes = attributes
744
- assert_nil topic.written_on
745
- end
746
-
747
- def test_multiparameter_attributes_on_time_will_ignore_date_if_empty
748
- attributes = {
749
- "written_on(1i)" => "", "written_on(2i)" => "", "written_on(3i)" => "",
750
- "written_on(4i)" => "16", "written_on(5i)" => "24"
751
- }
752
- topic = Topic.find(1)
753
- topic.attributes = attributes
754
- assert_nil topic.written_on
755
- end
756
- def test_multiparameter_attributes_on_time_with_seconds_will_ignore_date_if_empty
757
- attributes = {
758
- "written_on(1i)" => "", "written_on(2i)" => "", "written_on(3i)" => "",
759
- "written_on(4i)" => "16", "written_on(5i)" => "12", "written_on(6i)" => "02"
760
- }
761
- topic = Topic.find(1)
762
- topic.attributes = attributes
763
- assert_nil topic.written_on
764
- end
765
-
766
- def test_multiparameter_attributes_on_time_with_utc
767
- ActiveRecord::Base.default_timezone = :utc
768
- attributes = {
769
- "written_on(1i)" => "2004", "written_on(2i)" => "6", "written_on(3i)" => "24",
770
- "written_on(4i)" => "16", "written_on(5i)" => "24", "written_on(6i)" => "00"
771
- }
772
- topic = Topic.find(1)
773
- topic.attributes = attributes
774
- assert_equal Time.utc(2004, 6, 24, 16, 24, 0), topic.written_on
775
- ensure
776
- ActiveRecord::Base.default_timezone = :local
777
- end
778
-
779
- def test_multiparameter_attributes_on_time_with_time_zone_aware_attributes
780
- ActiveRecord::Base.time_zone_aware_attributes = true
781
- ActiveRecord::Base.default_timezone = :utc
782
- Time.zone = ActiveSupport::TimeZone[-28800]
783
- attributes = {
784
- "written_on(1i)" => "2004", "written_on(2i)" => "6", "written_on(3i)" => "24",
785
- "written_on(4i)" => "16", "written_on(5i)" => "24", "written_on(6i)" => "00"
786
- }
787
- topic = Topic.find(1)
788
- topic.attributes = attributes
789
- assert_equal Time.utc(2004, 6, 24, 23, 24, 0), topic.written_on
790
- assert_equal Time.utc(2004, 6, 24, 16, 24, 0), topic.written_on.time
791
- assert_equal Time.zone, topic.written_on.time_zone
792
- ensure
793
- ActiveRecord::Base.time_zone_aware_attributes = false
794
- ActiveRecord::Base.default_timezone = :local
795
- Time.zone = nil
796
- end
797
-
798
- def test_multiparameter_attributes_on_time_with_time_zone_aware_attributes_false
799
- ActiveRecord::Base.time_zone_aware_attributes = false
800
- Time.zone = ActiveSupport::TimeZone[-28800]
801
- attributes = {
802
- "written_on(1i)" => "2004", "written_on(2i)" => "6", "written_on(3i)" => "24",
803
- "written_on(4i)" => "16", "written_on(5i)" => "24", "written_on(6i)" => "00"
804
- }
805
- topic = Topic.find(1)
806
- topic.attributes = attributes
807
- assert_equal Time.local(2004, 6, 24, 16, 24, 0), topic.written_on
808
- assert_equal false, topic.written_on.respond_to?(:time_zone)
809
- ensure
810
- Time.zone = nil
811
- end
812
-
813
- def test_multiparameter_attributes_on_time_with_skip_time_zone_conversion_for_attributes
814
- ActiveRecord::Base.time_zone_aware_attributes = true
815
- ActiveRecord::Base.default_timezone = :utc
816
- Time.zone = ActiveSupport::TimeZone[-28800]
817
- Topic.skip_time_zone_conversion_for_attributes = [:written_on]
818
- attributes = {
819
- "written_on(1i)" => "2004", "written_on(2i)" => "6", "written_on(3i)" => "24",
820
- "written_on(4i)" => "16", "written_on(5i)" => "24", "written_on(6i)" => "00"
821
- }
822
- topic = Topic.find(1)
823
- topic.attributes = attributes
824
- assert_equal Time.utc(2004, 6, 24, 16, 24, 0), topic.written_on
825
- assert_equal false, topic.written_on.respond_to?(:time_zone)
826
- ensure
827
- ActiveRecord::Base.time_zone_aware_attributes = false
828
- ActiveRecord::Base.default_timezone = :local
829
- Time.zone = nil
830
- Topic.skip_time_zone_conversion_for_attributes = []
831
- end
832
-
833
- # Oracle, and Sybase do not have a TIME datatype.
834
- unless current_adapter?(:OracleAdapter, :SybaseAdapter)
835
- def test_multiparameter_attributes_on_time_only_column_with_time_zone_aware_attributes_does_not_do_time_zone_conversion
836
- ActiveRecord::Base.time_zone_aware_attributes = true
837
- ActiveRecord::Base.default_timezone = :utc
838
- Time.zone = ActiveSupport::TimeZone[-28800]
839
- attributes = {
840
- "bonus_time(1i)" => "2000", "bonus_time(2i)" => "1", "bonus_time(3i)" => "1",
841
- "bonus_time(4i)" => "16", "bonus_time(5i)" => "24"
842
- }
843
- topic = Topic.find(1)
844
- topic.attributes = attributes
845
- assert_equal Time.utc(2000, 1, 1, 16, 24, 0), topic.bonus_time
846
- assert topic.bonus_time.utc?
847
- ensure
848
- ActiveRecord::Base.time_zone_aware_attributes = false
849
- ActiveRecord::Base.default_timezone = :local
850
- Time.zone = nil
851
- end
852
- end
853
-
854
- def test_multiparameter_attributes_on_time_with_empty_seconds
855
- attributes = {
856
- "written_on(1i)" => "2004", "written_on(2i)" => "6", "written_on(3i)" => "24",
857
- "written_on(4i)" => "16", "written_on(5i)" => "24", "written_on(6i)" => ""
858
- }
859
- topic = Topic.find(1)
860
- topic.attributes = attributes
861
- assert_equal Time.local(2004, 6, 24, 16, 24, 0), topic.written_on
862
- end
863
-
864
- def test_multiparameter_assignment_of_aggregation
865
- customer = Customer.new
866
- address = Address.new("The Street", "The City", "The Country")
867
- attributes = { "address(1)" => address.street, "address(2)" => address.city, "address(3)" => address.country }
868
- customer.attributes = attributes
869
- assert_equal address, customer.address
870
- end
871
-
872
- def test_multiparameter_assignment_of_aggregation_out_of_order
873
- customer = Customer.new
874
- address = Address.new("The Street", "The City", "The Country")
875
- attributes = { "address(3)" => address.country, "address(2)" => address.city, "address(1)" => address.street }
876
- customer.attributes = attributes
877
- assert_equal address, customer.address
878
- end
879
-
880
- def test_multiparameter_assignment_of_aggregation_with_missing_values
881
- ex = assert_raise(ActiveRecord::MultiparameterAssignmentErrors) do
882
- customer = Customer.new
883
- address = Address.new("The Street", "The City", "The Country")
884
- attributes = { "address(2)" => address.city, "address(3)" => address.country }
885
- customer.attributes = attributes
886
- end
887
- assert_equal("address", ex.errors[0].attribute)
888
- end
889
-
890
- def test_multiparameter_assignment_of_aggregation_with_blank_values
891
- customer = Customer.new
892
- address = Address.new("The Street", "The City", "The Country")
893
- attributes = { "address(1)" => "", "address(2)" => address.city, "address(3)" => address.country }
894
- customer.attributes = attributes
895
- assert_equal Address.new(nil, "The City", "The Country"), customer.address
896
- end
897
-
898
- def test_multiparameter_assignment_of_aggregation_with_large_index
899
- ex = assert_raise(ActiveRecord::MultiparameterAssignmentErrors) do
900
- customer = Customer.new
901
- address = Address.new("The Street", "The City", "The Country")
902
- attributes = { "address(1)" => "The Street", "address(2)" => address.city, "address(3000)" => address.country }
903
- customer.attributes = attributes
904
- end
905
- assert_equal("address", ex.errors[0].attribute)
906
- end
907
-
908
- def test_attributes_on_dummy_time
909
- # Oracle, and Sybase do not have a TIME datatype.
910
- return true if current_adapter?(:OracleAdapter, :SybaseAdapter)
911
-
912
- attributes = {
913
- "bonus_time" => "5:42:00AM"
914
- }
915
- topic = Topic.find(1)
916
- topic.attributes = attributes
917
- assert_equal Time.local(2000, 1, 1, 5, 42, 0), topic.bonus_time
918
- end
919
-
920
- def test_boolean
921
- b_nil = Boolean.create({ "value" => nil })
922
- nil_id = b_nil.id
923
- b_false = Boolean.create({ "value" => false })
924
- false_id = b_false.id
925
- b_true = Boolean.create({ "value" => true })
926
- true_id = b_true.id
927
-
928
- b_nil = Boolean.find(nil_id)
929
- assert_nil b_nil.value
930
- b_false = Boolean.find(false_id)
931
- assert !b_false.value?
932
- b_true = Boolean.find(true_id)
933
- assert b_true.value?
934
- end
935
-
936
- def test_boolean_cast_from_string
937
- b_blank = Boolean.create({ "value" => "" })
938
- blank_id = b_blank.id
939
- b_false = Boolean.create({ "value" => "0" })
940
- false_id = b_false.id
941
- b_true = Boolean.create({ "value" => "1" })
942
- true_id = b_true.id
943
-
944
- b_blank = Boolean.find(blank_id)
945
- assert_nil b_blank.value
946
- b_false = Boolean.find(false_id)
947
- assert !b_false.value?
948
- b_true = Boolean.find(true_id)
949
- assert b_true.value?
950
- end
951
-
952
- def test_new_record_returns_boolean
953
- assert_equal false, Topic.new.persisted?
954
- assert_equal true, Topic.find(1).persisted?
955
- end
956
-
957
- def test_dup
958
- topic = Topic.find(1)
959
- duped_topic = nil
960
- assert_nothing_raised { duped_topic = topic.dup }
961
- assert_equal topic.title, duped_topic.title
962
- assert !duped_topic.persisted?
963
-
964
- # test if the attributes have been duped
965
- topic.title = "a"
966
- duped_topic.title = "b"
967
- assert_equal "a", topic.title
968
- assert_equal "b", duped_topic.title
969
-
970
- # test if the attribute values have been duped
971
- topic.title = {"a" => "b"}
972
- duped_topic = topic.dup
973
- duped_topic.title["a"] = "c"
974
- assert_equal "b", topic.title["a"]
975
-
976
- # test if attributes set as part of after_initialize are duped correctly
977
- assert_equal topic.author_email_address, duped_topic.author_email_address
978
-
979
- # test if saved clone object differs from original
980
- duped_topic.save
981
- assert duped_topic.persisted?
982
- assert_not_equal duped_topic.id, topic.id
983
-
984
- duped_topic.reload
985
- # FIXME: I think this is poor behavior, and will fix it with #5686
986
- assert_equal({'a' => 'c'}.to_yaml, duped_topic.title)
987
- end
988
-
989
- def test_dup_with_aggregate_of_same_name_as_attribute
990
- dev = DeveloperWithAggregate.find(1)
991
- assert_kind_of DeveloperSalary, dev.salary
992
-
993
- dup = nil
994
- assert_nothing_raised { dup = dev.dup }
995
- assert_kind_of DeveloperSalary, dup.salary
996
- assert_equal dev.salary.amount, dup.salary.amount
997
- assert !dup.persisted?
998
-
999
- # test if the attributes have been dupd
1000
- original_amount = dup.salary.amount
1001
- dev.salary.amount = 1
1002
- assert_equal original_amount, dup.salary.amount
1003
-
1004
- assert dup.save
1005
- assert dup.persisted?
1006
- assert_not_equal dup.id, dev.id
1007
- end
1008
-
1009
- def test_dup_does_not_copy_associations
1010
- author = authors(:david)
1011
- assert_not_equal [], author.posts
1012
- author.send(:clear_association_cache)
1013
-
1014
- author_dup = author.dup
1015
- assert_equal [], author_dup.posts
1016
- end
1017
-
1018
- def test_clone_preserves_subtype
1019
- clone = nil
1020
- assert_nothing_raised { clone = Company.find(3).clone }
1021
- assert_kind_of Client, clone
1022
- end
1023
-
1024
- def test_clone_of_new_object_with_defaults
1025
- developer = Developer.new
1026
- assert !developer.name_changed?
1027
- assert !developer.salary_changed?
1028
-
1029
- cloned_developer = developer.clone
1030
- assert !cloned_developer.name_changed?
1031
- assert !cloned_developer.salary_changed?
1032
- end
1033
-
1034
- def test_clone_of_new_object_marks_attributes_as_dirty
1035
- developer = Developer.new :name => 'Bjorn', :salary => 100000
1036
- assert developer.name_changed?
1037
- assert developer.salary_changed?
1038
-
1039
- cloned_developer = developer.clone
1040
- assert cloned_developer.name_changed?
1041
- assert cloned_developer.salary_changed?
1042
- end
1043
-
1044
- def test_clone_of_new_object_marks_as_dirty_only_changed_attributes
1045
- developer = Developer.new :name => 'Bjorn'
1046
- assert developer.name_changed? # obviously
1047
- assert !developer.salary_changed? # attribute has non-nil default value, so treated as not changed
1048
-
1049
- cloned_developer = developer.clone
1050
- assert cloned_developer.name_changed?
1051
- assert !cloned_developer.salary_changed? # ... and cloned instance should behave same
1052
- end
1053
-
1054
- def test_dup_of_saved_object_marks_attributes_as_dirty
1055
- developer = Developer.create! :name => 'Bjorn', :salary => 100000
1056
- assert !developer.name_changed?
1057
- assert !developer.salary_changed?
1058
-
1059
- cloned_developer = developer.dup
1060
- assert cloned_developer.name_changed? # both attributes differ from defaults
1061
- assert cloned_developer.salary_changed?
1062
- end
1063
-
1064
- def test_dup_of_saved_object_marks_as_dirty_only_changed_attributes
1065
- developer = Developer.create! :name => 'Bjorn'
1066
- assert !developer.name_changed? # both attributes of saved object should be treated as not changed
1067
- assert !developer.salary_changed?
1068
-
1069
- cloned_developer = developer.dup
1070
- assert cloned_developer.name_changed? # ... but on cloned object should be
1071
- assert !cloned_developer.salary_changed? # ... BUT salary has non-nil default which should be treated as not changed on cloned instance
1072
- end
1073
-
1074
- def test_bignum
1075
- company = Company.find(1)
1076
- company.rating = 2147483647
1077
- company.save
1078
- company = Company.find(1)
1079
- assert_equal 2147483647, company.rating
1080
- end
1081
-
1082
- # TODO: extend defaults tests to other databases!
1083
- if current_adapter?(:PostgreSQLAdapter)
1084
- def test_default
1085
- default = Default.new
1086
-
1087
- # fixed dates / times
1088
- assert_equal Date.new(2004, 1, 1), default.fixed_date
1089
- assert_equal Time.local(2004, 1,1,0,0,0,0), default.fixed_time
1090
-
1091
- # char types
1092
- assert_equal 'Y', default.char1
1093
- assert_equal 'a varchar field', default.char2
1094
- assert_equal 'a text field', default.char3
1095
- end
1096
-
1097
- class Geometric < ActiveRecord::Base; end
1098
- def test_geometric_content
1099
-
1100
- # accepted format notes:
1101
- # ()'s aren't required
1102
- # values can be a mix of float or integer
1103
-
1104
- g = Geometric.new(
1105
- :a_point => '(5.0, 6.1)',
1106
- #:a_line => '((2.0, 3), (5.5, 7.0))' # line type is currently unsupported in postgresql
1107
- :a_line_segment => '(2.0, 3), (5.5, 7.0)',
1108
- :a_box => '2.0, 3, 5.5, 7.0',
1109
- :a_path => '[(2.0, 3), (5.5, 7.0), (8.5, 11.0)]', # [ ] is an open path
1110
- :a_polygon => '((2.0, 3), (5.5, 7.0), (8.5, 11.0))',
1111
- :a_circle => '<(5.3, 10.4), 2>'
1112
- )
1113
-
1114
- assert g.save
1115
-
1116
- # Reload and check that we have all the geometric attributes.
1117
- h = ActiveRecord::IdentityMap.without { Geometric.find(g.id) }
1118
-
1119
- assert_equal '(5,6.1)', h.a_point
1120
- assert_equal '[(2,3),(5.5,7)]', h.a_line_segment
1121
- assert_equal '(5.5,7),(2,3)', h.a_box # reordered to store upper right corner then bottom left corner
1122
- assert_equal '[(2,3),(5.5,7),(8.5,11)]', h.a_path
1123
- assert_equal '((2,3),(5.5,7),(8.5,11))', h.a_polygon
1124
- assert_equal '<(5.3,10.4),2>', h.a_circle
1125
-
1126
- # use a geometric function to test for an open path
1127
- objs = Geometric.find_by_sql ["select isopen(a_path) from geometrics where id = ?", g.id]
1128
- assert_equal objs[0].isopen, 't'
1129
-
1130
- # test alternate formats when defining the geometric types
1131
-
1132
- g = Geometric.new(
1133
- :a_point => '5.0, 6.1',
1134
- #:a_line => '((2.0, 3), (5.5, 7.0))' # line type is currently unsupported in postgresql
1135
- :a_line_segment => '((2.0, 3), (5.5, 7.0))',
1136
- :a_box => '(2.0, 3), (5.5, 7.0)',
1137
- :a_path => '((2.0, 3), (5.5, 7.0), (8.5, 11.0))', # ( ) is a closed path
1138
- :a_polygon => '2.0, 3, 5.5, 7.0, 8.5, 11.0',
1139
- :a_circle => '((5.3, 10.4), 2)'
1140
- )
1141
-
1142
- assert g.save
1143
-
1144
- # Reload and check that we have all the geometric attributes.
1145
- h = ActiveRecord::IdentityMap.without { Geometric.find(g.id) }
1146
-
1147
- assert_equal '(5,6.1)', h.a_point
1148
- assert_equal '[(2,3),(5.5,7)]', h.a_line_segment
1149
- assert_equal '(5.5,7),(2,3)', h.a_box # reordered to store upper right corner then bottom left corner
1150
- assert_equal '((2,3),(5.5,7),(8.5,11))', h.a_path
1151
- assert_equal '((2,3),(5.5,7),(8.5,11))', h.a_polygon
1152
- assert_equal '<(5.3,10.4),2>', h.a_circle
1153
-
1154
- # use a geometric function to test for an closed path
1155
- objs = Geometric.find_by_sql ["select isclosed(a_path) from geometrics where id = ?", g.id]
1156
- assert_equal objs[0].isclosed, 't'
1157
- end
1158
- end
1159
-
1160
- class NumericData < ActiveRecord::Base
1161
- self.table_name = 'numeric_data'
1162
- end
1163
-
1164
- def test_big_decimal_conditions
1165
- m = NumericData.new(
1166
- :bank_balance => 1586.43,
1167
- :big_bank_balance => BigDecimal("1000234000567.95"),
1168
- :world_population => 6000000000,
1169
- :my_house_population => 3
1170
- )
1171
- assert m.save
1172
- assert_equal 0, NumericData.where("bank_balance > ?", 2000.0).count
1173
- end
1174
-
1175
- def test_numeric_fields
1176
- m = NumericData.new(
1177
- :bank_balance => 1586.43,
1178
- :big_bank_balance => BigDecimal("1000234000567.95"),
1179
- :world_population => 6000000000,
1180
- :my_house_population => 3
1181
- )
1182
- assert m.save
1183
-
1184
- m1 = NumericData.find(m.id)
1185
- assert_not_nil m1
1186
-
1187
- # As with migration_test.rb, we should make world_population >= 2**62
1188
- # to cover 64-bit platforms and test it is a Bignum, but the main thing
1189
- # is that it's an Integer.
1190
- unless current_adapter?(:IBM_DBAdapter)
1191
- assert_kind_of Integer, m1.world_population
1192
- else
1193
- assert_kind_of BigDecimal, m1.world_population
1194
- end
1195
- assert_equal 6000000000, m1.world_population
1196
-
1197
- unless current_adapter?(:IBM_DBAdapter)
1198
- assert_kind_of Fixnum, m1.my_house_population
1199
- else
1200
- assert_kind_of BigDecimal, m1.my_house_population
1201
- end
1202
- assert_equal 3, m1.my_house_population
1203
-
1204
- assert_kind_of BigDecimal, m1.bank_balance
1205
- assert_equal BigDecimal("1586.43"), m1.bank_balance
1206
-
1207
- assert_kind_of BigDecimal, m1.big_bank_balance
1208
- assert_equal BigDecimal("1000234000567.95"), m1.big_bank_balance
1209
- end
1210
-
1211
- def test_auto_id
1212
- auto = AutoId.new
1213
- auto.save
1214
- assert(auto.id > 0)
1215
- end
1216
-
1217
- def test_sql_injection_via_find
1218
- assert_raise(ActiveRecord::RecordNotFound, ActiveRecord::StatementInvalid) do
1219
- Topic.find("123456 OR id > 0")
1220
- end
1221
- end
1222
-
1223
- def test_column_name_properly_quoted
1224
- col_record = ColumnName.new
1225
- col_record.references = 40
1226
- assert col_record.save
1227
- col_record.references = 41
1228
- assert col_record.save
1229
- assert_not_nil c2 = ColumnName.find(col_record.id)
1230
- assert_equal(41, c2.references)
1231
- end
1232
-
1233
- def test_quoting_arrays
1234
- replies = Reply.find(:all, :conditions => [ "id IN (?)", topics(:first).replies.collect(&:id) ])
1235
- assert_equal topics(:first).replies.size, replies.size
1236
-
1237
- replies = Reply.find(:all, :conditions => [ "id IN (?)", [] ])
1238
- assert_equal 0, replies.size
1239
- end
1240
-
1241
- MyObject = Struct.new :attribute1, :attribute2
1242
-
1243
- def test_serialized_attribute
1244
- Topic.serialize("content", MyObject)
1245
-
1246
- myobj = MyObject.new('value1', 'value2')
1247
- topic = Topic.create("content" => myobj)
1248
- assert_equal(myobj, topic.content)
1249
-
1250
- topic.reload
1251
- assert_equal(myobj, topic.content)
1252
- end
1253
-
1254
- def test_serialized_attribute_in_base_class
1255
- Topic.serialize("content", Hash)
1256
-
1257
- hash = { 'content1' => 'value1', 'content2' => 'value2' }
1258
- important_topic = ImportantTopic.create("content" => hash)
1259
- assert_equal(hash, important_topic.content)
1260
-
1261
- important_topic.reload
1262
- assert_equal(hash, important_topic.content)
1263
- end
1264
-
1265
- # This test was added to fix GH #4004. Obviously the value returned
1266
- # is not really the value 'before type cast' so we should maybe think
1267
- # about changing that in the future.
1268
- def test_serialized_attribute_before_type_cast_returns_unserialized_value
1269
- klass = Class.new(ActiveRecord::Base)
1270
- klass.table_name = "topics"
1271
- klass.serialize :content, Hash
1272
-
1273
- t = klass.new(:content => { :foo => :bar })
1274
- assert_equal({ :foo => :bar }, t.content_before_type_cast)
1275
- t.save!
1276
- t.reload
1277
- assert_equal({ :foo => :bar }, t.content_before_type_cast)
1278
- end
1279
-
1280
- def test_serialized_attribute_declared_in_subclass
1281
- hash = { 'important1' => 'value1', 'important2' => 'value2' }
1282
- important_topic = ImportantTopic.create("important" => hash)
1283
- assert_equal(hash, important_topic.important)
1284
-
1285
- important_topic.reload
1286
- assert_equal(hash, important_topic.important)
1287
- assert_equal(hash, important_topic.read_attribute(:important))
1288
- end
1289
-
1290
- def test_serialized_time_attribute
1291
- myobj = Time.local(2008,1,1,1,0)
1292
- topic = Topic.create("content" => myobj).reload
1293
- assert_equal(myobj, topic.content)
1294
- end
1295
-
1296
- def test_serialized_string_attribute
1297
- myobj = "Yes"
1298
- topic = Topic.create("content" => myobj).reload
1299
- assert_equal(myobj, topic.content)
1300
- end
1301
-
1302
- def test_nil_serialized_attribute_with_class_constraint
1303
- topic = Topic.new
1304
- assert_nil topic.content
1305
- end
1306
-
1307
- def test_should_raise_exception_on_serialized_attribute_with_type_mismatch
1308
- myobj = MyObject.new('value1', 'value2')
1309
- topic = Topic.new(:content => myobj)
1310
- assert topic.save
1311
- Topic.serialize(:content, Hash)
1312
- assert_raise(ActiveRecord::SerializationTypeMismatch) { Topic.find(topic.id).reload.content }
1313
- ensure
1314
- Topic.serialize(:content)
1315
- end
1316
-
1317
- def test_serialized_attribute_with_class_constraint
1318
- settings = { "color" => "blue" }
1319
- Topic.serialize(:content, Hash)
1320
- topic = Topic.new(:content => settings)
1321
- assert topic.save
1322
- assert_equal(settings, Topic.find(topic.id).content)
1323
- ensure
1324
- Topic.serialize(:content)
1325
- end
1326
-
1327
- def test_serialized_default_class
1328
- Topic.serialize(:content, Hash)
1329
- topic = Topic.new
1330
- assert_equal Hash, topic.content.class
1331
- assert_equal Hash, topic.read_attribute(:content).class
1332
- topic.content["beer"] = "MadridRb"
1333
- assert topic.save
1334
- topic.reload
1335
- assert_equal Hash, topic.content.class
1336
- assert_equal "MadridRb", topic.content["beer"]
1337
- ensure
1338
- Topic.serialize(:content)
1339
- end
1340
-
1341
- def test_serialized_no_default_class_for_object
1342
- topic = Topic.new
1343
- assert_nil topic.content
1344
- end
1345
-
1346
- def test_serialized_boolean_value_true
1347
- Topic.serialize(:content)
1348
- topic = Topic.new(:content => true)
1349
- assert topic.save
1350
- topic = topic.reload
1351
- assert_equal topic.content, true
1352
- end
1353
-
1354
- def test_serialized_boolean_value_false
1355
- Topic.serialize(:content)
1356
- topic = Topic.new(:content => false)
1357
- assert topic.save
1358
- topic = topic.reload
1359
- assert_equal topic.content, false
1360
- end
1361
-
1362
- def test_serialize_with_coder
1363
- coder = Class.new {
1364
- # Identity
1365
- def load(thing)
1366
- thing
1367
- end
1368
-
1369
- # base 64
1370
- def dump(thing)
1371
- [thing].pack('m')
1372
- end
1373
- }.new
1374
-
1375
- Topic.serialize(:content, coder)
1376
- s = 'hello world'
1377
- topic = Topic.new(:content => s)
1378
- assert topic.save
1379
- topic = topic.reload
1380
- assert_equal [s].pack('m'), topic.content
1381
- ensure
1382
- Topic.serialize(:content)
1383
- end
1384
-
1385
- def test_serialize_with_bcrypt_coder
1386
- crypt_coder = Class.new {
1387
- def load(thing)
1388
- return unless thing
1389
- BCrypt::Password.new thing
1390
- end
1391
-
1392
- def dump(thing)
1393
- BCrypt::Password.create(thing).to_s
1394
- end
1395
- }.new
1396
-
1397
- Topic.serialize(:content, crypt_coder)
1398
- password = 'password'
1399
- topic = Topic.new(:content => password)
1400
- assert topic.save
1401
- topic = topic.reload
1402
- assert_kind_of BCrypt::Password, topic.content
1403
- assert_equal(true, topic.content == password, 'password should equal')
1404
- ensure
1405
- Topic.serialize(:content)
1406
- end
1407
-
1408
- def test_quote
1409
- author_name = "\\ \001 ' \n \\n \""
1410
- topic = Topic.create('author_name' => author_name)
1411
- assert_equal author_name, Topic.find(topic.id).author_name
1412
- end
1413
-
1414
- if RUBY_VERSION < '1.9'
1415
- def test_quote_chars
1416
- with_kcode('UTF8') do
1417
- str = 'The Narrator'
1418
- topic = Topic.create(:author_name => str)
1419
- assert_equal str, topic.author_name
1420
-
1421
- assert_kind_of ActiveSupport::Multibyte.proxy_class, str.mb_chars
1422
- topic = Topic.find_by_author_name(str.mb_chars)
1423
-
1424
- assert_kind_of Topic, topic
1425
- assert_equal str, topic.author_name, "The right topic should have been found by name even with name passed as Chars"
1426
- end
1427
- end
1428
- end
1429
-
1430
- def test_toggle_attribute
1431
- assert !topics(:first).approved?
1432
- topics(:first).toggle!(:approved)
1433
- assert topics(:first).approved?
1434
- topic = topics(:first)
1435
- topic.toggle(:approved)
1436
- assert !topic.approved?
1437
- topic.reload
1438
- assert topic.approved?
1439
- end
1440
-
1441
- def test_reload
1442
- t1 = Topic.find(1)
1443
- t2 = Topic.find(1)
1444
- t1.title = "something else"
1445
- t1.save
1446
- t2.reload
1447
- assert_equal t1.title, t2.title
1448
- end
1449
-
1450
- def test_reload_with_exclusive_scope
1451
- dev = DeveloperCalledDavid.first
1452
- dev.update_attributes!( :name => "NotDavid" )
1453
- assert_equal dev, dev.reload
1454
- end
1455
-
1456
- def test_set_table_name_with_value
1457
- k = Class.new( ActiveRecord::Base )
1458
- k.table_name = "foo"
1459
- assert_equal "foo", k.table_name
1460
-
1461
- assert_deprecated do
1462
- k.set_table_name "bar"
1463
- end
1464
- assert_equal "bar", k.table_name
1465
- end
1466
-
1467
- def test_switching_between_table_name
1468
- assert_difference("GoodJoke.count") do
1469
- Joke.table_name = "cold_jokes"
1470
- Joke.create
1471
-
1472
- Joke.table_name = "funny_jokes"
1473
- Joke.create
1474
- end
1475
- end
1476
-
1477
- def test_set_table_name_symbol_converted_to_string
1478
- Joke.table_name = :cold_jokes
1479
- assert_equal 'cold_jokes', Joke.table_name
1480
- end
1481
-
1482
- def test_quoted_table_name_after_set_table_name
1483
- klass = Class.new(ActiveRecord::Base)
1484
-
1485
- klass.table_name = "foo"
1486
- assert_equal "foo", klass.table_name
1487
- assert_equal klass.connection.quote_table_name("foo"), klass.quoted_table_name
1488
-
1489
- klass.table_name = "bar"
1490
- assert_equal "bar", klass.table_name
1491
- assert_equal klass.connection.quote_table_name("bar"), klass.quoted_table_name
1492
- end
1493
-
1494
- def test_set_table_name_with_block
1495
- k = Class.new( ActiveRecord::Base )
1496
- assert_deprecated do
1497
- k.set_table_name "foo"
1498
- k.set_table_name do
1499
- ActiveSupport::Deprecation.silence { original_table_name } + "ks"
1500
- end
1501
- end
1502
- assert_equal "fooks", k.table_name
1503
- end
1504
-
1505
- def test_set_table_name_with_inheritance
1506
- k = Class.new( ActiveRecord::Base )
1507
- def k.name; "Foo"; end
1508
- def k.table_name; super + "ks"; end
1509
- assert_equal "foosks", k.table_name
1510
- end
1511
-
1512
- def test_original_table_name
1513
- k = Class.new(ActiveRecord::Base)
1514
- def k.name; "Foo"; end
1515
- k.table_name = "bar"
1516
-
1517
- assert_deprecated do
1518
- assert_equal "foos", k.original_table_name
1519
- end
1520
-
1521
- k = Class.new(ActiveRecord::Base)
1522
- k.table_name = "omg"
1523
- k.table_name = "wtf"
1524
-
1525
- assert_deprecated do
1526
- assert_equal "omg", k.original_table_name
1527
- end
1528
- end
1529
-
1530
- def test_set_primary_key_with_value
1531
- k = Class.new( ActiveRecord::Base )
1532
- k.primary_key = "foo"
1533
- assert_equal "foo", k.primary_key
1534
-
1535
- assert_deprecated do
1536
- k.set_primary_key "bar"
1537
- end
1538
- assert_equal "bar", k.primary_key
1539
- end
1540
-
1541
- def test_set_primary_key_with_block
1542
- k = Class.new( ActiveRecord::Base )
1543
- k.primary_key = 'id'
1544
-
1545
- assert_deprecated do
1546
- k.set_primary_key do
1547
- "sys_" + ActiveSupport::Deprecation.silence { original_primary_key }
1548
- end
1549
- end
1550
- assert_equal "sys_id", k.primary_key
1551
- end
1552
-
1553
- def test_original_primary_key
1554
- k = Class.new(ActiveRecord::Base)
1555
- def k.name; "Foo"; end
1556
- k.table_name = "posts"
1557
- k.primary_key = "bar"
1558
-
1559
- assert_deprecated do
1560
- assert_equal "id", k.original_primary_key
1561
- end
1562
-
1563
- k = Class.new(ActiveRecord::Base)
1564
- k.primary_key = "omg"
1565
- k.primary_key = "wtf"
1566
-
1567
- assert_deprecated do
1568
- assert_equal "omg", k.original_primary_key
1569
- end
1570
- end
1571
-
1572
- def test_set_inheritance_column_with_value
1573
- k = Class.new( ActiveRecord::Base )
1574
- k.inheritance_column = "foo"
1575
- assert_equal "foo", k.inheritance_column
1576
-
1577
- assert_deprecated do
1578
- k.set_inheritance_column "bar"
1579
- end
1580
- assert_equal "bar", k.inheritance_column
1581
- end
1582
-
1583
- def test_set_inheritance_column_with_block
1584
- k = Class.new( ActiveRecord::Base )
1585
- assert_deprecated do
1586
- k.set_inheritance_column do
1587
- ActiveSupport::Deprecation.silence { original_inheritance_column } + "_id"
1588
- end
1589
- end
1590
- assert_equal "type_id", k.inheritance_column
1591
- end
1592
-
1593
- def test_original_inheritance_column
1594
- k = Class.new(ActiveRecord::Base)
1595
- def k.name; "Foo"; end
1596
- k.inheritance_column = "omg"
1597
-
1598
- assert_deprecated do
1599
- assert_equal "type", k.original_inheritance_column
1600
- end
1601
- end
1602
-
1603
- def test_set_sequence_name_with_value
1604
- k = Class.new( ActiveRecord::Base )
1605
- k.sequence_name = "foo"
1606
- assert_equal "foo", k.sequence_name
1607
-
1608
- assert_deprecated do
1609
- k.set_sequence_name "bar"
1610
- end
1611
- assert_equal "bar", k.sequence_name
1612
- end
1613
-
1614
- def test_set_sequence_name_with_block
1615
- k = Class.new( ActiveRecord::Base )
1616
- k.table_name = "projects"
1617
- orig_name = k.sequence_name
1618
- return skip "sequences not supported by db" unless orig_name
1619
-
1620
- assert_deprecated do
1621
- k.set_sequence_name do
1622
- ActiveSupport::Deprecation.silence { original_sequence_name } + "_lol"
1623
- end
1624
- end
1625
- assert_equal orig_name + "_lol", k.sequence_name
1626
- end
1627
-
1628
- def test_original_sequence_name
1629
- k = Class.new(ActiveRecord::Base)
1630
- k.table_name = "projects"
1631
- orig_name = k.sequence_name
1632
- return skip "sequences not supported by db" unless orig_name
1633
-
1634
- k = Class.new(ActiveRecord::Base)
1635
- k.table_name = "projects"
1636
- k.sequence_name = "omg"
1637
-
1638
- assert_deprecated do
1639
- assert_equal orig_name, k.original_sequence_name
1640
- end
1641
-
1642
- k = Class.new(ActiveRecord::Base)
1643
- k.table_name = "projects"
1644
- k.sequence_name = "omg"
1645
- k.sequence_name = "wtf"
1646
- assert_deprecated do
1647
- assert_equal "omg", k.original_sequence_name
1648
- end
1649
- end
1650
-
1651
- def test_sequence_name_with_abstract_class
1652
- ak = Class.new(ActiveRecord::Base)
1653
- ak.abstract_class = true
1654
- k = Class.new(ak)
1655
- k.table_name = "projects"
1656
- orig_name = k.sequence_name
1657
- return skip "sequences not supported by db" unless orig_name
1658
- assert_equal k.reset_sequence_name, orig_name
1659
- end
1660
-
1661
- def test_count_with_join
1662
- res = Post.count_by_sql "SELECT COUNT(*) FROM posts LEFT JOIN comments ON posts.id=comments.post_id WHERE posts.#{QUOTED_TYPE} = 'Post'"
1663
-
1664
- res2 = Post.count(:conditions => "posts.#{QUOTED_TYPE} = 'Post'", :joins => "LEFT JOIN comments ON posts.id=comments.post_id")
1665
- assert_equal res, res2
1666
-
1667
- res3 = nil
1668
- assert_nothing_raised do
1669
- res3 = Post.count(:conditions => "posts.#{QUOTED_TYPE} = 'Post'",
1670
- :joins => "LEFT JOIN comments ON posts.id=comments.post_id")
1671
- end
1672
- assert_equal res, res3
1673
-
1674
- res4 = Post.count_by_sql "SELECT COUNT(p.id) FROM posts p, comments co WHERE p.#{QUOTED_TYPE} = 'Post' AND p.id=co.post_id"
1675
- res5 = nil
1676
- assert_nothing_raised do
1677
- res5 = Post.count(:conditions => "p.#{QUOTED_TYPE} = 'Post' AND p.id=co.post_id",
1678
- :joins => "p, comments co",
1679
- :select => "p.id")
1680
- end
1681
-
1682
- assert_equal res4, res5
1683
-
1684
- res6 = Post.count_by_sql "SELECT COUNT(DISTINCT p.id) FROM posts p, comments co WHERE p.#{QUOTED_TYPE} = 'Post' AND p.id=co.post_id"
1685
- res7 = nil
1686
- assert_nothing_raised do
1687
- res7 = Post.count(:conditions => "p.#{QUOTED_TYPE} = 'Post' AND p.id=co.post_id",
1688
- :joins => "p, comments co",
1689
- :select => "p.id",
1690
- :distinct => true)
1691
- end
1692
- assert_equal res6, res7
1693
- end
1694
-
1695
- def test_scoped_find_conditions
1696
- scoped_developers = Developer.send(:with_scope, :find => { :conditions => 'salary > 90000' }) do
1697
- Developer.find(:all, :conditions => 'id < 5')
1698
- end
1699
- assert !scoped_developers.include?(developers(:david)) # David's salary is less than 90,000
1700
- assert_equal 3, scoped_developers.size
1701
- end
1702
-
1703
- def test_no_limit_offset
1704
- assert_nothing_raised do
1705
- Developer.find(:all, :offset => 2)
1706
- end
1707
- end
1708
-
1709
- def test_scoped_find_limit_offset
1710
- scoped_developers = Developer.send(:with_scope, :find => { :limit => 3, :offset => 2 }) do
1711
- Developer.find(:all, :order => 'id')
1712
- end
1713
- assert !scoped_developers.include?(developers(:david))
1714
- assert !scoped_developers.include?(developers(:jamis))
1715
- assert_equal 3, scoped_developers.size
1716
-
1717
- # Test without scoped find conditions to ensure we get the whole thing
1718
- developers = Developer.find(:all, :order => 'id')
1719
- assert_equal Developer.count, developers.size
1720
- end
1721
-
1722
- def test_scoped_find_order
1723
- # Test order in scope
1724
- scoped_developers = Developer.send(:with_scope, :find => { :limit => 1, :order => 'salary DESC' }) do
1725
- Developer.find(:all)
1726
- end
1727
- assert_equal 'Jamis', scoped_developers.first.name
1728
- assert scoped_developers.include?(developers(:jamis))
1729
- # Test scope without order and order in find
1730
- scoped_developers = Developer.send(:with_scope, :find => { :limit => 1 }) do
1731
- Developer.find(:all, :order => 'salary DESC')
1732
- end
1733
- # Test scope order + find order, order has priority
1734
- scoped_developers = Developer.send(:with_scope, :find => { :limit => 3, :order => 'id DESC' }) do
1735
- Developer.find(:all, :order => 'salary ASC')
1736
- end
1737
- assert scoped_developers.include?(developers(:poor_jamis))
1738
- assert ! scoped_developers.include?(developers(:david))
1739
- assert ! scoped_developers.include?(developers(:jamis))
1740
- assert_equal 3, scoped_developers.size
1741
-
1742
- # Test without scoped find conditions to ensure we get the right thing
1743
- assert ! scoped_developers.include?(Developer.find(1))
1744
- assert scoped_developers.include?(Developer.find(11))
1745
- end
1746
-
1747
- def test_scoped_find_limit_offset_including_has_many_association
1748
- topics = Topic.send(:with_scope, :find => {:limit => 1, :offset => 1, :include => :replies}) do
1749
- Topic.find(:all, :order => "topics.id")
1750
- end
1751
- assert_equal 1, topics.size
1752
- assert_equal 2, topics.first.id
1753
- end
1754
-
1755
- def test_scoped_find_order_including_has_many_association
1756
- developers = Developer.send(:with_scope, :find => { :order => 'developers.salary DESC', :include => :projects }) do
1757
- Developer.find(:all)
1758
- end
1759
- assert developers.size >= 2
1760
- for i in 1...developers.size
1761
- assert developers[i-1].salary >= developers[i].salary
1762
- end
1763
- end
1764
-
1765
- def test_scoped_find_with_group_and_having
1766
- developers = Developer.send(:with_scope, :find => { :group => 'developers.salary', :having => "SUM(salary) > 10000", :select => "SUM(salary) as salary" }) do
1767
- Developer.find(:all)
1768
- end
1769
- assert_equal 3, developers.size
1770
- end
1771
-
1772
- def test_find_last
1773
- last = Developer.find :last
1774
- assert_equal last, Developer.find(:first, :order => 'id desc')
1775
- end
1776
-
1777
- def test_last
1778
- assert_equal Developer.find(:first, :order => 'id desc'), Developer.last
1779
- end
1780
-
1781
- def test_all
1782
- developers = Developer.all
1783
- assert_kind_of Array, developers
1784
- assert_equal Developer.find(:all), developers
1785
- end
1786
-
1787
- def test_all_with_conditions
1788
- assert_equal Developer.find(:all, :order => 'id desc'), Developer.order('id desc').all
1789
- end
1790
-
1791
- def test_find_ordered_last
1792
- last = Developer.find :last, :order => 'developers.salary ASC'
1793
- assert_equal last, Developer.find(:all, :order => 'developers.salary ASC').last
1794
- end
1795
-
1796
- def test_find_reverse_ordered_last
1797
- last = Developer.find :last, :order => 'developers.salary DESC'
1798
- assert_equal last, Developer.find(:all, :order => 'developers.salary DESC').last
1799
- end
1800
-
1801
- def test_find_multiple_ordered_last
1802
- last = Developer.find :last, :order => 'developers.name, developers.salary DESC'
1803
- assert_equal last, Developer.find(:all, :order => 'developers.name, developers.salary DESC').last
1804
- end
1805
-
1806
- def test_find_keeps_multiple_order_values
1807
- combined = Developer.find(:all, :order => 'developers.name, developers.salary')
1808
- assert_equal combined, Developer.find(:all, :order => ['developers.name', 'developers.salary'])
1809
- end
1810
-
1811
- def test_find_keeps_multiple_group_values
1812
- combined = Developer.find(:all, :group => 'developers.name, developers.salary, developers.id, developers.created_at, developers.updated_at')
1813
- assert_equal combined, Developer.find(:all, :group => ['developers.name', 'developers.salary', 'developers.id', 'developers.created_at', 'developers.updated_at'])
1814
- end
1815
-
1816
- def test_find_symbol_ordered_last
1817
- last = Developer.find :last, :order => :salary
1818
- assert_equal last, Developer.find(:all, :order => :salary).last
1819
- end
1820
-
1821
- def test_find_scoped_ordered_last
1822
- last_developer = Developer.send(:with_scope, :find => { :order => 'developers.salary ASC' }) do
1823
- Developer.find(:last)
1824
- end
1825
- assert_equal last_developer, Developer.find(:all, :order => 'developers.salary ASC').last
1826
- end
1827
-
1828
- def test_abstract_class
1829
- assert !ActiveRecord::Base.abstract_class?
1830
- assert LoosePerson.abstract_class?
1831
- assert !LooseDescendant.abstract_class?
1832
- end
1833
-
1834
- def test_abstract_class_table_name
1835
- assert_nil AbstractCompany.table_name
1836
- end
1837
-
1838
- def test_base_class
1839
- assert_equal LoosePerson, LoosePerson.base_class
1840
- assert_equal LooseDescendant, LooseDescendant.base_class
1841
- assert_equal TightPerson, TightPerson.base_class
1842
- assert_equal TightPerson, TightDescendant.base_class
1843
-
1844
- assert_equal Post, Post.base_class
1845
- assert_equal Post, SpecialPost.base_class
1846
- assert_equal Post, StiPost.base_class
1847
- assert_equal SubStiPost, SubStiPost.base_class
1848
- end
1849
-
1850
- def test_descends_from_active_record
1851
- # Tries to call Object.abstract_class?
1852
- assert_raise(NoMethodError) do
1853
- ActiveRecord::Base.descends_from_active_record?
1854
- end
1855
-
1856
- # Abstract subclass of AR::Base.
1857
- assert LoosePerson.descends_from_active_record?
1858
-
1859
- # Concrete subclass of an abstract class.
1860
- assert LooseDescendant.descends_from_active_record?
1861
-
1862
- # Concrete subclass of AR::Base.
1863
- assert TightPerson.descends_from_active_record?
1864
-
1865
- # Concrete subclass of a concrete class but has no type column.
1866
- assert TightDescendant.descends_from_active_record?
1867
-
1868
- # Concrete subclass of AR::Base.
1869
- assert Post.descends_from_active_record?
1870
-
1871
- # Abstract subclass of a concrete class which has a type column.
1872
- # This is pathological, as you'll never have Sub < Abstract < Concrete.
1873
- assert !StiPost.descends_from_active_record?
1874
-
1875
- # Concrete subclasses an abstract class which has a type column.
1876
- assert !SubStiPost.descends_from_active_record?
1877
- end
1878
-
1879
- def test_find_on_abstract_base_class_doesnt_use_type_condition
1880
- old_class = LooseDescendant
1881
- Object.send :remove_const, :LooseDescendant
1882
-
1883
- descendant = old_class.create! :first_name => 'bob'
1884
- assert_not_nil LoosePerson.find(descendant.id), "Should have found instance of LooseDescendant when finding abstract LoosePerson: #{descendant.inspect}"
1885
- ensure
1886
- unless Object.const_defined?(:LooseDescendant)
1887
- Object.const_set :LooseDescendant, old_class
1888
- end
1889
- end
1890
-
1891
- def test_assert_queries
1892
- query = lambda { ActiveRecord::Base.connection.execute 'select count(*) from developers' }
1893
- assert_queries(2) { 2.times { query.call } }
1894
- assert_queries 1, &query
1895
- assert_no_queries { assert true }
1896
- end
1897
-
1898
- def test_to_param_should_return_string
1899
- assert_kind_of String, Client.find(:first).to_param
1900
- end
1901
-
1902
- def test_inspect_class
1903
- assert_equal 'ActiveRecord::Base', ActiveRecord::Base.inspect
1904
- assert_equal 'LoosePerson(abstract)', LoosePerson.inspect
1905
- assert_match(/^Topic\(id: integer, title: string/, Topic.inspect)
1906
- end
1907
-
1908
- def test_inspect_instance
1909
- topic = topics(:first)
1910
- assert_equal %(#<Topic id: 1, title: "The First Topic", author_name: "David", author_email_address: "david@loudthinking.com", written_on: "#{topic.written_on.to_s(:db)}", bonus_time: "#{topic.bonus_time.to_s(:db)}", last_read: "#{topic.last_read.to_s(:db)}", content: "Have a nice day", important: nil, approved: false, replies_count: 1, parent_id: nil, parent_title: nil, type: nil, group: nil, created_at: "#{topic.created_at.to_s(:db)}", updated_at: "#{topic.updated_at.to_s(:db)}">), topic.inspect
1911
- end
1912
-
1913
- def test_inspect_new_instance
1914
- assert_match(/Topic id: nil/, Topic.new.inspect)
1915
- end
1916
-
1917
- def test_inspect_limited_select_instance
1918
- assert_equal %(#<Topic id: 1>), Topic.find(:first, :select => 'id', :conditions => 'id = 1').inspect
1919
- assert_equal %(#<Topic id: 1, title: "The First Topic">), Topic.find(:first, :select => 'id, title', :conditions => 'id = 1').inspect
1920
- end
1921
-
1922
- def test_inspect_class_without_table
1923
- assert_equal "NonExistentTable(Table doesn't exist)", NonExistentTable.inspect
1924
- end
1925
-
1926
- def test_attribute_for_inspect
1927
- t = topics(:first)
1928
- t.title = "The First Topic Now Has A Title With\nNewlines And More Than 50 Characters"
1929
-
1930
- assert_equal %("#{t.written_on.to_s(:db)}"), t.attribute_for_inspect(:written_on)
1931
- assert_equal '"The First Topic Now Has A Title With\nNewlines And M..."', t.attribute_for_inspect(:title)
1932
- end
1933
-
1934
- def test_becomes
1935
- assert_kind_of Reply, topics(:first).becomes(Reply)
1936
- assert_equal "The First Topic", topics(:first).becomes(Reply).title
1937
- end
1938
-
1939
- def test_becomes_includes_errors
1940
- company = Company.new(:name => nil)
1941
- assert !company.valid?
1942
- original_errors = company.errors
1943
- client = company.becomes(Client)
1944
- assert_equal original_errors, client.errors
1945
- end
1946
-
1947
- def test_silence_sets_log_level_to_error_in_block
1948
- original_logger = ActiveRecord::Base.logger
1949
- log = StringIO.new
1950
- ActiveRecord::Base.logger = Logger.new(log)
1951
- ActiveRecord::Base.logger.level = Logger::DEBUG
1952
- ActiveRecord::Base.silence do
1953
- ActiveRecord::Base.logger.warn "warn"
1954
- ActiveRecord::Base.logger.error "error"
1955
- end
1956
- assert_equal "error\n", log.string
1957
- ensure
1958
- ActiveRecord::Base.logger = original_logger
1959
- end
1960
-
1961
- def test_silence_sets_log_level_back_to_level_before_yield
1962
- original_logger = ActiveRecord::Base.logger
1963
- log = StringIO.new
1964
- ActiveRecord::Base.logger = Logger.new(log)
1965
- ActiveRecord::Base.logger.level = Logger::WARN
1966
- ActiveRecord::Base.silence do
1967
- end
1968
- assert_equal Logger::WARN, ActiveRecord::Base.logger.level
1969
- ensure
1970
- ActiveRecord::Base.logger = original_logger
1971
- end
1972
-
1973
- def test_benchmark_with_log_level
1974
- original_logger = ActiveRecord::Base.logger
1975
- log = StringIO.new
1976
- ActiveRecord::Base.logger = Logger.new(log)
1977
- ActiveRecord::Base.logger.level = Logger::WARN
1978
- ActiveRecord::Base.benchmark("Debug Topic Count", :level => :debug) { Topic.count }
1979
- ActiveRecord::Base.benchmark("Warn Topic Count", :level => :warn) { Topic.count }
1980
- ActiveRecord::Base.benchmark("Error Topic Count", :level => :error) { Topic.count }
1981
- assert_no_match(/Debug Topic Count/, log.string)
1982
- assert_match(/Warn Topic Count/, log.string)
1983
- assert_match(/Error Topic Count/, log.string)
1984
- ensure
1985
- ActiveRecord::Base.logger = original_logger
1986
- end
1987
-
1988
- def test_benchmark_with_use_silence
1989
- original_logger = ActiveRecord::Base.logger
1990
- log = StringIO.new
1991
- ActiveRecord::Base.logger = Logger.new(log)
1992
- assert_deprecated do
1993
- ActiveRecord::Base.benchmark("Logging", :level => :debug, :silence => true) { ActiveRecord::Base.logger.debug "Loud" }
1994
- end
1995
- ActiveRecord::Base.benchmark("Logging", :level => :debug, :silence => false) { ActiveRecord::Base.logger.debug "Quiet" }
1996
- assert_no_match(/Loud/, log.string)
1997
- assert_match(/Quiet/, log.string)
1998
- ensure
1999
- ActiveRecord::Base.logger = original_logger
2000
- end
2001
-
2002
- def test_compute_type_success
2003
- assert_equal Author, ActiveRecord::Base.send(:compute_type, 'Author')
2004
- end
2005
-
2006
- def test_compute_type_nonexistent_constant
2007
- assert_raises NameError do
2008
- ActiveRecord::Base.send :compute_type, 'NonexistentModel'
2009
- end
2010
- end
2011
-
2012
- def test_compute_type_no_method_error
2013
- ActiveSupport::Dependencies.stubs(:constantize).raises(NoMethodError)
2014
- assert_raises NoMethodError do
2015
- ActiveRecord::Base.send :compute_type, 'InvalidModel'
2016
- end
2017
- end
2018
-
2019
- def test_compute_type_argument_error
2020
- ActiveSupport::Dependencies.stubs(:constantize).raises(ArgumentError)
2021
- assert_raises ArgumentError do
2022
- ActiveRecord::Base.send :compute_type, 'InvalidModel'
2023
- end
2024
- end
2025
-
2026
- def test_clear_cache!
2027
- # preheat cache
2028
- c1 = Post.connection.schema_cache.columns['posts']
2029
- ActiveRecord::Base.clear_cache!
2030
- c2 = Post.connection.schema_cache.columns['posts']
2031
- assert_not_equal c1, c2
2032
- end
2033
-
2034
- def test_current_scope_is_reset
2035
- Object.const_set :UnloadablePost, Class.new(ActiveRecord::Base)
2036
- UnloadablePost.send(:current_scope=, UnloadablePost.scoped)
2037
-
2038
- UnloadablePost.unloadable
2039
- assert_not_nil Thread.current[:UnloadablePost_current_scope]
2040
- ActiveSupport::Dependencies.remove_unloadable_constants!
2041
- assert_nil Thread.current[:UnloadablePost_current_scope]
2042
- ensure
2043
- Object.class_eval{ remove_const :UnloadablePost } if defined?(UnloadablePost)
2044
- end
2045
-
2046
- def test_marshal_round_trip
2047
- if ENV['TRAVIS'] && RUBY_VERSION == "1.8.7"
2048
- return skip("Marshalling tests disabled for Ruby 1.8.7 on Travis CI due to what appears " \
2049
- "to be a Ruby bug.")
2050
- end
2051
-
2052
- expected = posts(:welcome)
2053
- marshalled = Marshal.dump(expected)
2054
- actual = Marshal.load(marshalled)
2055
-
2056
- assert_equal expected.attributes, actual.attributes
2057
- end
2058
-
2059
- def test_marshal_new_record_round_trip
2060
- if ENV['TRAVIS'] && RUBY_VERSION == "1.8.7"
2061
- return skip("Marshalling tests disabled for Ruby 1.8.7 on Travis CI due to what appears " \
2062
- "to be a Ruby bug.")
2063
- end
2064
-
2065
- marshalled = Marshal.dump(Post.new)
2066
- post = Marshal.load(marshalled)
2067
-
2068
- assert post.new_record?, "should be a new record"
2069
- end
2070
-
2071
- def test_marshalling_with_associations
2072
- if ENV['TRAVIS'] && RUBY_VERSION == "1.8.7"
2073
- return skip("Marshalling tests disabled for Ruby 1.8.7 on Travis CI due to what appears " \
2074
- "to be a Ruby bug.")
2075
- end
2076
-
2077
- post = Post.new
2078
- post.comments.build
2079
-
2080
- marshalled = Marshal.dump(post)
2081
- post = Marshal.load(marshalled)
2082
-
2083
- assert_equal 1, post.comments.length
2084
- end
2085
-
2086
- def test_attribute_names
2087
- assert_equal ["id", "type", "ruby_type", "firm_id", "firm_name", "name", "client_of", "rating", "account_id"],
2088
- Company.attribute_names
2089
- end
2090
-
2091
- def test_attribute_names_on_table_not_exists
2092
- assert_equal [], NonExistentTable.attribute_names
2093
- end
2094
-
2095
- def test_attribtue_names_on_abstract_class
2096
- assert_equal [], AbstractCompany.attribute_names
2097
- end
2098
-
2099
- def test_cache_key_for_existing_record_is_not_timezone_dependent
2100
- ActiveRecord::Base.time_zone_aware_attributes = true
2101
-
2102
- Time.zone = "UTC"
2103
- utc_key = Developer.first.cache_key
2104
-
2105
- Time.zone = "EST"
2106
- est_key = Developer.first.cache_key
2107
-
2108
- assert_equal utc_key, est_key
2109
- ensure
2110
- ActiveRecord::Base.time_zone_aware_attributes = false
2111
- end
2112
-
2113
- def test_cache_key_format_for_existing_record_with_updated_at
2114
- dev = Developer.first
2115
- assert_equal "developers/#{dev.id}-#{dev.updated_at.utc.to_s(:number)}", dev.cache_key
2116
- end
2117
-
2118
- def test_cache_key_format_for_existing_record_with_nil_updated_at
2119
- dev = Developer.first
2120
- dev.update_attribute(:updated_at, nil)
2121
- assert_match(/\/#{dev.id}$/, dev.cache_key)
2122
- end
2123
-
2124
- def test_uniq_delegates_to_scoped
2125
- scope = stub
2126
- Bird.stubs(:scoped).returns(mock(:uniq => scope))
2127
- assert_equal scope, Bird.uniq
2128
- end
2129
-
2130
- def test_table_name_with_2_abstract_subclasses
2131
- assert_equal "photos", Photo.table_name
2132
- end
2133
- end
1
+ # encoding: utf-8
2
+
3
+ require "cases/helper"
4
+ require 'active_support/concurrency/latch'
5
+ require 'models/post'
6
+ require 'models/author'
7
+ require 'models/topic'
8
+ require 'models/reply'
9
+ require 'models/category'
10
+ require 'models/company'
11
+ require 'models/customer'
12
+ require 'models/developer'
13
+ require 'models/computer'
14
+ require 'models/project'
15
+ require 'models/default'
16
+ require 'models/auto_id'
17
+ require 'models/boolean'
18
+ require 'models/column_name'
19
+ require 'models/subscriber'
20
+ require 'models/keyboard'
21
+ require 'models/comment'
22
+ require 'models/minimalistic'
23
+ require 'models/warehouse_thing'
24
+ require 'models/parrot'
25
+ require 'models/person'
26
+ require 'models/edge'
27
+ require 'models/joke'
28
+ require 'models/bird'
29
+ require 'models/car'
30
+ require 'models/bulb'
31
+ require 'rexml/document'
32
+
33
+ class FirstAbstractClass < ActiveRecord::Base
34
+ self.abstract_class = true
35
+ end
36
+ class SecondAbstractClass < FirstAbstractClass
37
+ self.abstract_class = true
38
+ end
39
+ class Photo < SecondAbstractClass; end
40
+ class Category < ActiveRecord::Base; end
41
+ class Categorization < ActiveRecord::Base; end
42
+ class Smarts < ActiveRecord::Base; end
43
+ class CreditCard < ActiveRecord::Base
44
+ class PinNumber < ActiveRecord::Base
45
+ class CvvCode < ActiveRecord::Base; end
46
+ class SubCvvCode < CvvCode; end
47
+ end
48
+ class SubPinNumber < PinNumber; end
49
+ class Brand < Category; end
50
+ end
51
+ class MasterCreditCard < ActiveRecord::Base; end
52
+ class Post < ActiveRecord::Base; end
53
+ class Computer < ActiveRecord::Base; end
54
+ class NonExistentTable < ActiveRecord::Base; end
55
+ class TestOracleDefault < ActiveRecord::Base; end
56
+
57
+ class ReadonlyTitlePost < Post
58
+ attr_readonly :title
59
+ end
60
+
61
+ class Weird < ActiveRecord::Base; end
62
+
63
+ class Boolean < ActiveRecord::Base
64
+ def has_fun
65
+ super
66
+ end
67
+ end
68
+
69
+ class LintTest < ActiveRecord::TestCase
70
+ include ActiveModel::Lint::Tests
71
+
72
+ class LintModel < ActiveRecord::Base; end
73
+
74
+ def setup
75
+ @model = LintModel.new
76
+ end
77
+ end
78
+
79
+ class BasicsTest < ActiveRecord::TestCase
80
+ fixtures :topics, :companies, :developers, :projects, :computers, :accounts, :minimalistics, 'warehouse_things', :authors, :categorizations, :categories, :posts, :author_addresses
81
+
82
+ def test_column_names_are_escaped
83
+ conn = ActiveRecord::Base.connection
84
+ classname = conn.class.name[/[^:]*$/]
85
+ badchar = {
86
+ 'SQLite3Adapter' => '"',
87
+ 'MysqlAdapter' => '`',
88
+ 'Mysql2Adapter' => '`',
89
+ 'PostgreSQLAdapter' => '"',
90
+ 'OracleAdapter' => '"',
91
+ 'IBM_DBAdapter' => '"',
92
+ }.fetch(classname) {
93
+ raise "need a bad char for #{classname}"
94
+ }
95
+
96
+ quoted = conn.quote_column_name "foo#{badchar}bar"
97
+ if current_adapter?(:OracleAdapter)
98
+ # Oracle does not allow double quotes in table and column names at all
99
+ # therefore quoting removes them
100
+ assert_equal("#{badchar}foobar#{badchar}", quoted)
101
+ elsif current_adapter?(:IBM_DBAdapter)
102
+ assert_equal("foo#{badchar}bar", quoted)
103
+ else
104
+ assert_equal("#{badchar}foo#{badchar * 2}bar#{badchar}", quoted)
105
+ end
106
+ end
107
+
108
+ def test_columns_should_obey_set_primary_key
109
+ pk = Subscriber.columns_hash[Subscriber.primary_key]
110
+ assert_equal 'nick', pk.name, 'nick should be primary key'
111
+ end
112
+
113
+ def test_primary_key_with_no_id
114
+ assert_nil Edge.primary_key
115
+ end
116
+
117
+ unless current_adapter?(:PostgreSQLAdapter, :OracleAdapter, :SQLServerAdapter, :IBM_DBAdapter)
118
+ def test_limit_with_comma
119
+ assert Topic.limit("1,2").to_a
120
+ end
121
+ end
122
+
123
+ def test_limit_without_comma
124
+ assert_equal 1, Topic.limit("1").to_a.length
125
+ assert_equal 1, Topic.limit(1).to_a.length
126
+ end
127
+
128
+ def test_limit_should_take_value_from_latest_limit
129
+ assert_equal 1, Topic.limit(2).limit(1).to_a.length
130
+ end
131
+
132
+ def test_invalid_limit
133
+ assert_raises(ArgumentError) do
134
+ Topic.limit("asdfadf").to_a
135
+ end
136
+ end
137
+
138
+ def test_limit_should_sanitize_sql_injection_for_limit_without_commas
139
+ assert_raises(ArgumentError) do
140
+ Topic.limit("1 select * from schema").to_a
141
+ end
142
+ end
143
+
144
+ def test_limit_should_sanitize_sql_injection_for_limit_with_commas
145
+ assert_raises(ArgumentError) do
146
+ Topic.limit("1, 7 procedure help()").to_a
147
+ end
148
+ end
149
+
150
+ unless current_adapter?(:MysqlAdapter, :Mysql2Adapter, :IBM_DBAdapter)
151
+ def test_limit_should_allow_sql_literal
152
+ assert_equal 1, Topic.limit(Arel.sql('2-1')).to_a.length
153
+ end
154
+ end
155
+
156
+ def test_select_symbol
157
+ topic_ids = Topic.select(:id).map(&:id).sort
158
+ assert_equal Topic.pluck(:id).sort, topic_ids
159
+ end
160
+
161
+ def test_table_exists
162
+ assert !NonExistentTable.table_exists?
163
+ assert Topic.table_exists?
164
+ end
165
+
166
+ def test_preserving_date_objects
167
+ # Oracle enhanced adapter allows to define Date attributes in model class (see topic.rb)
168
+ assert_kind_of(
169
+ Date, Topic.find(1).last_read,
170
+ "The last_read attribute should be of the Date class"
171
+ )
172
+ end
173
+
174
+ def test_previously_changed
175
+ topic = Topic.first
176
+ topic.title = '<3<3<3'
177
+ assert_equal({}, topic.previous_changes)
178
+
179
+ topic.save!
180
+ expected = ["The First Topic", "<3<3<3"]
181
+ assert_equal(expected, topic.previous_changes['title'])
182
+ end
183
+
184
+ def test_previously_changed_dup
185
+ topic = Topic.first
186
+ topic.title = '<3<3<3'
187
+ topic.save!
188
+
189
+ t2 = topic.dup
190
+
191
+ assert_equal(topic.previous_changes, t2.previous_changes)
192
+
193
+ topic.title = "lolwut"
194
+ topic.save!
195
+
196
+ assert_not_equal(topic.previous_changes, t2.previous_changes)
197
+ end
198
+
199
+ def test_preserving_time_objects
200
+ assert_kind_of(
201
+ Time, Topic.find(1).bonus_time,
202
+ "The bonus_time attribute should be of the Time class"
203
+ )
204
+
205
+ assert_kind_of(
206
+ Time, Topic.find(1).written_on,
207
+ "The written_on attribute should be of the Time class"
208
+ )
209
+
210
+ # For adapters which support microsecond resolution.
211
+ if current_adapter?(:PostgreSQLAdapter, :SQLite3Adapter) || mysql_56?
212
+ assert_equal 11, Topic.find(1).written_on.sec
213
+ assert_equal 223300, Topic.find(1).written_on.usec
214
+ assert_equal 9900, Topic.find(2).written_on.usec
215
+ assert_equal 129346, Topic.find(3).written_on.usec
216
+ end
217
+ end
218
+
219
+ def test_preserving_time_objects_with_local_time_conversion_to_default_timezone_utc
220
+ with_env_tz 'America/New_York' do
221
+ with_timezone_config default: :utc do
222
+ time = Time.local(2000)
223
+ topic = Topic.create('written_on' => time)
224
+ saved_time = Topic.find(topic.id).reload.written_on
225
+ assert_equal time, saved_time
226
+ assert_equal [0, 0, 0, 1, 1, 2000, 6, 1, false, "EST"], time.to_a
227
+ assert_equal [0, 0, 5, 1, 1, 2000, 6, 1, false, "UTC"], saved_time.to_a
228
+ end
229
+ end
230
+ end
231
+
232
+ def test_preserving_time_objects_with_time_with_zone_conversion_to_default_timezone_utc
233
+ with_env_tz 'America/New_York' do
234
+ with_timezone_config default: :utc do
235
+ Time.use_zone 'Central Time (US & Canada)' do
236
+ time = Time.zone.local(2000)
237
+ topic = Topic.create('written_on' => time)
238
+ saved_time = Topic.find(topic.id).reload.written_on
239
+ assert_equal time, saved_time
240
+ assert_equal [0, 0, 0, 1, 1, 2000, 6, 1, false, "CST"], time.to_a
241
+ assert_equal [0, 0, 6, 1, 1, 2000, 6, 1, false, "UTC"], saved_time.to_a
242
+ end
243
+ end
244
+ end
245
+ end
246
+
247
+ def test_preserving_time_objects_with_utc_time_conversion_to_default_timezone_local
248
+ with_env_tz 'America/New_York' do
249
+ with_timezone_config default: :local do
250
+ time = Time.utc(2000)
251
+ topic = Topic.create('written_on' => time)
252
+ saved_time = Topic.find(topic.id).reload.written_on
253
+ assert_equal time, saved_time
254
+ assert_equal [0, 0, 0, 1, 1, 2000, 6, 1, false, "UTC"], time.to_a
255
+ assert_equal [0, 0, 19, 31, 12, 1999, 5, 365, false, "EST"], saved_time.to_a
256
+ end
257
+ end
258
+ end
259
+
260
+ def test_preserving_time_objects_with_time_with_zone_conversion_to_default_timezone_local
261
+ with_env_tz 'America/New_York' do
262
+ with_timezone_config default: :local do
263
+ Time.use_zone 'Central Time (US & Canada)' do
264
+ time = Time.zone.local(2000)
265
+ topic = Topic.create('written_on' => time)
266
+ saved_time = Topic.find(topic.id).reload.written_on
267
+ assert_equal time, saved_time
268
+ assert_equal [0, 0, 0, 1, 1, 2000, 6, 1, false, "CST"], time.to_a
269
+ assert_equal [0, 0, 1, 1, 1, 2000, 6, 1, false, "EST"], saved_time.to_a
270
+ end
271
+ end
272
+ end
273
+ end
274
+
275
+ def test_custom_mutator
276
+ topic = Topic.find(1)
277
+ # This mutator is protected in the class definition
278
+ topic.send(:approved=, true)
279
+ assert topic.instance_variable_get("@custom_approved")
280
+ end
281
+
282
+ def test_initialize_with_attributes
283
+ topic = Topic.new({
284
+ "title" => "initialized from attributes", "written_on" => "2003-12-12 23:23"
285
+ })
286
+
287
+ assert_equal("initialized from attributes", topic.title)
288
+ end
289
+
290
+ def test_initialize_with_invalid_attribute
291
+ Topic.new({ "title" => "test",
292
+ "last_read(1i)" => "2005", "last_read(2i)" => "2", "last_read(3i)" => "31"})
293
+ rescue ActiveRecord::MultiparameterAssignmentErrors => ex
294
+ assert_equal(1, ex.errors.size)
295
+ assert_equal("last_read", ex.errors[0].attribute)
296
+ end
297
+
298
+ def test_create_after_initialize_without_block
299
+ cb = CustomBulb.create(:name => 'Dude')
300
+ assert_equal('Dude', cb.name)
301
+ assert_equal(true, cb.frickinawesome)
302
+ end
303
+
304
+ def test_create_after_initialize_with_block
305
+ cb = CustomBulb.create {|c| c.name = 'Dude' }
306
+ assert_equal('Dude', cb.name)
307
+ assert_equal(true, cb.frickinawesome)
308
+ end
309
+
310
+ def test_create_after_initialize_with_array_param
311
+ cbs = CustomBulb.create([{ name: 'Dude' }, { name: 'Bob' }])
312
+ assert_equal 'Dude', cbs[0].name
313
+ assert_equal 'Bob', cbs[1].name
314
+ assert cbs[0].frickinawesome
315
+ assert !cbs[1].frickinawesome
316
+ end
317
+
318
+ def test_load
319
+ topics = Topic.all.merge!(:order => 'id').to_a
320
+ assert_equal(5, topics.size)
321
+ assert_equal(topics(:first).title, topics.first.title)
322
+ end
323
+
324
+ def test_load_with_condition
325
+ topics = Topic.all.merge!(:where => "author_name = 'Mary'").to_a
326
+
327
+ assert_equal(1, topics.size)
328
+ assert_equal(topics(:second).title, topics.first.title)
329
+ end
330
+
331
+ GUESSED_CLASSES = [Category, Smarts, CreditCard, CreditCard::PinNumber, CreditCard::PinNumber::CvvCode, CreditCard::SubPinNumber, CreditCard::Brand, MasterCreditCard]
332
+
333
+ def test_table_name_guesses
334
+ assert_equal "topics", Topic.table_name
335
+
336
+ assert_equal "categories", Category.table_name
337
+ assert_equal "smarts", Smarts.table_name
338
+ assert_equal "credit_cards", CreditCard.table_name
339
+ assert_equal "credit_card_pin_numbers", CreditCard::PinNumber.table_name
340
+ assert_equal "credit_card_pin_number_cvv_codes", CreditCard::PinNumber::CvvCode.table_name
341
+ assert_equal "credit_card_pin_numbers", CreditCard::SubPinNumber.table_name
342
+ assert_equal "categories", CreditCard::Brand.table_name
343
+ assert_equal "master_credit_cards", MasterCreditCard.table_name
344
+ ensure
345
+ GUESSED_CLASSES.each(&:reset_table_name)
346
+ end
347
+
348
+ def test_singular_table_name_guesses
349
+ ActiveRecord::Base.pluralize_table_names = false
350
+ GUESSED_CLASSES.each(&:reset_table_name)
351
+
352
+ assert_equal "category", Category.table_name
353
+ assert_equal "smarts", Smarts.table_name
354
+ assert_equal "credit_card", CreditCard.table_name
355
+ assert_equal "credit_card_pin_number", CreditCard::PinNumber.table_name
356
+ assert_equal "credit_card_pin_number_cvv_code", CreditCard::PinNumber::CvvCode.table_name
357
+ assert_equal "credit_card_pin_number", CreditCard::SubPinNumber.table_name
358
+ assert_equal "category", CreditCard::Brand.table_name
359
+ assert_equal "master_credit_card", MasterCreditCard.table_name
360
+ ensure
361
+ ActiveRecord::Base.pluralize_table_names = true
362
+ GUESSED_CLASSES.each(&:reset_table_name)
363
+ end
364
+
365
+ def test_table_name_guesses_with_prefixes_and_suffixes
366
+ ActiveRecord::Base.table_name_prefix = "test_"
367
+ Category.reset_table_name
368
+ assert_equal "test_categories", Category.table_name
369
+ ActiveRecord::Base.table_name_suffix = "_test"
370
+ Category.reset_table_name
371
+ assert_equal "test_categories_test", Category.table_name
372
+ ActiveRecord::Base.table_name_prefix = ""
373
+ Category.reset_table_name
374
+ assert_equal "categories_test", Category.table_name
375
+ ActiveRecord::Base.table_name_suffix = ""
376
+ Category.reset_table_name
377
+ assert_equal "categories", Category.table_name
378
+ ensure
379
+ ActiveRecord::Base.table_name_prefix = ""
380
+ ActiveRecord::Base.table_name_suffix = ""
381
+ GUESSED_CLASSES.each(&:reset_table_name)
382
+ end
383
+
384
+ def test_singular_table_name_guesses_with_prefixes_and_suffixes
385
+ ActiveRecord::Base.pluralize_table_names = false
386
+
387
+ ActiveRecord::Base.table_name_prefix = "test_"
388
+ Category.reset_table_name
389
+ assert_equal "test_category", Category.table_name
390
+ ActiveRecord::Base.table_name_suffix = "_test"
391
+ Category.reset_table_name
392
+ assert_equal "test_category_test", Category.table_name
393
+ ActiveRecord::Base.table_name_prefix = ""
394
+ Category.reset_table_name
395
+ assert_equal "category_test", Category.table_name
396
+ ActiveRecord::Base.table_name_suffix = ""
397
+ Category.reset_table_name
398
+ assert_equal "category", Category.table_name
399
+ ensure
400
+ ActiveRecord::Base.pluralize_table_names = true
401
+ ActiveRecord::Base.table_name_prefix = ""
402
+ ActiveRecord::Base.table_name_suffix = ""
403
+ GUESSED_CLASSES.each(&:reset_table_name)
404
+ end
405
+
406
+ def test_table_name_guesses_with_inherited_prefixes_and_suffixes
407
+ GUESSED_CLASSES.each(&:reset_table_name)
408
+
409
+ CreditCard.table_name_prefix = "test_"
410
+ CreditCard.reset_table_name
411
+ Category.reset_table_name
412
+ assert_equal "test_credit_cards", CreditCard.table_name
413
+ assert_equal "categories", Category.table_name
414
+ CreditCard.table_name_suffix = "_test"
415
+ CreditCard.reset_table_name
416
+ Category.reset_table_name
417
+ assert_equal "test_credit_cards_test", CreditCard.table_name
418
+ assert_equal "categories", Category.table_name
419
+ CreditCard.table_name_prefix = ""
420
+ CreditCard.reset_table_name
421
+ Category.reset_table_name
422
+ assert_equal "credit_cards_test", CreditCard.table_name
423
+ assert_equal "categories", Category.table_name
424
+ CreditCard.table_name_suffix = ""
425
+ CreditCard.reset_table_name
426
+ Category.reset_table_name
427
+ assert_equal "credit_cards", CreditCard.table_name
428
+ assert_equal "categories", Category.table_name
429
+ ensure
430
+ CreditCard.table_name_prefix = ""
431
+ CreditCard.table_name_suffix = ""
432
+ GUESSED_CLASSES.each(&:reset_table_name)
433
+ end
434
+
435
+ def test_singular_table_name_guesses_for_individual_table
436
+ Post.pluralize_table_names = false
437
+ Post.reset_table_name
438
+ assert_equal "post", Post.table_name
439
+ assert_equal "categories", Category.table_name
440
+ ensure
441
+ Post.pluralize_table_names = true
442
+ Post.reset_table_name
443
+ end
444
+
445
+ if current_adapter?(:MysqlAdapter, :Mysql2Adapter)
446
+ def test_update_all_with_order_and_limit
447
+ assert_equal 1, Topic.limit(1).order('id DESC').update_all(:content => 'bulk updated!')
448
+ end
449
+ end
450
+
451
+ def test_null_fields
452
+ assert_nil Topic.find(1).parent_id
453
+ assert_nil Topic.create("title" => "Hey you").parent_id
454
+ end
455
+
456
+ def test_default_values
457
+ topic = Topic.new
458
+ assert topic.approved?
459
+ assert_nil topic.written_on
460
+ assert_nil topic.bonus_time
461
+ assert_nil topic.last_read
462
+
463
+ topic.save
464
+
465
+ topic = Topic.find(topic.id)
466
+ assert topic.approved?
467
+ assert_nil topic.last_read
468
+
469
+ # Oracle has some funky default handling, so it requires a bit of
470
+ # extra testing. See ticket #2788.
471
+ if current_adapter?(:OracleAdapter)
472
+ test = TestOracleDefault.new
473
+ assert_equal "X", test.test_char
474
+ assert_equal "hello", test.test_string
475
+ assert_equal 3, test.test_int
476
+ end
477
+ end
478
+
479
+ # Oracle does not have a TIME datatype.
480
+ unless current_adapter?(:OracleAdapter)
481
+ def test_utc_as_time_zone
482
+ with_timezone_config default: :utc do
483
+ attributes = { "bonus_time" => "5:42:00AM" }
484
+ topic = Topic.find(1)
485
+ topic.attributes = attributes
486
+ assert_equal Time.utc(2000, 1, 1, 5, 42, 0), topic.bonus_time
487
+ end
488
+ end
489
+
490
+ def test_utc_as_time_zone_and_new
491
+ with_timezone_config default: :utc do
492
+ attributes = { "bonus_time(1i)"=>"2000",
493
+ "bonus_time(2i)"=>"1",
494
+ "bonus_time(3i)"=>"1",
495
+ "bonus_time(4i)"=>"10",
496
+ "bonus_time(5i)"=>"35",
497
+ "bonus_time(6i)"=>"50" }
498
+ topic = Topic.new(attributes)
499
+ assert_equal Time.utc(2000, 1, 1, 10, 35, 50), topic.bonus_time
500
+ end
501
+ end
502
+ end
503
+
504
+ def test_default_values_on_empty_strings
505
+ topic = Topic.new
506
+ topic.approved = nil
507
+ topic.last_read = nil
508
+
509
+ topic.save
510
+
511
+ topic = Topic.find(topic.id)
512
+ assert_nil topic.last_read
513
+
514
+ assert_nil topic.approved
515
+ end
516
+
517
+ def test_equality
518
+ assert_equal Topic.find(1), Topic.find(2).topic
519
+ end
520
+
521
+ def test_find_by_slug
522
+ assert_equal Topic.find('1-meowmeow'), Topic.find(1)
523
+ end
524
+
525
+ def test_find_by_slug_with_array
526
+ assert_equal Topic.find(['1-meowmeow', '2-hello']), Topic.find([1, 2])
527
+ end
528
+
529
+ def test_find_by_slug_with_range
530
+ assert_equal Topic.where(id: '1-meowmeow'..'2-hello'), Topic.where(id: 1..2)
531
+ end
532
+
533
+ def test_equality_of_new_records
534
+ assert_not_equal Topic.new, Topic.new
535
+ assert_equal false, Topic.new == Topic.new
536
+ end
537
+
538
+ def test_equality_of_destroyed_records
539
+ topic_1 = Topic.new(:title => 'test_1')
540
+ topic_1.save
541
+ topic_2 = Topic.find(topic_1.id)
542
+ topic_1.destroy
543
+ assert_equal topic_1, topic_2
544
+ assert_equal topic_2, topic_1
545
+ end
546
+
547
+ def test_equality_with_blank_ids
548
+ one = Subscriber.new(:id => '')
549
+ two = Subscriber.new(:id => '')
550
+ assert_equal one, two
551
+ end
552
+
553
+ def test_equality_of_relation_and_collection_proxy
554
+ car = Car.create!
555
+ car.bulbs.build
556
+ car.save
557
+
558
+ assert car.bulbs == Bulb.where(car_id: car.id), 'CollectionProxy should be comparable with Relation'
559
+ assert Bulb.where(car_id: car.id) == car.bulbs, 'Relation should be comparable with CollectionProxy'
560
+ end
561
+
562
+ def test_equality_of_relation_and_array
563
+ car = Car.create!
564
+ car.bulbs.build
565
+ car.save
566
+
567
+ assert Bulb.where(car_id: car.id) == car.bulbs.to_a, 'Relation should be comparable with Array'
568
+ end
569
+
570
+ def test_equality_of_relation_and_association_relation
571
+ car = Car.create!
572
+ car.bulbs.build
573
+ car.save
574
+
575
+ assert_equal Bulb.where(car_id: car.id), car.bulbs.includes(:car), 'Relation should be comparable with AssociationRelation'
576
+ assert_equal car.bulbs.includes(:car), Bulb.where(car_id: car.id), 'AssociationRelation should be comparable with Relation'
577
+ end
578
+
579
+ def test_equality_of_collection_proxy_and_association_relation
580
+ car = Car.create!
581
+ car.bulbs.build
582
+ car.save
583
+
584
+ assert_equal car.bulbs, car.bulbs.includes(:car), 'CollectionProxy should be comparable with AssociationRelation'
585
+ assert_equal car.bulbs.includes(:car), car.bulbs, 'AssociationRelation should be comparable with CollectionProxy'
586
+ end
587
+
588
+ def test_hashing
589
+ assert_equal [ Topic.find(1) ], [ Topic.find(2).topic ] & [ Topic.find(1) ]
590
+ end
591
+
592
+ def test_successful_comparison_of_like_class_records
593
+ topic_1 = Topic.create!
594
+ topic_2 = Topic.create!
595
+
596
+ assert_equal [topic_2, topic_1].sort, [topic_1, topic_2]
597
+ end
598
+
599
+ def test_failed_comparison_of_unlike_class_records
600
+ assert_raises ArgumentError do
601
+ [ topics(:first), posts(:welcome) ].sort
602
+ end
603
+ end
604
+
605
+ def test_create_without_prepared_statement
606
+ topic = Topic.connection.unprepared_statement do
607
+ Topic.create(:title => 'foo')
608
+ end
609
+
610
+ assert_equal topic, Topic.find(topic.id)
611
+ end
612
+
613
+ def test_destroy_without_prepared_statement
614
+ topic = Topic.create(title: 'foo')
615
+ Topic.connection.unprepared_statement do
616
+ Topic.find(topic.id).destroy
617
+ end
618
+
619
+ assert_equal nil, Topic.find_by_id(topic.id)
620
+ end
621
+
622
+ def test_comparison_with_different_objects
623
+ topic = Topic.create
624
+ category = Category.create(:name => "comparison")
625
+ assert_nil topic <=> category
626
+ end
627
+
628
+ def test_comparison_with_different_objects_in_array
629
+ topic = Topic.create
630
+ assert_raises(ArgumentError) do
631
+ [1, topic].sort
632
+ end
633
+ end
634
+
635
+ def test_readonly_attributes
636
+ assert_equal Set.new([ 'title' , 'comments_count' ]), ReadonlyTitlePost.readonly_attributes
637
+
638
+ post = ReadonlyTitlePost.create(:title => "cannot change this", :body => "changeable")
639
+ post.reload
640
+ assert_equal "cannot change this", post.title
641
+
642
+ post.update(title: "try to change", body: "changed")
643
+ post.reload
644
+ assert_equal "cannot change this", post.title
645
+ assert_equal "changed", post.body
646
+ end
647
+
648
+ def test_unicode_column_name
649
+ Weird.reset_column_information
650
+ weird = Weird.create(:なまえ => 'たこ焼き仮面')
651
+ assert_equal 'たこ焼き仮面', weird.なまえ
652
+ end
653
+
654
+ unless current_adapter?(:PostgreSQLAdapter)
655
+ def test_respect_internal_encoding
656
+ old_default_internal = Encoding.default_internal
657
+ silence_warnings { Encoding.default_internal = "EUC-JP" }
658
+
659
+ Weird.reset_column_information
660
+
661
+ assert_equal ["EUC-JP"], Weird.columns.map {|c| c.name.encoding.name }.uniq
662
+ ensure
663
+ silence_warnings { Encoding.default_internal = old_default_internal }
664
+ Weird.reset_column_information
665
+ end
666
+ end
667
+
668
+ def test_non_valid_identifier_column_name
669
+ weird = Weird.create('a$b' => 'value')
670
+ weird.reload
671
+ assert_equal 'value', weird.send('a$b')
672
+ assert_equal 'value', weird.read_attribute('a$b')
673
+
674
+ weird.update_columns('a$b' => 'value2')
675
+ weird.reload
676
+ assert_equal 'value2', weird.send('a$b')
677
+ assert_equal 'value2', weird.read_attribute('a$b')
678
+ end
679
+
680
+ def test_group_weirds_by_from
681
+ Weird.create('a$b' => 'value', :from => 'aaron')
682
+ count = Weird.group(Weird.arel_table[:from]).count
683
+ assert_equal 1, count['aaron']
684
+ end
685
+
686
+ def test_attributes_on_dummy_time
687
+ # Oracle does not have a TIME datatype.
688
+ return true if current_adapter?(:OracleAdapter)
689
+
690
+ with_timezone_config default: :local do
691
+ attributes = {
692
+ "bonus_time" => "5:42:00AM"
693
+ }
694
+ topic = Topic.find(1)
695
+ topic.attributes = attributes
696
+ assert_equal Time.local(2000, 1, 1, 5, 42, 0), topic.bonus_time
697
+ end
698
+ end
699
+
700
+ def test_attributes_on_dummy_time_with_invalid_time
701
+ # Oracle does not have a TIME datatype.
702
+ return true if current_adapter?(:OracleAdapter)
703
+
704
+ attributes = {
705
+ "bonus_time" => "not a time"
706
+ }
707
+ topic = Topic.find(1)
708
+ topic.attributes = attributes
709
+ assert_nil topic.bonus_time
710
+ end
711
+
712
+ def test_boolean
713
+ b_nil = Boolean.create({ "value" => nil })
714
+ nil_id = b_nil.id
715
+ b_false = Boolean.create({ "value" => false })
716
+ false_id = b_false.id
717
+ b_true = Boolean.create({ "value" => true })
718
+ true_id = b_true.id
719
+
720
+ b_nil = Boolean.find(nil_id)
721
+ assert_nil b_nil.value
722
+ b_false = Boolean.find(false_id)
723
+ assert !b_false.value?
724
+ b_true = Boolean.find(true_id)
725
+ assert b_true.value?
726
+ end
727
+
728
+ def test_boolean_without_questionmark
729
+ b_true = Boolean.create({ "value" => true })
730
+ true_id = b_true.id
731
+
732
+ subclass = Class.new(Boolean).find true_id
733
+ superclass = Boolean.find true_id
734
+
735
+ assert_equal superclass.read_attribute(:has_fun), subclass.read_attribute(:has_fun)
736
+ end
737
+
738
+ def test_boolean_cast_from_string
739
+ b_blank = Boolean.create({ "value" => "" })
740
+ blank_id = b_blank.id
741
+ b_false = Boolean.create({ "value" => "0" })
742
+ false_id = b_false.id
743
+ b_true = Boolean.create({ "value" => "1" })
744
+ true_id = b_true.id
745
+
746
+ b_blank = Boolean.find(blank_id)
747
+ assert_nil b_blank.value
748
+ b_false = Boolean.find(false_id)
749
+ assert !b_false.value?
750
+ b_true = Boolean.find(true_id)
751
+ assert b_true.value?
752
+ end
753
+
754
+ def test_new_record_returns_boolean
755
+ assert_equal false, Topic.new.persisted?
756
+ assert_equal true, Topic.find(1).persisted?
757
+ end
758
+
759
+ def test_dup
760
+ topic = Topic.find(1)
761
+ duped_topic = nil
762
+ assert_nothing_raised { duped_topic = topic.dup }
763
+ assert_equal topic.title, duped_topic.title
764
+ assert !duped_topic.persisted?
765
+
766
+ # test if the attributes have been duped
767
+ topic.title = "a"
768
+ duped_topic.title = "b"
769
+ assert_equal "a", topic.title
770
+ assert_equal "b", duped_topic.title
771
+
772
+ # test if the attribute values have been duped
773
+ duped_topic = topic.dup
774
+ duped_topic.title.replace "c"
775
+ assert_equal "a", topic.title
776
+
777
+ # test if attributes set as part of after_initialize are duped correctly
778
+ assert_equal topic.author_email_address, duped_topic.author_email_address
779
+
780
+ # test if saved clone object differs from original
781
+ duped_topic.save
782
+ assert duped_topic.persisted?
783
+ assert_not_equal duped_topic.id, topic.id
784
+
785
+ duped_topic.reload
786
+ assert_equal("c", duped_topic.title)
787
+ end
788
+
789
+ DeveloperSalary = Struct.new(:amount)
790
+ def test_dup_with_aggregate_of_same_name_as_attribute
791
+ developer_with_aggregate = Class.new(ActiveRecord::Base) do
792
+ self.table_name = 'developers'
793
+ composed_of :salary, :class_name => 'BasicsTest::DeveloperSalary', :mapping => [%w(salary amount)]
794
+ end
795
+
796
+ dev = developer_with_aggregate.find(1)
797
+ assert_kind_of DeveloperSalary, dev.salary
798
+
799
+ dup = nil
800
+ assert_nothing_raised { dup = dev.dup }
801
+ assert_kind_of DeveloperSalary, dup.salary
802
+ assert_equal dev.salary.amount, dup.salary.amount
803
+ assert !dup.persisted?
804
+
805
+ # test if the attributes have been dupd
806
+ original_amount = dup.salary.amount
807
+ dev.salary.amount = 1
808
+ assert_equal original_amount, dup.salary.amount
809
+
810
+ assert dup.save
811
+ assert dup.persisted?
812
+ assert_not_equal dup.id, dev.id
813
+ end
814
+
815
+ def test_dup_does_not_copy_associations
816
+ author = authors(:david)
817
+ assert_not_equal [], author.posts
818
+ author.send(:clear_association_cache)
819
+
820
+ author_dup = author.dup
821
+ assert_equal [], author_dup.posts
822
+ end
823
+
824
+ def test_clone_preserves_subtype
825
+ clone = nil
826
+ assert_nothing_raised { clone = Company.find(3).clone }
827
+ assert_kind_of Client, clone
828
+ end
829
+
830
+ def test_clone_of_new_object_with_defaults
831
+ developer = Developer.new
832
+ assert !developer.name_changed?
833
+ assert !developer.salary_changed?
834
+
835
+ cloned_developer = developer.clone
836
+ assert !cloned_developer.name_changed?
837
+ assert !cloned_developer.salary_changed?
838
+ end
839
+
840
+ def test_clone_of_new_object_marks_attributes_as_dirty
841
+ developer = Developer.new :name => 'Bjorn', :salary => 100000
842
+ assert developer.name_changed?
843
+ assert developer.salary_changed?
844
+
845
+ cloned_developer = developer.clone
846
+ assert cloned_developer.name_changed?
847
+ assert cloned_developer.salary_changed?
848
+ end
849
+
850
+ def test_clone_of_new_object_marks_as_dirty_only_changed_attributes
851
+ developer = Developer.new :name => 'Bjorn'
852
+ assert developer.name_changed? # obviously
853
+ assert !developer.salary_changed? # attribute has non-nil default value, so treated as not changed
854
+
855
+ cloned_developer = developer.clone
856
+ assert cloned_developer.name_changed?
857
+ assert !cloned_developer.salary_changed? # ... and cloned instance should behave same
858
+ end
859
+
860
+ def test_dup_of_saved_object_marks_attributes_as_dirty
861
+ developer = Developer.create! :name => 'Bjorn', :salary => 100000
862
+ assert !developer.name_changed?
863
+ assert !developer.salary_changed?
864
+
865
+ cloned_developer = developer.dup
866
+ assert cloned_developer.name_changed? # both attributes differ from defaults
867
+ assert cloned_developer.salary_changed?
868
+ end
869
+
870
+ def test_dup_of_saved_object_marks_as_dirty_only_changed_attributes
871
+ developer = Developer.create! :name => 'Bjorn'
872
+ assert !developer.name_changed? # both attributes of saved object should be treated as not changed
873
+ assert !developer.salary_changed?
874
+
875
+ cloned_developer = developer.dup
876
+ assert cloned_developer.name_changed? # ... but on cloned object should be
877
+ assert !cloned_developer.salary_changed? # ... BUT salary has non-nil default which should be treated as not changed on cloned instance
878
+ end
879
+
880
+ def test_bignum
881
+ company = Company.find(1)
882
+ company.rating = 2147483647
883
+ company.save
884
+ company = Company.find(1)
885
+ assert_equal 2147483647, company.rating
886
+ end
887
+
888
+ # TODO: extend defaults tests to other databases!
889
+ if current_adapter?(:PostgreSQLAdapter)
890
+ def test_default
891
+ with_timezone_config default: :local do
892
+ default = Default.new
893
+
894
+ # fixed dates / times
895
+ assert_equal Date.new(2004, 1, 1), default.fixed_date
896
+ assert_equal Time.local(2004, 1,1,0,0,0,0), default.fixed_time
897
+
898
+ # char types
899
+ assert_equal 'Y', default.char1
900
+ assert_equal 'a varchar field', default.char2
901
+ assert_equal 'a text field', default.char3
902
+ end
903
+ end
904
+
905
+ class Geometric < ActiveRecord::Base; end
906
+ def test_geometric_content
907
+
908
+ # accepted format notes:
909
+ # ()'s aren't required
910
+ # values can be a mix of float or integer
911
+
912
+ g = Geometric.new(
913
+ :a_point => '(5.0, 6.1)',
914
+ #:a_line => '((2.0, 3), (5.5, 7.0))' # line type is currently unsupported in postgresql
915
+ :a_line_segment => '(2.0, 3), (5.5, 7.0)',
916
+ :a_box => '2.0, 3, 5.5, 7.0',
917
+ :a_path => '[(2.0, 3), (5.5, 7.0), (8.5, 11.0)]', # [ ] is an open path
918
+ :a_polygon => '((2.0, 3), (5.5, 7.0), (8.5, 11.0))',
919
+ :a_circle => '<(5.3, 10.4), 2>'
920
+ )
921
+
922
+ assert g.save
923
+
924
+ # Reload and check that we have all the geometric attributes.
925
+ h = Geometric.find(g.id)
926
+
927
+ assert_equal [5.0, 6.1], h.a_point
928
+ assert_equal '[(2,3),(5.5,7)]', h.a_line_segment
929
+ assert_equal '(5.5,7),(2,3)', h.a_box # reordered to store upper right corner then bottom left corner
930
+ assert_equal '[(2,3),(5.5,7),(8.5,11)]', h.a_path
931
+ assert_equal '((2,3),(5.5,7),(8.5,11))', h.a_polygon
932
+ assert_equal '<(5.3,10.4),2>', h.a_circle
933
+
934
+ # use a geometric function to test for an open path
935
+ objs = Geometric.find_by_sql ["select isopen(a_path) from geometrics where id = ?", g.id]
936
+
937
+ assert_equal true, objs[0].isopen
938
+
939
+ # test alternate formats when defining the geometric types
940
+
941
+ g = Geometric.new(
942
+ :a_point => '5.0, 6.1',
943
+ #:a_line => '((2.0, 3), (5.5, 7.0))' # line type is currently unsupported in postgresql
944
+ :a_line_segment => '((2.0, 3), (5.5, 7.0))',
945
+ :a_box => '(2.0, 3), (5.5, 7.0)',
946
+ :a_path => '((2.0, 3), (5.5, 7.0), (8.5, 11.0))', # ( ) is a closed path
947
+ :a_polygon => '2.0, 3, 5.5, 7.0, 8.5, 11.0',
948
+ :a_circle => '((5.3, 10.4), 2)'
949
+ )
950
+
951
+ assert g.save
952
+
953
+ # Reload and check that we have all the geometric attributes.
954
+ h = Geometric.find(g.id)
955
+
956
+ assert_equal [5.0, 6.1], h.a_point
957
+ assert_equal '[(2,3),(5.5,7)]', h.a_line_segment
958
+ assert_equal '(5.5,7),(2,3)', h.a_box # reordered to store upper right corner then bottom left corner
959
+ assert_equal '((2,3),(5.5,7),(8.5,11))', h.a_path
960
+ assert_equal '((2,3),(5.5,7),(8.5,11))', h.a_polygon
961
+ assert_equal '<(5.3,10.4),2>', h.a_circle
962
+
963
+ # use a geometric function to test for an closed path
964
+ objs = Geometric.find_by_sql ["select isclosed(a_path) from geometrics where id = ?", g.id]
965
+
966
+ assert_equal true, objs[0].isclosed
967
+
968
+ # test native ruby formats when defining the geometric types
969
+ g = Geometric.new(
970
+ :a_point => [5.0, 6.1],
971
+ #:a_line => '((2.0, 3), (5.5, 7.0))' # line type is currently unsupported in postgresql
972
+ :a_line_segment => '((2.0, 3), (5.5, 7.0))',
973
+ :a_box => '(2.0, 3), (5.5, 7.0)',
974
+ :a_path => '((2.0, 3), (5.5, 7.0), (8.5, 11.0))', # ( ) is a closed path
975
+ :a_polygon => '2.0, 3, 5.5, 7.0, 8.5, 11.0',
976
+ :a_circle => '((5.3, 10.4), 2)'
977
+ )
978
+
979
+ assert g.save
980
+
981
+ # Reload and check that we have all the geometric attributes.
982
+ h = Geometric.find(g.id)
983
+
984
+ assert_equal [5.0, 6.1], h.a_point
985
+ assert_equal '[(2,3),(5.5,7)]', h.a_line_segment
986
+ assert_equal '(5.5,7),(2,3)', h.a_box # reordered to store upper right corner then bottom left corner
987
+ assert_equal '((2,3),(5.5,7),(8.5,11))', h.a_path
988
+ assert_equal '((2,3),(5.5,7),(8.5,11))', h.a_polygon
989
+ assert_equal '<(5.3,10.4),2>', h.a_circle
990
+ end
991
+ end
992
+
993
+ class NumericData < ActiveRecord::Base
994
+ self.table_name = 'numeric_data'
995
+
996
+ attribute :my_house_population, Type::Integer.new
997
+ attribute :atoms_in_universe, Type::Integer.new
998
+ end
999
+
1000
+ def test_big_decimal_conditions
1001
+ m = NumericData.new(
1002
+ :bank_balance => 1586.43,
1003
+ :big_bank_balance => BigDecimal("1000234000567.95"),
1004
+ :world_population => 6000000000,
1005
+ :my_house_population => 3
1006
+ )
1007
+ assert m.save
1008
+ assert_equal 0, NumericData.where("bank_balance > ?", 2000.0).count
1009
+ end
1010
+
1011
+ def test_numeric_fields
1012
+ m = NumericData.new(
1013
+ :bank_balance => 1586.43,
1014
+ :big_bank_balance => BigDecimal("1000234000567.95"),
1015
+ :world_population => 6000000000,
1016
+ :my_house_population => 3
1017
+ )
1018
+ assert m.save
1019
+
1020
+ m1 = NumericData.find(m.id)
1021
+ assert_not_nil m1
1022
+
1023
+ # As with migration_test.rb, we should make world_population >= 2**62
1024
+ # to cover 64-bit platforms and test it is a Bignum, but the main thing
1025
+ # is that it's an Integer.
1026
+ unless current_adapter?(:IBM_DBAdapter)
1027
+ assert_kind_of Integer, m1.world_population
1028
+ else
1029
+ assert_kind_of BigDecimal, m1.world_population
1030
+ end
1031
+ assert_equal 6000000000, m1.world_population
1032
+
1033
+ unless current_adapter?(:IBM_DBAdapter)
1034
+ assert_kind_of Fixnum, m1.my_house_population
1035
+ else
1036
+ assert_kind_of BigDecimal, m1.my_house_population
1037
+ end
1038
+ assert_equal 3, m1.my_house_population
1039
+
1040
+ assert_kind_of BigDecimal, m1.bank_balance
1041
+ assert_equal BigDecimal("1586.43"), m1.bank_balance
1042
+
1043
+ assert_kind_of BigDecimal, m1.big_bank_balance
1044
+ assert_equal BigDecimal("1000234000567.95"), m1.big_bank_balance
1045
+ end
1046
+
1047
+ def test_auto_id
1048
+ auto = AutoId.new
1049
+ auto.save
1050
+ assert(auto.id > 0)
1051
+ end
1052
+
1053
+ def test_sql_injection_via_find
1054
+ assert_raise(ActiveRecord::RecordNotFound, ActiveRecord::StatementInvalid) do
1055
+ Topic.find("123456 OR id > 0")
1056
+ end
1057
+ end
1058
+
1059
+ def test_column_name_properly_quoted
1060
+ col_record = ColumnName.new
1061
+ col_record.references = 40
1062
+ assert col_record.save
1063
+ col_record.references = 41
1064
+ assert col_record.save
1065
+ assert_not_nil c2 = ColumnName.find(col_record.id)
1066
+ assert_equal(41, c2.references)
1067
+ end
1068
+
1069
+ def test_quoting_arrays
1070
+ replies = Reply.all.merge!(:where => [ "id IN (?)", topics(:first).replies.collect(&:id) ]).to_a
1071
+ assert_equal topics(:first).replies.size, replies.size
1072
+
1073
+ replies = Reply.all.merge!(:where => [ "id IN (?)", [] ]).to_a
1074
+ assert_equal 0, replies.size
1075
+ end
1076
+
1077
+ def test_quote
1078
+ author_name = "\\ \001 ' \n \\n \""
1079
+ topic = Topic.create('author_name' => author_name)
1080
+ assert_equal author_name, Topic.find(topic.id).author_name
1081
+ end
1082
+
1083
+ def test_toggle_attribute
1084
+ assert !topics(:first).approved?
1085
+ topics(:first).toggle!(:approved)
1086
+ assert topics(:first).approved?
1087
+ topic = topics(:first)
1088
+ topic.toggle(:approved)
1089
+ assert !topic.approved?
1090
+ topic.reload
1091
+ assert topic.approved?
1092
+ end
1093
+
1094
+ def test_reload
1095
+ t1 = Topic.find(1)
1096
+ t2 = Topic.find(1)
1097
+ t1.title = "something else"
1098
+ t1.save
1099
+ t2.reload
1100
+ assert_equal t1.title, t2.title
1101
+ end
1102
+
1103
+ def test_reload_with_exclusive_scope
1104
+ dev = DeveloperCalledDavid.first
1105
+ dev.update!(name: "NotDavid" )
1106
+ assert_equal dev, dev.reload
1107
+ end
1108
+
1109
+ def test_switching_between_table_name
1110
+ assert_difference("GoodJoke.count") do
1111
+ Joke.table_name = "cold_jokes"
1112
+ Joke.create
1113
+
1114
+ Joke.table_name = "funny_jokes"
1115
+ Joke.create
1116
+ end
1117
+ end
1118
+
1119
+ def test_clear_cash_when_setting_table_name
1120
+ Joke.table_name = "cold_jokes"
1121
+ before_columns = Joke.columns
1122
+ before_seq = Joke.sequence_name
1123
+
1124
+ Joke.table_name = "funny_jokes"
1125
+ after_columns = Joke.columns
1126
+ after_seq = Joke.sequence_name
1127
+
1128
+ assert_not_equal before_columns, after_columns
1129
+ assert_not_equal before_seq, after_seq unless before_seq.nil? && after_seq.nil?
1130
+ end
1131
+
1132
+ def test_dont_clear_sequence_name_when_setting_explicitly
1133
+ Joke.sequence_name = "black_jokes_seq"
1134
+ Joke.table_name = "cold_jokes"
1135
+ before_seq = Joke.sequence_name
1136
+
1137
+ Joke.table_name = "funny_jokes"
1138
+ after_seq = Joke.sequence_name
1139
+
1140
+ assert_equal before_seq, after_seq unless before_seq.nil? && after_seq.nil?
1141
+ ensure
1142
+ Joke.reset_sequence_name
1143
+ end
1144
+
1145
+ def test_dont_clear_inheritance_column_when_setting_explicitly
1146
+ Joke.inheritance_column = "my_type"
1147
+ before_inherit = Joke.inheritance_column
1148
+
1149
+ Joke.reset_column_information
1150
+ after_inherit = Joke.inheritance_column
1151
+
1152
+ assert_equal before_inherit, after_inherit unless before_inherit.blank? && after_inherit.blank?
1153
+ end
1154
+
1155
+ def test_set_table_name_symbol_converted_to_string
1156
+ Joke.table_name = :cold_jokes
1157
+ assert_equal 'cold_jokes', Joke.table_name
1158
+ end
1159
+
1160
+ def test_quoted_table_name_after_set_table_name
1161
+ klass = Class.new(ActiveRecord::Base)
1162
+
1163
+ klass.table_name = "foo"
1164
+ assert_equal "foo", klass.table_name
1165
+ assert_equal klass.connection.quote_table_name("foo"), klass.quoted_table_name
1166
+
1167
+ klass.table_name = "bar"
1168
+ assert_equal "bar", klass.table_name
1169
+ assert_equal klass.connection.quote_table_name("bar"), klass.quoted_table_name
1170
+ end
1171
+
1172
+ def test_set_table_name_with_inheritance
1173
+ k = Class.new( ActiveRecord::Base )
1174
+ def k.name; "Foo"; end
1175
+ def k.table_name; super + "ks"; end
1176
+ assert_equal "foosks", k.table_name
1177
+ end
1178
+
1179
+ def test_sequence_name_with_abstract_class
1180
+ ak = Class.new(ActiveRecord::Base)
1181
+ ak.abstract_class = true
1182
+ k = Class.new(ak)
1183
+ k.table_name = "projects"
1184
+ orig_name = k.sequence_name
1185
+ skip "sequences not supported by db" unless orig_name
1186
+ assert_equal k.reset_sequence_name, orig_name
1187
+ end
1188
+
1189
+ def test_count_with_join
1190
+ res = Post.count_by_sql "SELECT COUNT(*) FROM posts LEFT JOIN comments ON posts.id=comments.post_id WHERE posts.#{QUOTED_TYPE} = 'Post'"
1191
+
1192
+ res2 = Post.where("posts.#{QUOTED_TYPE} = 'Post'").joins("LEFT JOIN comments ON posts.id=comments.post_id").count
1193
+ assert_equal res, res2
1194
+
1195
+ res3 = nil
1196
+ assert_nothing_raised do
1197
+ res3 = Post.where("posts.#{QUOTED_TYPE} = 'Post'").joins("LEFT JOIN comments ON posts.id=comments.post_id").count
1198
+ end
1199
+ assert_equal res, res3
1200
+
1201
+ res4 = Post.count_by_sql "SELECT COUNT(p.id) FROM posts p, comments co WHERE p.#{QUOTED_TYPE} = 'Post' AND p.id=co.post_id"
1202
+ res5 = nil
1203
+ assert_nothing_raised do
1204
+ res5 = Post.where("p.#{QUOTED_TYPE} = 'Post' AND p.id=co.post_id").joins("p, comments co").select("p.id").count
1205
+ end
1206
+
1207
+ assert_equal res4, res5
1208
+
1209
+ res6 = Post.count_by_sql "SELECT COUNT(DISTINCT p.id) FROM posts p, comments co WHERE p.#{QUOTED_TYPE} = 'Post' AND p.id=co.post_id"
1210
+ res7 = nil
1211
+ assert_nothing_raised do
1212
+ res7 = Post.where("p.#{QUOTED_TYPE} = 'Post' AND p.id=co.post_id").joins("p, comments co").select("p.id").distinct.count
1213
+ end
1214
+ assert_equal res6, res7
1215
+ end
1216
+
1217
+ def test_no_limit_offset
1218
+ assert_nothing_raised do
1219
+ Developer.all.merge!(:offset => 2).to_a
1220
+ end
1221
+ end
1222
+
1223
+ def test_find_last
1224
+ last = Developer.last
1225
+ assert_equal last, Developer.all.merge!(:order => 'id desc').first
1226
+ end
1227
+
1228
+ def test_last
1229
+ assert_equal Developer.all.merge!(:order => 'id desc').first, Developer.last
1230
+ end
1231
+
1232
+ def test_all
1233
+ developers = Developer.all
1234
+ assert_kind_of ActiveRecord::Relation, developers
1235
+ assert_equal Developer.all, developers
1236
+ end
1237
+
1238
+ def test_all_with_conditions
1239
+ assert_equal Developer.all.merge!(:order => 'id desc').to_a, Developer.order('id desc').to_a
1240
+ end
1241
+
1242
+ def test_find_ordered_last
1243
+ last = Developer.all.merge!(:order => 'developers.salary ASC').last
1244
+ assert_equal last, Developer.all.merge!(:order => 'developers.salary ASC').to_a.last
1245
+ end
1246
+
1247
+ def test_find_reverse_ordered_last
1248
+ last = Developer.all.merge!(:order => 'developers.salary DESC').last
1249
+ assert_equal last, Developer.all.merge!(:order => 'developers.salary DESC').to_a.last
1250
+ end
1251
+
1252
+ def test_find_multiple_ordered_last
1253
+ last = Developer.all.merge!(:order => 'developers.name, developers.salary DESC').last
1254
+ assert_equal last, Developer.all.merge!(:order => 'developers.name, developers.salary DESC').to_a.last
1255
+ end
1256
+
1257
+ def test_find_keeps_multiple_order_values
1258
+ combined = Developer.all.merge!(:order => 'developers.name, developers.salary').to_a
1259
+ assert_equal combined, Developer.all.merge!(:order => ['developers.name', 'developers.salary']).to_a
1260
+ end
1261
+
1262
+ def test_find_keeps_multiple_group_values
1263
+ combined = Developer.all.merge!(:group => 'developers.name, developers.salary, developers.id, developers.created_at, developers.updated_at, developers.created_on, developers.updated_on').to_a
1264
+ assert_equal combined, Developer.all.merge!(:group => ['developers.name', 'developers.salary', 'developers.id', 'developers.created_at', 'developers.updated_at', 'developers.created_on', 'developers.updated_on']).to_a
1265
+ end
1266
+
1267
+ def test_find_symbol_ordered_last
1268
+ last = Developer.all.merge!(:order => :salary).last
1269
+ assert_equal last, Developer.all.merge!(:order => :salary).to_a.last
1270
+ end
1271
+
1272
+ def test_abstract_class
1273
+ assert !ActiveRecord::Base.abstract_class?
1274
+ assert LoosePerson.abstract_class?
1275
+ assert !LooseDescendant.abstract_class?
1276
+ end
1277
+
1278
+ def test_abstract_class_table_name
1279
+ assert_nil AbstractCompany.table_name
1280
+ end
1281
+
1282
+ def test_descends_from_active_record
1283
+ assert !ActiveRecord::Base.descends_from_active_record?
1284
+
1285
+ # Abstract subclass of AR::Base.
1286
+ assert LoosePerson.descends_from_active_record?
1287
+
1288
+ # Concrete subclass of an abstract class.
1289
+ assert LooseDescendant.descends_from_active_record?
1290
+
1291
+ # Concrete subclass of AR::Base.
1292
+ assert TightPerson.descends_from_active_record?
1293
+
1294
+ # Concrete subclass of a concrete class but has no type column.
1295
+ assert TightDescendant.descends_from_active_record?
1296
+
1297
+ # Concrete subclass of AR::Base.
1298
+ assert Post.descends_from_active_record?
1299
+
1300
+ # Abstract subclass of a concrete class which has a type column.
1301
+ # This is pathological, as you'll never have Sub < Abstract < Concrete.
1302
+ assert !StiPost.descends_from_active_record?
1303
+
1304
+ # Concrete subclasses an abstract class which has a type column.
1305
+ assert !SubStiPost.descends_from_active_record?
1306
+ end
1307
+
1308
+ def test_find_on_abstract_base_class_doesnt_use_type_condition
1309
+ old_class = LooseDescendant
1310
+ Object.send :remove_const, :LooseDescendant
1311
+
1312
+ descendant = old_class.create! :first_name => 'bob'
1313
+ assert_not_nil LoosePerson.find(descendant.id), "Should have found instance of LooseDescendant when finding abstract LoosePerson: #{descendant.inspect}"
1314
+ ensure
1315
+ unless Object.const_defined?(:LooseDescendant)
1316
+ Object.const_set :LooseDescendant, old_class
1317
+ end
1318
+ end
1319
+
1320
+ def test_assert_queries
1321
+ query = lambda { ActiveRecord::Base.connection.execute 'select count(*) from developers' }
1322
+ assert_queries(2) { 2.times { query.call } }
1323
+ assert_queries 1, &query
1324
+ assert_no_queries { assert true }
1325
+ end
1326
+
1327
+ def test_benchmark_with_log_level
1328
+ original_logger = ActiveRecord::Base.logger
1329
+ log = StringIO.new
1330
+ ActiveRecord::Base.logger = ActiveSupport::Logger.new(log)
1331
+ ActiveRecord::Base.logger.level = Logger::WARN
1332
+ ActiveRecord::Base.benchmark("Debug Topic Count", :level => :debug) { Topic.count }
1333
+ ActiveRecord::Base.benchmark("Warn Topic Count", :level => :warn) { Topic.count }
1334
+ ActiveRecord::Base.benchmark("Error Topic Count", :level => :error) { Topic.count }
1335
+ assert_no_match(/Debug Topic Count/, log.string)
1336
+ assert_match(/Warn Topic Count/, log.string)
1337
+ assert_match(/Error Topic Count/, log.string)
1338
+ ensure
1339
+ ActiveRecord::Base.logger = original_logger
1340
+ end
1341
+
1342
+ def test_benchmark_with_use_silence
1343
+ original_logger = ActiveRecord::Base.logger
1344
+ log = StringIO.new
1345
+ ActiveRecord::Base.logger = ActiveSupport::Logger.new(log)
1346
+ ActiveRecord::Base.benchmark("Logging", :level => :debug, :silence => false) { ActiveRecord::Base.logger.debug "Quiet" }
1347
+ assert_match(/Quiet/, log.string)
1348
+ ensure
1349
+ ActiveRecord::Base.logger = original_logger
1350
+ end
1351
+
1352
+ def test_compute_type_success
1353
+ assert_equal Author, ActiveRecord::Base.send(:compute_type, 'Author')
1354
+ end
1355
+
1356
+ def test_compute_type_nonexistent_constant
1357
+ e = assert_raises NameError do
1358
+ ActiveRecord::Base.send :compute_type, 'NonexistentModel'
1359
+ end
1360
+ assert_equal 'uninitialized constant ActiveRecord::Base::NonexistentModel', e.message
1361
+ assert_equal 'ActiveRecord::Base::NonexistentModel', e.name
1362
+ end
1363
+
1364
+ def test_compute_type_no_method_error
1365
+ ActiveSupport::Dependencies.stubs(:safe_constantize).raises(NoMethodError)
1366
+ assert_raises NoMethodError do
1367
+ ActiveRecord::Base.send :compute_type, 'InvalidModel'
1368
+ end
1369
+ end
1370
+
1371
+ def test_compute_type_on_undefined_method
1372
+ error = nil
1373
+ begin
1374
+ Class.new(Author) do
1375
+ alias_method :foo, :bar
1376
+ end
1377
+ rescue => e
1378
+ error = e
1379
+ end
1380
+
1381
+ ActiveSupport::Dependencies.stubs(:safe_constantize).raises(e)
1382
+
1383
+ exception = assert_raises NameError do
1384
+ ActiveRecord::Base.send :compute_type, 'InvalidModel'
1385
+ end
1386
+ assert_equal error.message, exception.message
1387
+ end
1388
+
1389
+ def test_compute_type_argument_error
1390
+ ActiveSupport::Dependencies.stubs(:safe_constantize).raises(ArgumentError)
1391
+ assert_raises ArgumentError do
1392
+ ActiveRecord::Base.send :compute_type, 'InvalidModel'
1393
+ end
1394
+ end
1395
+
1396
+ def test_clear_cache!
1397
+ # preheat cache
1398
+ c1 = Post.connection.schema_cache.columns('posts')
1399
+ ActiveRecord::Base.clear_cache!
1400
+ c2 = Post.connection.schema_cache.columns('posts')
1401
+ c1.each_with_index do |v, i|
1402
+ assert_not_same v, c2[i]
1403
+ end
1404
+ assert_equal c1, c2
1405
+ end
1406
+
1407
+ def test_current_scope_is_reset
1408
+ Object.const_set :UnloadablePost, Class.new(ActiveRecord::Base)
1409
+ UnloadablePost.send(:current_scope=, UnloadablePost.all)
1410
+
1411
+ UnloadablePost.unloadable
1412
+ assert_not_nil ActiveRecord::Scoping::ScopeRegistry.value_for(:current_scope, "UnloadablePost")
1413
+ ActiveSupport::Dependencies.remove_unloadable_constants!
1414
+ assert_nil ActiveRecord::Scoping::ScopeRegistry.value_for(:current_scope, "UnloadablePost")
1415
+ ensure
1416
+ Object.class_eval{ remove_const :UnloadablePost } if defined?(UnloadablePost)
1417
+ end
1418
+
1419
+ def test_marshal_round_trip
1420
+ expected = posts(:welcome)
1421
+ marshalled = Marshal.dump(expected)
1422
+ actual = Marshal.load(marshalled)
1423
+
1424
+ assert_equal expected.attributes, actual.attributes
1425
+ end
1426
+
1427
+ def test_marshal_new_record_round_trip
1428
+ marshalled = Marshal.dump(Post.new)
1429
+ post = Marshal.load(marshalled)
1430
+
1431
+ assert post.new_record?, "should be a new record"
1432
+ end
1433
+
1434
+ def test_marshalling_with_associations
1435
+ post = Post.new
1436
+ post.comments.build
1437
+
1438
+ marshalled = Marshal.dump(post)
1439
+ post = Marshal.load(marshalled)
1440
+
1441
+ assert_equal 1, post.comments.length
1442
+ end
1443
+
1444
+ if Process.respond_to?(:fork) && !in_memory_db?
1445
+ def test_marshal_between_processes
1446
+ # Define a new model to ensure there are no caches
1447
+ if self.class.const_defined?("Post", false)
1448
+ flunk "there should be no post constant"
1449
+ end
1450
+
1451
+ self.class.const_set("Post", Class.new(ActiveRecord::Base) {
1452
+ has_many :comments
1453
+ })
1454
+
1455
+ rd, wr = IO.pipe
1456
+ rd.binmode
1457
+ wr.binmode
1458
+
1459
+ ActiveRecord::Base.connection_handler.clear_all_connections!
1460
+
1461
+ fork do
1462
+ rd.close
1463
+ post = Post.new
1464
+ post.comments.build
1465
+ wr.write Marshal.dump(post)
1466
+ wr.close
1467
+ end
1468
+
1469
+ wr.close
1470
+ assert Marshal.load rd.read
1471
+ rd.close
1472
+ end
1473
+ end
1474
+
1475
+ def test_marshalling_new_record_round_trip_with_associations
1476
+ post = Post.new
1477
+ post.comments.build
1478
+
1479
+ post = Marshal.load(Marshal.dump(post))
1480
+
1481
+ assert post.new_record?, "should be a new record"
1482
+ end
1483
+
1484
+ def test_attribute_names
1485
+ assert_equal ["id", "type", "firm_id", "firm_name", "name", "client_of", "rating", "account_id", "description"],
1486
+ Company.attribute_names
1487
+ end
1488
+
1489
+ def test_attribute_names_on_table_not_exists
1490
+ assert_equal [], NonExistentTable.attribute_names
1491
+ end
1492
+
1493
+ def test_attribute_names_on_abstract_class
1494
+ assert_equal [], AbstractCompany.attribute_names
1495
+ end
1496
+
1497
+ def test_touch_should_raise_error_on_a_new_object
1498
+ company = Company.new(:rating => 1, :name => "37signals", :firm_name => "37signals")
1499
+ assert_raises(ActiveRecord::ActiveRecordError) do
1500
+ company.touch :updated_at
1501
+ end
1502
+ end
1503
+
1504
+ def test_uniq_delegates_to_scoped
1505
+ scope = stub
1506
+ Bird.stubs(:all).returns(mock(:uniq => scope))
1507
+ assert_equal scope, Bird.uniq
1508
+ end
1509
+
1510
+ def test_distinct_delegates_to_scoped
1511
+ scope = stub
1512
+ Bird.stubs(:all).returns(mock(:distinct => scope))
1513
+ assert_equal scope, Bird.distinct
1514
+ end
1515
+
1516
+ def test_table_name_with_2_abstract_subclasses
1517
+ assert_equal "photos", Photo.table_name
1518
+ end
1519
+
1520
+ def test_column_types_typecast
1521
+ topic = Topic.first
1522
+ assert_not_equal 't.lo', topic.author_name
1523
+
1524
+ attrs = topic.attributes.dup
1525
+ attrs.delete 'id'
1526
+
1527
+ typecast = Class.new(ActiveRecord::Type::Value) {
1528
+ def type_cast value
1529
+ "t.lo"
1530
+ end
1531
+ }
1532
+
1533
+ types = { 'author_name' => typecast.new }
1534
+ topic = Topic.instantiate(attrs, types)
1535
+
1536
+ assert_equal 't.lo', topic.author_name
1537
+ end
1538
+
1539
+ def test_typecasting_aliases
1540
+ assert_equal 10, Topic.select('10 as tenderlove').first.tenderlove
1541
+ end
1542
+
1543
+ def test_slice
1544
+ company = Company.new(:rating => 1, :name => "37signals", :firm_name => "37signals")
1545
+ hash = company.slice(:name, :rating, "arbitrary_method")
1546
+ assert_equal hash[:name], company.name
1547
+ assert_equal hash['name'], company.name
1548
+ assert_equal hash[:rating], company.rating
1549
+ assert_equal hash['arbitrary_method'], company.arbitrary_method
1550
+ assert_equal hash[:arbitrary_method], company.arbitrary_method
1551
+ assert_nil hash[:firm_name]
1552
+ assert_nil hash['firm_name']
1553
+ end
1554
+
1555
+ def test_default_values_are_deeply_dupped
1556
+ company = Company.new
1557
+ company.description << "foo"
1558
+ assert_equal "", Company.new.description
1559
+ end
1560
+
1561
+ test "scoped can take a values hash" do
1562
+ klass = Class.new(ActiveRecord::Base)
1563
+ assert_equal ['foo'], klass.all.merge!(select: 'foo').select_values
1564
+ end
1565
+
1566
+ test "connection_handler can be overridden" do
1567
+ klass = Class.new(ActiveRecord::Base)
1568
+ orig_handler = klass.connection_handler
1569
+ new_handler = ActiveRecord::ConnectionAdapters::ConnectionHandler.new
1570
+ thread_connection_handler = nil
1571
+
1572
+ t = Thread.new do
1573
+ klass.connection_handler = new_handler
1574
+ thread_connection_handler = klass.connection_handler
1575
+ end
1576
+ t.join
1577
+
1578
+ assert_equal klass.connection_handler, orig_handler
1579
+ assert_equal thread_connection_handler, new_handler
1580
+ end
1581
+
1582
+ test "new threads get default the default connection handler" do
1583
+ klass = Class.new(ActiveRecord::Base)
1584
+ orig_handler = klass.connection_handler
1585
+ handler = nil
1586
+
1587
+ t = Thread.new do
1588
+ handler = klass.connection_handler
1589
+ end
1590
+ t.join
1591
+
1592
+ assert_equal handler, orig_handler
1593
+ assert_equal klass.connection_handler, orig_handler
1594
+ assert_equal klass.default_connection_handler, orig_handler
1595
+ end
1596
+
1597
+ test "changing a connection handler in a main thread does not poison the other threads" do
1598
+ klass = Class.new(ActiveRecord::Base)
1599
+ orig_handler = klass.connection_handler
1600
+ new_handler = ActiveRecord::ConnectionAdapters::ConnectionHandler.new
1601
+ after_handler = nil
1602
+ latch1 = ActiveSupport::Concurrency::Latch.new
1603
+ latch2 = ActiveSupport::Concurrency::Latch.new
1604
+
1605
+ t = Thread.new do
1606
+ klass.connection_handler = new_handler
1607
+ latch1.release
1608
+ latch2.await
1609
+ after_handler = klass.connection_handler
1610
+ end
1611
+
1612
+ latch1.await
1613
+
1614
+ klass.connection_handler = orig_handler
1615
+ latch2.release
1616
+ t.join
1617
+
1618
+ assert_equal after_handler, new_handler
1619
+ assert_equal orig_handler, klass.connection_handler
1620
+ end
1621
+
1622
+ # Note: This is a performance optimization for Array#uniq and Hash#[] with
1623
+ # AR::Base objects. If the future has made this irrelevant, feel free to
1624
+ # delete this.
1625
+ test "records without an id have unique hashes" do
1626
+ assert_not_equal Post.new.hash, Post.new.hash
1627
+ end
1628
+
1629
+ test "resetting column information doesn't remove attribute methods" do
1630
+ topic = topics(:first)
1631
+
1632
+ assert_not topic.id_changed?
1633
+
1634
+ Topic.reset_column_information
1635
+
1636
+ assert_not topic.id_changed?
1637
+ end
1638
+ end