ibm_db 1.1.1-mswin32

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