このコミットが含まれているのは:
@@ -27,8 +27,8 @@ Rails.application.routes.draw do
|
|||||||
get :deerjikists
|
get :deerjikists
|
||||||
put :deerjikists, action: :update_deerjikists
|
put :deerjikists, action: :update_deerjikists
|
||||||
|
|
||||||
get :description, action: 'tag_descriptions#show'
|
get :description, to: 'tag_descriptions#show'
|
||||||
put :description, action: 'tag_descriptions#update'
|
put :description, to: 'tag_descriptions#update'
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@@ -42,10 +42,10 @@ RSpec.describe 'Tag description native API', type: :request do
|
|||||||
expect(request.path_parameters).to include(controller: 'tag_descriptions', action:)
|
expect(request.path_parameters).to include(controller: 'tag_descriptions', action:)
|
||||||
end
|
end
|
||||||
|
|
||||||
def native_write method, body:, request_headers: headers
|
def native_write body:, request_headers: headers
|
||||||
public_send(method, endpoint, params: { body: }, headers: request_headers)
|
put endpoint, params: { body: }, headers: request_headers
|
||||||
action = method == :post ? 'create' : 'update'
|
expect(request.path_parameters).to include(
|
||||||
expect(request.path_parameters).to include(controller: 'tag_descriptions', action:)
|
controller: 'tag_descriptions', action: 'update')
|
||||||
end
|
end
|
||||||
|
|
||||||
def expect_current description
|
def expect_current description
|
||||||
@@ -71,9 +71,8 @@ RSpec.describe 'Tag description native API', type: :request do
|
|||||||
controller: 'tag_descriptions', action: 'show_by_title', name: 'native_description')
|
controller: 'tag_descriptions', action: 'show_by_title', name: 'native_description')
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'routes POST by Tag ID to create' do
|
it 'does not route POST by Tag ID' do
|
||||||
expect(post: '/tags/123/description').to route_to(
|
expect(post: '/tags/123/description').not_to be_routable
|
||||||
controller: 'tag_descriptions', action: 'create', id: '123')
|
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'routes PUT by Tag ID to update' do
|
it 'routes PUT by Tag ID to update' do
|
||||||
@@ -203,170 +202,158 @@ RSpec.describe 'Tag description native API', type: :request do
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe 'POST /tags/:id/description' do
|
describe 'PUT /tags/:id/description' do
|
||||||
before { tag }
|
context 'without an existing description' do
|
||||||
|
before { tag }
|
||||||
|
|
||||||
it 'creates both current resources and histories with matching compatibility IDs' do
|
it 'creates both current resources and histories with matching compatibility IDs' do
|
||||||
expect { native_write(:post, body: 'created natively') }
|
expect { native_write(body: 'created natively') }
|
||||||
.to change(TagDescription, :count).by(1)
|
.to change(TagDescription, :count).by(1)
|
||||||
.and change(TagDescriptionVersion, :count).by(1)
|
.and change(TagDescriptionVersion, :count).by(1)
|
||||||
.and change(WikiPage, :count).by(1)
|
.and change(WikiPage, :count).by(1)
|
||||||
.and change(WikiRevision, :count).by(1)
|
.and change(WikiRevision, :count).by(1)
|
||||||
.and change(WikiVersion, :count).by(1)
|
.and change(WikiVersion, :count).by(1)
|
||||||
expect(response).to have_http_status(:created)
|
expect(response).to have_http_status(:created)
|
||||||
description = TagDescription.find_by!(tag_id: tag.id, language_code: 'ja')
|
description = TagDescription.find_by!(tag_id: tag.id, language_code: 'ja')
|
||||||
page = WikiPage.find(description.id)
|
page = WikiPage.find(description.id)
|
||||||
revision = page.current_revision
|
revision = page.current_revision
|
||||||
version = TagDescriptionVersion.find(revision.id)
|
version = TagDescriptionVersion.find(revision.id)
|
||||||
expect(description).to have_attributes(
|
expect(description).to have_attributes(
|
||||||
body: 'created natively', version_no: 1,
|
body: 'created natively', version_no: 1,
|
||||||
created_by_user_id: editor.id, updated_by_user_id: editor.id)
|
created_by_user_id: editor.id, updated_by_user_id: editor.id)
|
||||||
expect(version).to have_attributes(
|
expect(version).to have_attributes(
|
||||||
tag_id: tag.id, language_code: 'ja', body: description.body,
|
tag_id: tag.id, language_code: 'ja', body: description.body,
|
||||||
version_no: 1, event_type: 'create', created_by_user_id: editor.id)
|
version_no: 1, event_type: 'create', created_by_user_id: editor.id)
|
||||||
expect(page).to have_attributes(tag_name_id: tag_name.id, body: description.body)
|
expect(page).to have_attributes(tag_name_id: tag_name.id, body: description.body)
|
||||||
expect(revision.body).to eq(version.body)
|
expect(revision.body).to eq(version.body)
|
||||||
expect(page.wiki_versions.last.body).to eq(version.body)
|
expect(page.wiki_versions.last.body).to eq(version.body)
|
||||||
expect_current(description)
|
expect_current(description)
|
||||||
|
|
||||||
get "/wiki/#{ description.id }"
|
get "/wiki/#{ description.id }"
|
||||||
expect(response).to have_http_status(:ok)
|
expect(response).to have_http_status(:ok)
|
||||||
expect(json).to include('body' => description.body, 'revision_id' => version.id)
|
expect(json).to include('body' => description.body, 'revision_id' => version.id)
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'returns the existing validation error format on duplicate creation' do
|
[WikiPage, WikiRevision, WikiVersion, TagDescription, TagDescriptionVersion].each do |model|
|
||||||
existing = TagDescription.create!(
|
it "rolls back native create when #{ model.name } persistence fails" do
|
||||||
tag:, language_code: 'ja', body: 'existing', version_no: 1,
|
headers
|
||||||
created_by_user_id: author.id, updated_by_user_id: author.id)
|
before_state = stored_state
|
||||||
before_state = stored_state
|
aggregate_failures do
|
||||||
native_write(:post, body: 'duplicate')
|
fail_save(model) { native_write(body: 'rollback create') }
|
||||||
expect(response).to have_http_status(:unprocessable_entity)
|
expect(stored_state).to eq(before_state)
|
||||||
expect(json).to include('type' => 'validation_error', 'errors' => a_kind_of(Hash),
|
end
|
||||||
'base_errors' => a_kind_of(Array), 'message' => a_kind_of(String))
|
|
||||||
expect(stored_state).to eq(before_state)
|
|
||||||
expect(existing.reload.body).to eq('existing')
|
|
||||||
end
|
|
||||||
|
|
||||||
[WikiPage, WikiRevision, WikiVersion, TagDescription, TagDescriptionVersion].each do |model|
|
|
||||||
it "rolls back native create when #{ model.name } persistence fails" do
|
|
||||||
headers
|
|
||||||
before_state = stored_state
|
|
||||||
aggregate_failures do
|
|
||||||
fail_save(model) { native_write(:post, body: 'rollback create') }
|
|
||||||
expect(stored_state).to eq(before_state)
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
|
||||||
|
|
||||||
describe 'PUT /tags/:id/description' do
|
context 'with an existing description' do
|
||||||
let!(:page) do
|
let!(:page) do
|
||||||
tag
|
tag
|
||||||
Wiki::Commit.create_content!(tag_name:, body: 'before', created_by_user: author)
|
Wiki::Commit.create_content!(tag_name:, body: 'before', created_by_user: author)
|
||||||
end
|
end
|
||||||
let!(:description) do
|
let!(:description) do
|
||||||
TagDescription.find_by!(tag_id: tag.id, language_code: 'ja')
|
TagDescription.find_by!(tag_id: tag.id, language_code: 'ja')
|
||||||
end
|
|
||||||
|
|
||||||
it 'writes matching histories when the body changes' do
|
|
||||||
expect(page.reload.version_no).to eq(1)
|
|
||||||
expect(description.version_no).to eq(1)
|
|
||||||
expect { native_write(:put, body: 'after') }
|
|
||||||
.to change(WikiRevision, :count).by(1)
|
|
||||||
.and change(WikiVersion, :count).by(1)
|
|
||||||
.and change(TagDescriptionVersion, :count).by(1)
|
|
||||||
expect(response).to have_http_status(:ok)
|
|
||||||
expect(description.reload).to have_attributes(
|
|
||||||
body: 'after', version_no: 2,
|
|
||||||
created_by_user_id: author.id, updated_by_user_id: editor.id)
|
|
||||||
expect(page.reload).to have_attributes(body: 'after', version_no: 2)
|
|
||||||
|
|
||||||
revision = page.current_revision
|
|
||||||
version = TagDescriptionVersion.find(revision.id)
|
|
||||||
expect(version).to have_attributes(
|
|
||||||
tag_id: tag.id, language_code: 'ja', version_no: 2, event_type: 'update',
|
|
||||||
body: 'after', created_by_user_id: editor.id)
|
|
||||||
expect(revision.body).to eq(version.body)
|
|
||||||
expect(page.wiki_versions.order(:version_no).last.body).to eq(version.body)
|
|
||||||
get "/wiki/#{ description.id }"
|
|
||||||
expect(response).to have_http_status(:ok)
|
|
||||||
expect(json).to include('body' => 'after', 'revision_id' => version.id)
|
|
||||||
end
|
|
||||||
|
|
||||||
['before', "before\n\n"].each do |body|
|
|
||||||
it 'does not write histories when the normalised body is unchanged' do
|
|
||||||
before_state = stored_state
|
|
||||||
page_state = page.reload.attributes
|
|
||||||
description_state = description.reload.attributes
|
|
||||||
current_revision_id = page.current_revision.id
|
|
||||||
|
|
||||||
native_write(:put, body:)
|
|
||||||
|
|
||||||
expect(response).to have_http_status(:ok)
|
|
||||||
expect(stored_state).to eq(before_state)
|
|
||||||
expect(page.reload.attributes).to eq(page_state)
|
|
||||||
expect(description.reload.attributes).to eq(description_state)
|
|
||||||
expect(page.current_revision.id).to eq(current_revision_id)
|
|
||||||
end
|
end
|
||||||
end
|
|
||||||
|
|
||||||
[WikiPage, WikiRevision, WikiVersion, TagDescription, TagDescriptionVersion].each do |model|
|
it 'writes matching histories when the body changes' do
|
||||||
it "rolls back native update when #{ model.name } persistence fails" do
|
expect(page.reload.version_no).to eq(1)
|
||||||
headers
|
expect(description.version_no).to eq(1)
|
||||||
before_state = stored_state
|
expect { native_write(body: 'after') }
|
||||||
aggregate_failures do
|
.to change(WikiRevision, :count).by(1)
|
||||||
fail_save(model) { native_write(:put, body: 'rollback update') }
|
.and change(WikiVersion, :count).by(1)
|
||||||
|
.and change(TagDescriptionVersion, :count).by(1)
|
||||||
|
expect(response).to have_http_status(:ok)
|
||||||
|
expect(description.reload).to have_attributes(
|
||||||
|
body: 'after', version_no: 2,
|
||||||
|
created_by_user_id: author.id, updated_by_user_id: editor.id)
|
||||||
|
expect(page.reload).to have_attributes(body: 'after', version_no: 2)
|
||||||
|
|
||||||
|
revision = page.current_revision
|
||||||
|
version = TagDescriptionVersion.find(revision.id)
|
||||||
|
expect(version).to have_attributes(
|
||||||
|
tag_id: tag.id, language_code: 'ja', version_no: 2, event_type: 'update',
|
||||||
|
body: 'after', created_by_user_id: editor.id)
|
||||||
|
expect(revision.body).to eq(version.body)
|
||||||
|
expect(page.wiki_versions.order(:version_no).last.body).to eq(version.body)
|
||||||
|
get "/wiki/#{ description.id }"
|
||||||
|
expect(response).to have_http_status(:ok)
|
||||||
|
expect(json).to include('body' => 'after', 'revision_id' => version.id)
|
||||||
|
end
|
||||||
|
|
||||||
|
['before', "before\n\n"].each do |body|
|
||||||
|
it 'does not write histories when the normalised body is unchanged' do
|
||||||
|
before_state = stored_state
|
||||||
|
page_state = page.reload.attributes
|
||||||
|
description_state = description.reload.attributes
|
||||||
|
current_revision_id = page.current_revision.id
|
||||||
|
|
||||||
|
native_write(body:)
|
||||||
|
|
||||||
|
expect(response).to have_http_status(:ok)
|
||||||
expect(stored_state).to eq(before_state)
|
expect(stored_state).to eq(before_state)
|
||||||
|
expect(page.reload.attributes).to eq(page_state)
|
||||||
|
expect(description.reload.attributes).to eq(description_state)
|
||||||
|
expect(page.current_revision.id).to eq(current_revision_id)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
[WikiPage, WikiRevision, WikiVersion, TagDescription, TagDescriptionVersion].each do |model|
|
||||||
|
it "rolls back native update when #{ model.name } persistence fails" do
|
||||||
|
headers
|
||||||
|
before_state = stored_state
|
||||||
|
aggregate_failures do
|
||||||
|
fail_save(model) { native_write(body: 'rollback update') }
|
||||||
|
expect(stored_state).to eq(before_state)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe 'write access and validation' do
|
describe 'write access and validation' do
|
||||||
[:post, :put].each do |method|
|
before { tag }
|
||||||
context "with #{ method.upcase }" do
|
|
||||||
before do
|
|
||||||
tag
|
|
||||||
if method == :put
|
|
||||||
Wiki::Commit.create_content!(tag_name:, body: 'existing', created_by_user: author)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'requires authentication without changing either system' do
|
it 'requires authentication without changing either system' do
|
||||||
before_state = stored_state
|
before_state = stored_state
|
||||||
native_write(method, body: 'unauthorised', request_headers: { })
|
native_write(body: 'unauthorised', request_headers: { })
|
||||||
expect(response).to have_http_status(:unauthorized)
|
expect(response).to have_http_status(:unauthorized)
|
||||||
expect(stored_state).to eq(before_state)
|
expect(stored_state).to eq(before_state)
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'rejects a guest without changing either system' do
|
it 'rejects a guest without changing either system' do
|
||||||
guest = create(:user, :guest)
|
guest = create(:user, :guest)
|
||||||
before_state = stored_state
|
before_state = stored_state
|
||||||
native_write(method, body: 'forbidden',
|
native_write(body: 'forbidden',
|
||||||
request_headers: { 'X-Transfer-Code' => guest.inheritance_code })
|
request_headers: { 'X-Transfer-Code' => guest.inheritance_code })
|
||||||
expect(response).to have_http_status(:forbidden)
|
expect(response).to have_http_status(:forbidden)
|
||||||
expect(stored_state).to eq(before_state)
|
expect(stored_state).to eq(before_state)
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'rejects a banned member without changing either system' do
|
it 'rejects a banned member without changing either system' do
|
||||||
banned = create(:user, :member, :banned)
|
banned = create(:user, :member, :banned)
|
||||||
before_state = stored_state
|
before_state = stored_state
|
||||||
native_write(method, body: 'forbidden',
|
native_write(body: 'forbidden',
|
||||||
request_headers: { 'X-Transfer-Code' => banned.inheritance_code })
|
request_headers: { 'X-Transfer-Code' => banned.inheritance_code })
|
||||||
expect(response).to have_http_status(:forbidden)
|
expect(response).to have_http_status(:forbidden)
|
||||||
expect(stored_state).to eq(before_state)
|
expect(stored_state).to eq(before_state)
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'rejects blank body using the existing validation error format' do
|
it 'rejects blank body using the existing validation error format' do
|
||||||
headers
|
headers
|
||||||
before_state = stored_state
|
before_state = stored_state
|
||||||
native_write(method, body: '')
|
native_write(body: '')
|
||||||
expect(response).to have_http_status(:unprocessable_entity)
|
expect(response).to have_http_status(:unprocessable_entity)
|
||||||
expect(json).to include('type' => 'validation_error',
|
expect(json).to include('type' => 'validation_error',
|
||||||
'errors' => include('body' => a_kind_of(Array)))
|
'errors' => include('body' => a_kind_of(Array)))
|
||||||
expect(stored_state).to eq(before_state)
|
expect(stored_state).to eq(before_state)
|
||||||
end
|
end
|
||||||
end
|
|
||||||
|
it 'returns 404 when PUT targets a missing Tag' do
|
||||||
|
missing_id = Tag.maximum(:id).to_i + 1
|
||||||
|
put "/tags/#{ missing_id }/description",
|
||||||
|
params: { body: 'missing' },
|
||||||
|
headers: headers
|
||||||
|
expect(response).to have_http_status(:not_found)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
新しいイシューから参照
ユーザーをブロックする