Files
once-campfire/test/models/response_cache_test.rb
T
2026-10-07 21:38:36 +02:00

89 lines
3.1 KiB
Ruby

require "test_helper"
require "tmpdir"
class ResponseCacheTest < ActiveSupport::TestCase
setup do
@directory = Dir.mktmpdir
@database = File.join(@directory, "cache.sqlite3")
SQLite3::Database.new(@database) do |database|
database.execute("PRAGMA journal_mode=WAL")
database.execute("CREATE TABLE values_for_test (value TEXT)")
end
ActiveRecord::Base.stubs(:connection_db_config).returns(Struct.new(:database).new(@database))
@cache = ResponseCache.new
@cache.stubs(:budget).returns(1024)
end
teardown do
@cache.clear
FileUtils.remove_entry(@directory)
end
test "foreign commits expire entries and reject old render admission" do
version = @cache.version
@cache.write("page", version, entry("first"))
assert_equal "first", @cache.read("page", version)[:body]
SQLite3::Database.new(@database) { |database| database.execute("INSERT INTO values_for_test VALUES ('committed')") }
assert_nil @cache.read("page", version)
@cache.write("page", version, entry("old in-flight render"))
assert_nil @cache.read("page", @cache.version)
@cache.write("page", @cache.version, entry("fresh"))
assert_equal "fresh", @cache.read("page", @cache.version)[:body]
end
test "byte budget evicts oldest and bypasses oversized entries and keys" do
version = @cache.version
@cache.stubs(:budget).returns(512)
@cache.write("first", version, entry("a" * 100))
@cache.write("second", version, entry("b" * 100))
assert_nil @cache.read("first", version)
assert_equal "b" * 100, @cache.read("second", version)[:body]
@cache.write("oversized", version, entry("c" * 1000))
assert_nil @cache.read("oversized", version)
@cache.write("k" * 2049, version, entry("small"))
assert_nil @cache.read("k" * 2049, version)
end
test "clearing the observer gives later pages a distinct namespace" do
version = @cache.version
@cache.clear
assert_not_equal version, @cache.version
end
test "cold renders coalesce without holding the observer mutex" do
version = @cache.version
entered = Queue.new
release = Queue.new
first = Thread.new do
@cache.synchronize_render("page", version) do
entered << true
release.pop
@cache.write("page", version, entry("completed render"))
end
end
entered.pop
second_started = Queue.new
second = Thread.new do
second_started << true
@cache.synchronize_render("page", version) { @cache.read("page", version) }
end
second_started.pop
deadline = Process.clock_gettime(Process::CLOCK_MONOTONIC) + 2
Thread.pass until second.status == "sleep" || Process.clock_gettime(Process::CLOCK_MONOTONIC) >= deadline
assert_equal "sleep", second.status
assert_nil @cache.read("page", version)
assert_equal version, @cache.version
release << true
first.value
assert_equal "completed render", second.value[:body]
ensure
release << true if release
[ first, second ].compact.each { |thread| thread.join(2) }
end
private
def entry(body)
{ body: body, marker: "unexposed-marker", headers: {} }
end
end