test: cover AuthzArticlePolicy, destroy? and CacheHelpersTest
This commit is contained in:
1 parent
f0803046dd
commit
730c66caf0
2 files changed
+180
No files matched your search
@@ -0,0 +1,113 @@
|
|||||||
|
require 'minitest/autorun'
|
||||||
|
require 'prism'
|
||||||
|
require 'support'
|
||||||
|
require 'tmpdir'
|
||||||
|
|
||||||
|
class AuthzArticlePolicy < Prism::Policy
|
||||||
|
def destroy?
|
||||||
|
user && record.user_id == user.id
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
class AuthzSessionsController < Prism::Controller
|
||||||
|
include Prism::Authenticatable
|
||||||
|
authenticate_with model: :AuthzUser
|
||||||
|
|
||||||
|
def create
|
||||||
|
user = AuthzUser.find_by(email: params['email'])
|
||||||
|
if user
|
||||||
|
sign_in(user)
|
||||||
|
halt(200, 'signed in')
|
||||||
|
else
|
||||||
|
halt(401, 'invalid credentials')
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
class ArticlesController < Prism::Controller
|
||||||
|
include Prism::Authenticatable
|
||||||
|
include Prism::Authorizable
|
||||||
|
authenticate_with model: :AuthzUser
|
||||||
|
before_action :require_authentication, only: %i[destroy]
|
||||||
|
rescue_from Prism::Policy::NotAuthorizedError, with: :forbidden
|
||||||
|
|
||||||
|
def destroy
|
||||||
|
article = AuthzArticle.find(params['id'])
|
||||||
|
authorize(article)
|
||||||
|
article.destroy
|
||||||
|
halt(200, 'deleted')
|
||||||
|
rescue Prism::Orm::RecordNotFound
|
||||||
|
halt(404, 'not found')
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def forbidden(_exception)
|
||||||
|
halt(403, 'forbidden')
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
class AuthorizableApp < Prism::Application
|
||||||
|
enable_sessions(secret: 'authz-secret')
|
||||||
|
post '/sign_in', to: 'authz_sessions#create'
|
||||||
|
delete '/articles/:id', to: 'articles#destroy'
|
||||||
|
end
|
||||||
|
|
||||||
|
class AuthorizableTest < Minitest::Test
|
||||||
|
include TestSupport
|
||||||
|
|
||||||
|
def setup
|
||||||
|
@dir = Dir.mktmpdir('prism-authorizable')
|
||||||
|
@store = Prism::Orm::Store.new(File.join(@dir, 'test.pstore'))
|
||||||
|
@store.create_table(:authz_users, %w[id email])
|
||||||
|
@store.create_table(:authz_articles, %w[id title user_id])
|
||||||
|
|
||||||
|
user_class = Class.new(Prism::Orm::Model) { table_name :authz_users }
|
||||||
|
user_class.store = @store
|
||||||
|
Object.const_set(:AuthzUser, user_class)
|
||||||
|
|
||||||
|
article_class = Class.new(Prism::Orm::Model) { table_name :authz_articles }
|
||||||
|
article_class.store = @store
|
||||||
|
Object.const_set(:AuthzArticle, article_class)
|
||||||
|
|
||||||
|
@owner = AuthzUser.create!(email: 'owner@example.com')
|
||||||
|
@stranger = AuthzUser.create!(email: 'stranger@example.com')
|
||||||
|
@article = AuthzArticle.create!(title: 'mine', user_id: @owner.id)
|
||||||
|
end
|
||||||
|
|
||||||
|
def teardown
|
||||||
|
FileUtils.remove_entry(@dir)
|
||||||
|
Object.send(:remove_const, :AuthzUser) if Object.const_defined?(:AuthzUser)
|
||||||
|
Object.send(:remove_const, :AuthzArticle) if Object.const_defined?(:AuthzArticle)
|
||||||
|
end
|
||||||
|
|
||||||
|
def call(method, path, **opts)
|
||||||
|
AuthorizableApp.call(env_for(method, path, **opts))
|
||||||
|
end
|
||||||
|
|
||||||
|
def cookie_for(email)
|
||||||
|
_, headers, = call('POST', '/sign_in',
|
||||||
|
headers: { 'Content-Type' => 'application/x-www-form-urlencoded' },
|
||||||
|
body: URI.encode_www_form('email' => email))
|
||||||
|
lines = headers['Set-Cookie'].split("\n")
|
||||||
|
lines.find { |line| line.start_with?('prism.session=') }.split(';').first
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_owner_may_destroy_their_own_article
|
||||||
|
status, _, body = call('DELETE', "/articles/#{@article.id}", cookies: cookie_for('owner@example.com'))
|
||||||
|
assert_equal 200, status
|
||||||
|
assert_equal 'deleted', body.join
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_stranger_gets_403_instead_of_destroying_someone_elses_article
|
||||||
|
status, _, body = call('DELETE', "/articles/#{@article.id}", cookies: cookie_for('stranger@example.com'))
|
||||||
|
assert_equal 403, status
|
||||||
|
assert_equal 'forbidden', body.join
|
||||||
|
assert AuthzArticle.find_by(id: @article.id), 'the article must survive a denied authorization'
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_signed_out_request_never_reaches_the_policy_at_all
|
||||||
|
status, = call('DELETE', "/articles/#{@article.id}")
|
||||||
|
assert_equal 401, status
|
||||||
|
end
|
||||||
|
end
|
||||||
@@ -0,0 +1,67 @@
|
|||||||
|
require 'minitest/autorun'
|
||||||
|
require 'prism'
|
||||||
|
require 'tmpdir'
|
||||||
|
require 'fileutils'
|
||||||
|
|
||||||
|
class CacheHelpersTest < Minitest::Test
|
||||||
|
def setup
|
||||||
|
Prism::Cache.store.clear
|
||||||
|
@dir = Dir.mktmpdir('prism-cache-helpers')
|
||||||
|
FileUtils.mkdir_p(File.join(@dir, 'pages'))
|
||||||
|
@view = Prism::View.new(@dir)
|
||||||
|
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 test_cache_block_renders_and_stores_the_fragment
|
||||||
|
write('pages/show.erb', '<% cache("greeting") do %><%= greeting %><% end %>')
|
||||||
|
result = @view.render('pages/show', locals: { greeting: 'hi' })
|
||||||
|
assert_equal 'hi', result
|
||||||
|
assert_equal 'hi', Prism::Cache.store.read('greeting')
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_cache_block_is_not_reevaluated_on_a_hit
|
||||||
|
calls = []
|
||||||
|
write('pages/show.erb', '<% cache("counter") do %><%= track! %><% end %>')
|
||||||
|
@view = Prism::View.new(@dir, helpers: CounterHelper.new(calls))
|
||||||
|
|
||||||
|
first = @view.render('pages/show')
|
||||||
|
second = @view.render('pages/show')
|
||||||
|
|
||||||
|
assert_equal first, second
|
||||||
|
assert_equal 1, calls.size
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_cache_key_can_be_an_array_mixing_scope_and_a_model
|
||||||
|
model = Struct.new(:cache_key).new('posts/7-42')
|
||||||
|
write('pages/show.erb', '<% cache([:sidebar, model]) do %>rendered<% end %>')
|
||||||
|
@view.render('pages/show', locals: { model: model })
|
||||||
|
assert @view.expand_cache_key([:sidebar, model]) == 'sidebar/posts/7-42'
|
||||||
|
assert_equal 'rendered', Prism::Cache.store.read('sidebar/posts/7-42')
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_expires_in_evicts_the_entry_after_the_given_time
|
||||||
|
write('pages/show.erb', '<% cache("temp", expires_in: 0) do %>value<% end %>')
|
||||||
|
@view.render('pages/show')
|
||||||
|
sleep 0.01
|
||||||
|
assert_nil Prism::Cache.store.read('temp')
|
||||||
|
end
|
||||||
|
|
||||||
|
class CounterHelper
|
||||||
|
def initialize(calls)
|
||||||
|
@calls = calls
|
||||||
|
end
|
||||||
|
|
||||||
|
def track!
|
||||||
|
@calls << true
|
||||||
|
'x'
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
Reference in new issue
Block a user