ibm_db 2.5.6-x86-mingw32

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. data/CHANGES +181 -0
  2. data/LICENSE +18 -0
  3. data/MANIFEST +14 -0
  4. data/ParameterizedQueries README +39 -0
  5. data/README +282 -0
  6. data/ext/Makefile.nt32 +181 -0
  7. data/ext/extconf.rb +66 -0
  8. data/ext/ibm_db.c +11166 -0
  9. data/ext/ruby_ibm_db.h +236 -0
  10. data/ext/ruby_ibm_db_cli.c +738 -0
  11. data/ext/ruby_ibm_db_cli.h +431 -0
  12. data/init.rb +42 -0
  13. data/lib/IBM_DB.rb +2 -0
  14. data/lib/active_record/connection_adapters/ibm_db_adapter.rb +2598 -0
  15. data/lib/active_record/connection_adapters/ibm_db_pstmt.rb +1965 -0
  16. data/lib/active_record/vendor/db2-i5-zOS.yaml +328 -0
  17. data/lib/mswin32/ibm_db.rb +1 -0
  18. data/lib/mswin32/rb18x/ibm_db.so +0 -0
  19. data/lib/mswin32/rb19x/ibm_db.so +0 -0
  20. data/test/cases/adapter_test.rb +202 -0
  21. data/test/cases/associations/belongs_to_associations_test.rb +486 -0
  22. data/test/cases/associations/cascaded_eager_loading_test.rb +183 -0
  23. data/test/cases/associations/eager_test.rb +862 -0
  24. data/test/cases/associations/has_and_belongs_to_many_associations_test.rb +917 -0
  25. data/test/cases/associations/has_many_through_associations_test.rb +461 -0
  26. data/test/cases/associations/join_model_test.rb +793 -0
  27. data/test/cases/attribute_methods_test.rb +621 -0
  28. data/test/cases/base_test.rb +1486 -0
  29. data/test/cases/calculations_test.rb +362 -0
  30. data/test/cases/finder_test.rb +1088 -0
  31. data/test/cases/fixtures_test.rb +684 -0
  32. data/test/cases/migration_test.rb +2014 -0
  33. data/test/cases/schema_dumper_test.rb +232 -0
  34. data/test/cases/validations/uniqueness_validation_test.rb +283 -0
  35. data/test/connections/native_ibm_db/connection.rb +42 -0
  36. data/test/ibm_db_test.rb +25 -0
  37. data/test/models/warehouse_thing.rb +5 -0
  38. data/test/schema/i5/ibm_db_specific_schema.rb +135 -0
  39. data/test/schema/ids/ibm_db_specific_schema.rb +138 -0
  40. data/test/schema/luw/ibm_db_specific_schema.rb +135 -0
  41. data/test/schema/schema.rb +647 -0
  42. data/test/schema/zOS/ibm_db_specific_schema.rb +206 -0
  43. metadata +123 -0
