require 'rails_helper' RSpec.describe 'Post history privacy', type: :request do let(:origin) { Time.utc(2020, 1, 1) } let!(:post_record) do create(:post, title: 'current', created_at: origin, updated_at: origin) end let!(:other_post) do create(:post, created_at: origin, updated_at: origin + 3.days + 1.hour) end let!(:tag) { create(:tag, primary_name: 'private-history-tag') } let!(:external) { create(:external_tag, name: 'external-history-tag') } before do ['create', 'discard'].each_with_index do |event, index| TagVersion.create!(tag:, version_no: index + 1, event_type: event, name: 'private-history-tag', category: 'general', aliases: '', parent_tag_ids: '', names: { ja: { name: 'private-history-tag', script_code: 'Jpan', auto_generated: false, aliases: [] } }, created_at: origin + index.hours) end NicoTagVersion.create!(external_tag: external, version_no: 1, event_type: 'create', name: 'nico:external-history-tag', linked_tags: 'private-history-tag', created_at: origin) ['create', 'discard', 'restore', 'update'].each_with_index do |event, index| snapshots = if index < 2 [{ tag_id: tag.id, version_no: 1, name: 'private-history-tag', sections: [] }] elsif index == 3 [{ external_tag_id: external.id }] else [] end PostVersion.create!(post: post_record, version_no: index + 1, event_type: event, title: "version #{ index + 1 }", url: post_record.url, tags: '', tags_json: snapshots, parent_post_ids: '', created_at: origin + (index + 1).days) end end ['anonymous', 'member', 'admin'].each do |role| context "as #{ role }" do before do sign_in_as(create(:user, role.to_sym)) unless role == 'anonymous' end it 'uses only authorised history timestamps for display' do get '/posts' expect(response).to have_http_status(:ok) row = response.parsed_body.fetch('posts').find { _1.fetch('id') == post_record.id } expected = origin + (role == 'admin' ? 4 : 3).days expect(Time.zone.parse(row.fetch('updated_at'))).to eq(expected) end it 'uses the same visibility for date bounds and count' do get '/posts', params: { updated_from: (origin + 4.days).iso8601, updated_to: (origin + 4.days).iso8601 } expect(response).to have_http_status(:ok) ids = response.parsed_body.fetch('posts').pluck('id') expect(ids).to eq(role == 'admin' ? [post_record.id] : []) expect(response.parsed_body.fetch('count')).to eq(ids.length) end it 'sorts using authorised timestamps in both directions' do ['asc', 'desc'].each do |direction| get '/posts', params: { order: "updated_at:#{ direction }" } expect(response).to have_http_status(:ok) expected = role == 'admin' ? [other_post.id, post_record.id] : [post_record.id, other_post.id] expected.reverse! if direction == 'desc' expect(response.parsed_body.fetch('posts').pluck('id')).to eq(expected) end end it 'counts only visible versions and masks a private previous version' do get '/posts/versions', params: { post: post_record.id } expect(response).to have_http_status(:ok) body = response.parsed_body expected = role == 'admin' ? [4, 3, 2, 1] : [3] expect(body.fetch('count')).to eq(expected.length) expect(body.fetch('versions').pluck('version_no')).to eq(expected) restored = body.fetch('versions').find { _1.fetch('version_no') == 3 } expect(restored.fetch('title').fetch('prev')).to eq(role == 'admin' ? 'version 2' : nil) end it 'uses only an authorised previous version for tag filtering' do get '/posts/versions', params: { post: post_record.id, tag: tag.id } expect(response).to have_http_status(:ok) expected = role == 'admin' ? [3, 2, 1] : [] expect(response.parsed_body.fetch('versions').pluck('version_no')).to eq(expected) expect(response.parsed_body.fetch('count')).to eq(expected.length) end end end end