このコミットが含まれているのは:
2026-07-16 20:05:09 +09:00
コミット 90d8d3ff08
27個のファイルの変更725行の追加257行の削除
+21
ファイルの表示
@@ -336,5 +336,26 @@ RSpec.describe Post, type: :model do
expect(post).to be_valid
end
it 'rejects invalid calendar dates and invalid hours' do
invalid_dates = [
'2024-02-31T12:00',
'2023-02-29T12:00',
'2024-02-29T24:00'
]
invalid_dates.each do |value|
post = described_class.new(
title: 'title',
url: 'https://example.com/post',
original_created_from: value
)
expect(post).to be_invalid
expect(post.errors[:original_created_from]).to eq(
[described_class::ORIGINAL_CREATED_INVALID_MESSAGE]
)
end
end
end
end
+44
ファイルの表示
@@ -115,6 +115,50 @@ RSpec.describe 'Post imports API', type: :request do
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)
+2 -2
ファイルの表示
@@ -68,7 +68,7 @@ RSpec.describe PostImportPreviewer do
allow(PostMetadataFetcher).to receive(:fetch).and_return(
title: 'metadata title',
thumbnail_base: 'https://example.com/thumb.jpg',
duration: 2_000,
duration: '2',
tags: 'known-tag'
)
@@ -79,7 +79,7 @@ RSpec.describe PostImportPreviewer do
expect(result.fetch(:attributes)).to include(
'title' => 'metadata title',
'thumbnail_base' => 'https://example.com/thumb.jpg',
'duration' => 2_000,
'duration' => '2',
'tags' => 'known-tag'
)
expect(result.fetch(:field_warnings)).not_to have_key('tags')
+2 -2
ファイルの表示
@@ -6,7 +6,7 @@ RSpec.describe PostImportRowNormaliser do
sourceRow: '1',
url: 'https://example.com/post',
metadataUrl: 'https://example.com/post',
attributes: { title: 'title', duration: 1_000 },
attributes: { title: 'title', duration: '1' },
provenance: { url: 'manual', title: 'automatic' },
tagSources: { automatic: 'tag', manual: '' }
}.deep_merge(overrides)
@@ -21,7 +21,7 @@ RSpec.describe PostImportRowNormaliser do
'source_row' => 1,
'url' => 'https://example.com/post',
'metadata_url' => 'https://example.com/post',
'attributes' => { 'title' => 'title', 'duration' => 1_000 },
'attributes' => { 'title' => 'title', 'duration' => '1' },
'provenance' => { 'url' => 'manual', 'title' => 'automatic' },
'tag_sources' => { 'automatic' => 'tag', 'manual' => '' }
}
+38
ファイルの表示
@@ -68,4 +68,42 @@ RSpec.describe PostMetadataFetcher do
expect(result.fetch(:original_created_from)).to be_nil
expect(result.fetch(:original_created_before)).to be_nil
end
it 'returns nil dates for invalid calendar dates and invalid hour values' do
invalid_day = fetch_with_published_time('2024-02-31T12:00Z')
invalid_leap = fetch_with_published_time('2023-02-29T12:00Z')
invalid_hour = fetch_with_published_time('2024-02-29T24:00Z')
[invalid_day, invalid_leap, invalid_hour].each do |result|
expect(result.fetch(:original_created_from)).to be_nil
expect(result.fetch(:original_created_before)).to be_nil
end
end
it 'accepts a valid leap-day timestamp at minute precision' do
result = fetch_with_published_time('2024-02-29T12:34Z')
expect(result.fetch(:original_created_from)).to eq('2024-02-29T12:34:00Z')
expect(result.fetch(:original_created_before)).to eq('2024-02-29T12:35:00Z')
end
it 'serialises metadata duration as the same seconds string contract used by forms' do
html = <<~HTML
<html><head>
<meta property="article:published_time" content="2024-02-03T12:34:56.123+02:30">
<meta property="og:video:duration" content="2.5">
</head></html>
HTML
uri = URI.parse('https://example.com/video')
allow(Preview::UrlSafety).to receive(:validate)
.with('https://example.com/video')
.and_return([uri, ['8.8.8.8']])
allow(Preview::HttpFetcher).to receive(:fetch).and_return(Response.new(html))
allow(Preview::HtmlMetadataExtractor).to receive(:extract)
.and_return(title: 'title', image_url: nil)
result = described_class.fetch('https://example.com/video')
expect(result.fetch(:duration)).to eq('2.5')
end
end