|
36 | 36 | schedule_task, |
37 | 37 | cancel_task, |
38 | 38 | maintain_tasks, |
| 39 | + reschedule_task, |
39 | 40 | _enqueue_task, |
40 | 41 | _on_success_callback, |
41 | 42 | _on_failure_callback |
@@ -511,6 +512,156 @@ def test_no_task_found(self): |
511 | 512 | cancel_task('non-existent-task-uuid') |
512 | 513 |
|
513 | 514 |
|
| 515 | +class TestRescheduleTask(GrimoireLabTestCase): |
| 516 | + def setUp(self): |
| 517 | + GRIMOIRELAB_TASK_MODELS.clear() |
| 518 | + task_class, job_class = register_task_model('test_task', SchedulerTestTask) |
| 519 | + |
| 520 | + def cleanup_test_model(): |
| 521 | + with django.db.connection.schema_editor() as schema_editor: |
| 522 | + schema_editor.delete_model(job_class) |
| 523 | + schema_editor.delete_model(task_class) |
| 524 | + |
| 525 | + with django.db.connection.schema_editor() as schema_editor: |
| 526 | + schema_editor.create_model(task_class) |
| 527 | + schema_editor.create_model(job_class) |
| 528 | + |
| 529 | + self.addCleanup(cleanup_test_model) |
| 530 | + super().setUp() |
| 531 | + |
| 532 | + def test_reschedule_task_completed(self): |
| 533 | + """Test a task is rescheduled correctly""" |
| 534 | + |
| 535 | + task_args = { |
| 536 | + 'a': 1, |
| 537 | + 'b': 2, |
| 538 | + } |
| 539 | + |
| 540 | + # Enqueue the task |
| 541 | + enqueued_at = grimoirelab_toolkit.datetime.datetime_utcnow() |
| 542 | + task = schedule_task('test_task', task_args) |
| 543 | + |
| 544 | + # Check if a new job is created and the task is enqueued |
| 545 | + self.assertEqual(task.status, SchedulerStatus.ENQUEUED) |
| 546 | + self.assertGreaterEqual(task.scheduled_at, enqueued_at) |
| 547 | + |
| 548 | + # Run the job |
| 549 | + before_run_call_dt = grimoirelab_toolkit.datetime.datetime_utcnow() |
| 550 | + worker = django_rq.workers.get_worker('testing') |
| 551 | + processed = worker.work(burst=True, with_scheduler=True) |
| 552 | + after_run_call_dt = grimoirelab_toolkit.datetime.datetime_utcnow() |
| 553 | + |
| 554 | + self.assertEqual(processed, True) |
| 555 | + |
| 556 | + # Check task and task state after execution |
| 557 | + task.refresh_from_db() |
| 558 | + self.assertEqual(task.status, SchedulerStatus.COMPLETED) |
| 559 | + self.assertEqual(task.runs, 1) |
| 560 | + self.assertEqual(task.failures, 0) |
| 561 | + self.assertGreater(task.last_run, before_run_call_dt) |
| 562 | + self.assertLess(task.last_run, after_run_call_dt) |
| 563 | + |
| 564 | + # Reschedule the completed task |
| 565 | + enqueued_at = grimoirelab_toolkit.datetime.datetime_utcnow() |
| 566 | + reschedule_task(task.uuid) |
| 567 | + |
| 568 | + # Check if the task is rescheduled |
| 569 | + task.refresh_from_db() |
| 570 | + self.assertEqual(task.status, SchedulerStatus.ENQUEUED) |
| 571 | + self.assertGreater(task.scheduled_at, enqueued_at) |
| 572 | + |
| 573 | + # Run the job |
| 574 | + before_run_call_dt = grimoirelab_toolkit.datetime.datetime_utcnow() |
| 575 | + worker = django_rq.workers.get_worker('testing') |
| 576 | + processed = worker.work(burst=True, with_scheduler=True) |
| 577 | + after_run_call_dt = grimoirelab_toolkit.datetime.datetime_utcnow() |
| 578 | + |
| 579 | + self.assertEqual(processed, True) |
| 580 | + |
| 581 | + # Check task and task state after execution |
| 582 | + task.refresh_from_db() |
| 583 | + self.assertEqual(task.status, SchedulerStatus.COMPLETED) |
| 584 | + self.assertEqual(task.runs, 2) |
| 585 | + self.assertEqual(task.failures, 0) |
| 586 | + self.assertGreater(task.last_run, before_run_call_dt) |
| 587 | + self.assertLess(task.last_run, after_run_call_dt) |
| 588 | + |
| 589 | + def test_reschedule_task_enqueued(self): |
| 590 | + """Test if rescheduling a task enqueued change the scheduled time""" |
| 591 | + |
| 592 | + # Enqueue the task |
| 593 | + task_args = { |
| 594 | + 'a': 1, |
| 595 | + 'b': 2, |
| 596 | + } |
| 597 | + first_schedule_time = datetime.datetime(2100, 1, 1, tzinfo=datetime.timezone.utc) |
| 598 | + |
| 599 | + task = SchedulerTestTask.create_task(task_args, 360, 10) |
| 600 | + job_db = _enqueue_task(task, scheduled_at=first_schedule_time) |
| 601 | + |
| 602 | + # Check initial state of the task |
| 603 | + self.assertEqual(task.task_id, f'grimoire:task:{task.uuid}') |
| 604 | + self.assertEqual(task.status, SchedulerStatus.ENQUEUED) |
| 605 | + self.assertEqual(task.scheduled_at, first_schedule_time) |
| 606 | + |
| 607 | + # Reschedule the task |
| 608 | + reschedule_task(task.uuid) |
| 609 | + second_schedule_time = grimoirelab_toolkit.datetime.datetime_utcnow() |
| 610 | + |
| 611 | + # Check if the task is rescheduled |
| 612 | + task.refresh_from_db() |
| 613 | + self.assertEqual(task.status, SchedulerStatus.ENQUEUED) |
| 614 | + self.assertLess(task.scheduled_at, second_schedule_time) |
| 615 | + |
| 616 | + def test_reschedule_inconsistent_task(self): |
| 617 | + """Test if reschedules a task with an inconsistent state""" |
| 618 | + |
| 619 | + # Enqueue the task |
| 620 | + task_args = { |
| 621 | + 'a': 1, |
| 622 | + 'b': 2, |
| 623 | + } |
| 624 | + |
| 625 | + task = SchedulerTestTask.create_task(task_args, 360, 10) |
| 626 | + job_db = _enqueue_task(task) |
| 627 | + |
| 628 | + # Check initial state of the task |
| 629 | + self.assertEqual(task.status, SchedulerStatus.ENQUEUED) |
| 630 | + |
| 631 | + # Delete job manually to create the inconsistent state |
| 632 | + job_rq = rq.job.Job.fetch(job_db.uuid, connection=django_rq.get_connection()) |
| 633 | + job_rq.delete() |
| 634 | + |
| 635 | + # Make sure the job was deleted |
| 636 | + with self.assertRaises(rq.exceptions.NoSuchJobError): |
| 637 | + rq.job.Job.fetch(job_db.uuid, connection=django_rq.get_connection()) |
| 638 | + |
| 639 | + # Reschedule the task |
| 640 | + scheduled_date = grimoirelab_toolkit.datetime.datetime_utcnow() |
| 641 | + reschedule_task(task.uuid) |
| 642 | + |
| 643 | + # Check if the task is rescheduled |
| 644 | + task.refresh_from_db() |
| 645 | + job_db = task.jobs.order_by('-scheduled_at').first() |
| 646 | + |
| 647 | + self.assertEqual(task.status, SchedulerStatus.ENQUEUED) |
| 648 | + self.assertEqual(job_db.status, SchedulerStatus.ENQUEUED) |
| 649 | + self.assertGreater(task.scheduled_at, scheduled_date) |
| 650 | + |
| 651 | + # Run the job |
| 652 | + worker = django_rq.workers.get_worker('testing') |
| 653 | + processed = worker.work(burst=True, with_scheduler=True) |
| 654 | + |
| 655 | + self.assertEqual(processed, True) |
| 656 | + |
| 657 | + # Check task and task state after execution |
| 658 | + task.refresh_from_db() |
| 659 | + self.assertEqual(task.status, SchedulerStatus.COMPLETED) |
| 660 | + self.assertEqual(task.runs, 1) |
| 661 | + self.assertEqual(task.failures, 0) |
| 662 | + self.assertGreater(task.last_run, scheduled_date) |
| 663 | + |
| 664 | + |
514 | 665 | class TestOnSuccessCallback(GrimoireLabTestCase): |
515 | 666 | """Unit tests for the default on_success_callback function""" |
516 | 667 |
|
|
0 commit comments