# frozen_string_literal: true require 'rails_helper' RSpec.describe Points::RawData::Verifier do let(:user) { create(:user) } let(:verifier) { described_class.new } before do allow(PointsChannel).to receive(:broadcast_to) end describe '#verify_specific_archive' do let(:test_date) { 3.months.ago.beginning_of_month.utc } let!(:points) do create_list(:point, 5, user: user, timestamp: test_date.to_i, raw_data: { lon: 13.4, lat: 52.5 }) end let(:archive) do # Create archive archiver = Points::RawData::Archiver.new archiver.archive_specific_month(user.id, test_date.year, test_date.month) Points::RawDataArchive.last end it 'verifies a valid archive successfully' do expect(archive.verified_at).to be_nil verifier.verify_specific_archive(archive.id) archive.reload expect(archive.verified_at).to be_present end it 'detects missing file' do archive.file.purge archive.reload expect do verifier.verify_specific_archive(archive.id) end.not_to change { archive.reload.verified_at } end it 'detects point count mismatch' do # Tamper with point count archive.update_column(:point_count, 999) expect do verifier.verify_specific_archive(archive.id) end.not_to change { archive.reload.verified_at } end it 'detects checksum mismatch' do # Tamper with checksum archive.update_column(:point_ids_checksum, 'invalid') expect do verifier.verify_specific_archive(archive.id) end.not_to change { archive.reload.verified_at } end it 'still verifies successfully when points are deleted from database' do # Force archive creation first archive_id = archive.id # Then delete one point from database points.first.destroy # Verification should still succeed - deleted points are acceptable # (users should be able to delete their data without failing archive verification) expect do verifier.verify_specific_archive(archive_id) end.to change { archive.reload.verified_at }.from(nil) end it 'detects raw_data mismatch between archive and database' do # Force archive creation first archive_id = archive.id # Then modify raw_data in database after archiving points.first.update_column(:raw_data, { lon: 999.0, lat: 999.0 }) expect do verifier.verify_specific_archive(archive_id) end.not_to change { archive.reload.verified_at } end it 'verifies raw_data matches between archive and database' do # Ensure data hasn't changed expect(points.first.raw_data).to eq({ 'lon' => 13.4, 'lat' => 52.5 }) verifier.verify_specific_archive(archive.id) expect(archive.reload.verified_at).to be_present end end describe '#verify_month' do let(:test_date) { 3.months.ago.beginning_of_month.utc } before do # Create points create_list(:point, 5, user: user, timestamp: test_date.to_i, raw_data: { lon: 13.4, lat: 52.5 }) # Archive them archiver = Points::RawData::Archiver.new archiver.archive_specific_month(user.id, test_date.year, test_date.month) end it 'verifies all archives for a month' do expect(Points::RawDataArchive.where(verified_at: nil).count).to eq(1) verifier.verify_month(user.id, test_date.year, test_date.month) expect(Points::RawDataArchive.where(verified_at: nil).count).to eq(0) end end describe '#call' do let(:test_date) { 3.months.ago.beginning_of_month.utc } before do # Create points and archive create_list(:point, 5, user: user, timestamp: test_date.to_i, raw_data: { lon: 13.4, lat: 52.5 }) archiver = Points::RawData::Archiver.new archiver.archive_specific_month(user.id, test_date.year, test_date.month) end it 'verifies all unverified archives' do expect(Points::RawDataArchive.where(verified_at: nil).count).to eq(1) result = verifier.call expect(result[:verified]).to eq(1) expect(result[:failed]).to eq(0) expect(Points::RawDataArchive.where(verified_at: nil).count).to eq(0) end it 'reports failures' do # Tamper with archive Points::RawDataArchive.last.update_column(:point_count, 999) result = verifier.call expect(result[:verified]).to eq(0) expect(result[:failed]).to eq(1) end it 'skips already verified archives' do # Verify once verifier.call # Try to verify again with a new verifier instance new_verifier = Points::RawData::Verifier.new result = new_verifier.call expect(result[:verified]).to eq(0) expect(result[:failed]).to eq(0) end end end