canvas_sync 0.16.4 → 0.17.0.beta1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +49 -137
- data/app/models/canvas_sync/sync_batch.rb +5 -0
- data/db/migrate/20170915210836_create_canvas_sync_job_log.rb +12 -31
- data/db/migrate/20180725155729_add_job_id_to_canvas_sync_job_logs.rb +4 -13
- data/db/migrate/20190916154829_add_fork_count_to_canvas_sync_job_logs.rb +3 -11
- data/db/migrate/20201018210836_create_canvas_sync_sync_batches.rb +11 -0
- data/lib/canvas_sync.rb +35 -118
- data/lib/canvas_sync/job.rb +5 -5
- data/lib/canvas_sync/job_batches/batch.rb +399 -0
- data/lib/canvas_sync/job_batches/batch_aware_job.rb +62 -0
- data/lib/canvas_sync/job_batches/callback.rb +153 -0
- data/lib/canvas_sync/job_batches/chain_builder.rb +203 -0
- data/lib/canvas_sync/job_batches/context_hash.rb +147 -0
- data/lib/canvas_sync/job_batches/jobs/base_job.rb +7 -0
- data/lib/canvas_sync/job_batches/jobs/concurrent_batch_job.rb +18 -0
- data/lib/canvas_sync/job_batches/jobs/serial_batch_job.rb +73 -0
- data/lib/canvas_sync/job_batches/sidekiq.rb +91 -0
- data/lib/canvas_sync/job_batches/status.rb +63 -0
- data/lib/canvas_sync/jobs/begin_sync_chain_job.rb +34 -0
- data/lib/canvas_sync/jobs/report_checker.rb +3 -6
- data/lib/canvas_sync/jobs/report_processor_job.rb +2 -5
- data/lib/canvas_sync/jobs/report_starter.rb +27 -19
- data/lib/canvas_sync/jobs/sync_accounts_job.rb +3 -5
- data/lib/canvas_sync/jobs/sync_admins_job.rb +2 -4
- data/lib/canvas_sync/jobs/sync_assignment_groups_job.rb +2 -4
- data/lib/canvas_sync/jobs/sync_assignments_job.rb +2 -4
- data/lib/canvas_sync/jobs/sync_context_module_items_job.rb +2 -4
- data/lib/canvas_sync/jobs/sync_context_modules_job.rb +2 -4
- data/lib/canvas_sync/jobs/sync_provisioning_report_job.rb +4 -31
- data/lib/canvas_sync/jobs/sync_roles_job.rb +2 -5
- data/lib/canvas_sync/jobs/sync_simple_table_job.rb +11 -32
- data/lib/canvas_sync/jobs/sync_submissions_job.rb +2 -4
- data/lib/canvas_sync/jobs/sync_terms_job.rb +22 -7
- data/lib/canvas_sync/misc_helper.rb +15 -0
- data/lib/canvas_sync/version.rb +1 -1
- data/spec/canvas_sync/canvas_sync_spec.rb +126 -153
- data/spec/canvas_sync/jobs/job_spec.rb +9 -17
- data/spec/canvas_sync/jobs/report_checker_spec.rb +1 -3
- data/spec/canvas_sync/jobs/report_processor_job_spec.rb +0 -3
- data/spec/canvas_sync/jobs/report_starter_spec.rb +19 -28
- data/spec/canvas_sync/jobs/sync_admins_job_spec.rb +1 -4
- data/spec/canvas_sync/jobs/sync_assignment_groups_job_spec.rb +2 -1
- data/spec/canvas_sync/jobs/sync_assignments_job_spec.rb +3 -2
- data/spec/canvas_sync/jobs/sync_context_module_items_job_spec.rb +3 -2
- data/spec/canvas_sync/jobs/sync_context_modules_job_spec.rb +3 -2
- data/spec/canvas_sync/jobs/sync_provisioning_report_job_spec.rb +3 -35
- data/spec/canvas_sync/jobs/sync_roles_job_spec.rb +1 -4
- data/spec/canvas_sync/jobs/sync_simple_table_job_spec.rb +5 -12
- data/spec/canvas_sync/jobs/sync_submissions_job_spec.rb +2 -1
- data/spec/canvas_sync/jobs/sync_terms_job_spec.rb +1 -4
- data/spec/dummy/app/models/account.rb +3 -0
- data/spec/dummy/app/models/pseudonym.rb +14 -0
- data/spec/dummy/app/models/submission.rb +1 -0
- data/spec/dummy/app/models/user.rb +1 -0
- data/spec/dummy/config/environments/test.rb +2 -0
- data/spec/dummy/db/migrate/20201016181346_create_pseudonyms.rb +24 -0
- data/spec/dummy/db/schema.rb +24 -4
- data/spec/job_batching/batch_aware_job_spec.rb +100 -0
- data/spec/job_batching/batch_spec.rb +363 -0
- data/spec/job_batching/callback_spec.rb +38 -0
- data/spec/job_batching/flow_spec.rb +91 -0
- data/spec/job_batching/integration/integration.rb +57 -0
- data/spec/job_batching/integration/nested.rb +88 -0
- data/spec/job_batching/integration/simple.rb +47 -0
- data/spec/job_batching/integration/workflow.rb +134 -0
- data/spec/job_batching/integration_helper.rb +48 -0
- data/spec/job_batching/sidekiq_spec.rb +124 -0
- data/spec/job_batching/status_spec.rb +92 -0
- data/spec/job_batching/support/base_job.rb +14 -0
- data/spec/job_batching/support/sample_callback.rb +2 -0
- data/spec/spec_helper.rb +10 -0
- metadata +90 -8
- data/lib/canvas_sync/job_chain.rb +0 -57
- data/lib/canvas_sync/jobs/fork_gather.rb +0 -59
- data/spec/canvas_sync/jobs/fork_gather_spec.rb +0 -73
| @@ -0,0 +1,14 @@ | |
| 1 | 
            +
            # #
         | 
