diff --git a/test/test_flash.rb b/test/test_flash.rb new file mode 100644 index 0000000..c54a286 --- /dev/null +++ b/test/test_flash.rb @@ -0,0 +1,69 @@ +require 'minitest/autorun' +require 'prism' + +class FlashTest < Minitest::Test + def test_set_value_is_not_visible_in_same_flash_read_before_sweep + session = Prism::Params.new + flash = Prism::Flash.new(session) + flash[:notice] = 'saved' + assert_nil flash[:notice], 'value set this request should not be readable via [] until next request' + end + + def test_sweep_persists_pending_values_into_session + session = Prism::Params.new + flash = Prism::Flash.new(session) + flash[:notice] = 'saved' + flash.sweep + assert_equal({ 'notice' => 'saved' }, session['_flash']) + end + + def test_next_request_reads_previous_pending_value + session = Prism::Params.new + first = Prism::Flash.new(session) + first[:notice] = 'saved' + first.sweep + + second = Prism::Flash.new(session) + assert_equal 'saved', second[:notice] + end + + def test_flash_does_not_carry_over_a_second_time + session = Prism::Params.new + first = Prism::Flash.new(session) + first[:notice] = 'saved' + first.sweep + + second = Prism::Flash.new(session) + second[:notice] + second.sweep + + third = Prism::Flash.new(session) + assert_nil third[:notice] + end + + def test_now_reads_current_flash_without_waiting_for_sweep + session = Prism::Params.new + session['_flash'] = { 'notice' => 'saved' } + flash = Prism::Flash.new(session) + assert_equal 'saved', flash.now[:notice] + end + + def test_keep_carries_current_flash_into_next_request + session = Prism::Params.new + session['_flash'] = { 'notice' => 'saved' } + flash = Prism::Flash.new(session) + flash.keep + flash.sweep + + next_flash = Prism::Flash.new(session) + assert_equal 'saved', next_flash[:notice] + end + + def test_empty_predicate + session = Prism::Params.new + flash = Prism::Flash.new(session) + assert flash.empty? + session['_flash'] = { 'notice' => 'x' } + assert Prism::Flash.new(session).empty? == false + end +end diff --git a/test/test_session.rb b/test/test_session.rb new file mode 100644 index 0000000..bce4212 --- /dev/null +++ b/test/test_session.rb @@ -0,0 +1,33 @@ +require 'minitest/autorun' +require 'prism' + +class SessionTest < Minitest::Test + def test_roundtrip + cookie = Prism::Session.encode({ 'user' => 'foksiu', 'count' => 3 }, 'top-secret') + decoded = Prism::Session.decode(cookie, 'top-secret') + assert_equal 'foksiu', decoded['user'] + assert_equal 3, decoded['count'] + end + + def test_tampering_rejected + cookie = Prism::Session.encode({ 'admin' => false }, 'top-secret') + payload, signature = cookie.split('--', 2) + forged = "#{payload}--#{'0' * signature.size}" + assert_equal({}, Prism::Session.decode(forged, 'top-secret')) + end + + def test_wrong_secret + cookie = Prism::Session.encode({ 'x' => 1 }, 'secret-a') + assert_equal({}, Prism::Session.decode(cookie, 'secret-b')) + end + + def test_cookie_is_url_safe + cookie = Prism::Session.encode({ 'blob' => 'a' * 60 }, 's') + assert_nil cookie[/[^A-Za-z0-9_\-]/], "expected url safe cookie, got: #{cookie}" + end + + def test_missing_cookie + assert_equal({}, Prism::Session.decode(nil, 's')) + assert_equal({}, Prism::Session.decode('', 's')) + end +end diff --git a/test/test_template.rb b/test/test_template.rb new file mode 100644 index 0000000..d75f916 --- /dev/null +++ b/test/test_template.rb @@ -0,0 +1,29 @@ +require 'minitest/autorun' +require 'prism' + +class TemplateTest < Minitest::Test + def test_interpolation + assert_equal 'Hello, Ruby!', Prism::Template.render('Hello, <%= name %>!', name: 'Ruby') + end + + def test_html_escape + result = Prism::Template.render('<%= value %>', value: '') + assert_equal '<script>alert(1)</script>', result + end + + def test_raw_output + result = Prism::Template.render('<%== value %>', value: 'bold') + assert_equal 'bold', result + end + + def test_logic + template = '<% items.each do |n| %><%= n %>;<% end %>' + assert_equal '1;2;3;', Prism::Template.render(template, items: [1, 2, 3]) + end + + def test_conditional + template = '<% if admin %>yes<% else %>no<% end %>' + assert_equal 'yes', Prism::Template.render(template, admin: true) + assert_equal 'no', Prism::Template.render(template, admin: false) + end +end diff --git a/test/test_view.rb b/test/test_view.rb new file mode 100644 index 0000000..c76f0e5 --- /dev/null +++ b/test/test_view.rb @@ -0,0 +1,55 @@ +require 'minitest/autorun' +require 'prism' +require 'tmpdir' +require 'fileutils' + +class ViewTest < Minitest::Test + def setup + @dir = Dir.mktmpdir('prism-view') + FileUtils.mkdir_p(File.join(@dir, 'posts')) + FileUtils.mkdir_p(File.join(@dir, 'layouts')) + File.write(File.join(@dir, 'posts', 'index.erb'), '<%= greeting %>') + File.write(File.join(@dir, 'posts', '_item.erb'), '[<%= label %>]') + File.write(File.join(@dir, 'layouts', 'application.erb'), 'before-<%== content %>-after') + @view = Prism::View.new(@dir) + end + + def teardown + FileUtils.remove_entry(@dir) + end + + def test_render_reads_and_evaluates_template + assert_equal 'hi', @view.render('posts/index', locals: { greeting: 'hi' }) + end + + def test_render_wraps_content_with_layout + result = @view.render('posts/index', locals: { greeting: 'hi' }, layout: :application) + assert_equal 'before-hi-after', result + end + + def test_render_partial_directly + assert_equal '[x]', @view.render_partial('posts/item', locals: { label: 'x' }) + end + + def test_partial_called_from_within_a_template_resolves_relative_to_it + File.write(File.join(@dir, 'posts', 'index.erb'), "<%== partial('item', locals: { label: greeting }) %>") + assert_equal '[hi]', @view.render('posts/index', locals: { greeting: 'hi' }) + end + + def test_render_collection_exposes_item_and_counter_locals + File.write(File.join(@dir, 'posts', '_item.erb'), '<%= label_counter %>:<%= label %>;') + result = @view.render_collection('posts/item', %w[a b], as: :label) + assert_equal '1:a;2:b;', result + end + + def test_render_collection_defaults_local_name_to_partial_basename + File.write(File.join(@dir, 'posts', '_item.erb'), '<%= item %>;') + result = @view.render_collection('posts/item', %w[a b]) + assert_equal 'a;b;', result + end + + def test_missing_template_raises_a_descriptive_error + error = assert_raises(Prism::View::TemplateNotFound) { @view.render('posts/missing') } + assert_includes error.message, 'posts/missing' + end +end