diff options
Diffstat (limited to 'activerecord/test/cases')
43 files changed, 783 insertions, 122 deletions
diff --git a/activerecord/test/cases/adapter_test.rb b/activerecord/test/cases/adapter_test.rb index ba04859bf0..ce2ed06c1d 100644 --- a/activerecord/test/cases/adapter_test.rb +++ b/activerecord/test/cases/adapter_test.rb @@ -490,6 +490,8 @@ module ActiveRecord @connection.truncate("posts") assert_equal 0, Post.count + ensure + reset_fixtures("posts") end def test_truncate_with_query_cache @@ -501,6 +503,7 @@ module ActiveRecord assert_equal 0, Post.count ensure + reset_fixtures("posts") @connection.disable_query_cache! end @@ -514,6 +517,8 @@ module ActiveRecord assert_equal 0, Post.count assert_equal 0, Author.count assert_equal 0, AuthorAddress.count + ensure + reset_fixtures("posts", "authors", "author_addresses") end def test_truncate_tables_with_query_cache @@ -529,6 +534,7 @@ module ActiveRecord assert_equal 0, Author.count assert_equal 0, AuthorAddress.count ensure + reset_fixtures("posts", "authors", "author_addresses") @connection.disable_query_cache! end @@ -551,6 +557,16 @@ module ActiveRecord assert_nothing_raised { sub.save! } end end + + private + + def reset_fixtures(*fixture_names) + ActiveRecord::FixtureSet.reset_cache + + fixture_names.each do |fixture_name| + ActiveRecord::FixtureSet.create_fixtures(FIXTURES_ROOT, fixture_name) + end + end end end diff --git a/activerecord/test/cases/adapters/mysql2/active_schema_test.rb b/activerecord/test/cases/adapters/mysql2/active_schema_test.rb index 88c2ac5d0a..c2c357d0c1 100644 --- a/activerecord/test/cases/adapters/mysql2/active_schema_test.rb +++ b/activerecord/test/cases/adapters/mysql2/active_schema_test.rb @@ -10,7 +10,15 @@ class Mysql2ActiveSchemaTest < ActiveRecord::Mysql2TestCase ActiveRecord::Base.connection.send(:default_row_format) ActiveRecord::Base.connection.singleton_class.class_eval do alias_method :execute_without_stub, :execute - def execute(sql, name = nil) sql end + def execute(sql, name = nil) + ActiveSupport::Notifications.instrumenter.instrument( + "sql.active_record", + sql: sql, + name: name, + connection: self) do + sql + end + end end end @@ -89,17 +97,19 @@ class Mysql2ActiveSchemaTest < ActiveRecord::Mysql2TestCase %w(SPATIAL FULLTEXT UNIQUE).each do |type| expected = "ALTER TABLE `people` ADD #{type} INDEX `index_people_on_last_name` (`last_name`)" - actual = ActiveRecord::Base.connection.change_table(:people, bulk: true) do |t| - t.index :last_name, type: type + assert_sql(expected) do + ActiveRecord::Base.connection.change_table(:people, bulk: true) do |t| + t.index :last_name, type: type + end end - assert_equal expected, actual end expected = "ALTER TABLE `people` ADD INDEX `index_people_on_last_name` USING btree (`last_name`(10)), ALGORITHM = COPY" - actual = ActiveRecord::Base.connection.change_table(:people, bulk: true) do |t| - t.index :last_name, length: 10, using: :btree, algorithm: :copy + assert_sql(expected) do + ActiveRecord::Base.connection.change_table(:people, bulk: true) do |t| + t.index :last_name, length: 10, using: :btree, algorithm: :copy + end end - assert_equal expected, actual end def test_drop_table diff --git a/activerecord/test/cases/adapters/mysql2/count_deleted_rows_with_lock_test.rb b/activerecord/test/cases/adapters/mysql2/count_deleted_rows_with_lock_test.rb new file mode 100644 index 0000000000..4d361e405c --- /dev/null +++ b/activerecord/test/cases/adapters/mysql2/count_deleted_rows_with_lock_test.rb @@ -0,0 +1,28 @@ +# frozen_string_literal: true + +require "cases/helper" +require "support/connection_helper" +require "models/author" +require "models/bulb" + +module ActiveRecord + class CountDeletedRowsWithLockTest < ActiveRecord::Mysql2TestCase + test "delete and create in different threads synchronize correctly" do + Bulb.unscoped.delete_all + Bulb.create!(name: "Jimmy", color: "blue") + + delete_thread = Thread.new do + Bulb.unscoped.delete_all + end + + create_thread = Thread.new do + Author.create!(name: "Tommy") + end + + delete_thread.join + create_thread.join + + assert_equal 1, delete_thread.value + end + end +end diff --git a/activerecord/test/cases/adapters/postgresql/bytea_test.rb b/activerecord/test/cases/adapters/postgresql/bytea_test.rb index 3988c2adca..531e6b2328 100644 --- a/activerecord/test/cases/adapters/postgresql/bytea_test.rb +++ b/activerecord/test/cases/adapters/postgresql/bytea_test.rb @@ -35,7 +35,7 @@ class PostgresqlByteaTest < ActiveRecord::PostgreSQLTestCase def test_binary_columns_are_limitless_the_upper_limit_is_one_GB assert_equal "bytea", @connection.type_to_sql(:binary, limit: 100_000) - assert_raise ActiveRecord::ActiveRecordError do + assert_raise ArgumentError do @connection.type_to_sql(:binary, limit: 4294967295) end end diff --git a/activerecord/test/cases/adapters/postgresql/datatype_test.rb b/activerecord/test/cases/adapters/postgresql/datatype_test.rb index b7535d5c9a..562cf1f2d1 100644 --- a/activerecord/test/cases/adapters/postgresql/datatype_test.rb +++ b/activerecord/test/cases/adapters/postgresql/datatype_test.rb @@ -64,7 +64,7 @@ class PostgresqlDataTypeTest < ActiveRecord::PostgreSQLTestCase def test_text_columns_are_limitless_the_upper_limit_is_one_GB assert_equal "text", @connection.type_to_sql(:text, limit: 100_000) - assert_raise ActiveRecord::ActiveRecordError do + assert_raise ArgumentError do @connection.type_to_sql(:text, limit: 4294967295) end end diff --git a/activerecord/test/cases/ar_schema_test.rb b/activerecord/test/cases/ar_schema_test.rb index 9027cc582a..7aa6d089c5 100644 --- a/activerecord/test/cases/ar_schema_test.rb +++ b/activerecord/test/cases/ar_schema_test.rb @@ -160,7 +160,7 @@ class ActiveRecordSchemaTest < ActiveRecord::TestCase end if subsecond_precision_supported? - def test_timestamps_sets_presicion_on_create_table + def test_timestamps_sets_precision_on_create_table ActiveRecord::Schema.define do create_table :has_timestamps do |t| t.timestamps @@ -171,7 +171,7 @@ class ActiveRecordSchemaTest < ActiveRecord::TestCase assert @connection.column_exists?(:has_timestamps, :updated_at, precision: 6, null: false) end - def test_timestamps_sets_presicion_on_change_table + def test_timestamps_sets_precision_on_change_table ActiveRecord::Schema.define do create_table :has_timestamps @@ -185,7 +185,7 @@ class ActiveRecordSchemaTest < ActiveRecord::TestCase end if ActiveRecord::Base.connection.supports_bulk_alter? - def test_timestamps_sets_presicion_on_change_table_with_bulk + def test_timestamps_sets_precision_on_change_table_with_bulk ActiveRecord::Schema.define do create_table :has_timestamps @@ -199,7 +199,7 @@ class ActiveRecordSchemaTest < ActiveRecord::TestCase end end - def test_timestamps_sets_presicion_on_add_timestamps + def test_timestamps_sets_precision_on_add_timestamps ActiveRecord::Schema.define do create_table :has_timestamps add_timestamps :has_timestamps, default: Time.now diff --git a/activerecord/test/cases/arel/visitors/oracle12_test.rb b/activerecord/test/cases/arel/visitors/oracle12_test.rb index 4ce5cab4db..ebea12910d 100644 --- a/activerecord/test/cases/arel/visitors/oracle12_test.rb +++ b/activerecord/test/cases/arel/visitors/oracle12_test.rb @@ -8,6 +8,7 @@ module Arel before do @visitor = Oracle12.new Table.engine.connection @table = Table.new(:users) + @attr = @table[:id] end def compile(node) @@ -95,6 +96,26 @@ module Arel sql.must_be_like %{ "users"."name" IS NOT NULL } end end + + describe "Nodes::In" do + it "should know how to visit" do + ary = (1 .. 1001).to_a + node = @attr.in ary + compile(node).must_be_like %{ + "users"."id" IN (1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255, 256, 257, 258, 259, 260, 261, 262, 263, 264, 265, 266, 267, 268, 269, 270, 271, 272, 273, 274, 275, 276, 277, 278, 279, 280, 281, 282, 283, 284, 285, 286, 287, 288, 289, 290, 291, 292, 293, 294, 295, 296, 297, 298, 299, 300, 301, 302, 303, 304, 305, 306, 307, 308, 309, 310, 311, 312, 313, 314, 315, 316, 317, 318, 319, 320, 321, 322, 323, 324, 325, 326, 327, 328, 329, 330, 331, 332, 333, 334, 335, 336, 337, 338, 339, 340, 341, 342, 343, 344, 345, 346, 347, 348, 349, 350, 351, 352, 353, 354, 355, 356, 357, 358, 359, 360, 361, 362, 363, 364, 365, 366, 367, 368, 369, 370, 371, 372, 373, 374, 375, 376, 377, 378, 379, 380, 381, 382, 383, 384, 385, 386, 387, 388, 389, 390, 391, 392, 393, 394, 395, 396, 397, 398, 399, 400, 401, 402, 403, 404, 405, 406, 407, 408, 409, 410, 411, 412, 413, 414, 415, 416, 417, 418, 419, 420, 421, 422, 423, 424, 425, 426, 427, 428, 429, 430, 431, 432, 433, 434, 435, 436, 437, 438, 439, 440, 441, 442, 443, 444, 445, 446, 447, 448, 449, 450, 451, 452, 453, 454, 455, 456, 457, 458, 459, 460, 461, 462, 463, 464, 465, 466, 467, 468, 469, 470, 471, 472, 473, 474, 475, 476, 477, 478, 479, 480, 481, 482, 483, 484, 485, 486, 487, 488, 489, 490, 491, 492, 493, 494, 495, 496, 497, 498, 499, 500, 501, 502, 503, 504, 505, 506, 507, 508, 509, 510, 511, 512, 513, 514, 515, 516, 517, 518, 519, 520, 521, 522, 523, 524, 525, 526, 527, 528, 529, 530, 531, 532, 533, 534, 535, 536, 537, 538, 539, 540, 541, 542, 543, 544, 545, 546, 547, 548, 549, 550, 551, 552, 553, 554, 555, 556, 557, 558, 559, 560, 561, 562, 563, 564, 565, 566, 567, 568, 569, 570, 571, 572, 573, 574, 575, 576, 577, 578, 579, 580, 581, 582, 583, 584, 585, 586, 587, 588, 589, 590, 591, 592, 593, 594, 595, 596, 597, 598, 599, 600, 601, 602, 603, 604, 605, 606, 607, 608, 609, 610, 611, 612, 613, 614, 615, 616, 617, 618, 619, 620, 621, 622, 623, 624, 625, 626, 627, 628, 629, 630, 631, 632, 633, 634, 635, 636, 637, 638, 639, 640, 641, 642, 643, 644, 645, 646, 647, 648, 649, 650, 651, 652, 653, 654, 655, 656, 657, 658, 659, 660, 661, 662, 663, 664, 665, 666, 667, 668, 669, 670, 671, 672, 673, 674, 675, 676, 677, 678, 679, 680, 681, 682, 683, 684, 685, 686, 687, 688, 689, 690, 691, 692, 693, 694, 695, 696, 697, 698, 699, 700, 701, 702, 703, 704, 705, 706, 707, 708, 709, 710, 711, 712, 713, 714, 715, 716, 717, 718, 719, 720, 721, 722, 723, 724, 725, 726, 727, 728, 729, 730, 731, 732, 733, 734, 735, 736, 737, 738, 739, 740, 741, 742, 743, 744, 745, 746, 747, 748, 749, 750, 751, 752, 753, 754, 755, 756, 757, 758, 759, 760, 761, 762, 763, 764, 765, 766, 767, 768, 769, 770, 771, 772, 773, 774, 775, 776, 777, 778, 779, 780, 781, 782, 783, 784, 785, 786, 787, 788, 789, 790, 791, 792, 793, 794, 795, 796, 797, 798, 799, 800, 801, 802, 803, 804, 805, 806, 807, 808, 809, 810, 811, 812, 813, 814, 815, 816, 817, 818, 819, 820, 821, 822, 823, 824, 825, 826, 827, 828, 829, 830, 831, 832, 833, 834, 835, 836, 837, 838, 839, 840, 841, 842, 843, 844, 845, 846, 847, 848, 849, 850, 851, 852, 853, 854, 855, 856, 857, 858, 859, 860, 861, 862, 863, 864, 865, 866, 867, 868, 869, 870, 871, 872, 873, 874, 875, 876, 877, 878, 879, 880, 881, 882, 883, 884, 885, 886, 887, 888, 889, 890, 891, 892, 893, 894, 895, 896, 897, 898, 899, 900, 901, 902, 903, 904, 905, 906, 907, 908, 909, 910, 911, 912, 913, 914, 915, 916, 917, 918, 919, 920, 921, 922, 923, 924, 925, 926, 927, 928, 929, 930, 931, 932, 933, 934, 935, 936, 937, 938, 939, 940, 941, 942, 943, 944, 945, 946, 947, 948, 949, 950, 951, 952, 953, 954, 955, 956, 957, 958, 959, 960, 961, 962, 963, 964, 965, 966, 967, 968, 969, 970, 971, 972, 973, 974, 975, 976, 977, 978, 979, 980, 981, 982, 983, 984, 985, 986, 987, 988, 989, 990, 991, 992, 993, 994, 995, 996, 997, 998, 999, 1000) OR \"users\".\"id\" IN (1001) + } + end + end + + describe "Nodes::NotIn" do + it "should know how to visit" do + ary = (1 .. 1001).to_a + node = @attr.not_in ary + compile(node).must_be_like %{ + "users"."id" NOT IN (1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255, 256, 257, 258, 259, 260, 261, 262, 263, 264, 265, 266, 267, 268, 269, 270, 271, 272, 273, 274, 275, 276, 277, 278, 279, 280, 281, 282, 283, 284, 285, 286, 287, 288, 289, 290, 291, 292, 293, 294, 295, 296, 297, 298, 299, 300, 301, 302, 303, 304, 305, 306, 307, 308, 309, 310, 311, 312, 313, 314, 315, 316, 317, 318, 319, 320, 321, 322, 323, 324, 325, 326, 327, 328, 329, 330, 331, 332, 333, 334, 335, 336, 337, 338, 339, 340, 341, 342, 343, 344, 345, 346, 347, 348, 349, 350, 351, 352, 353, 354, 355, 356, 357, 358, 359, 360, 361, 362, 363, 364, 365, 366, 367, 368, 369, 370, 371, 372, 373, 374, 375, 376, 377, 378, 379, 380, 381, 382, 383, 384, 385, 386, 387, 388, 389, 390, 391, 392, 393, 394, 395, 396, 397, 398, 399, 400, 401, 402, 403, 404, 405, 406, 407, 408, 409, 410, 411, 412, 413, 414, 415, 416, 417, 418, 419, 420, 421, 422, 423, 424, 425, 426, 427, 428, 429, 430, 431, 432, 433, 434, 435, 436, 437, 438, 439, 440, 441, 442, 443, 444, 445, 446, 447, 448, 449, 450, 451, 452, 453, 454, 455, 456, 457, 458, 459, 460, 461, 462, 463, 464, 465, 466, 467, 468, 469, 470, 471, 472, 473, 474, 475, 476, 477, 478, 479, 480, 481, 482, 483, 484, 485, 486, 487, 488, 489, 490, 491, 492, 493, 494, 495, 496, 497, 498, 499, 500, 501, 502, 503, 504, 505, 506, 507, 508, 509, 510, 511, 512, 513, 514, 515, 516, 517, 518, 519, 520, 521, 522, 523, 524, 525, 526, 527, 528, 529, 530, 531, 532, 533, 534, 535, 536, 537, 538, 539, 540, 541, 542, 543, 544, 545, 546, 547, 548, 549, 550, 551, 552, 553, 554, 555, 556, 557, 558, 559, 560, 561, 562, 563, 564, 565, 566, 567, 568, 569, 570, 571, 572, 573, 574, 575, 576, 577, 578, 579, 580, 581, 582, 583, 584, 585, 586, 587, 588, 589, 590, 591, 592, 593, 594, 595, 596, 597, 598, 599, 600, 601, 602, 603, 604, 605, 606, 607, 608, 609, 610, 611, 612, 613, 614, 615, 616, 617, 618, 619, 620, 621, 622, 623, 624, 625, 626, 627, 628, 629, 630, 631, 632, 633, 634, 635, 636, 637, 638, 639, 640, 641, 642, 643, 644, 645, 646, 647, 648, 649, 650, 651, 652, 653, 654, 655, 656, 657, 658, 659, 660, 661, 662, 663, 664, 665, 666, 667, 668, 669, 670, 671, 672, 673, 674, 675, 676, 677, 678, 679, 680, 681, 682, 683, 684, 685, 686, 687, 688, 689, 690, 691, 692, 693, 694, 695, 696, 697, 698, 699, 700, 701, 702, 703, 704, 705, 706, 707, 708, 709, 710, 711, 712, 713, 714, 715, 716, 717, 718, 719, 720, 721, 722, 723, 724, 725, 726, 727, 728, 729, 730, 731, 732, 733, 734, 735, 736, 737, 738, 739, 740, 741, 742, 743, 744, 745, 746, 747, 748, 749, 750, 751, 752, 753, 754, 755, 756, 757, 758, 759, 760, 761, 762, 763, 764, 765, 766, 767, 768, 769, 770, 771, 772, 773, 774, 775, 776, 777, 778, 779, 780, 781, 782, 783, 784, 785, 786, 787, 788, 789, 790, 791, 792, 793, 794, 795, 796, 797, 798, 799, 800, 801, 802, 803, 804, 805, 806, 807, 808, 809, 810, 811, 812, 813, 814, 815, 816, 817, 818, 819, 820, 821, 822, 823, 824, 825, 826, 827, 828, 829, 830, 831, 832, 833, 834, 835, 836, 837, 838, 839, 840, 841, 842, 843, 844, 845, 846, 847, 848, 849, 850, 851, 852, 853, 854, 855, 856, 857, 858, 859, 860, 861, 862, 863, 864, 865, 866, 867, 868, 869, 870, 871, 872, 873, 874, 875, 876, 877, 878, 879, 880, 881, 882, 883, 884, 885, 886, 887, 888, 889, 890, 891, 892, 893, 894, 895, 896, 897, 898, 899, 900, 901, 902, 903, 904, 905, 906, 907, 908, 909, 910, 911, 912, 913, 914, 915, 916, 917, 918, 919, 920, 921, 922, 923, 924, 925, 926, 927, 928, 929, 930, 931, 932, 933, 934, 935, 936, 937, 938, 939, 940, 941, 942, 943, 944, 945, 946, 947, 948, 949, 950, 951, 952, 953, 954, 955, 956, 957, 958, 959, 960, 961, 962, 963, 964, 965, 966, 967, 968, 969, 970, 971, 972, 973, 974, 975, 976, 977, 978, 979, 980, 981, 982, 983, 984, 985, 986, 987, 988, 989, 990, 991, 992, 993, 994, 995, 996, 997, 998, 999, 1000) AND \"users\".\"id\" NOT IN (1001) + } + end + end end end end diff --git a/activerecord/test/cases/arel/visitors/oracle_test.rb b/activerecord/test/cases/arel/visitors/oracle_test.rb index 893edc7f74..f69b201855 100644 --- a/activerecord/test/cases/arel/visitors/oracle_test.rb +++ b/activerecord/test/cases/arel/visitors/oracle_test.rb @@ -8,6 +8,7 @@ module Arel before do @visitor = Oracle.new Table.engine.connection @table = Table.new(:users) + @attr = @table[:id] end def compile(node) @@ -231,6 +232,26 @@ module Arel sql.must_be_like %{ "users"."name" IS NOT NULL } end end + + describe "Nodes::In" do + it "should know how to visit" do + ary = (1 .. 1001).to_a + node = @attr.in ary + compile(node).must_be_like %{ + "users"."id" IN (1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255, 256, 257, 258, 259, 260, 261, 262, 263, 264, 265, 266, 267, 268, 269, 270, 271, 272, 273, 274, 275, 276, 277, 278, 279, 280, 281, 282, 283, 284, 285, 286, 287, 288, 289, 290, 291, 292, 293, 294, 295, 296, 297, 298, 299, 300, 301, 302, 303, 304, 305, 306, 307, 308, 309, 310, 311, 312, 313, 314, 315, 316, 317, 318, 319, 320, 321, 322, 323, 324, 325, 326, 327, 328, 329, 330, 331, 332, 333, 334, 335, 336, 337, 338, 339, 340, 341, 342, 343, 344, 345, 346, 347, 348, 349, 350, 351, 352, 353, 354, 355, 356, 357, 358, 359, 360, 361, 362, 363, 364, 365, 366, 367, 368, 369, 370, 371, 372, 373, 374, 375, 376, 377, 378, 379, 380, 381, 382, 383, 384, 385, 386, 387, 388, 389, 390, 391, 392, 393, 394, 395, 396, 397, 398, 399, 400, 401, 402, 403, 404, 405, 406, 407, 408, 409, 410, 411, 412, 413, 414, 415, 416, 417, 418, 419, 420, 421, 422, 423, 424, 425, 426, 427, 428, 429, 430, 431, 432, 433, 434, 435, 436, 437, 438, 439, 440, 441, 442, 443, 444, 445, 446, 447, 448, 449, 450, 451, 452, 453, 454, 455, 456, 457, 458, 459, 460, 461, 462, 463, 464, 465, 466, 467, 468, 469, 470, 471, 472, 473, 474, 475, 476, 477, 478, 479, 480, 481, 482, 483, 484, 485, 486, 487, 488, 489, 490, 491, 492, 493, 494, 495, 496, 497, 498, 499, 500, 501, 502, 503, 504, 505, 506, 507, 508, 509, 510, 511, 512, 513, 514, 515, 516, 517, 518, 519, 520, 521, 522, 523, 524, 525, 526, 527, 528, 529, 530, 531, 532, 533, 534, 535, 536, 537, 538, 539, 540, 541, 542, 543, 544, 545, 546, 547, 548, 549, 550, 551, 552, 553, 554, 555, 556, 557, 558, 559, 560, 561, 562, 563, 564, 565, 566, 567, 568, 569, 570, 571, 572, 573, 574, 575, 576, 577, 578, 579, 580, 581, 582, 583, 584, 585, 586, 587, 588, 589, 590, 591, 592, 593, 594, 595, 596, 597, 598, 599, 600, 601, 602, 603, 604, 605, 606, 607, 608, 609, 610, 611, 612, 613, 614, 615, 616, 617, 618, 619, 620, 621, 622, 623, 624, 625, 626, 627, 628, 629, 630, 631, 632, 633, 634, 635, 636, 637, 638, 639, 640, 641, 642, 643, 644, 645, 646, 647, 648, 649, 650, 651, 652, 653, 654, 655, 656, 657, 658, 659, 660, 661, 662, 663, 664, 665, 666, 667, 668, 669, 670, 671, 672, 673, 674, 675, 676, 677, 678, 679, 680, 681, 682, 683, 684, 685, 686, 687, 688, 689, 690, 691, 692, 693, 694, 695, 696, 697, 698, 699, 700, 701, 702, 703, 704, 705, 706, 707, 708, 709, 710, 711, 712, 713, 714, 715, 716, 717, 718, 719, 720, 721, 722, 723, 724, 725, 726, 727, 728, 729, 730, 731, 732, 733, 734, 735, 736, 737, 738, 739, 740, 741, 742, 743, 744, 745, 746, 747, 748, 749, 750, 751, 752, 753, 754, 755, 756, 757, 758, 759, 760, 761, 762, 763, 764, 765, 766, 767, 768, 769, 770, 771, 772, 773, 774, 775, 776, 777, 778, 779, 780, 781, 782, 783, 784, 785, 786, 787, 788, 789, 790, 791, 792, 793, 794, 795, 796, 797, 798, 799, 800, 801, 802, 803, 804, 805, 806, 807, 808, 809, 810, 811, 812, 813, 814, 815, 816, 817, 818, 819, 820, 821, 822, 823, 824, 825, 826, 827, 828, 829, 830, 831, 832, 833, 834, 835, 836, 837, 838, 839, 840, 841, 842, 843, 844, 845, 846, 847, 848, 849, 850, 851, 852, 853, 854, 855, 856, 857, 858, 859, 860, 861, 862, 863, 864, 865, 866, 867, 868, 869, 870, 871, 872, 873, 874, 875, 876, 877, 878, 879, 880, 881, 882, 883, 884, 885, 886, 887, 888, 889, 890, 891, 892, 893, 894, 895, 896, 897, 898, 899, 900, 901, 902, 903, 904, 905, 906, 907, 908, 909, 910, 911, 912, 913, 914, 915, 916, 917, 918, 919, 920, 921, 922, 923, 924, 925, 926, 927, 928, 929, 930, 931, 932, 933, 934, 935, 936, 937, 938, 939, 940, 941, 942, 943, 944, 945, 946, 947, 948, 949, 950, 951, 952, 953, 954, 955, 956, 957, 958, 959, 960, 961, 962, 963, 964, 965, 966, 967, 968, 969, 970, 971, 972, 973, 974, 975, 976, 977, 978, 979, 980, 981, 982, 983, 984, 985, 986, 987, 988, 989, 990, 991, 992, 993, 994, 995, 996, 997, 998, 999, 1000) OR \"users\".\"id\" IN (1001) + } + end + end + + describe "Nodes::NotIn" do + it "should know how to visit" do + ary = (1 .. 1001).to_a + node = @attr.not_in ary + compile(node).must_be_like %{ + "users"."id" NOT IN (1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255, 256, 257, 258, 259, 260, 261, 262, 263, 264, 265, 266, 267, 268, 269, 270, 271, 272, 273, 274, 275, 276, 277, 278, 279, 280, 281, 282, 283, 284, 285, 286, 287, 288, 289, 290, 291, 292, 293, 294, 295, 296, 297, 298, 299, 300, 301, 302, 303, 304, 305, 306, 307, 308, 309, 310, 311, 312, 313, 314, 315, 316, 317, 318, 319, 320, 321, 322, 323, 324, 325, 326, 327, 328, 329, 330, 331, 332, 333, 334, 335, 336, 337, 338, 339, 340, 341, 342, 343, 344, 345, 346, 347, 348, 349, 350, 351, 352, 353, 354, 355, 356, 357, 358, 359, 360, 361, 362, 363, 364, 365, 366, 367, 368, 369, 370, 371, 372, 373, 374, 375, 376, 377, 378, 379, 380, 381, 382, 383, 384, 385, 386, 387, 388, 389, 390, 391, 392, 393, 394, 395, 396, 397, 398, 399, 400, 401, 402, 403, 404, 405, 406, 407, 408, 409, 410, 411, 412, 413, 414, 415, 416, 417, 418, 419, 420, 421, 422, 423, 424, 425, 426, 427, 428, 429, 430, 431, 432, 433, 434, 435, 436, 437, 438, 439, 440, 441, 442, 443, 444, 445, 446, 447, 448, 449, 450, 451, 452, 453, 454, 455, 456, 457, 458, 459, 460, 461, 462, 463, 464, 465, 466, 467, 468, 469, 470, 471, 472, 473, 474, 475, 476, 477, 478, 479, 480, 481, 482, 483, 484, 485, 486, 487, 488, 489, 490, 491, 492, 493, 494, 495, 496, 497, 498, 499, 500, 501, 502, 503, 504, 505, 506, 507, 508, 509, 510, 511, 512, 513, 514, 515, 516, 517, 518, 519, 520, 521, 522, 523, 524, 525, 526, 527, 528, 529, 530, 531, 532, 533, 534, 535, 536, 537, 538, 539, 540, 541, 542, 543, 544, 545, 546, 547, 548, 549, 550, 551, 552, 553, 554, 555, 556, 557, 558, 559, 560, 561, 562, 563, 564, 565, 566, 567, 568, 569, 570, 571, 572, 573, 574, 575, 576, 577, 578, 579, 580, 581, 582, 583, 584, 585, 586, 587, 588, 589, 590, 591, 592, 593, 594, 595, 596, 597, 598, 599, 600, 601, 602, 603, 604, 605, 606, 607, 608, 609, 610, 611, 612, 613, 614, 615, 616, 617, 618, 619, 620, 621, 622, 623, 624, 625, 626, 627, 628, 629, 630, 631, 632, 633, 634, 635, 636, 637, 638, 639, 640, 641, 642, 643, 644, 645, 646, 647, 648, 649, 650, 651, 652, 653, 654, 655, 656, 657, 658, 659, 660, 661, 662, 663, 664, 665, 666, 667, 668, 669, 670, 671, 672, 673, 674, 675, 676, 677, 678, 679, 680, 681, 682, 683, 684, 685, 686, 687, 688, 689, 690, 691, 692, 693, 694, 695, 696, 697, 698, 699, 700, 701, 702, 703, 704, 705, 706, 707, 708, 709, 710, 711, 712, 713, 714, 715, 716, 717, 718, 719, 720, 721, 722, 723, 724, 725, 726, 727, 728, 729, 730, 731, 732, 733, 734, 735, 736, 737, 738, 739, 740, 741, 742, 743, 744, 745, 746, 747, 748, 749, 750, 751, 752, 753, 754, 755, 756, 757, 758, 759, 760, 761, 762, 763, 764, 765, 766, 767, 768, 769, 770, 771, 772, 773, 774, 775, 776, 777, 778, 779, 780, 781, 782, 783, 784, 785, 786, 787, 788, 789, 790, 791, 792, 793, 794, 795, 796, 797, 798, 799, 800, 801, 802, 803, 804, 805, 806, 807, 808, 809, 810, 811, 812, 813, 814, 815, 816, 817, 818, 819, 820, 821, 822, 823, 824, 825, 826, 827, 828, 829, 830, 831, 832, 833, 834, 835, 836, 837, 838, 839, 840, 841, 842, 843, 844, 845, 846, 847, 848, 849, 850, 851, 852, 853, 854, 855, 856, 857, 858, 859, 860, 861, 862, 863, 864, 865, 866, 867, 868, 869, 870, 871, 872, 873, 874, 875, 876, 877, 878, 879, 880, 881, 882, 883, 884, 885, 886, 887, 888, 889, 890, 891, 892, 893, 894, 895, 896, 897, 898, 899, 900, 901, 902, 903, 904, 905, 906, 907, 908, 909, 910, 911, 912, 913, 914, 915, 916, 917, 918, 919, 920, 921, 922, 923, 924, 925, 926, 927, 928, 929, 930, 931, 932, 933, 934, 935, 936, 937, 938, 939, 940, 941, 942, 943, 944, 945, 946, 947, 948, 949, 950, 951, 952, 953, 954, 955, 956, 957, 958, 959, 960, 961, 962, 963, 964, 965, 966, 967, 968, 969, 970, 971, 972, 973, 974, 975, 976, 977, 978, 979, 980, 981, 982, 983, 984, 985, 986, 987, 988, 989, 990, 991, 992, 993, 994, 995, 996, 997, 998, 999, 1000) AND \"users\".\"id\" NOT IN (1001) + } + end + end end end end diff --git a/activerecord/test/cases/associations/eager_load_nested_include_test.rb b/activerecord/test/cases/associations/eager_load_nested_include_test.rb index 525ad3197a..849939de75 100644 --- a/activerecord/test/cases/associations/eager_load_nested_include_test.rb +++ b/activerecord/test/cases/associations/eager_load_nested_include_test.rb @@ -110,10 +110,10 @@ class EagerLoadNestedIncludeWithMissingDataTest < ActiveRecord::TestCase end teardown do - @davey_mcdave.destroy - @first_post.destroy @first_comment.destroy @first_categorization.destroy + @davey_mcdave.destroy + @first_post.destroy end def test_missing_data_in_a_nested_include_should_not_cause_errors_when_constructing_objects diff --git a/activerecord/test/cases/associations/eager_test.rb b/activerecord/test/cases/associations/eager_test.rb index cd9c8a5285..594d161fa3 100644 --- a/activerecord/test/cases/associations/eager_test.rb +++ b/activerecord/test/cases/associations/eager_test.rb @@ -1400,11 +1400,24 @@ class EagerAssociationTest < ActiveRecord::TestCase assert_equal expected, FirstPost.unscoped.find(2) end - test "preload ignores the scoping" do - assert_equal( - Comment.find(1).post, - Post.where("1 = 0").scoping { Comment.preload(:post).find(1).post } - ) + test "belongs_to association ignores the scoping" do + post = Comment.find(1).post + + Post.where("1=0").scoping do + assert_equal post, Comment.find(1).post + assert_equal post, Comment.preload(:post).find(1).post + assert_equal post, Comment.eager_load(:post).find(1).post + end + end + + test "has_many association ignores the scoping" do + comments = Post.find(1).comments.to_a + + Comment.where("1=0").scoping do + assert_equal comments, Post.find(1).comments + assert_equal comments, Post.preload(:comments).find(1).comments + assert_equal comments, Post.eager_load(:comments).find(1).comments + end end test "deep preload" do diff --git a/activerecord/test/cases/associations/extension_test.rb b/activerecord/test/cases/associations/extension_test.rb index aef8f31112..d93d787f7c 100644 --- a/activerecord/test/cases/associations/extension_test.rb +++ b/activerecord/test/cases/associations/extension_test.rb @@ -89,6 +89,6 @@ class AssociationsExtensionsTest < ActiveRecord::TestCase private def extend!(model) - ActiveRecord::Associations::Builder::HasMany.define_extensions(model, :association_name) { } + ActiveRecord::Associations::Builder::HasMany.send(:define_extensions, model, :association_name) { } end end diff --git a/activerecord/test/cases/associations/has_many_associations_test.rb b/activerecord/test/cases/associations/has_many_associations_test.rb index 6a7efe2121..32285f269a 100644 --- a/activerecord/test/cases/associations/has_many_associations_test.rb +++ b/activerecord/test/cases/associations/has_many_associations_test.rb @@ -2578,22 +2578,22 @@ class HasManyAssociationsTest < ActiveRecord::TestCase test "association with extend option" do post = posts(:welcome) - assert_equal "lifo", post.comments_with_extend.author - assert_equal "hello", post.comments_with_extend.greeting + assert_equal "lifo", post.comments_with_extend.author + assert_equal "hello :)", post.comments_with_extend.greeting end test "association with extend option with multiple extensions" do post = posts(:welcome) - assert_equal "lifo", post.comments_with_extend_2.author - assert_equal "hullo", post.comments_with_extend_2.greeting + assert_equal "lifo", post.comments_with_extend_2.author + assert_equal "hullo :)", post.comments_with_extend_2.greeting end test "extend option affects per association" do post = posts(:welcome) - assert_equal "lifo", post.comments_with_extend.author - assert_equal "lifo", post.comments_with_extend_2.author - assert_equal "hello", post.comments_with_extend.greeting - assert_equal "hullo", post.comments_with_extend_2.greeting + assert_equal "lifo", post.comments_with_extend.author + assert_equal "lifo", post.comments_with_extend_2.author + assert_equal "hello :)", post.comments_with_extend.greeting + assert_equal "hullo :)", post.comments_with_extend_2.greeting end test "delete record with complex joins" do diff --git a/activerecord/test/cases/associations/has_many_through_associations_test.rb b/activerecord/test/cases/associations/has_many_through_associations_test.rb index fa540c9dab..c13789f7ec 100644 --- a/activerecord/test/cases/associations/has_many_through_associations_test.rb +++ b/activerecord/test/cases/associations/has_many_through_associations_test.rb @@ -33,6 +33,9 @@ require "models/organization" require "models/user" require "models/family" require "models/family_tree" +require "models/section" +require "models/seminar" +require "models/session" class HasManyThroughAssociationsTest < ActiveRecord::TestCase fixtures :posts, :readers, :people, :comments, :authors, :categories, :taggings, :tags, @@ -1492,6 +1495,19 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase end end + def test_circular_autosave_association_correctly_saves_multiple_records + cs180 = Seminar.new(name: "CS180") + fall = Session.new(name: "Fall") + sections = [ + cs180.sections.build(short_name: "A"), + cs180.sections.build(short_name: "B"), + ] + fall.sections << sections + fall.save! + fall.reload + assert_equal sections, fall.sections.sort_by(&:id) + end + private def make_model(name) Class.new(ActiveRecord::Base) { define_singleton_method(:name) { name } } diff --git a/activerecord/test/cases/associations/inverse_associations_test.rb b/activerecord/test/cases/associations/inverse_associations_test.rb index da3a42e2b5..669e176dcb 100644 --- a/activerecord/test/cases/associations/inverse_associations_test.rb +++ b/activerecord/test/cases/associations/inverse_associations_test.rb @@ -8,6 +8,7 @@ require "models/zine" require "models/club" require "models/sponsor" require "models/rating" +require "models/post" require "models/comment" require "models/car" require "models/bulb" @@ -62,6 +63,14 @@ class AutomaticInverseFindingTests < ActiveRecord::TestCase assert_equal rating_reflection, comment_reflection.inverse_of, "The Comment reflection's inverse should be the Rating reflection" end + def test_has_many_and_belongs_to_should_find_inverse_automatically_for_extension_block + comment_reflection = Comment.reflect_on_association(:post) + post_reflection = Post.reflect_on_association(:comments) + + assert_predicate post_reflection, :has_inverse? + assert_equal comment_reflection, post_reflection.inverse_of + end + def test_has_many_and_belongs_to_should_find_inverse_automatically_for_sti author_reflection = Author.reflect_on_association(:posts) author_child_reflection = Author.reflect_on_association(:special_posts) diff --git a/activerecord/test/cases/attribute_methods_test.rb b/activerecord/test/cases/attribute_methods_test.rb index 9fd62dcf72..5cbe5d796d 100644 --- a/activerecord/test/cases/attribute_methods_test.rb +++ b/activerecord/test/cases/attribute_methods_test.rb @@ -1081,9 +1081,9 @@ class AttributeMethodsTest < ActiveRecord::TestCase assert_equal ["title"], model.accessed_fields end - test "generated attribute methods ancestors have correct class" do + test "generated attribute methods ancestors have correct module" do mod = Topic.send(:generated_attribute_methods) - assert_match %r(Topic::GeneratedAttributeMethods), mod.inspect + assert_equal "Topic::GeneratedAttributeMethods", mod.inspect end private diff --git a/activerecord/test/cases/base_test.rb b/activerecord/test/cases/base_test.rb index 99f47cfe37..ddafa468ed 100644 --- a/activerecord/test/cases/base_test.rb +++ b/activerecord/test/cases/base_test.rb @@ -67,6 +67,16 @@ end class BasicsTest < ActiveRecord::TestCase fixtures :topics, :companies, :developers, :projects, :computers, :accounts, :minimalistics, "warehouse-things", :authors, :author_addresses, :categorizations, :categories, :posts + def test_generated_association_methods_module_name + mod = Post.send(:generated_association_methods) + assert_equal "Post::GeneratedAssociationMethods", mod.inspect + end + + def test_generated_relation_methods_module_name + mod = Post.send(:generated_relation_methods) + assert_equal "Post::GeneratedRelationMethods", mod.inspect + end + def test_column_names_are_escaped conn = ActiveRecord::Base.connection classname = conn.class.name[/[^:]*$/] @@ -1205,6 +1215,8 @@ class BasicsTest < ActiveRecord::TestCase wr.close assert Marshal.load rd.read rd.close + ensure + self.class.send(:remove_const, "Post") if self.class.const_defined?("Post", false) end end diff --git a/activerecord/test/cases/calculations_test.rb b/activerecord/test/cases/calculations_test.rb index c4070fc341..16c2a3661d 100644 --- a/activerecord/test/cases/calculations_test.rb +++ b/activerecord/test/cases/calculations_test.rb @@ -363,6 +363,17 @@ class CalculationsTest < ActiveRecord::TestCase assert_equal 60, c[2] end + def test_should_calculate_grouped_with_longer_field + field = "a" * Account.connection.max_identifier_length + + Account.update_all("#{field} = credit_limit") + + c = Account.group(:firm_id).sum(field) + assert_equal 50, c[1] + assert_equal 105, c[6] + assert_equal 60, c[2] + end + def test_should_calculate_with_invalid_field assert_equal 6, Account.calculate(:count, "*") assert_equal 6, Account.calculate(:count, :all) diff --git a/activerecord/test/cases/callbacks_test.rb b/activerecord/test/cases/callbacks_test.rb index 4d6a112af5..b4026078f1 100644 --- a/activerecord/test/cases/callbacks_test.rb +++ b/activerecord/test/cases/callbacks_test.rb @@ -458,10 +458,6 @@ class CallbacksTest < ActiveRecord::TestCase [ :before_validation, :object ], [ :before_validation, :block ], [ :before_validation, :throwing_abort ], - [ :after_rollback, :block ], - [ :after_rollback, :object ], - [ :after_rollback, :proc ], - [ :after_rollback, :method ], ], david.history end diff --git a/activerecord/test/cases/collection_cache_key_test.rb b/activerecord/test/cases/collection_cache_key_test.rb index 483383257b..f07f3c42e6 100644 --- a/activerecord/test/cases/collection_cache_key_test.rb +++ b/activerecord/test/cases/collection_cache_key_test.rb @@ -171,5 +171,39 @@ module ActiveRecord assert_match(/\Adevelopers\/query-(\h+)-(\d+)-(\d+)\z/, developers.cache_key) end + + test "cache_key should be stable when using collection_cache_versioning" do + with_collection_cache_versioning do + developers = Developer.where(salary: 100000) + + assert_match(/\Adevelopers\/query-(\h+)\z/, developers.cache_key) + + /\Adevelopers\/query-(\h+)\z/ =~ developers.cache_key + + assert_equal ActiveSupport::Digest.hexdigest(developers.to_sql), $1 + end + end + + test "cache_version for relation" do + with_collection_cache_versioning do + developers = Developer.where(salary: 100000).order(updated_at: :desc) + last_developer_timestamp = developers.first.updated_at + + assert_match(/(\d+)-(\d+)\z/, developers.cache_version) + + /(\d+)-(\d+)\z/ =~ developers.cache_version + + assert_equal developers.count.to_s, $1 + assert_equal last_developer_timestamp.to_s(ActiveRecord::Base.cache_timestamp_format), $2 + end + end + + def with_collection_cache_versioning(value = true) + @old_collection_cache_versioning = ActiveRecord::Base.collection_cache_versioning + ActiveRecord::Base.collection_cache_versioning = value + yield + ensure + ActiveRecord::Base.collection_cache_versioning = @old_collection_cache_versioning + end end end diff --git a/activerecord/test/cases/connection_adapters/connection_handlers_multi_db_test.rb b/activerecord/test/cases/connection_adapters/connection_handlers_multi_db_test.rb index 36591097b6..d3184f39f5 100644 --- a/activerecord/test/cases/connection_adapters/connection_handlers_multi_db_test.rb +++ b/activerecord/test/cases/connection_adapters/connection_handlers_multi_db_test.rb @@ -203,26 +203,53 @@ module ActiveRecord assert_equal "must provide a `database` or a `role`.", error.message end - def test_switching_connections_with_database_symbol + def test_switching_connections_with_database_symbol_uses_default_role previous_env, ENV["RAILS_ENV"] = ENV["RAILS_ENV"], "default_env" config = { "default_env" => { - "readonly" => { adapter: "sqlite3", database: "db/readonly.sqlite3" }, - "primary" => { adapter: "sqlite3", database: "db/primary.sqlite3" } + "animals" => { adapter: "sqlite3", database: "db/animals.sqlite3" }, + "primary" => { adapter: "sqlite3", database: "db/primary.sqlite3" } } } @prev_configs, ActiveRecord::Base.configurations = ActiveRecord::Base.configurations, config - ActiveRecord::Base.connected_to(database: :readonly) do - assert_equal :readonly, ActiveRecord::Base.current_role - assert ActiveRecord::Base.connected_to?(role: :readonly) + ActiveRecord::Base.connected_to(database: :animals) do + assert_equal :writing, ActiveRecord::Base.current_role + assert ActiveRecord::Base.connected_to?(role: :writing) handler = ActiveRecord::Base.connection_handler - assert_equal handler, ActiveRecord::Base.connection_handlers[:readonly] + assert_equal handler, ActiveRecord::Base.connection_handlers[:writing] + + assert_not_nil pool = handler.retrieve_connection_pool("primary") + assert_equal(config["default_env"]["animals"], pool.spec.config) + end + ensure + ActiveRecord::Base.configurations = @prev_configs + ActiveRecord::Base.establish_connection(:arunit) + ENV["RAILS_ENV"] = previous_env + end + + def test_switching_connections_with_database_hash_uses_passed_role_and_database + previous_env, ENV["RAILS_ENV"] = ENV["RAILS_ENV"], "default_env" + + config = { + "default_env" => { + "animals" => { adapter: "sqlite3", database: "db/animals.sqlite3" }, + "primary" => { adapter: "sqlite3", database: "db/primary.sqlite3" } + } + } + @prev_configs, ActiveRecord::Base.configurations = ActiveRecord::Base.configurations, config + + ActiveRecord::Base.connected_to(database: { writing: :primary }) do + assert_equal :writing, ActiveRecord::Base.current_role + assert ActiveRecord::Base.connected_to?(role: :writing) + + handler = ActiveRecord::Base.connection_handler + assert_equal handler, ActiveRecord::Base.connection_handlers[:writing] assert_not_nil pool = handler.retrieve_connection_pool("primary") - assert_equal(config["default_env"]["readonly"], pool.spec.config) + assert_equal(config["default_env"]["primary"], pool.spec.config) end ensure ActiveRecord::Base.configurations = @prev_configs diff --git a/activerecord/test/cases/connection_adapters/schema_cache_test.rb b/activerecord/test/cases/connection_adapters/schema_cache_test.rb index 89a9c30f9b..28e232b88f 100644 --- a/activerecord/test/cases/connection_adapters/schema_cache_test.rb +++ b/activerecord/test/cases/connection_adapters/schema_cache_test.rb @@ -95,6 +95,10 @@ module ActiveRecord assert_no_queries do assert_equal @database_version.to_s, @cache.database_version.to_s + + if current_adapter?(:Mysql2Adapter) + assert_not_nil @cache.database_version.full_version_string + end end end diff --git a/activerecord/test/cases/date_time_precision_test.rb b/activerecord/test/cases/date_time_precision_test.rb index 9d1af9362d..79d63949ca 100644 --- a/activerecord/test/cases/date_time_precision_test.rb +++ b/activerecord/test/cases/date_time_precision_test.rb @@ -82,7 +82,7 @@ if subsecond_precision_supported? end def test_invalid_datetime_precision_raises_error - assert_raises ActiveRecord::ActiveRecordError do + assert_raises ArgumentError do @connection.create_table(:foos, force: true) do |t| t.timestamps precision: 7 end diff --git a/activerecord/test/cases/insert_all_test.rb b/activerecord/test/cases/insert_all_test.rb index fc25701c80..f24c63031c 100644 --- a/activerecord/test/cases/insert_all_test.rb +++ b/activerecord/test/cases/insert_all_test.rb @@ -104,6 +104,44 @@ class InsertAllTest < ActiveRecord::TestCase end end + def test_insert_all_with_skip_duplicates_and_autonumber_id_not_given + skip unless supports_insert_on_duplicate_skip? + + assert_difference "Book.count", 1 do + # These two books are duplicates according to an index on %i[author_id name] + # but their IDs are not specified so they will be assigned different IDs + # by autonumber. We will get an exception from MySQL if we attempt to skip + # one of these records by assigning its ID. + Book.insert_all [ + { author_id: 8, name: "Refactoring" }, + { author_id: 8, name: "Refactoring" } + ] + end + end + + def test_insert_all_with_skip_duplicates_and_autonumber_id_given + skip unless supports_insert_on_duplicate_skip? + + assert_difference "Book.count", 1 do + Book.insert_all [ + { id: 200, author_id: 8, name: "Refactoring" }, + { id: 201, author_id: 8, name: "Refactoring" } + ] + end + end + + def test_skip_duplicates_strategy_does_not_secretly_upsert + skip unless supports_insert_on_duplicate_skip? + + book = Book.create!(author_id: 8, name: "Refactoring", format: "EXPECTED") + + assert_no_difference "Book.count" do + Book.insert(author_id: 8, name: "Refactoring", format: "UNEXPECTED") + end + + assert_equal "EXPECTED", book.reload.format + end + def test_insert_all_will_raise_if_duplicates_are_skipped_only_for_a_certain_conflict_target skip unless supports_insert_on_duplicate_skip? && supports_insert_conflict_target? @@ -143,6 +181,42 @@ class InsertAllTest < ActiveRecord::TestCase end end + def test_insert_logs_message_including_model_name + skip unless supports_insert_conflict_target? + + capture_log_output do |output| + Book.insert(name: "Rework", author_id: 1) + assert_match "Book Insert", output.string + end + end + + def test_insert_all_logs_message_including_model_name + skip unless supports_insert_conflict_target? + + capture_log_output do |output| + Book.insert_all [{ name: "Remote", author_id: 1 }, { name: "Renote", author_id: 1 }] + assert_match "Book Bulk Insert", output.string + end + end + + def test_upsert_logs_message_including_model_name + skip unless supports_insert_on_duplicate_update? + + capture_log_output do |output| + Book.upsert(name: "Remote", author_id: 1) + assert_match "Book Upsert", output.string + end + end + + def test_upsert_all_logs_message_including_model_name + skip unless supports_insert_on_duplicate_update? + + capture_log_output do |output| + Book.upsert_all [{ name: "Remote", author_id: 1 }, { name: "Renote", author_id: 1 }] + assert_match "Book Bulk Upsert", output.string + end + end + def test_upsert_all_updates_existing_records skip unless supports_insert_on_duplicate_update? @@ -186,4 +260,17 @@ class InsertAllTest < ActiveRecord::TestCase Book.insert_all! [{ unknown_attribute: "Test" }] end end + + private + + def capture_log_output + output = StringIO.new + old_logger, ActiveRecord::Base.logger = ActiveRecord::Base.logger, ActiveSupport::Logger.new(output) + + begin + yield output + ensure + ActiveRecord::Base.logger = old_logger + end + end end diff --git a/activerecord/test/cases/invertible_migration_test.rb b/activerecord/test/cases/invertible_migration_test.rb index d68cc40107..7f67b945f0 100644 --- a/activerecord/test/cases/invertible_migration_test.rb +++ b/activerecord/test/cases/invertible_migration_test.rb @@ -103,6 +103,32 @@ module ActiveRecord end end + class ChangeColumnComment1 < SilentMigration + def change + create_table("horses") do |t| + t.column :name, :string, comment: "Sekitoba" + end + end + end + + class ChangeColumnComment2 < SilentMigration + def change + change_column_comment :horses, :name, from: "Sekitoba", to: "Diomed" + end + end + + class ChangeTableComment1 < SilentMigration + def change + create_table("horses", comment: "Sekitoba") + end + end + + class ChangeTableComment2 < SilentMigration + def change + change_table_comment :horses, from: "Sekitoba", to: "Diomed" + end + end + class DisableExtension1 < SilentMigration def change enable_extension "hstore" @@ -290,6 +316,7 @@ module ActiveRecord def test_migrate_revert_change_column_default migration1 = ChangeColumnDefault1.new migration1.migrate(:up) + Horse.reset_column_information assert_equal "Sekitoba", Horse.new.name migration2 = ChangeColumnDefault2.new @@ -302,6 +329,38 @@ module ActiveRecord assert_equal "Sekitoba", Horse.new.name end + if ActiveRecord::Base.connection.supports_comments? + def test_migrate_revert_change_column_comment + migration1 = ChangeColumnComment1.new + migration1.migrate(:up) + Horse.reset_column_information + assert_equal "Sekitoba", Horse.columns_hash["name"].comment + + migration2 = ChangeColumnComment2.new + migration2.migrate(:up) + Horse.reset_column_information + assert_equal "Diomed", Horse.columns_hash["name"].comment + + migration2.migrate(:down) + Horse.reset_column_information + assert_equal "Sekitoba", Horse.columns_hash["name"].comment + end + + def test_migrate_revert_change_table_comment + connection = ActiveRecord::Base.connection + migration1 = ChangeTableComment1.new + migration1.migrate(:up) + assert_equal "Sekitoba", connection.table_comment("horses") + + migration2 = ChangeTableComment2.new + migration2.migrate(:up) + assert_equal "Diomed", connection.table_comment("horses") + + migration2.migrate(:down) + assert_equal "Sekitoba", connection.table_comment("horses") + end + end + if current_adapter?(:PostgreSQLAdapter) def test_migrate_enable_and_disable_extension migration1 = InvertibleMigration.new diff --git a/activerecord/test/cases/locking_test.rb b/activerecord/test/cases/locking_test.rb index 33bd74e114..04f9b26960 100644 --- a/activerecord/test/cases/locking_test.rb +++ b/activerecord/test/cases/locking_test.rb @@ -182,7 +182,9 @@ class OptimisticLockingTest < ActiveRecord::TestCase p1.touch assert_equal 1, p1.lock_version - assert_not p1.changed?, "Changes should have been cleared" + assert_not_predicate p1, :changed?, "Changes should have been cleared" + assert_predicate p1, :saved_changes? + assert_equal ["lock_version", "updated_at"], p1.saved_changes.keys.sort end def test_touch_stale_object @@ -193,6 +195,8 @@ class OptimisticLockingTest < ActiveRecord::TestCase assert_raises(ActiveRecord::StaleObjectError) do stale_person.touch end + + assert_not_predicate stale_person, :saved_changes? end def test_update_with_dirty_primary_key @@ -296,6 +300,9 @@ class OptimisticLockingTest < ActiveRecord::TestCase t1.touch assert_equal 1, t1.lock_version + assert_not_predicate t1, :changed? + assert_predicate t1, :saved_changes? + assert_equal ["lock_version", "updated_at"], t1.saved_changes.keys.sort end def test_touch_stale_object_with_lock_without_default @@ -307,6 +314,8 @@ class OptimisticLockingTest < ActiveRecord::TestCase assert_raises(ActiveRecord::StaleObjectError) do stale_object.touch end + + assert_not_predicate stale_object, :saved_changes? end def test_lock_without_default_should_work_with_null_in_the_database diff --git a/activerecord/test/cases/migration/column_attributes_test.rb b/activerecord/test/cases/migration/column_attributes_test.rb index 6f9190c110..b6064500ee 100644 --- a/activerecord/test/cases/migration/column_attributes_test.rb +++ b/activerecord/test/cases/migration/column_attributes_test.rb @@ -176,9 +176,9 @@ module ActiveRecord if current_adapter?(:Mysql2Adapter, :PostgreSQLAdapter) def test_out_of_range_limit_should_raise - assert_raise(ActiveRecordError) { add_column :test_models, :integer_too_big, :integer, limit: 10 } - assert_raise(ActiveRecordError) { add_column :test_models, :text_too_big, :text, limit: 0xfffffffff } - assert_raise(ActiveRecordError) { add_column :test_models, :binary_too_big, :binary, limit: 0xfffffffff } + assert_raise(ArgumentError) { add_column :test_models, :integer_too_big, :integer, limit: 10 } + assert_raise(ArgumentError) { add_column :test_models, :text_too_big, :text, limit: 0xfffffffff } + assert_raise(ArgumentError) { add_column :test_models, :binary_too_big, :binary, limit: 0xfffffffff } end end end diff --git a/activerecord/test/cases/migration/command_recorder_test.rb b/activerecord/test/cases/migration/command_recorder_test.rb index 01f8628fc5..c9f3756b1f 100644 --- a/activerecord/test/cases/migration/command_recorder_test.rb +++ b/activerecord/test/cases/migration/command_recorder_test.rb @@ -182,6 +182,40 @@ module ActiveRecord assert_equal [:change_column_default, [:table, :column, from: false, to: true]], change end + if ActiveRecord::Base.connection.supports_comments? + def test_invert_change_column_comment + assert_raises(ActiveRecord::IrreversibleMigration) do + @recorder.inverse_of :change_column_comment, [:table, :column, "comment"] + end + end + + def test_invert_change_column_comment_with_from_and_to + change = @recorder.inverse_of :change_column_comment, [:table, :column, from: "old_value", to: "new_value"] + assert_equal [:change_column_comment, [:table, :column, from: "new_value", to: "old_value"]], change + end + + def test_invert_change_column_comment_with_from_and_to_with_nil + change = @recorder.inverse_of :change_column_comment, [:table, :column, from: nil, to: "new_value"] + assert_equal [:change_column_comment, [:table, :column, from: "new_value", to: nil]], change + end + + def test_invert_change_table_comment + assert_raises(ActiveRecord::IrreversibleMigration) do + @recorder.inverse_of :change_column_comment, [:table, :column, "comment"] + end + end + + def test_invert_change_table_comment_with_from_and_to + change = @recorder.inverse_of :change_table_comment, [:table, from: "old_value", to: "new_value"] + assert_equal [:change_table_comment, [:table, from: "new_value", to: "old_value"]], change + end + + def test_invert_change_table_comment_with_from_and_to_with_nil + change = @recorder.inverse_of :change_table_comment, [:table, from: nil, to: "new_value"] + assert_equal [:change_table_comment, [:table, from: "new_value", to: nil]], change + end + end + def test_invert_change_column_null add = @recorder.inverse_of :change_column_null, [:table, :column, true] assert_equal [:change_column_null, [:table, :column, false]], add diff --git a/activerecord/test/cases/migration/compatibility_test.rb b/activerecord/test/cases/migration/compatibility_test.rb index 5753bd7117..726ccf925e 100644 --- a/activerecord/test/cases/migration/compatibility_test.rb +++ b/activerecord/test/cases/migration/compatibility_test.rb @@ -220,6 +220,35 @@ module ActiveRecord end end + if ActiveRecord::Base.connection.supports_comments? + def test_change_column_comment_can_be_reverted + migration = Class.new(ActiveRecord::Migration[5.2]) { + def migrate(x) + revert do + change_column_comment(:testings, :foo, "comment") + end + end + }.new + + ActiveRecord::Migrator.new(:up, [migration]).migrate + assert connection.column_exists?(:testings, :foo, comment: "comment") + end + + def test_change_table_comment_can_be_reverted + migration = Class.new(ActiveRecord::Migration[5.2]) { + def migrate(x) + revert do + change_table_comment(:testings, "comment") + end + end + }.new + + ActiveRecord::Migrator.new(:up, [migration]).migrate + + assert_equal "comment", connection.table_comment("testings") + end + end + if current_adapter?(:PostgreSQLAdapter) class Testing < ActiveRecord::Base end @@ -242,9 +271,9 @@ module ActiveRecord private def precision_implicit_default if current_adapter?(:Mysql2Adapter) - { presicion: 0 } + { precision: 0 } else - { presicion: nil } + { precision: nil } end end end diff --git a/activerecord/test/cases/migration_test.rb b/activerecord/test/cases/migration_test.rb index f10c26c308..8e8ed494d9 100644 --- a/activerecord/test/cases/migration_test.rb +++ b/activerecord/test/cases/migration_test.rb @@ -583,7 +583,7 @@ class MigrationTest < ActiveRecord::TestCase if current_adapter?(:Mysql2Adapter, :PostgreSQLAdapter) def test_out_of_range_integer_limit_should_raise - e = assert_raise(ActiveRecord::ActiveRecordError, "integer limit didn't raise") do + e = assert_raise(ArgumentError) do Person.connection.create_table :test_integer_limits, force: true do |t| t.column :bigone, :integer, limit: 10 end @@ -595,7 +595,7 @@ class MigrationTest < ActiveRecord::TestCase end def test_out_of_range_text_limit_should_raise - e = assert_raise(ActiveRecord::ActiveRecordError, "text limit didn't raise") do + e = assert_raise(ArgumentError) do Person.connection.create_table :test_text_limits, force: true do |t| t.text :bigtext, limit: 0xfffffffff end @@ -607,15 +607,15 @@ class MigrationTest < ActiveRecord::TestCase end def test_out_of_range_binary_limit_should_raise - e = assert_raise(ActiveRecord::ActiveRecordError) do - Person.connection.create_table :test_text_limits, force: true do |t| + e = assert_raise(ArgumentError) do + Person.connection.create_table :test_binary_limits, force: true do |t| t.binary :bigbinary, limit: 0xfffffffff end end assert_includes e.message, "No binary type has byte size #{0xfffffffff}" ensure - Person.connection.drop_table :test_text_limits, if_exists: true + Person.connection.drop_table :test_binary_limits, if_exists: true end end diff --git a/activerecord/test/cases/primary_keys_test.rb b/activerecord/test/cases/primary_keys_test.rb index 4759d3b6b2..511d7fc982 100644 --- a/activerecord/test/cases/primary_keys_test.rb +++ b/activerecord/test/cases/primary_keys_test.rb @@ -203,6 +203,14 @@ class PrimaryKeysTest < ActiveRecord::TestCase assert_queries(3, ignore_none: true) { klass.create! } end + def test_assign_id_raises_error_if_primary_key_doesnt_exist + klass = Class.new(ActiveRecord::Base) do + self.table_name = "dashboards" + end + dashboard = klass.new + assert_raises(ActiveModel::MissingAttributeError) { dashboard.id = "1" } + end + if current_adapter?(:PostgreSQLAdapter) def test_serial_with_quoted_sequence_name column = MixedCaseMonkey.columns_hash[MixedCaseMonkey.primary_key] diff --git a/activerecord/test/cases/relation/delegation_test.rb b/activerecord/test/cases/relation/delegation_test.rb index 8208c45df1..085006c9a2 100644 --- a/activerecord/test/cases/relation/delegation_test.rb +++ b/activerecord/test/cases/relation/delegation_test.rb @@ -56,7 +56,7 @@ module ActiveRecord :first_or_create, :first_or_create!, :first_or_initialize, :find_or_create_by, :find_or_create_by!, :find_or_initialize_by, :create_or_find_by, :create_or_find_by!, - :destroy_all, :delete_all, :update_all, :delete_by, :destroy_by + :destroy_all, :delete_all, :update_all, :touch_all, :delete_by, :destroy_by ] def test_delegate_querying_methods diff --git a/activerecord/test/cases/relation/update_all_test.rb b/activerecord/test/cases/relation/update_all_test.rb index 0500574f28..e45531b4a9 100644 --- a/activerecord/test/cases/relation/update_all_test.rb +++ b/activerecord/test/cases/relation/update_all_test.rb @@ -241,6 +241,38 @@ class UpdateAllTest < ActiveRecord::TestCase end end + def test_klass_level_update_all + travel 5.seconds do + now = Time.now.utc + + Person.all.each do |person| + assert_not_equal now, person.updated_at + end + + Person.update_all(updated_at: now) + + Person.all.each do |person| + assert_equal now, person.updated_at + end + end + end + + def test_klass_level_touch_all + travel 5.seconds do + now = Time.now.utc + + Person.all.each do |person| + assert_not_equal now, person.updated_at + end + + Person.touch_all(time: now) + + Person.all.each do |person| + assert_equal now, person.updated_at + end + end + end + # Oracle UPDATE does not support ORDER BY unless current_adapter?(:OracleAdapter) def test_update_all_ignores_order_without_limit_from_association diff --git a/activerecord/test/cases/relation/where_clause_test.rb b/activerecord/test/cases/relation/where_clause_test.rb index 0b06cec40b..b26a1a1d80 100644 --- a/activerecord/test/cases/relation/where_clause_test.rb +++ b/activerecord/test/cases/relation/where_clause_test.rb @@ -106,7 +106,7 @@ class ActiveRecord::Relation Arel::Nodes::Not.new(random_object) ]) - assert_equal expected, original.invert + assert_equal expected, original.invert(:nor) end test "except removes binary predicates referencing a given column" do diff --git a/activerecord/test/cases/relation/where_test.rb b/activerecord/test/cases/relation/where_test.rb index b045184d7d..2ecccfe1f3 100644 --- a/activerecord/test/cases/relation/where_test.rb +++ b/activerecord/test/cases/relation/where_test.rb @@ -115,13 +115,58 @@ module ActiveRecord assert_equal expected.to_sql, actual.to_sql end - def test_polymorphic_shallow_where_not - treasure = treasures(:sapphire) + def test_where_not_polymorphic_association + sapphire = treasures(:sapphire) - expected = [price_estimates(:diamond), price_estimates(:honda)] - actual = PriceEstimate.where.not(estimate_of: treasure) + all = [treasures(:diamond), sapphire, cars(:honda), sapphire] + assert_equal all, PriceEstimate.all.sort_by(&:id).map(&:estimate_of) - assert_equal expected.sort_by(&:id), actual.sort_by(&:id) + actual = PriceEstimate.where.not(estimate_of: sapphire) + only = PriceEstimate.where(estimate_of: sapphire) + + expected = all - [sapphire] + assert_equal expected, actual.sort_by(&:id).map(&:estimate_of) + assert_equal all - expected, only.sort_by(&:id).map(&:estimate_of) + end + + def test_where_not_polymorphic_id_and_type_as_nand + sapphire = treasures(:sapphire) + + all = [treasures(:diamond), sapphire, cars(:honda), sapphire] + assert_equal all, PriceEstimate.all.sort_by(&:id).map(&:estimate_of) + + actual = PriceEstimate.where.yield_self do |where_chain| + where_chain.stub(:not_behaves_as_nor?, false) do + where_chain.not(estimate_of_type: sapphire.class.polymorphic_name, estimate_of_id: sapphire.id) + end + end + only = PriceEstimate.where(estimate_of_type: sapphire.class.polymorphic_name, estimate_of_id: sapphire.id) + + expected = all - [sapphire] + assert_equal expected, actual.sort_by(&:id).map(&:estimate_of) + assert_equal all - expected, only.sort_by(&:id).map(&:estimate_of) + end + + def test_where_not_polymorphic_id_and_type_as_nor_is_deprecated + sapphire = treasures(:sapphire) + + all = [treasures(:diamond), sapphire, cars(:honda), sapphire] + assert_equal all, PriceEstimate.all.sort_by(&:id).map(&:estimate_of) + + message = <<~MSG.squish + NOT conditions will no longer behave as NOR in Rails 6.1. + To continue using NOR conditions, NOT each conditions manually + (`.where.not(:estimate_of_type => ...).where.not(:estimate_of_id => ...)`). + MSG + actual = assert_deprecated(message) do + PriceEstimate.where.not(estimate_of_type: sapphire.class.polymorphic_name, estimate_of_id: sapphire.id) + end + only = PriceEstimate.where(estimate_of_type: sapphire.class.polymorphic_name, estimate_of_id: sapphire.id) + + expected = all - [sapphire] + # NOT (estimate_of_type = 'Treasure' OR estimate_of_id = sapphire.id) matches only `cars(:honda)` unfortunately. + assert_not_equal expected, actual.sort_by(&:id).map(&:estimate_of) + assert_equal all - expected, only.sort_by(&:id).map(&:estimate_of) end def test_polymorphic_nested_array_where diff --git a/activerecord/test/cases/relation_test.rb b/activerecord/test/cases/relation_test.rb index 00a7b3841f..3f370e5ede 100644 --- a/activerecord/test/cases/relation_test.rb +++ b/activerecord/test/cases/relation_test.rb @@ -101,6 +101,9 @@ module ActiveRecord relation.merge!(relation) assert_predicate relation, :empty_scope? + + assert_not_predicate NullPost.all, :empty_scope? + assert_not_predicate FirstPost.all, :empty_scope? end def test_bad_constants_raise_errors @@ -289,6 +292,7 @@ module ActiveRecord klass.create!(description: "foo") assert_equal ["foo"], klass.select(:description).from(klass.all).map(&:desc) + assert_equal ["foo"], klass.reselect(:description).from(klass.all).map(&:desc) end def test_relation_merging_with_merged_joins_as_strings diff --git a/activerecord/test/cases/scoping/relation_scoping_test.rb b/activerecord/test/cases/scoping/relation_scoping_test.rb index a95ab0f429..50b514d464 100644 --- a/activerecord/test/cases/scoping/relation_scoping_test.rb +++ b/activerecord/test/cases/scoping/relation_scoping_test.rb @@ -411,7 +411,19 @@ class HasManyScopingTest < ActiveRecord::TestCase def test_nested_scope_finder Comment.where("1=0").scoping do - assert_equal 0, @welcome.comments.count + assert_equal 2, @welcome.comments.count + assert_equal "a comment...", @welcome.comments.what_are_you + end + + Comment.where("1=1").scoping do + assert_equal 2, @welcome.comments.count + assert_equal "a comment...", @welcome.comments.what_are_you + end + end + + def test_none_scoping + Comment.none.scoping do + assert_equal 2, @welcome.comments.count assert_equal "a comment...", @welcome.comments.what_are_you end @@ -452,7 +464,19 @@ class HasAndBelongsToManyScopingTest < ActiveRecord::TestCase def test_nested_scope_finder Category.where("1=0").scoping do - assert_equal 0, @welcome.categories.count + assert_equal 2, @welcome.categories.count + assert_equal "a category...", @welcome.categories.what_are_you + end + + Category.where("1=1").scoping do + assert_equal 2, @welcome.categories.count + assert_equal "a category...", @welcome.categories.what_are_you + end + end + + def test_none_scoping + Category.none.scoping do + assert_equal 2, @welcome.categories.count assert_equal "a category...", @welcome.categories.what_are_you end diff --git a/activerecord/test/cases/tasks/database_tasks_test.rb b/activerecord/test/cases/tasks/database_tasks_test.rb index dd4a0b0455..ffe94eee0f 100644 --- a/activerecord/test/cases/tasks/database_tasks_test.rb +++ b/activerecord/test/cases/tasks/database_tasks_test.rb @@ -760,7 +760,7 @@ module ActiveRecord end class DatabaseTasksMigrateTest < DatabaseTasksMigrationTestCase - def test_migrate_set_and_unset_verbose_and_version_env_vars + def test_can_migrate_from_pending_migration_error_action_dispatch verbose, version = ENV["VERBOSE"], ENV["VERSION"] ENV["VERSION"] = "2" ENV["VERBOSE"] = "false" @@ -772,7 +772,9 @@ module ActiveRecord ENV.delete("VERBOSE") # re-run up migration - assert_includes capture_migration_output, "migrating" + assert_includes(capture(:stdout) do + ActiveSupport::ActionableError.dispatch ActiveRecord::PendingMigrationError, "Run pending migrations" + end, "migrating") ensure ENV["VERBOSE"], ENV["VERSION"] = verbose, version end diff --git a/activerecord/test/cases/time_precision_test.rb b/activerecord/test/cases/time_precision_test.rb index 2f534ea110..1abd857216 100644 --- a/activerecord/test/cases/time_precision_test.rb +++ b/activerecord/test/cases/time_precision_test.rb @@ -75,7 +75,7 @@ if subsecond_precision_supported? end def test_invalid_time_precision_raises_error - assert_raises ActiveRecord::ActiveRecordError do + assert_raises ArgumentError do @connection.create_table(:foos, force: true) do |t| t.time :start, precision: 7 t.time :finish, precision: 7 diff --git a/activerecord/test/cases/timestamp_test.rb b/activerecord/test/cases/timestamp_test.rb index 75ecd6fc40..232e018e03 100644 --- a/activerecord/test/cases/timestamp_test.rb +++ b/activerecord/test/cases/timestamp_test.rb @@ -40,17 +40,25 @@ class TimestampTest < ActiveRecord::TestCase assert_not_equal @previously_updated_at, @developer.updated_at assert_equal previous_salary + 10000, @developer.salary - assert @developer.salary_changed?, "developer salary should have changed" - assert @developer.changed?, "developer should be marked as changed" + assert_predicate @developer, :salary_changed?, "developer salary should have changed" + assert_predicate @developer, :changed?, "developer should be marked as changed" + assert_equal ["salary"], @developer.changed + assert_predicate @developer, :saved_changes? + assert_equal ["updated_at", "updated_on"], @developer.saved_changes.keys.sort + @developer.reload assert_equal previous_salary, @developer.salary end def test_touching_a_record_with_default_scope_that_excludes_it_updates_its_timestamp developer = @developer.becomes(DeveloperCalledJamis) - developer.touch + assert_not_equal @previously_updated_at, developer.updated_at + assert_not_predicate developer, :changed? + assert_predicate developer, :saved_changes? + assert_equal ["updated_at", "updated_on"], developer.saved_changes.keys.sort + developer.reload assert_not_equal @previously_updated_at, developer.updated_at end diff --git a/activerecord/test/cases/touch_later_test.rb b/activerecord/test/cases/touch_later_test.rb index cd3d5ed7d1..f1a9cf2d05 100644 --- a/activerecord/test/cases/touch_later_test.rb +++ b/activerecord/test/cases/touch_later_test.rb @@ -10,7 +10,7 @@ require "models/tree" class TouchLaterTest < ActiveRecord::TestCase fixtures :nodes, :trees - def test_touch_laster_raise_if_non_persisted + def test_touch_later_raise_if_non_persisted invoice = Invoice.new Invoice.transaction do assert_not_predicate invoice, :persisted? diff --git a/activerecord/test/cases/transaction_callbacks_test.rb b/activerecord/test/cases/transaction_callbacks_test.rb index e88d20a453..53fe31e087 100644 --- a/activerecord/test/cases/transaction_callbacks_test.rb +++ b/activerecord/test/cases/transaction_callbacks_test.rb @@ -111,6 +111,32 @@ class TransactionCallbacksTest < ActiveRecord::TestCase assert_equal [:after_commit], @first.history end + def test_dont_call_any_callbacks_after_transaction_commits_for_invalid_record + @first.after_commit_block { |r| r.history << :after_commit } + @first.after_rollback_block { |r| r.history << :after_rollback } + + def @first.valid?(*) + false + end + + assert_not @first.save + assert_equal [], @first.history + end + + def test_dont_call_any_callbacks_after_explicit_transaction_commits_for_invalid_record + @first.after_commit_block { |r| r.history << :after_commit } + @first.after_rollback_block { |r| r.history << :after_rollback } + + def @first.valid?(*) + false + end + + @first.transaction do + assert_not @first.save + end + assert_equal [], @first.history + end + def test_only_call_after_commit_on_save_after_transaction_commits_for_saving_record record = TopicWithCallbacks.new(title: "New topic", written_on: Date.today) record.after_commit_block(:save) { |r| r.history << :after_save } @@ -598,7 +624,7 @@ class TransactionEnrollmentCallbacksTest < ActiveRecord::TestCase @topic.content = "foo" @topic.save! end - @topic.class.connection.add_transaction_record(@topic) + @topic.send(:add_to_transaction) end assert_equal [:before_commit, :after_commit], @topic.history end @@ -608,7 +634,7 @@ class TransactionEnrollmentCallbacksTest < ActiveRecord::TestCase @topic.transaction(requires_new: true) do @topic.content = "foo" @topic.save! - @topic.class.connection.add_transaction_record(@topic) + @topic.send(:add_to_transaction) end end assert_equal [:before_commit, :after_commit], @topic.history @@ -629,7 +655,7 @@ class TransactionEnrollmentCallbacksTest < ActiveRecord::TestCase @topic.content = "foo" @topic.save! end - @topic.class.connection.add_transaction_record(@topic) + @topic.send(:add_to_transaction) raise ActiveRecord::Rollback end assert_equal [:rollback], @topic.history diff --git a/activerecord/test/cases/transactions_test.rb b/activerecord/test/cases/transactions_test.rb index 1009dd0f99..6795996cca 100644 --- a/activerecord/test/cases/transactions_test.rb +++ b/activerecord/test/cases/transactions_test.rb @@ -18,6 +18,65 @@ class TransactionTest < ActiveRecord::TestCase @first, @second = Topic.find(1, 2).sort_by(&:id) end + def test_rollback_dirty_changes + topic = topics(:fifth) + + ActiveRecord::Base.transaction do + topic.update(title: "Ruby on Rails") + raise ActiveRecord::Rollback + end + + title_change = ["The Fifth Topic of the day", "Ruby on Rails"] + assert_equal title_change, topic.changes["title"] + end + + def test_rollback_dirty_changes_multiple_saves + topic = topics(:fifth) + + ActiveRecord::Base.transaction do + topic.update(title: "Ruby on Rails") + topic.update(title: "Another Title") + raise ActiveRecord::Rollback + end + + title_change = ["The Fifth Topic of the day", "Another Title"] + assert_equal title_change, topic.changes["title"] + end + + def test_rollback_dirty_changes_then_retry_save + topic = topics(:fifth) + + ActiveRecord::Base.transaction do + topic.update(title: "Ruby on Rails") + raise ActiveRecord::Rollback + end + + title_change = ["The Fifth Topic of the day", "Ruby on Rails"] + assert_equal title_change, topic.changes["title"] + + assert topic.save + + assert_equal title_change, topic.saved_changes["title"] + assert_equal topic.title, topic.reload.title + end + + def test_rollback_dirty_changes_then_retry_save_on_new_record + topic = Topic.new(title: "Ruby on Rails") + + ActiveRecord::Base.transaction do + topic.save + raise ActiveRecord::Rollback + end + + title_change = [nil, "Ruby on Rails"] + assert_equal title_change, topic.changes["title"] + + assert topic.save + + assert_equal title_change, topic.saved_changes["title"] + assert_equal topic.title, topic.reload.title + end + def test_persisted_in_a_model_with_custom_primary_key_after_failed_save movie = Movie.create assert_not_predicate movie, :persisted? @@ -26,28 +85,31 @@ class TransactionTest < ActiveRecord::TestCase def test_raise_after_destroy assert_not_predicate @first, :frozen? - assert_raises(RuntimeError) { - Topic.transaction do - @first.destroy - assert_predicate @first, :frozen? - raise + assert_not_called(@first, :rolledback!) do + assert_raises(RuntimeError) do + Topic.transaction do + @first.destroy + assert_predicate @first, :frozen? + raise + end end - } + end - assert @first.reload assert_not_predicate @first, :frozen? end def test_successful - Topic.transaction do - @first.approved = true - @second.approved = false - @first.save - @second.save + assert_not_called(@first, :committed!) do + Topic.transaction do + @first.approved = true + @second.approved = false + @first.save + @second.save + end end - assert Topic.find(1).approved?, "First should have been approved" - assert_not Topic.find(2).approved?, "Second should have been unapproved" + assert_predicate Topic.find(1), :approved?, "First should have been approved" + assert_not_predicate Topic.find(2), :approved?, "Second should have been unapproved" end def transaction_with_return @@ -62,7 +124,7 @@ class TransactionTest < ActiveRecord::TestCase def test_add_to_null_transaction topic = Topic.new - topic.add_to_transaction + topic.send(:add_to_transaction) end def test_successful_with_return @@ -76,11 +138,13 @@ class TransactionTest < ActiveRecord::TestCase end end - transaction_with_return + assert_not_called(@first, :committed!) do + transaction_with_return + end assert committed - assert Topic.find(1).approved?, "First should have been approved" - assert_not Topic.find(2).approved?, "Second should have been unapproved" + assert_predicate Topic.find(1), :approved?, "First should have been approved" + assert_not_predicate Topic.find(2), :approved?, "Second should have been unapproved" ensure Topic.connection.class_eval do remove_method :commit_db_transaction @@ -99,9 +163,11 @@ class TransactionTest < ActiveRecord::TestCase end end - Topic.transaction do - @first.approved = true - @first.save! + assert_not_called(@first, :committed!) do + Topic.transaction do + @first.approved = true + @first.save! + end end assert_equal 0, num @@ -113,19 +179,21 @@ class TransactionTest < ActiveRecord::TestCase end def test_successful_with_instance_method - @first.transaction do - @first.approved = true - @second.approved = false - @first.save - @second.save + assert_not_called(@first, :committed!) do + @first.transaction do + @first.approved = true + @second.approved = false + @first.save + @second.save + end end - assert Topic.find(1).approved?, "First should have been approved" - assert_not Topic.find(2).approved?, "Second should have been unapproved" + assert_predicate Topic.find(1), :approved?, "First should have been approved" + assert_not_predicate Topic.find(2), :approved?, "Second should have been unapproved" end def test_failing_on_exception - begin + assert_not_called(@first, :rolledback!) do Topic.transaction do @first.approved = true @second.approved = false @@ -137,11 +205,11 @@ class TransactionTest < ActiveRecord::TestCase # caught it end - assert @first.approved?, "First should still be changed in the objects" - assert_not @second.approved?, "Second should still be changed in the objects" + assert_predicate @first, :approved?, "First should still be changed in the objects" + assert_not_predicate @second, :approved?, "Second should still be changed in the objects" - assert_not Topic.find(1).approved?, "First shouldn't have been approved" - assert Topic.find(2).approved?, "Second should still be approved" + assert_not_predicate Topic.find(1), :approved?, "First shouldn't have been approved" + assert_predicate Topic.find(2), :approved?, "Second should still be approved" end def test_raising_exception_in_callback_rollbacks_in_save @@ -150,8 +218,10 @@ class TransactionTest < ActiveRecord::TestCase end @first.approved = true - e = assert_raises(RuntimeError) { @first.save } - assert_equal "Make the transaction rollback", e.message + assert_not_called(@first, :rolledback!) do + e = assert_raises(RuntimeError) { @first.save } + assert_equal "Make the transaction rollback", e.message + end assert_not_predicate Topic.find(1), :approved? end @@ -159,13 +229,15 @@ class TransactionTest < ActiveRecord::TestCase def @first.before_save_for_transaction raise ActiveRecord::Rollback end - assert_not @first.approved + assert_not_predicate @first, :approved? - Topic.transaction do - @first.approved = true - @first.save! + assert_not_called(@first, :rolledback!) do + Topic.transaction do + @first.approved = true + @first.save! + end end - assert_not Topic.find(@first.id).approved?, "Should not commit the approved flag" + assert_not_predicate Topic.find(@first.id), :approved?, "Should not commit the approved flag" end def test_raising_exception_in_nested_transaction_restore_state_in_save @@ -175,11 +247,13 @@ class TransactionTest < ActiveRecord::TestCase raise "Make the transaction rollback" end - assert_raises(RuntimeError) do - Topic.transaction { topic.save } + assert_not_called(topic, :rolledback!) do + assert_raises(RuntimeError) do + Topic.transaction { topic.save } + end end - assert topic.new_record?, "#{topic.inspect} should be new record" + assert_predicate topic, :new_record?, "#{topic.inspect} should be new record" end def test_transaction_state_is_cleared_when_record_is_persisted diff --git a/activerecord/test/cases/validations_test.rb b/activerecord/test/cases/validations_test.rb index 9a70934b7e..4f98a6b7fc 100644 --- a/activerecord/test/cases/validations_test.rb +++ b/activerecord/test/cases/validations_test.rb @@ -145,15 +145,17 @@ class ValidationsTest < ActiveRecord::TestCase end def test_validates_acceptance_of_with_undefined_attribute_methods - Topic.validates_acceptance_of(:approved) - topic = Topic.new(approved: true) - Topic.undefine_attribute_methods + klass = Class.new(Topic) + klass.validates_acceptance_of(:approved) + topic = klass.new(approved: true) + klass.undefine_attribute_methods assert topic.approved end def test_validates_acceptance_of_as_database_column - Topic.validates_acceptance_of(:approved) - topic = Topic.create("approved" => true) + klass = Class.new(Topic) + klass.validates_acceptance_of(:approved) + topic = klass.create("approved" => true) assert topic["approved"] end |