185 行
6.1 KiB
Ruby
185 行
6.1 KiB
Ruby
require 'rails_helper'
|
|
|
|
RSpec.describe 'Post imports API', type: :request do
|
|
let(:member) { create(:user, :member) }
|
|
|
|
before do
|
|
allow(Preview::UrlSafety).to receive(:validate) do |url|
|
|
[URI.parse(url), ['8.8.8.8']]
|
|
end
|
|
allow(PostMetadataFetcher).to receive(:fetch).and_return(
|
|
title: 'fetched title',
|
|
thumbnail_base: nil,
|
|
tags: ''
|
|
)
|
|
end
|
|
|
|
describe 'POST /posts/import/preview' do
|
|
it 'requires a member' do
|
|
sign_out
|
|
post '/posts/import/preview', params: { source: 'https://example.com/post' }
|
|
expect(response).to have_http_status(:unauthorized)
|
|
|
|
sign_in_as(create(:user, :guest))
|
|
post '/posts/import/preview', params: { source: 'https://example.com/post' }
|
|
expect(response).to have_http_status(:forbidden)
|
|
end
|
|
|
|
it 'parses a URL list and returns preview rows' do
|
|
sign_in_as(member)
|
|
|
|
post '/posts/import/preview', params: {
|
|
source: " https://example.com/one \r\n\r\nhttps://example.com/two"
|
|
}
|
|
|
|
expect(response).to have_http_status(:ok)
|
|
expect(json.fetch('rows').map { _1.fetch('source_row') }).to eq([1, 3])
|
|
expect(json.fetch('rows').map { _1.fetch('url') }).to eq([
|
|
'https://example.com/one',
|
|
'https://example.com/two'
|
|
])
|
|
end
|
|
|
|
it 'returns a safe 400 response for an invalid source' do
|
|
sign_in_as(member)
|
|
|
|
post '/posts/import/preview', params: { source: '' }
|
|
|
|
expect(response).to have_http_status(:bad_request)
|
|
expect(json.fetch('message')).to eq('URL を入力してください.')
|
|
end
|
|
end
|
|
|
|
describe 'POST /posts/import/validate' do
|
|
it 'accepts camel-case row properties and returns their warnings' do
|
|
sign_in_as(member)
|
|
|
|
post '/posts/import/validate', params: {
|
|
rows: [{
|
|
sourceRow: '1',
|
|
url: 'https://example.com/post',
|
|
metadataUrl: 'https://example.com/post',
|
|
attributes: { title: 'manual title' },
|
|
provenance: { url: 'manual', title: 'manual' },
|
|
tagSources: { automatic: '', manual: '' },
|
|
fieldWarnings: { title: ['old warning'] },
|
|
baseWarnings: ['base warning']
|
|
}],
|
|
changed_row: -1
|
|
}
|
|
|
|
expect(response).to have_http_status(:ok)
|
|
result = json.fetch('rows').first
|
|
expect(result.fetch('source_row')).to eq(1)
|
|
expect(result.fetch('tag_sources')).to eq('automatic' => '', 'manual' => '')
|
|
expect(result.fetch('field_warnings')).to eq('title' => ['old warning'])
|
|
expect(result.fetch('base_warnings')).to eq(['base warning'])
|
|
end
|
|
|
|
it 'rejects a non-array rows value with 400' do
|
|
sign_in_as(member)
|
|
|
|
post '/posts/import/validate', params: { rows: { sourceRow: 1 }, changed_row: -1 }
|
|
|
|
expect(response).to have_http_status(:bad_request)
|
|
expect(json.fetch('message')).to eq('取込行の形式が不正です.')
|
|
end
|
|
|
|
it 'returns original created datetime validation errors for minute precision' do
|
|
sign_in_as(member)
|
|
|
|
post '/posts/import/validate', params: {
|
|
rows: [{
|
|
sourceRow: 1,
|
|
url: 'https://example.com/post',
|
|
metadataUrl: 'https://example.com/post',
|
|
attributes: {
|
|
originalCreatedFrom: '2020-01-01T00:00:30Z',
|
|
originalCreatedBefore: '2020-01-01T00:01Z' },
|
|
provenance: {
|
|
url: 'manual',
|
|
originalCreatedFrom: 'manual',
|
|
originalCreatedBefore: 'manual' },
|
|
tagSources: { automatic: '', manual: '' }
|
|
}],
|
|
changed_row: -1
|
|
}
|
|
|
|
expect(response).to have_http_status(:ok)
|
|
expect(json.fetch('rows').first.fetch('validation_errors')).to include(
|
|
'original_created_from' => ['オリジナルの作成日時は分単位で入力してください.']
|
|
)
|
|
expect(json.fetch('rows').first.fetch('validation_errors'))
|
|
.not_to have_key('original_created_at')
|
|
end
|
|
end
|
|
|
|
describe 'POST /posts/import' do
|
|
it 'keeps the duration string contract through preview, validate, and import' do
|
|
sign_in_as(member)
|
|
allow(PostMetadataFetcher).to receive(:fetch).and_return(
|
|
title: 'fetched title',
|
|
thumbnail_base: nil,
|
|
duration: '2.5',
|
|
tags: '動画'
|
|
)
|
|
|
|
post '/posts/import/preview', params: {
|
|
source: 'https://example.com/video'
|
|
}
|
|
preview_row = json.fetch('rows').first
|
|
expect(preview_row.dig('attributes', 'duration')).to eq('2.5')
|
|
|
|
post '/posts/import/validate', params: {
|
|
rows: [{
|
|
sourceRow: preview_row.fetch('source_row'),
|
|
url: preview_row.fetch('url'),
|
|
attributes: preview_row.fetch('attributes'),
|
|
provenance: preview_row.fetch('provenance'),
|
|
tagSources: preview_row.fetch('tag_sources'),
|
|
metadataUrl: preview_row.fetch('metadata_url')
|
|
}],
|
|
changed_row: -1
|
|
}
|
|
validated_row = json.fetch('rows').first
|
|
expect(validated_row.dig('attributes', 'duration')).to eq('2.5')
|
|
|
|
post '/posts/import', params: {
|
|
rows: [{
|
|
sourceRow: validated_row.fetch('source_row'),
|
|
url: validated_row.fetch('url'),
|
|
attributes: validated_row.fetch('attributes'),
|
|
provenance: validated_row.fetch('provenance'),
|
|
tagSources: validated_row.fetch('tag_sources'),
|
|
metadataUrl: validated_row.fetch('metadata_url')
|
|
}]
|
|
}
|
|
|
|
expect(response).to have_http_status(:ok)
|
|
expect(Post.order(:id).last.video_ms).to eq(2_500)
|
|
end
|
|
|
|
it 'returns a formal skipped result for an existing post' do
|
|
existing = create(:post, url: 'https://example.com/existing')
|
|
sign_in_as(member)
|
|
|
|
post '/posts/import', params: {
|
|
rows: [{
|
|
sourceRow: 1,
|
|
url: existing.url,
|
|
attributes: { title: 'ignored' },
|
|
provenance: { url: 'manual', title: 'manual' },
|
|
tagSources: { automatic: '', manual: '' }
|
|
}]
|
|
}
|
|
|
|
expect(response).to have_http_status(:ok)
|
|
expect(json).to include('created' => 0, 'skipped' => 1, 'failed' => 0)
|
|
expect(json.fetch('rows').first).to include(
|
|
'status' => 'skipped',
|
|
'existing_post_id' => existing.id
|
|
)
|
|
end
|
|
end
|
|
end
|