db2 2.5.10 → 2.6.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (40) hide show
  1. data/.gitignore +1 -0
  2. data/CHANGES +0 -17
  3. data/LICENSE +18 -0
  4. data/ParameterizedQueries README +39 -0
  5. data/README +141 -79
  6. data/ext/Makefile.nt32 +181 -0
  7. data/ext/extconf.rb +14 -75
  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 +2557 -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/test/cases/adapter_test.rb +202 -0
  18. data/test/cases/associations/belongs_to_associations_test.rb +486 -0
  19. data/test/cases/associations/cascaded_eager_loading_test.rb +183 -0
  20. data/test/cases/associations/eager_test.rb +862 -0
  21. data/test/cases/associations/has_and_belongs_to_many_associations_test.rb +917 -0
  22. data/test/cases/associations/has_many_through_associations_test.rb +461 -0
  23. data/test/cases/associations/join_model_test.rb +793 -0
  24. data/test/cases/attribute_methods_test.rb +621 -0
  25. data/test/cases/base_test.rb +1486 -0
  26. data/test/cases/calculations_test.rb +362 -0
  27. data/test/cases/finder_test.rb +1088 -0
  28. data/test/cases/fixtures_test.rb +684 -0
  29. data/test/cases/migration_test.rb +2014 -0
  30. data/test/cases/schema_dumper_test.rb +232 -0
  31. data/test/cases/validations/uniqueness_validation_test.rb +283 -0
  32. data/test/connections/native_ibm_db/connection.rb +42 -0
  33. data/test/ibm_db_test.rb +25 -0
  34. data/test/models/warehouse_thing.rb +5 -0
  35. data/test/schema/i5/ibm_db_specific_schema.rb +135 -0
  36. data/test/schema/ids/ibm_db_specific_schema.rb +138 -0
  37. data/test/schema/luw/ibm_db_specific_schema.rb +135 -0
  38. data/test/schema/schema.rb +647 -0
  39. data/test/schema/zOS/ibm_db_specific_schema.rb +206 -0
  40. metadata +68 -32
@@ -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