From f56394c187a2291286a0ffd179cfdce1ca677373 Mon Sep 17 00:00:00 2001 From: Daniel Colson Date: Sun, 28 Oct 2018 16:01:48 -0400 Subject: [PATCH] Set timestamps before clearing changes e7f66fb2 fixed one problem and introduced a new one. Setting timestamps after clearing_changes_information doesn't work, since using the created_at and updated_at setters will change the object again. I added some additional specs for this case (our only spec related to clearing changes used an object without timestamps). For the timezone problem, I did a little digging and realized the casting behavior was actually correct. The reason it was behaving in a different way than I was expecting is because we had `time_zone_aware_attributes` set to false, whereas the AR railtie [sets it to true](https://github.com/rails/rails/blob/54652d886ad1c9ffca36c6f2baadcdd6135ef4dd/activerecord/lib/active_record/railtie.rb#L71). Setting it to true causes date and datetime attributes [to get decorated with a TimeZoneConverter](https://github.com/rails/rails/blob/54652d886ad1c9ffca36c6f2baadcdd6135ef4dd/activerecord/lib/active_record/attribute_methods/time_zone_conversion.rb#L68..L87) --- lib/factory_bot/strategy/stub.rb | 2 +- spec/acceptance/build_stubbed_spec.rb | 104 +++++++++++++++++++++++--- 2 files changed, 96 insertions(+), 10 deletions(-) diff --git a/lib/factory_bot/strategy/stub.rb b/lib/factory_bot/strategy/stub.rb index ead07fa..1f9ab14 100644 --- a/lib/factory_bot/strategy/stub.rb +++ b/lib/factory_bot/strategy/stub.rb @@ -31,8 +31,8 @@ module FactoryBot def result(evaluation) evaluation.object.tap do |instance| stub_database_interaction_on_result(instance) - clear_changes_information(instance) set_timestamps(instance) + clear_changes_information(instance) evaluation.notify(:after_stub, instance) end end diff --git a/spec/acceptance/build_stubbed_spec.rb b/spec/acceptance/build_stubbed_spec.rb index f024eaf..f54044c 100644 --- a/spec/acceptance/build_stubbed_spec.rb +++ b/spec/acceptance/build_stubbed_spec.rb @@ -156,14 +156,9 @@ describe "defaulting `created_at`" do expect(build_stubbed(:thing_with_timestamp).created_at).to eq Time.now end - it "defaults created_at for objects with created_at to the correct time with zone" do - original_timezone = ENV["TZ"] - ENV["TZ"] = "UTC" - Time.zone = "Eastern Time (US & Canada)" - - expect(build_stubbed(:thing_with_timestamp).created_at.zone).to eq "EST" - - ENV["TZ"] = original_timezone + it "is doesn't mark the object as changed" do + stub = build_stubbed(:thing_with_timestamp) + expect(stub).not_to be_changed end it "doesn't add created_at to objects who don't have the method" do @@ -172,7 +167,9 @@ describe "defaulting `created_at`" do end it "allows overriding created_at for objects with created_at" do - expect(build_stubbed(:thing_with_timestamp, created_at: 3.days.ago).created_at).to eq 3.days.ago + created_at = 3.days.ago + stubbed = build_stubbed(:thing_with_timestamp, created_at: created_at) + expect(stubbed.created_at).to eq created_at end it "doesn't allow setting created_at on an object that doesn't define it" do @@ -186,6 +183,48 @@ describe "defaulting `created_at`" do stub.created_at = 3.days.ago expect(stub.created_at).to eq 3.days.ago end + + context "when using time_zone_aware_attributes" do + it "defaults created_at to the local time zone" do + Time.zone = "Eastern Time (US & Canada)" + ActiveRecord::Base.time_zone_aware_attributes = true + + stub = build_stubbed(:thing_with_timestamp) + + expect(stub.created_at.zone).to eq "EST" + end + + it "casts created_at override to local time zone" do + Time.zone = "Eastern Time (US & Canada)" + ActiveRecord::Base.time_zone_aware_attributes = true + created_at = Time.now.in_time_zone("CET") + + stub = build_stubbed(:thing_with_timestamp, created_at: created_at) + + expect(stub.created_at.zone).to eq "EST" + end + end + + context "when NOT using time_zone_aware_attributes" do + it "defaults created_at to UTC" do + Time.zone = "Eastern Time (US & Canada)" + ActiveRecord::Base.time_zone_aware_attributes = false + + stub = build_stubbed(:thing_with_timestamp) + + expect(stub.created_at.zone).to eq "UTC" + end + + it "casts created_at override to UTC" do + Time.zone = "Eastern Time (US & Canada)" + ActiveRecord::Base.time_zone_aware_attributes = false + created_at = Time.now.in_time_zone("CET") + + stub = build_stubbed(:thing_with_timestamp, created_at: created_at) + + expect(stub.created_at.zone).to eq "UTC" + end + end end describe "defaulting `updated_at`" do @@ -207,6 +246,11 @@ describe "defaulting `updated_at`" do expect(build_stubbed(:thing_with_timestamp).updated_at).to eq Time.current end + it "is doesn't mark the object as changed" do + stub = build_stubbed(:thing_with_timestamp) + expect(stub).not_to be_changed + end + it "doesn't add updated_at to objects who don't have the method" do expect(build_stubbed(:thing_without_timestamp)). not_to respond_to(:updated_at) @@ -229,6 +273,48 @@ describe "defaulting `updated_at`" do stub.updated_at = 3.days.ago expect(stub.updated_at).to eq 3.days.ago end + + context "when using time_zone_aware_attributes" do + it "defaults updated_at to the local time zone" do + Time.zone = "Eastern Time (US & Canada)" + ActiveRecord::Base.time_zone_aware_attributes = true + + stub = build_stubbed(:thing_with_timestamp) + + expect(stub.updated_at.zone).to eq "EST" + end + + it "casts updated_at override to local time zone" do + Time.zone = "Eastern Time (US & Canada)" + ActiveRecord::Base.time_zone_aware_attributes = true + updated_at = Time.now.in_time_zone("CET") + + stub = build_stubbed(:thing_with_timestamp, updated_at: updated_at) + + expect(stub.updated_at.zone).to eq "EST" + end + end + + context "when NOT using time_zone_aware_attributes" do + it "defaults updated_at to UTC" do + Time.zone = "Eastern Time (US & Canada)" + ActiveRecord::Base.time_zone_aware_attributes = false + + stub = build_stubbed(:thing_with_timestamp) + + expect(stub.updated_at.zone).to eq "UTC" + end + + it "casts updated_at override to UTC" do + Time.zone = "Eastern Time (US & Canada)" + ActiveRecord::Base.time_zone_aware_attributes = false + updated_at = Time.now.in_time_zone("CET") + + stub = build_stubbed(:thing_with_timestamp, updated_at: updated_at) + + expect(stub.updated_at.zone).to eq "UTC" + end + end end describe "defaulting `id`" do