feat: expand blog_demo
This commit is contained in:
1 parent
35141a84d5
commit
1910de414a
1 file changed
+173
+173
@@ -337,6 +337,179 @@ Net::HTTP.start('127.0.0.1', port) do |http|
|
||||
|
||||
show_with_edited_comment = http.get(post_path, 'Cookie' => cookie)
|
||||
raise 'edited comment body did not appear on the show page' unless show_with_edited_comment.body.include?('Nice post, edited!')
|
||||
unless show_with_edited_comment.body.include?('changed: body') || show_with_edited_comment.body.include?('zmienione: body')
|
||||
raise 'Comment#saved_changes-backed notice should name body as the changed field'
|
||||
end
|
||||
puts "GET #{post_path} -> comment body updated, dirty-tracking notice named the changed field"
|
||||
|
||||
comment_noop_body = URI.encode_www_form('comment[body]' => 'Nice post, edited!', '_method' => 'PATCH',
|
||||
'authenticity_token' => token)
|
||||
comment_noop = http.post(comment_edit_path, comment_noop_body,
|
||||
'Content-Type' => 'application/x-www-form-urlencoded', 'Cookie' => cookie)
|
||||
raise "expected the no-op comment update to redirect too, got #{comment_noop.code}" unless comment_noop.code == '302'
|
||||
comment_noop_cookie = cookie_from(comment_noop)
|
||||
cookie = comment_noop_cookie unless comment_noop_cookie.empty?
|
||||
comment_noop_show = http.get(post_path, 'Cookie' => cookie)
|
||||
unless comment_noop_show.body.include?('Nothing changed') || comment_noop_show.body.include?('Nic sie nie zmienilo')
|
||||
raise 'submitting an identical comment body should report that zero fields actually changed'
|
||||
end
|
||||
puts "POST #{comment_edit_path} with identical body -> Comment#saved_changes was empty, notice said nothing changed"
|
||||
|
||||
# Prism::Orm::Locking on Comment: same "two browser tabs" scenario as the
|
||||
# Post edit above, proving the mechanism (auto-detected via the
|
||||
# add_lock_version_to_comments migration, zero model code) works
|
||||
# identically for a second model, not just the one it was built against.
|
||||
comment_tab_a_edit = http.get("#{comment_edit_path}/edit", 'Cookie' => cookie)
|
||||
comment_tab_b_edit = http.get("#{comment_edit_path}/edit", 'Cookie' => cookie)
|
||||
comment_tab_a_lock_version = comment_tab_a_edit.body[/name="comment\[lock_version\]"[^>]*value="([^"]*)"/, 1]
|
||||
comment_tab_b_lock_version = comment_tab_b_edit.body[/name="comment\[lock_version\]"[^>]*value="([^"]*)"/, 1]
|
||||
raise 'comment edit form should carry the current lock_version in a hidden field' if comment_tab_a_lock_version.to_s.empty?
|
||||
unless comment_tab_a_lock_version == comment_tab_b_lock_version
|
||||
raise 'both comment tabs should have observed the same lock_version before either saved'
|
||||
end
|
||||
|
||||
comment_tab_a_save_body = URI.encode_www_form(
|
||||
'comment[body]' => 'Nice post, tab A won!', 'comment[lock_version]' => comment_tab_a_lock_version,
|
||||
'_method' => 'PATCH', 'authenticity_token' => token
|
||||
)
|
||||
comment_tab_a_save = http.post(comment_edit_path, comment_tab_a_save_body,
|
||||
'Content-Type' => 'application/x-www-form-urlencoded', 'Cookie' => cookie)
|
||||
raise "expected comment tab A's save to succeed, got #{comment_tab_a_save.code}" unless comment_tab_a_save.code == '302'
|
||||
comment_tab_a_cookie = cookie_from(comment_tab_a_save)
|
||||
cookie = comment_tab_a_cookie unless comment_tab_a_cookie.empty?
|
||||
puts "POST #{comment_edit_path} tab A (lock_version=#{comment_tab_a_lock_version}) -> #{comment_tab_a_save.code}, saved first"
|
||||
|
||||
comment_tab_b_save_body = URI.encode_www_form(
|
||||
'comment[body]' => 'Nice post, tab B lost!', 'comment[lock_version]' => comment_tab_b_lock_version,
|
||||
'_method' => 'PATCH', 'authenticity_token' => token
|
||||
)
|
||||
comment_tab_b_save = http.post(comment_edit_path, comment_tab_b_save_body,
|
||||
'Content-Type' => 'application/x-www-form-urlencoded', 'Cookie' => cookie)
|
||||
unless comment_tab_b_save.code == '409'
|
||||
raise "expected comment tab B's stale save to be rejected with 409, got #{comment_tab_b_save.code}"
|
||||
end
|
||||
unless comment_tab_b_save.body.include?('Someone else changed this comment') ||
|
||||
comment_tab_b_save.body.include?('Ktos inny zmienil ten komentarz')
|
||||
raise 'the 409 response should render the comment stale-conflict notice'
|
||||
end
|
||||
puts "POST #{comment_edit_path} tab B (stale lock_version=#{comment_tab_b_lock_version}) -> " \
|
||||
"#{comment_tab_b_save.code} (expected 409, StaleObjectError caught)"
|
||||
|
||||
after_comment_conflict = http.get(post_path, 'Cookie' => cookie)
|
||||
unless after_comment_conflict.body.include?('Nice post, tab A won!')
|
||||
raise "tab B's rejected comment write must not have overwritten tab A's body"
|
||||
end
|
||||
raise "tab B's rejected comment write must not have leaked through" if after_comment_conflict.body.include?('tab B lost')
|
||||
puts "GET #{post_path} -> tab A's comment body survives untouched, Prism::Orm::Locking prevented the lost update"
|
||||
|
||||
# Comment's enum :status defaults every new comment to "pending" (column
|
||||
# default 0), so this comment - created way back before this feature
|
||||
# existed in the script - is STILL pending right now. The moderation
|
||||
# visibility scene further below (reader2, then an anonymous visitor)
|
||||
# depends on it already being approved by the time they look, same as
|
||||
# any real moderator would approve a comment before strangers see it.
|
||||
raise "the pending badge should show for the comment's own author before it's approved" unless after_comment_conflict.body.include?('Pending approval')
|
||||
approve_body = URI.encode_www_form('_method' => 'PATCH', 'authenticity_token' => token)
|
||||
approve_comment = http.post("#{comment_edit_path}/approve", approve_body,
|
||||
'Content-Type' => 'application/x-www-form-urlencoded', 'Cookie' => cookie)
|
||||
raise "expected the comment approval to redirect, got #{approve_comment.code}" unless approve_comment.code == '302'
|
||||
approve_comment_cookie = cookie_from(approve_comment)
|
||||
cookie = approve_comment_cookie unless approve_comment_cookie.empty?
|
||||
approved_show = http.get(post_path, 'Cookie' => cookie)
|
||||
raise 'an approved comment should no longer show the pending badge' if approved_show.body.include?('Pending approval')
|
||||
puts "POST #{comment_edit_path}/approve -> #{approve_comment.code} (Comment#status: pending -> approved, " \
|
||||
'Comment enum :status + CommentPolicy#approve? checked the POST owner, not the comment author)'
|
||||
|
||||
fresh_show = http.get(post_path, 'Cookie' => cookie)
|
||||
etag = fresh_show['etag']
|
||||
raise 'posts#show should set an ETag now that it uses fresh_when' unless etag
|
||||
puts "GET #{post_path} -> #{fresh_show.code}, ETag=#{etag}"
|
||||
|
||||
conditional_show = http.get(post_path, 'Cookie' => cookie, 'If-None-Match' => etag)
|
||||
puts "GET #{post_path} with If-None-Match: #{etag} -> #{conditional_show.code}"
|
||||
raise "expected a fresh conditional GET to return 304, got #{conditional_show.code}" unless conditional_show.code == '304'
|
||||
raise '304 response must not carry a body' unless conditional_show.body.to_s.empty?
|
||||
|
||||
second_comment_body = URI.encode_www_form(
|
||||
'comment[author]' => 'Another reader', 'comment[body]' => 'Me too!', 'authenticity_token' => token
|
||||
)
|
||||
second_comment = http.post("/posts/#{post_id}/comments", second_comment_body,
|
||||
'Content-Type' => 'application/x-www-form-urlencoded', 'Cookie' => cookie)
|
||||
raise "expected the second comment to redirect, got #{second_comment.code}" unless second_comment.code == '302'
|
||||
second_comment_cookie = cookie_from(second_comment)
|
||||
cookie = second_comment_cookie unless second_comment_cookie.empty?
|
||||
|
||||
now_stale = http.get(post_path, 'Cookie' => cookie, 'If-None-Match' => etag)
|
||||
puts "GET #{post_path} with the OLD If-None-Match after a new comment -> #{now_stale.code}"
|
||||
unless now_stale.code == '200' && now_stale.body.include?('Me too!')
|
||||
raise "expected the stale ETag to be rejected and the fresh page (with the new comment) rendered, got #{now_stale.code}"
|
||||
end
|
||||
raise 'the fresh render must carry a NEW ETag, not the stale one' if now_stale['etag'] == etag
|
||||
puts "GET #{post_path} -> #{now_stale.code} (ETag changed after a comment was added, Prism::ConditionalGet is content-aware)"
|
||||
|
||||
json_index = http.get('/posts.json')
|
||||
puts "GET /posts.json -> #{json_index.code} #{json_index['content-type']}"
|
||||
parsed = JSON.parse(json_index.body)
|
||||
raise 'json index did not include the new post' unless parsed.any? { |row| row['title'] == 'Hello from Prism' }
|
||||
|
||||
puts '-' * 40
|
||||
puts 'CORS: Prism::Middleware::Cors + Prism::Cors, matched against the .json API surface only - a ' \
|
||||
'public read-only rule for any origin, and a credentialed read/write rule for one trusted admin origin'
|
||||
|
||||
public_client_origin = 'https://blog-widget.example.net'
|
||||
cors_public_get = http.get('/posts.json', 'Origin' => public_client_origin)
|
||||
unless cors_public_get.code == '200' && cors_public_get['access-control-allow-origin'] == public_client_origin
|
||||
raise "expected a public GET /posts.json to echo Access-Control-Allow-Origin: #{public_client_origin}, " \
|
||||
"got #{cors_public_get['access-control-allow-origin'].inspect}"
|
||||
end
|
||||
if cors_public_get['access-control-allow-credentials']
|
||||
raise 'a public (non-credentialed) CORS resource must not set Access-Control-Allow-Credentials'
|
||||
end
|
||||
puts "GET /posts.json with Origin: #{public_client_origin} -> #{cors_public_get.code}, " \
|
||||
"Access-Control-Allow-Origin=#{cors_public_get['access-control-allow-origin']} (any origin can read)"
|
||||
|
||||
cors_public_write_preflight = http.options('/posts', 'Origin' => public_client_origin,
|
||||
'Access-Control-Request-Method' => 'POST')
|
||||
unless cors_public_write_preflight.code == '403'
|
||||
raise "expected the public origin's write preflight to be rejected, got #{cors_public_write_preflight.code}"
|
||||
end
|
||||
puts "OPTIONS /posts (preflight for POST) with Origin: #{public_client_origin} -> " \
|
||||
"#{cors_public_write_preflight.code} (the public rule only allows GET, POST is refused before it " \
|
||||
'ever reaches the router)'
|
||||
|
||||
admin_origin = 'https://admin.blog-demo.internal'
|
||||
cors_admin_preflight = http.options('/posts', 'Origin' => admin_origin,
|
||||
'Access-Control-Request-Method' => 'POST',
|
||||
'Access-Control-Request-Headers' => 'Content-Type, X-CSRF-Token')
|
||||
unless cors_admin_preflight.code == '204' && cors_admin_preflight['access-control-allow-credentials'] == 'true'
|
||||
raise "expected the trusted admin origin's preflight to succeed with credentials allowed, got " \
|
||||
"#{cors_admin_preflight.code}"
|
||||
end
|
||||
unless cors_admin_preflight['access-control-allow-methods'].to_s.include?('POST')
|
||||
raise 'expected the preflight response to advertise POST among the allowed methods'
|
||||
end
|
||||
puts "OPTIONS /posts (preflight for POST) with Origin: #{admin_origin} -> #{cors_admin_preflight.code}, " \
|
||||
"Allow-Methods=#{cors_admin_preflight['access-control-allow-methods']}, Allow-Credentials=true " \
|
||||
'(a second, more privileged CORS rule for the trusted admin dashboard origin)'
|
||||
|
||||
cors_unrelated_path = http.get('/', 'Origin' => public_client_origin)
|
||||
if cors_unrelated_path['access-control-allow-origin']
|
||||
raise 'a path with no configured CORS resource must not carry any Access-Control-* headers, even with ' \
|
||||
'an Origin header present'
|
||||
end
|
||||
puts "GET / with Origin: #{public_client_origin} -> #{cors_unrelated_path.code}, no Access-Control-* " \
|
||||
'headers (the homepage is not a registered CORS resource, so the middleware passes it straight through)'
|
||||
|
||||
puts '-' * 40
|
||||
puts 'JSON:API-lite validation/conflict errors (Prism::Orm::Errors#to_jsonapi + ' \
|
||||
'Renderable#render_errors) - PostsController#create/#update now respond_to format.json ' \
|
||||
'on both the happy and the failure path, not just posts#index'
|
||||
json_create_body = URI.encode_www_form(
|
||||
'post[title]' => 'JSON API post', 'post[body]' => 'Created via the .json format.',
|
||||
'authenticity_token' => token
|
||||
)
|
||||
json_create = http.post('/posts.json', json_create_body,
|
||||
'Content-Type' => 'application/x-www-form-urlencoded', 'Cookie' => cookie)
|
||||
end
|
||||
|
||||
server.stop
|
||||
|
||||
Reference in new issue
Block a user