canvas_sync 0.16.4 → 0.21.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (260) hide show
  1. checksums.yaml +5 -5
  2. data/README.md +235 -151
  3. data/app/controllers/{api → canvas_sync/api}/v1/health_check_controller.rb +1 -1
  4. data/app/controllers/canvas_sync/api/v1/live_events_controller.rb +122 -0
  5. data/app/models/canvas_sync/sync_batch.rb +5 -0
  6. data/config/initializers/apartment.rb +10 -1
  7. data/config/routes.rb +7 -0
  8. data/db/migrate/20170915210836_create_canvas_sync_job_log.rb +12 -31
  9. data/db/migrate/20180725155729_add_job_id_to_canvas_sync_job_logs.rb +4 -13
  10. data/db/migrate/20190916154829_add_fork_count_to_canvas_sync_job_logs.rb +3 -11
  11. data/db/migrate/20201018210836_create_canvas_sync_sync_batches.rb +11 -0
  12. data/db/migrate/20201030210836_add_full_sync_to_canvas_sync_sync_batch.rb +7 -0
  13. data/lib/canvas_sync/batch_processor.rb +41 -0
  14. data/lib/canvas_sync/concerns/ability_helper.rb +72 -0
  15. data/lib/canvas_sync/concerns/account/ancestry.rb +2 -0
  16. data/lib/canvas_sync/concerns/account/base.rb +15 -0
  17. data/lib/canvas_sync/concerns/api_syncable.rb +17 -10
  18. data/lib/canvas_sync/concerns/live_event_sync.rb +46 -0
  19. data/lib/canvas_sync/concerns/role/base.rb +57 -0
  20. data/lib/canvas_sync/concerns/sync_mapping.rb +120 -0
  21. data/lib/canvas_sync/engine.rb +80 -0
  22. data/lib/canvas_sync/generators/install_generator.rb +1 -0
  23. data/lib/canvas_sync/generators/install_live_events_generator.rb +0 -1
  24. data/lib/canvas_sync/generators/templates/migrations/create_content_migrations.rb +24 -0
  25. data/lib/canvas_sync/generators/templates/migrations/create_course_nicknames.rb +17 -0
  26. data/lib/canvas_sync/generators/templates/migrations/create_grading_period_groups.rb +18 -0
  27. data/lib/canvas_sync/generators/templates/migrations/create_grading_periods.rb +22 -0
  28. data/lib/canvas_sync/generators/templates/migrations/create_learning_outcome_results.rb +46 -0
  29. data/lib/canvas_sync/generators/templates/migrations/create_learning_outcomes.rb +30 -0
  30. data/lib/canvas_sync/generators/templates/migrations/create_rubric_assessments.rb +31 -0
  31. data/lib/canvas_sync/generators/templates/migrations/create_rubric_associations.rb +36 -0
  32. data/lib/canvas_sync/generators/templates/migrations/create_rubrics.rb +38 -0
  33. data/lib/canvas_sync/generators/templates/migrations/create_user_observers.rb +17 -0
  34. data/lib/canvas_sync/generators/templates/migrations/create_users.rb +0 -1
  35. data/lib/canvas_sync/generators/templates/models/account.rb +3 -0
  36. data/lib/canvas_sync/generators/templates/models/admin.rb +2 -0
  37. data/lib/canvas_sync/generators/templates/models/assignment.rb +3 -0
  38. data/lib/canvas_sync/generators/templates/models/assignment_group.rb +3 -0
  39. data/lib/canvas_sync/generators/templates/models/content_migration.rb +12 -0
  40. data/lib/canvas_sync/generators/templates/models/context_module.rb +3 -0
  41. data/lib/canvas_sync/generators/templates/models/context_module_item.rb +3 -0
  42. data/lib/canvas_sync/generators/templates/models/course.rb +11 -0
  43. data/lib/canvas_sync/generators/templates/models/course_nickname.rb +13 -0
  44. data/lib/canvas_sync/generators/templates/models/enrollment.rb +14 -0
  45. data/lib/canvas_sync/generators/templates/models/grading_period.rb +10 -0
  46. data/lib/canvas_sync/generators/templates/models/grading_period_group.rb +9 -0
  47. data/lib/canvas_sync/generators/templates/models/group.rb +2 -0
  48. data/lib/canvas_sync/generators/templates/models/group_membership.rb +2 -0
  49. data/lib/canvas_sync/generators/templates/models/learning_outcome.rb +24 -0
  50. data/lib/canvas_sync/generators/templates/models/learning_outcome_result.rb +48 -0
  51. data/lib/canvas_sync/generators/templates/models/pseudonym.rb +2 -0
  52. data/lib/canvas_sync/generators/templates/models/role.rb +2 -0
  53. data/lib/canvas_sync/generators/templates/models/rubric.rb +29 -0
  54. data/lib/canvas_sync/generators/templates/models/rubric_assessment.rb +17 -0
  55. data/lib/canvas_sync/generators/templates/models/rubric_association.rb +14 -0
  56. data/lib/canvas_sync/generators/templates/models/section.rb +9 -0
  57. data/lib/canvas_sync/generators/templates/models/submission.rb +3 -0
  58. data/lib/canvas_sync/generators/templates/models/term.rb +3 -0
  59. data/lib/canvas_sync/generators/templates/models/user.rb +11 -0
  60. data/lib/canvas_sync/generators/templates/models/user_observer.rb +13 -0
  61. data/lib/canvas_sync/generators/templates/services/live_events/assignment_event.rb +1 -1
  62. data/lib/canvas_sync/generators/templates/services/live_events/assignment_group_event.rb +1 -1
  63. data/lib/canvas_sync/generators/templates/services/live_events/course_event.rb +1 -3
  64. data/lib/canvas_sync/generators/templates/services/live_events/course_section_event.rb +1 -1
  65. data/lib/canvas_sync/generators/templates/services/live_events/enrollment_event.rb +1 -1
  66. data/lib/canvas_sync/generators/templates/services/live_events/grade_event.rb +1 -1
  67. data/lib/canvas_sync/generators/templates/services/live_events/module_event.rb +1 -1
  68. data/lib/canvas_sync/generators/templates/services/live_events/module_item_event.rb +1 -1
  69. data/lib/canvas_sync/generators/templates/services/live_events/submission_event.rb +1 -1
  70. data/lib/canvas_sync/generators/templates/services/live_events/syllabus_event.rb +1 -1
  71. data/lib/canvas_sync/generators/templates/services/live_events/user_event.rb +1 -3
  72. data/lib/canvas_sync/importers/bulk_importer.rb +138 -31
  73. data/lib/canvas_sync/job.rb +7 -5
  74. data/lib/canvas_sync/job_batches/active_job.rb +108 -0
  75. data/lib/canvas_sync/job_batches/batch.rb +543 -0
  76. data/lib/canvas_sync/job_batches/callback.rb +149 -0
  77. data/lib/canvas_sync/job_batches/chain_builder.rb +249 -0
  78. data/lib/canvas_sync/job_batches/context_hash.rb +159 -0
  79. data/lib/canvas_sync/job_batches/hier_batch_ids.lua +25 -0
  80. data/lib/canvas_sync/job_batches/jobs/base_job.rb +7 -0
  81. data/lib/canvas_sync/job_batches/jobs/concurrent_batch_job.rb +22 -0
  82. data/lib/canvas_sync/job_batches/jobs/managed_batch_job.rb +170 -0
  83. data/lib/canvas_sync/job_batches/jobs/serial_batch_job.rb +22 -0
  84. data/lib/canvas_sync/job_batches/pool.rb +245 -0
  85. data/lib/canvas_sync/job_batches/pool_refill.lua +47 -0
  86. data/lib/canvas_sync/job_batches/redis_model.rb +69 -0
  87. data/lib/canvas_sync/job_batches/redis_script.rb +163 -0
  88. data/lib/canvas_sync/job_batches/schedule_callback.lua +14 -0
  89. data/lib/canvas_sync/job_batches/sidekiq/web/batches_assets/css/styles.less +182 -0
  90. data/lib/canvas_sync/job_batches/sidekiq/web/batches_assets/js/batch_tree.js +108 -0
  91. data/lib/canvas_sync/job_batches/sidekiq/web/batches_assets/js/util.js +2 -0
  92. data/lib/canvas_sync/job_batches/sidekiq/web/helpers.rb +41 -0
  93. data/lib/canvas_sync/job_batches/sidekiq/web/views/_batch_tree.erb +6 -0
  94. data/lib/canvas_sync/job_batches/sidekiq/web/views/_batches_table.erb +44 -0
  95. data/lib/canvas_sync/job_batches/sidekiq/web/views/_common.erb +13 -0
  96. data/lib/canvas_sync/job_batches/sidekiq/web/views/_jobs_table.erb +21 -0
  97. data/lib/canvas_sync/job_batches/sidekiq/web/views/_pagination.erb +26 -0
  98. data/lib/canvas_sync/job_batches/sidekiq/web/views/batch.erb +81 -0
  99. data/lib/canvas_sync/job_batches/sidekiq/web/views/batches.erb +23 -0
  100. data/lib/canvas_sync/job_batches/sidekiq/web/views/pool.erb +137 -0
  101. data/lib/canvas_sync/job_batches/sidekiq/web/views/pools.erb +47 -0
  102. data/lib/canvas_sync/job_batches/sidekiq/web.rb +218 -0
  103. data/lib/canvas_sync/job_batches/sidekiq.rb +136 -0
  104. data/lib/canvas_sync/job_batches/status.rb +91 -0
  105. data/lib/canvas_sync/jobs/begin_sync_chain_job.rb +99 -0
  106. data/lib/canvas_sync/jobs/canvas_process_waiter.rb +41 -0
  107. data/lib/canvas_sync/jobs/report_checker.rb +70 -8
  108. data/lib/canvas_sync/jobs/report_processor_job.rb +4 -7
  109. data/lib/canvas_sync/jobs/report_starter.rb +34 -20
  110. data/lib/canvas_sync/jobs/sync_accounts_job.rb +3 -5
  111. data/lib/canvas_sync/jobs/sync_admins_job.rb +2 -4
  112. data/lib/canvas_sync/jobs/sync_assignment_groups_job.rb +2 -4
  113. data/lib/canvas_sync/jobs/sync_assignments_job.rb +2 -4
  114. data/lib/canvas_sync/jobs/sync_content_migrations_job.rb +20 -0
  115. data/lib/canvas_sync/jobs/sync_context_module_items_job.rb +2 -4
  116. data/lib/canvas_sync/jobs/sync_context_modules_job.rb +2 -4
  117. data/lib/canvas_sync/jobs/sync_provisioning_report_job.rb +16 -50
  118. data/lib/canvas_sync/jobs/sync_roles_job.rb +2 -5
  119. data/lib/canvas_sync/jobs/sync_rubric_assessments_job.rb +15 -0
  120. data/lib/canvas_sync/jobs/sync_rubric_associations_job.rb +15 -0
  121. data/lib/canvas_sync/jobs/sync_rubrics_job.rb +15 -0
  122. data/lib/canvas_sync/jobs/sync_simple_table_job.rb +11 -32
  123. data/lib/canvas_sync/jobs/sync_submissions_job.rb +6 -4
  124. data/lib/canvas_sync/jobs/sync_terms_job.rb +9 -8
  125. data/lib/canvas_sync/jobs/term_batches_job.rb +50 -0
  126. data/lib/canvas_sync/{generators/templates/services/live_events/base_event.rb → live_events/base_handler.rb} +6 -10
  127. data/lib/canvas_sync/live_events/process_event_job.rb +26 -0
  128. data/lib/canvas_sync/live_events.rb +38 -0
  129. data/lib/canvas_sync/misc_helper.rb +63 -0
  130. data/lib/canvas_sync/processors/assignment_groups_processor.rb +3 -8
  131. data/lib/canvas_sync/processors/assignments_processor.rb +3 -8
  132. data/lib/canvas_sync/processors/content_migrations_processor.rb +19 -0
  133. data/lib/canvas_sync/processors/context_module_items_processor.rb +3 -8
  134. data/lib/canvas_sync/processors/context_modules_processor.rb +3 -8
  135. data/lib/canvas_sync/processors/model_mappings.yml +420 -0
  136. data/lib/canvas_sync/processors/normal_processor.rb +3 -3
  137. data/lib/canvas_sync/processors/provisioning_report_processor.rb +42 -55
  138. data/lib/canvas_sync/processors/report_processor.rb +15 -9
  139. data/lib/canvas_sync/processors/rubric_assessments_processor.rb +19 -0
  140. data/lib/canvas_sync/processors/rubric_associations_processor.rb +19 -0
  141. data/lib/canvas_sync/processors/rubrics_processor.rb +19 -0
  142. data/lib/canvas_sync/processors/submissions_processor.rb +3 -8
  143. data/lib/canvas_sync/record.rb +103 -0
  144. data/lib/canvas_sync/version.rb +1 -1
  145. data/lib/canvas_sync.rb +124 -125
  146. data/spec/canvas_sync/canvas_sync_spec.rb +224 -155
  147. data/spec/canvas_sync/jobs/canvas_process_waiter_spec.rb +34 -0
  148. data/spec/canvas_sync/jobs/job_spec.rb +9 -17
  149. data/spec/canvas_sync/jobs/report_checker_spec.rb +1 -3
  150. data/spec/canvas_sync/jobs/report_processor_job_spec.rb +0 -3
  151. data/spec/canvas_sync/jobs/report_starter_spec.rb +19 -28
  152. data/spec/canvas_sync/jobs/sync_admins_job_spec.rb +1 -4
  153. data/spec/canvas_sync/jobs/sync_assignment_groups_job_spec.rb +2 -1
  154. data/spec/canvas_sync/jobs/sync_assignments_job_spec.rb +3 -2
  155. data/spec/canvas_sync/jobs/sync_content_migrations_job_spec.rb +30 -0
  156. data/spec/canvas_sync/jobs/sync_context_module_items_job_spec.rb +3 -2
  157. data/spec/canvas_sync/jobs/sync_context_modules_job_spec.rb +3 -2
  158. data/spec/canvas_sync/jobs/sync_provisioning_report_job_spec.rb +7 -41
  159. data/spec/canvas_sync/jobs/sync_roles_job_spec.rb +1 -4
  160. data/spec/canvas_sync/jobs/sync_simple_table_job_spec.rb +5 -12
  161. data/spec/canvas_sync/jobs/sync_submissions_job_spec.rb +8 -2
  162. data/spec/canvas_sync/jobs/sync_terms_job_spec.rb +1 -4
  163. data/spec/canvas_sync/live_events/live_event_sync_spec.rb +27 -0
  164. data/spec/canvas_sync/live_events/live_events_controller_spec.rb +54 -0
  165. data/spec/canvas_sync/live_events/process_event_job_spec.rb +38 -0
  166. data/spec/canvas_sync/misc_helper_spec.rb +58 -0
  167. data/spec/canvas_sync/models/assignment_spec.rb +1 -1
  168. data/spec/canvas_sync/processors/content_migrations_processor_spec.rb +13 -0
  169. data/spec/canvas_sync/processors/provisioning_report_processor_spec.rb +101 -1
  170. data/spec/canvas_sync/processors/rubric_assessments_spec.rb +16 -0
  171. data/spec/canvas_sync/processors/rubric_associations_spec.rb +16 -0
  172. data/spec/canvas_sync/processors/rubrics_processor_spec.rb +17 -0
  173. data/spec/dummy/app/models/account.rb +6 -0
  174. data/spec/dummy/app/models/admin.rb +2 -0
  175. data/spec/dummy/app/models/assignment.rb +3 -0
  176. data/spec/dummy/app/models/assignment_group.rb +3 -0
  177. data/spec/dummy/app/models/content_migration.rb +18 -0
  178. data/spec/dummy/app/models/context_module.rb +3 -0
  179. data/spec/dummy/app/models/context_module_item.rb +3 -0
  180. data/spec/dummy/app/models/course.rb +11 -0
  181. data/spec/dummy/app/models/course_nickname.rb +19 -0
  182. data/spec/dummy/app/models/enrollment.rb +14 -0
  183. data/spec/dummy/app/models/grading_period.rb +16 -0
  184. data/spec/dummy/app/models/grading_period_group.rb +15 -0
  185. data/spec/dummy/app/models/group.rb +2 -0
  186. data/spec/dummy/app/models/group_membership.rb +2 -0
  187. data/spec/dummy/app/models/learning_outcome.rb +30 -0
  188. data/spec/dummy/app/models/learning_outcome_result.rb +54 -0
  189. data/spec/dummy/app/models/pseudonym.rb +16 -0
  190. data/spec/dummy/app/models/role.rb +2 -0
  191. data/spec/dummy/app/models/rubric.rb +35 -0
  192. data/spec/dummy/app/models/rubric_assessment.rb +22 -0
  193. data/spec/dummy/app/models/rubric_association.rb +20 -0
  194. data/spec/dummy/app/models/section.rb +9 -0
  195. data/spec/dummy/app/models/submission.rb +4 -0
  196. data/spec/dummy/app/models/term.rb +3 -0
  197. data/spec/dummy/app/models/user.rb +11 -0
  198. data/spec/dummy/app/models/user_observer.rb +19 -0
  199. data/spec/dummy/app/services/live_events/assignment_event.rb +1 -1
  200. data/spec/dummy/app/services/live_events/course_event.rb +1 -3
  201. data/spec/dummy/app/services/live_events/course_section_event.rb +1 -1
  202. data/spec/dummy/app/services/live_events/enrollment_event.rb +1 -1
  203. data/spec/dummy/app/services/live_events/grade_event.rb +1 -1
  204. data/spec/dummy/app/services/live_events/module_event.rb +1 -1
  205. data/spec/dummy/app/services/live_events/module_item_event.rb +1 -1
  206. data/spec/dummy/app/services/live_events/submission_event.rb +1 -1
  207. data/spec/dummy/app/services/live_events/syllabus_event.rb +1 -1
  208. data/spec/dummy/app/services/live_events/user_event.rb +1 -3
  209. data/spec/dummy/config/environments/test.rb +2 -0
  210. data/spec/dummy/config/routes.rb +1 -0
  211. data/spec/dummy/db/migrate/20201016181346_create_pseudonyms.rb +24 -0
  212. data/spec/dummy/db/migrate/20210907233329_create_user_observers.rb +23 -0
  213. data/spec/dummy/db/migrate/20210907233330_create_grading_periods.rb +28 -0
  214. data/spec/dummy/db/migrate/20211001184920_create_grading_period_groups.rb +24 -0
  215. data/spec/dummy/db/migrate/20220308072643_create_content_migrations.rb +30 -0
  216. data/spec/dummy/db/migrate/20220712210559_create_learning_outcomes.rb +36 -0
  217. data/spec/dummy/db/migrate/{20190702203620_create_users.rb → 20220926221926_create_users.rb} +0 -1
  218. data/spec/dummy/db/migrate/20240408223326_create_course_nicknames.rb +23 -0
  219. data/spec/dummy/db/migrate/20240509105100_create_rubrics.rb +44 -0
  220. data/spec/dummy/db/migrate/20240510094100_create_rubric_associations.rb +42 -0
  221. data/spec/dummy/db/migrate/20240510101100_create_rubric_assessments.rb +37 -0
  222. data/spec/dummy/db/migrate/20240523101010_create_learning_outcome_results.rb +52 -0
  223. data/spec/dummy/db/schema.rb +244 -5
  224. data/spec/factories/user_factory.rb +0 -1
  225. data/spec/job_batching/active_job_spec.rb +107 -0
  226. data/spec/job_batching/batch_spec.rb +489 -0
  227. data/spec/job_batching/callback_spec.rb +38 -0
  228. data/spec/job_batching/context_hash_spec.rb +54 -0
  229. data/spec/job_batching/flow_spec.rb +82 -0
  230. data/spec/job_batching/integration/fail_then_succeed.rb +42 -0
  231. data/spec/job_batching/integration/integration.rb +57 -0
  232. data/spec/job_batching/integration/nested.rb +88 -0
  233. data/spec/job_batching/integration/simple.rb +47 -0
  234. data/spec/job_batching/integration/workflow.rb +134 -0
  235. data/spec/job_batching/integration_helper.rb +50 -0
  236. data/spec/job_batching/pool_spec.rb +161 -0
  237. data/spec/job_batching/sidekiq_spec.rb +125 -0
  238. data/spec/job_batching/status_spec.rb +76 -0
  239. data/spec/job_batching/support/base_job.rb +14 -0
  240. data/spec/job_batching/support/sample_callback.rb +2 -0
  241. data/spec/spec_helper.rb +17 -0
  242. data/spec/support/fixtures/reports/content_migrations.csv +3 -0
  243. data/spec/support/fixtures/reports/course_nicknames.csv +3 -0
  244. data/spec/support/fixtures/reports/grading_period_groups.csv +2 -0
  245. data/spec/support/fixtures/reports/grading_periods.csv +3 -0
  246. data/spec/support/fixtures/reports/learning_outcome_results.csv +3 -0
  247. data/spec/support/fixtures/reports/learning_outcomes.csv +3 -0
  248. data/spec/support/fixtures/reports/provisioning_csv_unzipped/courses.csv +3 -0
  249. data/spec/support/fixtures/reports/provisioning_csv_unzipped/users.csv +4 -0
  250. data/spec/support/fixtures/reports/rubric_assessments.csv +3 -0
  251. data/spec/support/fixtures/reports/rubric_associations.csv +3 -0
  252. data/spec/support/fixtures/reports/rubrics.csv +3 -0
  253. data/spec/support/fixtures/reports/user_observers.csv +3 -0
  254. data/spec/support/fixtures/reports/users.csv +3 -2
  255. data/spec/support/fixtures/reports/xlist.csv +1 -1
  256. metadata +329 -27
  257. data/app/controllers/api/v1/live_events_controller.rb +0 -18
  258. data/lib/canvas_sync/job_chain.rb +0 -57
  259. data/lib/canvas_sync/jobs/fork_gather.rb +0 -59
  260. data/spec/canvas_sync/jobs/fork_gather_spec.rb +0 -73
