mirror of
https://github.com/basecamp/once-campfire.git
synced 2026-10-09 00:00:12 +09:00
Keep fixtures :all; isolate trigger lifecycle tests
Drop the explicit fixture list and prepend module. Override load_fixtures only long enough to ensure! + backfill after alphabetical fixture load. Move destructive trigger DDL into its own test file so parallel CI workers do not strip triggers from the counter examples. Co-authored-by: Thomas Klemm <github@tklemm.eu>
This commit is contained in:
@@ -0,0 +1,102 @@
|
||||
require "test_helper"
|
||||
require "sqlite3"
|
||||
|
||||
# Destructive trigger DDL and foreign connections need their own file so parallel
|
||||
# workers (and transactional tests in messages_count_test) keep a stable trigger set.
|
||||
class Room::MessagesCountLifecycleTest < ActiveSupport::TestCase
|
||||
self.use_transactional_tests = false
|
||||
|
||||
setup do
|
||||
Room::MessagesCount.ensure!
|
||||
@room = rooms(:designers)
|
||||
@other_room = rooms(:pets)
|
||||
Room::MessagesCount.backfill!
|
||||
@room.reload
|
||||
@other_room.reload
|
||||
end
|
||||
|
||||
teardown do
|
||||
Message.where("client_message_id LIKE ?", "count-%").delete_all
|
||||
Room::MessagesCount.ensure!
|
||||
Room::MessagesCount.backfill!
|
||||
end
|
||||
|
||||
test "ensure! installs missing triggers without changing existing counts" do
|
||||
before = @room.reload.messages_count
|
||||
Room::MessagesCount.uninstall!
|
||||
|
||||
assert_not Room::MessagesCount.trigger_installed?(ActiveRecord::Base.connection, Room::MessagesCount::INSERT_TRIGGER)
|
||||
|
||||
Room::MessagesCount.ensure!
|
||||
|
||||
assert Room::MessagesCount::TRIGGERS.all? { |name|
|
||||
Room::MessagesCount.trigger_installed?(ActiveRecord::Base.connection, name)
|
||||
}
|
||||
assert_equal before, @room.reload.messages_count
|
||||
|
||||
assert_difference -> { @room.reload.messages_count }, +1 do
|
||||
@room.messages.create!(creator: users(:jason), body: "After ensure", client_message_id: "count-ensure")
|
||||
end
|
||||
end
|
||||
|
||||
test "ensure! repairs a partial trigger install" do
|
||||
Room::MessagesCount.uninstall!
|
||||
ActiveRecord::Base.connection.execute <<~SQL
|
||||
CREATE TRIGGER #{Room::MessagesCount::INSERT_TRIGGER} AFTER INSERT ON messages
|
||||
BEGIN
|
||||
UPDATE rooms SET messages_count = messages_count + 1 WHERE id = NEW.room_id;
|
||||
END
|
||||
SQL
|
||||
|
||||
assert Room::MessagesCount.trigger_installed?(ActiveRecord::Base.connection, Room::MessagesCount::INSERT_TRIGGER)
|
||||
assert_not Room::MessagesCount.trigger_installed?(ActiveRecord::Base.connection, Room::MessagesCount::DELETE_TRIGGER)
|
||||
|
||||
Room::MessagesCount.ensure!
|
||||
|
||||
assert Room::MessagesCount::TRIGGERS.all? { |name|
|
||||
Room::MessagesCount.trigger_installed?(ActiveRecord::Base.connection, name)
|
||||
}
|
||||
end
|
||||
|
||||
test "foreign SQLite connections keep the counter in step" do
|
||||
path = File.expand_path(ActiveRecord::Base.connection_db_config.database)
|
||||
now = Time.current.utc.strftime("%Y-%m-%d %H:%M:%S.%6N")
|
||||
before = @room.reload.messages_count
|
||||
other_before = @other_room.reload.messages_count
|
||||
|
||||
ActiveRecord::Base.connection_pool.release_connection
|
||||
|
||||
SQLite3::Database.new(path) do |db|
|
||||
db.busy_timeout = 5_000
|
||||
db.execute(
|
||||
"INSERT INTO messages (room_id, creator_id, client_message_id, created_at, updated_at) VALUES (?, ?, ?, ?, ?)",
|
||||
[ @room.id, users(:david).id, "count-foreign-insert", now, now ]
|
||||
)
|
||||
end
|
||||
|
||||
assert_equal before + 1, @room.reload.messages_count
|
||||
assert_equal @room.messages.count, @room.messages_count
|
||||
|
||||
foreign_id = Message.find_by!(client_message_id: "count-foreign-insert").id
|
||||
|
||||
ActiveRecord::Base.connection_pool.release_connection
|
||||
|
||||
SQLite3::Database.new(path) do |db|
|
||||
db.busy_timeout = 5_000
|
||||
db.execute("UPDATE messages SET room_id = ? WHERE id = ?", [ @other_room.id, foreign_id ])
|
||||
end
|
||||
|
||||
assert_equal before, @room.reload.messages_count
|
||||
assert_equal other_before + 1, @other_room.reload.messages_count
|
||||
|
||||
ActiveRecord::Base.connection_pool.release_connection
|
||||
|
||||
SQLite3::Database.new(path) do |db|
|
||||
db.busy_timeout = 5_000
|
||||
db.execute("DELETE FROM messages WHERE id = ?", [ foreign_id ])
|
||||
end
|
||||
|
||||
assert_equal before, @room.reload.messages_count
|
||||
assert_equal other_before, @other_room.reload.messages_count
|
||||
end
|
||||
end
|
||||
@@ -1,5 +1,4 @@
|
||||
require "test_helper"
|
||||
require "sqlite3"
|
||||
|
||||
class Room::MessagesCountTest < ActiveSupport::TestCase
|
||||
setup do
|
||||
@@ -85,102 +84,3 @@ class Room::MessagesCountTest < ActiveSupport::TestCase
|
||||
assert_not reflection.counter_cache_column
|
||||
end
|
||||
end
|
||||
|
||||
# Destructive trigger DDL and foreign connections need a committed DB.
|
||||
class Room::MessagesCountLifecycleTest < ActiveSupport::TestCase
|
||||
self.use_transactional_tests = false
|
||||
|
||||
setup do
|
||||
Room::MessagesCount.ensure!
|
||||
@room = rooms(:designers)
|
||||
@other_room = rooms(:pets)
|
||||
Room::MessagesCount.backfill!
|
||||
@room.reload
|
||||
@other_room.reload
|
||||
end
|
||||
|
||||
teardown do
|
||||
Message.where("client_message_id LIKE ?", "count-%").delete_all
|
||||
Room::MessagesCount.ensure!
|
||||
Room::MessagesCount.backfill!
|
||||
end
|
||||
|
||||
test "ensure! installs missing triggers without changing existing counts" do
|
||||
before = @room.reload.messages_count
|
||||
Room::MessagesCount.uninstall!
|
||||
|
||||
assert_not Room::MessagesCount.trigger_installed?(ActiveRecord::Base.connection, Room::MessagesCount::INSERT_TRIGGER)
|
||||
|
||||
Room::MessagesCount.ensure!
|
||||
|
||||
assert Room::MessagesCount::TRIGGERS.all? { |name|
|
||||
Room::MessagesCount.trigger_installed?(ActiveRecord::Base.connection, name)
|
||||
}
|
||||
assert_equal before, @room.reload.messages_count
|
||||
|
||||
assert_difference -> { @room.reload.messages_count }, +1 do
|
||||
@room.messages.create!(creator: users(:jason), body: "After ensure", client_message_id: "count-ensure")
|
||||
end
|
||||
end
|
||||
|
||||
test "ensure! repairs a partial trigger install" do
|
||||
Room::MessagesCount.uninstall!
|
||||
ActiveRecord::Base.connection.execute <<~SQL
|
||||
CREATE TRIGGER #{Room::MessagesCount::INSERT_TRIGGER} AFTER INSERT ON messages
|
||||
BEGIN
|
||||
UPDATE rooms SET messages_count = messages_count + 1 WHERE id = NEW.room_id;
|
||||
END
|
||||
SQL
|
||||
|
||||
assert Room::MessagesCount.trigger_installed?(ActiveRecord::Base.connection, Room::MessagesCount::INSERT_TRIGGER)
|
||||
assert_not Room::MessagesCount.trigger_installed?(ActiveRecord::Base.connection, Room::MessagesCount::DELETE_TRIGGER)
|
||||
|
||||
Room::MessagesCount.ensure!
|
||||
|
||||
assert Room::MessagesCount::TRIGGERS.all? { |name|
|
||||
Room::MessagesCount.trigger_installed?(ActiveRecord::Base.connection, name)
|
||||
}
|
||||
end
|
||||
|
||||
test "foreign SQLite connections keep the counter in step" do
|
||||
path = File.expand_path(ActiveRecord::Base.connection_db_config.database)
|
||||
now = Time.current.utc.strftime("%Y-%m-%d %H:%M:%S.%6N")
|
||||
before = @room.reload.messages_count
|
||||
other_before = @other_room.reload.messages_count
|
||||
|
||||
ActiveRecord::Base.connection_pool.release_connection
|
||||
|
||||
SQLite3::Database.new(path) do |db|
|
||||
db.busy_timeout = 5_000
|
||||
db.execute(
|
||||
"INSERT INTO messages (room_id, creator_id, client_message_id, created_at, updated_at) VALUES (?, ?, ?, ?, ?)",
|
||||
[ @room.id, users(:david).id, "count-foreign-insert", now, now ]
|
||||
)
|
||||
end
|
||||
|
||||
assert_equal before + 1, @room.reload.messages_count
|
||||
assert_equal @room.messages.count, @room.messages_count
|
||||
|
||||
foreign_id = Message.find_by!(client_message_id: "count-foreign-insert").id
|
||||
|
||||
ActiveRecord::Base.connection_pool.release_connection
|
||||
|
||||
SQLite3::Database.new(path) do |db|
|
||||
db.busy_timeout = 5_000
|
||||
db.execute("UPDATE messages SET room_id = ? WHERE id = ?", [ @other_room.id, foreign_id ])
|
||||
end
|
||||
|
||||
assert_equal before, @room.reload.messages_count
|
||||
assert_equal other_before + 1, @other_room.reload.messages_count
|
||||
|
||||
ActiveRecord::Base.connection_pool.release_connection
|
||||
|
||||
SQLite3::Database.new(path) do |db|
|
||||
db.busy_timeout = 5_000
|
||||
db.execute("DELETE FROM messages WHERE id = ?", [ foreign_id ])
|
||||
end
|
||||
|
||||
assert_equal before, @room.reload.messages_count
|
||||
assert_equal other_before, @other_room.reload.messages_count
|
||||
end
|
||||
end
|
||||
|
||||
+11
-18
@@ -6,31 +6,15 @@ require "mocha/minitest"
|
||||
require "webmock/minitest"
|
||||
require "turbo/broadcastable/test_helper"
|
||||
|
||||
# maintain_test_schema! may reload schema.rb after after_initialize; triggers are
|
||||
# not dumped, so ensure they exist before fixtures insert messages.
|
||||
Room::MessagesCount.ensure!
|
||||
|
||||
WebMock.enable!
|
||||
|
||||
module RoomMessagesCountFixtures
|
||||
# Fixture YAML still inserts messages before rooms via some load paths, so
|
||||
# INSERT triggers can update zero room rows. Reconcile once after load.
|
||||
def load_fixtures(config)
|
||||
fixtures = super
|
||||
Room::MessagesCount.backfill!
|
||||
fixtures
|
||||
end
|
||||
end
|
||||
|
||||
class ActiveSupport::TestCase
|
||||
include ActiveJob::TestHelper
|
||||
prepend RoomMessagesCountFixtures
|
||||
|
||||
parallelize(workers: :number_of_processors)
|
||||
|
||||
# Prefer rooms before messages when the loader honors declaration order.
|
||||
fixtures :accounts, :users, :rooms, :memberships, :messages, "action_text/rich_texts",
|
||||
:boosts, :searches, :sessions, :webhooks, "push/subscriptions"
|
||||
# Setup all fixtures in test/fixtures/*.yml for all tests in alphabetical order.
|
||||
fixtures :all
|
||||
|
||||
include SessionTestHelper, MentionTestHelper, TurboTestHelper, DnsTestHelper
|
||||
|
||||
@@ -49,4 +33,13 @@ class ActiveSupport::TestCase
|
||||
teardown do
|
||||
WebMock.reset!
|
||||
end
|
||||
|
||||
# fixtures :all inserts messages before rooms, so INSERT triggers cannot count
|
||||
# yet. Backfill once after load; ensure! covers schema.rb (triggers not dumped).
|
||||
def load_fixtures(config)
|
||||
fixtures = super
|
||||
Room::MessagesCount.ensure!
|
||||
Room::MessagesCount.backfill!
|
||||
fixtures
|
||||
end
|
||||
end
|
||||
|
||||
Reference in New Issue
Block a user