|
|
@@ -16,9 +16,8 @@ RSpec.describe 'Wiki API', type: :request do |
|
|
get '/wiki' |
|
|
get '/wiki' |
|
|
|
|
|
|
|
|
expect(response).to have_http_status(:ok) |
|
|
expect(response).to have_http_status(:ok) |
|
|
json = JSON.parse(response.body) |
|
|
|
|
|
|
|
|
|
|
|
expect(json).to be_a(Array) |
|
|
|
|
|
|
|
|
expect(json).to be_an(Array) |
|
|
expect(json).not_to be_empty |
|
|
expect(json).not_to be_empty |
|
|
|
|
|
|
|
|
expect(json[0]).to have_key('title') |
|
|
expect(json[0]).to have_key('title') |
|
|
@@ -26,12 +25,225 @@ RSpec.describe 'Wiki API', type: :request do |
|
|
end |
|
|
end |
|
|
end |
|
|
end |
|
|
|
|
|
|
|
|
|
|
|
describe 'GET /wiki/:id' do |
|
|
|
|
|
subject(:request) do |
|
|
|
|
|
get "/wiki/#{ page_id }" |
|
|
|
|
|
end |
|
|
|
|
|
|
|
|
|
|
|
let(:page_id) { page.id } |
|
|
|
|
|
|
|
|
|
|
|
context 'when wiki page exists' do |
|
|
|
|
|
it 'returns wiki page with title' do |
|
|
|
|
|
request |
|
|
|
|
|
expect(response).to have_http_status(:ok) |
|
|
|
|
|
|
|
|
|
|
|
expect(json).to include( |
|
|
|
|
|
'id' => page.id, |
|
|
|
|
|
'title' => 'spec_wiki_title') |
|
|
|
|
|
end |
|
|
|
|
|
end |
|
|
|
|
|
|
|
|
|
|
|
context 'when wiki page does not exist' do |
|
|
|
|
|
let(:page_id) { 9_999_999 } |
|
|
|
|
|
|
|
|
|
|
|
it 'returns 404' do |
|
|
|
|
|
request |
|
|
|
|
|
expect(response).to have_http_status(:not_found) |
|
|
|
|
|
end |
|
|
|
|
|
end |
|
|
|
|
|
end |
|
|
|
|
|
|
|
|
|
|
|
describe 'POST /wiki' do |
|
|
|
|
|
let(:endpoint) { '/wiki' } |
|
|
|
|
|
|
|
|
|
|
|
let(:member) { create(:user, role: 'member') } |
|
|
|
|
|
let(:guest) { create(:user, role: 'guest') } |
|
|
|
|
|
|
|
|
|
|
|
def auth_headers(user) |
|
|
|
|
|
{ 'X-Transfer-Code' => user.inheritance_code } |
|
|
|
|
|
end |
|
|
|
|
|
|
|
|
|
|
|
context 'when not logged in' do |
|
|
|
|
|
it 'returns 401' do |
|
|
|
|
|
post endpoint, params: { title: 'Test', body: 'Hello' } |
|
|
|
|
|
expect(response).to have_http_status(:unauthorized) |
|
|
|
|
|
end |
|
|
|
|
|
end |
|
|
|
|
|
|
|
|
|
|
|
context 'when logged in but not member' do |
|
|
|
|
|
it 'returns 403' do |
|
|
|
|
|
post endpoint, params: { title: 'Test', body: 'Hello' }, headers: auth_headers(guest) |
|
|
|
|
|
expect(response).to have_http_status(:forbidden) |
|
|
|
|
|
end |
|
|
|
|
|
end |
|
|
|
|
|
|
|
|
|
|
|
context 'when params invalid' do |
|
|
|
|
|
it 'returns 422 when title blank' do |
|
|
|
|
|
post endpoint, params: { title: '', body: 'Hello' }, headers: auth_headers(member) |
|
|
|
|
|
expect(response).to have_http_status(:unprocessable_entity) |
|
|
|
|
|
end |
|
|
|
|
|
|
|
|
|
|
|
it 'returns 422 when body blank' do |
|
|
|
|
|
post endpoint, params: { title: 'Test', body: '' }, headers: auth_headers(member) |
|
|
|
|
|
expect(response).to have_http_status(:unprocessable_entity) |
|
|
|
|
|
end |
|
|
|
|
|
end |
|
|
|
|
|
|
|
|
|
|
|
context 'when success' do |
|
|
|
|
|
it 'creates wiki_page and first content revision' do |
|
|
|
|
|
expect do |
|
|
|
|
|
post endpoint, params: { title: 'TestPage', body: "a\nb\nc", message: 'init' }, |
|
|
|
|
|
headers: auth_headers(member) |
|
|
|
|
|
end |
|
|
|
|
|
.to change(WikiPage, :count).by(1) |
|
|
|
|
|
.and change(WikiRevision, :count).by(1) |
|
|
|
|
|
|
|
|
|
|
|
expect(response).to have_http_status(:created) |
|
|
|
|
|
|
|
|
|
|
|
page_id = json.fetch('id') |
|
|
|
|
|
expect(json.fetch('title')).to eq('TestPage') |
|
|
|
|
|
|
|
|
|
|
|
page = WikiPage.find(page_id) |
|
|
|
|
|
rev = page.current_revision |
|
|
|
|
|
expect(rev).to be_present |
|
|
|
|
|
expect(rev).to be_content |
|
|
|
|
|
expect(rev.message).to eq('init') |
|
|
|
|
|
|
|
|
|
|
|
# body が復元できること |
|
|
|
|
|
expect(page.body).to eq("a\nb\nc") |
|
|
|
|
|
|
|
|
|
|
|
# 行数とリレーションの整合 |
|
|
|
|
|
expect(rev.lines_count).to eq(3) |
|
|
|
|
|
expect(rev.wiki_revision_lines.order(:position).pluck(:position)).to eq([0, 1, 2]) |
|
|
|
|
|
expect(rev.wiki_lines.pluck(:body)).to match_array(%w[a b c]) |
|
|
|
|
|
end |
|
|
|
|
|
|
|
|
|
|
|
it 'reuses existing WikiLine rows by sha256' do |
|
|
|
|
|
# 先に同じ行を作っておく |
|
|
|
|
|
WikiLine.create!(sha256: Digest::SHA256.hexdigest('a'), body: 'a', created_at: Time.current, updated_at: Time.current) |
|
|
|
|
|
|
|
|
|
|
|
post endpoint, |
|
|
|
|
|
params: { title: 'Reuse', body: "a\na" }, |
|
|
|
|
|
headers: auth_headers(member) |
|
|
|
|
|
|
|
|
|
|
|
page = WikiPage.find(JSON.parse(response.body).fetch('id')) |
|
|
|
|
|
rev = page.current_revision |
|
|
|
|
|
expect(rev.lines_count).to eq(2) |
|
|
|
|
|
|
|
|
|
|
|
# "a" の WikiLine が増殖しない(1行のはず) |
|
|
|
|
|
expect(WikiLine.where(body: 'a').count).to eq(1) |
|
|
|
|
|
end |
|
|
|
|
|
end |
|
|
|
|
|
end |
|
|
|
|
|
|
|
|
|
|
|
describe 'PUT /wiki/:id' do |
|
|
|
|
|
let(:member) { create(:user, role: 'member', inheritance_code: SecureRandom.hex(16)) } |
|
|
|
|
|
let(:guest) { create(:user, role: 'guest', inheritance_code: SecureRandom.hex(16)) } |
|
|
|
|
|
|
|
|
|
|
|
def auth_headers(user) |
|
|
|
|
|
{ 'X-Transfer-Code' => user.inheritance_code } |
|
|
|
|
|
end |
|
|
|
|
|
|
|
|
|
|
|
#let!(:page) { create(:wiki_page, title: 'TestPage') } |
|
|
|
|
|
let!(:page) do |
|
|
|
|
|
build(:wiki_page, title: 'TestPage').tap do |p| |
|
|
|
|
|
puts p.errors.full_messages unless p.valid? |
|
|
|
|
|
p.save! |
|
|
|
|
|
end |
|
|
|
|
|
end |
|
|
|
|
|
|
|
|
|
|
|
before do |
|
|
|
|
|
# 初期版を 1 つ作っておく(更新が“2版目”になるように) |
|
|
|
|
|
Wiki::Commit.content!(page: page, body: "a\nb", created_user: member, message: 'init') |
|
|
|
|
|
end |
|
|
|
|
|
|
|
|
|
|
|
context 'when not logged in' do |
|
|
|
|
|
it 'returns 401' do |
|
|
|
|
|
put "/wiki/#{page.id}", params: { title: 'TestPage', body: 'x' } |
|
|
|
|
|
expect(response).to have_http_status(:unauthorized) |
|
|
|
|
|
end |
|
|
|
|
|
end |
|
|
|
|
|
|
|
|
|
|
|
context 'when logged in but not member' do |
|
|
|
|
|
it 'returns 403' do |
|
|
|
|
|
put "/wiki/#{page.id}", |
|
|
|
|
|
params: { title: 'TestPage', body: 'x' }, |
|
|
|
|
|
headers: auth_headers(guest) |
|
|
|
|
|
expect(response).to have_http_status(:forbidden) |
|
|
|
|
|
end |
|
|
|
|
|
end |
|
|
|
|
|
|
|
|
|
|
|
context 'when params invalid' do |
|
|
|
|
|
it 'returns 422 when body blank' do |
|
|
|
|
|
put "/wiki/#{page.id}", |
|
|
|
|
|
params: { title: 'TestPage', body: '' }, |
|
|
|
|
|
headers: auth_headers(member) |
|
|
|
|
|
expect(response).to have_http_status(:unprocessable_entity) |
|
|
|
|
|
end |
|
|
|
|
|
|
|
|
|
|
|
it 'returns 422 when title mismatched (if you forbid rename here)' do |
|
|
|
|
|
put "/wiki/#{page.id}", |
|
|
|
|
|
params: { title: 'OtherTitle', body: 'x' }, |
|
|
|
|
|
headers: auth_headers(member) |
|
|
|
|
|
# 君の controller 例だと title 変更は 422 にしてた |
|
|
|
|
|
expect(response).to have_http_status(:unprocessable_entity) |
|
|
|
|
|
end |
|
|
|
|
|
end |
|
|
|
|
|
|
|
|
|
|
|
context 'when success' do |
|
|
|
|
|
it 'creates new revision and returns 200' do |
|
|
|
|
|
current_id = page.wiki_revisions.maximum(:id) |
|
|
|
|
|
|
|
|
|
|
|
expect do |
|
|
|
|
|
put "/wiki/#{page.id}", |
|
|
|
|
|
params: { title: 'TestPage', body: "x\ny", message: 'edit', base_revision_id: current_id }, |
|
|
|
|
|
headers: auth_headers(member) |
|
|
|
|
|
end.to change(WikiRevision, :count).by(1) |
|
|
|
|
|
|
|
|
|
|
|
expect(response).to have_http_status(:ok) |
|
|
|
|
|
|
|
|
|
|
|
page.reload |
|
|
|
|
|
rev = page.current_revision |
|
|
|
|
|
expect(rev).to be_content |
|
|
|
|
|
expect(rev.message).to eq('edit') |
|
|
|
|
|
expect(page.body).to eq("x\ny") |
|
|
|
|
|
expect(rev.base_revision_id).to eq(current_id) |
|
|
|
|
|
end |
|
|
|
|
|
end |
|
|
|
|
|
|
|
|
|
|
|
# TODO: コンフリクト未実装のため,実装したらコメント外す. |
|
|
|
|
|
# context 'when conflict' do |
|
|
|
|
|
# it 'returns 409 when base_revision_id mismatches' do |
|
|
|
|
|
# # 先に別ユーザ(同じ member でもOK)が 1 回更新して先頭を進める |
|
|
|
|
|
# Wiki::Commit.content!(page: page, body: "zzz", created_user: member, message: 'other edit') |
|
|
|
|
|
# page.reload |
|
|
|
|
|
|
|
|
|
|
|
# stale_id = page.wiki_revisions.order(:id).first.id # わざと古い id |
|
|
|
|
|
# put "/wiki/#{page.id}", |
|
|
|
|
|
# params: { title: 'TestPage', body: 'x', base_revision_id: stale_id }, |
|
|
|
|
|
# headers: auth_headers(member) |
|
|
|
|
|
|
|
|
|
|
|
# expect(response).to have_http_status(:conflict) |
|
|
|
|
|
# json = JSON.parse(response.body) |
|
|
|
|
|
# expect(json['error']).to eq('conflict') |
|
|
|
|
|
# end |
|
|
|
|
|
# end |
|
|
|
|
|
|
|
|
|
|
|
context 'when page not found' do |
|
|
|
|
|
it 'returns 404' do |
|
|
|
|
|
put "/wiki/99999999", |
|
|
|
|
|
params: { title: 'X', body: 'x' }, |
|
|
|
|
|
headers: auth_headers(member) |
|
|
|
|
|
expect(response).to have_http_status(:not_found) |
|
|
|
|
|
end |
|
|
|
|
|
end |
|
|
|
|
|
end |
|
|
|
|
|
|
|
|
describe 'GET /wiki/title/:title' do |
|
|
describe 'GET /wiki/title/:title' do |
|
|
it 'returns wiki page by title' do |
|
|
it 'returns wiki page by title' do |
|
|
get "/wiki/title/#{CGI.escape('spec_wiki_title')}" |
|
|
get "/wiki/title/#{CGI.escape('spec_wiki_title')}" |
|
|
|
|
|
|
|
|
expect(response).to have_http_status(:ok) |
|
|
expect(response).to have_http_status(:ok) |
|
|
json = JSON.parse(response.body) |
|
|
|
|
|
|
|
|
|
|
|
expect(json).to have_key('id') |
|
|
expect(json).to have_key('id') |
|
|
expect(json).to have_key('title') |
|
|
expect(json).to have_key('title') |
|
|
|