@@ -0,0 +1,362 @@
1
+ require "cases/helper"
2
+ require 'models/company'
3
+ require 'models/topic'
4
+ require 'models/edge'
5
+ require 'models/club'
6
+ require 'models/organization'
7
+
8
+ Company.has_many :accounts
9
+
10
+ class NumericData < ActiveRecord::Base
11
+ self.table_name = 'numeric_data'
12
+ end
13
+
14
+ class CalculationsTest < ActiveRecord::TestCase
15
+ fixtures :companies, :accounts, :topics
16
+
17
+ def test_should_sum_field
18
+ assert_equal 318, Account.sum(:credit_limit)
19
+ end
20
+
21
+ def test_should_average_field
22
+ value = Account.average(:credit_limit)
23
+ assert_equal 53.0, value
24
+ end
25
+
26
+ def test_should_return_nil_as_average
27
+ assert_nil NumericData.average(:bank_balance)
28
+ end
29
+
30
+ def test_type_cast_calculated_value_should_convert_db_averages_of_fixnum_class_to_decimal
31
+ assert_equal 0, NumericData.scoped.send(:type_cast_calculated_value, 0, nil, 'avg')
32
+ assert_equal 53.0, NumericData.scoped.send(:type_cast_calculated_value, 53, nil, 'avg')
33
+ end
34
+
35
+ def test_should_get_maximum_of_field
36
+ assert_equal 60, Account.maximum(:credit_limit)
37
+ end
38
+
39
+ def test_should_get_maximum_of_field_with_include
40
+ assert_equal 55, Account.maximum(:credit_limit, :include => :firm, :conditions => "companies.name != 'Summit'")
41
+ end
42
+
43
+ def test_should_get_maximum_of_field_with_scoped_include
44
+ Account.send :with_scope, :find => { :include => :firm, :conditions => "companies.name != 'Summit'" } do
45
+ assert_equal 55, Account.maximum(:credit_limit)
46
+ end
47
+ end
48
+
49
+ def test_should_get_minimum_of_field
50
+ assert_equal 50, Account.minimum(:credit_limit)
51
+ end
52
+
53
+ def test_should_group_by_field
54
+ c = Account.sum(:credit_limit, :group => :firm_id)
55
+ [1,6,2].each { |firm_id| assert c.keys.include?(firm_id) }
56
+ end
57
+
58
+ def test_should_group_by_summed_field
59
+ c = Account.sum(:credit_limit, :group => :firm_id)
60
+ assert_equal 50, c[1]
61
+ assert_equal 105, c[6]
62
+ assert_equal 60, c[2]
63
+ end
64
+
65
+ def test_should_order_by_grouped_field
66
+ c = Account.sum(:credit_limit, :group => :firm_id, :order => "firm_id")
67
+ assert_equal [1, 2, 6, 9], c.keys.compact
68
+ end
69
+
70
+ def test_should_order_by_calculation
71
+ c = Account.sum(:credit_limit, :group => :firm_id, :order => "sum_credit_limit desc, firm_id")
72
+ assert_equal [105, 60, 53, 50, 50], c.keys.collect { |k| c[k] }
73
+ assert_equal [6, 2, 9, 1], c.keys.compact
74
+ end
75
+
76
+ def test_should_limit_calculation
77
+ c = Account.sum(:credit_limit, :conditions => "firm_id IS NOT NULL",
78
+ :group => :firm_id, :order => "firm_id", :limit => 2)
79
+ assert_equal [1, 2], c.keys.compact
80
+ end
81
+
82
+ def test_should_limit_calculation_with_offset
83
+ c = Account.sum(:credit_limit, :conditions => "firm_id IS NOT NULL",
84
+ :group => :firm_id, :order => "firm_id", :limit => 2, :offset => 1)
85
+ assert_equal [2, 6], c.keys.compact
86
+ end
87
+
88
+ def test_should_group_by_summed_field_having_condition
89
+ c = Account.sum(:credit_limit, :group => :firm_id,
90
+ :having => 'sum(credit_limit) > 50')
91
+ assert_nil c[1]
92
+ assert_equal 105, c[6]
93
+ assert_equal 60, c[2]
94
+ end
95
+
96
+ def test_should_group_by_summed_field_having_sanitized_condition
97
+ c = Account.sum(:credit_limit, :group => :firm_id,
98
+ :having => ['sum(credit_limit) > ?', 50])
99
+ assert_nil c[1]
100
+ assert_equal 105, c[6]
101
+ assert_equal 60, c[2]
102
+ end
103
+
104
+ def test_should_group_by_summed_association
105
+ c = Account.sum(:credit_limit, :group => :firm)
106
+ assert_equal 50, c[companies(:first_firm)]
107
+ assert_equal 105, c[companies(:rails_core)]
108
+ assert_equal 60, c[companies(:first_client)]
109
+ end
110
+
111
+ def test_should_sum_field_with_conditions
112
+ assert_equal 105, Account.sum(:credit_limit, :conditions => 'firm_id = 6')
113
+ end
114
+
115
+ def test_should_return_zero_if_sum_conditions_return_nothing
116
+ assert_equal 0, Account.sum(:credit_limit, :conditions => '1 = 2')
117
+ assert_equal 0, companies(:rails_core).companies.sum(:id, :conditions => '1 = 2')
118
+ end
119
+
120
+ def test_sum_should_return_valid_values_for_decimals
121
+ NumericData.create(:bank_balance => 19.83)
122
+ assert_equal 19.83, NumericData.sum(:bank_balance)
123
+ end
124
+
125
+ def test_should_group_by_summed_field_with_conditions
126
+ c = Account.sum(:credit_limit, :conditions => 'firm_id > 1',
127
+ :group => :firm_id)
128
+ assert_nil c[1]
129
+ assert_equal 105, c[6]
130
+ assert_equal 60, c[2]
131
+ end
132
+
133
+ def test_should_group_by_summed_field_with_conditions_and_having
134
+ c = Account.sum(:credit_limit, :conditions => 'firm_id > 1',
135
+ :group => :firm_id,
136
+ :having => 'sum(credit_limit) > 60')
137
+ assert_nil c[1]
138
+ assert_equal 105, c[6]
139
+ assert_nil c[2]
140
+ end
141
+
142
+ def test_should_group_by_fields_with_table_alias
143
+ c = Account.sum(:credit_limit, :group => 'accounts.firm_id')
144
+ assert_equal 50, c[1]
145
+ assert_equal 105, c[6]
146
+ assert_equal 60, c[2]
147
+ end
148
+
149
+ def test_should_calculate_with_invalid_field
150
+ assert_equal 6, Account.calculate(:count, '*')
151
+ assert_equal 6, Account.calculate(:count, :all)
152
+ end
153
+
154
+ def test_should_calculate_grouped_with_invalid_field
155
+ c = Account.count(:all, :group => 'accounts.firm_id')
156
+ assert_equal 1, c[1]
157
+ assert_equal 2, c[6]
158
+ assert_equal 1, c[2]
159
+ end
160
+
161
+ def test_should_calculate_grouped_association_with_invalid_field
162
+ c = Account.count(:all, :group => :firm)
163
+ assert_equal 1, c[companies(:first_firm)]
164
+ assert_equal 2, c[companies(:rails_core)]
165
+ assert_equal 1, c[companies(:first_client)]
166
+ end
167
+
168
+ def test_should_group_by_association_with_non_numeric_foreign_key
169
+ if( ActiveRecord::Base.connection.respond_to?(:pstmt_support_on) && ActiveRecord::Base.connection.pstmt_support_on == true )
170
+ ActiveRecord::Base.connection.expects(:prepared_select).returns([{"count_all" => 1, "firm_id" => "ABC"}])
171
+ else
172
+ ActiveRecord::Base.connection.expects(:select_all).returns([{"count_all" => 1, "firm_id" => "ABC"}])
173
+ end
174
+
175
+ firm = mock()
176
+ firm.expects(:id).returns("ABC")
177
+ firm.expects(:class).returns(Firm)
178
+ Company.expects(:find).with(["ABC"]).returns([firm])
179
+
180
+ column = mock()
181
+ column.expects(:name).at_least_once.returns(:firm_id)
182
+ column.expects(:type_cast).with("ABC").returns("ABC")
183
+ Account.expects(:columns).at_least_once.returns([column])
184
+
185
+ c = Account.count(:all, :group => :firm)
186
+ first_key = c.keys.first
187
+ assert_equal Firm, first_key.class
188
+ assert_equal 1, c[first_key]
189
+ end
190
+
191
+ def test_should_calculate_grouped_association_with_foreign_key_option
192
+ Account.belongs_to :another_firm, :class_name => 'Firm', :foreign_key => 'firm_id'
193
+ c = Account.count(:all, :group => :another_firm)
194
+ assert_equal 1, c[companies(:first_firm)]
195
+ assert_equal 2, c[companies(:rails_core)]
196
+ assert_equal 1, c[companies(:first_client)]
197
+ end
198
+
199
+ def test_should_not_modify_options_when_using_includes
200
+ options = {:conditions => 'companies.id > 1', :include => :firm}
201
+ options_copy = options.dup
202
+
203
+ Account.count(:all, options)
204
+ assert_equal options_copy, options
205
+ end
206
+
207
+ def test_should_calculate_grouped_by_function
208
+ c = Company.count(:all, :group => "UPPER(#{QUOTED_TYPE})")
209
+ assert_equal 2, c[nil]
210
+ assert_equal 1, c['DEPENDENTFIRM']
211
+ assert_equal 4, c['CLIENT']
212
+ assert_equal 2, c['FIRM']
213
+ end
214
+
215
+ def test_should_calculate_grouped_by_function_with_table_alias
216
+ c = Company.count(:all, :group => "UPPER(companies.#{QUOTED_TYPE})")
217
+ assert_equal 2, c[nil]
218
+ assert_equal 1, c['DEPENDENTFIRM']
219
+ assert_equal 4, c['CLIENT']
220
+ assert_equal 2, c['FIRM']
221
+ end
222
+
223
+ def test_should_not_overshadow_enumerable_sum
224
+ assert_equal 6, [1, 2, 3].sum(&:abs)
225
+ end
226
+
227
+ def test_should_sum_scoped_field
228
+ assert_equal 15, companies(:rails_core).companies.sum(:id)
229
+ end
230
+
231
+ def test_should_sum_scoped_field_with_from
232
+ assert_equal Club.count, Organization.clubs.count
233
+ end
234
+
235
+ def test_should_sum_scoped_field_with_conditions
236
+ assert_equal 8, companies(:rails_core).companies.sum(:id, :conditions => 'id > 7')
237
+ end
238
+
239
+ def test_should_group_by_scoped_field
240
+ c = companies(:rails_core).companies.sum(:id, :group => :name)
241
+ assert_equal 7, c['Leetsoft']
242
+ assert_equal 8, c['Jadedpixel']
243
+ end
244
+
245
+ def test_should_group_by_summed_field_through_association_and_having
246
+ c = companies(:rails_core).companies.sum(:id, :group => :name,
247
+ :having => 'sum(id) > 7')
248
+ assert_nil c['Leetsoft']
249
+ assert_equal 8, c['Jadedpixel']
250
+ end
251
+
252
+ def test_should_count_selected_field_with_include
253
+ assert_equal 6, Account.count(:distinct => true, :include => :firm)
254
+ assert_equal 4, Account.count(:distinct => true, :include => :firm, :select => :credit_limit)
255
+ end
256
+
257
+ def test_should_count_scoped_select
258
+ Account.update_all("credit_limit = NULL")
259
+ assert_equal 0, Account.scoped(:select => "credit_limit").count
260
+ end
261
+
262
+ def test_should_count_scoped_select_with_options
263
+ Account.update_all("credit_limit = NULL")
264
+ Account.last.update_attribute('credit_limit', 49)
265
+ Account.first.update_attribute('credit_limit', 51)
266
+
267
+ assert_equal 1, Account.scoped(:select => "credit_limit").count(:conditions => ['credit_limit >= 50'])
268
+ end
269
+
270
+ def test_should_count_manual_select_with_include
271
+ assert_equal 6, Account.count(:select => "DISTINCT accounts.id", :include => :firm)
272
+ end
273
+
274
+ def test_count_with_column_parameter
275
+ assert_equal 5, Account.count(:firm_id)
276
+ end
277
+
278
+ def test_count_with_column_and_options_parameter
279
+ assert_equal 2, Account.count(:firm_id, :conditions => "credit_limit = 50 AND firm_id IS NOT NULL")
280
+ end
281
+
282
+ def test_should_count_field_in_joined_table
283
+ assert_equal 5, Account.count('companies.id', :joins => :firm)
284
+ assert_equal 4, Account.count('companies.id', :joins => :firm, :distinct => true)
285
+ end
286
+
287
+ def test_should_count_field_in_joined_table_with_group_by
288
+ c = Account.count('companies.id', :group => 'accounts.firm_id', :joins => :firm)
289
+
290
+ [1,6,2,9].each { |firm_id| assert c.keys.include?(firm_id) }
291
+ end
292
+
293
+ def test_count_with_no_parameters_isnt_deprecated
294
+ assert_not_deprecated { Account.count }
295
+ end
296
+
297
+ def test_count_with_too_many_parameters_raises
298
+ assert_raise(ArgumentError) { Account.count(1, 2, 3) }
299
+ end
300
+
301
+ def test_should_sum_expression
302
+ # Oracle adapter returns floating point value 636.0 after SUM
303
+ if current_adapter?(:OracleAdapter)
304
+ assert_equal 636, Account.sum("2 * credit_limit")
305
+ else
306
+ assert_equal 636, Account.sum("2 * credit_limit").to_i
307
+ end
308
+ end
309
+
310
+ def test_count_with_from_option
311
+ assert_equal Company.count(:all), Company.count(:all, :from => 'companies')
312
+ assert_equal Account.count(:all, :conditions => "credit_limit = 50"),
313
+ Account.count(:all, :from => 'accounts', :conditions => "credit_limit = 50")
314
+ assert_equal Company.count(:type, :conditions => {:type => "Firm"}),
315
+ Company.count(:type, :conditions => {:type => "Firm"}, :from => 'companies')
316
+ end
317
+
318
+ def test_sum_with_from_option
319
+ assert_equal Account.sum(:credit_limit), Account.sum(:credit_limit, :from => 'accounts')
320
+ assert_equal Account.sum(:credit_limit, :conditions => "credit_limit > 50"),
321
+ Account.sum(:credit_limit, :from => 'accounts', :conditions => "credit_limit > 50")
322
+ end
323
+
324
+ def test_average_with_from_option
325
+ assert_equal Account.average(:credit_limit), Account.average(:credit_limit, :from => 'accounts')
326
+ assert_equal Account.average(:credit_limit, :conditions => "credit_limit > 50"),
327
+ Account.average(:credit_limit, :from => 'accounts', :conditions => "credit_limit > 50")
328
+ end
329
+
330
+ def test_minimum_with_from_option
331
+ assert_equal Account.minimum(:credit_limit), Account.minimum(:credit_limit, :from => 'accounts')
332
+ assert_equal Account.minimum(:credit_limit, :conditions => "credit_limit > 50"),
333
+ Account.minimum(:credit_limit, :from => 'accounts', :conditions => "credit_limit > 50")
334
+ end
335
+
336
+ def test_maximum_with_from_option
337
+ assert_equal Account.maximum(:credit_limit), Account.maximum(:credit_limit, :from => 'accounts')
338
+ assert_equal Account.maximum(:credit_limit, :conditions => "credit_limit > 50"),
339
+ Account.maximum(:credit_limit, :from => 'accounts', :conditions => "credit_limit > 50")
340
+ end
341
+
342
+ def test_from_option_with_specified_index
343
+ if Edge.connection.adapter_name == 'MySQL'
344
+ assert_equal Edge.count(:all), Edge.count(:all, :from => 'edges USE INDEX(unique_edge_index)')
345
+ assert_equal Edge.count(:all, :conditions => 'sink_id < 5'),
346
+ Edge.count(:all, :from => 'edges USE INDEX(unique_edge_index)', :conditions => 'sink_id < 5')
347
+ end
348
+ end
349
+
350
+ def test_from_option_with_table_different_than_class
351
+ assert_equal Account.count(:all), Company.count(:all, :from => 'accounts')
352
+ end
353
+
354
+ def test_distinct_is_honored_when_used_with_count_operation_after_group
355
+ # Count the number of authors for approved topics
356
+ approved_topics_count = Topic.group(:approved).count(:author_name)[true]
357
+ assert_equal approved_topics_count, 3
358
+ # Count the number of distinct authors for approved Topics
359
+ distinct_authors_for_approved_count = Topic.group(:approved).count(:author_name, :distinct => true)[true]
360
+ assert_equal distinct_authors_for_approved_count, 2
361
+ end
362
+ end
@@ -0,0 +1,1088 @@
1
+ require "cases/helper"
2
+ require 'models/post'
3
+ require 'models/author'
4
+ require 'models/categorization'
5
+ require 'models/comment'
6
+ require 'models/company'
7
+ require 'models/topic'
8
+ require 'models/reply'
9
+ require 'models/entrant'
10
+ require 'models/project'
11
+ require 'models/developer'
12
+ require 'models/customer'
13
+
14
+ class DynamicFinderMatchTest < ActiveRecord::TestCase
15
+ def test_find_no_match
16
+ assert_nil ActiveRecord::DynamicFinderMatch.match("not_a_finder")
17
+ end
18
+
19
+ def test_find_by
20
+ match = ActiveRecord::DynamicFinderMatch.match("find_by_age_and_sex_and_location")
21
+ assert_not_nil match
22
+ assert match.finder?
23
+ assert_equal :first, match.finder
24
+ assert_equal %w(age sex location), match.attribute_names
25
+ end
26
+
27
+ def find_by_bang
28
+ match = ActiveRecord::DynamicFinderMatch.match("find_by_age_and_sex_and_location!")
29
+ assert_not_nil match
30
+ assert match.finder?
31
+ assert match.bang?
32
+ assert_equal :first, match.finder
33
+ assert_equal %w(age sex location), match.attribute_names
34
+ end
35
+
36
+ def test_find_all_by
37
+ match = ActiveRecord::DynamicFinderMatch.match("find_all_by_age_and_sex_and_location")
38
+ assert_not_nil match
39
+ assert match.finder?
40
+ assert_equal :all, match.finder
41
+ assert_equal %w(age sex location), match.attribute_names
42
+ end
43
+
44
+ def test_find_or_initialize_by
45
+ match = ActiveRecord::DynamicFinderMatch.match("find_or_initialize_by_age_and_sex_and_location")
46
+ assert_not_nil match
47
+ assert !match.finder?
48
+ assert match.instantiator?
49
+ assert_equal :first, match.finder
50
+ assert_equal :new, match.instantiator
51
+ assert_equal %w(age sex location), match.attribute_names
52
+ end
53
+
54
+ def test_find_or_create_by
55
+ match = ActiveRecord::DynamicFinderMatch.match("find_or_create_by_age_and_sex_and_location")
56
+ assert_not_nil match
57
+ assert !match.finder?
58
+ assert match.instantiator?
59
+ assert_equal :first, match.finder
60
+ assert_equal :create, match.instantiator
61
+ assert_equal %w(age sex location), match.attribute_names
62
+ end
63
+ end
64
+
65
+ class FinderTest < ActiveRecord::TestCase
66
+ fixtures :companies, :topics, :entrants, :developers, :developers_projects, :posts, :comments, :accounts, :authors, :customers, :categories, :categorizations
67
+
68
+ def test_find
69
+ assert_equal(topics(:first).title, Topic.find(1).title)
70
+ end
71
+
72
+ # find should handle strings that come from URLs
73
+ # (example: Category.find(params[:id]))
74
+ def test_find_with_string
75
+ assert_equal(Topic.find(1).title,Topic.find("1").title)
76
+ end
77
+
78
+ def test_exists
79
+ assert Topic.exists?(1)
80
+ assert Topic.exists?("1")
81
+ assert Topic.exists?(:author_name => "David")
82
+ assert Topic.exists?(:author_name => "Mary", :approved => true)
83
+ assert Topic.exists?(["parent_id = ?", 1])
84
+ assert !Topic.exists?(45)
85
+
86
+ begin
87
+ assert !Topic.exists?("foo")
88
+ rescue ActiveRecord::StatementInvalid
89
+ # PostgreSQL complains about string comparison with integer field
90
+ rescue Exception
91
+ flunk
92
+ end
93
+
94
+ assert_raise(NoMethodError) { Topic.exists?([1,2]) }
95
+ end
96
+
97
+ def test_exists_returns_true_with_one_record_and_no_args
98
+ assert Topic.exists?
99
+ end
100
+
101
+ def test_does_not_exist_with_empty_table_and_no_args_given
102
+ Topic.delete_all
103
+ assert !Topic.exists?
104
+ end
105
+
106
+ def test_exists_with_aggregate_having_three_mappings
107
+ existing_address = customers(:david).address
108
+ assert Customer.exists?(:address => existing_address)
109
+ end
110
+
111
+ def test_exists_with_aggregate_having_three_mappings_with_one_difference
112
+ existing_address = customers(:david).address
113
+ assert !Customer.exists?(:address =>
114
+ Address.new(existing_address.street, existing_address.city, existing_address.country + "1"))
115
+ assert !Customer.exists?(:address =>
116
+ Address.new(existing_address.street, existing_address.city + "1", existing_address.country))
117
+ assert !Customer.exists?(:address =>
118
+ Address.new(existing_address.street + "1", existing_address.city, existing_address.country))
119
+ end
120
+
121
+ def test_exists_with_scoped_include
122
+ Developer.send(:with_scope, :find => { :include => :projects, :order => "projects.name" }) do
123
+ assert Developer.exists?
124
+ end
125
+ end
126
+
127
+ def test_find_by_array_of_one_id
128
+ assert_kind_of(Array, Topic.find([ 1 ]))
129
+ assert_equal(1, Topic.find([ 1 ]).length)
130
+ end
131
+
132
+ def test_find_by_ids
133
+ assert_equal 2, Topic.find(1, 2).size
134
+ assert_equal topics(:second).title, Topic.find([2]).first.title
135
+ end
136
+
137
+ def test_find_by_ids_with_limit_and_offset
138
+ assert_equal 2, Entrant.find([1,3,2], :limit => 2).size
139
+ assert_equal 1, Entrant.find([1,3,2], :limit => 3, :offset => 2).size
140
+
141
+ # Also test an edge case: If you have 11 results, and you set a
142
+ # limit of 3 and offset of 9, then you should find that there
143
+ # will be only 2 results, regardless of the limit.
144
+ devs = Developer.find :all
145
+ last_devs = Developer.find devs.map(&:id), :limit => 3, :offset => 9
146
+ assert_equal 2, last_devs.size
147
+ end
148
+
149
+ def test_find_an_empty_array
150
+ assert_equal [], Topic.find([])
151
+ end
152
+
153
+ def test_find_by_ids_missing_one
154
+ assert_raise(ActiveRecord::RecordNotFound) { Topic.find(1, 2, 45) }
155
+ end
156
+
157
+ def test_find_all_with_limit
158
+ assert_equal(2, Entrant.find(:all, :limit => 2).size)
159
+ assert_equal(0, Entrant.find(:all, :limit => 0).size)
160
+ end
161
+
162
+ def test_find_all_with_prepared_limit_and_offset
163
+ entrants = Entrant.find(:all, :order => "id ASC", :limit => 2, :offset => 1)
164
+
165
+ assert_equal(2, entrants.size)
166
+ assert_equal(entrants(:second).name, entrants.first.name)
167
+
168
+ assert_equal 3, Entrant.count
169
+ entrants = Entrant.find(:all, :order => "id ASC", :limit => 2, :offset => 2)
170
+ assert_equal(1, entrants.size)
171
+ assert_equal(entrants(:third).name, entrants.first.name)
172
+ end
173
+
174
+ def test_find_all_with_limit_and_offset_and_multiple_order_clauses
175
+ first_three_posts = Post.find :all, :order => 'author_id, id', :limit => 3, :offset => 0
176
+ second_three_posts = Post.find :all, :order => ' author_id,id ', :limit => 3, :offset => 3
177
+ last_posts = Post.find :all, :order => ' author_id, id ', :limit => 3, :offset => 6
178
+
179
+ assert_equal [[0,3],[1,1],[1,2]], first_three_posts.map { |p| [p.author_id, p.id] }
180
+ assert_equal [[1,4],[1,5],[1,6]], second_three_posts.map { |p| [p.author_id, p.id] }
181
+ assert_equal [[2,7]], last_posts.map { |p| [p.author_id, p.id] }
182
+ end
183
+
184
+
185
+ def test_find_with_group
186
+ developers = Developer.find(:all, :group => "salary", :select => "salary")
187
+ assert_equal 4, developers.size
188
+ assert_equal 4, developers.map(&:salary).uniq.size
189
+ end
190
+
191
+ def test_find_with_group_and_having
192
+ developers = Developer.find(:all, :group => "salary", :having => "sum(salary) > 10000", :select => "salary")
193
+ assert_equal 3, developers.size
194
+ assert_equal 3, developers.map(&:salary).uniq.size
195
+ assert developers.all? { |developer| developer.salary > 10000 }
196
+ end
197
+
198
+ def test_find_with_group_and_sanitized_having
199
+ developers = Developer.find(:all, :group => "salary", :having => ["sum(salary) > ?", 10000], :select => "salary")
200
+ assert_equal 3, developers.size
201
+ assert_equal 3, developers.map(&:salary).uniq.size
202
+ assert developers.all? { |developer| developer.salary > 10000 }
203
+ end
204
+
205
+ def test_find_with_entire_select_statement
206
+ topics = Topic.find_by_sql "SELECT * FROM topics WHERE author_name = 'Mary'"
207
+
208
+ assert_equal(1, topics.size)
209
+ assert_equal(topics(:second).title, topics.first.title)
210
+ end
211
+
212
+ def test_find_with_prepared_select_statement
213
+ topics = Topic.find_by_sql ["SELECT * FROM topics WHERE author_name = ?", "Mary"]
214
+
215
+ assert_equal(1, topics.size)
216
+ assert_equal(topics(:second).title, topics.first.title)
217
+ end
218
+
219
+ def test_find_by_sql_with_sti_on_joined_table
220
+ accounts = Account.find_by_sql("SELECT * FROM accounts INNER JOIN companies ON companies.id = accounts.firm_id")
221
+ assert_equal [Account], accounts.collect(&:class).uniq
222
+ end
223
+
224
+ def test_find_first
225
+ first = Topic.find(:first, :conditions => "title = 'The First Topic'")
226
+ assert_equal(topics(:first).title, first.title)
227
+ end
228
+
229
+ def test_find_first_failing
230
+ first = Topic.find(:first, :conditions => "title = 'The First Topic!'")
231
+ assert_nil(first)
232
+ end
233
+
234
+ def test_first
235
+ assert_equal topics(:second).title, Topic.where("title = 'The Second Topic of the day'").first.title
236
+ end
237
+
238
+ def test_first_failing
239
+ assert_nil Topic.where("title = 'The Second Topic of the day!'").first
240
+ end
241
+
242
+ def test_unexisting_record_exception_handling
243
+ assert_raise(ActiveRecord::RecordNotFound) {
244
+ Topic.find(1).parent
245
+ }
246
+
247
+ Topic.find(2).topic
248
+ end
249
+
250
+ def test_find_only_some_columns
251
+ topic = Topic.find(1, :select => "author_name")
252
+ assert_raise(ActiveModel::MissingAttributeError) {topic.title}
253
+ assert_equal "David", topic.author_name
254
+ assert !topic.attribute_present?("title")
255
+ #assert !topic.respond_to?("title")
256
+ assert topic.attribute_present?("author_name")
257
+ assert_respond_to topic, "author_name"
258
+ end
259
+
260
+ def test_find_on_blank_conditions
261
+ [nil, " ", [], {}].each do |blank|
262
+ assert_nothing_raised { Topic.find(:first, :conditions => blank) }
263
+ end
264
+ end
265
+
266
+ def test_find_on_blank_bind_conditions
267
+ [ [""], ["",{}] ].each do |blank|
268
+ assert_nothing_raised { Topic.find(:first, :conditions => blank) }
269
+ end
270
+ end
271
+
272
+ def test_find_on_array_conditions
273
+ assert Topic.find(1, :conditions => ["approved = ?", false])
274
+ assert_raise(ActiveRecord::RecordNotFound) { Topic.find(1, :conditions => ["approved = ?", true]) }
275
+ end
276
+
277
+ def test_find_on_hash_conditions
278
+ assert Topic.find(1, :conditions => { :approved => false })
279
+ assert_raise(ActiveRecord::RecordNotFound) { Topic.find(1, :conditions => { :approved => true }) }
280
+ end
281
+
282
+ def test_find_on_hash_conditions_with_explicit_table_name
283
+ assert Topic.find(1, :conditions => { 'topics.approved' => false })
284
+ assert_raise(ActiveRecord::RecordNotFound) { Topic.find(1, :conditions => { 'topics.approved' => true }) }
285
+ end
286
+
287
+ def test_find_on_hash_conditions_with_hashed_table_name
288
+ assert Topic.find(1, :conditions => {:topics => { :approved => false }})
289
+ assert_raise(ActiveRecord::RecordNotFound) { Topic.find(1, :conditions => {:topics => { :approved => true }}) }
290
+ end
291
+
292
+ def test_find_with_hash_conditions_on_joined_table
293
+ firms = Firm.joins(:account).where(:accounts => { :credit_limit => 50 })
294
+ assert_equal 1, firms.size
295
+ assert_equal companies(:first_firm), firms.first
296
+ end
297
+
298
+ def test_find_with_hash_conditions_on_joined_table_and_with_range
299
+ firms = DependentFirm.all :joins => :account, :conditions => {:name => 'RailsCore', :accounts => { :credit_limit => 55..60 }}
300
+ assert_equal 1, firms.size
301
+ assert_equal companies(:rails_core), firms.first
302
+ end
303
+
304
+ def test_find_on_hash_conditions_with_explicit_table_name_and_aggregate
305
+ david = customers(:david)
306
+ assert Customer.find(david.id, :conditions => { 'customers.name' => david.name, :address => david.address })
307
+ assert_raise(ActiveRecord::RecordNotFound) {
308
+ Customer.find(david.id, :conditions => { 'customers.name' => david.name + "1", :address => david.address })
309
+ }
310
+ end
311
+
312
+ def test_find_on_association_proxy_conditions
313
+ assert_equal [1, 2, 3, 5, 6, 7, 8, 9, 10], Comment.find_all_by_post_id(authors(:david).posts).map(&:id).sort
314
+ end
315
+
316
+ def test_find_on_hash_conditions_with_range
317
+ assert_equal [1,2], Topic.find(:all, :conditions => { :id => 1..2 }).map(&:id).sort
318
+ assert_raise(ActiveRecord::RecordNotFound) { Topic.find(1, :conditions => { :id => 2..3 }) }
319
+ end
320
+
321
+ def test_find_on_hash_conditions_with_end_exclusive_range
322
+ assert_equal [1,2,3], Topic.find(:all, :conditions => { :id => 1..3 }).map(&:id).sort
323
+ assert_equal [1,2], Topic.find(:all, :conditions => { :id => 1...3 }).map(&:id).sort
324
+ assert_raise(ActiveRecord::RecordNotFound) { Topic.find(3, :conditions => { :id => 2...3 }) }
325
+ end
326
+
327
+ def test_find_on_hash_conditions_with_multiple_ranges
328
+ assert_equal [1,2,3], Comment.find(:all, :conditions => { :id => 1..3, :post_id => 1..2 }).map(&:id).sort
329
+ assert_equal [1], Comment.find(:all, :conditions => { :id => 1..1, :post_id => 1..10 }).map(&:id).sort
330
+ end
331
+
332
+ def test_find_on_multiple_hash_conditions
333
+ assert Topic.find(1, :conditions => { :author_name => "David", :title => "The First Topic", :replies_count => 1, :approved => false })
334
+ assert_raise(ActiveRecord::RecordNotFound) { Topic.find(1, :conditions => { :author_name => "David", :title => "The First Topic", :replies_count => 1, :approved => true }) }
335
+ assert_raise(ActiveRecord::RecordNotFound) { Topic.find(1, :conditions => { :author_name => "David", :title => "HHC", :replies_count => 1, :approved => false }) }
336
+ assert_raise(ActiveRecord::RecordNotFound) { Topic.find(1, :conditions => { :author_name => "David", :title => "The First Topic", :replies_count => 1, :approved => true }) }
337
+ end
338
+
339
+ def test_condition_interpolation
340
+ assert_kind_of Firm, Company.find(:first, :conditions => ["name = '%s'", "37signals"])
341
+ assert_nil Company.find(:first, :conditions => ["name = '%s'", "37signals!"])
342
+ assert_nil Company.find(:first, :conditions => ["name = '%s'", "37signals!' OR 1=1"])
343
+ assert_kind_of Time, Topic.find(:first, :conditions => ["id = %d", 1]).written_on
344
+ end
345
+
346
+ def test_condition_array_interpolation
347
+ assert_kind_of Firm, Company.find(:first, :conditions => ["name = '%s'", "37signals"])
348
+ assert_nil Company.find(:first, :conditions => ["name = '%s'", "37signals!"])
349
+ assert_nil Company.find(:first, :conditions => ["name = '%s'", "37signals!' OR 1=1"])
350
+ assert_kind_of Time, Topic.find(:first, :conditions => ["id = %d", 1]).written_on
351
+ end
352
+
353
+ def test_condition_hash_interpolation
354
+ assert_kind_of Firm, Company.find(:first, :conditions => { :name => "37signals"})
355
+ assert_nil Company.find(:first, :conditions => { :name => "37signals!"})
356
+ assert_kind_of Time, Topic.find(:first, :conditions => {:id => 1}).written_on
357
+ end
358
+
359
+ def test_hash_condition_find_malformed
360
+ assert_raise(ActiveRecord::StatementInvalid) {
361
+ Company.find(:first, :conditions => { :id => 2, :dhh => true })
362
+ }
363
+ end
364
+
365
+ def test_hash_condition_find_with_escaped_characters
366
+ Company.create("name" => "Ain't noth'n like' \#stuff")
367
+ assert Company.find(:first, :conditions => { :name => "Ain't noth'n like' \#stuff" })
368
+ end
369
+
370
+ def test_hash_condition_find_with_array
371
+ p1, p2 = Post.find(:all, :limit => 2, :order => 'id asc')
372
+ assert_equal [p1, p2], Post.find(:all, :conditions => { :id => [p1, p2] }, :order => 'id asc')
373
+ assert_equal [p1, p2], Post.find(:all, :conditions => { :id => [p1, p2.id] }, :order => 'id asc')
374
+ end
375
+
376
+ def test_hash_condition_find_with_nil
377
+ topic = Topic.find(:first, :conditions => { :last_read => nil } )
378
+ assert_not_nil topic
379
+ assert_nil topic.last_read
380
+ end
381
+
382
+ def test_hash_condition_find_with_aggregate_having_one_mapping
383
+ balance = customers(:david).balance
384
+ assert_kind_of Money, balance
385
+ found_customer = Customer.find(:first, :conditions => {:balance => balance})
386
+ assert_equal customers(:david), found_customer
387
+ end
388
+
389
+ def test_hash_condition_find_with_aggregate_attribute_having_same_name_as_field_and_key_value_being_aggregate
390
+ gps_location = customers(:david).gps_location
391
+ assert_kind_of GpsLocation, gps_location
392
+ found_customer = Customer.find(:first, :conditions => {:gps_location => gps_location})
393
+ assert_equal customers(:david), found_customer
394
+ end
395
+
396
+ def test_hash_condition_find_with_aggregate_having_one_mapping_and_key_value_being_attribute_value
397
+ balance = customers(:david).balance
398
+ assert_kind_of Money, balance
399
+ found_customer = Customer.find(:first, :conditions => {:balance => balance.amount})
400
+ assert_equal customers(:david), found_customer
401
+ end
402
+
403
+ def test_hash_condition_find_with_aggregate_attribute_having_same_name_as_field_and_key_value_being_attribute_value
404
+ gps_location = customers(:david).gps_location
405
+ assert_kind_of GpsLocation, gps_location
406
+ found_customer = Customer.find(:first, :conditions => {:gps_location => gps_location.gps_location})
407
+ assert_equal customers(:david), found_customer
408
+ end
409
+
410
+ def test_hash_condition_find_with_aggregate_having_three_mappings
411
+ address = customers(:david).address
412
+ assert_kind_of Address, address
413
+ found_customer = Customer.find(:first, :conditions => {:address => address})
414
+ assert_equal customers(:david), found_customer
415
+ end
416
+
417
+ def test_hash_condition_find_with_one_condition_being_aggregate_and_another_not
418
+ address = customers(:david).address
419
+ assert_kind_of Address, address
420
+ found_customer = Customer.find(:first, :conditions => {:address => address, :name => customers(:david).name})
421
+ assert_equal customers(:david), found_customer
422
+ end
423
+
424
+ def test_condition_utc_time_interpolation_with_default_timezone_local
425
+ with_env_tz 'America/New_York' do
426
+ with_active_record_default_timezone :local do
427
+ topic = Topic.first
428
+ assert_equal topic, Topic.find(:first, :conditions => ['written_on = ?', topic.written_on.getutc])
429
+ end
430
+ end
431
+ end
432
+
433
+ def test_hash_condition_utc_time_interpolation_with_default_timezone_local
434
+ with_env_tz 'America/New_York' do
435
+ with_active_record_default_timezone :local do
436
+ topic = Topic.first
437
+ assert_equal topic, Topic.find(:first, :conditions => {:written_on => topic.written_on.getutc})
438
+ end
439
+ end
440
+ end
441
+
442
+ def test_condition_local_time_interpolation_with_default_timezone_utc
443
+ with_env_tz 'America/New_York' do
444
+ with_active_record_default_timezone :utc do
445
+ topic = Topic.first
446
+ assert_equal topic, Topic.find(:first, :conditions => ['written_on = ?', topic.written_on.getlocal])
447
+ end
448
+ end
449
+ end
450
+
451
+ def test_hash_condition_local_time_interpolation_with_default_timezone_utc
452
+ with_env_tz 'America/New_York' do
453
+ with_active_record_default_timezone :utc do
454
+ topic = Topic.first
455
+ assert_equal topic, Topic.find(:first, :conditions => {:written_on => topic.written_on.getlocal})
456
+ end
457
+ end
458
+ end
459
+
460
+ def test_bind_variables
461
+ assert_kind_of Firm, Company.find(:first, :conditions => ["name = ?", "37signals"])
462
+ assert_nil Company.find(:first, :conditions => ["name = ?", "37signals!"])
463
+ assert_nil Company.find(:first, :conditions => ["name = ?", "37signals!' OR 1=1"])
464
+ assert_kind_of Time, Topic.find(:first, :conditions => ["id = ?", 1]).written_on
465
+ assert_raise(ActiveRecord::PreparedStatementInvalid) {
466
+ Company.find(:first, :conditions => ["id=? AND name = ?", 2])
467
+ }
468
+ assert_raise(ActiveRecord::PreparedStatementInvalid) {
469
+ Company.find(:first, :conditions => ["id=?", 2, 3, 4])
470
+ }
471
+ end
472
+
473
+ def test_bind_variables_with_quotes
474
+ Company.create("name" => "37signals' go'es agains")
475
+ assert Company.find(:first, :conditions => ["name = ?", "37signals' go'es agains"])
476
+ end
477
+
478
+ def test_named_bind_variables_with_quotes
479
+ Company.create("name" => "37signals' go'es agains")
480
+ assert Company.find(:first, :conditions => ["name = :name", {:name => "37signals' go'es agains"}])
481
+ end
482
+
483
+ def test_bind_arity
484
+ assert_nothing_raised { bind '' }
485
+ assert_raise(ActiveRecord::PreparedStatementInvalid) { bind '', 1 }
486
+
487
+ assert_raise(ActiveRecord::PreparedStatementInvalid) { bind '?' }
488
+ assert_nothing_raised { bind '?', 1 }
489
+ assert_raise(ActiveRecord::PreparedStatementInvalid) { bind '?', 1, 1 }
490
+ end
491
+
492
+ def test_named_bind_variables
493
+ assert_equal '1', bind(':a', :a => 1) # ' ruby-mode
494
+ assert_equal '1 1', bind(':a :a', :a => 1) # ' ruby-mode
495
+
496
+ assert_nothing_raised { bind("'+00:00'", :foo => "bar") }
497
+
498
+ assert_kind_of Firm, Company.find(:first, :conditions => ["name = :name", { :name => "37signals" }])
499
+ assert_nil Company.find(:first, :conditions => ["name = :name", { :name => "37signals!" }])
500
+ assert_nil Company.find(:first, :conditions => ["name = :name", { :name => "37signals!' OR 1=1" }])
501
+ assert_kind_of Time, Topic.find(:first, :conditions => ["id = :id", { :id => 1 }]).written_on
502
+ end
503
+
504
+ class SimpleEnumerable
505
+ include Enumerable
506
+
507
+ def initialize(ary)
508
+ @ary = ary
509
+ end
510
+
511
+ def each(&b)
512
+ @ary.each(&b)
513
+ end
514
+ end
515
+
516
+ def test_bind_enumerable
517
+ quoted_abc = %(#{ActiveRecord::Base.connection.quote('a')},#{ActiveRecord::Base.connection.quote('b')},#{ActiveRecord::Base.connection.quote('c')})
518
+
519
+ assert_equal '1,2,3', bind('?', [1, 2, 3])
520
+ assert_equal quoted_abc, bind('?', %w(a b c))
521
+
522
+ assert_equal '1,2,3', bind(':a', :a => [1, 2, 3])
523
+ assert_equal quoted_abc, bind(':a', :a => %w(a b c)) # '
524
+
525
+ assert_equal '1,2,3', bind('?', SimpleEnumerable.new([1, 2, 3]))
526
+ assert_equal quoted_abc, bind('?', SimpleEnumerable.new(%w(a b c)))
527
+
528
+ assert_equal '1,2,3', bind(':a', :a => SimpleEnumerable.new([1, 2, 3]))
529
+ assert_equal quoted_abc, bind(':a', :a => SimpleEnumerable.new(%w(a b c))) # '
530
+ end
531
+
532
+ def test_bind_empty_enumerable
533
+ quoted_nil = ActiveRecord::Base.connection.quote(nil)
534
+ assert_equal quoted_nil, bind('?', [])
535
+ assert_equal " in (#{quoted_nil})", bind(' in (?)', [])
536
+ assert_equal "foo in (#{quoted_nil})", bind('foo in (?)', [])
537
+ end
538
+
539
+ def test_bind_empty_string
540
+ quoted_empty = ActiveRecord::Base.connection.quote('')
541
+ assert_equal quoted_empty, bind('?', '')
542
+ end
543
+
544
+ def test_bind_chars
545
+ quoted_bambi = ActiveRecord::Base.connection.quote("Bambi")
546
+ quoted_bambi_and_thumper = ActiveRecord::Base.connection.quote("Bambi\nand\nThumper")
547
+ assert_equal "name=#{quoted_bambi}", bind('name=?', "Bambi")
548
+ assert_equal "name=#{quoted_bambi_and_thumper}", bind('name=?', "Bambi\nand\nThumper")
549
+ assert_equal "name=#{quoted_bambi}", bind('name=?', "Bambi".mb_chars)
550
+ assert_equal "name=#{quoted_bambi_and_thumper}", bind('name=?', "Bambi\nand\nThumper".mb_chars)
551
+ end
552
+
553
+ def test_bind_record
554
+ o = Struct.new(:quoted_id).new(1)
555
+ assert_equal '1', bind('?', o)
556
+
557
+ os = [o] * 3
558
+ assert_equal '1,1,1', bind('?', os)
559
+ end
560
+
561
+ def test_named_bind_with_postgresql_type_casts
562
+ l = Proc.new { bind(":a::integer '2009-01-01'::date", :a => '10') }
563
+ assert_nothing_raised(&l)
564
+ assert_equal "#{ActiveRecord::Base.quote_value('10')}::integer '2009-01-01'::date", l.call
565
+ end
566
+
567
+ def test_string_sanitation
568
+ assert_not_equal "'something ' 1=1'", ActiveRecord::Base.sanitize("something ' 1=1")
569
+ assert_equal "'something; select table'", ActiveRecord::Base.sanitize("something; select table")
570
+ end
571
+
572
+ def test_count
573
+ assert_equal(0, Entrant.count(:conditions => "id > 3"))
574
+ assert_equal(1, Entrant.count(:conditions => ["id > ?", 2]))
575
+ assert_equal(2, Entrant.count(:conditions => ["id > ?", 1]))
576
+ end
577
+
578
+ def test_count_by_sql
579
+ assert_equal(0, Entrant.count_by_sql("SELECT COUNT(*) FROM entrants WHERE id > 3"))
580
+ assert_equal(1, Entrant.count_by_sql(["SELECT COUNT(*) FROM entrants WHERE id > ?", 2]))
581
+ assert_equal(2, Entrant.count_by_sql(["SELECT COUNT(*) FROM entrants WHERE id > ?", 1]))
582
+ end
583
+
584
+ def test_find_by_one_attribute
585
+ assert_equal topics(:first), Topic.find_by_title("The First Topic")
586
+ assert_nil Topic.find_by_title("The First Topic!")
587
+ end
588
+
589
+ def test_find_by_one_attribute_bang
590
+ assert_equal topics(:first), Topic.find_by_title!("The First Topic")
591
+ assert_raise(ActiveRecord::RecordNotFound) { Topic.find_by_title!("The First Topic!") }
592
+ end
593
+
594
+ def test_find_by_one_attribute_with_order_option
595
+ assert_equal accounts(:signals37), Account.find_by_credit_limit(50, :order => 'id')
596
+ assert_equal accounts(:rails_core_account), Account.find_by_credit_limit(50, :order => 'id DESC')
597
+ end
598
+
599
+ def test_find_by_one_attribute_with_conditions
600
+ assert_equal accounts(:rails_core_account), Account.find_by_credit_limit(50, :conditions => ['firm_id = ?', 6])
601
+ end
602
+
603
+ def test_find_by_one_attribute_that_is_an_aggregate
604
+ address = customers(:david).address
605
+ assert_kind_of Address, address
606
+ found_customer = Customer.find_by_address(address)
607
+ assert_equal customers(:david), found_customer
608
+ end
609
+
610
+ def test_find_by_one_attribute_that_is_an_aggregate_with_one_attribute_difference
611
+ address = customers(:david).address
612
+ assert_kind_of Address, address
613
+ missing_address = Address.new(address.street, address.city, address.country + "1")
614
+ assert_nil Customer.find_by_address(missing_address)
615
+ missing_address = Address.new(address.street, address.city + "1", address.country)
616
+ assert_nil Customer.find_by_address(missing_address)
617
+ missing_address = Address.new(address.street + "1", address.city, address.country)
618
+ assert_nil Customer.find_by_address(missing_address)
619
+ end
620
+
621
+ def test_find_by_two_attributes_that_are_both_aggregates
622
+ balance = customers(:david).balance
623
+ address = customers(:david).address
624
+ assert_kind_of Money, balance
625
+ assert_kind_of Address, address
626
+ found_customer = Customer.find_by_balance_and_address(balance, address)
627
+ assert_equal customers(:david), found_customer
628
+ end
629
+
630
+ def test_find_by_two_attributes_with_one_being_an_aggregate
631
+ balance = customers(:david).balance
632
+ assert_kind_of Money, balance
633
+ found_customer = Customer.find_by_balance_and_name(balance, customers(:david).name)
634
+ assert_equal customers(:david), found_customer
635
+ end
636
+
637
+ def test_dynamic_finder_on_one_attribute_with_conditions_returns_same_results_after_caching
638
+ # ensure this test can run independently of order
639
+ class << Account; self; end.send(:remove_method, :find_by_credit_limit) if Account.public_methods.any? { |m| m.to_s == 'find_by_credit_limit' }
640
+ a = Account.find_by_credit_limit(50, :conditions => ['firm_id = ?', 6])
641
+ assert_equal a, Account.find_by_credit_limit(50, :conditions => ['firm_id = ?', 6]) # find_by_credit_limit has been cached
642
+ end
643
+
644
+ def test_find_by_one_attribute_with_several_options
645
+ assert_equal accounts(:unknown), Account.find_by_credit_limit(50, :order => 'id DESC', :conditions => ['id != ?', 3])
646
+ end
647
+
648
+ def test_find_by_one_missing_attribute
649
+ assert_raise(NoMethodError) { Topic.find_by_undertitle("The First Topic!") }
650
+ end
651
+
652
+ def test_find_by_invalid_method_syntax
653
+ assert_raise(NoMethodError) { Topic.fail_to_find_by_title("The First Topic") }
654
+ assert_raise(NoMethodError) { Topic.find_by_title?("The First Topic") }
655
+ assert_raise(NoMethodError) { Topic.fail_to_find_or_create_by_title("Nonexistent Title") }
656
+ assert_raise(NoMethodError) { Topic.find_or_create_by_title?("Nonexistent Title") }
657
+ end
658
+
659
+ def test_find_by_two_attributes
660
+ assert_equal topics(:first), Topic.find_by_title_and_author_name("The First Topic", "David")
661
+ assert_nil Topic.find_by_title_and_author_name("The First Topic", "Mary")
662
+ end
663
+
664
+ def test_find_last_by_one_attribute
665
+ assert_equal Topic.last, Topic.find_last_by_title(Topic.last.title)
666
+ assert_nil Topic.find_last_by_title("A title with no matches")
667
+ end
668
+
669
+ def test_find_last_by_invalid_method_syntax
670
+ assert_raise(NoMethodError) { Topic.fail_to_find_last_by_title("The First Topic") }
671
+ assert_raise(NoMethodError) { Topic.find_last_by_title?("The First Topic") }
672
+ end
673
+
674
+ def test_find_last_by_one_attribute_with_several_options
675
+ assert_equal accounts(:signals37), Account.find_last_by_credit_limit(50, :order => 'id DESC', :conditions => ['id != ?', 3])
676
+ end
677
+
678
+ def test_find_last_by_one_missing_attribute
679
+ assert_raise(NoMethodError) { Topic.find_last_by_undertitle("The Last Topic!") }
680
+ end
681
+
682
+ def test_find_last_by_two_attributes
683
+ topic = Topic.last
684
+ assert_equal topic, Topic.find_last_by_title_and_author_name(topic.title, topic.author_name)
685
+ assert_nil Topic.find_last_by_title_and_author_name(topic.title, "Anonymous")
686
+ end
687
+
688
+ def test_find_all_by_one_attribute
689
+ topics = Topic.find_all_by_content("Have a nice day")
690
+ assert_equal 2, topics.size
691
+ assert topics.include?(topics(:first))
692
+
693
+ assert_equal [], Topic.find_all_by_title("The First Topic!!")
694
+ end
695
+
696
+ def test_find_all_by_one_attribute_which_is_a_symbol
697
+ topics = Topic.find_all_by_content("Have a nice day".to_sym)
698
+ assert_equal 2, topics.size
699
+ assert topics.include?(topics(:first))
700
+
701
+ assert_equal [], Topic.find_all_by_title("The First Topic!!")
702
+ end
703
+
704
+ def test_find_all_by_one_attribute_that_is_an_aggregate
705
+ balance = customers(:david).balance
706
+ assert_kind_of Money, balance
707
+ found_customers = Customer.find_all_by_balance(balance)
708
+ assert_equal 1, found_customers.size
709
+ assert_equal customers(:david), found_customers.first
710
+ end
711
+
712
+ def test_find_all_by_two_attributes_that_are_both_aggregates
713
+ balance = customers(:david).balance
714
+ address = customers(:david).address
715
+ assert_kind_of Money, balance
716
+ assert_kind_of Address, address
717
+ found_customers = Customer.find_all_by_balance_and_address(balance, address)
718
+ assert_equal 1, found_customers.size
719
+ assert_equal customers(:david), found_customers.first
720
+ end
721
+
722
+ def test_find_all_by_two_attributes_with_one_being_an_aggregate
723
+ balance = customers(:david).balance
724
+ assert_kind_of Money, balance
725
+ found_customers = Customer.find_all_by_balance_and_name(balance, customers(:david).name)
726
+ assert_equal 1, found_customers.size
727
+ assert_equal customers(:david), found_customers.first
728
+ end
729
+
730
+ def test_find_all_by_one_attribute_with_options
731
+ topics = Topic.find_all_by_content("Have a nice day", :order => "id DESC")
732
+ assert_equal topics(:first), topics.last
733
+
734
+ topics = Topic.find_all_by_content("Have a nice day", :order => "id")
735
+ assert_equal topics(:first), topics.first
736
+ end
737
+
738
+ def test_find_all_by_array_attribute
739
+ assert_equal 2, Topic.find_all_by_title(["The First Topic", "The Second Topic of the day"]).size
740
+ end
741
+
742
+ def test_find_all_by_boolean_attribute
743
+ topics = Topic.find_all_by_approved(false)
744
+ assert_equal 1, topics.size
745
+ assert topics.include?(topics(:first))
746
+
747
+ topics = Topic.find_all_by_approved(true)
748
+ assert_equal 3, topics.size
749
+ assert topics.include?(topics(:second))
750
+ end
751
+
752
+ def test_find_by_nil_attribute
753
+ topic = Topic.find_by_last_read nil
754
+ assert_not_nil topic
755
+ assert_nil topic.last_read
756
+ end
757
+
758
+ def test_find_all_by_nil_attribute
759
+ topics = Topic.find_all_by_last_read nil
760
+ assert_equal 3, topics.size
761
+ assert topics.collect(&:last_read).all?(&:nil?)
762
+ end
763
+
764
+ def test_find_by_nil_and_not_nil_attributes
765
+ topic = Topic.find_by_last_read_and_author_name nil, "Mary"
766
+ assert_equal "Mary", topic.author_name
767
+ end
768
+
769
+ def test_find_all_by_nil_and_not_nil_attributes
770
+ topics = Topic.find_all_by_last_read_and_author_name nil, "Mary"
771
+ assert_equal 1, topics.size
772
+ assert_equal "Mary", topics[0].author_name
773
+ end
774
+
775
+ def test_find_or_create_from_one_attribute
776
+ number_of_companies = Company.count
777
+ sig38 = Company.find_or_create_by_name("38signals")
778
+ assert_equal number_of_companies + 1, Company.count
779
+ assert_equal sig38, Company.find_or_create_by_name("38signals")
780
+ assert sig38.persisted?
781
+ end
782
+
783
+ def test_find_or_create_from_two_attributes
784
+ number_of_topics = Topic.count
785
+ another = Topic.find_or_create_by_title_and_author_name("Another topic","John")
786
+ assert_equal number_of_topics + 1, Topic.count
787
+ assert_equal another, Topic.find_or_create_by_title_and_author_name("Another topic", "John")
788
+ assert another.persisted?
789
+ end
790
+
791
+ def test_find_or_create_from_two_attributes_with_one_being_an_aggregate
792
+ number_of_customers = Customer.count
793
+ created_customer = Customer.find_or_create_by_balance_and_name(Money.new(123), "Elizabeth")
794
+ assert_equal number_of_customers + 1, Customer.count
795
+ assert_equal created_customer, Customer.find_or_create_by_balance(Money.new(123), "Elizabeth")
796
+ assert created_customer.persisted?
797
+ end
798
+
799
+ def test_find_or_create_from_one_attribute_and_hash
800
+ number_of_companies = Company.count
801
+ sig38 = Company.find_or_create_by_name({:name => "38signals", :firm_id => 17, :client_of => 23})
802
+ assert_equal number_of_companies + 1, Company.count
803
+ assert_equal sig38, Company.find_or_create_by_name({:name => "38signals", :firm_id => 17, :client_of => 23})
804
+ assert sig38.persisted?
805
+ assert_equal "38signals", sig38.name
806
+ assert_equal 17, sig38.firm_id
807
+ assert_equal 23, sig38.client_of
808
+ end
809
+
810
+ def test_find_or_create_from_one_aggregate_attribute
811
+ number_of_customers = Customer.count
812
+ created_customer = Customer.find_or_create_by_balance(Money.new(123))
813
+ assert_equal number_of_customers + 1, Customer.count
814
+ assert_equal created_customer, Customer.find_or_create_by_balance(Money.new(123))
815
+ assert created_customer.persisted?
816
+ end
817
+
818
+ def test_find_or_create_from_one_aggregate_attribute_and_hash
819
+ number_of_customers = Customer.count
820
+ balance = Money.new(123)
821
+ name = "Elizabeth"
822
+ created_customer = Customer.find_or_create_by_balance({:balance => balance, :name => name})
823
+ assert_equal number_of_customers + 1, Customer.count
824
+ assert_equal created_customer, Customer.find_or_create_by_balance({:balance => balance, :name => name})
825
+ assert created_customer.persisted?
826
+ assert_equal balance, created_customer.balance
827
+ assert_equal name, created_customer.name
828
+ end
829
+
830
+ def test_find_or_initialize_from_one_attribute
831
+ sig38 = Company.find_or_initialize_by_name("38signals")
832
+ assert_equal "38signals", sig38.name
833
+ assert !sig38.persisted?
834
+ end
835
+
836
+ def test_find_or_initialize_from_one_aggregate_attribute
837
+ new_customer = Customer.find_or_initialize_by_balance(Money.new(123))
838
+ assert_equal 123, new_customer.balance.amount
839
+ assert !new_customer.persisted?
840
+ end
841
+
842
+ def test_find_or_initialize_from_one_attribute_should_not_set_attribute_even_when_protected
843
+ c = Company.find_or_initialize_by_name({:name => "Fortune 1000", :rating => 1000})
844
+ assert_equal "Fortune 1000", c.name
845
+ assert_not_equal 1000, c.rating
846
+ assert c.valid?
847
+ assert !c.persisted?
848
+ end
849
+
850
+ def test_find_or_create_from_one_attribute_should_not_set_attribute_even_when_protected
851
+ c = Company.find_or_create_by_name({:name => "Fortune 1000", :rating => 1000})
852
+ assert_equal "Fortune 1000", c.name
853
+ assert_not_equal 1000, c.rating
854
+ assert c.valid?
855
+ assert c.persisted?
856
+ end
857
+
858
+ def test_find_or_initialize_from_one_attribute_should_set_attribute_even_when_protected
859
+ c = Company.find_or_initialize_by_name_and_rating("Fortune 1000", 1000)
860
+ assert_equal "Fortune 1000", c.name
861
+ assert_equal 1000, c.rating
862
+ assert c.valid?
863
+ assert !c.persisted?
864
+ end
865
+
866
+ def test_find_or_create_from_one_attribute_should_set_attribute_even_when_protected
867
+ c = Company.find_or_create_by_name_and_rating("Fortune 1000", 1000)
868
+ assert_equal "Fortune 1000", c.name
869
+ assert_equal 1000, c.rating
870
+ assert c.valid?
871
+ assert c.persisted?
872
+ end
873
+
874
+ def test_find_or_initialize_from_one_attribute_should_set_attribute_even_when_protected_and_also_set_the_hash
875
+ c = Company.find_or_initialize_by_rating(1000, {:name => "Fortune 1000"})
876
+ assert_equal "Fortune 1000", c.name
877
+ assert_equal 1000, c.rating
878
+ assert c.valid?
879
+ assert !c.persisted?
880
+ end
881
+
882
+ def test_find_or_create_from_one_attribute_should_set_attribute_even_when_protected_and_also_set_the_hash
883
+ c = Company.find_or_create_by_rating(1000, {:name => "Fortune 1000"})
884
+ assert_equal "Fortune 1000", c.name
885
+ assert_equal 1000, c.rating
886
+ assert c.valid?
887
+ assert c.persisted?
888
+ end
889
+
890
+ def test_find_or_initialize_should_set_protected_attributes_if_given_as_block
891
+ c = Company.find_or_initialize_by_name(:name => "Fortune 1000") { |f| f.rating = 1000 }
892
+ assert_equal "Fortune 1000", c.name
893
+ assert_equal 1000.to_f, c.rating.to_f
894
+ assert c.valid?
895
+ assert !c.persisted?
896
+ end
897
+
898
+ def test_find_or_create_should_set_protected_attributes_if_given_as_block
899
+ c = Company.find_or_create_by_name(:name => "Fortune 1000") { |f| f.rating = 1000 }
900
+ assert_equal "Fortune 1000", c.name
901
+ assert_equal 1000.to_f, c.rating.to_f
902
+ assert c.valid?
903
+ assert c.persisted?
904
+ end
905
+
906
+ def test_find_or_create_should_work_with_block_on_first_call
907
+ class << Company
908
+ undef_method(:find_or_create_by_name) if method_defined?(:find_or_create_by_name)
909
+ end
910
+ c = Company.find_or_create_by_name(:name => "Fortune 1000") { |f| f.rating = 1000 }
911
+ assert_equal "Fortune 1000", c.name
912
+ assert_equal 1000.to_f, c.rating.to_f
913
+ assert c.valid?
914
+ assert c.persisted?
915
+ end
916
+
917
+ def test_find_or_initialize_from_two_attributes
918
+ another = Topic.find_or_initialize_by_title_and_author_name("Another topic","John")
919
+ assert_equal "Another topic", another.title
920
+ assert_equal "John", another.author_name
921
+ assert !another.persisted?
922
+ end
923
+
924
+ def test_find_or_initialize_from_one_aggregate_attribute_and_one_not
925
+ new_customer = Customer.find_or_initialize_by_balance_and_name(Money.new(123), "Elizabeth")
926
+ assert_equal 123, new_customer.balance.amount
927
+ assert_equal "Elizabeth", new_customer.name
928
+ assert !new_customer.persisted?
929
+ end
930
+
931
+ def test_find_or_initialize_from_one_attribute_and_hash
932
+ sig38 = Company.find_or_initialize_by_name({:name => "38signals", :firm_id => 17, :client_of => 23})
933
+ assert_equal "38signals", sig38.name
934
+ assert_equal 17, sig38.firm_id
935
+ assert_equal 23, sig38.client_of
936
+ assert !sig38.persisted?
937
+ end
938
+
939
+ def test_find_or_initialize_from_one_aggregate_attribute_and_hash
940
+ balance = Money.new(123)
941
+ name = "Elizabeth"
942
+ new_customer = Customer.find_or_initialize_by_balance({:balance => balance, :name => name})
943
+ assert_equal balance, new_customer.balance
944
+ assert_equal name, new_customer.name
945
+ assert !new_customer.persisted?
946
+ end
947
+
948
+ def test_find_with_bad_sql
949
+ assert_raise(ActiveRecord::StatementInvalid) { Topic.find_by_sql "select 1 from badtable" }
950
+ end
951
+
952
+ def test_find_with_invalid_params
953
+ assert_raise(ArgumentError) { Topic.find :first, :join => "It should be `joins'" }
954
+ assert_raise(ArgumentError) { Topic.find :first, :conditions => '1 = 1', :join => "It should be `joins'" }
955
+ end
956
+
957
+ def test_dynamic_finder_with_invalid_params
958
+ assert_raise(ArgumentError) { Topic.find_by_title 'No Title', :join => "It should be `joins'" }
959
+ end
960
+
961
+ def test_find_all_with_join
962
+ developers_on_project_one = Developer.find(
963
+ :all,
964
+ :joins => 'LEFT JOIN developers_projects ON developers.id = developers_projects.developer_id',
965
+ :conditions => 'project_id=1'
966
+ )
967
+ assert_equal 3, developers_on_project_one.length
968
+ developer_names = developers_on_project_one.map { |d| d.name }
969
+ assert developer_names.include?('David')
970
+ assert developer_names.include?('Jamis')
971
+ end
972
+
973
+ def test_joins_dont_clobber_id
974
+ first = Firm.find(
975
+ :first,
976
+ :joins => 'INNER JOIN companies clients ON clients.firm_id = companies.id',
977
+ :conditions => 'companies.id = 1'
978
+ )
979
+ assert_equal 1, first.id
980
+ end
981
+
982
+ def test_joins_with_string_array
983
+ person_with_reader_and_post = Post.find(
984
+ :all,
985
+ :joins => [
986
+ "INNER JOIN categorizations ON categorizations.post_id = posts.id",
987
+ "INNER JOIN categories ON categories.id = categorizations.category_id AND categories.type = 'SpecialCategory'"
988
+ ]
989
+ )
990
+ assert_equal 1, person_with_reader_and_post.size
991
+ end
992
+
993
+ def test_find_by_id_with_conditions_with_or
994
+ assert_nothing_raised do
995
+ Post.find([1,2,3],
996
+ :conditions => "posts.id <= 3 OR posts.#{QUOTED_TYPE} = 'Post'")
997
+ end
998
+ end
999
+
1000
+ # http://dev.rubyonrails.org/ticket/6778
1001
+ def test_find_ignores_previously_inserted_record
1002
+ post = Post.create!(:title => 'test', :body => 'it out')
1003
+ assert_equal [], Post.find_all_by_id(nil)
1004
+ end
1005
+
1006
+ def test_find_by_empty_ids
1007
+ assert_equal [], Post.find([])
1008
+ end
1009
+
1010
+ def test_find_by_empty_in_condition
1011
+ assert_equal [], Post.find(:all, :conditions => ['id in (?)', []])
1012
+ end
1013
+
1014
+ def test_find_by_records
1015
+ p1, p2 = Post.find(:all, :limit => 2, :order => 'id asc')
1016
+ assert_equal [p1, p2], Post.find(:all, :conditions => ['id in (?)', [p1, p2]], :order => 'id asc')
1017
+ assert_equal [p1, p2], Post.find(:all, :conditions => ['id in (?)', [p1, p2.id]], :order => 'id asc')
1018
+ end
1019
+
1020
+ def test_select_value
1021
+ assert_equal "37signals", Company.connection.select_value("SELECT name FROM companies WHERE id = 1")
1022
+ assert_nil Company.connection.select_value("SELECT name FROM companies WHERE id = -1")
1023
+ # make sure we didn't break count...
1024
+ assert_equal 0, Company.count_by_sql("SELECT COUNT(*) FROM companies WHERE name = 'Halliburton'")
1025
+ assert_equal 1, Company.count_by_sql("SELECT COUNT(*) FROM companies WHERE name = '37signals'")
1026
+ end
1027
+
1028
+ def test_select_values
1029
+ assert_equal ["1","2","3","4","5","6","7","8","9", "10"], Company.connection.select_values("SELECT id FROM companies ORDER BY id").map! { |i| i.to_s }
1030
+ assert_equal ["37signals","Summit","Microsoft", "Flamboyant Software", "Ex Nihilo", "RailsCore", "Leetsoft", "Jadedpixel", "Odegy", "Ex Nihilo Part Deux"], Company.connection.select_values("SELECT name FROM companies ORDER BY id")
1031
+ end
1032
+
1033
+ def test_select_rows
1034
+ assert_equal(
1035
+ [["1", nil, nil, "37signals"],
1036
+ ["2", "1", "2", "Summit"],
1037
+ ["3", "1", "1", "Microsoft"]],
1038
+ Company.connection.select_rows("SELECT id, firm_id, client_of, name FROM companies WHERE id IN (1,2,3) ORDER BY id").map! {|i| i.map! {|j| j.to_s unless j.nil?}})
1039
+ assert_equal [["1", "37signals"], ["2", "Summit"], ["3", "Microsoft"]],
1040
+ Company.connection.select_rows("SELECT id, name FROM companies WHERE id IN (1,2,3) ORDER BY id").map! {|i| i.map! {|j| j.to_s unless j.nil?}}
1041
+ end
1042
+
1043
+ unless current_adapter?(:IBM_DBAdapter)
1044
+ def test_find_with_order_on_included_associations_with_construct_finder_sql_for_association_limiting_and_is_distinct
1045
+ assert_equal 2, Post.find(:all, :include => { :authors => :author_address }, :order => ' author_addresses.id DESC ', :limit => 2).size
1046
+
1047
+ assert_equal 3, Post.find(:all, :include => { :author => :author_address, :authors => :author_address},
1048
+ :order => ' author_addresses_authors.id DESC ', :limit => 3).size
1049
+ end
1050
+ end
1051
+
1052
+ def test_with_limiting_with_custom_select
1053
+ posts = Post.find(:all, :include => :author, :select => ' posts.*, authors.id as "author_id"', :limit => 3, :order => 'posts.id')
1054
+ assert_equal 3, posts.size
1055
+ assert_equal [0, 1, 1], posts.map(&:author_id).sort
1056
+ end
1057
+
1058
+ def test_finder_with_scoped_from
1059
+ all_topics = Topic.find(:all)
1060
+
1061
+ Topic.send(:with_scope, :find => { :from => 'fake_topics' }) do
1062
+ assert_equal all_topics, Topic.from('topics').to_a
1063
+ end
1064
+ end
1065
+
1066
+ protected
1067
+ def bind(statement, *vars)
1068
+ if vars.first.is_a?(Hash)
1069
+ ActiveRecord::Base.send(:replace_named_bind_variables, statement, vars.first)
1070
+ else
1071
+ ActiveRecord::Base.send(:replace_bind_variables, statement, vars)
1072
+ end
1073
+ end
1074
+
1075
+ def with_env_tz(new_tz = 'US/Eastern')
1076
+ old_tz, ENV['TZ'] = ENV['TZ'], new_tz
1077
+ yield
1078
+ ensure
1079
+ old_tz ? ENV['TZ'] = old_tz : ENV.delete('TZ')
1080
+ end
1081
+
1082
+ def with_active_record_default_timezone(zone)
1083
+ old_zone, ActiveRecord::Base.default_timezone = ActiveRecord::Base.default_timezone, zone
1084
+ yield
1085
+ ensure
1086
+ ActiveRecord::Base.default_timezone = old_zone
1087
+ end
1088
+ end