@@ -0,0 +1,489 @@
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[CanvasSync::JobBatches::CURRENT_BATCH_THREAD_KEY]).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 contain_exactly('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_failed_job(bid, 'failed-job-id')
191
+ end
192
+ end
193
+
194
+ context 'success' do
195
+ before { batch.on(:complete, Object) }
196
+
197
+ it 'tries to call complete callback' do
198
+ expect(CanvasSync::JobBatches::Batch).to receive(:enqueue_callbacks).with(:complete, bid).ordered
199
+ expect(CanvasSync::JobBatches::Batch).to receive(:enqueue_callbacks).with(:success, bid).ordered
200
+ CanvasSync::JobBatches::Batch.process_successful_job(bid, jid)
201
+ end
202
+
203
+ it 'tries to call success callback after a previous failure' do
204
+ expect(CanvasSync::JobBatches::Batch).to receive(:enqueue_callbacks).with(:complete, bid).ordered
205
+ CanvasSync::JobBatches::Batch.process_failed_job(bid, jid)
206
+
207
+ expect(CanvasSync::JobBatches::Batch).to receive(:enqueue_callbacks).with(:complete, bid).ordered
208
+ expect(CanvasSync::JobBatches::Batch).to receive(:enqueue_callbacks).with(:success, bid).ordered
209
+ CanvasSync::JobBatches::Batch.process_successful_job(bid, jid)
210
+ end
211
+
212
+ it 'triggers callbacks as expected' do
213
+ ActiveJob::Base.queue_adapter = :sidekiq
214
+ CanvasSync::JobBatches::Batch::Callback.worker_class = CanvasSync::JobBatches::Sidekiq::SidekiqCallbackWorker
215
+
216
+ callback_instance = double('SampleCallback')
217
+ expect(SampleCallback).to receive(:new).at_least(1).times.and_return(callback_instance)
218
+ expect(callback_instance).to receive(:on_complete)
219
+ expect(callback_instance).to receive(:on_success)
220
+
221
+ batch.on(:complete, SampleCallback)
222
+ batch.on(:success, SampleCallback)
223
+
224
+ Sidekiq::Testing.inline! do
225
+ CanvasSync::JobBatches::Batch.process_failed_job(bid, jid)
226
+ CanvasSync::JobBatches::Batch.process_successful_job(bid, jid)
227
+ end
228
+ end
229
+
230
+ it 'delays triggering callbacks if keep_open is set' do
231
+ ActiveJob::Base.queue_adapter = :sidekiq
232
+ CanvasSync::JobBatches::Batch::Callback.worker_class = CanvasSync::JobBatches::Sidekiq::SidekiqCallbackWorker
233
+
234
+ callback_instance = double('SampleCallback')
235
+ expect(SampleCallback).to receive(:new).at_least(1).times.and_return(callback_instance)
236
+ expect(callback_instance).not_to receive(:on_complete)
237
+ expect(callback_instance).not_to receive(:on_success)
238
+
239
+ batch.on(:complete, SampleCallback)
240
+ batch.on(:success, SampleCallback)
241
+ batch.keep_open!
242
+
243
+ Sidekiq::Testing.inline! do
244
+ CanvasSync::JobBatches::Batch.process_failed_job(bid, jid)
245
+ CanvasSync::JobBatches::Batch.process_successful_job(bid, jid)
246
+ end
247
+
248
+ RSpec::Mocks.space.proxy_for(callback_instance).reset
249
+
250
+ expect(callback_instance).to receive(:on_complete)
251
+ expect(callback_instance).to receive(:on_success)
252
+
253
+ Sidekiq::Testing.inline! do
254
+ batch.let_close!
255
+ end
256
+ end
257
+
258
+ it 'triggers callbacks as expected' do
259
+ ActiveJob::Base.queue_adapter = :sidekiq
260
+ CanvasSync::JobBatches::Batch.redis { |r| r.hset("BID-#{bid}", 'pending', 0) }
261
+
262
+ class RetryingJob < BatchTestJobBase
263
+ @@failed = false
264
+
265
+ def perform
266
+ unless @@failed
267
+ @@failed = true
268
+ raise "A Failure"
269
+ end
270
+ end
271
+ end
272
+
273
+ callback_instance = double('SampleCallback')
274
+ expect(SampleCallback).to receive(:new).at_least(1).times.and_return(callback_instance)
275
+ expect(callback_instance).to receive(:on_complete)
276
+ expect(callback_instance).to receive(:on_success)
277
+
278
+ batch.on(:complete, SampleCallback)
279
+ batch.on(:success, SampleCallback)
280
+
281
+ batch.jobs do
282
+ RetryingJob.perform_later
283
+ end
284
+
285
+ job_def = Sidekiq::Worker.jobs[0]
286
+ int_job_class = job_def["class"].constantize
287
+
288
+ begin
289
+ int_job_class.process_job(job_def)
290
+ rescue
291
+ end
292
+ Sidekiq::Worker.drain_all
293
+ end
294
+
295
+ it 'cleanups redis key' do
296
+ CanvasSync::JobBatches::Batch.process_successful_job(bid, jid)
297
+ expect(CanvasSync::JobBatches::Batch.redis { |r| r.get("BID-#{bid}-pending") }.to_i).to eq(0)
298
+ end
299
+ end
300
+ end
301
+
302
+ describe '#increment_job_queue' do
303
+ let(:batch) { CanvasSync::JobBatches::Batch.new }
304
+
305
+ it 'increments pending' do
306
+ batch.jobs do TestWorker.perform_async end
307
+ pending = CanvasSync::JobBatches::Batch.redis { |r| r.hget("BID-#{batch.bid}", 'pending') }
308
+ expect(pending).to eq('1')
309
+ end
310
+ end
311
+
312
+ describe '#enqueue_callbacks' do
313
+ let(:callback) { double('callback') }
314
+ let(:event) { :complete }
315
+
316
+ context 'on :success' do
317
+ let(:event) { :success }
318
+ context 'when no callbacks are defined' do
319
+ it 'clears redis keys' do
320
+ batch = CanvasSync::JobBatches::Batch.new
321
+ batch.jobs {}
322
+ expect(CanvasSync::JobBatches::Batch).to receive(:cleanup_redis).with(batch.bid)
323
+ CanvasSync::JobBatches::Batch.enqueue_callbacks(event, batch.bid)
324
+ end
325
+ end
326
+ end
327
+
328
+ context 'when already called' do
329
+ it 'returns and does not enqueue callbacks' do
330
+ batch = CanvasSync::JobBatches::Batch.new
331
+ batch.on(event, SampleCallback)
332
+ CanvasSync::JobBatches::Batch.redis { |r| r.hset("BID-#{batch.bid}", event, "true") }
333
+
334
+ expect(batch).not_to receive(:push_callbacks)
335
+ CanvasSync::JobBatches::Batch.enqueue_callbacks(event, batch.bid)
336
+ end
337
+ end
338
+
339
+ context 'With ActiveJob Adapter' do
340
+ around(:all) do |block|
341
+ CanvasSync::JobBatches::Batch::Callback.worker_class = CanvasSync::JobBatches::ActiveJob::ActiveJobCallbackWorker
342
+ block.run
343
+ end
344
+
345
+ context 'when not yet called' do
346
+ context 'when there is no callback' do
347
+ it 'it returns' do
348
+ batch = CanvasSync::JobBatches::Batch.new
349
+
350
+ expect(batch).not_to receive(:push_callbacks)
351
+ CanvasSync::JobBatches::Batch.enqueue_callbacks(event, batch.bid)
352
+ end
353
+ end
354
+
355
+ context 'when callback defined' do
356
+ let(:opts) { { 'a' => 'b' } }
357
+
358
+ it 'calls it passing options' do
359
+ ActiveJob::Base.queue_adapter = :test
360
+
361
+ batch = CanvasSync::JobBatches::Batch.new
362
+ batch.on(event, SampleCallback, opts)
363
+ batch.jobs {}
364
+
365
+ CanvasSync::JobBatches::Batch.enqueue_callbacks(event, batch.bid)
366
+
367
+ expect(CanvasSync::JobBatches::Batch::Callback.worker_class).to have_been_enqueued.with(
368
+ 'SampleCallback', event.to_s, opts, batch.bid, nil
369
+ )
370
+ end
371
+ end
372
+
373
+ context 'when multiple callbacks are defined' do
374
+ let(:opts) { { 'a' => 'b' } }
375
+ let(:opts2) { { 'b' => 'a' } }
376
+
377
+ it 'enqueues each callback passing their options' do
378
+ ActiveJob::Base.queue_adapter = :test
379
+
380
+ batch = CanvasSync::JobBatches::Batch.new
381
+ batch.on(event, SampleCallback, opts)
382
+ batch.on(event, SampleCallback2, opts2)
383
+ batch.jobs{}
384
+
385
+ CanvasSync::JobBatches::Batch.enqueue_callbacks(event, batch.bid)
386
+ expect(CanvasSync::JobBatches::Batch::Callback.worker_class).to have_been_enqueued.with(
387
+ 'SampleCallback2', event.to_s, opts2, batch.bid, nil
388
+ )
389
+ expect(CanvasSync::JobBatches::Batch::Callback.worker_class).to have_been_enqueued.with(
390
+ 'SampleCallback', event.to_s, opts, batch.bid, nil
391
+ )
392
+ end
393
+ end
394
+ end
395
+ end
396
+
397
+ context 'With Sidekiq Adapter' do
398
+ around(:all) do |block|
399
+ CanvasSync::JobBatches::Batch::Callback.worker_class = CanvasSync::JobBatches::Sidekiq::SidekiqCallbackWorker
400
+ block.run
401
+ end
402
+
403
+ context 'when not yet called' do
404
+ context 'when there is no callback' do
405
+ it 'it returns' do
406
+ batch = CanvasSync::JobBatches::Batch.new
407
+
408
+ expect(batch).not_to receive(:push_callbacks)
409
+ CanvasSync::JobBatches::Batch.enqueue_callbacks(event, batch.bid)
410
+ end
411
+ end
412
+
413
+ context 'when callback defined' do
414
+ let(:opts) { { 'a' => 'b' } }
415
+
416
+ it 'calls it passing options' do
417
+ batch = CanvasSync::JobBatches::Batch.new
418
+ batch.on(event, SampleCallback, opts)
419
+ batch.jobs{}
420
+
421
+ expect(Sidekiq::Client).to receive(:push_bulk).with(
422
+ 'class' => Sidekiq::Batch::Callback.worker_class,
423
+ 'args' => [['SampleCallback', event.to_s, opts, batch.bid, nil]],
424
+ 'queue' => 'default'
425
+ )
426
+
427
+ CanvasSync::JobBatches::Batch.enqueue_callbacks(event, batch.bid)
428
+ end
429
+ end
430
+
431
+ context 'when multiple callbacks are defined' do
432
+ let(:opts) { { 'a' => 'b' } }
433
+ let(:opts2) { { 'b' => 'a' } }
434
+
435
+ it 'enqueues each callback passing their options' do
436
+ batch = CanvasSync::JobBatches::Batch.new
437
+ batch.on(event, SampleCallback, opts)
438
+ batch.on(event, SampleCallback2, opts2)
439
+ batch.jobs{}
440
+
441
+ expect(Sidekiq::Client).to receive(:push_bulk).with(
442
+ 'class' => Sidekiq::Batch::Callback.worker_class,
443
+ 'args' => array_including(
444
+ ['SampleCallback', event.to_s, opts, batch.bid, nil],
445
+ ['SampleCallback2', event.to_s, opts2, batch.bid, nil],
446
+ ),
447
+ 'queue' => 'default'
448
+ )
449
+
450
+ CanvasSync::JobBatches::Batch.enqueue_callbacks(event, batch.bid)
451
+ end
452
+ end
453
+ end
454
+ end
455
+ end
456
+
457
+ describe '.redis' do
458
+ it 'returns the same connection if called when nested' do
459
+ CanvasSync::JobBatches::Batch.redis do |r1|
460
+ CanvasSync::JobBatches::Batch.redis do |r2|
461
+ expect(r1).to be r2
462
+ end
463
+ end
464
+ end
465
+
466
+ it 'returns a RedisProxy object if no block is given' do
467
+ expect(CanvasSync::JobBatches::Batch.redis).to be_a(CanvasSync::JobBatches::Batch::RedisProxy)
468
+ end
469
+
470
+ describe 'RedisProxy' do
471
+ let!(:proxy) { CanvasSync::JobBatches::Batch.redis }
472
+
473
+ it 'calls Batch.redis with a block' do
474
+ expect(CanvasSync::JobBatches::Batch).to receive(:redis) do |&block|
475
+ expect(block).to be_a Proc
476
+ end
477
+ proxy.sadd('KEY', 5)
478
+ end
479
+
480
+ describe '#multi' do
481
+ it 'works with 1 arity' do
482
+ proxy.multi do |r|
483
+ r.sadd('KEY', 5)
484
+ end
485
+ end
486
+ end
487
+ end
488
+ end
489
+ end
@@ -0,0 +1,38 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe CanvasSync::JobBatches::Batch::Callback.worker_class do
4
+ describe '#perform' do
5
+ it 'does not do anything if it cannot find the callback class' do
6
+ subject.perform('SampleCallback', 'complete', {}, 'ABCD', 'EFGH')
7
+ end
8
+
9
+ it 'does not do anything if event is different from complete or success' do
10
+ expect(SampleCallback).not_to receive(:new)
11
+ subject.perform('SampleCallback', 'ups', {}, 'ABCD', 'EFGH')
12
+ end
13
+
14
+ it 'calls on_success if defined' do
15
+ callback_instance = double('SampleCallback', on_success: true)
16
+ expect(SampleCallback).to receive(:new).and_return(callback_instance)
17
+ expect(callback_instance).to receive(:on_success)
18
+ .with(instance_of(CanvasSync::JobBatches::Batch::Status), {})
19
+ subject.perform('SampleCallback', 'success', {}, 'ABCD', 'EFGH')
20
+ end
21
+
22
+ it 'calls on_complete if defined' do
23
+ callback_instance = double('SampleCallback')
24
+ expect(SampleCallback).to receive(:new).and_return(callback_instance)
25
+ expect(callback_instance).to receive(:on_complete)
26
+ .with(instance_of(CanvasSync::JobBatches::Batch::Status), {})
27
+ subject.perform('SampleCallback', 'complete', {}, 'ABCD', 'EFGH')
28
+ end
29
+
30
+ it 'calls specific callback if defined' do
31
+ callback_instance = double('SampleCallback')
32
+ expect(SampleCallback).to receive(:new).and_return(callback_instance)
33
+ expect(callback_instance).to receive(:sample_method)
34
+ .with(instance_of(CanvasSync::JobBatches::Batch::Status), {})
35
+ subject.perform('SampleCallback#sample_method', 'complete', {}, 'ABCD', 'EFGH')
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,54 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe CanvasSync::JobBatches::ContextHash do
4
+
5
+ class ContextedJob < BatchTestJobBase
6
+ def perform
7
+ was_performed(batch_context.flatten)
8
+ end
9
+
10
+ def was_performed(*args); end
11
+
12
+ def self.callback_perform(*args)
13
+ perform_later
14
+ end
15
+ end
16
+
17
+ it "works with first-level jobs" do
18
+ ActiveJob::Base.queue_adapter = :sidekiq
19
+ b = CanvasSync::JobBatches::Batch.new
20
+ b.context = { hello: 'world' }
21
+ expect_any_instance_of(ContextedJob).to receive(:was_performed).with({ 'hello' => 'world' })
22
+ b.jobs do
23
+ ContextedJob.perform_later
24
+ end
25
+ Sidekiq::Worker.drain_all
26
+ end
27
+
28
+ it "works with nested-batch jobs" do
29
+ ActiveJob::Base.queue_adapter = :sidekiq
30
+ b = CanvasSync::JobBatches::Batch.new
31
+ b.context = { hello: 'world', foo: 'bar' }
32
+ expect_any_instance_of(ContextedJob).to receive(:was_performed).with({ 'hello' => 'world', 'foo' => 'baz', 'some' => 'other' })
33
+ b.jobs do
34
+ b2 = CanvasSync::JobBatches::Batch.new
35
+ b2.context = { some: 'other', foo: 'baz' }
36
+ b2.jobs do
37
+ ContextedJob.perform_later
38
+ end
39
+ end
40
+ Sidekiq::Worker.drain_all
41
+ end
42
+
43
+ it 'works with a callback batch' do
44
+ ActiveJob::Base.queue_adapter = :sidekiq
45
+ b = CanvasSync::JobBatches::Batch.new
46
+ b.context = { hello: 'world' }
47
+ b.on(:success, "ContextedJob.callback_perform")
48
+ expect_any_instance_of(ContextedJob).to receive(:was_performed).with({ 'hello' => 'world' })
49
+ b.jobs do
50
+ BatchTestJobBase.perform_later
51
+ end
52
+ Sidekiq::Worker.drain_all
53
+ end
54
+ end
@@ -0,0 +1,82 @@
1
+ require 'spec_helper'
2
+
3
+ class WorkerA < BatchTestJobBase
4
+ def perform
5
+ end
6
+ end
7
+
8
+ class WorkerB < BatchTestJobBase
9
+ def perform
10
+ end
11
+ end
12
+
13
+ class WorkerC < BatchTestJobBase
14
+ def perform
15
+ end
16
+ end
17
+
18
+ RSpec.describe 'Batch flow' do
19
+ context 'when handling a batch' do
20
+ let(:batch) { CanvasSync::JobBatches::Batch.new }
21
+ before { batch.on(:complete, SampleCallback, :id => 42) }
22
+ before { batch.description = 'describing the batch' }
23
+ let(:status) { CanvasSync::JobBatches::Batch::Status.new(batch.bid) }
24
+ let(:queue) { Sidekiq::Queue.new }
25
+
26
+ it 'correctly initializes' do
27
+ expect(batch.bid).not_to be_nil
28
+ expect(batch.description).to eq('describing the batch')
29
+
30
+ batch.jobs do
31
+ 3.times do
32
+ TestWorker.perform_async
33
+ end
34
+ end
35
+
36
+ expect(status.pending).to eq(3)
37
+ expect(status.failures).to eq(0)
38
+ expect(status.complete?).to be false
39
+ expect(status.created_at).not_to be_nil
40
+ expect(status.bid).to eq(batch.bid)
41
+ end
42
+ end
43
+
44
+ context 'when handling a nested batch' do
45
+ let(:batchA) { CanvasSync::JobBatches::Batch.new }
46
+ let(:batchB) { CanvasSync::JobBatches::Batch.new }
47
+ let(:batchC) { CanvasSync::JobBatches::Batch.new(batchA.bid) }
48
+ let(:batchD) { CanvasSync::JobBatches::Batch.new }
49
+ let(:jids) { [] }
50
+ let(:parent) { batchA.bid }
51
+ let(:children) { [] }
52
+
53
+ it 'handles a basic nested batch' do
54
+ batchA.jobs do
55
+ jids << WorkerA.perform_async
56
+ batchB.jobs do
57
+ jids << WorkerB.perform_async
58
+ end
59
+ jids << WorkerA.perform_async
60
+ children << batchB.bid
61
+ end
62
+
63
+ batchC.jobs do
64
+ batchD.jobs do
65
+ jids << WorkerC.perform_async
66
+ end
67
+ children << batchD.bid
68
+ end
69
+
70
+ expect(jids.size).to eq(4)
71
+ expect(CanvasSync::JobBatches::Batch::Status.new(parent).child_count).to eq(2)
72
+ children.each do |kid|
73
+ status = CanvasSync::JobBatches::Batch::Status.new(kid)
74
+ expect(status.child_count).to eq(0)
75
+ expect(status.pending).to eq(1)
76
+ expect(status.parent_bid).to eq(parent)
77
+ end
78
+
79
+ end
80
+
81
+ end
82
+ end
@@ -0,0 +1,42 @@
1
+ require_relative '../integration_helper'
2
+
3
+ # Workflow when a Job fails, retries, and then succeeds
4
+
5
+ class Worker1
6
+ include Sidekiq::Worker
7
+ sidekiq_options retry: 5
8
+
9
+ @@failed = false
10
+
11
+ def perform
12
+ Sidekiq.logger.info "Work 1"
13
+
14
+ unless @@failed
15
+ @@failed = true
16
+ raise "One Failure"
17
+ end
18
+ end
19
+ end
20
+
21
+ class MyCallback
22
+ def on_success(status, options)
23
+ Sidekiq.logger.info "Overall Success #{options} #{status.data}"
24
+ end
25
+ alias_method :multi, :on_success
26
+
27
+ def on_complete(status, options)
28
+ Sidekiq.logger.info "Overall Complete #{options} #{status.data}"
29
+ end
30
+ end
31
+
32
+ overall = CanvasSync::JobBatches::Batch.new
33
+ overall.on(:success, MyCallback, to: 'success@gmail.com')
34
+ overall.on(:complete, MyCallback, to: 'success@gmail.com')
35
+ overall.jobs do
36
+ Worker1.perform_async
37
+ end
38
+
39
+ puts "Overall bid #{overall.bid}"
40
+
41
+ output, keys = process_tests
42
+ overall_tests(output, keys, file: __FILE__)