87 lines
3.2 KiB
Ruby
87 lines
3.2 KiB
Ruby
require 'minitest/autorun'
|
|
require 'prism'
|
|
require 'tmpdir'
|
|
require 'fileutils'
|
|
|
|
# Session 62's "kierunki dalsze" debt, closed here: examples/blog/app/views/
|
|
# posts/index.erb wraps its "recent posts" sidebar in `cache(:recent_posts_
|
|
# sidebar, ...)` but does NOT include current_user in the cache key, unlike
|
|
# the fresh_when etag on the same action (fixed in session 62, see
|
|
# test_conditional_get_user_scoped_etag.rb). That asymmetry is deliberate,
|
|
# not a leftover bug: the sidebar's own content (5 most recent post titles)
|
|
# never depends on who is looking, and the parts of the page that DO vary
|
|
# per viewer (signed-in-as banner, new-post form) render outside the cached
|
|
# block entirely - so unlike the etag case, sharing one cache entry across
|
|
# every viewer can never leak one visitor's per-user markup into another's
|
|
# response. This test proves that reasoning against the exact template
|
|
# shape (a cache block with viewer-independent content, sitting next to
|
|
# viewer-dependent content rendered outside it), the same isolated-app
|
|
# convention test_cache_helpers.rb and test_conditional_get_user_scoped_
|
|
# etag.rb already use rather than loading examples/blog/app.rb directly.
|
|
class FragmentCacheViewerIndependentTest < Minitest::Test
|
|
def setup
|
|
Prism::Cache.store.clear
|
|
@dir = Dir.mktmpdir('prism-fragment-cache-viewer')
|
|
FileUtils.mkdir_p(File.join(@dir, 'pages'))
|
|
@view = Prism::View.new(@dir)
|
|
write('pages/show.erb', <<~ERB)
|
|
<% cache(:recent_posts_sidebar, expires_in: 60) do %>
|
|
<%= render_count! %> recent posts, none of them yours
|
|
<% end %>
|
|
signed in as <%= viewer %>
|
|
ERB
|
|
end
|
|
|
|
def teardown
|
|
FileUtils.remove_entry(@dir)
|
|
Prism::Cache.store.clear
|
|
end
|
|
|
|
def write(relative, source)
|
|
File.write(File.join(@dir, relative), source)
|
|
end
|
|
|
|
def render_as(viewer, tracker)
|
|
@view = Prism::View.new(@dir, helpers: Tracker.new(tracker))
|
|
@view.render('pages/show', locals: { viewer: viewer })
|
|
end
|
|
|
|
def test_cache_block_renders_once_and_is_shared_across_different_viewers
|
|
tracker = []
|
|
|
|
anon_output = render_as('anonymous', tracker)
|
|
alice_output = render_as('alice', tracker)
|
|
bob_output = render_as('bob', tracker)
|
|
|
|
assert_equal 1, tracker.size,
|
|
'the fragment inside cache() should render exactly once total, not once per viewer'
|
|
assert_includes anon_output, '1 recent posts, none of them yours'
|
|
cached_line = anon_output.lines.find { |line| line.include?('recent posts') }
|
|
assert_equal cached_line, alice_output.lines.find { |line| line.include?('recent posts') }
|
|
assert_equal cached_line, bob_output.lines.find { |line| line.include?('recent posts') }
|
|
end
|
|
|
|
def test_content_outside_the_cache_block_still_varies_per_viewer
|
|
tracker = []
|
|
|
|
anon_output = render_as('anonymous', tracker)
|
|
alice_output = render_as('alice', tracker)
|
|
|
|
assert_includes anon_output, 'signed in as anonymous'
|
|
assert_includes alice_output, 'signed in as alice'
|
|
refute_equal anon_output, alice_output,
|
|
'the viewer-dependent line must still differ even though the cached fragment is shared'
|
|
end
|
|
|
|
class Tracker
|
|
def initialize(calls)
|
|
@calls = calls
|
|
end
|
|
|
|
def render_count!
|
|
@calls << true
|
|
@calls.size
|
|
end
|
|
end
|
|
end
|