# frozen_string_literal: true

RSpec.describe Jobs do
  describe "enqueue" do
    describe "run_later!" do
      before { Jobs.run_later! }

      it "enqueues a job in sidekiq" do
        Sidekiq::Testing.fake! do
          jobs = Jobs::ProcessPost.jobs

          jobs.clear
          Jobs.enqueue(:process_post, post_id: 1)
          expect(jobs.length).to eq(1)
          job = jobs.first

          expected = {
            "class" => "Jobs::ProcessPost",
            "args" => [{ "post_id" => 1, "current_site_id" => "default" }],
            "queue" => "default",
          }
          expect(job.slice("class", "args", "queue")).to eq(expected)
        end
      end

      it "enqueues the job after the current transaction has committed" do
        jobs = Jobs::ProcessPost.jobs
        expect(jobs.length).to eq(0)

        Jobs.enqueue(:process_post, post_id: 1)
        expect(jobs.length).to eq(1)

        ActiveRecord::Base.transaction do
          Jobs.enqueue(:process_post, post_id: 1)
          expect(jobs.length).to eq(1)
        end
        expect(jobs.length).to eq(2)

        # Failed transaction
        ActiveRecord::Base.transaction do
          Jobs.enqueue(:process_post, post_id: 1)
          raise ActiveRecord::Rollback
        end

        expect(jobs.length).to eq(2) # No change
      end

      it "does not pass current_site_id when 'all_sites' is present" do
        Sidekiq::Testing.fake! do
          jobs = Jobs::ProcessPost.jobs

          jobs.clear
          Jobs.enqueue(:process_post, post_id: 1, all_sites: true)

          expect(jobs.length).to eq(1)
          job = jobs.first

          expected = {
            "class" => "Jobs::ProcessPost",
            "args" => [{ "post_id" => 1 }],
            "queue" => "default",
          }
          expect(job.slice("class", "args", "queue")).to eq(expected)
        end
      end

      it "doesn't execute the job" do
        Sidekiq::Client.stubs(:enqueue)
        Jobs::ProcessPost.any_instance.expects(:perform).never
        Jobs.enqueue(:process_post, post_id: 1)
      end

      it "enqueues with the specified current_site_id" do
        Sidekiq::Testing.fake! do
          jobs = Jobs::ProcessPost.jobs

          jobs.clear
          Jobs.enqueue(:process_post, post_id: 1, current_site_id: "test_db")

          expect(jobs.length).to eq(1)
          job = jobs.first

          expected = {
            "class" => "Jobs::ProcessPost",
            "args" => [{ "post_id" => 1, "current_site_id" => "test_db" }],
            "queue" => "default",
          }
          expect(job.slice("class", "args", "queue")).to eq(expected)
        end
      end
    end

    describe "run_immediately!" do
      before { Jobs.run_immediately! }

      it "doesn't enqueue in sidekiq" do
        Sidekiq::Client.expects(:enqueue).with(Jobs::ProcessPost, {}).never
        Jobs.enqueue(:process_post, post_id: 1)
      end

      it "executes the job right away" do
        Jobs::ProcessPost
          .any_instance
          .expects(:perform_immediately)
          .with({ "post_id" => 1, "current_site_id" => "default" })

        Jobs.enqueue(:process_post, post_id: 1)
      end

      context "when current_site_id option is given and does not match the current connection" do
        before do
          Sidekiq::Client.stubs(:enqueue)
          Jobs::ProcessPost.any_instance.stubs(:execute).returns(true)
        end

        it "raises ArgumentError without executing the job or changing connections" do
          Jobs::ProcessPost.any_instance.expects(:execute).never
          RailsMultisite::ConnectionManagement.expects(:establish_connection).never

          expect {
            Jobs.enqueue(:process_post, post_id: 1, current_site_id: "test_db")
          }.to raise_error(ArgumentError)
        end
      end
    end
  end

  describe "cancel_scheduled_job" do
    class Jobs::SomeJob < ::Jobs::Base
      def execute(args)
      end
    end

    let(:scheduled_jobs) { Sidekiq::ScheduledSet.new }

    after { scheduled_jobs.clear }

    it "deletes the matching job" do
      Sidekiq::Testing.disable! do
        scheduled_jobs.clear
        expect(scheduled_jobs.size).to eq(0)

        Jobs.enqueue_in(1.year, :some_job, topic_id: 123)
        Jobs.enqueue_in(2.years, :some_job, topic_id: 456)
        Jobs.enqueue_in(3.years, :some_job, topic_id: 123, current_site_id: "foo")
        Jobs.enqueue_in(4.years, :some_job, topic_id: 123, current_site_id: "bar")

        expect(scheduled_jobs.size).to eq(4)

        Jobs.cancel_scheduled_job(:some_job, topic_id: 123)

        expect(scheduled_jobs.size).to eq(3)

        Jobs.cancel_scheduled_job(:some_job, topic_id: 123, all_sites: true)

        expect(scheduled_jobs.size).to eq(1)
      end
    end
  end

  describe "enqueue_at" do
    it "calls enqueue_in for you" do
      freeze_time

      expect_enqueued_with(job: :process_post, at: 3.hours.from_now) do
        Jobs.enqueue_at(3.hours.from_now, :process_post, {})
      end
    end

    it "handles datetimes that are in the past" do
      freeze_time

      expect_enqueued_with(job: :process_post, at: Time.zone.now) do
        Jobs.enqueue_at(3.hours.ago, :process_post, {})
      end
    end
  end
end