| 2 | 
            +
            # AUTO GENERATED MODEL
         | 
| 3 | 
            +
            # This model was auto generated by the CanvasSync Gem.
         | 
| 4 | 
            +
            # You can customize it as needed, but make sure you test
         | 
| 5 | 
            +
            # any changes you make to the auto generated methods.
         | 
| 6 | 
            +
            #
         | 
| 7 | 
            +
             | 
| 8 | 
            +
             | 
| 9 | 
            +
            class Pseudonym < ApplicationRecord
         | 
| 10 | 
            +
              include CanvasSync::Record
         | 
| 11 | 
            +
             | 
| 12 | 
            +
              validates :canvas_id, uniqueness: true, presence: true
         | 
| 13 | 
            +
              belongs_to :user, primary_key: :canvas_id, foreign_key: :canvas_user_id
         | 
| 14 | 
            +
            end
         | 
| @@ -11,6 +11,7 @@ class User < ApplicationRecord | |
| 11 11 | 
             
              include CanvasSync::Concerns::ApiSyncable
         | 
| 12 12 |  | 
| 13 13 | 
             
              validates :canvas_id, uniqueness: true, presence: true
         | 
| 14 | 
            +
              has_many :pseudonyms, primary_key: :canvas_id, foreign_key: :canvas_user_id
         | 
| 14 15 | 
             
              has_many :enrollments, primary_key: :canvas_id, foreign_key: :canvas_user_id
         | 
| 15 16 | 
             
              has_many :admins, primary_key: :canvas_id, foreign_key: :canvas_user_id
         | 
| 16 17 | 
             
              has_many :admin_roles, through: :admins, source: :role
         | 
| @@ -37,6 +37,8 @@ Rails.application.configure do | |
| 37 37 | 
             
              # Print deprecation notices to the stderr.
         | 
| 38 38 | 
             
              config.active_support.deprecation = :stderr
         | 
| 39 39 |  | 
| 40 | 
            +
              config.active_job.queue_adapter = :test
         | 
| 41 | 
            +
             | 
| 40 42 | 
             
              # Raises error for missing translations
         | 
| 41 43 | 
             
              # config.action_view.raise_on_missing_translations = true
         | 
| 42 44 | 
             
            end
         | 
| @@ -0,0 +1,24 @@ | |
| 1 | 
            +
            # #
         | 
| 2 | 
            +
            # AUTO GENERATED MIGRATION
         | 
| 3 | 
            +
            # This migration was auto generated by the CanvasSync Gem.
         | 
| 4 | 
            +
            # You can add new columns to this table, but removing or
         | 
| 5 | 
            +
            # re-naming ones created here may break Canvas Syncing.
         | 
| 6 | 
            +
            #
         | 
| 7 | 
            +
             | 
| 8 | 
            +
             | 
| 9 | 
            +
            class CreatePseudonyms < ActiveRecord::Migration[5.1]
         | 
| 10 | 
            +
              def change
         | 
| 11 | 
            +
                create_table :pseudonyms do |t|
         | 
| 12 | 
            +
                  t.bigint :canvas_id, null: false
         | 
| 13 | 
            +
                  t.bigint :canvas_user_id
         | 
| 14 | 
            +
                  t.string :sis_id
         | 
| 15 | 
            +
                  t.string :unique_id
         | 
| 16 | 
            +
                  t.string :workflow_state
         | 
| 17 | 
            +
             | 
| 18 | 
            +
                  t.timestamps
         | 
| 19 | 
            +
                end
         | 
| 20 | 
            +
             | 
| 21 | 
            +
                add_index :pseudonyms, :canvas_id, unique: true
         | 
| 22 | 
            +
                add_index :pseudonyms, :canvas_user_id
         | 
| 23 | 
            +
              end
         | 
| 24 | 
            +
            end
         | 
    
        data/spec/dummy/db/schema.rb
    CHANGED
    
    | @@ -10,7 +10,7 @@ | |
| 10 10 | 
             
            #
         | 
| 11 11 | 
             
            # It's strongly recommended that you check this file into your version control system.
         | 
