ファイル
2026-10-31 14:25:31 +09:00

103 行
4.7 KiB
Ruby

require 'rails_helper'
require 'tmpdir'
RSpec.describe 'Counts native GET acknowledgement', type: :request do
include ActiveSupport::Testing::TimeHelpers
around do |example|
Dir.mktmpdir('counts-download-request') do |path|
@counts_download_dir = Pathname.new(path)
example.run
end
end
before do
allow(CountsExportDownload).to receive(:directory).and_return(@counts_download_dir)
end
def ready_job(format)
CountsExportJobs.pulse
post '/counts/export/jobs', params: { resource: 'posts', definition: 'history',
mode: 'details', columns: 'resource_id', locale: 'ja', format: }
expect(response).to have_http_status(:accepted)
job = response.parsed_body.fetch('job')
signature = CountsExportJobs.verifier.verified(job, purpose: 'counts_export_job')
owner = signature.fetch('owner')
exporter = instance_double(CountsExporter, resource: 'posts', format:, body: ['payload'])
file = HistoryAccess.set(user: nil) { CountsExportDownload.prepare(exporter, owner) }
state = CountsExportJobs.read(signature.fetch('id'))
state.merge!('status' => 'ready', 'token' => file)
CountsExportJobs.write(state)
post "/counts/export/jobs/#{ job }/download"
expect(response).to have_http_status(:ok)
[job, file, response.parsed_body.fetch('download_attempt')]
end
['csv', 'json'].each do |format|
it "acknowledges only an authorised #{ format } GET body, not HEAD or acceptance" do
job, file, attempt = ready_job(format)
get "/counts/export/jobs/#{ job }"
expect(response.parsed_body['download_status']).to eq('pending')
head "/counts/export/files/#{ file }", params: { job:, attempt: }
expect(response).to have_http_status(:method_not_allowed)
get "/counts/export/jobs/#{ job }"
expect(response.parsed_body['download_status']).to eq('pending')
get "/counts/export/files/#{ file }", params: { job:, attempt: }
expect(response).to have_http_status(:ok)
expect(response.headers['Content-Disposition']).to include("counts-posts.#{ format }")
expect(response.body).to eq('payload')
get "/counts/export/jobs/#{ job }"
expect(response.parsed_body['download_status']).to eq('started')
get "/counts/export/files/#{ file }", params: { job:, attempt: }
expect(response).to have_http_status(:not_found)
end
end
it 'rejects wrong attempts, cancellation and a newly discarded resource' do
job, file, attempt = ready_job('json')
get "/counts/export/files/#{ file }", params: { job:, attempt: 'wrong' }
expect(response).to have_http_status(:not_found)
delete "/counts/export/jobs/#{ job }"
expect(response).to have_http_status(:ok)
get "/counts/export/files/#{ file }", params: { job:, attempt: }
expect(response).to have_http_status(:not_found)
job, file, attempt = ready_job('json')
record = create(:post)
PostVersion.create!(post: record, version_no: 1, event_type: 'discard', title: 'private',
url: record.url, tags: '', tags_json: [], parent_post_ids: '', created_at: Time.utc(2024))
get "/counts/export/files/#{ file }", params: { job:, attempt: }
expect(response).to have_http_status(:not_found)
expect(response.body).not_to include('payload', 'private')
end
it 'does not let a second browser session inspect the job or download its file' do
job, file, attempt = ready_job('json')
other = ActionDispatch::Integration::Session.new(Rails.application)
other.get("/counts/export/jobs/#{ job }")
expect(other.response).to have_http_status(:not_found)
other.get("/counts/export/files/#{ file }", params: { job:, attempt: })
expect(other.response).to have_http_status(:not_found)
expect(other.response.body).not_to include('payload')
end
it 'does not acknowledge an expired attempt' do
job, file, attempt = ready_job('json')
travel_to(Time.current + 46.seconds) do
get "/counts/export/files/#{ file }", params: { job:, attempt: }
expect(response).to have_http_status(:not_found)
get "/counts/export/jobs/#{ job }"
expect(response.parsed_body['download_status']).to eq('timeout')
end
end
it 'records a missing file without accepting the GET as started' do
job, file, attempt = ready_job('json')
owner = CountsExportJobs.verifier.verified(job, purpose: 'counts_export_job').fetch('owner')
metadata = HistoryAccess.set(user: nil) { CountsExportDownload.find(file, owner) }
File.unlink(metadata.fetch('path'))
get "/counts/export/files/#{ file }", params: { job:, attempt: }
expect(response).to have_http_status(:not_found)
get "/counts/export/jobs/#{ job }"
expect(response.parsed_body['download_status']).to eq('unavailable')
end
end