# frozen_string_literal: true require 'rails_helper' RSpec.describe Tracks::DailyGenerationJob, type: :job do describe '#perform' do let!(:active_user) { create(:user, settings: { 'minutes_between_routes' => 60, 'meters_between_routes' => 500 }) } let!(:trial_user) { create(:user, :trial) } let!(:inactive_user) { create(:user, :inactive) } let!(:active_user_old_track) do create(:track, user: active_user, start_at: 2.days.ago, end_at: 2.days.ago + 1.hour) end let!(:active_user_new_points) do create_list(:point, 3, user: active_user, timestamp: 1.hour.ago.to_i) end let!(:trial_user_old_track) do create(:track, user: trial_user, start_at: 3.days.ago, end_at: 3.days.ago + 1.hour) end let!(:trial_user_new_points) do create_list(:point, 2, user: trial_user, timestamp: 30.minutes.ago.to_i) end before do active_user.update!(points_count: active_user.points.count) trial_user.update!(points_count: trial_user.points.count) allow(User).to receive(:active_or_trial) .and_return(User.where(id: [active_user.id, trial_user.id])) ActiveJob::Base.queue_adapter.enqueued_jobs.clear end it 'processes all active and trial users' do expect { described_class.perform_now }.to \ have_enqueued_job(Tracks::ParallelGeneratorJob).twice end it 'does not process inactive users' do # Clear points and tracks to make destruction possible Point.destroy_all Track.destroy_all # Remove active and trial users to isolate test active_user.destroy trial_user.destroy expect do described_class.perform_now end.not_to have_enqueued_job(Tracks::ParallelGeneratorJob) end it 'enqueues correct number of parallel generation jobs for users with new points' do expect { described_class.perform_now }.to \ have_enqueued_job(Tracks::ParallelGeneratorJob).exactly(2).times end it 'enqueues parallel generation job for active user with correct parameters' do expect { described_class.perform_now }.to \ have_enqueued_job(Tracks::ParallelGeneratorJob).with( active_user.id, hash_including(mode: 'daily') ) end it 'enqueues parallel generation job for trial user' do expect { described_class.perform_now }.to \ have_enqueued_job(Tracks::ParallelGeneratorJob).with( trial_user.id, hash_including(mode: 'daily') ) end it 'does not enqueue jobs for users without new points' do Point.destroy_all expect { described_class.perform_now }.not_to \ have_enqueued_job(Tracks::ParallelGeneratorJob) end context 'when processing fails' do before do allow_any_instance_of(User).to receive(:tracks).and_raise(StandardError, 'Database error') allow(ExceptionReporter).to receive(:call) active_user.update!(points_count: 5) trial_user.update!(points_count: 3) end it 'does not raise errors when processing fails' do expect { described_class.perform_now }.not_to raise_error end it 'reports exceptions when processing fails' do described_class.perform_now expect(ExceptionReporter).to have_received(:call).at_least(:once) end end context 'when user has no points' do let!(:empty_user) { create(:user) } it 'skips users with no points' do expect { described_class.perform_now }.not_to \ have_enqueued_job(Tracks::ParallelGeneratorJob).with(empty_user.id, any_args) end end context 'when user has tracks but no new points' do let!(:user_with_current_tracks) { create(:user) } let!(:recent_points) { create_list(:point, 2, user: user_with_current_tracks, timestamp: 1.hour.ago.to_i) } let!(:recent_track) do create(:track, user: user_with_current_tracks, start_at: 1.hour.ago, end_at: 30.minutes.ago) end before do user_with_current_tracks.update!(points_count: user_with_current_tracks.points.count) end it 'skips users without new points since last track' do expect { described_class.perform_now }.not_to \ have_enqueued_job(Tracks::ParallelGeneratorJob).with(user_with_current_tracks.id, any_args) end end end end