feat: build out blog_demo
This commit is contained in:
1 parent
1910de414a
commit
4d4bf2f007
1 file changed
+173
+173
@@ -510,6 +510,179 @@ Net::HTTP.start('127.0.0.1', port) do |http|
|
|||||||
)
|
)
|
||||||
json_create = http.post('/posts.json', json_create_body,
|
json_create = http.post('/posts.json', json_create_body,
|
||||||
'Content-Type' => 'application/x-www-form-urlencoded', 'Cookie' => cookie)
|
'Content-Type' => 'application/x-www-form-urlencoded', 'Cookie' => cookie)
|
||||||
|
raise "expected creating a post via .json to return 201, got #{json_create.code}" unless json_create.code == '201'
|
||||||
|
created_via_json = JSON.parse(json_create.body)
|
||||||
|
json_post_id = created_via_json['id']
|
||||||
|
# posts#create/#index/#update .json bodies now carry `slug` alongside
|
||||||
|
# `id` (see PostsController) - a JSON client has no other way to build
|
||||||
|
# a valid /posts/:slug URL for a post it just created, now that show/
|
||||||
|
# update/destroy are slug-keyed (see app.rb's `param: :slug`).
|
||||||
|
json_post_slug = created_via_json['slug']
|
||||||
|
raise 'json create response should echo the saved title' unless created_via_json['title'] == 'JSON API post'
|
||||||
|
raise 'json create response should carry the generated slug' if json_post_slug.to_s.empty?
|
||||||
|
puts "POST /posts.json -> #{json_create.code}, created post #{json_post_id} as a JSON payload (no redirect)"
|
||||||
|
|
||||||
|
json_create_cookie = cookie_from(json_create)
|
||||||
|
cookie = json_create_cookie unless json_create_cookie.empty?
|
||||||
|
|
||||||
|
invalid_json_create_body = URI.encode_www_form(
|
||||||
|
'post[title]' => '', 'post[body]' => '', 'authenticity_token' => token
|
||||||
|
)
|
||||||
|
invalid_json_create = http.post('/posts.json', invalid_json_create_body,
|
||||||
|
'Content-Type' => 'application/x-www-form-urlencoded', 'Cookie' => cookie)
|
||||||
|
unless invalid_json_create.code == '422'
|
||||||
|
raise "expected a blank title via .json to fail with 422, got #{invalid_json_create.code}"
|
||||||
|
end
|
||||||
|
invalid_create_errors = JSON.parse(invalid_json_create.body)['errors']
|
||||||
|
unless invalid_create_errors.is_a?(Array) && invalid_create_errors.any? { |e| e['source']['pointer'] == '/data/attributes/title' }
|
||||||
|
raise "expected a JSON:API error object pointing at /data/attributes/title, got #{invalid_json_create.body}"
|
||||||
|
end
|
||||||
|
raise 'every JSON:API error object should carry the response status as a string' unless invalid_create_errors.all? { |e| e['status'] == '422' }
|
||||||
|
puts "POST /posts.json with a blank title -> #{invalid_json_create.code}, " \
|
||||||
|
"errors[0].source.pointer=#{invalid_create_errors.first['source']['pointer']}"
|
||||||
|
|
||||||
|
json_update_body = URI.encode_www_form(
|
||||||
|
'post[title]' => 'JSON API post (edited)', 'post[body]' => 'Updated via the .json format.',
|
||||||
|
'post[lock_version]' => '0', '_method' => 'PATCH', 'authenticity_token' => token
|
||||||
|
)
|
||||||
|
json_update = http.post("/posts/#{json_post_slug}.json", json_update_body,
|
||||||
|
'Content-Type' => 'application/x-www-form-urlencoded', 'Cookie' => cookie)
|
||||||
|
raise "expected updating a post via .json to return 200, got #{json_update.code}" unless json_update.code == '200'
|
||||||
|
updated_via_json = JSON.parse(json_update.body)
|
||||||
|
raise 'json update response should reflect the new title' unless updated_via_json['title'] == 'JSON API post (edited)'
|
||||||
|
puts "PATCH /posts/#{json_post_slug}.json -> #{json_update.code}, title is now '#{updated_via_json['title']}'"
|
||||||
|
|
||||||
|
json_update_cookie = cookie_from(json_update)
|
||||||
|
cookie = json_update_cookie unless json_update_cookie.empty?
|
||||||
|
|
||||||
|
stale_json_update_body = URI.encode_www_form(
|
||||||
|
'post[title]' => 'stale attempt', 'post[body]' => 'this must be rejected',
|
||||||
|
'post[lock_version]' => '0', '_method' => 'PATCH', 'authenticity_token' => token
|
||||||
|
)
|
||||||
|
stale_json_update = http.post("/posts/#{json_post_slug}.json", stale_json_update_body,
|
||||||
|
'Content-Type' => 'application/x-www-form-urlencoded', 'Cookie' => cookie)
|
||||||
|
unless stale_json_update.code == '409'
|
||||||
|
raise "expected a stale lock_version via .json to be rejected with 409, got #{stale_json_update.code}"
|
||||||
|
end
|
||||||
|
stale_update_errors = JSON.parse(stale_json_update.body)['errors']
|
||||||
|
unless stale_update_errors.is_a?(Array) && stale_update_errors.first['source']['pointer'] == '/data/attributes/lock_version'
|
||||||
|
raise "expected the 409 body to be a JSON:API error pointing at lock_version, got #{stale_json_update.body}"
|
||||||
|
end
|
||||||
|
puts "PATCH /posts/#{json_post_slug}.json with a stale lock_version -> #{stale_json_update.code} " \
|
||||||
|
"(StaleObjectError still caught, now with a JSON:API error body instead of a re-rendered form)"
|
||||||
|
|
||||||
|
# 'no-such-post-slug', not a bare number - posts#show is slug-keyed now
|
||||||
|
# (see app.rb's `param: :slug`), so this exercises the SAME "no row
|
||||||
|
# matches" 404 path a real mistyped/stale URL would hit.
|
||||||
|
missing_html = http.get('/posts/no-such-post-slug', 'Cookie' => cookie)
|
||||||
|
puts "GET /posts/no-such-post-slug (HTML) -> #{missing_html.code} #{missing_html['content-type']}"
|
||||||
|
raise "expected a plain-text/HTML 404 for a missing post, got #{missing_html.code}" unless missing_html.code == '404'
|
||||||
|
raise 'an HTML-format 404 must not be a JSON:API error body' if missing_html.body.strip.start_with?('{')
|
||||||
|
|
||||||
|
missing_json = http.get('/posts/no-such-post-slug.json', 'Cookie' => cookie)
|
||||||
|
puts "GET /posts/no-such-post-slug.json -> #{missing_json.code} #{missing_json['content-type']}"
|
||||||
|
raise "expected a JSON:API 404 for a missing post via .json, got #{missing_json.code}" unless missing_json.code == '404'
|
||||||
|
missing_errors = JSON.parse(missing_json.body)['errors']
|
||||||
|
unless missing_errors.is_a?(Array) && missing_errors.size == 1 && missing_errors.first['status'] == '404'
|
||||||
|
raise "expected a single JSON:API error object with status 404, got #{missing_json.body}"
|
||||||
|
end
|
||||||
|
raise 'a 404 JSON:API error has no single attribute to point at, so source must be absent' if missing_errors.first.key?('source')
|
||||||
|
puts "GET /posts/no-such-post-slug.json -> #{missing_json.code}, errors[0]={status: #{missing_errors.first['status'].inspect}, " \
|
||||||
|
"detail: #{missing_errors.first['detail'].inspect}} (respond_not_found: same JSON:API shape as render_errors, no source.pointer)"
|
||||||
|
|
||||||
|
puts '-' * 40
|
||||||
|
puts 'Comment belongs_to :post, counter_cache: true - posts#index reads post.comments_count, ' \
|
||||||
|
'a plain column, instead of running a query per post to count its comments'
|
||||||
|
home_with_two_comments = http.get('/', 'Cookie' => cookie)
|
||||||
|
unless home_with_two_comments.body.include?('2 comment(s)')
|
||||||
|
raise "expected the index listing to show the counter-cached total of 2 comments, body was: " \
|
||||||
|
"#{home_with_two_comments.body[/<li class="post".*?<\/li>/m]}"
|
||||||
|
end
|
||||||
|
puts "GET / -> post listing shows '2 comment(s)' (Post#comments_count bumped twice by Comment's after_create hook)"
|
||||||
|
|
||||||
|
second_comment_id = now_stale.body.scan(/id="comment-(\d+)"/).flatten.first
|
||||||
|
raise 'could not find the second comment id (newest-first ordering) on the post page' unless second_comment_id
|
||||||
|
second_comment_delete_path = "/posts/#{post_id}/comments/#{second_comment_id}"
|
||||||
|
second_comment_destroy = http.post(
|
||||||
|
second_comment_delete_path, URI.encode_www_form('_method' => 'DELETE', 'authenticity_token' => token),
|
||||||
|
'Content-Type' => 'application/x-www-form-urlencoded', 'Cookie' => cookie
|
||||||
|
)
|
||||||
|
unless second_comment_destroy.code == '302'
|
||||||
|
raise "expected destroying the second comment to redirect, got #{second_comment_destroy.code}"
|
||||||
|
end
|
||||||
|
destroy_comment_cookie = cookie_from(second_comment_destroy)
|
||||||
|
cookie = destroy_comment_cookie unless destroy_comment_cookie.empty?
|
||||||
|
puts "POST #{second_comment_delete_path} with _method=DELETE -> #{second_comment_destroy.code}"
|
||||||
|
|
||||||
|
home_with_one_comment = http.get('/', 'Cookie' => cookie)
|
||||||
|
unless home_with_one_comment.body.include?('1 comment(s)')
|
||||||
|
raise 'expected the index listing to show comments_count back down to 1 after destroying a comment'
|
||||||
|
end
|
||||||
|
raise 'stale 2-comment count must not still be showing' if home_with_one_comment.body.include?('2 comment(s)')
|
||||||
|
puts "GET / -> post listing shows '1 comment(s)' (Comment#destroy decremented Post#comments_count back down)"
|
||||||
|
|
||||||
|
polish = http.get('/?locale=pl')
|
||||||
|
raise 'locale switch did not translate the tagline' unless polish.body.include?('Maly blog')
|
||||||
|
puts 'GET /?locale=pl -> tagline rendered in Polish'
|
||||||
|
|
||||||
|
edit_page = http.get("#{post_path}/edit", 'Cookie' => cookie)
|
||||||
|
puts "GET #{post_path}/edit -> #{edit_page.code}"
|
||||||
|
raise 'edit form should be pre-filled with the current title' unless edit_page.body.include?('value="Hello from Prism"')
|
||||||
|
|
||||||
|
puts '-' * 40
|
||||||
|
puts 'PostsController#preview - diffs a proposed edit against the saved row without persisting it, ' \
|
||||||
|
'using Prism::Orm::Dirty#restore_attributes to revert its Post instance so the SAME response can ' \
|
||||||
|
'still show the actual saved title alongside the proposed one'
|
||||||
|
preview_lock_version = edit_page.body[/name="post\[lock_version\]"[^>]*value="([^"]*)"/, 1]
|
||||||
|
raise 'edit form should carry the current lock_version for the preview scene' if preview_lock_version.to_s.empty?
|
||||||
|
preview_attempt_body = URI.encode_www_form(
|
||||||
|
'post[title]' => 'Previewed title, not yet saved', 'post[body]' => 'Previewed body, not yet saved.',
|
||||||
|
'post[lock_version]' => preview_lock_version, '_method' => 'PATCH', 'authenticity_token' => token
|
||||||
|
)
|
||||||
|
preview = http.post("#{post_path}/preview", preview_attempt_body,
|
||||||
|
'Content-Type' => 'application/x-www-form-urlencoded', 'Cookie' => cookie)
|
||||||
|
puts "POST #{post_path}/preview with _method=PATCH -> #{preview.code}"
|
||||||
|
raise "expected the preview action to render 200, got #{preview.code}" unless preview.code == '200'
|
||||||
|
unless preview.body.include?('Previewed title, not yet saved')
|
||||||
|
raise 'preview response should show the proposed title in the diff table'
|
||||||
|
end
|
||||||
|
unless preview.body.include?('Hello from Prism')
|
||||||
|
raise "preview response should still show the post's actual saved title - " \
|
||||||
|
"Post#restore_attributes should have reverted the in-memory change before rendering"
|
||||||
|
end
|
||||||
|
puts "POST #{post_path}/preview -> #{preview.code}, response shows both the proposed title (diff) and the " \
|
||||||
|
"still-saved current title (restore_attributes reverted the model instance within the same request)"
|
||||||
|
|
||||||
|
not_yet_saved = http.get(post_path, 'Cookie' => cookie)
|
||||||
|
raise 'the previewed title must never have been persisted' if not_yet_saved.body.include?('Previewed title, not yet saved')
|
||||||
|
puts "GET #{post_path} -> the previewed title was never written to disk, only diffed"
|
||||||
|
|
||||||
|
update_body = URI.encode_www_form(
|
||||||
|
'post[title]' => 'Hello from Prism (edited)', 'post[body]' => 'Updated through a dirty-tracked update.',
|
||||||
|
'_method' => 'PATCH', 'authenticity_token' => token
|
||||||
|
)
|
||||||
|
update = http.post(post_path, update_body, 'Content-Type' => 'application/x-www-form-urlencoded', 'Cookie' => cookie)
|
||||||
|
puts "POST #{post_path} with _method=PATCH -> #{update.code} Location=#{update['location']}"
|
||||||
|
raise "expected the update to redirect back to the post, got #{update.code}" unless update.code == '302'
|
||||||
|
post_update_cookie = cookie_from(update)
|
||||||
|
cookie = post_update_cookie unless post_update_cookie.empty?
|
||||||
|
|
||||||
|
updated_show = http.get(post_path, 'Cookie' => cookie)
|
||||||
|
raise 'updated title did not appear on the show page' unless updated_show.body.include?('Hello from Prism (edited)')
|
||||||
|
unless updated_show.body.include?('changed: title, body') || updated_show.body.include?('zmienione: title, body')
|
||||||
|
raise 'Prism::Orm::Dirty-backed notice should list the fields that actually changed'
|
||||||
|
end
|
||||||
|
puts "GET #{post_path} -> title/body updated, dirty-tracking notice named the changed fields"
|
||||||
|
|
||||||
|
noop_body = URI.encode_www_form(
|
||||||
|
'post[title]' => 'Hello from Prism (edited)', 'post[body]' => 'Updated through a dirty-tracked update.',
|
||||||
|
'_method' => 'PATCH', 'authenticity_token' => token
|
||||||
|
)
|
||||||
|
noop = http.post(post_path, noop_body, 'Content-Type' => 'application/x-www-form-urlencoded', 'Cookie' => cookie)
|
||||||
|
raise "expected the no-op update to redirect too, got #{noop.code}" unless noop.code == '302'
|
||||||
|
noop_cookie = cookie_from(noop)
|
||||||
|
cookie = noop_cookie unless noop_cookie.empty?
|
||||||
|
noop_show = http.get(post_path, 'Cookie' => cookie)
|
||||||
end
|
end
|
||||||
|
|
||||||
server.stop
|
server.stop
|
||||||
|
|||||||
Reference in new issue
Block a user