2019-07-17 09:55:06 -04:00
# frozen_string_literal: true
require " cases/helper "
require " models/developer "
2021-06-15 10:17:25 -04:00
require " models/contract "
2021-03-03 16:33:32 -05:00
require " models/company "
2019-07-17 09:55:06 -04:00
require " models/computer "
2020-02-21 13:11:24 -05:00
require " models/mentor "
require " models/project "
require " models/ship "
2021-03-03 16:33:32 -05:00
require " models/ship_part "
2020-12-10 15:00:40 -05:00
require " models/strict_zine "
require " models/interest "
2021-03-03 16:33:32 -05:00
require " models/treasure "
2021-08-04 23:32:43 -04:00
require " models/pirate "
2019-07-17 09:55:06 -04:00
class StrictLoadingTest < ActiveRecord :: TestCase
2021-03-03 16:33:32 -05:00
fixtures :developers , :developers_projects , :projects , :ships
2019-07-17 09:55:06 -04:00
2020-05-30 06:31:05 -04:00
def test_strict_loading!
developer = Developer . first
assert_not_predicate developer , :strict_loading?
2021-04-05 04:56:32 -04:00
assert developer . strict_loading!
2020-05-30 06:31:05 -04:00
assert_predicate developer , :strict_loading?
assert_raises ActiveRecord :: StrictLoadingViolationError do
developer . audit_logs . to_a
end
2021-01-19 16:12:18 -05:00
2021-04-05 04:56:32 -04:00
assert_not developer . strict_loading! ( false )
2021-01-19 16:12:18 -05:00
assert_not_predicate developer , :strict_loading?
assert_nothing_raised do
developer . audit_logs . to_a
end
2021-04-05 04:56:32 -04:00
assert developer . strict_loading! ( mode : :n_plus_one_only )
assert developer . strict_loading_n_plus_one_only?
2020-05-30 06:31:05 -04:00
end
2021-03-03 16:33:32 -05:00
def test_strict_loading_n_plus_one_only_mode
developer = Developer . first
ship = Ship . first
ShipPart . create! ( name : " Stern " , ship : ship )
2021-03-27 21:48:56 -04:00
firm = Firm . create! ( name : " NASA " )
2021-03-03 16:33:32 -05:00
project = Project . create! ( name : " Apollo " , firm : firm )
ship . update_column ( :developer_id , developer . id )
developer . projects << project
developer . reload
developer . strict_loading! ( mode : :n_plus_one_only )
assert_predicate developer , :strict_loading?
# Does not raise when loading a has_many association (:projects)
assert_nothing_raised do
developer . projects . to_a
end
# strict_loading is enabled for has_many associations
assert developer . projects . all? ( & :strict_loading? )
assert_raises ActiveRecord :: StrictLoadingViolationError do
developer . projects . last . firm
end
# Does not raise when a belongs_to association (:ship) loads its
# has_many association (:parts)
assert_nothing_raised do
developer . ship . parts . to_a
end
# strict_loading is enabled for has_many through a belongs_to
assert_not developer . ship . strict_loading?
assert developer . ship . parts . all? ( & :strict_loading? )
assert_raises ActiveRecord :: StrictLoadingViolationError do
developer . ship . parts . first . trinkets . to_a
end
end
2019-07-17 09:55:06 -04:00
def test_strict_loading
Developer . all . each { | d | assert_not d . strict_loading? }
Developer . strict_loading . each { | d | assert d . strict_loading? }
end
2020-05-30 08:09:28 -04:00
def test_strict_loading_by_default
with_strict_loading_by_default ( Developer ) do
Developer . all . each { | d | assert d . strict_loading? }
end
end
def test_strict_loading_by_default_can_be_set_per_model
model1 = Class . new ( ActiveRecord :: Base ) do
self . table_name = " developers "
self . strict_loading_by_default = true
end . new
model2 = Class . new ( ActiveRecord :: Base ) do
self . table_name = " developers "
self . strict_loading_by_default = false
end . new
assert_predicate model1 , :strict_loading?
assert_not_predicate model2 , :strict_loading?
end
def test_strict_loading_by_default_is_inheritable
with_strict_loading_by_default ( ActiveRecord :: Base ) do
model1 = Class . new ( ActiveRecord :: Base ) do
self . table_name = " developers "
end . new
model2 = Class . new ( ActiveRecord :: Base ) do
self . table_name = " developers "
self . strict_loading_by_default = false
end . new
assert_predicate model1 , :strict_loading?
assert_not_predicate model2 , :strict_loading?
end
end
2019-07-17 09:55:06 -04:00
def test_raises_if_strict_loading_and_lazy_loading
dev = Developer . strict_loading . first
assert_predicate dev , :strict_loading?
assert_raises ActiveRecord :: StrictLoadingViolationError do
dev . audit_logs . to_a
end
end
2020-05-30 08:09:28 -04:00
def test_raises_if_strict_loading_by_default_and_lazy_loading
with_strict_loading_by_default ( Developer ) do
dev = Developer . first
assert_predicate dev , :strict_loading?
assert_raises ActiveRecord :: StrictLoadingViolationError do
dev . audit_logs . to_a
end
end
end
2020-12-09 13:42:24 -05:00
def test_strict_loading_is_ignored_in_validation_context
with_strict_loading_by_default ( Developer ) do
developer = Developer . first
assert_predicate developer , :strict_loading?
assert_nothing_raised do
AuditLogRequired . create! developer_id : developer . id , message : " i am a message "
end
end
end
def test_strict_loading_with_reflection_is_ignored_in_validation_context
with_strict_loading_by_default ( Developer ) do
developer = Developer . first
assert_predicate developer , :strict_loading?
developer . required_audit_logs . build ( message : " I am message " )
developer . save!
end
end
2021-01-12 11:03:44 -05:00
def test_strict_loading_has_one_reload
with_strict_loading_by_default ( Developer ) do
ship = Ship . create! ( developer : Developer . first , name : " The Great Ship " )
developer = Developer . preload ( :ship ) . first
assert_predicate developer , :strict_loading?
assert_equal ship , developer . ship
developer . reload
assert_nothing_raised do
assert_equal ship , developer . ship
end
end
end
def test_strict_loading_with_has_many
with_strict_loading_by_default ( Developer ) do
devs = Developer . preload ( :audit_logs ) . all
assert_nothing_raised do
devs . map ( & :audit_logs ) . to_a
end
devs . reload
assert_nothing_raised do
devs . map ( & :audit_logs ) . to_a
end
end
end
def test_strict_loading_with_has_many_singular_association_and_reload
with_strict_loading_by_default ( Developer ) do
dev = Developer . preload ( :audit_logs ) . first
assert_nothing_raised do
dev . audit_logs . to_a
end
dev . reload
assert_nothing_raised do
dev . audit_logs . to_a
end
end
end
2021-06-15 10:17:25 -04:00
def test_strict_loading_with_has_many_through_cascade_down_to_middle_records
dev = Developer . first
firm = Firm . create! ( name : " NASA " )
contract = Contract . create! ( developer : dev , firm : firm )
dev . contracts << contract
dev = Developer . strict_loading . includes ( :firms ) . first
assert_predicate dev , :strict_loading?
[
proc { dev . firms . first . contracts . first } ,
proc { dev . contracts . first } ,
proc { dev . ship }
] . each do | block |
assert_raises ActiveRecord :: StrictLoadingViolationError do
block . call
end
end
end
2019-07-17 09:55:06 -04:00
def test_preload_audit_logs_are_strict_loading_because_parent_is_strict_loading
developer = Developer . first
3 . times do
AuditLog . create ( developer : developer , message : " I am message " )
end
dev = Developer . includes ( :audit_logs ) . strict_loading . first
assert_predicate dev , :strict_loading?
assert dev . audit_logs . all? ( & :strict_loading? ) , " Expected all audit logs to be strict_loading "
end
2020-05-30 08:09:28 -04:00
def test_preload_audit_logs_are_strict_loading_because_it_is_strict_loading_by_default
with_strict_loading_by_default ( AuditLog ) do
developer = Developer . first
3 . times do
AuditLog . create ( developer : developer , message : " I am message " )
end
dev = Developer . includes ( :audit_logs ) . first
assert_not_predicate dev , :strict_loading?
assert dev . audit_logs . all? ( & :strict_loading? ) , " Expected all audit logs to be strict_loading "
end
end
2019-07-17 09:55:06 -04:00
def test_eager_load_audit_logs_are_strict_loading_because_parent_is_strict_loading_in_hm_relation
developer = Developer . first
3 . times do
AuditLog . create ( developer : developer , message : " I am message " )
end
dev = Developer . eager_load ( :strict_loading_audit_logs ) . first
assert dev . strict_loading_audit_logs . all? ( & :strict_loading? ) , " Expected all audit logs to be strict_loading "
2021-01-19 16:12:18 -05:00
dev = Developer . eager_load ( :strict_loading_audit_logs ) . strict_loading ( false ) . first
assert dev . audit_logs . none? ( & :strict_loading? ) , " Expected no audit logs to be strict_loading "
2019-07-17 09:55:06 -04:00
end
def test_eager_load_audit_logs_are_strict_loading_because_parent_is_strict_loading
developer = Developer . first
3 . times do
AuditLog . create ( developer : developer , message : " I am message " )
end
dev = Developer . eager_load ( :audit_logs ) . strict_loading . first
assert_predicate dev , :strict_loading?
assert dev . audit_logs . all? ( & :strict_loading? ) , " Expected all audit logs to be strict_loading "
2021-01-19 16:12:18 -05:00
dev = Developer . eager_load ( :audit_logs ) . strict_loading ( false ) . first
assert_not_predicate dev , :strict_loading?
assert dev . audit_logs . none? ( & :strict_loading? ) , " Expected no audit logs to be strict_loading "
2019-07-17 09:55:06 -04:00
end
2020-05-30 08:09:28 -04:00
def test_eager_load_audit_logs_are_strict_loading_because_it_is_strict_loading_by_default
with_strict_loading_by_default ( AuditLog ) do
developer = Developer . first
3 . times do
AuditLog . create ( developer : developer , message : " I am message " )
end
dev = Developer . eager_load ( :audit_logs ) . first
assert_not_predicate dev , :strict_loading?
assert_predicate AuditLog . last , :strict_loading?
assert dev . audit_logs . all? ( & :strict_loading? ) , " Expected all audit logs to be strict_loading "
end
end
2019-07-17 09:55:06 -04:00
def test_raises_on_unloaded_relation_methods_if_strict_loading
dev = Developer . strict_loading . first
assert_predicate dev , :strict_loading?
assert_raises ActiveRecord :: StrictLoadingViolationError do
dev . audit_logs . first
end
end
2020-02-21 13:11:24 -05:00
2020-05-30 08:09:28 -04:00
def test_raises_on_unloaded_relation_methods_if_strict_loading_by_default
with_strict_loading_by_default ( Developer ) do
dev = Developer . first
assert_predicate dev , :strict_loading?
assert_raises ActiveRecord :: StrictLoadingViolationError do
dev . audit_logs . first
end
end
end
2020-02-21 13:11:24 -05:00
def test_raises_on_lazy_loading_a_strict_loading_belongs_to_relation
mentor = Mentor . create! ( name : " Mentor " )
developer = Developer . first
developer . update_column ( :mentor_id , mentor . id )
assert_raises ActiveRecord :: StrictLoadingViolationError do
developer . strict_loading_mentor
end
end
2020-05-30 08:09:28 -04:00
def test_raises_on_lazy_loading_a_belongs_to_relation_if_strict_loading_by_default
with_strict_loading_by_default ( Developer ) do
mentor = Mentor . create! ( name : " Mentor " )
developer = Developer . first
developer . update_column ( :mentor_id , mentor . id )
assert_raises ActiveRecord :: StrictLoadingViolationError do
developer . mentor
end
end
end
2021-03-17 09:19:55 -04:00
def test_strict_loading_can_be_turned_off_on_an_association_in_a_model_with_strict_loading_on
2021-03-16 16:21:35 -04:00
with_strict_loading_by_default ( Developer ) do
mentor = Mentor . create! ( name : " Mentor " )
developer = Developer . first
developer . update_column ( :mentor_id , mentor . id )
assert_nothing_raised do
developer . strict_loading_off_mentor
end
end
end
2020-02-21 13:11:24 -05:00
def test_does_not_raise_on_eager_loading_a_strict_loading_belongs_to_relation
mentor = Mentor . create! ( name : " Mentor " )
Developer . first . update_column ( :mentor_id , mentor . id )
developer = Developer . includes ( :strict_loading_mentor ) . first
assert_nothing_raised { developer . strict_loading_mentor }
end
2020-05-30 08:09:28 -04:00
def test_does_not_raise_on_eager_loading_a_belongs_to_relation_if_strict_loading_by_default
with_strict_loading_by_default ( Developer ) do
mentor = Mentor . create! ( name : " Mentor " )
Developer . first . update_column ( :mentor_id , mentor . id )
developer = Developer . includes ( :mentor ) . first
assert_nothing_raised { developer . mentor }
end
end
2020-02-21 13:11:24 -05:00
def test_raises_on_lazy_loading_a_strict_loading_has_one_relation
developer = Developer . first
ship = Ship . first
ship . update_column ( :developer_id , developer . id )
assert_raises ActiveRecord :: StrictLoadingViolationError do
developer . strict_loading_ship
end
end
2020-05-30 08:09:28 -04:00
def test_raises_on_lazy_loading_a_has_one_relation_if_strict_loading_by_default
with_strict_loading_by_default ( Developer ) do
developer = Developer . first
ship = Ship . first
ship . update_column ( :developer_id , developer . id )
assert_raises ActiveRecord :: StrictLoadingViolationError do
developer . ship
end
end
end
2020-02-21 13:11:24 -05:00
def test_does_not_raise_on_eager_loading_a_strict_loading_has_one_relation
Ship . first . update_column ( :developer_id , Developer . first . id )
developer = Developer . includes ( :strict_loading_ship ) . first
assert_nothing_raised { developer . strict_loading_ship }
end
2020-05-30 08:09:28 -04:00
def test_does_not_raise_on_eager_loading_a_has_one_relation_if_strict_loading_by_default
with_strict_loading_by_default ( Developer ) do
Ship . first . update_column ( :developer_id , Developer . first . id )
developer = Developer . includes ( :ship ) . first
assert_nothing_raised { developer . ship }
end
end
2020-02-21 13:11:24 -05:00
def test_raises_on_lazy_loading_a_strict_loading_has_many_relation
developer = Developer . first
AuditLog . create (
3 . times . map do
{ developer_id : developer . id , message : " I am message " }
end
)
assert_raises ActiveRecord :: StrictLoadingViolationError do
developer . strict_loading_opt_audit_logs . first
end
end
2020-05-30 08:09:28 -04:00
def test_raises_on_lazy_loading_a_has_many_relation_if_strict_loading_by_default
with_strict_loading_by_default ( Developer ) do
developer = Developer . first
AuditLog . create (
3 . times . map do
{ developer_id : developer . id , message : " I am message " }
end
)
assert_raises ActiveRecord :: StrictLoadingViolationError do
developer . audit_logs . first
end
end
end
2020-02-21 13:11:24 -05:00
def test_does_not_raise_on_eager_loading_a_strict_loading_has_many_relation
developer = Developer . first
AuditLog . create (
3 . times . map do
{ developer_id : developer . id , message : " I am message " }
end
)
developer = Developer . includes ( :strict_loading_opt_audit_logs ) . first
assert_nothing_raised { developer . strict_loading_opt_audit_logs . first }
end
2020-05-30 08:09:28 -04:00
def test_does_not_raise_on_eager_loading_a_has_many_relation_if_strict_loading_by_default
with_strict_loading_by_default ( Developer ) do
developer = Developer . first
AuditLog . create (
3 . times . map do
{ developer_id : developer . id , message : " I am message " }
end
)
developer = Developer . includes ( :audit_logs ) . first
assert_nothing_raised { developer . audit_logs . first }
end
end
2020-02-21 13:11:24 -05:00
def test_raises_on_lazy_loading_a_strict_loading_habtm_relation
developer = Developer . first
developer . projects << Project . first
assert_not developer . strict_loading_projects . loaded?
assert_raises ActiveRecord :: StrictLoadingViolationError do
developer . strict_loading_projects . first
end
end
2020-05-30 08:09:28 -04:00
def test_raises_on_lazy_loading_a_habtm_relation_if_strict_loading_by_default
with_strict_loading_by_default ( Developer ) do
developer = Developer . first
developer . projects << Project . first
assert_not developer . projects . loaded?
assert_raises ActiveRecord :: StrictLoadingViolationError do
developer . projects . first
end
end
end
2020-02-21 13:11:24 -05:00
def test_does_not_raise_on_eager_loading_a_strict_loading_habtm_relation
Developer . first . projects << Project . first
developer = Developer . includes ( :strict_loading_projects ) . first
assert_nothing_raised { developer . strict_loading_projects . first }
end
2020-05-30 08:09:28 -04:00
def test_does_not_raise_on_eager_loading_a_habtm_relation_if_strict_loading_by_default
with_strict_loading_by_default ( Developer ) do
Developer . first . projects << Project . first
developer = Developer . includes ( :projects ) . first
assert_nothing_raised { developer . projects . first }
end
end
2020-10-30 10:40:35 -04:00
def test_strict_loading_violation_raises_by_default
2021-06-10 10:21:20 -04:00
assert_equal :raise , ActiveRecord . action_on_strict_loading_violation
2020-10-30 10:40:35 -04:00
developer = Developer . first
assert_not_predicate developer , :strict_loading?
developer . strict_loading!
assert_predicate developer , :strict_loading?
assert_raises ActiveRecord :: StrictLoadingViolationError do
developer . audit_logs . to_a
end
end
def test_strict_loading_violation_can_log_instead_of_raise
2021-06-10 10:21:20 -04:00
old_value = ActiveRecord . action_on_strict_loading_violation
ActiveRecord . action_on_strict_loading_violation = :log
assert_equal :log , ActiveRecord . action_on_strict_loading_violation
2020-10-30 10:40:35 -04:00
developer = Developer . first
assert_not_predicate developer , :strict_loading?
developer . strict_loading!
assert_predicate developer , :strict_loading?
2021-01-13 15:00:18 -05:00
assert_logged ( " Strict loading violation: Developer is marked for strict loading. The AuditLog association named :audit_logs cannot be lazily loaded. " ) do
2020-10-30 10:40:35 -04:00
developer . audit_logs . to_a
end
ensure
2021-06-10 10:21:20 -04:00
ActiveRecord . action_on_strict_loading_violation = old_value
2020-10-30 10:40:35 -04:00
end
2020-05-30 08:09:28 -04:00
private
def with_strict_loading_by_default ( model )
previous_strict_loading_by_default = model . strict_loading_by_default
model . strict_loading_by_default = true
yield
ensure
model . strict_loading_by_default = previous_strict_loading_by_default
end
2020-10-30 10:40:35 -04:00
def assert_logged ( message )
old_logger = ActiveRecord :: Base . logger
log = StringIO . new
ActiveRecord :: Base . logger = Logger . new ( log )
begin
yield
log . rewind
assert_match message , log . read
ensure
ActiveRecord :: Base . logger = old_logger
end
end
2019-07-17 09:55:06 -04:00
end
2020-12-10 15:00:40 -05:00
class StrictLoadingFixturesTest < ActiveRecord :: TestCase
fixtures :strict_zines
test " strict loading violations are ignored on fixtures " do
ActiveRecord :: FixtureSet . reset_cache
create_fixtures ( " strict_zines " )
assert_nothing_raised do
strict_zines ( :going_out ) . interests . to_a
end
assert_raises ( ActiveRecord :: StrictLoadingViolationError ) do
StrictZine . first . interests . to_a
end
end
end