| 12 12 |  | 
| 13 | 
            -
            ActiveRecord::Schema.define(version:  | 
| 13 | 
            +
            ActiveRecord::Schema.define(version: 2020_10_18_210836) do
         | 
| 14 14 |  | 
| 15 15 | 
             
              # These are extensions that must be enabled in order to support this database
         | 
| 16 16 | 
             
              enable_extension "plpgsql"
         | 
| @@ -83,7 +83,7 @@ ActiveRecord::Schema.define(version: 2020_04_16_214248) do | |
| 83 83 | 
             
                t.index ["canvas_id"], name: "index_assignments_on_canvas_id", unique: true
         | 
| 84 84 | 
             
              end
         | 
| 85 85 |  | 
| 86 | 
            -
              create_table "canvas_sync_job_logs", force: :cascade do |t|
         | 
| 86 | 
            +
              create_table "canvas_sync_job_logs", id: :serial, force: :cascade do |t|
         | 
| 87 87 | 
             
                t.datetime "started_at"
         | 
| 88 88 | 
             
                t.datetime "completed_at"
         | 
| 89 89 | 
             
                t.string "exception"
         | 
| @@ -92,13 +92,21 @@ ActiveRecord::Schema.define(version: 2020_04_16_214248) do | |
| 92 92 | 
             
                t.string "status"
         | 
| 93 93 | 
             
                t.text "metadata"
         | 
| 94 94 | 
             
                t.text "job_arguments"
         | 
| 95 | 
            -
                t.datetime "created_at" | 
| 96 | 
            -
                t.datetime "updated_at" | 
| 95 | 
            +
                t.datetime "created_at"
         | 
| 96 | 
            +
                t.datetime "updated_at"
         | 
| 97 97 | 
             
                t.string "job_id"
         | 
| 98 98 | 
             
                t.integer "fork_count"
         | 
| 99 99 | 
             
                t.index ["job_id"], name: "index_canvas_sync_job_logs_on_job_id"
         | 
| 100 100 | 
             
              end
         | 
| 101 101 |  | 
| 102 | 
            +
              create_table "canvas_sync_sync_batches", id: :serial, force: :cascade do |t|
         | 
| 103 | 
            +
                t.datetime "started_at"
         | 
| 104 | 
            +
                t.datetime "completed_at"
         | 
| 105 | 
            +
                t.string "status"
         | 
| 106 | 
            +
                t.datetime "created_at"
         | 
| 107 | 
            +
                t.datetime "updated_at"
         | 
| 108 | 
            +
              end
         | 
| 109 | 
            +
             | 
| 102 110 | 
             
              create_table "context_module_items", force: :cascade do |t|
         | 
| 103 111 | 
             
                t.bigint "canvas_id"
         | 
| 104 112 | 
             
                t.bigint "canvas_context_module_id"
         | 
| @@ -191,6 +199,18 @@ ActiveRecord::Schema.define(version: 2020_04_16_214248) do | |
| 191 199 | 
             
                t.index ["canvas_id"], name: "index_groups_on_canvas_id", unique: true
         | 
| 192 200 | 
             
              end
         | 
| 193 201 |  | 
| 202 | 
            +
              create_table "pseudonyms", force: :cascade do |t|
         | 
| 203 | 
            +
                t.bigint "canvas_id", null: false
         | 
| 204 | 
            +
                t.bigint "canvas_user_id"
         | 
| 205 | 
            +
                t.string "sis_id"
         | 
| 206 | 
            +
                t.string "unique_id"
         | 
| 207 | 
            +
                t.string "workflow_state"
         | 
| 208 | 
            +
                t.datetime "created_at", null: false
         | 
| 209 | 
            +
                t.datetime "updated_at", null: false
         | 
| 210 | 
            +
                t.index ["canvas_id"], name: "index_pseudonyms_on_canvas_id", unique: true
         | 
| 211 | 
            +
                t.index ["canvas_user_id"], name: "index_pseudonyms_on_canvas_user_id"
         | 
| 212 | 
            +
              end
         | 
| 213 | 
            +
             | 
| 194 214 | 
             
              create_table "roles", force: :cascade do |t|
         | 
| 195 215 | 
             
                t.integer "canvas_id", null: false
         | 
| 196 216 | 
             
                t.string "label"
         | 
| @@ -0,0 +1,100 @@ | |
| 1 | 
            +
            require 'spec_helper'
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            RSpec.describe CanvasSync::JobBatches::BatchAwareJob do
         | 
| 4 | 
            +
              include ActiveJob::TestHelper
         | 
| 5 | 
            +
             | 
| 6 | 
            +
              after do
         | 
| 7 | 
            +
                clear_enqueued_jobs
         | 
| 8 | 
            +
                clear_performed_jobs
         | 
| 9 | 
            +
              end
         | 
| 10 | 
            +
             | 
| 11 | 
            +
              context "When Performing" do
         | 
| 12 | 
            +
                context 'when without batch' do
         | 
| 13 | 
            +
                  it 'just yields' do
         | 
| 14 | 
            +
                    expect(CanvasSync::JobBatches::Batch).not_to receive(:process_successful_job)
         | 
| 15 | 
            +
                    expect(CanvasSync::JobBatches::Batch).not_to receive(:process_failed_job)
         | 
| 16 | 
            +
                    expect_any_instance_of(BatchTestJobBase).to receive(:perform)
         | 
| 17 | 
            +
             | 
| 18 | 
            +
                    BatchTestJobBase.perform_now
         | 
| 19 | 
            +
                  end
         | 
| 20 | 
            +
                end
         | 
| 21 | 
            +
             | 
| 22 | 
            +
                context 'when in batch' do
         | 
| 23 | 
            +
                  let(:bid) { 'SAMPLEBID' }
         | 
| 24 | 
            +
             | 
| 25 | 
            +
                  context 'when successful' do
         | 
| 26 | 
            +
                    it 'yields' do
         | 
| 27 | 
            +
                      expect_any_instance_of(BatchTestJobBase).to receive(:perform)
         | 
| 28 | 
            +
                      BatchTestJobBase.perform_now
         | 
| 29 | 
            +
                    end
         | 
| 30 | 
            +
             | 
| 31 | 
            +
                    it 'calls process_successful_job' do
         | 
| 32 | 
            +
                      job = BatchTestJobBase.new
         | 
| 33 | 
            +
                      job.instance_variable_set(:@bid, bid)
         | 
| 34 | 
            +
                      expect(CanvasSync::JobBatches::Batch).to receive(:process_successful_job).with(bid, job.job_id)
         | 
| 35 | 
            +
                      job.perform_now
         | 
| 36 | 
            +
                    end
         | 
| 37 | 
            +
                  end
         | 
| 38 | 
            +
             | 
| 39 | 
            +
                  context 'when failed' do
         | 
| 40 | 
            +
                    it 'calls process_failed_job and reraises exception' do
         | 
| 41 | 
            +
                      reraised = false
         | 
| 42 | 
            +
                      job = FailingBatchTestJobBase.new
         | 
| 43 | 
            +
                      job.instance_variable_set(:@bid, bid)
         | 
| 44 | 
            +
                      expect(CanvasSync::JobBatches::Batch).to receive(:process_failed_job)
         | 
| 45 | 
            +
                      begin
         | 
| 46 | 
            +
                        job.perform_now
         | 
| 47 | 
            +
                      rescue
         | 
| 48 | 
            +
                        reraised = true
         | 
| 49 | 
            +
                      end
         | 
| 50 | 
            +
                      expect(reraised).to be_truthy
         | 
| 51 | 
            +
                    end
         | 
| 52 | 
            +
                  end
         | 
| 53 | 
            +
                end
         | 
| 54 | 
            +
              end
         | 
| 55 | 
            +
             | 
| 56 | 
            +
              context "When Enqueueing" do
         | 
| 57 | 
            +
                context 'when without batch' do
         | 
| 58 | 
            +
                  it 'just yields' do
         | 
| 59 | 
            +
                    expect(CanvasSync::JobBatches::Batch).not_to receive(:increment_job_queue)
         | 
| 60 | 
            +
                    BatchTestJobBase.perform_later
         | 
| 61 | 
            +
                    expect(BatchTestJobBase).to have_been_enqueued
         | 
| 62 | 
            +
                  end
         | 
| 63 | 
            +
                end
         | 
| 64 | 
            +
             | 
| 65 | 
            +
                context 'when in batch' do
         | 
| 66 | 
            +
                  let(:bid) { 'SAMPLEBID' }
         | 
| 67 | 
            +
             | 
| 68 | 
            +
                  before do
         | 
| 69 | 
            +
                    Thread.current[:batch] = CanvasSync::JobBatches::Batch.new(bid)
         | 
| 70 | 
            +
                    Thread.current[:batch].instance_variable_set(:@open, true)
         | 
| 71 | 
            +
                  end
         | 
| 72 | 
            +
             | 
| 73 | 
            +
                  it 'yields' do
         | 
| 74 | 
            +
                    expect {
         | 
| 75 | 
            +
                      BatchTestJobBase.perform_later
         | 
| 76 | 
            +
                    }.to enqueue_job(BatchTestJobBase)
         | 
| 77 | 
            +
                  end
         | 
| 78 | 
            +
             | 
| 79 | 
            +
                  it 'assigns bid to job metadata' do
         | 
| 80 | 
            +
                    job = BatchTestJobBase.perform_later
         | 
| 81 | 
            +
                    expect(job.bid).to eq bid
         | 
| 82 | 
            +
                    expect(job.serialize['batch_id']).to eq bid
         | 
| 83 | 
            +
                  end
         | 
| 84 | 
            +
                end
         | 
| 85 | 
            +
              end
         | 
| 86 | 
            +
             | 
| 87 | 
            +
              context 'worker' do
         | 
| 88 | 
            +
                it 'defines method bid' do
         | 
| 89 | 
            +
                  expect(ActiveJob::Base.instance_methods).to include(:bid)
         | 
| 90 | 
            +
                end
         | 
| 91 | 
            +
             | 
| 92 | 
            +
                it 'defines method batch' do
         | 
| 93 | 
            +
                  expect(ActiveJob::Base.instance_methods).to include(:batch)
         | 
| 94 | 
            +
                end
         | 
| 95 | 
            +
             | 
| 96 | 
            +
                it 'defines method valid_within_batch?' do
         | 
| 97 | 
            +
                  expect(ActiveJob::Base.instance_methods).to include(:valid_within_batch?)
         | 
| 98 | 
            +
                end
         | 
| 99 | 
            +
              end
         | 
| 100 | 
            +
            end
         | 
| @@ -0,0 +1,363 @@ | |
| 1 | 
            +
            require 'spec_helper'
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            class TestWorker < BatchTestJobBase
         | 
| 4 | 
            +
              def perform
         | 
| 5 | 
            +
              end
         | 
| 6 | 
            +
            end
         | 
| 7 | 
            +
             | 
| 8 | 
            +
            RSpec.describe CanvasSync::JobBatches::Batch do
         | 
| 9 | 
            +
              describe '#initialize' do
         | 
| 10 | 
            +
                subject { described_class }
         | 
| 11 | 
            +
             | 
| 12 | 
            +
                it 'creates bid when called without it' do
         | 
| 13 | 
            +
                  expect(subject.new.bid).not_to be_nil
         | 
| 14 | 
            +
                end
         | 
| 15 | 
            +
             | 
| 16 | 
            +
                it 'reuses bid when called with it' do
         | 
| 17 | 
            +
                  batch = subject.new('dayPO5KxuRXXxw')
         | 
| 18 | 
            +
                  expect(batch.bid).to eq('dayPO5KxuRXXxw')
         | 
| 19 | 
            +
                end
         | 
| 20 | 
            +
              end
         | 
| 21 | 
            +
             | 
| 22 | 
            +
              describe '#description' do
         | 
| 23 | 
            +
                let(:description) { 'custom description' }
         | 
| 24 | 
            +
                before do
         | 
| 25 | 
            +
                  subject.description = description
         | 
| 26 | 
            +
                  subject.jobs { }
         | 
| 27 | 
            +
                end
         | 
| 28 | 
            +
             | 
| 29 | 
            +
                it 'sets descriptions' do
         | 
| 30 | 
            +
                  expect(subject.description).to eq(description)
         | 
| 31 | 
            +
                end
         | 
| 32 | 
            +
             | 
| 33 | 
            +
                it 'persists description' do
         | 
| 34 | 
            +
                  expect(CanvasSync::JobBatches::Batch.redis { |r| r.hget("BID-#{subject.bid}", 'description') })
         | 
| 35 | 
            +
                    .to eq(description)
         | 
| 36 | 
            +
                end
         | 
| 37 | 
            +
              end
         | 
| 38 | 
            +
             | 
| 39 | 
            +
              describe '#callback_queue' do
         | 
| 40 | 
            +
                let(:callback_queue) { 'custom_queue' }
         | 
| 41 | 
            +
                before do
         | 
| 42 | 
            +
                  subject.callback_queue = callback_queue
         | 
| 43 | 
            +
                  subject.jobs { }
         | 
| 44 | 
            +
                end
         | 
| 45 | 
            +
             | 
| 46 | 
            +
                it 'sets callback_queue' do
         | 
| 47 | 
            +
                  expect(subject.callback_queue).to eq(callback_queue)
         | 
| 48 | 
            +
                end
         | 
| 49 | 
            +
             | 
| 50 | 
            +
                it 'persists callback_queue' do
         | 
| 51 | 
            +
                  expect(CanvasSync::JobBatches::Batch.redis { |r| r.hget("BID-#{subject.bid}", 'callback_queue') })
         | 
| 52 | 
            +
                    .to eq(callback_queue)
         | 
| 53 | 
            +
                end
         | 
| 54 | 
            +
              end
         | 
| 55 | 
            +
             | 
| 56 | 
            +
              describe '#jobs' do
         | 
| 57 | 
            +
                it 'throws error if no block given' do
         | 
| 58 | 
            +
                  expect { subject.jobs }.to raise_error CanvasSync::JobBatches::Batch::NoBlockGivenError
         | 
| 59 | 
            +
                end
         | 
| 60 | 
            +
             | 
| 61 | 
            +
                it 'increments to_process (when started)'
         | 
| 62 | 
            +
             | 
| 63 | 
            +
                it 'decrements to_process (when finished)'
         | 
| 64 | 
            +
                # it 'calls process_successful_job to wait for block to finish' do
         | 
| 65 | 
            +
                #   batch = CanvasSync::JobBatches::Batch.new
         | 
| 66 | 
            +
                #   expect(CanvasSync::JobBatches::Batch).to receive(:process_successful_job).with(batch.bid)
         | 
| 67 | 
            +
                #   batch.jobs {}
         | 
| 68 | 
            +
                # end
         | 
| 69 | 
            +
             | 
| 70 | 
            +
                it 'sets Thread.current bid' do
         | 
| 71 | 
            +
                  batch = CanvasSync::JobBatches::Batch.new
         | 
| 72 | 
            +
                  batch.jobs do
         | 
| 73 | 
            +
                    expect(Thread.current[:batch]).to eq(batch)
         | 
| 74 | 
            +
                  end
         | 
| 75 | 
            +
                end
         | 
| 76 | 
            +
              end
         | 
| 77 | 
            +
             | 
| 78 | 
            +
              describe '#invalidate_all' do
         | 
| 79 | 
            +
                class InvalidatableJob < BatchTestJobBase
         | 
| 80 | 
            +
                  def perform
         | 
| 81 | 
            +
                    return unless valid_within_batch?
         | 
| 82 | 
            +
                    was_performed
         | 
| 83 | 
            +
                  end
         | 
| 84 | 
            +
             | 
| 85 | 
            +
                  def was_performed; end
         | 
| 86 | 
            +
                end
         | 
| 87 | 
            +
             | 
| 88 | 
            +
                it 'marks batch in redis as invalidated' do
         | 
| 89 | 
            +
                  batch = CanvasSync::JobBatches::Batch.new
         | 
| 90 | 
            +
                  job = InvalidatableJob.new
         | 
| 91 | 
            +
                  allow(job).to receive(:was_performed)
         | 
| 92 | 
            +
             | 
| 93 | 
            +
                  batch.invalidate_all
         | 
| 94 | 
            +
                  batch.jobs { job.perform }
         | 
| 95 | 
            +
             | 
| 96 | 
            +
                  expect(job).not_to have_received(:was_performed)
         | 
| 97 | 
            +
                end
         | 
| 98 | 
            +
             | 
| 99 | 
            +
                context 'nested batches' do
         | 
| 100 | 
            +
                  let(:batch_parent) { CanvasSync::JobBatches::Batch.new }
         | 
| 101 | 
            +
                  let(:batch_child_1) { CanvasSync::JobBatches::Batch.new }
         | 
| 102 | 
            +
                  let(:batch_child_2) { CanvasSync::JobBatches::Batch.new }
         | 
| 103 | 
            +
                  let(:job_of_parent) { InvalidatableJob.new }
         | 
| 104 | 
            +
                  let(:job_of_child_1) { InvalidatableJob.new }
         | 
| 105 | 
            +
                  let(:job_of_child_2) { InvalidatableJob.new }
         | 
| 106 | 
            +
             | 
| 107 | 
            +
                  before do
         | 
| 108 | 
            +
                    allow(job_of_parent).to receive(:was_performed)
         | 
| 109 | 
            +
                    allow(job_of_child_1).to receive(:was_performed)
         | 
| 110 | 
            +
                    allow(job_of_child_2).to receive(:was_performed)
         | 
| 111 | 
            +
                  end
         | 
| 112 | 
            +
             | 
| 113 | 
            +
                  it 'invalidates all job if parent batch is marked as invalidated' do
         | 
| 114 | 
            +
                    batch_parent.invalidate_all
         | 
| 115 | 
            +
                    batch_parent.jobs do
         | 
| 116 | 
            +
                      [
         | 
| 117 | 
            +
                        job_of_parent.perform,
         | 
| 118 | 
            +
                        batch_child_1.jobs do
         | 
| 119 | 
            +
                          [
         | 
| 120 | 
            +
                            job_of_child_1.perform,
         | 
| 121 | 
            +
                            batch_child_2.jobs { job_of_child_2.perform }
         | 
| 122 | 
            +
                          ]
         | 
| 123 | 
            +
                        end
         | 
| 124 | 
            +
                      ]
         | 
| 125 | 
            +
                    end
         | 
| 126 | 
            +
             | 
| 127 | 
            +
                    expect(job_of_parent).not_to have_received(:was_performed)
         | 
| 128 | 
            +
                    expect(job_of_child_1).not_to have_received(:was_performed)
         | 
| 129 | 
            +
                    expect(job_of_child_2).not_to have_received(:was_performed)
         | 
| 130 | 
            +
                  end
         | 
| 131 | 
            +
             | 
| 132 | 
            +
                  it 'invalidates only requested batch' do
         | 
| 133 | 
            +
                    batch_child_2.invalidate_all
         | 
| 134 | 
            +
                    batch_parent.jobs do
         | 
| 135 | 
            +
                      [
         | 
| 136 | 
            +
                        job_of_parent.perform,
         | 
| 137 | 
            +
                        batch_child_1.jobs do
         | 
| 138 | 
            +
                          [
         | 
| 139 | 
            +
                            job_of_child_1.perform,
         | 
| 140 | 
            +
                            batch_child_2.jobs { job_of_child_2.perform }
         | 
| 141 | 
            +
                          ]
         | 
| 142 | 
            +
                        end
         | 
| 143 | 
            +
                      ]
         | 
| 144 | 
            +
                    end
         | 
| 145 | 
            +
             | 
| 146 | 
            +
                    expect(job_of_parent).to have_received(:was_performed)
         | 
| 147 | 
            +
                    expect(job_of_child_1).to have_received(:was_performed)
         | 
| 148 | 
            +
                    expect(job_of_child_2).not_to have_received(:was_performed)
         | 
| 149 | 
            +
                  end
         | 
| 150 | 
            +
                end
         | 
| 151 | 
            +
              end
         | 
| 152 | 
            +
             | 
| 153 | 
            +
              describe '#process_failed_job' do
         | 
| 154 | 
            +
                let(:batch) { CanvasSync::JobBatches::Batch.new }
         | 
| 155 | 
            +
                let(:bid) { batch.bid }
         | 
| 156 | 
            +
                let(:jid) { 'ABCD' }
         | 
| 157 | 
            +
                before { CanvasSync::JobBatches::Batch.redis { |r| r.hset("BID-#{bid}", 'pending', 1) } }
         | 
| 158 | 
            +
             | 
| 159 | 
            +
                context 'complete' do
         | 
| 160 | 
            +
                  let(:failed_jid) { 'xxx' }
         | 
| 161 | 
            +
             | 
| 162 | 
            +
                  it 'tries to call complete callback' do
         | 
| 163 | 
            +
                    expect(CanvasSync::JobBatches::Batch).to receive(:enqueue_callbacks).with(:complete, bid)
         | 
| 164 | 
            +
                    CanvasSync::JobBatches::Batch.process_failed_job(bid, failed_jid)
         | 
| 165 | 
            +
                  end
         | 
| 166 | 
            +
             | 
| 167 | 
            +
                  it 'add job to failed list' do
         | 
| 168 | 
            +
                    CanvasSync::JobBatches::Batch.process_failed_job(bid, 'failed-job-id')
         | 
| 169 | 
            +
                    CanvasSync::JobBatches::Batch.process_failed_job(bid, failed_jid)
         | 
| 170 | 
            +
                    failed = CanvasSync::JobBatches::Batch.redis { |r| r.smembers("BID-#{bid}-failed") }
         | 
| 171 | 
            +
                    expect(failed).to eq(['xxx', 'failed-job-id'])
         | 
| 172 | 
            +
                  end
         | 
| 173 | 
            +
                end
         | 
| 174 | 
            +
              end
         | 
| 175 | 
            +
             | 
| 176 | 
            +
              describe '#process_successful_job' do
         | 
| 177 | 
            +
                let(:batch) { CanvasSync::JobBatches::Batch.new }
         | 
| 178 | 
            +
                let(:bid) { batch.bid }
         | 
| 179 | 
            +
                let(:jid) { 'ABCD' }
         | 
| 180 | 
            +
                before { CanvasSync::JobBatches::Batch.redis { |r| r.hset("BID-#{bid}", 'pending', 1) } }
         | 
| 181 | 
            +
             | 
| 182 | 
            +
                context 'complete' do
         | 
| 183 | 
            +
                  before { batch.on(:complete, Object) }
         | 
| 184 | 
            +
                  # before { batch.increment_job_queue(bid) }
         | 
| 185 | 
            +
                  before { batch.jobs do TestWorker.perform_async end }
         | 
| 186 | 
            +
                  before { CanvasSync::JobBatches::Batch.process_failed_job(bid, 'failed-job-id') }
         | 
| 187 | 
            +
             | 
| 188 | 
            +
                  it 'tries to call complete callback' do
         | 
| 189 | 
            +
                    expect(CanvasSync::JobBatches::Batch).to receive(:enqueue_callbacks).with(:complete, bid)
         | 
| 190 | 
            +
                    CanvasSync::JobBatches::Batch.process_successful_job(bid, 'failed-job-id')
         | 
| 191 | 
            +
                  end
         | 
| 192 | 
            +
                end
         | 
| 193 | 
            +
             | 
| 194 | 
            +
                context 'success' do
         | 
| 195 | 
            +
                  before { batch.on(:complete, Object) }
         | 
| 196 | 
            +
                  it 'tries to call complete callback' do
         | 
| 197 | 
            +
                    expect(CanvasSync::JobBatches::Batch).to receive(:enqueue_callbacks).with(:complete, bid).ordered
         | 
| 198 | 
            +
                    expect(CanvasSync::JobBatches::Batch).to receive(:enqueue_callbacks).with(:success, bid).ordered
         | 
| 199 | 
            +
                    CanvasSync::JobBatches::Batch.process_successful_job(bid, jid)
         | 
| 200 | 
            +
                  end
         | 
| 201 | 
            +
             | 
| 202 | 
            +
                  it 'cleanups redis key' do
         | 
| 203 | 
            +
                    CanvasSync::JobBatches::Batch.process_successful_job(bid, jid)
         | 
| 204 | 
            +
                    expect(CanvasSync::JobBatches::Batch.redis { |r| r.get("BID-#{bid}-pending") }.to_i).to eq(0)
         | 
| 205 | 
            +
                  end
         | 
| 206 | 
            +
                end
         | 
| 207 | 
            +
              end
         | 
| 208 | 
            +
             | 
| 209 | 
            +
              describe '#increment_job_queue' do
         | 
| 210 | 
            +
                let(:bid) { 'BID' }
         | 
| 211 | 
            +
                let(:batch) { CanvasSync::JobBatches::Batch.new }
         | 
| 212 | 
            +
             | 
| 213 | 
            +
                it 'increments pending' do
         | 
| 214 | 
            +
                  batch.jobs do TestWorker.perform_async end
         | 
| 215 | 
            +
                  pending = CanvasSync::JobBatches::Batch.redis { |r| r.hget("BID-#{batch.bid}", 'pending') }
         | 
| 216 | 
            +
                  expect(pending).to eq('1')
         | 
| 217 | 
            +
                end
         | 
| 218 | 
            +
             | 
| 219 | 
            +
                it 'increments total' do
         | 
| 220 | 
            +
                  batch.jobs do TestWorker.perform_async end
         | 
| 221 | 
            +
                  total = CanvasSync::JobBatches::Batch.redis { |r| r.hget("BID-#{batch.bid}", 'total') }
         | 
| 222 | 
            +
                  expect(total).to eq('1')
         | 
| 223 | 
            +
                end
         | 
| 224 | 
            +
              end
         | 
| 225 | 
            +
             | 
| 226 | 
            +
              describe '#enqueue_callbacks' do
         | 
| 227 | 
            +
                let(:callback) { double('callback') }
         | 
| 228 | 
            +
                let(:event) { :complete }
         | 
| 229 | 
            +
             | 
| 230 | 
            +
                context 'on :success' do
         | 
| 231 | 
            +
                  let(:event) { :success }
         | 
| 232 | 
            +
                  context 'when no callbacks are defined' do
         | 
| 233 | 
            +
                    it 'clears redis keys' do
         | 
| 234 | 
            +
                      batch = CanvasSync::JobBatches::Batch.new
         | 
| 235 | 
            +
                      expect(CanvasSync::JobBatches::Batch).to receive(:cleanup_redis).with(batch.bid)
         | 
| 236 | 
            +
                      CanvasSync::JobBatches::Batch.enqueue_callbacks(event, batch.bid)
         | 
| 237 | 
            +
                    end
         | 
| 238 | 
            +
                  end
         | 
| 239 | 
            +
                end
         | 
| 240 | 
            +
             | 
| 241 | 
            +
                context 'when already called' do
         | 
| 242 | 
            +
                  it 'returns and does not enqueue callbacks' do
         | 
| 243 | 
            +
                    batch = CanvasSync::JobBatches::Batch.new
         | 
| 244 | 
            +
                    batch.on(event, SampleCallback)
         | 
| 245 | 
            +
                    CanvasSync::JobBatches::Batch.redis { |r| r.hset("BID-#{batch.bid}", event, true) }
         | 
| 246 | 
            +
             | 
| 247 | 
            +
                    expect(batch).not_to receive(:push_callbacks)
         | 
| 248 | 
            +
                    CanvasSync::JobBatches::Batch.enqueue_callbacks(event, batch.bid)
         | 
| 249 | 
            +
                  end
         | 
| 250 | 
            +
                end
         | 
| 251 | 
            +
             | 
| 252 | 
            +
                context 'With ActiveJob Adapter' do
         | 
| 253 | 
            +
                  around(:all) do |block|
         | 
| 254 | 
            +
                    CanvasSync::JobBatches::Batch::Callback.send(:remove_const, :Worker)
         | 
| 255 | 
            +
                    CanvasSync::JobBatches::Batch::Callback.const_set(:Worker, CanvasSync::JobBatches::Batch::Callback::ActiveJobCallbackWorker)
         | 
| 256 | 
            +
                    block.run
         | 
| 257 | 
            +
                  end
         | 
| 258 | 
            +
             | 
| 259 | 
            +
                  context 'when not yet called' do
         | 
| 260 | 
            +
                    context 'when there is no callback' do
         | 
| 261 | 
            +
                      it 'it returns' do
         | 
| 262 | 
            +
                        batch = CanvasSync::JobBatches::Batch.new
         | 
| 263 | 
            +
             | 
| 264 | 
            +
                        expect(batch).not_to receive(:push_callbacks)
         | 
| 265 | 
            +
                        CanvasSync::JobBatches::Batch.enqueue_callbacks(event, batch.bid)
         | 
| 266 | 
            +
                      end
         | 
| 267 | 
            +
                    end
         | 
| 268 | 
            +
             | 
| 269 | 
            +
                    context 'when callback defined' do
         | 
| 270 | 
            +
                      let(:opts) { { 'a' => 'b' } }
         | 
| 271 | 
            +
             | 
| 272 | 
            +
                      it 'calls it passing options' do
         | 
| 273 | 
            +
                        batch = CanvasSync::JobBatches::Batch.new
         | 
| 274 | 
            +
                        batch.on(event, SampleCallback, opts)
         | 
| 275 | 
            +
             | 
| 276 | 
            +
                        CanvasSync::JobBatches::Batch.enqueue_callbacks(event, batch.bid)
         | 
| 277 | 
            +
             | 
| 278 | 
            +
                        expect(CanvasSync::JobBatches::Batch::Callback::Worker).to have_been_enqueued.with(
         | 
| 279 | 
            +
                          'SampleCallback', event.to_s, opts, batch.bid, nil
         | 
| 280 | 
            +
                        )
         | 
| 281 | 
            +
                      end
         | 
| 282 | 
            +
                    end
         | 
| 283 | 
            +
             | 
| 284 | 
            +
                    context 'when multiple callbacks are defined' do
         | 
| 285 | 
            +
                      let(:opts) { { 'a' => 'b' } }
         | 
| 286 | 
            +
                      let(:opts2) { { 'b' => 'a' } }
         | 
| 287 | 
            +
             | 
| 288 | 
            +
                      it 'enqueues each callback passing their options' do
         | 
| 289 | 
            +
                        batch = CanvasSync::JobBatches::Batch.new
         | 
| 290 | 
            +
                        batch.on(event, SampleCallback, opts)
         | 
| 291 | 
            +
                        batch.on(event, SampleCallback2, opts2)
         | 
| 292 | 
            +
             | 
| 293 | 
            +
                        CanvasSync::JobBatches::Batch.enqueue_callbacks(event, batch.bid)
         | 
| 294 | 
            +
                        expect(CanvasSync::JobBatches::Batch::Callback::Worker).to have_been_enqueued.with(
         | 
| 295 | 
            +
                          'SampleCallback2', event.to_s, opts2, batch.bid, nil
         | 
| 296 | 
            +
                        )
         | 
| 297 | 
            +
                        expect(CanvasSync::JobBatches::Batch::Callback::Worker).to have_been_enqueued.with(
         | 
| 298 | 
            +
                          'SampleCallback', event.to_s, opts, batch.bid, nil
         | 
| 299 | 
            +
                        )
         | 
| 300 | 
            +
                      end
         | 
| 301 | 
            +
                    end
         | 
| 302 | 
            +
                  end
         | 
| 303 | 
            +
                end
         | 
| 304 | 
            +
             | 
| 305 | 
            +
                context 'With Sidekiq Adapter' do
         | 
| 306 | 
            +
                  around(:all) do |block|
         | 
| 307 | 
            +
                    CanvasSync::JobBatches::Batch::Callback.send(:remove_const, :Worker)
         | 
| 308 | 
            +
                    CanvasSync::JobBatches::Batch::Callback.const_set(:Worker, CanvasSync::JobBatches::Batch::Callback::SidekiqCallbackWorker)
         | 
| 309 | 
            +
                    block.run
         | 
| 310 | 
            +
                  end
         | 
| 311 | 
            +
             | 
| 312 | 
            +
                  context 'when not yet called' do
         | 
| 313 | 
            +
                    context 'when there is no callback' do
         | 
| 314 | 
            +
                      it 'it returns' do
         | 
| 315 | 
            +
                        batch = CanvasSync::JobBatches::Batch.new
         | 
| 316 | 
            +
             | 
| 317 | 
            +
                        expect(batch).not_to receive(:push_callbacks)
         | 
| 318 | 
            +
                        CanvasSync::JobBatches::Batch.enqueue_callbacks(event, batch.bid)
         | 
| 319 | 
            +
                      end
         | 
| 320 | 
            +
                    end
         | 
| 321 | 
            +
             | 
| 322 | 
            +
                    context 'when callback defined' do
         | 
| 323 | 
            +
                      let(:opts) { { 'a' => 'b' } }
         | 
| 324 | 
            +
             | 
| 325 | 
            +
                      it 'calls it passing options' do
         | 
| 326 | 
            +
                        batch = CanvasSync::JobBatches::Batch.new
         | 
| 327 | 
            +
                        batch.on(event, SampleCallback, opts)
         | 
| 328 | 
            +
             | 
| 329 | 
            +
                        expect(Sidekiq::Client).to receive(:push_bulk).with(
         | 
| 330 | 
            +
                          'class' => Sidekiq::Batch::Callback::Worker,
         | 
| 331 | 
            +
                          'args' => [['SampleCallback', event.to_s, opts, batch.bid, nil]],
         | 
| 332 | 
            +
                          'queue' => 'default'
         | 
| 333 | 
            +
                        )
         | 
| 334 | 
            +
             | 
| 335 | 
            +
                        CanvasSync::JobBatches::Batch.enqueue_callbacks(event, batch.bid)
         | 
| 336 | 
            +
                      end
         | 
| 337 | 
            +
                    end
         | 
| 338 | 
            +
             | 
| 339 | 
            +
                    context 'when multiple callbacks are defined' do
         | 
| 340 | 
            +
                      let(:opts) { { 'a' => 'b' } }
         | 
| 341 | 
            +
                      let(:opts2) { { 'b' => 'a' } }
         | 
| 342 | 
            +
             | 
| 343 | 
            +
                      it 'enqueues each callback passing their options' do
         | 
| 344 | 
            +
                        batch = CanvasSync::JobBatches::Batch.new
         | 
| 345 | 
            +
                        batch.on(event, SampleCallback, opts)
         | 
| 346 | 
            +
                        batch.on(event, SampleCallback2, opts2)
         | 
| 347 | 
            +
             | 
| 348 | 
            +
                        expect(Sidekiq::Client).to receive(:push_bulk).with(
         | 
| 349 | 
            +
                          'class' => Sidekiq::Batch::Callback::Worker,
         | 
| 350 | 
            +
                          'args' => [
         | 
| 351 | 
            +
                            ['SampleCallback2', event.to_s, opts2, batch.bid, nil],
         | 
| 352 | 
            +
                            ['SampleCallback', event.to_s, opts, batch.bid, nil]
         | 
| 353 | 
            +
                          ],
         | 
| 354 | 
            +
                          'queue' => 'default'
         | 
| 355 | 
            +
                        )
         | 
| 356 | 
            +
             | 
| 357 | 
            +
                        CanvasSync::JobBatches::Batch.enqueue_callbacks(event, batch.bid)
         | 
| 358 | 
            +
                      end
         | 
| 359 | 
            +
                    end
         | 
| 360 | 
            +
                  end
         | 
| 361 | 
            +
                end
         | 
| 362 | 
            +
              end
         | 
| 363 | 
            +
            end
         |