feat: build out blog_demo

This commit is contained in:
dualfroz committed 2026-09-01 20:55:29 +02:00
1 parent 7e6bd6a5af
commit 889030db77
1 file changed
+173
+173
View File
@@ -1721,6 +1721,179 @@ Net::HTTP.start('127.0.0.1', port) do |http|
end end
puts 'Like/Comment rows under the trashed post are hidden but not gone (only_deleted still finds them, ' \ puts 'Like/Comment rows under the trashed post are hidden but not gone (only_deleted still finds them, ' \
'polymorphic dependent: :destroy through a soft-delete)' 'polymorphic dependent: :destroy through a soft-delete)'
trash_page = http.get('/posts/trash', 'Cookie' => cookie)
unless trash_page.code == '200' && trash_page.body.include?("id=\"post-#{post_id}\"")
raise "expected the post's own author to see it in their trash, got #{trash_page.code}"
end
puts "GET /posts/trash as the post's author -> 200, trashed post listed"
restore_body = URI.encode_www_form('_method' => 'PATCH', 'authenticity_token' => token)
restore = http.post("/posts/#{post_id}/restore", restore_body,
'Content-Type' => 'application/x-www-form-urlencoded', 'Cookie' => cookie)
unless restore.code == '302' && restore['location'].to_s.end_with?('/posts/trash')
raise "expected restore to redirect back to the trash, got #{restore.code} Location=#{restore['location']}"
end
puts "POST /posts/#{post_id}/restore with _method=PATCH -> #{restore.code} Location=#{restore['location']}"
restored = http.get(post_path, 'Cookie' => cookie)
raise "restored post should be reachable again, got #{restored.code}" unless restored.code == '200'
raise 'restored post should still reference its original cover file' unless restored.body.include?(File.basename(cover_file))
puts "GET #{post_path} after restore -> 200, original cover still referenced"
trash_page_after_restore = http.get('/posts/trash', 'Cookie' => cookie)
if trash_page_after_restore.body.include?("id=\"post-#{post_id}\"")
raise 'a restored post must no longer show up in the trash'
end
puts 'GET /posts/trash after restore -> the restored post is no longer listed'
# PostsController#restore calls post.restore(recursive: true) - the post's
# own Like, its cascaded-away Comment, AND that comment's own Like (a
# grandchild) should all have come back with it, not just the post row.
unless Comment.where(id: comment_id.to_i).count == 1
raise 'restore(recursive: true) should have brought the cascaded comment back too'
end
unless Like.where(likeable_type: 'Post', likeable_id: post_row_id).count == 1
raise "restore(recursive: true) should have brought the post's own Like back too"
end
unless Like.where(likeable_type: 'Comment', likeable_id: comment_id.to_i).count == 1
raise "restore(recursive: true) should have recursively restored the comment's own Like too"
end
raise 'restoring a comment should have re-incremented comments_count' unless Post.find_by!(slug: post_id).comments_count.positive?
unless Post.find_by!(slug: post_id).likes_count == 1
raise "Post#after_restore counter_cache hook should have re-incremented likes_count for the post's own Like"
end
unless Comment.find(comment_id).likes_count == 1
raise "cascaded Comment#after_restore counter_cache hook should have re-incremented its own likes_count too"
end
puts 'restore(recursive: true) brought the comment AND both Likes (post-level and comment-level) back ' \
'(Associable#cascade_restore_dependent_associations recursing two levels deep, counter_cache re-synced)'
puts '-' * 40
puts 'deleting and restoring ONE comment on its own, independent of its (live, not trashed) post - ' \
'CommentsController#trash/#restore, a flat top-level page (not nested under /posts/:post_id)'
comments_count_before_solo_delete = Post.find_by!(slug: post_id).comments_count
solo_delete_body = URI.encode_www_form('_method' => 'DELETE', 'authenticity_token' => token)
solo_delete = http.post(comment_edit_path, solo_delete_body,
'Content-Type' => 'application/x-www-form-urlencoded', 'Cookie' => cookie)
unless solo_delete.code == '302' && solo_delete['location'] == post_path
raise "expected the solo comment delete to redirect back to the post, got #{solo_delete.code}"
end
puts "POST #{comment_edit_path} with _method=DELETE -> #{solo_delete.code} (soft-deleted, Comment#acts_as_paranoid)"
post_after_solo_delete = http.get(post_path, 'Cookie' => cookie)
raise 'the post itself must stay reachable when only one of its comments is deleted' unless post_after_solo_delete.code == '200'
if post_after_solo_delete.body.include?("id=\"comment-#{comment_id}\"")
raise 'the soft-deleted comment should no longer show up on its (still live) post'
end
unless Post.find_by!(slug: post_id).comments_count == comments_count_before_solo_delete - 1
raise 'deleting one comment should decrement its post comments_count, independent of any post-level trash'
end
puts "GET #{post_path} -> post still live (200), the deleted comment is gone from it, comments_count decremented " \
'(nothing about the POST changed - Post itself was never touched)'
unless Like.where(likeable_type: 'Comment', likeable_id: comment_id.to_i).count.zero?
raise "deleting the comment should have cascaded (soft-deleted) its own Like via dependent: :destroy"
end
unless Like.only_deleted.where(likeable_type: 'Comment', likeable_id: comment_id.to_i).count == 1
raise "the comment's cascaded Like should be soft-deleted (only_deleted), not gone for good"
end
comments_trash_page = http.get('/comments/trash', 'Cookie' => cookie)
unless comments_trash_page.code == '200' && comments_trash_page.body.include?("id=\"comment-#{comment_id}\"")
raise "expected the comment author's own trash to list it, got #{comments_trash_page.code}"
end
unless comments_trash_page.body.include?('Hello from Prism')
raise "the comments trash page should label each row with its (still live) post's title"
end
puts "GET /comments/trash -> 200, deleted comment listed with a link back to its still-live post"
solo_restore_body = URI.encode_www_form('_method' => 'PATCH', 'authenticity_token' => token)
solo_restore = http.post("/comments/#{comment_id}/restore", solo_restore_body,
'Content-Type' => 'application/x-www-form-urlencoded', 'Cookie' => cookie)
unless solo_restore.code == '302' && solo_restore['location'].to_s.end_with?('/comments/trash')
raise "expected the solo comment restore to redirect back to /comments/trash, got #{solo_restore.code}"
end
puts "POST /comments/#{comment_id}/restore with _method=PATCH -> #{solo_restore.code} Location=#{solo_restore['location']}"
post_after_solo_restore = http.get(post_path, 'Cookie' => cookie)
unless post_after_solo_restore.body.include?("id=\"comment-#{comment_id}\"")
raise 'the restored comment should be back on its post'
end
unless Post.find_by!(slug: post_id).comments_count == comments_count_before_solo_delete
raise 'restoring the comment should re-increment comments_count back to what it was'
end
unless Like.where(likeable_type: 'Comment', likeable_id: comment_id.to_i).count == 1
raise "restore(recursive: true) inside CommentsController#restore should have brought the comment's own Like back too"
end
puts "GET #{post_path} -> the restored comment (and its Like) are back, comments_count restored"
comments_trash_after_restore = http.get('/comments/trash', 'Cookie' => cookie)
if comments_trash_after_restore.body.include?("id=\"comment-#{comment_id}\"")
raise 'a restored comment must no longer show up in /comments/trash'
end
puts 'GET /comments/trash after restore -> the restored comment is no longer listed'
# AccountController#comments ("Your comments" nav link) - current_user.
# comments is the one call site in this app where Comment.where(user_id:
# ...) is NOT already chained off post.comments first, so it is the only
# place the comments.user_id secondary index (added this session) gets
# picked over the pre-existing comments.post_id one for the same query.
# comment_id is one of this user's OLDEST comments (created near the very
# start of this whole demo run) and the listing orders newest-first, same
# as every other activity feed in this app, so it lands on the LAST page
# rather than page 1 once this account has commented more than 10 times.
owner_id = Comment.find(comment_id.to_i).user_id
my_comments_last_page = (Comment.where(user_id: owner_id).count / 10.0).ceil
my_comments_page = http.get("/account/comments?page=#{my_comments_last_page}", 'Cookie' => cookie)
unless my_comments_page.code == '200' && my_comments_page.body.include?("id=\"comment-#{comment_id}\"")
raise "expected /account/comments (page #{my_comments_last_page}) to list the signed-in user's own " \
"restored comment, got #{my_comments_page.code}"
end
unless my_comments_page.body.include?('Hello from Prism')
raise "the account comments page should label each row with its post's title, same as /comments/trash"
end
puts "GET /account/comments -> 200, the signed-in user's own comment listed with its post's title"
puts '-' * 40
# _method=DELETE makes Request#request_method report DELETE even though
# this is a real HTTP POST, so this call never touches the logins/ip
# throttle (that discriminator checks for POST specifically) regardless
# of the .json extension exercising SessionController#destroy's JSON branch.
logout_body = URI.encode_www_form('_method' => 'DELETE', 'authenticity_token' => token)
logout = http.post('/session.json', logout_body, 'Content-Type' => 'application/x-www-form-urlencoded', 'Cookie' => cookie)
puts "POST /session.json with _method=DELETE (sign out) -> #{logout.code}"
raise "expected JSON sign-out to return 200, got #{logout.code}" unless logout.code == '200'
raise 'JSON sign-out response should carry a message' unless JSON.parse(logout.body)['message']
signed_out_cookie = cookie_from(logout)
cookie = signed_out_cookie unless signed_out_cookie.empty?
# Authenticatable#sign_out (remember_sessions: variant) destroys THIS
# device's own LoginSession row, so device A's OLD remember-me cookie
# captured earlier must stop working now, not remain valid until
# whatever calendar date it expires.
stale_remember_attempt = http.get('/posts/trash', 'Cookie' => remember_session_token_a)
unless stale_remember_attempt.code == '302' && stale_remember_attempt['location'].to_s.end_with?('/session/new')
raise "expected the OLD remember-me cookie to stop working after sign-out, got #{stale_remember_attempt.code}"
end
puts "GET /posts/trash with device A's now-stale remember-me cookie after sign-out -> " \
"#{stale_remember_attempt.code} (sign_out destroyed its LoginSession row, so a captured old cookie is dead)"
blocked_again = http.post(
'/posts', URI.encode_www_form('post[title]' => 'still blocked', 'authenticity_token' => token),
'Content-Type' => 'application/x-www-form-urlencoded', 'Cookie' => cookie
)
puts "POST /posts after sign out -> #{blocked_again.code} Location=#{blocked_again['location']}"
unless blocked_again.code == '302' && blocked_again['location'].to_s.end_with?('/session/new')
raise "expected post#create to require authentication again after sign-out, got #{blocked_again.code}"
end
puts '-' * 40
puts 'hammering POST /session with bad credentials to exercise Prism::Middleware::RateLimiting'
login_attempt_body = URI.encode_www_form(
'user[email]' => 'writer@example.com', 'user[password]' => 'wrong password', 'authenticity_token' => token
)
# Three POST /session slots are already spent before this loop starts:
end end
server.stop server.stop