mirror of
https://github.com/basecamp/once-campfire.git
synced 2026-10-08 15:50:08 +09:00
ac73267b07
Transfer the C completed-response cache lesson into Rails, keeping authentication, room checks and cookies per request. A persistent read-only SQLite observer detects local and foreign commits and rejects racing admission. Whole-page misses render fresh to avoid stale nested fragments; message ETags reflect token-neutral presentation.
140 lines
5.3 KiB
Ruby
140 lines
5.3 KiB
Ruby
require "test_helper"
|
|
|
|
class CachedResponsesTest < ActionDispatch::IntegrationTest
|
|
self.use_transactional_tests = false
|
|
|
|
setup do
|
|
host! "once.campfire.test"
|
|
sign_in :david
|
|
@previous_forgery = ActionController::Base.allow_forgery_protection
|
|
ActionController::Base.allow_forgery_protection = true
|
|
@previous_cache = Rails.cache
|
|
@previous_caching = ActionController::Base.perform_caching
|
|
Rails.cache = ActiveSupport::Cache::MemoryStore.new
|
|
ActionController::Base.perform_caching = true
|
|
@room = rooms(:watercooler)
|
|
# Establish last-room and CSRF cookies before checking reuse.
|
|
2.times { get room_url(@room) }
|
|
ResponseCache.instance.clear
|
|
end
|
|
|
|
teardown do
|
|
ResponseCache.instance.clear
|
|
ActionController::Base.allow_forgery_protection = @previous_forgery
|
|
Rails.cache = @previous_cache
|
|
ActionController::Base.perform_caching = @previous_caching
|
|
end
|
|
|
|
test "all four read actions reuse completed pages" do
|
|
urls = [ room_url(@room), room_messages_url(@room), user_sidebar_url(:me), searches_url(q: "hello") ]
|
|
urls.each do |url|
|
|
get url
|
|
assert_response :success
|
|
end
|
|
ResponseCache.instance.expects(:write).never
|
|
urls.each do |url|
|
|
get url
|
|
assert_response :success
|
|
end
|
|
end
|
|
|
|
test "cached tokens stay fresh and literal token-like text survives" do
|
|
get room_url(@room)
|
|
literal = css_select('meta[name="csrf-token"]').first["content"]
|
|
@room.messages.create!(creator: users(:david), body: "literal #{literal}")
|
|
get room_url(@room)
|
|
first = css_select('meta[name="csrf-token"]').first["content"]
|
|
get room_url(@room)
|
|
second = css_select('meta[name="csrf-token"]').first["content"]
|
|
assert_not_equal first, second
|
|
assert_includes response.body, "literal #{literal}"
|
|
assert_no_match /campfire-csrf-/, response.body
|
|
|
|
post room_messages_url(@room, format: :turbo_stream), params: {
|
|
authenticity_token: second, message: { body: "cached token works", client_message_id: "cache-token" } }
|
|
assert_response :success
|
|
end
|
|
|
|
test "local and foreign commits invalidate pages and nested fragments" do
|
|
message = @room.messages.ordered.last
|
|
get room_url(@room)
|
|
@room.messages.create!(creator: users(:david), body: "local commit")
|
|
get room_url(@room)
|
|
assert_includes response.body, "local commit"
|
|
|
|
foreign_write("UPDATE users SET name = ? WHERE id = ?", "Foreign creator", message.creator_id)
|
|
get room_url(@room)
|
|
assert_includes response.body, "Foreign creator"
|
|
foreign_write("UPDATE action_text_rich_texts SET body = ? WHERE record_type = 'Message' AND record_id = ?", "foreign message body", message.id)
|
|
get room_url(@room)
|
|
assert_includes response.body, "foreign message body"
|
|
end
|
|
|
|
test "cached room access and sessions are checked afresh" do
|
|
get room_url(@room)
|
|
foreign_write("DELETE FROM memberships WHERE room_id = ? AND user_id = ?", @room.id, users(:david).id)
|
|
get room_url(@room)
|
|
assert_redirected_to root_url
|
|
get user_sidebar_url(:me)
|
|
foreign_write("DELETE FROM sessions WHERE user_id = ?", users(:david).id)
|
|
get user_sidebar_url(:me)
|
|
assert_redirected_to new_session_url
|
|
end
|
|
|
|
test "conditional requests keep native validators" do
|
|
get room_messages_url(@room)
|
|
etag = response.headers["ETag"]
|
|
assert etag.present?
|
|
get room_messages_url(@room), headers: { "If-None-Match" => etag }
|
|
assert_response :not_modified
|
|
end
|
|
|
|
test "foreign presentation changes cannot return a false not-modified response" do
|
|
get room_messages_url(@room)
|
|
etag = response.headers["ETag"]
|
|
message = @room.messages.ordered.last
|
|
foreign_write("UPDATE action_text_rich_texts SET body = ? WHERE record_type = 'Message' AND record_id = ?", "changed presentation", message.id)
|
|
get room_messages_url(@room), headers: { "If-None-Match" => etag }
|
|
assert_response :success
|
|
assert_includes response.body, "changed presentation"
|
|
assert_not_equal etag, response.headers["ETag"]
|
|
end
|
|
|
|
test "origins and frame variants have separate entries" do
|
|
get room_url(@room)
|
|
ResponseCache.instance.expects(:write).twice
|
|
get room_url(@room), headers: { "Turbo-Frame" => "different-frame" }
|
|
host! "once.campfire.test:8081"
|
|
get room_path(@room)
|
|
assert_response :success
|
|
end
|
|
|
|
test "cache can be disabled" do
|
|
ResponseCache.instance.stubs(:budget).returns(0)
|
|
ResponseCache.instance.expects(:read).never
|
|
2.times { get room_url(@room) }
|
|
assert_response :success
|
|
end
|
|
|
|
test "a commit after authentication cannot admit captured user fields" do
|
|
Users::SidebarsController.any_instance.stubs(:set_version_headers).with do
|
|
foreign_write("UPDATE users SET name = ? WHERE id = ?", "During authentication", users(:david).id)
|
|
true
|
|
end
|
|
get user_sidebar_url(:me)
|
|
Users::SidebarsController.any_instance.unstub(:set_version_headers)
|
|
ResponseCache.instance.expects(:write).once
|
|
get user_sidebar_url(:me)
|
|
assert_includes response.body, "During authentication"
|
|
end
|
|
|
|
private
|
|
def foreign_write(sql, *bindings)
|
|
SQLite3::Database.new(ActiveRecord::Base.connection_db_config.database) do |database|
|
|
database.execute(sql, bindings)
|
|
end
|
|
# Fixtures share this thread's query cache outside the request executor.
|
|
ActiveRecord::Base.clear_query_caches_for_current_thread
|
|
end
|
|
end
|