def activate(self):
    """ activate workflows just once after the class has been initialized

    This should be in setUpClass method, but we can't access the server
    context from there."""
    gen = WorkflowsGenerator()

    # generate cycle for the only one time wf
    wf1 = Workflow.query.filter_by(status="Draft", slug="wf-1").first()
    if wf1:
      gen.generate_cycle(wf1)

    workflows = Workflow.query.filter_by(status="Draft").all()
    for wf in workflows:
      gen.activate_workflow(wf)
Esempio n. 2
0
    def activate(self):
        """ activate workflows just once after the class has been initialized

    This should be in setUpClass method, but we can't access the server
    context from there."""
        gen = WorkflowsGenerator()

        # generate cycle for the only one time wf
        wf1 = Workflow.query.filter_by(status="Draft", slug="wf-1").first()
        if wf1:
            gen.generate_cycle(wf1)

        workflows = Workflow.query.filter_by(status="Draft").all()
        for wf in workflows:
            gen.activate_workflow(wf)
Esempio n. 3
0
  def activate(self):
    """ activate workflows just once after the class has been initialized

    This should be in setUpClass method, but we can't access the server
    context from there."""
    gen = WorkflowsGenerator()

    # generate cycle for the only one time wf
    wf1 = Workflow.query.filter_by(status="Draft", slug="wf-1").first()
    if wf1:
      gen.generate_cycle(wf1)

    # Only workflows with at least one task group could be activated
    workflows = db.session.query(Workflow).join(TaskGroup).filter(
        Workflow.id == TaskGroup.workflow_id,
        Workflow.status == 'Draft').all()
    for workflow in workflows:
      gen.activate_workflow(workflow)
    def activate(self):
        """ activate workflows just once after the class has been initialized

    This should be in setUpClass method, but we can't access the server
    context from there."""
        gen = WorkflowsGenerator()

        # generate cycle for the only one time wf
        wf1 = Workflow.query.filter_by(status="Draft", slug="wf-1").first()
        if wf1:
            gen.generate_cycle(wf1)

        # Only workflows with at least one task group could be activated
        workflows = db.session.query(Workflow).join(TaskGroup).filter(
            Workflow.id == TaskGroup.workflow_id,
            Workflow.status == 'Draft').all()
        for workflow in workflows:
            gen.activate_workflow(workflow)
    def setUp(self):
        super(TestComprehensiveSheets, self).setUp()
        self.client.get("/login")
        self.generator = ObjectGenerator()

        self.create_custom_attributes()
        # TODO: use here such a CSV that doesn't have errors or warnings
        self.import_file("comprehensive_sheet1.csv", safe=False)

        gen = WorkflowsGenerator()
        wfs = all_models.Workflow.eager_query().filter_by(status='Draft').all()
        for workflow in wfs:
            _, cycle = gen.generate_cycle(workflow)
            self.assertIsNotNone(cycle)
Esempio n. 6
0
  def setUp(self):
    TestCase.setUp(self)
    self.generator = ObjectGenerator()
    self.client.get("/login")

    self.create_custom_attributes()
    filename = "comprehensive_sheet1.csv"
    self.import_file(filename)

    gen = WorkflowsGenerator()
    wfs = all_models.Workflow.eager_query().filter_by(status='Draft').all()
    for workflow in wfs:
      _, cycle = gen.generate_cycle(workflow)
      self.assertIsNotNone(cycle)
Esempio n. 7
0
  def setUp(self):
    super(TestComprehensiveSheets, self).setUp()
    self.client.get("/login")
    self.generator = ObjectGenerator()

    self.create_custom_attributes()
    # TODO: use here such a CSV that doesn't have errors or warnings
    self.import_file("comprehensive_sheet1.csv", safe=False)

    gen = WorkflowsGenerator()
    wfs = all_models.Workflow.eager_query().filter_by(status='Draft').all()
    for workflow in wfs:
      _, cycle = gen.generate_cycle(workflow)
      self.assertIsNotNone(cycle)
  def setUp(self):
    self.client.get("/login")
    self.generator = ObjectGenerator()
    if TestComprehensiveSheets.first_run:
      TestComprehensiveSheets.first_run = False
      super(TestComprehensiveSheets, self).setUp()

      self.create_custom_attributes()
      self.import_file("comprehensive_sheet1.csv")

      gen = WorkflowsGenerator()
      wfs = all_models.Workflow.eager_query().filter_by(status='Draft').all()
      for workflow in wfs:
        _, cycle = gen.generate_cycle(workflow)
        self.assertIsNotNone(cycle)
  def setUp(self):
    self.client.get("/login")
    self.generator = ObjectGenerator()
    if TestComprehensiveSheets.first_run:
      TestComprehensiveSheets.first_run = False
      super(TestComprehensiveSheets, self).setUp()

      self.create_custom_attributes()
      self.import_file("comprehensive_sheet1.csv")

      gen = WorkflowsGenerator()
      wfs = all_models.Workflow.eager_query().filter_by(status='Draft').all()
      for workflow in wfs:
        _, cycle = gen.generate_cycle(workflow)
        self.assertIsNotNone(cycle)
class TestTaskOverdueNotificationsUsingImports(TestTaskOverdueNotifications):
  """Tests for overdue notifications when changing Tasks via imports."""

  # pylint: disable=invalid-name

  CSV_DIR = join(abspath(dirname(__file__)), "../converters/test_csvs/")

  def setUp(self):
    self.wf_generator = WorkflowsGenerator()
    self._fix_notification_init()

  @patch("ggrc.notifications.common.send_email")
  def test_creating_overdue_notifications_for_new_tasks(self, _):
    """Overdue notifications should be created for tasks created with imports.
    """
    Workflow.query.delete()
    models.Notification.query.delete()
    db.session.commit()

    filename = join(self.CSV_DIR, "workflow_small_sheet.csv")
    response = self.import_file(filename)
    self._check_csv_response(response, expected_messages={})

    workflow = Workflow.query.one()
    self.wf_generator.generate_cycle(workflow)
    response, workflow = self.wf_generator.activate_workflow(workflow)

    user = models.Person.query.filter(
        models.Person.email == '*****@*****.**').one()

    with freeze_time("2020-01-01 00:00:00"):  # afer all tasks' due dates
      _, notif_data = common.get_daily_notifications()
      user_notifs = notif_data.get(user.email, {})
      self.assertIn("task_overdue", user_notifs)
      self.assertEqual(len(user_notifs["task_overdue"]), 4)

  @patch("ggrc.notifications.common.send_email")
  def test_overdue_notifications_when_task_due_date_is_changed(self, _):
    """Overdue notifications should adjust to task due date changes."""
    Workflow.query.delete()
    models.Notification.query.delete()
    db.session.commit()

    filename = join(self.CSV_DIR, "workflow_small_sheet.csv")
    response = self.import_file(filename)
    self._check_csv_response(response, expected_messages={})

    workflow = Workflow.query.one()
    self.wf_generator.generate_cycle(workflow)
    response, workflow = self.wf_generator.activate_workflow(workflow)

    user = models.Person.query.filter(
        models.Person.email == '*****@*****.**').one()

    with freeze_time("2015-01-01 00:00:00"):  # before all tasks' due dates
      _, notif_data = common.get_daily_notifications()
      user_notifs = notif_data.get(user.email, {})
      self.assertNotIn("task_overdue", user_notifs)

      # now modify task's due date and check if overdue notification appears
      task = CycleTaskGroupObjectTask.query.filter(
          CycleTaskGroupObjectTask.title == "task for wf-2").one()
      task_id, task_code = task.id, task.slug

      response = self.import_data(OrderedDict((
          ("object_type", "CycleTask"),
          ("Code*", task_code),
          ("Start Date", "12/15/2014"),
          ("Due Date", "12/31/2014"),
      )))
      self._check_csv_response(response, expected_messages={})

      _, notif_data = common.get_daily_notifications()
      user_notifs = notif_data.get(user.email, {})
      self.assertIn("task_overdue", user_notifs)
      self.assertEqual(len(user_notifs["task_overdue"]), 1)
      self.assertIn(task_id, user_notifs["task_overdue"])
class TestWorkflowsCycleGeneration(TestCase):
    """
  Tests for Cycle generation logic
  """
    def setUp(self):
        super(TestWorkflowsCycleGeneration, self).setUp()
        self.api = Api()
        self.generator = WorkflowsGenerator()
        self.object_generator = ObjectGenerator()

    def tearDown(self):
        pass

    @ddt.data(True, False)
    def test_recurring_without_tgts_skip(self, has_tg):
        """Test that Active Workflow without TGTs is skipped on cron job"""
        with freeze_time(dtm.date(2017, 9, 25)):
            with factories.single_commit():
                workflow = wf_factories.WorkflowFactory(
                    repeat_every=1, unit=Workflow.MONTH_UNIT)
                workflow_id = workflow.id
                group = wf_factories.TaskGroupFactory(workflow=workflow)
                wf_factories.TaskGroupTaskFactory(
                    task_group=group,
                    start_date=dtm.date(2017, 9, 26),
                    end_date=dtm.date(2017, 9, 26) + dtm.timedelta(days=4))
            self.generator.activate_workflow(workflow)
            active_wf = db.session.query(Workflow).filter(
                Workflow.id == workflow_id).one()
            self.assertEqual(active_wf.next_cycle_start_date,
                             dtm.date(2017, 9, 26))
            self.assertEqual(active_wf.recurrences, True)
            self.assertEqual(len(active_wf.cycles), 0)
            TaskGroupTask.query.delete()
            if not has_tg:
                TaskGroup.query.delete()
            db.session.commit()

        with freeze_time(dtm.date(2017, 10, 25)):
            start_recurring_cycles()
            active_wf = db.session.query(Workflow).filter(
                Workflow.id == workflow_id).one()
            self.assertEqual(active_wf.next_cycle_start_date,
                             dtm.date(2017, 9, 26))
            self.assertEqual(active_wf.recurrences, True)
            self.assertEqual(len(active_wf.cycles), 0)

    @ddt.data(
        # (expected, setup_date),
        (dtm.date(2017, 2, 28), dtm.date(2017, 2, 28)),
        (dtm.date(2017, 3, 3), dtm.date(2017, 3, 3)),
        (dtm.date(2017, 8, 4), dtm.date(2017, 8, 5)),
        (dtm.date(2017, 7, 21), dtm.date(2017, 7, 22)),
    )
    @ddt.unpack
    def test_one_time_wf_start_date_shifting(self, expected, setup_date):
        """Test case for correct cycle task start_ dates for one_time wf"""
        with factories.single_commit():
            workflow = wf_factories.WorkflowFactory()
            group = wf_factories.TaskGroupFactory(workflow=workflow)
            wf_factories.TaskGroupTaskFactory(task_group=group,
                                              start_date=setup_date,
                                              end_date=setup_date +
                                              dtm.timedelta(days=4))
        self.generator.generate_cycle(workflow)
        self.generator.activate_workflow(workflow)
        active_wf = db.session.query(Workflow).filter(
            Workflow.status == 'Active').one()
        self.assertEqual(
            1, len(active_wf.cycles[0].cycle_task_group_object_tasks))
        cycle_task = active_wf.cycles[0].cycle_task_group_object_tasks[0]
        adj_start_date = cycle_task.start_date
        self.assertEqual(expected, adj_start_date)

    @ddt.data(
        # (expected, end_date),
        (dtm.date(2017, 2, 28), dtm.date(2017, 2, 28)),
        (dtm.date(2017, 3, 3), dtm.date(2017, 3, 3)),
        (dtm.date(2017, 8, 4), dtm.date(2017, 8, 5)),
        (dtm.date(2017, 7, 21), dtm.date(2017, 7, 22)),
    )
    @ddt.unpack
    def test_one_time_wf_end_date_shifting(self, expected, setup_date):
        """Test case for correct cycle task end_ dates for one_time wf"""
        with factories.single_commit():
            workflow = wf_factories.WorkflowFactory()
            group = wf_factories.TaskGroupFactory(workflow=workflow)
            wf_factories.TaskGroupTaskFactory(task_group=group,
                                              start_date=setup_date -
                                              dtm.timedelta(days=4),
                                              end_date=setup_date)
        self.generator.generate_cycle(workflow)
        self.generator.activate_workflow(workflow)
        active_wf = db.session.query(Workflow).filter(
            Workflow.status == 'Active').one()
        self.assertEqual(
            1, len(active_wf.cycles[0].cycle_task_group_object_tasks))
        cycle_task = active_wf.cycles[0].cycle_task_group_object_tasks[0]
        adj_end_date = cycle_task.end_date
        self.assertEqual(expected, adj_end_date)

    # pylint: disable=too-many-arguments
    @ddt.data(
        # (expected, setup_date, freeze_date, repeat_every, unit),
        (dtm.date(2017, 3, 31), dtm.date(2017, 2, 28), dtm.date(
            2017, 4, 1), 1, Workflow.MONTH_UNIT),
        (dtm.date(2016, 3, 28), dtm.date(2016, 2, 28), dtm.date(
            2016, 4, 1), 1, Workflow.MONTH_UNIT),
        (dtm.date(2017, 2, 28), dtm.date(2017, 1, 31), dtm.date(
            2017, 3, 31), 1, Workflow.MONTH_UNIT),
        (dtm.date(2017, 3, 17), dtm.date(2017, 3, 10), dtm.date(
            2017, 3, 24), 1, Workflow.WEEK_UNIT),
        (dtm.date(2017, 2, 28), dtm.date(2016, 2, 29), dtm.date(
            2017, 3, 29), 12, Workflow.MONTH_UNIT),
        (dtm.date(2017, 4, 28), dtm.date(2017, 1, 31), dtm.date(
            2017, 5, 31), 3, Workflow.MONTH_UNIT),
    )
    @ddt.unpack
    def test_recurring_wf_start_date_shifting(self, expected, setup_date,
                                              freeze_date, repeat_every, unit):
        """Test case for correct next cycle task start_date for recurring wf"""
        with freeze_time(freeze_date):
            with factories.single_commit():
                workflow = wf_factories.WorkflowFactory(
                    repeat_every=repeat_every, unit=unit)
                group = wf_factories.TaskGroupFactory(workflow=workflow)
                wf_factories.TaskGroupTaskFactory(task_group=group,
                                                  start_date=setup_date,
                                                  end_date=setup_date +
                                                  dtm.timedelta(days=4))
            self.generator.activate_workflow(workflow)

        active_wf = db.session.query(Workflow).filter(
            Workflow.status == 'Active').one()
        cycle_task = active_wf.cycles[1].cycle_task_group_object_tasks[0]
        adj_start_date = cycle_task.start_date
        self.assertEqual(expected, adj_start_date)

    @ddt.data(
        # today is dtm.date(2017, 8, 15), task repeat every
        # (setup_start_date, update_start_date, expected_date),
        (dtm.date(2017, 1, 15), dtm.date(2017, 1, 1), dtm.date(2017, 9, 1)),
        (dtm.date(2017, 1, 15), dtm.date(2017, 1, 5), dtm.date(2017, 9, 5)),
        # new cycle starts on weekend that's why it moves to friday
        (dtm.date(2017, 1, 15), dtm.date(2017, 1, 10), dtm.date(2017, 9, 8)),
        (dtm.date(2017, 1, 15), dtm.date(2017, 8, 15), dtm.date(2017, 9, 15)),
        # new cycle starts on weekend that's why it moves to friday
        (dtm.date(2017, 1, 15), dtm.date(2017, 1, 20), dtm.date(2017, 8, 18)),
        (dtm.date(2017, 1, 15), dtm.date(2017, 1, 25), dtm.date(2017, 8, 25)),
        (dtm.date(2017, 8, 16), dtm.date(2017, 8, 14), dtm.date(2017, 9, 14)),
        (dtm.date(2017, 8, 16), dtm.date(2017, 8, 18), dtm.date(2017, 8, 18)),
        (dtm.date(2017, 8, 14), dtm.date(2017, 8, 18), dtm.date(2017, 8, 18)),
    )
    @ddt.unpack
    def test_recalculate_date(self, setup_start_date, update_start_date,
                              expected_date):
        """Recalculate next cycle start date"""
        with freeze_time(dtm.date(2017, 8, 15)):
            with factories.single_commit():
                workflow = wf_factories.WorkflowFactory(
                    repeat_every=1,
                    status=Workflow.DRAFT,
                    unit=Workflow.MONTH_UNIT)
                group = wf_factories.TaskGroupFactory(workflow=workflow)
                task_id = wf_factories.TaskGroupTaskFactory(
                    task_group=group,
                    start_date=setup_start_date,
                    end_date=setup_start_date + dtm.timedelta(days=4)).id
            self.generator.activate_workflow(workflow)
            self.api.put(
                TaskGroupTask.query.get(task_id), {
                    "start_date": update_start_date,
                    "end_date": update_start_date + dtm.timedelta(4)
                })
        active_wf = db.session.query(Workflow).filter(
            Workflow.status == 'Active').one()
        self.assertEqual(expected_date, active_wf.next_cycle_start_date)

    def test_recalculate_date_not_started(self):
        """Changing start_date on notstarted workflow will
       not affect next_cycle_start_date"""
        setup_start_date = dtm.date(2017, 1, 15)
        update_start_date = dtm.date(2017, 1, 1)
        with freeze_time(dtm.date(2017, 8, 15)):
            with factories.single_commit():
                workflow = wf_factories.WorkflowFactory(
                    repeat_every=1,
                    status=Workflow.DRAFT,
                    unit=Workflow.MONTH_UNIT)
                group = wf_factories.TaskGroupFactory(workflow=workflow)
                task_id = wf_factories.TaskGroupTaskFactory(
                    task_group=group,
                    start_date=setup_start_date,
                    end_date=setup_start_date + dtm.timedelta(days=4)).id
                workflow_id = workflow.id
        self.api.put(
            TaskGroupTask.query.get(task_id), {
                "start_date": update_start_date,
                "end_date": update_start_date + dtm.timedelta(4)
            })
        self.assertIsNone(
            Workflow.query.get(workflow_id).next_cycle_start_date)

    @ddt.data(
        # (setup_date, freeze_date, repeat_every, unit),
        (dtm.date(2017, 2, 28), dtm.date(2017, 4, 28), 1, Workflow.MONTH_UNIT),
        (dtm.date(2017, 3, 10), dtm.date(2017, 3, 24), 1, Workflow.WEEK_UNIT),
    )
    @ddt.unpack
    def test_recurring_wf_start_date_and_cycles(self, setup_date, freeze_date,
                                                repeat_every, unit):
        """Test case for correct cycle start date and number of cycles"""
        with freeze_time(freeze_date):
            with factories.single_commit():
                workflow = wf_factories.WorkflowFactory(
                    repeat_every=repeat_every, unit=unit)
                group = wf_factories.TaskGroupFactory(workflow=workflow)
                wf_factories.TaskGroupTaskFactory(task_group=group,
                                                  start_date=setup_date,
                                                  end_date=setup_date +
                                                  dtm.timedelta(days=4))
            self.generator.activate_workflow(workflow)

        active_wf = db.session.query(Workflow).filter(
            Workflow.status == 'Active').one()
        # freeze_date is chosen so that we expect 3 cycles to be generated:
        self.assertEqual(len(active_wf.cycles), 3)
        cycle_task = active_wf.cycles[0].cycle_task_group_object_tasks[0]
        adj_start_date = cycle_task.start_date
        self.assertEqual(setup_date, adj_start_date)

    @ddt.data(
        # (setup_date, freeze_date, repeat_every, unit,
        #  exp_start_date, exp_end_date),
        (dtm.date(2017, 2, 28), dtm.date(2017, 4, 28), 1, Workflow.MONTH_UNIT,
         dtm.date(2017, 2, 27), dtm.date(2017, 3, 3)),
        (dtm.date(2017, 3, 10), dtm.date(2017, 3, 24), 1, Workflow.WEEK_UNIT,
         dtm.date(2017, 3, 9), dtm.date(2017, 3, 14)),
    )
    @ddt.unpack
    def test_recurring_wf_start_end_cycle_dates(self, setup_date, freeze_date,
                                                repeat_every, unit,
                                                exp_start_date, exp_end_date):
        """Test case for correct cycle start and end dates"""
        with freeze_time(freeze_date):
            with factories.single_commit():
                workflow = wf_factories.WorkflowFactory(
                    repeat_every=repeat_every, unit=unit)
                group = wf_factories.TaskGroupFactory(workflow=workflow)
                wf_factories.TaskGroupTaskFactory(task_group=group,
                                                  start_date=setup_date,
                                                  end_date=setup_date +
                                                  dtm.timedelta(days=4))
                wf_factories.TaskGroupTaskFactory(
                    task_group=group,
                    start_date=setup_date - dtm.timedelta(days=1),
                    end_date=setup_date + dtm.timedelta(days=3))
            self.generator.activate_workflow(workflow)

        active_wf = db.session.query(Workflow).filter(
            Workflow.status == 'Active').one()
        # freeze_date is chosen so that we expect 3 cycles to be generated:
        self.assertEqual(len(active_wf.cycles), 3)
        cycle = active_wf.cycles[0]
        self.assertEqual(cycle.start_date, exp_start_date)
        self.assertEqual(cycle.end_date, exp_end_date)

    @ddt.data(
        # (setup_date, freeze_date, repeat_every, unit),
        (dtm.date(2017, 4, 28), dtm.date(2017, 2, 28), 1, Workflow.MONTH_UNIT),
        (dtm.date(2017, 3, 5), dtm.date(2017, 3, 3), 1, Workflow.DAY_UNIT),
        (dtm.date(2017, 3, 24), dtm.date(2017, 3, 10), 1, Workflow.WEEK_UNIT),
    )
    @ddt.unpack
    def test_recurring_wf_future_start_date(self, setup_date, freeze_date,
                                            repeat_every, unit):
        """Test case for 0 number of cycles for future setup date"""
        with freeze_time(freeze_date):
            with factories.single_commit():
                workflow = wf_factories.WorkflowFactory(
                    repeat_every=repeat_every, unit=unit)
                group = wf_factories.TaskGroupFactory(workflow=workflow)
                wf_factories.TaskGroupTaskFactory(task_group=group,
                                                  start_date=setup_date,
                                                  end_date=setup_date +
                                                  dtm.timedelta(days=4))
            self.generator.activate_workflow(workflow)

        active_wf = db.session.query(Workflow).filter(
            Workflow.status == 'Active').one()
        # no cycles should be generated:
        self.assertEqual(len(active_wf.cycles), 0)

    @ddt.data(
        # (expected_date, expected_num, setup_date,
        #  freeze_date, repeat_every),
        # should not exclude Jul 4
        (dtm.date(2017, 1, 2), 2, dtm.date(2016, 12, 30), dtm.date(2017, 1,
                                                                   2), 1),
        (dtm.date(2017, 1, 4), 2, dtm.date(2016, 12, 30), dtm.date(2017, 1,
                                                                   8), 3),
        (dtm.date(2017, 7, 7), 5, dtm.date(2017, 7, 3), dtm.date(2017, 7,
                                                                 8), 1),
        (dtm.date(2017, 7, 11), 3, dtm.date(2017, 7, 3), dtm.date(2017, 7,
                                                                  12), 3),
        (dtm.date(2017, 7, 10), 2, dtm.date(2017, 7, 3), dtm.date(2017, 7,
                                                                  11), 5),
        (dtm.date(2017, 7, 12), 2, dtm.date(2017, 7, 3), dtm.date(2017, 7,
                                                                  20), 7),
        (dtm.date(2017, 7, 13), 2, dtm.date(2017, 6, 1), dtm.date(2017, 7,
                                                                  31), 30),
    )
    @ddt.unpack
    def test_recurring_daily_workflow_dates(self, expected_date, expected_num,
                                            setup_date, freeze_date,
                                            repeat_every):
        """Test for correct weekdays for daily based workflows.

    When calculating the dates for daily workflows - only week working days
    are taken into account. So neither start date nor end date can fall on
    a weekend. But can fall on holiday.
    Params:
    expected - last generated cycle start date
    """
        with freeze_time(freeze_date):
            with factories.single_commit():
                workflow = wf_factories.WorkflowFactory(
                    repeat_every=repeat_every, unit=Workflow.DAY_UNIT)
                group = wf_factories.TaskGroupFactory(workflow=workflow)
                wf_factories.TaskGroupTaskFactory(
                    task_group=group,
                    start_date=setup_date,
                    end_date=setup_date + dtm.timedelta(days=repeat_every))
            self.generator.activate_workflow(workflow)

        active_wf = db.session.query(Workflow).filter(
            Workflow.status == 'Active').one()
        self.assertEqual(len(active_wf.cycles), expected_num)
        last_cycle_task = active_wf.cycles[-1].cycle_task_group_object_tasks[0]
        self.assertEqual(expected_date, last_cycle_task.start_date)
Esempio n. 12
0
class TestMonthlyWorkflowNotification(TestCase):
    """ This class contains simple one time workflow tests that are not
  in the gsheet test grid
  """
    def setUp(self):
        super(TestMonthlyWorkflowNotification, self).setUp()
        self.api = Api()
        self.wf_generator = WorkflowsGenerator()
        self.object_generator = ObjectGenerator()

        self.random_objects = self.object_generator.generate_random_objects()
        self.person_1 = self.create_user_with_role(role="Administrator")
        self.person_2 = self.create_user_with_role(role="Administrator")
        self.secondary_assignee = self.create_user_with_role(role="Reader")
        self.create_test_cases()

        def init_decorator(init):
            def new_init(self, *args, **kwargs):
                init(self, *args, **kwargs)
                if hasattr(self, "created_at"):
                    self.created_at = dt.datetime.now()

            return new_init

        all_models.Notification.__init__ = init_decorator(
            all_models.Notification.__init__)

    @patch("ggrc.notifications.common.send_email")
    def test_auto_generate_cycle(self, mock_mail):
        """Test auto recurring cycles"""

        with freeze_time("2015-04-01"):
            _, wf = self.wf_generator.generate_workflow(
                self.monthly_workflow_1)
            self.wf_generator.activate_workflow(wf)
            _, notif_data = common.get_daily_notifications()
            contact = self.person_1
            task_assignees = [contact, self.secondary_assignee]

            for user in task_assignees:
                self.assertNotIn(user.email, notif_data)

        with freeze_time("2015-04-02"):
            self.api.client.get("nightly_cron_endpoint")
            _, notif_data = common.get_daily_notifications()
            for user in task_assignees:
                self.assertNotIn(user.email, notif_data)
            start_recurring_cycles()
            _, notif_data = common.get_daily_notifications()
            for user in task_assignees:
                self.assertNotIn(user.email, notif_data)

        # cycle starts on monday - 6th, and not on 5th
        with freeze_time("2015-04-03"):
            from ggrc.login import noop
            noop.login()
            start_recurring_cycles()
            _, notif_data = common.get_daily_notifications()
            for user in task_assignees:
                self.assertIn(user.email, notif_data)

            # cycle started notifs available only for contact
            self.assertIn("cycle_started", notif_data[contact.email])

        with freeze_time("2015-04-15"):  # one day before due date
            _, notif_data = common.get_daily_notifications()
            for user in task_assignees:
                self.assertIn(user.email, notif_data)

        with freeze_time("2015-04-25"):  # due date
            _, notif_data = common.get_daily_notifications()
            for user in task_assignees:
                self.assertIn(user.email, notif_data)

    @patch("ggrc.notifications.common.send_email")
    def test_manual_generate_cycle(self, mock_mail):
        """Test generation of manual cycles"""

        with freeze_time("2015-04-01"):
            _, wf = self.wf_generator.generate_workflow(
                self.monthly_workflow_1)
            self.wf_generator.activate_workflow(wf)

        with freeze_time("2015-04-03"):
            _, cycle = self.wf_generator.generate_cycle(wf)
            contact = self.person_1
            task_assignees = [contact, self.secondary_assignee]
            _, notif_data = common.get_daily_notifications()

            # cycle started notifs available only for contact
            self.assertIn("cycle_started", notif_data[contact.email])

        with freeze_time("2015-05-03"):  # two days before due date
            _, notif_data = common.get_daily_notifications()
            for user in task_assignees:
                self.assertIn(user.email, notif_data)

    def create_test_cases(self):
        def person_dict(person_id):
            return {
                "href": "/api/people/%d" % person_id,
                "id": person_id,
                "type": "Person"
            }

        task_secondary_assignee = all_models.AccessControlRole.query.filter(
            all_models.AccessControlRole.name == "Task Secondary Assignees",
            all_models.AccessControlRole.object_type == "TaskGroupTask",
        ).one().id

        self.monthly_workflow_1 = {
            "title":
            "test monthly wf notifications",
            "notify_on_change":
            True,
            "description":
            "some test workflow",
            # admin will be current user with id == 1
            "unit":
            "month",
            "recurrences":
            True,
            "repeat_every":
            1,
            "task_groups": [{
                "title":
                "one time task group",
                "contact":
                person_dict(self.person_1.id),
                "task_group_tasks": [{
                    "title":
                    "task 1",
                    "description":
                    "some task",
                    "access_control_list": [
                        acl_helper.get_acl_json(task_secondary_assignee,
                                                self.secondary_assignee.id),
                    ],
                    "contact":
                    person_dict(self.person_1.id),
                    "start_date":
                    dt.date(2015, 4, 5),
                    "end_date":
                    dt.date(2015, 4, 25),
                }, {
                    "title":
                    "task 2",
                    "description":
                    "some task",
                    "access_control_list": [
                        acl_helper.get_acl_json(task_secondary_assignee,
                                                self.secondary_assignee.id),
                    ],
                    "contact":
                    person_dict(self.person_1.id),
                    "start_date":
                    dt.date(2015, 4, 10),
                    "end_date":
                    dt.date(2015, 4, 21),
                }],
                "task_group_objects":
                self.random_objects[:2]
            }, {
                "title":
                "another one time task group",
                "contact":
                person_dict(self.person_1.id),
                "task_group_tasks": [{
                    "title":
                    "task 1 in tg 2",
                    "description":
                    "some task",
                    "access_control_list": [
                        acl_helper.get_acl_json(task_secondary_assignee,
                                                self.secondary_assignee.id),
                    ],
                    "contact":
                    person_dict(self.person_1.id),
                    "start_date":
                    dt.date(2015, 4, 15),
                    "end_date":
                    dt.date(2015, 4, 15),
                }, {
                    "title":
                    "task 2 in tg 2",
                    "description":
                    "some task",
                    "access_control_list": [
                        acl_helper.get_acl_json(task_secondary_assignee,
                                                self.secondary_assignee.id),
                    ],
                    "contact":
                    person_dict(self.person_2.id),
                    "start_date":
                    dt.date(2015, 4, 15),
                    "end_date":
                    dt.date(2015, 4, 28),
                }],
                "task_group_objects": []
            }]
        }
Esempio n. 13
0
class TestPersonResource(TestCase, WithQueryApi):
    """Tests for special people api endpoints."""
    def setUp(self):
        super(TestPersonResource, self).setUp()
        self.client.get("/login")
        self.generator = WorkflowsGenerator()

    def test_task_count_empty(self):
        """Test query count without any workflows and tasks."""
        user = all_models.Person.query.first()
        response = self.client.get("/api/people/{}/task_count".format(user.id))
        self.assertEqual(response.json, {
            "open_task_count": 0,
            "has_overdue": False
        })

    @ddt.data(
        (True, [
            ("task 1", "Finished", 3, True),
            ("task 1", "Verified", 2, True),
            ("task 2", "Declined", 2, True),
            ("task 2", "Verified", 1, False),
            ("task 2", "Finished", 2, True),
            ("task 3", "Verified", 1, True),
            ("task 2", "Verified", 0, False),
        ]),
        (False, [
            ("task 1", "Finished", 2, True),
            ("task 2", "InProgress", 2, True),
            ("task 2", "Finished", 1, False),
            ("task 3", "Finished", 0, False),
        ]),
    )
    @ddt.unpack
    def test_task_count(self, is_verification_needed, transitions):
        """Test person task counts.

    This tests checks for correct task counts
     - with inactive workflows and
     - with overdue tasks
     - without overdue tasks
     - with finished overdue tasks

    The four checks are done in a single test due to complex differences
    between tests that make ddt cumbersome and the single test also improves
    integration test performance due to slow workflow setup stage.
    """
        # pylint: disable=too-many-locals

        user = all_models.Person.query.first()
        dummy_user = factories.PersonFactory()
        user_id = user.id
        role_id = all_models.AccessControlRole.query.filter(
            all_models.AccessControlRole.name == "Task Assignees",
            all_models.AccessControlRole.object_type == "TaskGroupTask",
        ).one().id

        one_time_workflow = {
            "title":
            "Person resource test workflow",
            "notify_on_change":
            True,
            "description":
            "some test workflow",
            "owners": [create_stub(user)],
            "is_verification_needed":
            is_verification_needed,
            "task_groups": [{
                "title":
                "one time task group",
                "contact":
                create_stub(user),
                "task_group_tasks": [
                    {
                        "title":
                        "task 1",
                        "description":
                        "some task",
                        "access_control_list": [{
                            "person": create_stub(user),
                            "ac_role_id": role_id,
                        }],
                        "start_date":
                        date(2017, 5, 5),
                        "end_date":
                        date(2017, 8, 15),
                    },
                    {
                        "title":
                        "task 2",
                        "description":
                        "some task 3",
                        "access_control_list": [{
                            "person": create_stub(user),
                            "ac_role_id": role_id,
                        }],
                        "start_date":
                        date(2017, 5, 5),
                        "end_date":
                        date(2017, 9, 16),
                    },
                    {
                        "title":
                        "task 3",
                        "description":
                        "some task 4",
                        "access_control_list": [{
                            "person": create_stub(user),
                            "ac_role_id": role_id,
                        }, {
                            "person":
                            create_stub(dummy_user),
                            "ac_role_id":
                            role_id,
                        }],
                        "start_date":
                        date(2017, 6, 5),
                        "end_date":
                        date(2017, 10, 16),
                    },
                    {
                        "title":
                        "dummy task 4",  # task should not counted
                        "description":
                        "some task 4",
                        "access_control_list": [{
                            "person":
                            create_stub(dummy_user),
                            "ac_role_id":
                            role_id,
                        }],
                        "start_date":
                        date(2017, 6, 5),
                        "end_date":
                        date(2017, 11, 17),
                    },
                    {
                        "title":
                        "dummy task 5",  # task should not counted
                        "description":
                        "some task 4",
                        "access_control_list": [{
                            "person":
                            create_stub(dummy_user),
                            "ac_role_id":
                            role_id,
                        }],
                        "start_date":
                        date(2017, 6, 5),
                        "end_date":
                        date(2017, 11, 18),
                    }
                ],
                "task_group_objects": []
            }]
        }

        inactive_workflow = {
            "title":
            "Activated workflow with archived cycles",
            "notify_on_change":
            True,
            "description":
            "Extra test workflow",
            "owners": [create_stub(user)],
            "task_groups": [{
                "title":
                "Extra task group",
                "contact":
                create_stub(user),
                "task_group_tasks": [{
                    "title":
                    "not counted existing task",
                    "description":
                    "",
                    "access_control_list": [{
                        "person": create_stub(user),
                        "ac_role_id": role_id,
                    }],
                    "start_date":
                    date(2017, 5, 5),
                    "end_date":
                    date(2017, 8, 15),
                }],
                "task_group_objects": []
            }]
        }

        with freeze_time("2017-10-16 05:09:10"):
            self.client.get("/login")
            # Activate normal one time workflow
            _, workflow = self.generator.generate_workflow(one_time_workflow)
            _, cycle = self.generator.generate_cycle(workflow)
            tasks = {t.title: t for t in cycle.cycle_task_group_object_tasks}
            _, workflow = self.generator.activate_workflow(workflow)

            # Activate and close the inactive workflow
            _, workflow = self.generator.generate_workflow(inactive_workflow)
            _, cycle = self.generator.generate_cycle(workflow)
            _, workflow = self.generator.activate_workflow(workflow)
            self.generator.modify_object(cycle, data={"is_current": False})

        with freeze_time("2017-7-16 07:09:10"):
            self.client.get("/login")
            response = self.client.get(
                "/api/people/{}/task_count".format(user_id))
            self.assertEqual(response.json, {
                "open_task_count": 3,
                "has_overdue": False
            })

        with freeze_time("2017-10-16 08:09:10"):  # same day as task 3 end date
            self.client.get("/login")
            response = self.client.get(
                "/api/people/{}/task_count".format(user_id))
            self.assertEqual(response.json, {
                "open_task_count": 3,
                "has_overdue": True
            })

            for task, status, count, overdue in transitions:
                self.generator.modify_object(tasks[task],
                                             data={"status": status})
                response = self.client.get(
                    "/api/people/{}/task_count".format(user_id))
                self.assertEqual(response.json, {
                    "open_task_count": count,
                    "has_overdue": overdue
                })

    def test_task_count_multiple_wfs(self):
        """Test task count with both verified and non verified workflows.

    This checks task counts with 4 tasks
        2017, 8, 15  - verification needed
        2017, 11, 18  - verification needed
        2017, 8, 15  - No verification needed
        2017, 11, 18  - No verification needed
    """

        user = all_models.Person.query.first()
        user_id = user.id
        role_id = all_models.AccessControlRole.query.filter(
            all_models.AccessControlRole.name == "Task Assignees",
            all_models.AccessControlRole.object_type == "TaskGroupTask",
        ).one().id
        workflow_template = {
            "title":
            "verified workflow",
            "owners": [create_stub(user)],
            "is_verification_needed":
            True,
            "task_groups": [{
                "title":
                "one time task group",
                "contact":
                create_stub(user),
                "task_group_tasks": [{
                    "title":
                    "task 1",
                    "description":
                    "some task",
                    "access_control_list": [{
                        "person": create_stub(user),
                        "ac_role_id": role_id,
                    }],
                    "start_date":
                    date(2017, 5, 5),
                    "end_date":
                    date(2017, 8, 15),
                }, {
                    "title":
                    "dummy task 5",
                    "description":
                    "some task 4",
                    "access_control_list": [{
                        "person": create_stub(user),
                        "ac_role_id": role_id,
                    }],
                    "start_date":
                    date(2017, 6, 5),
                    "end_date":
                    date(2017, 11, 18),
                }],
                "task_group_objects": []
            }]
        }

        with freeze_time("2017-10-16 05:09:10"):
            self.client.get("/login")
            verified_workflow = workflow_template.copy()
            verified_workflow["is_verification_needed"] = True
            _, workflow = self.generator.generate_workflow(verified_workflow)
            _, cycle = self.generator.generate_cycle(workflow)
            verified_tasks = {
                task.title: task
                for task in cycle.cycle_task_group_object_tasks
            }
            _, workflow = self.generator.activate_workflow(workflow)

            non_verified_workflow = workflow_template.copy()
            non_verified_workflow["is_verification_needed"] = False
            _, workflow = self.generator.generate_workflow(
                non_verified_workflow)
            _, cycle = self.generator.generate_cycle(workflow)
            non_verified_tasks = {
                task.title: task
                for task in cycle.cycle_task_group_object_tasks
            }
            _, workflow = self.generator.activate_workflow(workflow)

        with freeze_time("2017-7-16 07:09:10"):
            self.client.get("/login")
            response = self.client.get(
                "/api/people/{}/task_count".format(user_id))
            self.assertEqual(response.json, {
                "open_task_count": 4,
                "has_overdue": False
            })

        with freeze_time("2017-10-16 08:09:10"):
            self.client.get("/login")
            response = self.client.get(
                "/api/people/{}/task_count".format(user_id))
            self.assertEqual(response.json, {
                "open_task_count": 4,
                "has_overdue": True
            })

            # transition 1, task that needs verification goes to finished state. This
            # transition should not change anything
            self.generator.modify_object(verified_tasks["task 1"],
                                         data={"status": "Finished"})
            response = self.client.get(
                "/api/people/{}/task_count".format(user_id))
            self.assertEqual(response.json, {
                "open_task_count": 4,
                "has_overdue": True
            })

            # transition 2, task that needs verification goes to verified state. This
            # transition should reduce task count.
            self.generator.modify_object(verified_tasks["task 1"],
                                         data={"status": "Verified"})
            response = self.client.get(
                "/api/people/{}/task_count".format(user_id))
            self.assertEqual(response.json, {
                "open_task_count": 3,
                "has_overdue": True
            })

            # transition 3, task that does not need verification goes into Finished
            # state. This transition should reduce task count and remove all overdue
            # tasks
            self.generator.modify_object(non_verified_tasks["task 1"],
                                         data={"status": "Finished"})
            response = self.client.get(
                "/api/people/{}/task_count".format(user_id))
            self.assertEqual(response.json, {
                "open_task_count": 2,
                "has_overdue": False
            })
class TestWorkflowsCycleGeneration(TestCase):
  """
  Tests for Cycle generation logic
  """
  def setUp(self):
    super(TestWorkflowsCycleGeneration, self).setUp()
    self.api = Api()
    self.generator = WorkflowsGenerator()
    self.object_generator = ObjectGenerator()

  def tearDown(self):
    pass

  @ddt.data(
      True, False
  )
  def test_recurring_without_tgts_skip(self, has_tg):
    """Test that Active Workflow without TGTs is skipped on cron job"""
    with freeze_time(dtm.date(2017, 9, 25)):
      with factories.single_commit():
        workflow = wf_factories.WorkflowFactory(repeat_every=1,
                                                unit=Workflow.MONTH_UNIT)
        workflow_id = workflow.id
        group = wf_factories.TaskGroupFactory(workflow=workflow)
        wf_factories.TaskGroupTaskFactory(
            task_group=group,
            start_date=dtm.date(2017, 9, 26),
            end_date=dtm.date(2017, 9, 26) + dtm.timedelta(days=4))
      self.generator.activate_workflow(workflow)
      active_wf = db.session.query(Workflow).filter(
          Workflow.id == workflow_id).one()
      self.assertEqual(active_wf.next_cycle_start_date, dtm.date(2017, 9, 26))
      self.assertEqual(active_wf.recurrences, True)
      self.assertEqual(len(active_wf.cycles), 0)
      TaskGroupTask.query.delete()
      if not has_tg:
        TaskGroup.query.delete()
      db.session.commit()

    with freeze_time(dtm.date(2017, 10, 25)):
      start_recurring_cycles()
      active_wf = db.session.query(Workflow).filter(
          Workflow.id == workflow_id).one()
      self.assertEqual(active_wf.next_cycle_start_date, dtm.date(2017, 9, 26))
      self.assertEqual(active_wf.recurrences, True)
      self.assertEqual(len(active_wf.cycles), 0)

  @ddt.data(
      # (expected, setup_date),
      (dtm.date(2017, 2, 28), dtm.date(2017, 2, 28)),
      (dtm.date(2017, 3, 3), dtm.date(2017, 3, 3)),
      (dtm.date(2017, 8, 4), dtm.date(2017, 8, 5)),
      (dtm.date(2017, 7, 21), dtm.date(2017, 7, 22)),
  )
  @ddt.unpack
  def test_one_time_wf_start_date_shifting(self, expected, setup_date):
    """Test case for correct cycle task start_ dates for one_time wf"""
    with factories.single_commit():
      workflow = wf_factories.WorkflowFactory()
      group = wf_factories.TaskGroupFactory(workflow=workflow)
      wf_factories.TaskGroupTaskFactory(
          task_group=group,
          start_date=setup_date,
          end_date=setup_date + dtm.timedelta(days=4))
    self.generator.generate_cycle(workflow)
    self.generator.activate_workflow(workflow)
    active_wf = db.session.query(Workflow).filter(
        Workflow.status == 'Active').one()
    self.assertEqual(1,
                     len(active_wf.cycles[0].cycle_task_group_object_tasks))
    cycle_task = active_wf.cycles[0].cycle_task_group_object_tasks[0]
    adj_start_date = cycle_task.start_date
    self.assertEqual(expected, adj_start_date)

  @ddt.data(
      # (expected, end_date),
      (dtm.date(2017, 2, 28), dtm.date(2017, 2, 28)),
      (dtm.date(2017, 3, 3), dtm.date(2017, 3, 3)),
      (dtm.date(2017, 8, 4), dtm.date(2017, 8, 5)),
      (dtm.date(2017, 7, 21), dtm.date(2017, 7, 22)),
  )
  @ddt.unpack
  def test_one_time_wf_end_date_shifting(self, expected, setup_date):
    """Test case for correct cycle task end_ dates for one_time wf"""
    with factories.single_commit():
      workflow = wf_factories.WorkflowFactory()
      group = wf_factories.TaskGroupFactory(workflow=workflow)
      wf_factories.TaskGroupTaskFactory(
          task_group=group,
          start_date=setup_date - dtm.timedelta(days=4),
          end_date=setup_date)
    self.generator.generate_cycle(workflow)
    self.generator.activate_workflow(workflow)
    active_wf = db.session.query(Workflow).filter(
        Workflow.status == 'Active').one()
    self.assertEqual(1,
                     len(active_wf.cycles[0].cycle_task_group_object_tasks))
    cycle_task = active_wf.cycles[0].cycle_task_group_object_tasks[0]
    adj_end_date = cycle_task.end_date
    self.assertEqual(expected, adj_end_date)

  # pylint: disable=too-many-arguments
  @ddt.data(
      # (expected, setup_date, freeze_date, repeat_every, unit),
      (dtm.date(2017, 3, 31), dtm.date(2017, 2, 28), dtm.date(2017, 4, 1), 1,
       Workflow.MONTH_UNIT),
      (dtm.date(2016, 3, 28), dtm.date(2016, 2, 28), dtm.date(2016, 4, 1), 1,
       Workflow.MONTH_UNIT),
      (dtm.date(2017, 2, 28), dtm.date(2017, 1, 31), dtm.date(2017, 3, 31), 1,
       Workflow.MONTH_UNIT),
      (dtm.date(2017, 3, 17), dtm.date(2017, 3, 10), dtm.date(2017, 3, 24), 1,
       Workflow.WEEK_UNIT),
      (dtm.date(2017, 2, 28), dtm.date(2016, 2, 29), dtm.date(2017, 3, 29), 12,
       Workflow.MONTH_UNIT),
      (dtm.date(2017, 4, 28), dtm.date(2017, 1, 31), dtm.date(2017, 5, 31), 3,
       Workflow.MONTH_UNIT),
  )
  @ddt.unpack
  def test_recurring_wf_start_date_shifting(self, expected, setup_date,
                                            freeze_date, repeat_every, unit):
    """Test case for correct next cycle task start_date for recurring wf"""
    with freeze_time(freeze_date):
      with factories.single_commit():
        workflow = wf_factories.WorkflowFactory(repeat_every=repeat_every,
                                                unit=unit)
        group = wf_factories.TaskGroupFactory(workflow=workflow)
        wf_factories.TaskGroupTaskFactory(
            task_group=group,
            start_date=setup_date,
            end_date=setup_date + dtm.timedelta(days=4))
      self.generator.activate_workflow(workflow)

    active_wf = db.session.query(Workflow).filter(
        Workflow.status == 'Active').one()
    cycle_task = active_wf.cycles[1].cycle_task_group_object_tasks[0]
    adj_start_date = cycle_task.start_date
    self.assertEqual(expected, adj_start_date)

  @ddt.data(
      # today is dtm.date(2017, 8, 15), task repeat every
      # (setup_start_date, update_start_date, expected_date),
      (dtm.date(2017, 1, 15), dtm.date(2017, 1, 1), dtm.date(2017, 9, 1)),
      (dtm.date(2017, 1, 15), dtm.date(2017, 1, 5), dtm.date(2017, 9, 5)),
      # new cycle starts on weekend that's why it moves to friday
      (dtm.date(2017, 1, 15), dtm.date(2017, 1, 10), dtm.date(2017, 9, 8)),
      (dtm.date(2017, 1, 15), dtm.date(2017, 8, 15), dtm.date(2017, 9, 15)),
      # new cycle starts on weekend that's why it moves to friday
      (dtm.date(2017, 1, 15), dtm.date(2017, 1, 20), dtm.date(2017, 8, 18)),
      (dtm.date(2017, 1, 15), dtm.date(2017, 1, 25), dtm.date(2017, 8, 25)),
      (dtm.date(2017, 8, 16), dtm.date(2017, 8, 14), dtm.date(2017, 9, 14)),
      (dtm.date(2017, 8, 16), dtm.date(2017, 8, 18), dtm.date(2017, 8, 18)),
      (dtm.date(2017, 8, 14), dtm.date(2017, 8, 18), dtm.date(2017, 8, 18)),
  )
  @ddt.unpack
  def test_recalculate_date(self,
                            setup_start_date,
                            update_start_date,
                            expected_date):
    """Recalculate next cycle start date"""
    with freeze_time(dtm.date(2017, 8, 15)):
      with factories.single_commit():
        workflow = wf_factories.WorkflowFactory(repeat_every=1,
                                                status=Workflow.DRAFT,
                                                unit=Workflow.MONTH_UNIT)
        group = wf_factories.TaskGroupFactory(workflow=workflow)
        task_id = wf_factories.TaskGroupTaskFactory(
            task_group=group,
            start_date=setup_start_date,
            end_date=setup_start_date + dtm.timedelta(days=4)).id
      self.generator.activate_workflow(workflow)
      self.api.put(TaskGroupTask.query.get(task_id),
                   {"start_date": update_start_date,
                    "end_date": update_start_date + dtm.timedelta(4)})
    active_wf = db.session.query(Workflow).filter(
        Workflow.status == 'Active').one()
    self.assertEqual(expected_date, active_wf.next_cycle_start_date)

  def test_recalculate_date_not_started(self):
    """Changing start_date on notstarted workflow will
       not affect next_cycle_start_date"""
    setup_start_date = dtm.date(2017, 1, 15)
    update_start_date = dtm.date(2017, 1, 1)
    with freeze_time(dtm.date(2017, 8, 15)):
      with factories.single_commit():
        workflow = wf_factories.WorkflowFactory(repeat_every=1,
                                                status=Workflow.DRAFT,
                                                unit=Workflow.MONTH_UNIT)
        group = wf_factories.TaskGroupFactory(workflow=workflow)
        task_id = wf_factories.TaskGroupTaskFactory(
            task_group=group,
            start_date=setup_start_date,
            end_date=setup_start_date + dtm.timedelta(days=4)).id
        workflow_id = workflow.id
    self.api.put(TaskGroupTask.query.get(task_id),
                 {"start_date": update_start_date,
                  "end_date": update_start_date + dtm.timedelta(4)})
    self.assertIsNone(Workflow.query.get(workflow_id).next_cycle_start_date)

  @ddt.data(
      # (setup_date, freeze_date, repeat_every, unit),
      (dtm.date(2017, 2, 28), dtm.date(2017, 4, 28), 1, Workflow.MONTH_UNIT),
      (dtm.date(2017, 3, 10), dtm.date(2017, 3, 24), 1, Workflow.WEEK_UNIT),
  )
  @ddt.unpack
  def test_recurring_wf_start_date_and_cycles(self, setup_date, freeze_date,
                                              repeat_every, unit):
    """Test case for correct cycle start date and number of cycles"""
    with freeze_time(freeze_date):
      with factories.single_commit():
        workflow = wf_factories.WorkflowFactory(repeat_every=repeat_every,
                                                unit=unit)
        group = wf_factories.TaskGroupFactory(workflow=workflow)
        wf_factories.TaskGroupTaskFactory(
            task_group=group,
            start_date=setup_date,
            end_date=setup_date + dtm.timedelta(days=4))
      self.generator.activate_workflow(workflow)

    active_wf = db.session.query(Workflow).filter(
        Workflow.status == 'Active').one()
    # freeze_date is chosen so that we expect 3 cycles to be generated:
    self.assertEqual(len(active_wf.cycles), 3)
    cycle_task = active_wf.cycles[0].cycle_task_group_object_tasks[0]
    adj_start_date = cycle_task.start_date
    self.assertEqual(setup_date, adj_start_date)

  @ddt.data(
      # (setup_date, freeze_date, repeat_every, unit,
      #  exp_start_date, exp_end_date),
      (dtm.date(2017, 2, 28), dtm.date(2017, 4, 28), 1, Workflow.MONTH_UNIT,
       dtm.date(2017, 2, 27), dtm.date(2017, 3, 3)),
      (dtm.date(2017, 3, 10), dtm.date(2017, 3, 24), 1, Workflow.WEEK_UNIT,
       dtm.date(2017, 3, 9), dtm.date(2017, 3, 14)),
  )
  @ddt.unpack
  def test_recurring_wf_start_end_cycle_dates(self,
                                              setup_date,
                                              freeze_date,
                                              repeat_every,
                                              unit,
                                              exp_start_date,
                                              exp_end_date):
    """Test case for correct cycle start and end dates"""
    with freeze_time(freeze_date):
      with factories.single_commit():
        workflow = wf_factories.WorkflowFactory(repeat_every=repeat_every,
                                                unit=unit)
        group = wf_factories.TaskGroupFactory(workflow=workflow)
        wf_factories.TaskGroupTaskFactory(
            task_group=group,
            start_date=setup_date,
            end_date=setup_date + dtm.timedelta(days=4))
        wf_factories.TaskGroupTaskFactory(
            task_group=group,
            start_date=setup_date - dtm.timedelta(days=1),
            end_date=setup_date + dtm.timedelta(days=3))
      self.generator.activate_workflow(workflow)

    active_wf = db.session.query(Workflow).filter(
        Workflow.status == 'Active').one()
    # freeze_date is chosen so that we expect 3 cycles to be generated:
    self.assertEqual(len(active_wf.cycles), 3)
    cycle = active_wf.cycles[0]
    self.assertEqual(cycle.start_date, exp_start_date)
    self.assertEqual(cycle.end_date, exp_end_date)

  @ddt.data(
      # (setup_date, freeze_date, repeat_every, unit),
      (dtm.date(2017, 4, 28), dtm.date(2017, 2, 28), 1, Workflow.MONTH_UNIT),
      (dtm.date(2017, 3, 5), dtm.date(2017, 3, 3), 1, Workflow.DAY_UNIT),
      (dtm.date(2017, 3, 24), dtm.date(2017, 3, 10), 1, Workflow.WEEK_UNIT),
  )
  @ddt.unpack
  def test_recurring_wf_future_start_date(self, setup_date, freeze_date,
                                          repeat_every, unit):
    """Test case for 0 number of cycles for future setup date"""
    with freeze_time(freeze_date):
      with factories.single_commit():
        workflow = wf_factories.WorkflowFactory(repeat_every=repeat_every,
                                                unit=unit)
        group = wf_factories.TaskGroupFactory(workflow=workflow)
        wf_factories.TaskGroupTaskFactory(
            task_group=group,
            start_date=setup_date,
            end_date=setup_date + dtm.timedelta(days=4))
      self.generator.activate_workflow(workflow)

    active_wf = db.session.query(Workflow).filter(
        Workflow.status == 'Active').one()
    # no cycles should be generated:
    self.assertEqual(len(active_wf.cycles), 0)

  @ddt.data(
      # (expected_date, expected_num, setup_date,
      #  freeze_date, repeat_every),
      # should not exclude Jul 4
      (dtm.date(2017, 1, 2), 2, dtm.date(2016, 12, 30),
       dtm.date(2017, 1, 2), 1),
      (dtm.date(2017, 1, 4), 2, dtm.date(2016, 12, 30),
       dtm.date(2017, 1, 8), 3),
      (dtm.date(2017, 7, 7), 5, dtm.date(2017, 7, 3),
       dtm.date(2017, 7, 8), 1),
      (dtm.date(2017, 7, 11), 3, dtm.date(2017, 7, 3),
       dtm.date(2017, 7, 12), 3),
      (dtm.date(2017, 7, 10), 2, dtm.date(2017, 7, 3),
       dtm.date(2017, 7, 11), 5),
      (dtm.date(2017, 7, 12), 2, dtm.date(2017, 7, 3),
       dtm.date(2017, 7, 20), 7),
      (dtm.date(2017, 7, 13), 2, dtm.date(2017, 6, 1),
       dtm.date(2017, 7, 31), 30),
  )
  @ddt.unpack
  def test_recurring_daily_workflow_dates(self,
                                          expected_date,
                                          expected_num,
                                          setup_date,
                                          freeze_date,
                                          repeat_every):
    """
    Test for correct weekdays for daily based workflows
    When calculating the dates for daily workflows - only week working days
    are taken into account. So neither start date nor end date can fall on
    a weekend. But can fall on holiday.
    Params:
    expected - last generated cycle start date
    """
    with freeze_time(freeze_date):
      with factories.single_commit():
        workflow = wf_factories.WorkflowFactory(repeat_every=repeat_every,
                                                unit=Workflow.DAY_UNIT)
        group = wf_factories.TaskGroupFactory(workflow=workflow)
        wf_factories.TaskGroupTaskFactory(
            task_group=group,
            start_date=setup_date,
            end_date=setup_date + dtm.timedelta(days=repeat_every))
      self.generator.activate_workflow(workflow)

    active_wf = db.session.query(Workflow).filter(
        Workflow.status == 'Active').one()
    self.assertEqual(len(active_wf.cycles), expected_num)
    last_cycle_task = active_wf.cycles[-1].cycle_task_group_object_tasks[0]
    self.assertEqual(expected_date, last_cycle_task.start_date)
class TestTaskDueNotifications(TestNotifications):
    """Test suite for task due soon/today notifications."""
    def setUp(self):
        super(TestTaskDueNotifications, self).setUp()
        self.api = Api()
        self.wf_generator = WorkflowsGenerator()
        self.object_generator = ObjectGenerator()
        all_models.Notification.query.delete()

        self._fix_notification_init()

        self.random_objects = self.object_generator.generate_random_objects(2)
        self.user = self.create_user_with_role(role="Administrator")
        self.secondary_assignee = self.create_user_with_role(role="Reader")

        task_assignee_role_id = all_models.AccessControlRole.query.filter(
            all_models.AccessControlRole.name == "Task Assignees",
            all_models.AccessControlRole.object_type == "TaskGroupTask",
        ).one().id

        task_secondary_assignee = all_models.AccessControlRole.query.filter(
            all_models.AccessControlRole.name == "Task Secondary Assignees",
            all_models.AccessControlRole.object_type == "TaskGroupTask",
        ).one().id

        self.one_time_workflow = {
            "title":
            "one time test workflow",
            "notify_on_change":
            True,
            "description":
            "some test workflow",
            "is_verification_needed":
            False,
            # admin will be current user with id == 1
            "task_groups": [{
                "title":
                "one time task group",
                "contact": {
                    "href": "/api/people/{}".format(self.user.id),
                    "id": self.user.id,
                    "type": "Person",
                },
                "task_group_tasks": [{
                    "title":
                    "task 1",
                    "description":
                    "some task",
                    "access_control_list": [
                        acl_helper.get_acl_json(task_assignee_role_id,
                                                self.user.id),
                        acl_helper.get_acl_json(task_secondary_assignee,
                                                self.secondary_assignee.id),
                    ],
                    "start_date":
                    date(2017, 5, 15),
                    "end_date":
                    date(2017, 6, 11),
                }, {
                    "title":
                    "task 2",
                    "description":
                    "some task 2",
                    "access_control_list": [
                        acl_helper.get_acl_json(task_assignee_role_id,
                                                self.user.id),
                        acl_helper.get_acl_json(task_secondary_assignee,
                                                self.secondary_assignee.id),
                    ],
                    "start_date":
                    date(2017, 5, 8),
                    "end_date":
                    date(2017, 6, 12),
                }, {
                    "title":
                    "task 3",
                    "description":
                    "some task 3",
                    "access_control_list": [
                        acl_helper.get_acl_json(task_assignee_role_id,
                                                self.user.id),
                        acl_helper.get_acl_json(task_secondary_assignee,
                                                self.secondary_assignee.id),
                    ],
                    "start_date":
                    date(2017, 5, 31),
                    "end_date":
                    date(2017, 6, 13),
                }, {
                    "title":
                    "task 4",
                    "description":
                    "some task 4",
                    "access_control_list": [
                        acl_helper.get_acl_json(task_assignee_role_id,
                                                self.user.id),
                        acl_helper.get_acl_json(task_secondary_assignee,
                                                self.secondary_assignee.id),
                    ],
                    "start_date":
                    date(2017, 6, 2),
                    "end_date":
                    date(2017, 6, 14),
                }, {
                    "title":
                    "task 5",
                    "description":
                    "some task 5",
                    "access_control_list": [
                        acl_helper.get_acl_json(task_assignee_role_id,
                                                self.user.id),
                        acl_helper.get_acl_json(task_secondary_assignee,
                                                self.secondary_assignee.id),
                    ],
                    "start_date":
                    date(2017, 6, 8),
                    "end_date":
                    date(2017, 6, 15),
                }],
                "task_group_objects":
                self.random_objects
            }]
        }

    @ddt.unpack
    @ddt.data(
        ("2017-06-12 12:12:12", ["task 1"], ["task 2"], ["task 3"]),
        ("2017-06-13 13:13:13", ["task 1", "task 2"], ["task 3"], ["task 4"]),
    )
    @patch("ggrc.notifications.common.send_email")
    def test_creating_obsolete_notifications(self, fake_now, expected_overdue,
                                             expected_due_today,
                                             expected_due_in, _):
        """Notifications already obsolete on creation date should not be created.
    """
        # pylint: disable=invalid-name
        with freeze_time("2017-06-12 09:39:32"):
            tmp = self.one_time_workflow.copy()
            _, workflow = self.wf_generator.generate_workflow(tmp)
            self.wf_generator.generate_cycle(workflow)
            response, workflow = self.wf_generator.activate_workflow(workflow)
            self.assert200(response)

        task_assignees = [self.user, self.secondary_assignee]
        with freeze_time(fake_now):
            # mark all yeasterday notifications as sent
            all_models.Notification.query.filter(
                sa.func.DATE(all_models.Notification.send_on) < date.today()
            ).update(
                {
                    all_models.Notification.sent_at:
                    datetime.now() - timedelta(1)
                },
                synchronize_session="fetch")

            _, notif_data = common.get_daily_notifications()
            for user in task_assignees:
                user_notifs = notif_data.get(user.email, {})

                actual_overdue = [
                    n['title']
                    for n in user_notifs.get("task_overdue", {}).itervalues()
                ]
                actual_overdue.sort()
                self.assertEqual(actual_overdue, expected_overdue)

                self.assertEqual([
                    n['title']
                    for n in user_notifs.get("due_today", {}).itervalues()
                ], expected_due_today)

                self.assertEqual([
                    n['title']
                    for n in user_notifs.get("due_in", {}).itervalues()
                ], expected_due_in)
Esempio n. 16
0
class TestOneTimeWorkflowNotification(TestCase):
    """ Tests are defined in the g-sheet test grid under:
    WF EMAILS for unit tests (middle level)
  """
    def setUp(self):
        super(TestOneTimeWorkflowNotification, self).setUp()
        self.api = Api()
        self.wf_generator = WorkflowsGenerator()
        self.object_generator = ObjectGenerator()

        self.random_objects = self.object_generator.generate_random_objects()
        self.random_people = [
            self.object_generator.generate_person(user_role="Administrator")[1]
            for _ in range(5)
        ]
        self.create_test_cases()

        self.create_users()

        db.session.query(Notification).delete()

        def init_decorator(init):
            def new_init(self, *args, **kwargs):
                init(self, *args, **kwargs)
                if hasattr(self, "created_at"):
                    self.created_at = datetime.now()

            return new_init

        Notification.__init__ = init_decorator(Notification.__init__)

    def tearDown(self):
        db.session.query(Notification).delete()

    def short_dict(self, obj, plural):
        return {
            "href": "/api/%s/%d" % (plural, obj.id),
            "id": obj.id,
            "type": obj.__class__.__name__,
        }

    def test_one_time_wf(self):
        # setup
        with freeze_time("2015-04-07 03:21:34"):
            wf_response, wf = self.wf_generator.generate_workflow(
                data={
                    "owners":
                    None,  # owner will be the current user
                    "notify_on_change":
                    True,  # force real time updates
                    "title":
                    "One-time WF",
                    "notify_custom_message":
                    textwrap.dedent("""\
              Hi all.
              Did you know that Irelnd city namd Newtownmountkennedy has 19
              letters? But it's not the longest one. The recordsman is the
              city in New Zealand that contains 97 letter."""),
                })

            _, tg = self.wf_generator.generate_task_group(
                wf,
                data={
                    "title": "TG #1 for the One-time WF",
                    "contact": self.short_dict(self.tgassignee1, "people"),
                })

            self.wf_generator.generate_task_group_task(
                tg, {
                    "title": "task #1 for one-time workflow",
                    "contact": self.short_dict(self.member1, "people"),
                    "start_date": "04/07/2015",
                    "end_date": "04/15/2015",
                })

            self.wf_generator.generate_task_group_object(
                tg, self.random_objects[0])
            self.wf_generator.generate_task_group_object(
                tg, self.random_objects[1])

        # test
        with freeze_time("2015-04-07 03:21:34"):
            cycle_response, cycle = self.wf_generator.generate_cycle(wf)
            self.wf_generator.activate_workflow(wf)

            common.get_daily_notifications()

    def create_test_cases(self):
        def person_dict(person_id):
            return {
                "href": "/api/people/%d" % person_id,
                "id": person_id,
                "type": "Person"
            }

        self.one_time_workflow_1 = {
            "title":
            "one time test workflow",
            "description":
            "some test workflow",
            "owners": [person_dict(self.random_people[3].id)],
            "task_groups": [
                {
                    "title":
                    "one time task group",
                    "task_group_tasks": [
                        {
                            "title": "task 1",
                            "description": "some task",
                            "contact": person_dict(self.random_people[0].id),
                            "start_date": date(2015, 5, 1),  # friday
                            "end_date": date(2015, 5, 5),
                        },
                        {
                            "title": "task 2",
                            "description": "some task",
                            "contact": person_dict(self.random_people[1].id),
                            "start_date": date(2015, 5, 4),
                            "end_date": date(2015, 5, 7),
                        }
                    ],
                    "task_group_objects":
                    self.random_objects[:2]
                },
                {
                    "title":
                    "another one time task group",
                    "task_group_tasks": [
                        {
                            "title": "task 1 in tg 2",
                            "description": "some task",
                            "contact": person_dict(self.random_people[0].id),
                            "start_date": date(2015, 5, 8),  # friday
                            "end_date": date(2015, 5, 12),
                        },
                        {
                            "title": "task 2 in tg 2",
                            "description": "some task",
                            "contact": person_dict(self.random_people[2].id),
                            "start_date": date(2015, 5, 1),  # friday
                            "end_date": date(2015, 5, 5),
                        }
                    ],
                    "task_group_objects": []
                }
            ]
        }

    def create_users(self):
        _, self.owner1 = self.object_generator.generate_person(
            # data={"name": "User1 Owner1", "email": "*****@*****.**"},
            user_role="Administrator")
        _, self.tgassignee1 = self.object_generator.generate_person(
            # data={"name": "User2 TGassignee1",
            #       "email": "*****@*****.**"},
            user_role="Administrator")
        _, self.member1 = self.object_generator.generate_person(
            # data={"name": "User3 Member1", "email": "*****@*****.**"},
            user_role="Administrator")
        _, self.member2 = self.object_generator.generate_person(
            # data={"name": "User4 Member2", "email": "*****@*****.**"},
            user_role="Administrator")
Esempio n. 17
0
class TestNotificationsHistory(TestCase):
  """Tests notifications history cron job."""
  def setUp(self):
    """Set up."""
    super(TestNotificationsHistory, self).setUp()
    self.wf_generator = WorkflowsGenerator()
    self.object_generator = ObjectGenerator()
    _, self.user = self.object_generator.generate_person(
        user_role="Administrator")

    self._create_test_cases()

  @patch("ggrc.notifications.common.send_email")
  def test_move_notif_to_history(self, mocked_send_email):
    """Tests moving notifications to history table."""
    # pylint: disable=unused-argument
    # pylint: disable=unused-variable
    date_time = "2018-06-10 16:55:15"
    with freeze_time(date_time):
      _, workflow = self.wf_generator.generate_workflow(
          self.one_time_workflow)

      _, cycle = self.wf_generator.generate_cycle(workflow)
      self.wf_generator.activate_workflow(workflow)

      notif_to_be_sent_ids = db.session.query(Notification.id).filter(and_(
          Notification.sent_at == None,  # noqa
          Notification.send_on == date.today(),
          Notification.repeating == false()
      )).all()

      self.assertEqual(db.session.query(Notification).count(), 5)
      self.assertEqual(db.session.query(NotificationHistory).count(), 0)

      with freeze_time(date_time):
        common.send_daily_digest_notifications()

      notif_count = db.session.query(Notification).filter(
          Notification.id.in_(notif_to_be_sent_ids)
      ).count()

      notif_history_count = db.session.query(NotificationHistory).count()

      self.assertEqual(notif_count, 0)
      self.assertEqual(notif_history_count, len(notif_to_be_sent_ids))

  def _create_test_cases(self):
    """Create configuration to use for generating a new workflow."""
    role_id = all_models.AccessControlRole.query.filter(
        all_models.AccessControlRole.name == "Task Assignees",
        all_models.AccessControlRole.object_type == "TaskGroupTask",
    ).one().id
    self.one_time_workflow = {
        "title": "one time test workflow",
        "notify_on_change": True,
        "description": "some test workflow",
        "task_groups": [{
            "title": "one time task group",
            "contact": {
                "href": "/api/people/" + str(self.user.id),
                "id": self.user.id,
                "type": "Person"
            },
            "task_group_tasks": [{
                "title": "task 1",
                "description": "some task",
                "start_date": date(2018, 6, 10),
                "end_date": date(2018, 7, 10),
                "access_control_list": [
                    acl_helper.get_acl_json(role_id, self.user.id)],
            }]
        }]
    }
class TestNotificationsHistory(TestCase):
    """Tests notifications history cron job."""
    def setUp(self):
        """Set up."""
        super(TestNotificationsHistory, self).setUp()
        self.wf_generator = WorkflowsGenerator()
        self.object_generator = ObjectGenerator()
        _, self.user = self.object_generator.generate_person(
            user_role="Administrator")

        self._create_test_cases()

    @patch("ggrc.notifications.common.send_email")
    def test_move_notif_to_history(self, mocked_send_email):
        """Tests moving notifications to history table."""
        # pylint: disable=unused-argument
        # pylint: disable=unused-variable
        date_time = "2018-06-10 16:55:15"
        with freeze_time(date_time):
            _, workflow = self.wf_generator.generate_workflow(
                self.one_time_workflow)

            _, cycle = self.wf_generator.generate_cycle(workflow)
            self.wf_generator.activate_workflow(workflow)

            notif_to_be_sent_ids = db.session.query(Notification.id).filter(
                and_(
                    Notification.sent_at == None,  # noqa
                    Notification.send_on == date.today(),
                    Notification.repeating == false())).all()

            self.assertEqual(db.session.query(Notification).count(), 5)
            self.assertEqual(db.session.query(NotificationHistory).count(), 0)

            with freeze_time(date_time):
                common.send_daily_digest_notifications()

            notif_count = db.session.query(Notification).filter(
                Notification.id.in_(notif_to_be_sent_ids)).count()

            notif_history_count = db.session.query(NotificationHistory).count()

            self.assertEqual(notif_count, 0)
            self.assertEqual(notif_history_count, len(notif_to_be_sent_ids))

    def _create_test_cases(self):
        """Create configuration to use for generating a new workflow."""
        role_id = all_models.AccessControlRole.query.filter(
            all_models.AccessControlRole.name == "Task Assignees",
            all_models.AccessControlRole.object_type == "TaskGroupTask",
        ).one().id
        self.one_time_workflow = {
            "title":
            "one time test workflow",
            "notify_on_change":
            True,
            "description":
            "some test workflow",
            "task_groups": [{
                "title":
                "one time task group",
                "contact": {
                    "href": "/api/people/" + str(self.user.id),
                    "id": self.user.id,
                    "type": "Person"
                },
                "task_group_tasks": [{
                    "title":
                    "task 1",
                    "description":
                    "some task",
                    "start_date":
                    date(2018, 6, 10),
                    "end_date":
                    date(2018, 7, 10),
                    "access_control_list":
                    [acl_helper.get_acl_json(role_id, self.user.id)],
                }]
            }]
        }
class TestOneTimeWorkflowNotification(TestCase):

  """ Tests are defined in the g-sheet test grid under:
    WF EMAILS for unit tests (middle level)
  """

  def setUp(self):
    super(TestOneTimeWorkflowNotification, self).setUp()
    self.api = Api()
    self.wf_generator = WorkflowsGenerator()
    self.object_generator = ObjectGenerator()

    self.random_objects = self.object_generator.generate_random_objects()
    self.random_people = [
        self.object_generator.generate_person(user_role="Administrator")[1]
        for _ in range(5)]
    self.create_test_cases()

    self.create_users()

    db.session.query(Notification).delete()

    def init_decorator(init):
      def new_init(self, *args, **kwargs):
        init(self, *args, **kwargs)
        if hasattr(self, "created_at"):
          self.created_at = datetime.now()
      return new_init

    Notification.__init__ = init_decorator(Notification.__init__)

  def tearDown(self):
    db.session.query(Notification).delete()

  def short_dict(self, obj, plural):
    return {
        "href": "/api/%s/%d" % (plural, obj.id),
        "id": obj.id,
        "type": obj.__class__.__name__,
    }

  def test_one_time_wf(self):
    # setup
    with freeze_time("2015-04-07 03:21:34"):
      wf_response, wf = self.wf_generator.generate_workflow(data={
          # admin will be the current user
          "notify_on_change": True,  # force real time updates
          "title": "One-time WF",
          "notify_custom_message": textwrap.dedent("""\
              Hi all.
              Did you know that Irelnd city namd Newtownmountkennedy has 19
              letters? But it's not the longest one. The recordsman is the
              city in New Zealand that contains 97 letter."""),
      })

      _, tg = self.wf_generator.generate_task_group(wf, data={
          "title": "TG #1 for the One-time WF",
          "contact": self.short_dict(self.tgassignee1, "people"),
      })

      self.wf_generator.generate_task_group_task(tg, {
          "title": "task #1 for one-time workflow",
          "contact": self.short_dict(self.member1, "people"),
          "start_date": "04/07/2015",
          "end_date": "04/15/2015",
      })

      self.wf_generator.generate_task_group_object(tg, self.random_objects[0])
      self.wf_generator.generate_task_group_object(tg, self.random_objects[1])

    # test
    with freeze_time("2015-04-07 03:21:34"):
      cycle_response, cycle = self.wf_generator.generate_cycle(wf)
      self.wf_generator.activate_workflow(wf)

      common.get_daily_notifications()

  def create_test_cases(self):
    def person_dict(person_id):
      return {
          "href": "/api/people/%d" % person_id,
          "id": person_id,
          "type": "Person"
      }

    self.one_time_workflow_1 = {
        "title": "one time test workflow",
        "description": "some test workflow",
        # admin will be current user with id == 1
        "task_groups": [{
            "title": "one time task group",
            "task_group_tasks": [{
                "title": "task 1",
                "description": "some task",
                "contact": person_dict(self.random_people[0].id),
                "start_date": date(2015, 5, 1),  # friday
                "end_date": date(2015, 5, 5),
            }, {
                "title": "task 2",
                "description": "some task",
                "contact": person_dict(self.random_people[1].id),
                "start_date": date(2015, 5, 4),
                "end_date": date(2015, 5, 7),
            }],
            "task_group_objects": self.random_objects[:2]
        }, {
            "title": "another one time task group",
            "task_group_tasks": [{
                "title": "task 1 in tg 2",
                "description": "some task",
                "contact": person_dict(self.random_people[0].id),
                "start_date": date(2015, 5, 8),  # friday
                "end_date": date(2015, 5, 12),
            }, {
                "title": "task 2 in tg 2",
                "description": "some task",
                "contact": person_dict(self.random_people[2].id),
                "start_date": date(2015, 5, 1),  # friday
                "end_date": date(2015, 5, 5),
            }],
            "task_group_objects": []
        }]
    }

  def create_users(self):
    _, self.admin1 = self.object_generator.generate_person(
        # data={"name": "User1 Admin1", "email": "*****@*****.**"},
        user_role="Administrator")
    _, self.tgassignee1 = self.object_generator.generate_person(
        # data={"name": "User2 TGassignee1",
        #       "email": "*****@*****.**"},
        user_role="Administrator")
    _, self.member1 = self.object_generator.generate_person(
        # data={"name": "User3 Member1", "email": "*****@*****.**"},
        user_role="Administrator")
    _, self.member2 = self.object_generator.generate_person(
        # data={"name": "User4 Member2", "email": "*****@*****.**"},
        user_role="Administrator")
class TestOneTimeWfEndDateChange(TestCase):
    """ This class contains simple one time workflow tests that are not
  in the gsheet test grid
  """
    def setUp(self):
        super(TestOneTimeWfEndDateChange, self).setUp()
        self.api = Api()
        self.wf_generator = WorkflowsGenerator()
        self.object_generator = ObjectGenerator()
        all_models.Notification.query.delete()

        self.random_objects = self.object_generator.generate_random_objects(2)
        self.user = self.create_user_with_role(role="Administrator")
        self.secondary_assignee = self.create_user_with_role(role="Reader")
        self.create_test_cases()

        def init_decorator(init):
            def new_init(self, *args, **kwargs):
                init(self, *args, **kwargs)
                if hasattr(self, "created_at"):
                    self.created_at = datetime.now()

            return new_init

        all_models.Notification.__init__ = init_decorator(
            all_models.Notification.__init__)

    @patch("ggrc.notifications.common.send_email")
    def test_no_date_change(self, mock_mail):
        """Test a basic case with no moving end date"""
        with freeze_time("2015-04-10 03:21:34"):
            _, workflow = self.wf_generator.generate_workflow(
                self.one_time_workflow_1)

            _, cycle = self.wf_generator.generate_cycle(workflow)
            self.wf_generator.activate_workflow(workflow)

        assignee = self.user
        task_assignees = [assignee, self.secondary_assignee]
        with freeze_time("2015-04-11 03:21:34"):
            _, notif_data = common.get_daily_notifications()

            # cycle started notifs available only for contact
            self.assertIn("cycle_started", notif_data[assignee.email])

        with freeze_time("2015-05-02 03:21:34"):
            _, notif_data = common.get_daily_notifications()
            self.assertIn(assignee.email, notif_data)

            # cycle started notifs available only for contact
            self.assertIn("cycle_started", notif_data[assignee.email])
            for user in task_assignees:
                self.assertNotIn("due_in", notif_data[user.email])
                self.assertNotIn("due_today", notif_data[user.email])

        with freeze_time("2015-05-02 03:21:34"):
            common.send_daily_digest_notifications()
            _, notif_data = common.get_daily_notifications()
            self.assertEqual(notif_data, {})

            # one email to admin, one to assigne and one to secondary assignee
            self.assertEqual(mock_mail.call_count, 3)

        with freeze_time("2015-05-04 03:21:34"):  # one day before due date
            _, notif_data = common.get_daily_notifications()
            for user in task_assignees:
                self.assertIn(user.email, notif_data)
                self.assertIn("due_in", notif_data[user.email])
                self.assertEqual(len(notif_data[user.email]["due_in"]), 2)

        with freeze_time("2015-05-04 03:21:34"):  # one day before due date
            common.send_daily_digest_notifications()
            _, notif_data = common.get_daily_notifications()
            self.assertEqual(notif_data, {})

            # one email to admin and two each to assigne and secondary assignee
            self.assertEqual(mock_mail.call_count, 5)

        with freeze_time("2015-05-05 03:21:34"):  # due date
            _, notif_data = common.get_daily_notifications()
            for user in task_assignees:
                self.assertIn("due_today", notif_data[user.email])
                self.assertEqual(len(notif_data[user.email]["due_today"]), 2)

    @patch("ggrc.notifications.common.send_email")
    def test_move_end_date_to_future(self, mock_mail):
        """Test moving the end date to the future.

    It is done before due_in and due_today notifications have been sent.
    """
        with freeze_time("2015-04-10 03:21:34"):
            _, workflow = self.wf_generator.generate_workflow(
                self.one_time_workflow_1)

            _, cycle = self.wf_generator.generate_cycle(workflow)
            self.wf_generator.activate_workflow(workflow)

        assignee = self.user
        task_assignees = [assignee, self.secondary_assignee]
        with freeze_time("2015-04-11 03:21:34"):
            _, notif_data = common.get_daily_notifications()
            # cycle started notifs available only for contact
            self.assertIn("cycle_started", notif_data[assignee.email])

        with freeze_time("2015-05-02 03:21:34"):
            _, notif_data = common.get_daily_notifications()
            for user in task_assignees:
                self.assertIn(user.email, notif_data)
                self.assertNotIn("due_in", notif_data[user.email])
                self.assertNotIn("due_today", notif_data[user.email])
            # cycle started notifs available only for contact
            self.assertIn("cycle_started", notif_data[assignee.email])

        with freeze_time("2015-05-02 03:21:34"):
            common.send_daily_digest_notifications()
            _, notif_data = common.get_daily_notifications()
            self.assertEqual(notif_data, {})

            # one email to admin and one each to assigne and secondary assignee
            self.assertEqual(mock_mail.call_count, 3)

        with freeze_time("2015-05-03 03:21:34"):
            cycle = Cycle.query.get(cycle.id)
            task1 = CycleTaskGroupObjectTask.query.get(
                cycle.cycle_task_group_object_tasks[0].id)
            task2 = CycleTaskGroupObjectTask.query.get(
                cycle.cycle_task_group_object_tasks[1].id)

            self.wf_generator.modify_object(
                task1, data={"end_date": date(2015, 5, 15)})
            self.wf_generator.modify_object(
                task2, data={"end_date": date(2015, 5, 15)})

        with freeze_time("2015-05-04 03:21:34"):  # one day before due date
            _, notif_data = common.get_daily_notifications()
            self.assertEqual(notif_data, {})

        with freeze_time("2015-05-05 03:21:34"):  # due date
            _, notif_data = common.get_daily_notifications()
            self.assertEqual(notif_data, {})

        with freeze_time("2015-05-14 03:21:34"):  # due date
            _, notif_data = common.get_daily_notifications()
            for user in task_assignees:
                self.assertIn(user.email, notif_data)
                self.assertIn("due_in", notif_data[user.email])
                self.assertEqual(len(notif_data[user.email]["due_in"]),
                                 len(self.random_objects))

        with freeze_time("2015-05-15 03:21:34"):  # due date
            _, notif_data = common.get_daily_notifications()
            for user in task_assignees:
                self.assertIn(user.email, notif_data)

                self.assertIn("due_in", notif_data[user.email])

                self.assertIn("due_today", notif_data[user.email])
                self.assertEqual(len(notif_data[user.email]["due_today"]),
                                 len(self.random_objects))

    @patch("ggrc.notifications.common.send_email")
    def test_move_end_date_to_past(self, mock_mail):
        """Test moving an end date to the past"""
        with freeze_time("2015-04-10 03:21:34"):
            _, workflow = self.wf_generator.generate_workflow(
                self.one_time_workflow_1)

            _, cycle = self.wf_generator.generate_cycle(workflow)
            self.wf_generator.activate_workflow(workflow)

        with freeze_time("2015-05-02 03:21:34"):
            common.send_daily_digest_notifications()
            _, notif_data = common.get_daily_notifications()
            self.assertEqual(notif_data, {})

            # one email to admin and one to assignee
            self.assertEqual(mock_mail.call_count, 3)

        with freeze_time("2015-05-03 03:21:34"):
            cycle = Cycle.query.get(cycle.id)
            task1 = CycleTaskGroupObjectTask.query.get(
                cycle.cycle_task_group_object_tasks[0].id)
            task2 = CycleTaskGroupObjectTask.query.get(
                cycle.cycle_task_group_object_tasks[1].id)

            self.wf_generator.modify_object(
                task1, data={"end_date": date(2015, 5, 1)})
            self.wf_generator.modify_object(
                task2, data={"end_date": date(2015, 5, 1)})

        task_assignees = [self.user, self.secondary_assignee]
        with freeze_time("2015-05-03 03:21:34"):  # two days after due date
            _, notif_data = common.get_daily_notifications()
            self.assertNotEqual(notif_data, {})
            for user in task_assignees:
                self.assertIn(user.email, notif_data)

                user_notifs = notif_data[user.email]
                self.assertNotIn("due_today", user_notifs)
                self.assertNotIn("due_in", user_notifs)

                self.assertIn("task_overdue", user_notifs)
                self.assertEqual(len(user_notifs["task_overdue"]), 2)

    @patch("ggrc.notifications.common.send_email")
    def test_move_end_date_to_today(self, mock_mail):
        """Test moving end date to today"""
        with freeze_time("2015-04-10 03:21:34"):
            _, workflow = self.wf_generator.generate_workflow(
                self.one_time_workflow_1)

            _, cycle = self.wf_generator.generate_cycle(workflow)
            self.wf_generator.activate_workflow(workflow)

        task_assignees = [self.user, self.secondary_assignee]
        with freeze_time("2015-05-02 03:21:34"):
            common.send_daily_digest_notifications()
            _, notif_data = common.get_daily_notifications()
            self.assertEqual(notif_data, {})

            # one email to admin and one to assigne
            self.assertEqual(mock_mail.call_count, 3)

        with freeze_time("2015-05-03 03:21:34"):
            cycle = Cycle.query.get(cycle.id)
            task1 = CycleTaskGroupObjectTask.query.get(
                cycle.cycle_task_group_object_tasks[0].id)
            task2 = CycleTaskGroupObjectTask.query.get(
                cycle.cycle_task_group_object_tasks[1].id)

            self.wf_generator.modify_object(
                task1, data={"end_date": date(2015, 5, 3)})
            self.wf_generator.modify_object(
                task2, data={"end_date": date(2015, 5, 4)})

        with freeze_time("2015-05-03 03:21:34"):  # one day before due date
            _, notif_data = common.get_daily_notifications()
            for user in task_assignees:
                self.assertNotEqual(notif_data, {})
                self.assertIn(user.email, notif_data)
                self.assertIn("due_today", notif_data[user.email])
                self.assertIn("due_in", notif_data[user.email])
                self.assertEqual(len(notif_data[user.email]["due_today"]), 1)

            common.send_daily_digest_notifications()

        with freeze_time("2015-05-04 03:21:34"):  # due date (of task2)
            _, notif_data = common.get_daily_notifications()
            for user in task_assignees:
                self.assertIn(user.email, notif_data)
                self.assertIn("due_today", notif_data[user.email])
                self.assertNotIn("due_in", notif_data[user.email])
            common.send_daily_digest_notifications()

        # check that overdue notifications are sent even on days after the day a
        # task has become due, unlike the "due_today" and "due_in" notifications
        with freeze_time("2015-05-05 03:21:34"):  # 1+ days after due date(s)
            _, notif_data = common.get_daily_notifications()
            self.assertNotEqual(notif_data, {})
            for user in task_assignees:
                self.assertIn(user.email, notif_data)

                user_notifs = notif_data[user.email]
                self.assertNotIn("due_today", user_notifs)
                self.assertNotIn("due_in", user_notifs)

                self.assertIn("task_overdue", user_notifs)
                self.assertEqual(len(user_notifs["task_overdue"]), 2)

    def create_test_cases(self):
        def person_dict(person_id):
            return {
                "href": "/api/people/%d" % person_id,
                "id": person_id,
                "type": "Person"
            }

        task_assignee_role_id = all_models.AccessControlRole.query.filter(
            all_models.AccessControlRole.name == "Task Assignees",
            all_models.AccessControlRole.object_type == "TaskGroupTask",
        ).one().id

        task_secondary_assignee = all_models.AccessControlRole.query.filter(
            all_models.AccessControlRole.name == "Task Secondary Assignees",
            all_models.AccessControlRole.object_type == "TaskGroupTask",
        ).one().id

        self.one_time_workflow_1 = {
            "title":
            "one time test workflow",
            "notify_on_change":
            True,
            "description":
            "some test workflow",
            # admin will be current user with id == 1
            "task_groups": [{
                "title":
                "one time task group",
                "contact":
                person_dict(self.user.id),
                "task_group_tasks": [
                    {
                        "title":
                        "task 1",
                        "description":
                        "some task",
                        "start_date":
                        date(2015, 5, 1),  # friday
                        "end_date":
                        date(2015, 5, 5),
                        "access_control_list": [
                            acl_helper.get_acl_json(task_assignee_role_id,
                                                    self.user.id),
                            acl_helper.get_acl_json(
                                task_secondary_assignee,
                                self.secondary_assignee.id),
                        ],
                    },
                    {
                        "title":
                        "task 2",
                        "description":
                        "some task 2",
                        "start_date":
                        date(2015, 5, 1),  # friday
                        "end_date":
                        date(2015, 5, 5),
                        "access_control_list": [
                            acl_helper.get_acl_json(task_assignee_role_id,
                                                    self.user.id),
                            acl_helper.get_acl_json(
                                task_secondary_assignee,
                                self.secondary_assignee.id),
                        ],
                    }
                ],
                "task_group_objects":
                self.random_objects
            }]
        }
class TestCycleTaskStatusChange(TestCase):

  """ This class contains simple one time workflow tests that are not
  in the gsheet test grid
  """

  def setUp(self):
    super(TestCycleTaskStatusChange, self).setUp()
    self.api = Api()
    self.wf_generator = WorkflowsGenerator()
    self.object_generator = ObjectGenerator()
    Notification.query.delete()

    self.random_objects = self.object_generator.generate_random_objects(2)
    _, self.user = self.object_generator.generate_person(
        user_role="Administrator")
    self.create_test_cases()

    def init_decorator(init):
      def new_init(self, *args, **kwargs):
        init(self, *args, **kwargs)
        if hasattr(self, "created_at"):
          self.created_at = datetime.now()
      return new_init

    Notification.__init__ = init_decorator(Notification.__init__)

  def test_task_declined_notification_created(self):
    with freeze_time("2015-05-01"):
      _, workflow = self.wf_generator.generate_workflow(
          self.one_time_workflow_1)

      _, cycle = self.wf_generator.generate_cycle(workflow)
      self.wf_generator.activate_workflow(workflow)

      cycle = Cycle.query.get(cycle.id)
      task1 = CycleTaskGroupObjectTask.query.get(
          cycle.cycle_task_group_object_tasks[0].id)

      self.task_change_status(task1, "Declined")

      notif = db.session.query(Notification).filter(and_(
          Notification.object_id == task1.id,
          Notification.object_type == task1.type,
          Notification.sent_at == None,  # noqa
          Notification.notification_type == self.get_notification_type(
              "cycle_task_declined"
          )
      )).all()

      self.assertEqual(len(notif), 1, "notifications: {}".format(str(notif)))

  def test_all_tasks_finished_notification_created(self):
    with freeze_time("2015-05-01"):
      _, workflow = self.wf_generator.generate_workflow(
          self.one_time_workflow_1)

      _, cycle = self.wf_generator.generate_cycle(workflow)
      self.wf_generator.activate_workflow(workflow)

      cycle = Cycle.query.get(cycle.id)
      task1 = CycleTaskGroupObjectTask.query.get(
          cycle.cycle_task_group_object_tasks[0].id)

      self.task_change_status(task1)

      notif = db.session.query(Notification).filter(and_(
          Notification.object_id == cycle.id,
          Notification.object_type == cycle.type,
          Notification.sent_at == None,  # noqa
          Notification.notification_type == self.get_notification_type(
              "all_cycle_tasks_completed"
          )
      )).all()

      self.assertEqual(len(notif), 1, "notifications: {}".format(str(notif)))

      notif = db.session.query(Notification).filter(and_(
          Notification.object_id == task1.id,
          Notification.object_type == task1.type,
          Notification.sent_at == None,  # noqa
          Notification.notification_type != self.get_notification_type(
              "all_cycle_tasks_completed"
          )
      )).all()

      self.assertEqual(notif, [])

  def test_multi_all_tasks_finished_notification_created(self):

    with freeze_time("2015-05-01"):
      _, workflow = self.wf_generator.generate_workflow(
          self.one_time_workflow_2)

      _, cycle = self.wf_generator.generate_cycle(workflow)
      self.wf_generator.activate_workflow(workflow)

      cycle = Cycle.query.get(cycle.id)
      task1 = CycleTaskGroupObjectTask.query.get(
          cycle.cycle_task_group_object_tasks[0].id)

      self.task_change_status(task1)

      notif = db.session.query(Notification).filter(and_(
          Notification.object_id == cycle.id,
          Notification.object_type == cycle.type,
          Notification.sent_at == None,  # noqa
          Notification.notification_type == self.get_notification_type(
              "all_cycle_tasks_completed"
          )
      )).all()

      # there is still one task in the cycle, so there should be no
      # notifications for all tasks completed
      self.assertEqual(notif, [])

      notif = db.session.query(Notification).filter(and_(
          Notification.object_id == task1.id,
          Notification.object_type == task1.type,
          Notification.sent_at == None,  # noqa
          Notification.notification_type != self.get_notification_type(
              "all_cycle_tasks_completed"
          )
      )).all()

      # The task was verified, so there should be no notifications left for due
      # dates.
      self.assertEqual(notif, [])

      task2 = CycleTaskGroupObjectTask.query.get(
          cycle.cycle_task_group_object_tasks[1].id)

      self.task_change_status(task2)

      notif = db.session.query(Notification).filter(and_(
          Notification.object_id == cycle.id,
          Notification.object_type == cycle.type,
          Notification.sent_at == None,  # noqa
          Notification.notification_type == self.get_notification_type(
              "all_cycle_tasks_completed"
          )
      )).all()

      self.assertEqual(len(notif), 1, "notifications: {}".format(str(notif)))

  @patch("ggrc.notifications.common.send_email")
  def test_single_task_declined(self, mock_mail):
    """
    test moving the end date to the future, befor due_in and due_today
    notifications have been sent
    """

    with freeze_time("2015-05-01"):
      _, workflow = self.wf_generator.generate_workflow(
          self.one_time_workflow_1)

      _, cycle = self.wf_generator.generate_cycle(workflow)
      self.wf_generator.activate_workflow(workflow)

    with freeze_time("2015-05-02"):
      common.send_daily_digest_notifications()

      cycle = Cycle.query.get(cycle.id)
      task1 = CycleTaskGroupObjectTask.query.get(
          cycle.cycle_task_group_object_tasks[0].id)

      self.task_change_status(task1, "Finished")

      _, notif_data = common.get_daily_notifications()
      self.assertEqual(notif_data, {})

    with freeze_time("2015-05-02"):
      common.send_daily_digest_notifications()

      cycle = Cycle.query.get(cycle.id)
      task1 = CycleTaskGroupObjectTask.query.get(
          cycle.cycle_task_group_object_tasks[0].id)

      self.task_change_status(task1, "Declined")

      user = Person.query.get(self.user.id)
      _, notif_data = common.get_daily_notifications()

      self.assertIn(user.email, notif_data)
      self.assertIn("task_declined", notif_data[user.email])

  @patch("ggrc.notifications.common.send_email")
  def test_single_task_accepted(self, mock_mail):
    """
    test moving the end date to the future, befor due_in and due_today
    notifications have been sent
    """

    with freeze_time("2015-05-01"):
      _, workflow = self.wf_generator.generate_workflow(
          self.one_time_workflow_1)

      _, cycle = self.wf_generator.generate_cycle(workflow)
      self.wf_generator.activate_workflow(workflow)

    with freeze_time("2015-05-02"):
      common.send_daily_digest_notifications()

      cycle = Cycle.query.get(cycle.id)
      task1 = CycleTaskGroupObjectTask.query.get(
          cycle.cycle_task_group_object_tasks[0].id)

      self.task_change_status(task1, "Finished")

      _, notif_data = common.get_daily_notifications()
      self.assertEqual(notif_data, {})

    with freeze_time("2015-05-03"):
      cycle = Cycle.query.get(cycle.id)
      task1 = CycleTaskGroupObjectTask.query.get(
          cycle.cycle_task_group_object_tasks[0].id)

      self.task_change_status(task1)

      user = Person.query.get(self.user.id)
      _, notif_data = common.get_daily_notifications()
      self.assertNotIn(user.email, notif_data)
      self.assertIn("all_tasks_completed", notif_data["*****@*****.**"])

  @patch("ggrc.notifications.common.send_email")
  def test_end_cycle(self, mock_mail):
    """
    manaually ending a cycle should stop all notifications for that cycle
    """

    with freeze_time("2015-05-01"):
      _, workflow = self.wf_generator.generate_workflow(
          self.one_time_workflow_1)
      _, cycle = self.wf_generator.generate_cycle(workflow)
      self.wf_generator.activate_workflow(workflow)

    with freeze_time("2015-05-03"):
      _, notif_data = common.get_daily_notifications()
      cycle = Cycle.query.get(cycle.id)
      user = Person.query.get(self.user.id)
      self.assertIn(user.email, notif_data)
      self.wf_generator.modify_object(cycle, data={"is_current": False})
      cycle = Cycle.query.get(cycle.id)
      self.assertFalse(cycle.is_current)

      _, notif_data = common.get_daily_notifications()
      self.assertNotIn(user.email, notif_data)

  def create_test_cases(self):
    def person_dict(person_id):
      return {
          "href": "/api/people/%d" % person_id,
          "id": person_id,
          "type": "Person"
      }

    self.one_time_workflow_1 = {
        "title": "one time test workflow",
        "notify_on_change": True,
        "description": "some test workflow",
        "owners": [person_dict(self.user.id)],
        "task_groups": [{
            "title": "single task group",
            "contact": person_dict(self.user.id),
            "task_group_tasks": [{
                "title": "task 1",
                "description": "single task in a wf",
                "contact": person_dict(self.user.id),
                "start_date": date(2015, 5, 1),  # friday
                "end_date": date(2015, 5, 5),
            }],
        }]
    }

    self.one_time_workflow_2 = {
        "title": "one time test workflow",
        "notify_on_change": True,
        "description": "some test workflow",
        "owners": [person_dict(self.user.id)],
        "task_groups": [{
            "title": "one time task group",
            "contact": person_dict(self.user.id),
            "task_group_tasks": [{
                "title": "task 1",
                "description": "two taks in wf with different objects",
                "contact": person_dict(self.user.id),
                "start_date": date(2015, 5, 1),  # friday
                "end_date": date(2015, 5, 5),
            }, {
                "title": "task 2",
                "description": "two taks in wf with different objects",
                "contact": person_dict(self.user.id),
                "start_date": date(2015, 5, 1),  # friday
                "end_date": date(2015, 5, 5),
            }],
            "task_group_objects": self.random_objects
        }]
    }

  def get_notification_type(self, name):
    return db.session.query(NotificationType).filter(
        NotificationType.name == name).one()

  def task_change_status(self, task, status="Verified"):
    self.wf_generator.modify_object(
        task, data={"status": status})

    task = CycleTaskGroupObjectTask.query.get(task.id)

    self.assertEqual(task.status, status)
Esempio n. 22
0
class TestOneTimeWorkflowNotification(TestCase):
    """ Tests are defined in the g-sheet test grid under:
    WF EMAILS for unit tests (middle level)
  """
    def setUp(self):
        super(TestOneTimeWorkflowNotification, self).setUp()
        self.api = Api()
        self.wf_generator = WorkflowsGenerator()
        self.object_generator = ObjectGenerator()

        self.random_objects = self.object_generator.generate_random_objects()
        self.random_people = [
            self.object_generator.generate_person(user_role="Administrator")[1]
            for _ in range(5)
        ]
        self.create_test_cases()

        self.create_users()

        db.session.query(Notification).delete()

        def init_decorator(init):
            def new_init(self, *args, **kwargs):
                init(self, *args, **kwargs)
                if hasattr(self, "created_at"):
                    self.created_at = datetime.now()

            return new_init

        Notification.__init__ = init_decorator(Notification.__init__)

    def tearDown(self):
        db.session.query(Notification).delete()

    def short_dict(self, obj, plural):
        return {
            "href": "/api/%s/%d" % (plural, obj.id),
            "id": obj.id,
            "type": obj.__class__.__name__,
        }

    def setup_cycle_tasks(self):
        """Prepare environment with couple of active cycle tasks."""
        with freeze_time("2018-11-01"):
            _, workflow = self.wf_generator.generate_workflow(
                self.one_time_workflow_1)
            self.wf_generator.generate_cycle(workflow)
            self.wf_generator.activate_workflow(workflow)
        return all_models.CycleTaskGroupObjectTask.query

    def assert_nofication_sent_with(self, text):
        """Assert if text exists in sent notification."""
        with mock.patch("ggrc.notifications.common.send_email") as send_email:
            self.client.get("/_notifications/send_daily_digest")
            _, _, content = send_email.call_args[0]
        self.assertIn(text, content)

    def assert_nofication_sent_without(self, text):
        """Assert if text doesn't exist in sent notification."""
        with mock.patch("ggrc.notifications.common.send_email") as send_email:
            self.client.get("/_notifications/send_daily_digest")
            _, _, content = send_email.call_args[0]
        self.assertNotIn(text, content)

    def test_one_time_wf(self):
        # setup
        with freeze_time("2015-04-07 03:21:34"):
            wf_response, wf = self.wf_generator.generate_workflow(
                data={
                    # admin will be the current user
                    "notify_on_change":
                    True,  # force real time updates
                    "title":
                    "One-time WF",
                    "notify_custom_message":
                    textwrap.dedent("""\
              Hi all.
              Did you know that Irelnd city namd Newtownmountkennedy has 19
              letters? But it's not the longest one. The recordsman is the
              city in New Zealand that contains 97 letter."""),
                })

            _, tg = self.wf_generator.generate_task_group(
                wf,
                data={
                    "title": "TG #1 for the One-time WF",
                    "contact": self.short_dict(self.tgassignee1, "people"),
                })

            self.wf_generator.generate_task_group_task(
                tg, {
                    "title": "task #1 for one-time workflow",
                    "contact": self.short_dict(self.member1, "people"),
                    "start_date": "04/07/2015",
                    "end_date": "04/15/2015",
                })

            self.wf_generator.generate_task_group_object(
                tg, self.random_objects[0])
            self.wf_generator.generate_task_group_object(
                tg, self.random_objects[1])

        # test
        with freeze_time("2015-04-07 03:21:34"):
            cycle_response, cycle = self.wf_generator.generate_cycle(wf)
            self.wf_generator.activate_workflow(wf)

            common.get_daily_notifications()

    def test_deprecated_ct_acl_update(self):
        """Test if acl update for deprecated CT will not create notification."""
        cycle_task = self.setup_cycle_tasks().first()
        cycle_task_title = cycle_task.title
        response = self.api.put(cycle_task, {"status": "Deprecated"})
        self.assert200(response)
        self.assert_nofication_sent_without(cycle_task_title)

        task_assignee = all_models.AccessControlRole.query.filter_by(
            name="Task Assignees",
            object_type="CycleTaskGroupObjectTask",
        ).first()
        person = self.object_generator.generate_person(user_role="Creator")[1]
        response = self.api.put(
            cycle_task, {
                "access_control_list": [{
                    "ac_role_id": task_assignee.id,
                    "person": {
                        "id": person.id,
                        "type": "Person",
                    }
                }]
            })
        self.assert200(response)
        self.assert_nofication_sent_without(cycle_task_title)

    def test_restore_deprecated_ct(self):
        """Test notifications for CT which was restored from Deprecated."""
        cycle_task = self.setup_cycle_tasks().first()
        cycle_task_title = cycle_task.title

        self.assert_nofication_sent_with(cycle_task_title)

        response = self.api.put(cycle_task, {"status": "Deprecated"})
        self.assert200(response)
        self.assert_nofication_sent_without(cycle_task_title)

        response = self.api.put(cycle_task, {"status": "Assigned"})
        self.assert200(response)
        self.assert_nofication_sent_with(cycle_task_title)

    def create_test_cases(self):
        def person_dict(person_id):
            return {
                "href": "/api/people/%d" % person_id,
                "id": person_id,
                "type": "Person"
            }

        self.one_time_workflow_1 = {
            "title":
            "one time test workflow",
            "description":
            "some test workflow",
            # admin will be current user with id == 1
            "task_groups": [
                {
                    "title":
                    "one time task group",
                    "task_group_tasks": [
                        {
                            "title": "task_{}".format(str(uuid.uuid4())),
                            "description": "some task",
                            "contact": person_dict(self.random_people[0].id),
                            "start_date": date(2015, 5, 1),  # friday
                            "end_date": date(2015, 5, 5),
                        },
                        {
                            "title": "task_{}".format(str(uuid.uuid4())),
                            "description": "some task",
                            "contact": person_dict(self.random_people[1].id),
                            "start_date": date(2015, 5, 4),
                            "end_date": date(2015, 5, 7),
                        }
                    ],
                    "task_group_objects":
                    self.random_objects[:2]
                },
                {
                    "title":
                    "another one time task group",
                    "task_group_tasks": [
                        {
                            "title": "task_{}".format(str(uuid.uuid4())),
                            "description": "some task",
                            "contact": person_dict(self.random_people[0].id),
                            "start_date": date(2015, 5, 8),  # friday
                            "end_date": date(2015, 5, 12),
                        },
                        {
                            "title": "task_{}".format(str(uuid.uuid4())),
                            "description": "some task",
                            "contact": person_dict(self.random_people[2].id),
                            "start_date": date(2015, 5, 1),  # friday
                            "end_date": date(2015, 5, 5),
                        }
                    ],
                    "task_group_objects": []
                }
            ]
        }

    def create_users(self):
        _, self.admin1 = self.object_generator.generate_person(
            # data={"name": "User1 Admin1", "email": "*****@*****.**"},
            user_role="Administrator")
        _, self.tgassignee1 = self.object_generator.generate_person(
            # data={"name": "User2 TGassignee1",
            #       "email": "*****@*****.**"},
            user_role="Administrator")
        _, self.member1 = self.object_generator.generate_person(
            # data={"name": "User3 Member1", "email": "*****@*****.**"},
            user_role="Administrator")
        _, self.member2 = self.object_generator.generate_person(
            # data={"name": "User4 Member2", "email": "*****@*****.**"},
            user_role="Administrator")
class TestExportMultipleObjects(TestCase):
    """Test export of multiple objects."""
    def setUp(self):
        self.clear_data()
        self.client.get("/login")
        self.wf_generator = WorkflowsGenerator()

    def test_workflow_task_group_mapping(self):  # pylint: disable=invalid-name
        """Test workflow and task group mappings."""
        with freeze_time("2017-03-07"):
            workflow = wf_factories.WorkflowFactory()
            workflow_slug = workflow.slug
            task_group1 = wf_factories.TaskGroupFactory(workflow=workflow)
            task_group1_slug = task_group1.slug

            task_group2 = wf_factories.TaskGroupFactory(workflow=workflow)
            task_group2_slug = task_group2.slug

        data = [
            {
                "object_name": "Workflow",
                "filters": {
                    "expression": {
                        "op": {
                            "name": "relevant"
                        },
                        "object_name": "TaskGroup",
                        "slugs": [task_group1_slug],
                    },
                },
                "fields": "all",
            },
            {
                "object_name": "TaskGroup",
                "filters": {
                    "expression": {
                        "op": {
                            "name": "relevant"
                        },
                        "object_name": "__previous__",
                        "ids": [0],
                    },
                },
                "fields": "all",
            },
        ]
        response = self.export_csv(data)
        self.assert200(response)
        response_data = response.data

        self.assertEqual(3, response_data.count(workflow_slug))
        self.assertIn(task_group1_slug, response_data)
        self.assertIn(task_group2_slug, response_data)

    def test_tg_task(self):
        """Test task group task mappings."""
        with freeze_time("2017-03-07"):
            workflow = wf_factories.WorkflowFactory()
            task_group1 = wf_factories.TaskGroupFactory(workflow=workflow)
            task_group1_slug = task_group1.slug
            task_group_task1 = wf_factories.TaskGroupTaskFactory(
                task_group=task_group1)
            task_group_task1_slug = task_group_task1.slug

            task_group_task2 = wf_factories.TaskGroupTaskFactory(
                task_group=task_group1)
            task_group_task2_slug = task_group_task2.slug

        data = [
            {
                "object_name": "TaskGroupTask",
                "filters": {
                    "expression": {
                        "op": {
                            "name": "relevant"
                        },
                        "object_name": "TaskGroup",
                        "slugs": [task_group1_slug],
                    },
                },
                "fields": "all",
            },
            {
                "object_name": "TaskGroup",
                "filters": {
                    "expression": {
                        "op": {
                            "name": "relevant"
                        },
                        "object_name": "__previous__",
                        "ids": ["0"],
                    },
                },
                "fields": "all",
            },
        ]
        response = self.export_csv(data)
        self.assert200(response)
        response_data = response.data
        self.assertEqual(3, response_data.count(task_group1_slug))
        self.assertIn(task_group_task1_slug, response_data)
        self.assertIn(task_group_task2_slug, response_data)

    def test_workflow_cycle_mapping(self):
        """Test workflow and cycle mappings."""
        with freeze_time("2017-03-07"):
            workflow = wf_factories.WorkflowFactory()
            workflow_slug = workflow.slug
            task_group = wf_factories.TaskGroupFactory(workflow=workflow)
            wf_factories.TaskGroupTaskFactory(task_group=task_group)

            wf_factories.TaskGroupTaskFactory(task_group=task_group)

            self.wf_generator.generate_cycle(workflow)
            self.wf_generator.activate_workflow(workflow)

        def block(obj, obj_id):
            return {
                "object_name": obj,
                "filters": {
                    "expression": {
                        "op": {
                            "name": "relevant"
                        },
                        "object_name": "__previous__",
                        "ids": [obj_id],
                    },
                },
                "fields": "all",
            }

        data = [
            {
                "object_name": "Cycle",
                "filters": {
                    "expression": {
                        "op": {
                            "name": "relevant"
                        },
                        "object_name": "Workflow",
                        "slugs": [workflow_slug],
                    },
                },
                "fields": "all",
            },
            block("Workflow", "0"),
            block("CycleTaskGroup", "0"),
            block("Cycle", "2"),
            block("CycleTaskGroupObjectTask", "2"),
            block("CycleTaskGroup", "4"),
        ]

        response = self.export_csv(data)
        self.assert200(response)
        response_data = response.data

        self.assertEqual(3, response_data.count(workflow_slug))
        self.assertEqual(4, response_data.count("CYCLEGROUP-"))
        self.assertEqual(6, response_data.count("CYCLE-"))
        self.assertEqual(2, response_data.count("CYCLETASK-"))

    def test_cycle_task_objects(self):
        """Test cycle task and various objects."""
        with freeze_time("2017-03-07"):
            workflow = wf_factories.WorkflowFactory()
            task_group = wf_factories.TaskGroupFactory(workflow=workflow)
            wf_factories.TaskGroupTaskFactory(task_group=task_group)

            wf_factories.TaskGroupTaskFactory(task_group=task_group)

            policy = factories.PolicyFactory()
            policy_slug = policy.slug
            factories.RelationshipFactory(source=task_group,
                                          destination=policy)

            self.wf_generator.generate_cycle(workflow)
            self.wf_generator.activate_workflow(workflow)

        data = [
            {
                "object_name": "CycleTaskGroupObjectTask",
                "filters": {
                    "expression": {
                        "op": {
                            "name": "relevant"
                        },
                        "object_name": "Policy",
                        "slugs": [policy_slug],
                    },
                },
                "fields": "all",
            },
            {
                "object_name": "Policy",
                "filters": {
                    "expression": {
                        "op": {
                            "name": "relevant"
                        },
                        "object_name": "__previous__",
                        "ids": ["0"],
                    },
                },
                "fields": ["slug", "title"],
            },
        ]
        response = self.export_csv(data)
        self.assert200(response)
        response_data = response.data

        self.assertEqual(2, response_data.count("CYCLETASK-"))
        self.assertEqual(3, response_data.count(policy_slug))

    def test_wf_indirect_relevant_filters(self):  # pylint: disable=invalid-name
        """Test related filter for indirect relationships on wf objects."""
        with freeze_time("2017-03-07"):
            workflow = wf_factories.WorkflowFactory(title="workflow-1")
            task_group1 = wf_factories.TaskGroupFactory(workflow=workflow)
            wf_factories.TaskGroupTaskFactory(task_group=task_group1)

            wf_factories.TaskGroupTaskFactory(task_group=task_group1)

            task_group2 = wf_factories.TaskGroupFactory(workflow=workflow)
            wf_factories.TaskGroupTaskFactory(task_group=task_group2)

            policy = factories.PolicyFactory()
            policy_slug = policy.slug
            factories.RelationshipFactory(source=task_group1,
                                          destination=policy)

            self.wf_generator.generate_cycle(workflow)
            self.wf_generator.activate_workflow(workflow)

        def block(obj):
            return {
                "object_name": obj,
                "fields": ["slug"],
                "filters": {
                    "expression": {
                        "object_name": "Policy",
                        "op": {
                            "name": "relevant"
                        },
                        "slugs": [policy_slug],
                    },
                },
            }

        data = [
            block("Workflow"),
            block("Cycle"),
            block("CycleTaskGroup"),
            block("CycleTaskGroupObjectTask"),
        ]
        response = self.export_csv(data)
        self.assert200(response)
        response_data = response.data

        wf1 = Workflow.query.filter_by(title="workflow-1").first()
        cycle = wf1.cycles[0]
        cycle_tasks = []
        for cycle_task in cycle.cycle_task_group_object_tasks:
            for related_object in cycle_task.related_objects():
                if related_object.slug == policy_slug:
                    cycle_tasks.append(cycle_task)
                    break

        cycle_task_groups = list(
            {cycle_task.cycle_task_group
             for cycle_task in cycle_tasks})

        self.assertEqual(1, response_data.count("WORKFLOW-"))

        self.assertRegexpMatches(response_data, ",{}[,\r\n]".format(wf1.slug))

        self.assertEqual(1, response_data.count("CYCLE-"))
        self.assertRegexpMatches(response_data,
                                 ",{}[,\r\n]".format(cycle.slug))

        self.assertEqual(1, response_data.count("CYCLEGROUP-"))
        self.assertEqual(1, len(cycle_task_groups))
        self.assertRegexpMatches(
            response_data, ",{}[,\r\n]".format(cycle_task_groups[0].slug))

        self.assertEqual(2, response_data.count("CYCLETASK-"))
        self.assertEqual(2, len(cycle_tasks))
        for cycle_task in cycle_tasks:
            self.assertRegexpMatches(response_data,
                                     ",{}[,\r\n]".format(cycle_task.slug))

        destinations = [
            ("Workflow", wf1.slug, 1),
            ("Cycle", cycle.slug, 1),
            ("CycleTaskGroupObjectTask", cycle_tasks[0].slug, 1),
            ("CycleTaskGroupObjectTask", cycle_tasks[1].slug, 1),
        ]
        for object_name, slug, count in destinations:
            data = [{
                "object_name": "Policy",
                "fields": ["slug"],
                "filters": {
                    "expression": {
                        "object_name": object_name,
                        "op": {
                            "name": "relevant"
                        },
                        "slugs": [slug],
                    },
                },
            }]
            response = self.export_csv(data)
            self.assert200(response)
            response_data = response.data
            self.assertEqual(count, response_data.count(",POLICY-"),
                             "Count for " + object_name)
            self.assertIn("," + policy_slug, response_data)
Esempio n. 24
0
class TestOneTimeWorkflowNotification(TestCase):

  """ Tests are defined in the g-sheet test grid under:
    WF EMAILS for unit tests (middle level)
  """

  def setUp(self):
    super(TestOneTimeWorkflowNotification, self).setUp()
    self.api = Api()
    self.wf_generator = WorkflowsGenerator()
    self.object_generator = ObjectGenerator()

    self.random_objects = self.object_generator.generate_random_objects()
    self.random_people = [
        self.object_generator.generate_person(user_role="Administrator")[1]
        for _ in range(5)]
    self.create_test_cases()

    self.create_users()

    db.session.query(Notification).delete()

    def init_decorator(init):
      def new_init(self, *args, **kwargs):
        init(self, *args, **kwargs)
        if hasattr(self, "created_at"):
          self.created_at = datetime.now()
      return new_init

    Notification.__init__ = init_decorator(Notification.__init__)

  def tearDown(self):
    db.session.query(Notification).delete()

  def short_dict(self, obj, plural):
    return {
        "href": "/api/%s/%d" % (plural, obj.id),
        "id": obj.id,
        "type": obj.__class__.__name__,
    }

  def setup_cycle_tasks(self):
    """Prepare environment with couple of active cycle tasks."""
    with freeze_time("2018-11-01"):
      _, workflow = self.wf_generator.generate_workflow(
          self.one_time_workflow_1
      )
      self.wf_generator.generate_cycle(workflow)
      self.wf_generator.activate_workflow(workflow)
    return all_models.CycleTaskGroupObjectTask.query

  def assert_nofication_sent_with(self, text):
    """Assert if text exists in sent notification."""
    with mock.patch("ggrc.notifications.common.send_email") as send_email:
      self.client.get("/_notifications/send_daily_digest")
      _, _, content = send_email.call_args[0]
    self.assertIn(text, content)

  def assert_nofication_sent_without(self, text):
    """Assert if text doesn't exist in sent notification."""
    with mock.patch("ggrc.notifications.common.send_email") as send_email:
      self.client.get("/_notifications/send_daily_digest")
      _, _, content = send_email.call_args[0]
    self.assertNotIn(text, content)

  def test_one_time_wf(self):
    # setup
    with freeze_time("2015-04-07 03:21:34"):
      wf_response, wf = self.wf_generator.generate_workflow(data={
          # admin will be the current user
          "notify_on_change": True,  # force real time updates
          "title": "One-time WF",
          "notify_custom_message": textwrap.dedent("""\
              Hi all.
              Did you know that Irelnd city namd Newtownmountkennedy has 19
              letters? But it's not the longest one. The recordsman is the
              city in New Zealand that contains 97 letter."""),
      })

      _, tg = self.wf_generator.generate_task_group(wf, data={
          "title": "TG #1 for the One-time WF",
          "contact": self.short_dict(self.tgassignee1, "people"),
      })

      self.wf_generator.generate_task_group_task(tg, {
          "title": "task #1 for one-time workflow",
          "contact": self.short_dict(self.member1, "people"),
          "start_date": "04/07/2015",
          "end_date": "04/15/2015",
      })

      self.wf_generator.generate_task_group_object(tg, self.random_objects[0])
      self.wf_generator.generate_task_group_object(tg, self.random_objects[1])

    # test
    with freeze_time("2015-04-07 03:21:34"):
      cycle_response, cycle = self.wf_generator.generate_cycle(wf)
      self.wf_generator.activate_workflow(wf)

      common.get_daily_notifications()

  def test_deprecated_ct_acl_update(self):
    """Test if acl update for deprecated CT will not create notification."""
    cycle_task = self.setup_cycle_tasks().first()
    cycle_task_title = cycle_task.title
    response = self.api.put(cycle_task, {"status": "Deprecated"})
    self.assert200(response)
    self.assert_nofication_sent_without(cycle_task_title)

    task_assignee = all_models.AccessControlRole.query.filter_by(
        name="Task Assignees",
        object_type="CycleTaskGroupObjectTask",
    ).first()
    person = self.object_generator.generate_person(user_role="Creator")[1]
    response = self.api.put(
        cycle_task,
        {
            "access_control_list": [{
                "ac_role_id": task_assignee.id,
                "person": {
                    "id": person.id,
                    "type": "Person",
                }
            }]
        }
    )
    self.assert200(response)
    self.assert_nofication_sent_without(cycle_task_title)

  def test_restore_deprecated_ct(self):
    """Test notifications for CT which was restored from Deprecated."""
    cycle_task = self.setup_cycle_tasks().first()
    cycle_task_title = cycle_task.title

    self.assert_nofication_sent_with(cycle_task_title)

    response = self.api.put(cycle_task, {"status": "Deprecated"})
    self.assert200(response)
    self.assert_nofication_sent_without(cycle_task_title)

    response = self.api.put(cycle_task, {"status": "Assigned"})
    self.assert200(response)
    self.assert_nofication_sent_with(cycle_task_title)

  def create_test_cases(self):
    def person_dict(person_id):
      return {
          "href": "/api/people/%d" % person_id,
          "id": person_id,
          "type": "Person"
      }

    self.one_time_workflow_1 = {
        "title": "one time test workflow",
        "description": "some test workflow",
        # admin will be current user with id == 1
        "task_groups": [{
            "title": "one time task group",
            "task_group_tasks": [{
                "title": "task_{}".format(str(uuid.uuid4())),
                "description": "some task",
                "contact": person_dict(self.random_people[0].id),
                "start_date": date(2015, 5, 1),  # friday
                "end_date": date(2015, 5, 5),
            }, {
                "title": "task_{}".format(str(uuid.uuid4())),
                "description": "some task",
                "contact": person_dict(self.random_people[1].id),
                "start_date": date(2015, 5, 4),
                "end_date": date(2015, 5, 7),
            }],
            "task_group_objects": self.random_objects[:2]
        }, {
            "title": "another one time task group",
            "task_group_tasks": [{
                "title": "task_{}".format(str(uuid.uuid4())),
                "description": "some task",
                "contact": person_dict(self.random_people[0].id),
                "start_date": date(2015, 5, 8),  # friday
                "end_date": date(2015, 5, 12),
            }, {
                "title": "task_{}".format(str(uuid.uuid4())),
                "description": "some task",
                "contact": person_dict(self.random_people[2].id),
                "start_date": date(2015, 5, 1),  # friday
                "end_date": date(2015, 5, 5),
            }],
            "task_group_objects": []
        }]
    }

  def create_users(self):
    _, self.admin1 = self.object_generator.generate_person(
        # data={"name": "User1 Admin1", "email": "*****@*****.**"},
        user_role="Administrator")
    _, self.tgassignee1 = self.object_generator.generate_person(
        # data={"name": "User2 TGassignee1",
        #       "email": "*****@*****.**"},
        user_role="Administrator")
    _, self.member1 = self.object_generator.generate_person(
        # data={"name": "User3 Member1", "email": "*****@*****.**"},
        user_role="Administrator")
    _, self.member2 = self.object_generator.generate_person(
        # data={"name": "User4 Member2", "email": "*****@*****.**"},
        user_role="Administrator")
class TestCycleTaskStatusChange(TestCase):
    """ This class contains simple one time workflow tests that are not
  in the gsheet test grid
  """
    def setUp(self):
        TestCase.setUp(self)
        self.api = Api()
        self.wf_generator = WorkflowsGenerator()
        self.object_generator = ObjectGenerator()
        Notification.query.delete()

        self.random_objects = self.object_generator.generate_random_objects(2)
        _, self.user = self.object_generator.generate_person(
            user_role="Administrator")
        self.create_test_cases()

        def init_decorator(init):
            def new_init(self, *args, **kwargs):
                init(self, *args, **kwargs)
                if hasattr(self, "created_at"):
                    self.created_at = datetime.now()

            return new_init

        Notification.__init__ = init_decorator(Notification.__init__)

    def test_task_declined_notification_created(self):
        with freeze_time("2015-05-01"):
            _, workflow = self.wf_generator.generate_workflow(
                self.one_time_workflow_1)

            _, cycle = self.wf_generator.generate_cycle(workflow)
            self.wf_generator.activate_workflow(workflow)

            cycle = Cycle.query.get(cycle.id)
            task1 = CycleTaskGroupObjectTask.query.get(
                cycle.cycle_task_group_object_tasks[0].id)

            self.task_change_status(task1, "Declined")

            notif = db.session.query(Notification).filter(
                and_(
                    Notification.object_id == task1.id,
                    Notification.object_type == task1.type,
                    Notification.sent_at == None,  # noqa
                    Notification.notification_type ==
                    self.get_notification_type("cycle_task_declined"))).all()

            self.assertEqual(len(notif), 1,
                             "notifications: {}".format(str(notif)))

    def test_all_tasks_finished_notification_created(self):
        with freeze_time("2015-05-01"):
            _, workflow = self.wf_generator.generate_workflow(
                self.one_time_workflow_1)

            _, cycle = self.wf_generator.generate_cycle(workflow)
            self.wf_generator.activate_workflow(workflow)

            cycle = Cycle.query.get(cycle.id)
            task1 = CycleTaskGroupObjectTask.query.get(
                cycle.cycle_task_group_object_tasks[0].id)

            self.task_change_status(task1)

            notif = db.session.query(Notification).filter(
                and_(
                    Notification.object_id == cycle.id,
                    Notification.object_type == cycle.type,
                    Notification.sent_at == None,  # noqa
                    Notification.notification_type == self.
                    get_notification_type("all_cycle_tasks_completed"))).all()

            self.assertEqual(len(notif), 1,
                             "notifications: {}".format(str(notif)))

            notif = db.session.query(Notification).filter(
                and_(
                    Notification.object_id == task1.id,
                    Notification.object_type == task1.type,
                    Notification.sent_at == None,  # noqa
                    Notification.notification_type != self.
                    get_notification_type("all_cycle_tasks_completed"))).all()

            self.assertEqual(notif, [])

    def test_multi_all_tasks_finished_notification_created(self):

        with freeze_time("2015-05-01"):
            _, workflow = self.wf_generator.generate_workflow(
                self.one_time_workflow_2)

            _, cycle = self.wf_generator.generate_cycle(workflow)
            self.wf_generator.activate_workflow(workflow)

            cycle = Cycle.query.get(cycle.id)
            task1 = CycleTaskGroupObjectTask.query.get(
                cycle.cycle_task_group_object_tasks[0].id)

            self.task_change_status(task1)

            notif = db.session.query(Notification).filter(
                and_(
                    Notification.object_id == cycle.id,
                    Notification.object_type == cycle.type,
                    Notification.sent_at == None,  # noqa
                    Notification.notification_type == self.
                    get_notification_type("all_cycle_tasks_completed"))).all()

            # there is still one task in the cycle, so there should be no
            # notifications for all tasks completed
            self.assertEqual(notif, [])

            notif = db.session.query(Notification).filter(
                and_(
                    Notification.object_id == task1.id,
                    Notification.object_type == task1.type,
                    Notification.sent_at == None,  # noqa
                    Notification.notification_type != self.
                    get_notification_type("all_cycle_tasks_completed"))).all()

            # The task was verified, so there should be no notifications left for due
            # dates.
            self.assertEqual(notif, [])

            task2 = CycleTaskGroupObjectTask.query.get(
                cycle.cycle_task_group_object_tasks[1].id)

            self.task_change_status(task2)

            notif = db.session.query(Notification).filter(
                and_(
                    Notification.object_id == cycle.id,
                    Notification.object_type == cycle.type,
                    Notification.sent_at == None,  # noqa
                    Notification.notification_type == self.
                    get_notification_type("all_cycle_tasks_completed"))).all()

            self.assertEqual(len(notif), 1,
                             "notifications: {}".format(str(notif)))

    @patch("ggrc.notifications.common.send_email")
    def test_single_task_declined(self, mock_mail):
        """
    test moving the end date to the future, befor due_in and due_today
    notifications have been sent
    """

        with freeze_time("2015-05-01"):
            _, workflow = self.wf_generator.generate_workflow(
                self.one_time_workflow_1)

            _, cycle = self.wf_generator.generate_cycle(workflow)
            self.wf_generator.activate_workflow(workflow)

        with freeze_time("2015-05-02"):
            common.send_daily_digest_notifications()

            cycle = Cycle.query.get(cycle.id)
            task1 = CycleTaskGroupObjectTask.query.get(
                cycle.cycle_task_group_object_tasks[0].id)

            self.task_change_status(task1, "Finished")

            _, notif_data = common.get_daily_notifications()
            self.assertEqual(notif_data, {})

        with freeze_time("2015-05-02"):
            common.send_daily_digest_notifications()

            cycle = Cycle.query.get(cycle.id)
            task1 = CycleTaskGroupObjectTask.query.get(
                cycle.cycle_task_group_object_tasks[0].id)

            self.task_change_status(task1, "Declined")

            user = Person.query.get(self.user.id)
            _, notif_data = common.get_daily_notifications()

            self.assertIn(user.email, notif_data)
            self.assertIn("task_declined", notif_data[user.email])

    @patch("ggrc.notifications.common.send_email")
    def test_single_task_accepted(self, mock_mail):
        """
    test moving the end date to the future, befor due_in and due_today
    notifications have been sent
    """

        with freeze_time("2015-05-01"):
            _, workflow = self.wf_generator.generate_workflow(
                self.one_time_workflow_1)

            _, cycle = self.wf_generator.generate_cycle(workflow)
            self.wf_generator.activate_workflow(workflow)

        with freeze_time("2015-05-02"):
            common.send_daily_digest_notifications()

            cycle = Cycle.query.get(cycle.id)
            task1 = CycleTaskGroupObjectTask.query.get(
                cycle.cycle_task_group_object_tasks[0].id)

            self.task_change_status(task1, "Finished")

            _, notif_data = common.get_daily_notifications()
            self.assertEqual(notif_data, {})

        with freeze_time("2015-05-03"):
            cycle = Cycle.query.get(cycle.id)
            task1 = CycleTaskGroupObjectTask.query.get(
                cycle.cycle_task_group_object_tasks[0].id)

            self.task_change_status(task1)

            user = Person.query.get(self.user.id)
            _, notif_data = common.get_daily_notifications()
            self.assertNotIn(user.email, notif_data)
            self.assertIn("all_tasks_completed",
                          notif_data["*****@*****.**"])

    @patch("ggrc.notifications.common.send_email")
    def test_end_cycle(self, mock_mail):
        """
    manaually ending a cycle should stop all notifications for that cycle
    """

        with freeze_time("2015-05-01"):
            _, workflow = self.wf_generator.generate_workflow(
                self.one_time_workflow_1)
            _, cycle = self.wf_generator.generate_cycle(workflow)
            self.wf_generator.activate_workflow(workflow)

        with freeze_time("2015-05-03"):
            _, notif_data = common.get_daily_notifications()
            cycle = Cycle.query.get(cycle.id)
            user = Person.query.get(self.user.id)
            self.assertIn(user.email, notif_data)
            self.wf_generator.modify_object(cycle, data={"is_current": False})
            cycle = Cycle.query.get(cycle.id)
            self.assertFalse(cycle.is_current)

            _, notif_data = common.get_daily_notifications()
            self.assertNotIn(user.email, notif_data)

    def create_test_cases(self):
        def person_dict(person_id):
            return {
                "href": "/api/people/%d" % person_id,
                "id": person_id,
                "type": "Person"
            }

        self.one_time_workflow_1 = {
            "title":
            "one time test workflow",
            "notify_on_change":
            True,
            "description":
            "some test workflow",
            "owners": [person_dict(self.user.id)],
            "task_groups": [{
                "title":
                "single task group",
                "contact":
                person_dict(self.user.id),
                "task_group_tasks": [{
                    "title": "task 1",
                    "description": "single task in a wf",
                    "contact": person_dict(self.user.id),
                    "start_date": date(2015, 5, 1),  # friday
                    "end_date": date(2015, 5, 5),
                }],
            }]
        }

        self.one_time_workflow_2 = {
            "title":
            "one time test workflow",
            "notify_on_change":
            True,
            "description":
            "some test workflow",
            "owners": [person_dict(self.user.id)],
            "task_groups": [{
                "title":
                "one time task group",
                "contact":
                person_dict(self.user.id),
                "task_group_tasks": [
                    {
                        "title": "task 1",
                        "description": "two taks in wf with different objects",
                        "contact": person_dict(self.user.id),
                        "start_date": date(2015, 5, 1),  # friday
                        "end_date": date(2015, 5, 5),
                    },
                    {
                        "title": "task 2",
                        "description": "two taks in wf with different objects",
                        "contact": person_dict(self.user.id),
                        "start_date": date(2015, 5, 1),  # friday
                        "end_date": date(2015, 5, 5),
                    }
                ],
                "task_group_objects":
                self.random_objects
            }]
        }

    def get_notification_type(self, name):
        return db.session.query(NotificationType).filter(
            NotificationType.name == name).one()

    def task_change_status(self, task, status="Verified"):
        self.wf_generator.modify_object(task, data={"status": status})

        task = CycleTaskGroupObjectTask.query.get(task.id)

        self.assertEqual(task.status, status)
Esempio n. 26
0
class TestMonthlyWorkflowNotification(TestCase):
    """ This class contains simple one time workflow tests that are not
  in the gsheet test grid
  """
    def setUp(self):
        TestCase.setUp(self)
        self.api = Api()
        self.wf_generator = WorkflowsGenerator()
        self.object_generator = ObjectGenerator()

        self.random_objects = self.object_generator.generate_random_objects()
        _, self.person_1 = self.object_generator.generate_person(
            user_role="Administrator")
        _, self.person_2 = self.object_generator.generate_person(
            user_role="Administrator")
        self.create_test_cases()

        def init_decorator(init):
            def new_init(self, *args, **kwargs):
                init(self, *args, **kwargs)
                if hasattr(self, "created_at"):
                    self.created_at = datetime.now()

            return new_init

        Notification.__init__ = init_decorator(Notification.__init__)

    @patch("ggrc.notifications.common.send_email")
    def test_auto_generate_cycle(self, mock_mail):

        with freeze_time("2015-04-01"):
            _, wf = self.wf_generator.generate_workflow(
                self.monthly_workflow_1)
            self.wf_generator.activate_workflow(wf)

            person_1 = Person.query.get(self.person_1.id)

        with freeze_time("2015-04-02"):
            _, notif_data = common.get_daily_notifications()
            self.assertIn(person_1.email, notif_data)
            self.assertIn("cycle_starts_in", notif_data[person_1.email])

        with freeze_time("2015-04-02"):
            self.api.tc.get("nightly_cron_endpoint")
            _, notif_data = common.get_daily_notifications()
            self.assertNotIn(person_1.email, notif_data)

        with freeze_time("2015-04-02"):
            start_recurring_cycles()
            _, notif_data = common.get_daily_notifications()
            self.assertNotIn(person_1.email, notif_data)

        # cycle starts on monday - 6th, and not on 5th
        with freeze_time("2015-04-03"):
            start_recurring_cycles()

        with freeze_time("2015-04-15"):  # one day befor due date
            _, notif_data = common.get_daily_notifications()
            person_1 = Person.query.get(self.person_1.id)
            self.assertIn(person_1.email, notif_data)

        with freeze_time("2015-04-25"):  # due date
            _, notif_data = common.get_daily_notifications()
            person_1 = Person.query.get(self.person_1.id)
            self.assertIn(person_1.email, notif_data)

    @patch("ggrc.notifications.common.send_email")
    def test_manual_generate_cycle(self, mock_mail):

        with freeze_time("2015-04-01"):
            _, wf = self.wf_generator.generate_workflow(
                self.monthly_workflow_1)
            self.wf_generator.activate_workflow(wf)

            person_1 = Person.query.get(self.person_1.id)

        with freeze_time("2015-04-03"):
            _, notif_data = common.get_daily_notifications()

        with freeze_time("2015-04-03"):
            _, cycle = self.wf_generator.generate_cycle(wf)
            _, notif_data = common.get_daily_notifications()
            person_1 = Person.query.get(self.person_1.id)
            self.assertIn("cycle_started", notif_data[person_1.email])

        with freeze_time("2015-05-03"):  # two days befor due date
            _, notif_data = common.get_daily_notifications()
            person_1 = Person.query.get(self.person_1.id)
            self.assertIn(person_1.email, notif_data)

    def create_test_cases(self):
        def person_dict(person_id):
            return {
                "href": "/api/people/%d" % person_id,
                "id": person_id,
                "type": "Person"
            }

        self.monthly_workflow_1 = {
            "title":
            "test monthly wf notifications",
            "notify_on_change":
            True,
            "description":
            "some test workflow",
            "owners": [person_dict(self.person_2.id)],
            "frequency":
            "monthly",
            "task_groups": [{
                "title":
                "one time task group",
                "contact":
                person_dict(self.person_1.id),
                "task_group_tasks": [{
                    "title": "task 1",
                    "description": "some task",
                    "contact": person_dict(self.person_1.id),
                    "relative_start_day": 5,
                    "relative_end_day": 25,
                }, {
                    "title": "task 2",
                    "description": "some task",
                    "contact": person_dict(self.person_1.id),
                    "relative_start_day": 10,
                    "relative_end_day": 21,
                }],
                "task_group_objects":
                self.random_objects[:2]
            }, {
                "title":
                "another one time task group",
                "contact":
                person_dict(self.person_1.id),
                "task_group_tasks": [{
                    "title": "task 1 in tg 2",
                    "description": "some task",
                    "contact": person_dict(self.person_1.id),
                    "relative_start_day": 15,
                    "relative_end_day": 15,
                }, {
                    "title": "task 2 in tg 2",
                    "description": "some task",
                    "contact": person_dict(self.person_2.id),
                    "relative_start_day": 15,
                    "relative_end_day": 28,
                }],
                "task_group_objects": []
            }]
        }
class TestBasicWorkflowActions(TestCase):
  """
  Tests for basic workflow actions
  """
  def setUp(self):
    super(TestBasicWorkflowActions, self).setUp()
    self.api = Api()
    self.generator = WorkflowsGenerator()
    self.object_generator = ObjectGenerator()

    self.random_objects = self.object_generator.generate_random_objects()
    self.create_test_cases()

  def tearDown(self):
    pass

  def test_create_workflows(self):
    _, wflow = self.generator.generate_workflow(self.one_time_workflow_1)
    self.assertIsInstance(wflow, Workflow)

    task_groups = db.session.query(TaskGroup)\
        .filter(TaskGroup.workflow_id == wflow.id).all()

    self.assertEqual(len(task_groups),
                     len(self.one_time_workflow_1["task_groups"]))

  def test_workflows(self):
    for workflow in self.all_workflows:
      _, wflow = self.generator.generate_workflow(workflow)
      self.assertIsInstance(wflow, Workflow)

      task_groups = db.session.query(TaskGroup)\
          .filter(TaskGroup.workflow_id == wflow.id).all()

      self.assertEqual(len(task_groups),
                       len(workflow["task_groups"]))

  def test_activate_wf(self):
    for workflow in self.all_workflows:
      _, wflow = self.generator.generate_workflow(workflow)
      response, wflow = self.generator.activate_workflow(wflow)

      self.assert200(response)

  def test_one_time_workflow_edits(self):
    _, wflow = self.generator.generate_workflow(self.one_time_workflow_1)

    wf_dict = {"title": "modified one time wf"}
    self.generator.modify_workflow(wflow, data=wf_dict)

    modified_wf = db.session.query(Workflow).filter(
        Workflow.id == wflow.id).one()
    self.assertEqual(wf_dict["title"], modified_wf.title)

  def test_one_time_wf_activate(self):
    _, wflow = self.generator.generate_workflow(self.one_time_workflow_1)
    self.generator.generate_cycle(wflow)
    self.generator.activate_workflow(wflow)

    tasks = [len(tg.get("task_group_tasks", []))
             for tg in self.one_time_workflow_1["task_groups"]]

    cycle_tasks = db.session.query(CycleTaskGroupObjectTask).join(
        Cycle).join(Workflow).filter(Workflow.id == wflow.id).all()
    active_wf = db.session.query(Workflow).filter(
        Workflow.id == wflow.id).one()

    self.assertEqual(sum(tasks), len(cycle_tasks))
    self.assertEqual(active_wf.status, "Active")

  def test_one_time_wf_state_transition_dates(self):
    _, wflow = self.generator.generate_workflow(self.one_time_workflow_1)
    self.generator.generate_cycle(wflow)
    self.generator.activate_workflow(wflow)

    cycle_tasks = db.session.query(CycleTaskGroupObjectTask).join(
        Cycle).join(Workflow).filter(Workflow.id == wflow.id).all()
    with freeze_time("2015-6-9 13:00:00"):
      today = dtm.datetime.now()
      transitions = [
          ("InProgress", None, None),
          ("Finished", today, None),
          ("Declined", None, None),
          ("Finished", today, None),
          ("Verified", today, today),
          ("Finished", today, None),
      ]
      # Iterate over possible transitions and check if dates got set correctly
      for (status, expected_finished, expected_verified) in transitions:
        cycle_task = cycle_tasks[0]
        _, task = self.generator.modify_object(cycle_task, {"status": status})
        self.assertEqual(task.finished_date, expected_finished)
        self.assertEqual(task.verified_date, expected_verified)

  def test_delete_calls(self):
    _, workflow = self.generator.generate_workflow()
    self.generator.generate_task_group(workflow)
    _, task_group = self.generator.generate_task_group(workflow)
    task_groups = db.session.query(TaskGroup).filter(
        TaskGroup.workflow_id == workflow.id).all()
    self.assertEqual(len(task_groups), 2)

    response = self.generator.api.delete(task_group)
    self.assert200(response)

    task_groups = db.session.query(TaskGroup).filter(
        TaskGroup.workflow_id == workflow.id).all()
    self.assertEqual(len(task_groups), 1)

  def create_test_cases(self):

    self.quarterly_wf_1 = {
        "title": "quarterly wf 1",
        "description": "",
        "unit": "month",
        "repeat_every": 3,
        "task_groups": [{
            "title": "tg_1",
            "task_group_tasks": [{
                "description": factories.random_str(100),
            }, {
                "description": factories.random_str(100),
            }, {
                "description": factories.random_str(100),
            },
            ],
        },
        ]
    }

    self.weekly_wf_1 = {
        "title": "weekly thingy",
        "description": "start this many a time",
        "unit": "week",
        "repeat_every": 1,
        "task_groups": [{
            "title": "tg_2",
            "task_group_tasks": [{
                "description": factories.random_str(100),
            }, {
                "title": "monday task",
            }, {
                "title": "weekend task",
            },
            ],
            "task_group_objects": self.random_objects
        },
        ]
    }

    self.one_time_workflow_1 = {
        "title": "one time wf test",
        "description": "some test workflow",
        "task_groups": [{
            "title": "tg_1",
            "task_group_tasks": [{}, {}, {}]
        }, {
            "title": "tg_2",
            "task_group_tasks": [{
                "description": factories.random_str(100)
            }, {}
            ],
            "task_group_objects": self.random_objects[:2]
        }, {
            "title": "tg_3",
            "task_group_tasks": [{
                "title": "simple task 1",
                "description": factories.random_str(100)
            }, {
                "title": factories.random_str(),
                "description": factories.random_str(100)
            }, {
                "title": factories.random_str(),
                "description": factories.random_str(100)
            }
            ],
            "task_group_objects": self.random_objects
        }
        ]
    }
    self.one_time_workflow_2 = {
        "title": "test_wf_title",
        "description": "some test workflow",
        "task_groups": [
            {
                "title": "tg_1",
                "task_group_tasks": [{}, {}, {}]
            },
            {
                "title": "tg_2",
                "task_group_tasks": [{
                    "description": factories.random_str(100)
                }, {}],
                "task_group_objects": self.random_objects[:2]
            },
            {
                "title": "tg_3",
                "task_group_tasks": [{
                    "title": "simple task 1",
                    "description": factories.random_str(100)
                }, {
                    "title": factories.random_str(),
                    "description": factories.random_str(100)
                }, {
                    "title": factories.random_str(),
                    "description": factories.random_str(100)
                }],
                "task_group_objects": []
            }
        ]
    }

    self.monthly_workflow_1 = {
        "title": "monthly test wf",
        "description": "start this many a time",
        "unit": "month",
        "repeat_every": 1,
        "task_groups": [
            {
                "title": "tg_2",
                "task_group_tasks": [{
                    "description": factories.random_str(100),
                }, {
                    "title": "monday task",
                }, {
                    "title": "weekend task",
                }],
                "task_group_objects": self.random_objects
            },
        ]
    }
    self.all_workflows = [
        self.one_time_workflow_1,
        self.one_time_workflow_2,
        self.weekly_wf_1,
        self.monthly_workflow_1,
        self.quarterly_wf_1,
    ]
class TestTaskDueNotifications(TestCase):
  """Test suite for task due soon/today notifications."""

  # pylint: disable=invalid-name

  def _fix_notification_init(self):
    """Fix Notification object init function.

    This is a fix needed for correct created_at field when using freezgun. By
    default the created_at field is left empty and filed by database, which
    uses system time and not the fake date set by freezugun plugin. This fix
    makes sure that object created in freeze_time block has all dates set with
    the correct date and time.
    """
    def init_decorator(init):
      """"Adjust the value of the object's created_at attribute to now."""
      @functools.wraps(init)
      def new_init(self, *args, **kwargs):
        init(self, *args, **kwargs)
        if hasattr(self, "created_at"):
          self.created_at = datetime.now()
      return new_init

    models.Notification.__init__ = init_decorator(models.Notification.__init__)

  def setUp(self):
    super(TestTaskDueNotifications, self).setUp()
    self.api = Api()
    self.wf_generator = WorkflowsGenerator()
    self.object_generator = ObjectGenerator()
    models.Notification.query.delete()

    self._fix_notification_init()

    self.random_objects = self.object_generator.generate_random_objects(2)
    _, self.user = self.object_generator.generate_person(
        user_role="Administrator")

    person_dict = {
        "href": "/api/people/{}".format(self.user.id),
        "id": self.user.id,
        "type": "Person",
    }

    self.one_time_workflow = {
        "title": "one time test workflow",
        "notify_on_change": True,
        "description": "some test workflow",
        "is_verification_needed": False,
        "owners": [person_dict.copy()],
        "task_groups": [{
            "title": "one time task group",
            "contact": person_dict.copy(),
            "task_group_tasks": [{
                "title": "task 1",
                "description": "some task",
                "contact": person_dict.copy(),
                "start_date": date(2017, 5, 15),
                "end_date": date(2017, 6, 11),
            }, {
                "title": "task 2",
                "description": "some task 2",
                "contact": person_dict.copy(),
                "start_date": date(2017, 5, 8),
                "end_date": date(2017, 6, 12),
            }, {
                "title": "task 3",
                "description": "some task 3",
                "contact": person_dict.copy(),
                "start_date": date(2017, 5, 31),
                "end_date": date(2017, 6, 13),
            }, {
                "title": "task 4",
                "description": "some task 4",
                "contact": person_dict.copy(),
                "start_date": date(2017, 6, 2),
                "end_date": date(2017, 6, 14),
            }, {
                "title": "task 5",
                "description": "some task 5",
                "contact": person_dict.copy(),
                "start_date": date(2017, 6, 8),
                "end_date": date(2017, 6, 15),
            }],
            "task_group_objects": self.random_objects
        }]
    }

  @ddt.unpack
  @ddt.data(
      ("2017-06-12 12:12:12", ["task 1"], ["task 2"], ["task 3"]),
      ("2017-06-13 13:13:13", ["task 1", "task 2"], ["task 3"], ["task 4"]),
  )
  @patch("ggrc.notifications.common.send_email")
  def test_creating_obsolete_notifications(
      self, fake_now, expected_overdue, expected_due_today, expected_due_in, _
  ):
    """Notifications already obsolete on creation date should not be created.
    """
    with freeze_time("2017-06-12 09:39:32"):
      tmp = self.one_time_workflow.copy()
      _, workflow = self.wf_generator.generate_workflow(tmp)
      self.wf_generator.generate_cycle(workflow)
      response, workflow = self.wf_generator.activate_workflow(workflow)
      self.assert200(response)

    user = models.Person.query.get(self.user.id)

    with freeze_time(fake_now):
      # mark all yeasterday notifications as sent
      models.all_models.Notification.query.filter(
          sa.func.DATE(models.all_models.Notification.send_on) < date.today()
      ).update({models.all_models.Notification.sent_at:
                datetime.now() - timedelta(1)},
               synchronize_session="fetch")

      _, notif_data = common.get_daily_notifications()
      user_notifs = notif_data.get(user.email, {})

      actual_overdue = [n['title'] for n in
                        user_notifs.get("task_overdue", {}).itervalues()]
      actual_overdue.sort()
      self.assertEqual(actual_overdue, expected_overdue)

      self.assertEqual(
          [n['title'] for n in user_notifs.get("due_today", {}).itervalues()],
          expected_due_today)

      self.assertEqual(
          [n['title'] for n in user_notifs.get("due_in", {}).itervalues()],
          expected_due_in)
Esempio n. 29
0
class TestPersonResource(TestCase, WithQueryApi):
  """Tests for special people api endpoints."""

  def setUp(self):
    super(TestPersonResource, self).setUp()
    self.client.get("/login")
    self.api = Api()
    self.generator = WorkflowsGenerator()

  @staticmethod
  def _create_users_names_rbac(users):
    """Create name and Creator role for users, created vid PersonFactory"""
    if not users:
      return
    roles = {r.name: r for r in all_models.Role.query.all()}
    for user in users:
      user.name = user.email.split("@")[0]
      rbac_factories.UserRoleFactory(role=roles["Creator"], person=user)

  def assert_profile_get_successful(self, response, expected_datetime):
    """Verify assertions for successful GET profile method"""
    self.assert200(response)
    response_datetime = date_parser.parse(response.json["last_seen_whats_new"])
    self.assertEqual(expected_datetime, response_datetime)

  @freeze_time("2018-05-20 12:23:17")
  def test_profile_get_successful(self):
    """Test person_profile GET method successfully achieves correct data"""
    with factories.single_commit():
      user = factories.PersonFactory()
      self._create_users_names_rbac([user])
    self.api.set_user(person=user)

    response = self.api.client.get("/api/people/{}/profile".format(user.id))
    self.assert_profile_get_successful(response, default_date())

  def test_profile_get_no_profile(self):
    """Test person_profile GET method achieves data with missing profile"""
    with factories.single_commit():
      user = factories.PersonFactory()
      self._create_users_names_rbac([user])
    self.api.set_user(person=user)

    profiles_table = PersonProfile.__table__
    db_request = profiles_table.delete().where(
        profiles_table.c.person_id == user.id)
    db.engine.execute(db_request)
    with freeze_time("2018-05-28 23:30:10"):
      response = self.api.client.get("/api/people/{}/profile".format(user.id))
      self.assert_profile_get_successful(response, default_date())

  def test_profile_get_failed(self):
    """Test person_profiles GET method fails

    Now only logged user can request his profile
    """
    with factories.single_commit():
      valid_user = factories.PersonFactory()
      self._create_users_names_rbac([valid_user])

    response = self.client.get(
        "/api/people/{}/profile".format(valid_user.id))
    # logged with default user during setUp
    self.assert403(response)
    response = self.api.client.get(
        "/api/people/{}/profile".format(valid_user.id))
    # not authorized user
    self.assert403(response)

  def assert_profile_put_successful(self,
                                    response,
                                    correct_response,
                                    user,
                                    new_date):
    """Verify assertions for successful PUT profile method"""
    self.assert200(response)
    self.assertEqual(response.json, correct_response)
    profile = PersonProfile.query.filter_by(person_id=user.id).first()
    self.assertEqual(profile.last_seen_whats_new, date_parser.parse(new_date))

  def test_profile_put_successful(self):
    """Test person_profile PUT method for setting data and correct response"""
    with factories.single_commit():
      user = factories.PersonFactory()
      self._create_users_names_rbac([user])
    self.api.set_user(person=user)

    new_date = "2018-05-20 16:38:17"
    data = {"last_seen_whats_new": new_date}
    correct_response = {"Person": {"id": user.id, "profile": data}}
    response = self.api.client.put("/api/people/{}/profile".format(user.id),
                                   content_type='application/json',
                                   data=json.dumps(data),
                                   headers=[('X-Requested-By', 'Tests')])
    self.assert_profile_put_successful(response,
                                       correct_response,
                                       user,
                                       new_date)

  def test_profile_put_no_profile(self):
    """Test person_profile PUT method for setting data for missing profile"""
    with factories.single_commit():
      user = factories.PersonFactory()
      self._create_users_names_rbac([user])
    self.api.set_user(person=user)

    new_date = "2018-05-20 22:05:17"
    data = {"last_seen_whats_new": new_date}
    correct_response = {"Person": {"id": user.id, "profile": data}}
    profiles_table = PersonProfile.__table__
    db_request = profiles_table.delete().where(
        profiles_table.c.person_id == user.id)
    db.engine.execute(db_request)
    response = self.api.client.put("/api/people/{}/profile".format(user.id),
                                   content_type='application/json',
                                   data=json.dumps(data),
                                   headers=[('X-Requested-By', 'Tests')])
    self.assert_profile_put_successful(response,
                                       correct_response,
                                       user,
                                       new_date)

  def test_profile_put_unauthorized(self):
    """Test person_profiles PUT method fails for unauthorized user"""
    with factories.single_commit():
      user = factories.PersonFactory()
      self._create_users_names_rbac([user])

    new_date = "2018-05-20 22:05:17"
    data = {"last_seen_whats_new": new_date}
    response = self.client.put("/api/people/{}/profile".format(user.id),
                               content_type='application/json',
                               data=json.dumps(data),
                               headers=[('X-Requested-By', 'Tests')])
    # logged with default user during setUp
    self.assert403(response)
    response = self.api.client.put("/api/people/{}/profile".format(user.id),
                                   content_type='application/json',
                                   data=json.dumps(data),
                                   headers=[('X-Requested-By', 'Tests')])
    # not authorized user
    self.assert403(response)

  @ddt.data({"last_seen_whats_new": "NOT A 123 DAT456A"},
            {"other_key": "2018-05-20 22:05:17", "one_more_key": 42})
  def test_profile_put_corrupted_data(self, data):
    """Test person_profiles PUT method fails via incorrect request data

    If request doesn't have "last_seen_whats_new" key or date is incorrect,
      response is code 400 "Bad Request"
    """
    with factories.single_commit():
      user = factories.PersonFactory()
      self._create_users_names_rbac([user])
    self.api.set_user(person=user)

    response = self.api.client.put("/api/people/{}/profile".format(user.id),
                                   content_type='application/json',
                                   data=json.dumps(data),
                                   headers=[('X-Requested-By', 'Tests')])
    # missed key in request
    self.assert400(response)

  def test_task_count_empty(self):
    """Test query count without any workflows and tasks."""
    user = all_models.Person.query.first()
    response = self.client.get("/api/people/{}/task_count".format(user.id))
    self.assertEqual(
        response.json,
        {"open_task_count": 0, "has_overdue": False}
    )

  @ddt.data(
      (True, [
          ("task 1", "Finished", 3, True, 3),
          ("task 1", "Verified", 2, True, 3),
          ("task 2", "Declined", 2, True, 3),
          ("task 2", "Verified", 1, False, 3),
          ("task 2", "Finished", 2, True, 3),
          ("task 3", "Verified", 1, True, 3),
          ("task 2", "Verified", 0, False, 3),
      ]),
      (False, [
          ("task 1", "Finished", 2, True, 3),
          ("task 2", "In Progress", 2, True, 3),
          ("task 2", "Finished", 1, False, 3),
          ("task 3", "Finished", 0, False, 3),
      ]),
  )
  @ddt.unpack
  def test_task_count(self, is_verification_needed, transitions):
    """Test person task counts.

    This tests checks for correct task counts
     - with inactive workflows and
     - with overdue tasks
     - without overdue tasks
     - with finished overdue tasks

    The four checks are done in a single test due to complex differences
    between tests that make ddt cumbersome and the single test also improves
    integration test performance due to slow workflow setup stage.
    """
    # pylint: disable=too-many-locals

    user = all_models.Person.query.first()
    dummy_user = factories.PersonFactory()
    user_id = user.id
    role_id = all_models.AccessControlRole.query.filter(
        all_models.AccessControlRole.name == "Task Assignees",
        all_models.AccessControlRole.object_type == "TaskGroupTask",
    ).one().id
    secondary_role_id = all_models.AccessControlRole.query.filter(
        all_models.AccessControlRole.name == "Task Secondary Assignees",
        all_models.AccessControlRole.object_type == "TaskGroupTask",
    ).one().id

    one_time_workflow = {
        "title": "Person resource test workflow",
        "notify_on_change": True,
        "description": "some test workflow",
        "owners": [create_stub(user)],
        "is_verification_needed": is_verification_needed,
        "task_groups": [{
            "title": "one time task group",
            "contact": create_stub(user),
            "task_group_tasks": [{
                "title": "task 1",
                "description": "some task",
                "access_control_list": [
                    acl_helper.get_acl_json(role_id, user.id),
                    acl_helper.get_acl_json(secondary_role_id, user.id)
                ],
                "start_date": date(2017, 5, 5),
                "end_date": date(2017, 8, 15),
            }, {
                "title": "task 2",
                "description": "some task 3",
                "access_control_list": [
                    acl_helper.get_acl_json(role_id, user.id),
                    acl_helper.get_acl_json(secondary_role_id, user.id),
                    acl_helper.get_acl_json(secondary_role_id, dummy_user.id)
                ],
                "start_date": date(2017, 5, 5),
                "end_date": date(2017, 9, 16),
            }, {
                "title": "task 3",
                "description": "some task 4",
                "access_control_list": [
                    acl_helper.get_acl_json(role_id, user.id),
                    acl_helper.get_acl_json(role_id, dummy_user.id)
                ],
                "start_date": date(2017, 6, 5),
                "end_date": date(2017, 10, 16),
            }, {
                "title": "dummy task 4",  # task should not counted
                "description": "some task 4",
                "access_control_list": [
                    acl_helper.get_acl_json(role_id, dummy_user.id)],
                "start_date": date(2017, 6, 5),
                "end_date": date(2017, 11, 17),
            }, {
                "title": "dummy task 5",  # task should not counted
                "description": "some task 4",
                "access_control_list": [
                    acl_helper.get_acl_json(role_id, dummy_user.id)],
                "start_date": date(2017, 6, 5),
                "end_date": date(2017, 11, 18),
            }],
            "task_group_objects": []
        }]
    }

    inactive_workflow = {
        "title": "Activated workflow with archived cycles",
        "notify_on_change": True,
        "description": "Extra test workflow",
        "owners": [create_stub(user)],
        "task_groups": [{
            "title": "Extra task group",
            "contact": create_stub(user),
            "task_group_tasks": [{
                "title": "not counted existing task",
                "description": "",
                "access_control_list": [
                    acl_helper.get_acl_json(role_id, user.id)],
                "start_date": date(2017, 5, 5),
                "end_date": date(2017, 8, 15),
            }],
            "task_group_objects": []
        }]
    }

    with freeze_time("2017-10-16 05:09:10"):
      self.client.get("/login")
      # Activate normal one time workflow
      _, workflow = self.generator.generate_workflow(one_time_workflow)
      _, cycle = self.generator.generate_cycle(workflow)
      tasks = {t.title: t for t in cycle.cycle_task_group_object_tasks}
      _, workflow = self.generator.activate_workflow(workflow)

      # Activate and close the inactive workflow
      _, workflow = self.generator.generate_workflow(inactive_workflow)
      _, cycle = self.generator.generate_cycle(workflow)
      _, workflow = self.generator.activate_workflow(workflow)
      self.generator.modify_object(cycle, data={"is_current": False})

    with freeze_time("2017-7-16 07:09:10"):
      self.client.get("/login")
      response = self.client.get("/api/people/{}/task_count".format(user_id))
      self.assertEqual(
          response.json,
          {"open_task_count": 3, "has_overdue": False}
      )

    with freeze_time("2017-10-16 08:09:10"):  # same day as task 3 end date
      self.client.get("/login")
      response = self.client.get("/api/people/{}/task_count".format(user_id))
      self.assertEqual(
          response.json,
          {"open_task_count": 3, "has_overdue": True}
      )

      for task, status, count, overdue, my_work_count in transitions:
        self.generator.modify_object(tasks[task], data={"status": status})
        task_count_response = \
            self.client.get("/api/people/{}/task_count".format(user_id))
        my_work_count_response = \
            self.client.get("/api/people/{}/my_work_count".format(user_id))

        self.assertEqual(
            task_count_response.json,
            {"open_task_count": count, "has_overdue": overdue}
        )

        self.assertEqual(
            my_work_count_response.json["CycleTaskGroupObjectTask"],
            my_work_count
        )

  def test_task_count_multiple_wfs(self):
    """Test task count with both verified and non verified workflows.

    This checks task counts with 4 tasks
        2017, 8, 15  - verification needed
        2017, 11, 18  - verification needed
        2017, 8, 15  - No verification needed
        2017, 11, 18  - No verification needed
    """

    user = all_models.Person.query.first()
    user_id = user.id
    role_id = all_models.AccessControlRole.query.filter(
        all_models.AccessControlRole.name == "Task Assignees",
        all_models.AccessControlRole.object_type == "TaskGroupTask",
    ).one().id
    workflow_template = {
        "title": "verified workflow",
        "owners": [create_stub(user)],
        "is_verification_needed": True,
        "task_groups": [{
            "title": "one time task group",
            "contact": create_stub(user),
            "task_group_tasks": [{
                "title": "task 1",
                "description": "some task",
                "access_control_list": [
                    acl_helper.get_acl_json(role_id, user.id)],
                "start_date": date(2017, 5, 5),
                "end_date": date(2017, 8, 15),
            }, {
                "title": "dummy task 5",
                "description": "some task 4",
                "access_control_list": [
                    acl_helper.get_acl_json(role_id, user.id)],
                "start_date": date(2017, 6, 5),
                "end_date": date(2017, 11, 18),
            }],
            "task_group_objects": []
        }]
    }

    with freeze_time("2017-10-16 05:09:10"):
      self.client.get("/login")
      verified_workflow = workflow_template.copy()
      verified_workflow["is_verification_needed"] = True
      _, workflow = self.generator.generate_workflow(verified_workflow)
      _, cycle = self.generator.generate_cycle(workflow)
      verified_tasks = {
          task.title: task
          for task in cycle.cycle_task_group_object_tasks
      }
      _, workflow = self.generator.activate_workflow(workflow)

      non_verified_workflow = workflow_template.copy()
      non_verified_workflow["is_verification_needed"] = False
      _, workflow = self.generator.generate_workflow(non_verified_workflow)
      _, cycle = self.generator.generate_cycle(workflow)
      non_verified_tasks = {
          task.title: task
          for task in cycle.cycle_task_group_object_tasks
      }
      _, workflow = self.generator.activate_workflow(workflow)

    with freeze_time("2017-7-16 07:09:10"):
      self.client.get("/login")
      response = self.client.get("/api/people/{}/task_count".format(user_id))
      self.assertEqual(
          response.json,
          {"open_task_count": 4, "has_overdue": False}
      )

    with freeze_time("2017-10-16 08:09:10"):
      self.client.get("/login")
      response = self.client.get("/api/people/{}/task_count".format(user_id))
      self.assertEqual(
          response.json,
          {"open_task_count": 4, "has_overdue": True}
      )

      # transition 1, task that needs verification goes to finished state. This
      # transition should not change anything
      self.generator.modify_object(
          verified_tasks["task 1"],
          data={"status": "Finished"}
      )
      response = self.client.get("/api/people/{}/task_count".format(user_id))
      self.assertEqual(
          response.json,
          {"open_task_count": 4, "has_overdue": True}
      )

      # transition 2, task that needs verification goes to verified state. This
      # transition should reduce task count.
      self.generator.modify_object(
          verified_tasks["task 1"],
          data={"status": "Verified"}
      )
      response = self.client.get("/api/people/{}/task_count".format(user_id))
      self.assertEqual(
          response.json,
          {"open_task_count": 3, "has_overdue": True}
      )

      # transition 3, task that does not need verification goes into Finished
      # state. This transition should reduce task count and remove all overdue
      # tasks
      self.generator.modify_object(
          non_verified_tasks["task 1"],
          data={"status": "Finished"}
      )
      response = self.client.get("/api/people/{}/task_count".format(user_id))
      self.assertEqual(
          response.json,
          {"open_task_count": 2, "has_overdue": False}
      )

  @ddt.data(("Creator", 403),
            ("Reader", 403),
            ("Editor", 200),
            ("Administrator", 200))
  @ddt.unpack
  def test_person_editing(self, role_name, status):
    """{0} should receive {1} status code on edit Person."""
    role = all_models.Role.query.filter(
        all_models.Role.name == role_name
    ).one()
    with factories.single_commit():
      client_user = factories.PersonFactory()
      rbac_factories.UserRoleFactory(role=role, person=client_user)
    self.api.set_user(client_user)
    self.client.get("/login")
    base_email = "*****@*****.**"
    person = factories.PersonFactory(email=base_email)
    person_id = person.id
    new_email = "new_{}".format(base_email)
    resp = self.api.put(person, {"email": new_email})
    self.assertEqual(status, resp.status_code)
    person = all_models.Person.query.get(person_id)
    if status == 200:
      self.assertEqual(new_email, person.email)
    else:
      self.assertEqual(base_email, person.email)
Esempio n. 30
0
class TestCycleImport(WorkflowTestCase):
    """Tests for Cycle model import"""
    def setUp(self):
        super(TestCycleImport, self).setUp()
        self.generator = WorkflowsGenerator()

    @ddt.data(
        ("*****@*****.**", []),
        ("[email protected]\[email protected]",
         [errors.MULTIPLE_ASSIGNEES.format(line=3, column_name="Assignee")]),
    )
    @ddt.unpack
    def test_assignee_updated(self, assignee, expected_warnings):
        """Tests updating cycle assignee via import"""
        with factories.single_commit():
            factories.PersonFactory(email="*****@*****.**")
            workflow = wf_factories.WorkflowFactory()
            group = wf_factories.TaskGroupFactory(workflow=workflow)
            wf_factories.TaskGroupTaskFactory(
                task_group=group,
                start_date=datetime.date(2017, 2, 28),
                end_date=datetime.date(2017, 2, 28) +
                datetime.timedelta(days=4))
        _, cycle = self.generator.generate_cycle(workflow)
        data = OrderedDict([
            ("object_type", "Cycle"),
            ("code", cycle.slug),
            ("title", cycle.title),
            ("Assignee", assignee),
        ])

        response = self.import_data(data)
        expected_messages = {
            "Cycle": {
                "row_warnings": set(expected_warnings),
            },
        }
        self._check_csv_response(response, expected_messages)
        cycle = all_models.Cycle.query.one()
        self.assertEqual(cycle.contact.email, "*****@*****.**")

    @ddt.data(
        ("", []),
        ("[email protected]\[email protected]", [
            errors.MULTIPLE_ASSIGNEES.format(line=3, column_name="Assignee"),
            errors.UNKNOWN_USER_WARNING.format(line=3,
                                               email="*****@*****.**"),
            errors.UNKNOWN_USER_WARNING.format(line=3,
                                               email="*****@*****.**")
        ]),
    )
    @ddt.unpack
    def test_assignee_not_updated(self, assignee, expected_warnings):
        """Tests cycle assignee wasn't updated with wrong user"""
        with factories.single_commit():
            workflow = wf_factories.WorkflowFactory()
            group = wf_factories.TaskGroupFactory(workflow=workflow)
            wf_factories.TaskGroupTaskFactory(
                task_group=group,
                start_date=datetime.date(2017, 2, 28),
                end_date=datetime.date(2017, 2, 28) +
                datetime.timedelta(days=4))
        _, cycle = self.generator.generate_cycle(workflow)
        data = OrderedDict([
            ("object_type", "Cycle"),
            ("code", cycle.slug),
            ("title", cycle.title),
            ("Assignee", assignee),
        ])

        response = self.import_data(data)
        expected_messages = {
            "Cycle": {
                "row_warnings": set(expected_warnings),
            },
        }
        self._check_csv_response(response, expected_messages)
        cycle = all_models.Cycle.query.one()
        self.assertFalse(cycle.contact)
class TestTaskDueNotifications(TestCase):
  """Test suite for task due soon/today notifications."""

  # pylint: disable=invalid-name

  def _fix_notification_init(self):
    """Fix Notification object init function.

    This is a fix needed for correct created_at field when using freezgun. By
    default the created_at field is left empty and filed by database, which
    uses system time and not the fake date set by freezugun plugin. This fix
    makes sure that object created in freeze_time block has all dates set with
    the correct date and time.
    """
    def init_decorator(init):
      """"Adjust the value of the object's created_at attribute to now."""
      @functools.wraps(init)
      def new_init(self, *args, **kwargs):
        init(self, *args, **kwargs)
        if hasattr(self, "created_at"):
          self.created_at = datetime.now()
      return new_init

    models.Notification.__init__ = init_decorator(models.Notification.__init__)

  def setUp(self):
    super(TestTaskDueNotifications, self).setUp()
    self.api = Api()
    self.wf_generator = WorkflowsGenerator()
    self.object_generator = ObjectGenerator()
    models.Notification.query.delete()

    self._fix_notification_init()

    self.random_objects = self.object_generator.generate_random_objects(2)
    _, self.user = self.object_generator.generate_person(
        user_role="Administrator")

    role_id = models.all_models.AccessControlRole.query.filter(
        models.all_models.AccessControlRole.name == "Task Assignees",
        models.all_models.AccessControlRole.object_type == "TaskGroupTask",
    ).one().id

    self.one_time_workflow = {
        "title": "one time test workflow",
        "notify_on_change": True,
        "description": "some test workflow",
        "is_verification_needed": False,
        # admin will be current user with id == 1
        "task_groups": [{
            "title": "one time task group",
            "contact": {
                "href": "/api/people/{}".format(self.user.id),
                "id": self.user.id,
                "type": "Person",
            },
            "task_group_tasks": [{
                "title": "task 1",
                "description": "some task",
                "access_control_list": [
                    acl_helper.get_acl_json(role_id, self.user.id)],
                "start_date": date(2017, 5, 15),
                "end_date": date(2017, 6, 11),
            }, {
                "title": "task 2",
                "description": "some task 2",
                "access_control_list": [
                    acl_helper.get_acl_json(role_id, self.user.id)],
                "start_date": date(2017, 5, 8),
                "end_date": date(2017, 6, 12),
            }, {
                "title": "task 3",
                "description": "some task 3",
                "access_control_list": [
                    acl_helper.get_acl_json(role_id, self.user.id)],
                "start_date": date(2017, 5, 31),
                "end_date": date(2017, 6, 13),
            }, {
                "title": "task 4",
                "description": "some task 4",
                "access_control_list": [
                    acl_helper.get_acl_json(role_id, self.user.id)],
                "start_date": date(2017, 6, 2),
                "end_date": date(2017, 6, 14),
            }, {
                "title": "task 5",
                "description": "some task 5",
                "access_control_list": [
                    acl_helper.get_acl_json(role_id, self.user.id)],
                "start_date": date(2017, 6, 8),
                "end_date": date(2017, 6, 15),
            }],
            "task_group_objects": self.random_objects
        }]
    }

  @ddt.unpack
  @ddt.data(
      ("2017-06-12 12:12:12", ["task 1"], ["task 2"], ["task 3"]),
      ("2017-06-13 13:13:13", ["task 1", "task 2"], ["task 3"], ["task 4"]),
  )
  @patch("ggrc.notifications.common.send_email")
  def test_creating_obsolete_notifications(
      self, fake_now, expected_overdue, expected_due_today, expected_due_in, _
  ):
    """Notifications already obsolete on creation date should not be created.
    """
    with freeze_time("2017-06-12 09:39:32"):
      tmp = self.one_time_workflow.copy()
      _, workflow = self.wf_generator.generate_workflow(tmp)
      self.wf_generator.generate_cycle(workflow)
      response, workflow = self.wf_generator.activate_workflow(workflow)
      self.assert200(response)

    user = models.Person.query.get(self.user.id)

    with freeze_time(fake_now):
      # mark all yeasterday notifications as sent
      models.all_models.Notification.query.filter(
          sa.func.DATE(models.all_models.Notification.send_on) < date.today()
      ).update({models.all_models.Notification.sent_at:
                datetime.now() - timedelta(1)},
               synchronize_session="fetch")

      _, notif_data = common.get_daily_notifications()
      user_notifs = notif_data.get(user.email, {})

      actual_overdue = [n['title'] for n in
                        user_notifs.get("task_overdue", {}).itervalues()]
      actual_overdue.sort()
      self.assertEqual(actual_overdue, expected_overdue)

      self.assertEqual(
          [n['title'] for n in user_notifs.get("due_today", {}).itervalues()],
          expected_due_today)

      self.assertEqual(
          [n['title'] for n in user_notifs.get("due_in", {}).itervalues()],
          expected_due_in)
Esempio n. 32
0
class TestCycleTaskStatusChange(TestCase):
    """ This class contains simple one time workflow tests that are not
  in the gsheet test grid
  """
    def setUp(self):
        super(TestCycleTaskStatusChange, self).setUp()
        self.api = Api()
        self.wf_generator = WorkflowsGenerator()
        self.object_generator = ObjectGenerator()
        all_models.Notification.query.delete()
        self.random_objects = self.object_generator.generate_random_objects(2)
        self.user = self.create_user_with_role(role="Administrator")
        self.secondary_assignee = self.create_user_with_role(role="Reader")
        self.create_test_cases()

        def init_decorator(init):
            def new_init(self, *args, **kwargs):
                init(self, *args, **kwargs)
                if hasattr(self, "created_at"):
                    self.created_at = datetime.now()

            return new_init

        all_models.Notification.__init__ = init_decorator(
            all_models.Notification.__init__)

    def test_task_declined_notification_created(self):
        """Test declined cycle task notifications"""
        with freeze_time("2015-05-01"):
            _, workflow = self.wf_generator.generate_workflow(
                self.one_time_workflow_1)

            _, cycle = self.wf_generator.generate_cycle(workflow)
            self.wf_generator.activate_workflow(workflow)

            cycle = Cycle.query.get(cycle.id)
            task1 = CycleTaskGroupObjectTask.query.get(
                cycle.cycle_task_group_object_tasks[0].id)

            self.task_change_status(task1, "Declined")

            notif = self.get_notifications_by_type(task1,
                                                   "cycle_task_declined")
            self.assertEqual(len(notif), 1,
                             "notifications: {}".format(str(notif)))

    def test_all_tasks_finished_notification_created(self):
        """Test all task completed notifications"""
        with freeze_time("2015-05-01 13:20:34"):
            _, workflow = self.wf_generator.generate_workflow(
                self.one_time_workflow_1)

            _, cycle = self.wf_generator.generate_cycle(workflow)
            self.wf_generator.activate_workflow(workflow)

            cycle = Cycle.query.get(cycle.id)
            task1 = CycleTaskGroupObjectTask.query.get(
                cycle.cycle_task_group_object_tasks[0].id)

            self.task_change_status(task1)

            generate_cycle_tasks_notifs()

            notif = self.get_notifications_by_type(
                cycle, "all_cycle_tasks_completed")
            self.assertEqual(len(notif), 1,
                             "notifications: {}".format(str(notif)))

            notif = self.get_notifications_by_type(
                task1, "all_cycle_tasks_completed")
            self.assertEqual(notif, [])

    def test_multi_all_tasks_finished_notification_created(self):
        """Test several all task completed notifications"""

        with freeze_time("2015-05-01 13:20:34"):
            _, workflow = self.wf_generator.generate_workflow(
                self.one_time_workflow_2)

            _, cycle = self.wf_generator.generate_cycle(workflow)
            self.wf_generator.activate_workflow(workflow)

            cycle = Cycle.query.get(cycle.id)
            task1 = CycleTaskGroupObjectTask.query.get(
                cycle.cycle_task_group_object_tasks[0].id)

            self.task_change_status(task1)

            generate_cycle_tasks_notifs()
            notif = self.get_notifications_by_type(
                cycle, "all_cycle_tasks_completed")

            # there is still one task in the cycle, so there should be no
            # notifications for all tasks completed
            self.assertEqual(notif, [])

            notif = self.get_notifications_by_type(
                task1, "all_cycle_tasks_completed")

            # The task was verified, so there should be no notifications left for due
            # dates.
            self.assertEqual(notif, [])

            task2 = CycleTaskGroupObjectTask.query.get(
                cycle.cycle_task_group_object_tasks[1].id)

            self.task_change_status(task2)

            generate_cycle_tasks_notifs()
            notif = self.get_notifications_by_type(
                cycle, "all_cycle_tasks_completed")

            self.assertEqual(len(notif), 1,
                             "notifications: {}".format(str(notif)))

    @patch("ggrc.notifications.common.send_email")
    def test_single_task_declined(self, mock_mail):
        """Test moving the end date to the future on declined task.

    It is done before due_in and due_today notifications have been sent.
    """

        with freeze_time("2015-05-01"):
            _, workflow = self.wf_generator.generate_workflow(
                self.one_time_workflow_1)

            _, cycle = self.wf_generator.generate_cycle(workflow)
            self.wf_generator.activate_workflow(workflow)

        with freeze_time("2015-05-02"):
            common.send_daily_digest_notifications()

            cycle = Cycle.query.get(cycle.id)
            task1 = CycleTaskGroupObjectTask.query.get(
                cycle.cycle_task_group_object_tasks[0].id)

            self.task_change_status(task1, "Finished")

            _, notif_data = common.get_daily_notifications()
            self.assertEqual(notif_data, {})

        task_assignees = [self.user, self.secondary_assignee]
        with freeze_time("2015-05-02"):
            common.send_daily_digest_notifications()

            cycle = Cycle.query.get(cycle.id)
            task1 = CycleTaskGroupObjectTask.query.get(
                cycle.cycle_task_group_object_tasks[0].id)

            self.task_change_status(task1, "Declined")

            _, notif_data = common.get_daily_notifications()
            for user in task_assignees:
                self.assertIn(user.email, notif_data)
                self.assertIn("task_declined", notif_data[user.email])

    @patch("ggrc.notifications.common.send_email")
    def test_single_task_accepted(self, mock_mail):
        """Test moving the end date to the future on accepted task.

    It is done before due_in and due_today notifications have been sent.
    """

        with freeze_time("2015-05-01"):
            _, workflow = self.wf_generator.generate_workflow(
                self.one_time_workflow_1)

            _, cycle = self.wf_generator.generate_cycle(workflow)
            self.wf_generator.activate_workflow(workflow)

        with freeze_time("2015-05-02"):
            common.send_daily_digest_notifications()

            cycle = Cycle.query.get(cycle.id)
            task1 = CycleTaskGroupObjectTask.query.get(
                cycle.cycle_task_group_object_tasks[0].id)

            self.task_change_status(task1, "Finished")

            _, notif_data = common.get_daily_notifications()
            self.assertEqual(notif_data, {})

        task_assignees = [self.user, self.secondary_assignee]
        with freeze_time("2015-05-03 13:20:34"):
            cycle = Cycle.query.get(cycle.id)
            task1 = CycleTaskGroupObjectTask.query.get(
                cycle.cycle_task_group_object_tasks[0].id)

            self.task_change_status(task1)

            generate_cycle_tasks_notifs()
            _, notif_data = common.get_daily_notifications()
            for user in task_assignees:
                self.assertNotIn(user.email, notif_data)
            self.assertIn("all_tasks_completed",
                          notif_data["*****@*****.**"])

    @patch("ggrc.notifications.common.send_email")
    def test_end_cycle(self, mock_mail):
        """Manually ending cycle should stop all notifications for that cycle."""

        with freeze_time("2015-05-01"):
            _, workflow = self.wf_generator.generate_workflow(
                self.one_time_workflow_1)
            _, cycle = self.wf_generator.generate_cycle(workflow)
            self.wf_generator.activate_workflow(workflow)

        task_assignees = [self.user, self.secondary_assignee]
        with freeze_time("2015-05-03"):
            _, notif_data = common.get_daily_notifications()
            cycle = Cycle.query.get(cycle.id)
            for user in task_assignees:
                self.assertIn(user.email, notif_data)
            self.wf_generator.modify_object(cycle, data={"is_current": False})
            cycle = Cycle.query.get(cycle.id)
            self.assertFalse(cycle.is_current)

            _, notif_data = common.get_daily_notifications()
            for user in task_assignees:
                self.assertNotIn(user.email, notif_data)

    def create_test_cases(self):
        def person_dict(person_id):
            return {
                "href": "/api/people/%d" % person_id,
                "id": person_id,
                "type": "Person"
            }

        task_assignee_role_id = all_models.AccessControlRole.query.filter(
            all_models.AccessControlRole.name == "Task Assignees",
            all_models.AccessControlRole.object_type == "TaskGroupTask",
        ).one().id

        task_secondary_assignee = all_models.AccessControlRole.query.filter(
            all_models.AccessControlRole.name == "Task Secondary Assignees",
            all_models.AccessControlRole.object_type == "TaskGroupTask",
        ).one().id

        self.one_time_workflow_1 = {
            "title":
            "one time test workflow",
            "notify_on_change":
            True,
            "description":
            "some test workflow",
            "is_verification_needed":
            True,
            # admin will be current user with id == 1
            "task_groups": [{
                "title":
                "single task group",
                "contact":
                person_dict(self.user.id),
                "task_group_tasks": [{
                    "title":
                    "task 1",
                    "description":
                    "single task in a wf",
                    "access_control_list": [
                        acl_helper.get_acl_json(task_assignee_role_id,
                                                self.user.id),
                        acl_helper.get_acl_json(task_secondary_assignee,
                                                self.secondary_assignee.id),
                    ],
                    "start_date":
                    date(2015, 5, 1),  # friday
                    "end_date":
                    date(2015, 5, 5),
                }],
            }]
        }

        self.one_time_workflow_2 = {
            "title":
            "one time test workflow",
            "notify_on_change":
            True,
            "description":
            "some test workflow",
            "is_verification_needed":
            True,
            # admin will be current user with id == 1
            "task_groups": [{
                "title":
                "one time task group",
                "contact":
                person_dict(self.user.id),
                "task_group_tasks": [
                    {
                        "title":
                        "task 1",
                        "description":
                        "two taks in wf with different objects",
                        "access_control_list": [
                            acl_helper.get_acl_json(task_assignee_role_id,
                                                    self.user.id)
                        ],
                        "start_date":
                        date(2015, 5, 1),  # friday
                        "end_date":
                        date(2015, 5, 5),
                    },
                    {
                        "title":
                        "task 2",
                        "description":
                        "two taks in wf with different objects",
                        "access_control_list": [
                            acl_helper.get_acl_json(task_assignee_role_id,
                                                    self.user.id)
                        ],
                        "start_date":
                        date(2015, 5, 1),  # friday
                        "end_date":
                        date(2015, 5, 5),
                    }
                ],
                "task_group_objects":
                self.random_objects
            }]
        }

    def get_notification_type(self, name):
        return all_models.NotificationType.query.filter(
            all_models.NotificationType.name == name).one()

    def task_change_status(self, task, status="Verified"):
        self.wf_generator.modify_object(task, data={"status": status})

        task = CycleTaskGroupObjectTask.query.get(task.id)

        self.assertEqual(task.status, status)

    def get_notifications_by_type(self, obj, notif_type):
        return all_models.Notification.query.filter(
            and_(
                all_models.Notification.object_id == obj.id,
                all_models.Notification.object_type == obj.type,
                all_models.Notification.sent_at == None,  # noqa
                all_models.Notification.notification_type ==
                self.get_notification_type(notif_type))).all()
class TestCycleTaskImportUpdate(BaseTestCycleTaskImportUpdate):

  """ This class contains simple cycle task update tests using import
  functionality
  """

  CSV_DIR = join(abspath(dirname(__file__)), "test_csvs/")

  def setUp(self):
    super(TestCycleTaskImportUpdate, self).setUp()
    self.wf_generator = WorkflowsGenerator()
    self.object_generator = ObjectGenerator()
    self.random_objects = self.object_generator.generate_random_objects(2)
    _, self.person_1 = self.object_generator.generate_person(
        user_role="Administrator")
    self.ftime_active = "2016-07-01"
    self.ftime_historical = "2014-05-01"
    self._create_test_cases_data()
    # It is needed because cycle-tasks are generated automatically with
    # 'slug' based on auto_increment 'id' field.
    # At start of each test we suppose that created cycle-task's 'slug'
    # lie in range from 1 to 10.
    db.session.execute('ALTER TABLE cycle_task_group_object_tasks '
                       'AUTO_INCREMENT = 1')

  def test_cycle_task_correct(self):
    """Test cycle task update via import with correct data"""
    self._generate_cycle_tasks()
    with freeze_time(self.ftime_active):
      response = self.import_file("cycle_task_correct.csv")
      self._check_csv_response(response, {})
      self._cmp_tasks(self.expected_cycle_task_correct)

  def test_cycle_task_warnings(self):
    """Test cycle task update via import with data which is the reason of
    warnings about non-importable columns."""
    self._generate_cycle_tasks()
    with freeze_time(self.ftime_active):
      response = self.import_file("cycle_task_warnings.csv")
      self._check_csv_response(response, self.expected_warnings)
      self._cmp_tasks(self.expected_cycle_task_correct)

  def test_cycle_task_create_error(self):
    """Test cycle task update via import with data which is the reason of
    errors about new cycle task creation."""
    self._generate_cycle_tasks()
    with freeze_time(self.ftime_active):
      response = self.import_file("cycle_task_create_error.csv")
      self._check_csv_response(response, self.expected_create_error)
      self._cmp_tasks(self.expected_cycle_task_correct)

  def test_cycle_task_date_error(self):
    """Test cycle task update via import with data which is the reason of
    errors about incorrect dates in csv file."""
    self._generate_cycle_tasks()
    with freeze_time(self.ftime_active):
      response = self.import_file("cycle_task_date_error.csv")
      self._check_csv_response(response, self.expected_date_error)
      self._cmp_tasks(self.expected_cycle_task_date_error)

  def test_cycle_task_permission_error(self):
    """Test cycle task update via import with non-admin user which is the
    reason of error. Only admin can update cycle tasks via import."""
    self._generate_cycle_tasks()
    with freeze_time(self.ftime_active):
      _, creator = self.object_generator.generate_person(user_role="Creator")
      response = self.import_file("cycle_task_correct.csv", person=creator)
      self._check_csv_response(response, self.expected_permission_error)
      # Cycle tasks' data shouldn't be changed in test DB after import run from
      # non-admin user
      expected_cycle_task_permission_error = {}
      expected_cycle_task_permission_error.update(
          self.generated_cycle_tasks_active)
      expected_cycle_task_permission_error.update(
          self.generated_cycle_tasks_historical)
      self._cmp_tasks(expected_cycle_task_permission_error)

  def _cmp_tasks(self, expected_ctasks):
    """Compare tasks values from argument's list and test DB."""
    for ctask in db.session.query(CycleTaskGroupObjectTask).all():
      if ctask.slug not in expected_ctasks:
        continue
      exp_task = expected_ctasks[ctask.slug]
      for attr, val in exp_task.iteritems():
        self.assertEqual(str(getattr(ctask, attr, None)), val)

  # pylint: disable=too-many-arguments
  def _activate_workflow(self, ftime, workflow, task_group, task_group_tasks,
                         random_object, cycle_tasks):
    """Helper which is responsible for active cycle-tasks creation"""
    with freeze_time(ftime):
      _, wf = self.wf_generator.generate_workflow(workflow)
      _, tg = self.wf_generator.generate_task_group(wf, task_group)
      for task in task_group_tasks:
        self.wf_generator.generate_task_group_task(tg, task)
      self.wf_generator.generate_task_group_object(tg, random_object)

      _, cycle = self.wf_generator.generate_cycle(wf)
      self.wf_generator.activate_workflow(wf)

      for exp_slug, exp_task in cycle_tasks.iteritems():
        obj = db.session.query(CycleTaskGroupObjectTask).filter_by(
            slug=exp_slug
        ).first()
        if exp_task["status"] == "Verified":
          self.wf_generator.modify_object(obj, {"status": "Finished"})
        self.wf_generator.modify_object(obj, {"status": exp_task["status"]})
    self._cmp_tasks(cycle_tasks)
    return cycle

  def _generate_cycle_tasks(self):
    """Helper which is responsible for test data creation"""
    self._activate_workflow(self.ftime_active, self.workflow_active,
                            self.task_group_active,
                            self.task_group_tasks_active,
                            self.random_objects[0],
                            self.generated_cycle_tasks_active)
    cycle = self._activate_workflow(self.ftime_historical,
                                    self.workflow_historical,
                                    self.task_group_historical,
                                    self.task_group_tasks_historical,
                                    self.random_objects[1],
                                    self.generated_cycle_tasks_historical)
    with freeze_time(self.ftime_historical):
      cycle = Cycle.query.get(cycle.id)
      self.wf_generator.modify_object(cycle, data={"is_current": False})

  def _create_test_cases_data(self):
    """Create test cases data: for object generation,
    expected data for checks"""
    def person_dict(person_id):
      """Return person data"""
      return {
          "href": "/api/people/%d" % person_id,
          "id": person_id,
          "type": "Person"
      }

    self.workflow_active = {
        "title": "workflow active title",
        "description": "workflow active description",
        "frequency": "one_time",
        "owners": [person_dict(self.person_1.id)],
        "notify_on_change": False,
    }

    self.task_group_active = {
        "title": "task group active title",
        "contact": person_dict(self.person_1.id),
    }

    self.task_group_tasks_active = [{
        "title": "task active title 1",
        "description": "task active description 1",
        "contact": person_dict(self.person_1.id),
        "start_date": "07/01/2016",
        "end_date": "07/06/2016",
    }, {
        "title": "task active title 2",
        "description": "task active description 2",
        "contact": person_dict(self.person_1.id),
        "start_date": "07/07/2016",
        "end_date": "07/12/2016",
    }, {
        "title": "task active title 3",
        "description": "task active description 3",
        "contact": person_dict(self.person_1.id),
        "start_date": "07/13/2016",
        "end_date": "07/18/2016",
    }, {
        "title": "task active title 4",
        "description": "task active description 4",
        "contact": person_dict(self.person_1.id),
        "start_date": "07/19/2016",
        "end_date": "07/24/2016",
    }, {
        "title": "task active title 5",
        "description": "task active description 5",
        "contact": person_dict(self.person_1.id),
        "start_date": "07/25/2016",
        "end_date": "07/30/2016",
    }]

    # Active cycle tasks which should be generated from previous structure
    # at the beginning of each test
    self.generated_cycle_tasks_active = {
        "CYCLETASK-1": {
            "title": self.task_group_tasks_active[0]["title"],
            "description": self.task_group_tasks_active[0]["description"],
            "start_date": "2016-07-01",
            "end_date": "2016-07-06",
            "finished_date": "None",
            "verified_date": "None",
            "status": "Assigned"
        },
        "CYCLETASK-2": {
            "title": self.task_group_tasks_active[1]["title"],
            "description": self.task_group_tasks_active[1]["description"],
            "start_date": "2016-07-07",
            "end_date": "2016-07-12",
            "finished_date": "None",
            "verified_date": "None",
            "status": "Declined"
        },
        "CYCLETASK-3": {
            "title": self.task_group_tasks_active[2]["title"],
            "description": self.task_group_tasks_active[2]["description"],
            "start_date": "2016-07-13",
            "end_date": "2016-07-18",
            "finished_date": "None",
            "verified_date": "None",
            "status": "InProgress"
        },
        "CYCLETASK-4": {
            "title": self.task_group_tasks_active[3]["title"],
            "description": self.task_group_tasks_active[3]["description"],
            "start_date": "2016-07-19",
            "end_date": "2016-07-24",
            "finished_date": "2016-07-01 00:00:00",
            "verified_date": "None",
            "status": "Finished"
        },
        "CYCLETASK-5": {
            "title": self.task_group_tasks_active[4]["title"],
            "description": self.task_group_tasks_active[4]["description"],
            "start_date": "2016-07-25",
            "end_date": "2016-07-30",
            "finished_date": "2016-07-01 00:00:00",
            "verified_date": "2016-07-01 00:00:00",
            "status": "Verified"
        }
    }

    self.workflow_historical = {
        "title": "workflow historical title",
        "description": "workflow historical description",
        "frequency": "one_time",
        "owners": [person_dict(self.person_1.id)],
        "notify_on_change": False,
    }

    self.task_group_historical = {
        "title": "task group historical title",
        "contact": person_dict(self.person_1.id),
    }

    self.task_group_tasks_historical = [{
        "title": "task historical title 1",
        "description": "task historical description 1",
        "contact": person_dict(self.person_1.id),
        "start_date": "05/01/2014",
        "end_date": "05/06/2014",
    }, {
        "title": "task historical title 2",
        "description": "task historical description 2",
        "contact": person_dict(self.person_1.id),
        "start_date": "05/07/2014",
        "end_date": "05/12/2014",
    }, {
        "title": "task historical title 3",
        "description": "task historical description 3",
        "contact": person_dict(self.person_1.id),
        "start_date": "05/13/2014",
        "end_date": "05/18/2014",
    }, {
        "title": "task historical title 4",
        "description": "task historical description 4",
        "contact": person_dict(self.person_1.id),
        "start_date": "05/19/2014",
        "end_date": "05/24/2014",
    }, {
        "title": "task historical title 5",
        "description": "task historical description 5",
        "contact": person_dict(self.person_1.id),
        "start_date": "05/25/2014",
        "end_date": "05/30/2014",
    },
    ]

    # Historical cycle tasks which should be generated from previous structure
    # at the beginning of each test.
    self.generated_cycle_tasks_historical = {
        "CYCLETASK-6": {
            "title": self.task_group_tasks_historical[0]["title"],
            "description": self.task_group_tasks_historical[0]["description"],
            "start_date": "2014-05-01",
            "end_date": "2014-05-06",
            "finished_date": "None",
            "verified_date": "None",
            "status": "Assigned"
        },
        "CYCLETASK-7": {
            "title": self.task_group_tasks_historical[1]["title"],
            "description": self.task_group_tasks_historical[1]["description"],
            "start_date": "2014-05-07",
            "end_date": "2014-05-12",
            "finished_date": "None",
            "verified_date": "None",
            "status": "Declined"
        },
        "CYCLETASK-8": {
            "title": self.task_group_tasks_historical[2]["title"],
            "description": self.task_group_tasks_historical[2]["description"],
            "start_date": "2014-05-13",
            "end_date": "2014-05-18",
            "finished_date": "None",
            "verified_date": "None",
            "status": "InProgress"
        },
        "CYCLETASK-9": {
            "title": self.task_group_tasks_historical[3]["title"],
            "description": self.task_group_tasks_historical[3]["description"],
            "start_date": "2014-05-19",
            "end_date": "2014-05-24",
            "finished_date": "2014-05-01 00:00:00",
            "verified_date": "None",
            "status": "Finished"
        },
        "CYCLETASK-10": {
            "title": self.task_group_tasks_historical[4]["title"],
            "description": self.task_group_tasks_historical[4]["description"],
            "start_date": "2014-05-25",
            "end_date": "2014-05-30",
            "finished_date": "2014-05-01 00:00:00",
            "verified_date": "2014-05-01 00:00:00",
            "status": "Verified"
        }
    }

    # Expected cycle tasks which should be created in correct cycle task update
    # case. It is needed for most tests.
    self.expected_cycle_task_correct = {
        "CYCLETASK-1": {
            "title": self.task_group_tasks_active[0]["title"] + " one",
            "description":
                self.task_group_tasks_active[0]["description"] + " one",
            "start_date": "2016-06-01",
            "end_date": "2016-06-06",
            "finished_date": "None",
            "verified_date": "None",
            "status": "Assigned"
        },
        "CYCLETASK-2": {
            "title": self.task_group_tasks_active[1]["title"] + " two",
            "description":
                self.task_group_tasks_active[1]["description"] + " two",
            "start_date": "2016-06-07",
            "end_date": "2016-06-12",
            "finished_date": "None",
            "verified_date": "None",
            "status": "Declined"
        },
        "CYCLETASK-3": {
            "title": self.task_group_tasks_active[2]["title"] + " three",
            "description":
                self.task_group_tasks_active[2]["description"] + " three",
            "start_date": "2016-06-13",
            "end_date": "2016-06-18",
            "finished_date": "None",
            "verified_date": "None",
            "status": "InProgress"
        },
        "CYCLETASK-4": {
            "title": self.task_group_tasks_active[3]["title"] + " four",
            "description":
                self.task_group_tasks_active[3]["description"] + " four",
            "start_date": "2016-06-19",
            "end_date": "2016-06-24",
            "finished_date": "2016-07-19 00:00:00",
            "verified_date": "None",
            "status": "Finished"
        },
        "CYCLETASK-5": {
            "title": self.task_group_tasks_active[4]["title"] + " five",
            "description":
                self.task_group_tasks_active[4]["description"] + " five",
            "start_date": "2016-06-25",
            "end_date": "2016-06-30",
            "finished_date": "2016-07-25 00:00:00",
            "verified_date": "2016-08-30 00:00:00",
            "status": "Verified"
        },
        "CYCLETASK-6": {
            "title": self.task_group_tasks_historical[0]["title"] + " one",
            "description":
                self.task_group_tasks_historical[0]["description"] + " one",
            "start_date": "2014-04-01",
            "end_date": "2014-04-06",
            "finished_date": "2014-05-01 00:00:00",
            "verified_date": "2014-06-06 00:00:00",
            "status": "Assigned"
        },
        "CYCLETASK-7": {
            "title": self.task_group_tasks_historical[1]["title"] + " two",
            "description":
                self.task_group_tasks_historical[1]["description"] + " two",
            "start_date": "2014-04-07",
            "end_date": "2014-04-12",
            "finished_date": "2014-05-07 00:00:00",
            "verified_date": "None",
            "status": "Declined"
        },
        "CYCLETASK-8": {
            "title": self.task_group_tasks_historical[2]["title"] + " three",
            "description":
                self.task_group_tasks_historical[2]["description"] + " three",
            "start_date": "2014-04-13",
            "end_date": "2014-04-18",
            "finished_date": "2014-05-13 00:00:00",
            "verified_date": "2014-06-18 00:00:00",
            "status": "InProgress"
        },
        "CYCLETASK-9": {
            "title": self.task_group_tasks_historical[3]["title"] + " four",
            "description":
                self.task_group_tasks_historical[3]["description"] + " four",
            "start_date": "2014-04-19",
            "end_date": "2014-04-24",
            "finished_date": "2014-05-19 00:00:00",
            "verified_date": "None",
            "status": "Finished"
        },
        "CYCLETASK-10": {
            "title": self.task_group_tasks_historical[4]["title"] + " five",
            "description":
                self.task_group_tasks_historical[4]["description"] + " five",
            "start_date": "2014-04-25",
            "end_date": "2014-04-30",
            "finished_date": "2014-05-25 00:00:00",
            "verified_date": "2014-06-30 00:00:00",
            "status": "Verified"
        }
    }

    # Below is description of warning for non-importable columns. It is needed
    # for test_cycle_task_warnings.
    importable_column_names = []
    for field_name in CycleTaskGroupObjectTask.IMPORTABLE_FIELDS:
      if field_name == 'slug':
        continue
      # pylint: disable=protected-access
      name = CycleTaskGroupObjectTask._aliases.get(field_name, field_name)
      if isinstance(name, dict):
        name = name['display_name']
      importable_column_names.append(name)
    self.expected_warnings = self.generate_expected_warning(
        *importable_column_names)

    # This is an error message which should be shown during
    # test_cycle_task_create_error test
    self.expected_create_error = {
        'Cycle Task': {
            'row_errors': {errors.CREATE_INSTANCE_ERROR.format(line=13)}
        }
    }

    # Below is expected date errors for test_cycle_task_date_error. They should
    # be shown during date validator's tests.
    self.expected_date_error = {
        'Cycle Task': {
            'row_errors': {
                errors.INVALID_START_END_DATES.format(
                    line=3,
                    start_date="Start Date",
                    end_date="End Date",
                ),
                errors.INVALID_STATUS_DATE_CORRELATION.format(
                    line=4,
                    date="Actual Finish Date",
                    status="not Finished",
                ),
                errors.INVALID_STATUS_DATE_CORRELATION.format(
                    line=5,
                    date="Actual Verified Date",
                    status="not Verified",
                ),
                errors.INVALID_STATUS_DATE_CORRELATION.format(
                    line=6,
                    date="Actual Verified Date",
                    status="not Verified",
                ),
                errors.INVALID_START_END_DATES.format(
                    line=7,
                    start_date="Actual Finish Date",
                    end_date="Actual Verified Date",
                ),
                errors.INVALID_START_END_DATES.format(
                    line=8,
                    start_date="Start Date",
                    end_date="End Date",
                ),
                errors.MISSING_VALUE_ERROR.format(
                    line=9,
                    column_name="Actual Finish Date",
                ),
                errors.INVALID_START_END_DATES.format(
                    line=10,
                    start_date="Actual Finish Date",
                    end_date="Actual Verified Date",
                ),
            },
        }
    }
    # Below is expected cycle-tasks data which should appear in test DB after
    # test_cycle_task_date_error run
    self.expected_cycle_task_date_error = dict()
    self.expected_cycle_task_date_error.update(
        self.generated_cycle_tasks_active)
    self.expected_cycle_task_date_error.update(
        self.generated_cycle_tasks_historical)
    self.expected_cycle_task_date_error["CYCLETASK-9"] = (
        self.expected_cycle_task_correct["CYCLETASK-9"])
    self.expected_cycle_task_date_error["CYCLETASK-10"] = (
        self.expected_cycle_task_correct["CYCLETASK-10"])

    # Expected error message which should be shown after
    # test_cycle_task_permission_error run
    self.expected_permission_error = {
        'Cycle Task': {
            'block_errors': {errors.PERMISSION_ERROR.format(line=2)}
        }
    }
class TestCycleTaskImportUpdate(TestCase):
    """ This class contains simple cycle task update tests using import
  functionality
  """

    CSV_DIR = join(abspath(dirname(__file__)), "test_csvs/")

    def setUp(self):
        super(TestCycleTaskImportUpdate, self).setUp()
        self.wf_generator = WorkflowsGenerator()
        self.object_generator = ObjectGenerator()
        self.random_objects = self.object_generator.generate_random_objects(2)
        _, self.person_1 = self.object_generator.generate_person(
            user_role="Administrator")
        self.ftime_active = "2016-07-01"
        self.ftime_historical = "2014-05-01"
        self._create_test_cases_data()
        # It is needed because cycle-tasks are generated automatically with
        # 'slug' based on auto_increment 'id' field.
        # At start of each test we suppose that created cycle-task's 'slug'
        # lie in range from 1 to 10.
        db.session.execute('ALTER TABLE cycle_task_group_object_tasks '
                           'AUTO_INCREMENT = 1')

    def test_cycle_task_correct(self):
        """Test cycle task update via import with correct data"""
        self._generate_cycle_tasks()
        with freeze_time(self.ftime_active):
            response = self.import_file("cycle_task_correct.csv")
            self._check_csv_response(response, {})
            self._cmp_tasks(self.expected_cycle_task_correct)

    def test_cycle_task_warnings(self):
        """Test cycle task update via import with data which is the reason of
    warnings about non-importable columns."""
        self._generate_cycle_tasks()
        with freeze_time(self.ftime_active):
            response = self.import_file("cycle_task_warnings.csv")
            self._check_csv_response(response, self.expected_warnings)
            self._cmp_tasks(self.expected_cycle_task_correct)

    def test_cycle_task_create_error(self):
        """Test cycle task update via import with data which is the reason of
    errors about new cycle task creation."""
        self._generate_cycle_tasks()
        with freeze_time(self.ftime_active):
            response = self.import_file("cycle_task_create_error.csv")
            self._check_csv_response(response, self.expected_create_error)
            self._cmp_tasks(self.expected_cycle_task_correct)

    def test_cycle_task_date_error(self):
        """Test cycle task update via import with data which is the reason of
    errors about incorrect dates in csv file."""
        self._generate_cycle_tasks()
        with freeze_time(self.ftime_active):
            response = self.import_file("cycle_task_date_error.csv")
            self._check_csv_response(response, self.expected_date_error)
            self._cmp_tasks(self.expected_cycle_task_date_error)

    def test_cycle_task_permission_error(self):
        """Test cycle task update via import with non-admin user which is the
    reason of error. Only admin can update cycle tasks via import."""
        self._generate_cycle_tasks()
        with freeze_time(self.ftime_active):
            _, creator = self.object_generator.generate_person(
                user_role="Creator")
            response = self.import_file("cycle_task_correct.csv",
                                        person=creator)
            self._check_csv_response(response, self.expected_permission_error)
            # Cycle tasks' data shouldn't be changed in test DB after import run from
            # non-admin user
            expected_cycle_task_permission_error = {}
            expected_cycle_task_permission_error.update(
                self.generated_cycle_tasks_active)
            expected_cycle_task_permission_error.update(
                self.generated_cycle_tasks_historical)
            self._cmp_tasks(expected_cycle_task_permission_error)

    def _cmp_tasks(self, expected_ctasks):
        """Compare tasks values from argument's list and test DB."""
        for ctask in db.session.query(CycleTaskGroupObjectTask).all():
            if ctask.slug not in expected_ctasks:
                continue
            exp_task = expected_ctasks[ctask.slug]
            for attr, val in exp_task.iteritems():
                self.assertEqual(str(getattr(ctask, attr, None)), val)

    # pylint: disable=too-many-arguments
    def _activate_workflow(self, ftime, workflow, task_group, task_group_tasks,
                           random_object, cycle_tasks):
        """Helper which is responsible for active cycle-tasks creation"""
        with freeze_time(ftime):
            _, wf = self.wf_generator.generate_workflow(workflow)
            _, tg = self.wf_generator.generate_task_group(wf, task_group)
            for task in task_group_tasks:
                self.wf_generator.generate_task_group_task(tg, task)
            self.wf_generator.generate_task_group_object(tg, random_object)

            _, cycle = self.wf_generator.generate_cycle(wf)
            self.wf_generator.activate_workflow(wf)

            for exp_slug, exp_task in cycle_tasks.iteritems():
                obj = db.session.query(CycleTaskGroupObjectTask).filter_by(
                    slug=exp_slug).first()
                if exp_task["status"] == "Verified":
                    self.wf_generator.modify_object(obj,
                                                    {"status": "Finished"})
                self.wf_generator.modify_object(obj,
                                                {"status": exp_task["status"]})
        self._cmp_tasks(cycle_tasks)
        return cycle

    def _generate_cycle_tasks(self):
        """Helper which is responsible for test data creation"""
        self._activate_workflow(self.ftime_active, self.workflow_active,
                                self.task_group_active,
                                self.task_group_tasks_active,
                                self.random_objects[0],
                                self.generated_cycle_tasks_active)
        cycle = self._activate_workflow(self.ftime_historical,
                                        self.workflow_historical,
                                        self.task_group_historical,
                                        self.task_group_tasks_historical,
                                        self.random_objects[1],
                                        self.generated_cycle_tasks_historical)
        with freeze_time(self.ftime_historical):
            cycle = Cycle.query.get(cycle.id)
            self.wf_generator.modify_object(cycle, data={"is_current": False})

    def _create_test_cases_data(self):
        """Create test cases data: for object generation,
    expected data for checks"""
        def person_dict(person_id):
            """Return person data"""
            return {
                "href": "/api/people/%d" % person_id,
                "id": person_id,
                "type": "Person"
            }

        self.workflow_active = {
            "title": "workflow active title",
            "description": "workflow active description",
            "frequency": "one_time",
            "owners": [person_dict(self.person_1.id)],
            "notify_on_change": False,
        }

        self.task_group_active = {
            "title": "task group active title",
            "contact": person_dict(self.person_1.id),
        }

        self.task_group_tasks_active = [{
            "title":
            "task active title 1",
            "description":
            "task active description 1",
            "contact":
            person_dict(self.person_1.id),
            "start_date":
            "07/01/2016",
            "end_date":
            "07/06/2016",
        }, {
            "title":
            "task active title 2",
            "description":
            "task active description 2",
            "contact":
            person_dict(self.person_1.id),
            "start_date":
            "07/07/2016",
            "end_date":
            "07/12/2016",
        }, {
            "title":
            "task active title 3",
            "description":
            "task active description 3",
            "contact":
            person_dict(self.person_1.id),
            "start_date":
            "07/13/2016",
            "end_date":
            "07/18/2016",
        }, {
            "title":
            "task active title 4",
            "description":
            "task active description 4",
            "contact":
            person_dict(self.person_1.id),
            "start_date":
            "07/19/2016",
            "end_date":
            "07/24/2016",
        }, {
            "title":
            "task active title 5",
            "description":
            "task active description 5",
            "contact":
            person_dict(self.person_1.id),
            "start_date":
            "07/25/2016",
            "end_date":
            "07/30/2016",
        }]

        # Active cycle tasks which should be generated from previous structure
        # at the beginning of each test
        self.generated_cycle_tasks_active = {
            "CYCLETASK-1": {
                "title": self.task_group_tasks_active[0]["title"],
                "description": self.task_group_tasks_active[0]["description"],
                "start_date": "2016-07-01",
                "end_date": "2016-07-06",
                "finished_date": "None",
                "verified_date": "None",
                "status": "Assigned"
            },
            "CYCLETASK-2": {
                "title": self.task_group_tasks_active[1]["title"],
                "description": self.task_group_tasks_active[1]["description"],
                "start_date": "2016-07-07",
                "end_date": "2016-07-12",
                "finished_date": "None",
                "verified_date": "None",
                "status": "Declined"
            },
            "CYCLETASK-3": {
                "title": self.task_group_tasks_active[2]["title"],
                "description": self.task_group_tasks_active[2]["description"],
                "start_date": "2016-07-13",
                "end_date": "2016-07-18",
                "finished_date": "None",
                "verified_date": "None",
                "status": "InProgress"
            },
            "CYCLETASK-4": {
                "title": self.task_group_tasks_active[3]["title"],
                "description": self.task_group_tasks_active[3]["description"],
                "start_date": "2016-07-19",
                "end_date": "2016-07-24",
                "finished_date": "2016-07-01 00:00:00",
                "verified_date": "None",
                "status": "Finished"
            },
            "CYCLETASK-5": {
                "title": self.task_group_tasks_active[4]["title"],
                "description": self.task_group_tasks_active[4]["description"],
                "start_date": "2016-07-25",
                "end_date": "2016-07-30",
                "finished_date": "2016-07-01 00:00:00",
                "verified_date": "2016-07-01 00:00:00",
                "status": "Verified"
            }
        }

        self.workflow_historical = {
            "title": "workflow historical title",
            "description": "workflow historical description",
            "frequency": "one_time",
            "owners": [person_dict(self.person_1.id)],
            "notify_on_change": False,
        }

        self.task_group_historical = {
            "title": "task group historical title",
            "contact": person_dict(self.person_1.id),
        }

        self.task_group_tasks_historical = [
            {
                "title": "task historical title 1",
                "description": "task historical description 1",
                "contact": person_dict(self.person_1.id),
                "start_date": "05/01/2014",
                "end_date": "05/06/2014",
            },
            {
                "title": "task historical title 2",
                "description": "task historical description 2",
                "contact": person_dict(self.person_1.id),
                "start_date": "05/07/2014",
                "end_date": "05/12/2014",
            },
            {
                "title": "task historical title 3",
                "description": "task historical description 3",
                "contact": person_dict(self.person_1.id),
                "start_date": "05/13/2014",
                "end_date": "05/18/2014",
            },
            {
                "title": "task historical title 4",
                "description": "task historical description 4",
                "contact": person_dict(self.person_1.id),
                "start_date": "05/19/2014",
                "end_date": "05/24/2014",
            },
            {
                "title": "task historical title 5",
                "description": "task historical description 5",
                "contact": person_dict(self.person_1.id),
                "start_date": "05/25/2014",
                "end_date": "05/30/2014",
            },
        ]

        # Historical cycle tasks which should be generated from previous structure
        # at the beginning of each test.
        self.generated_cycle_tasks_historical = {
            "CYCLETASK-6": {
                "title": self.task_group_tasks_historical[0]["title"],
                "description":
                self.task_group_tasks_historical[0]["description"],
                "start_date": "2014-05-01",
                "end_date": "2014-05-06",
                "finished_date": "None",
                "verified_date": "None",
                "status": "Assigned"
            },
            "CYCLETASK-7": {
                "title": self.task_group_tasks_historical[1]["title"],
                "description":
                self.task_group_tasks_historical[1]["description"],
                "start_date": "2014-05-07",
                "end_date": "2014-05-12",
                "finished_date": "None",
                "verified_date": "None",
                "status": "Declined"
            },
            "CYCLETASK-8": {
                "title": self.task_group_tasks_historical[2]["title"],
                "description":
                self.task_group_tasks_historical[2]["description"],
                "start_date": "2014-05-13",
                "end_date": "2014-05-18",
                "finished_date": "None",
                "verified_date": "None",
                "status": "InProgress"
            },
            "CYCLETASK-9": {
                "title": self.task_group_tasks_historical[3]["title"],
                "description":
                self.task_group_tasks_historical[3]["description"],
                "start_date": "2014-05-19",
                "end_date": "2014-05-24",
                "finished_date": "2014-05-01 00:00:00",
                "verified_date": "None",
                "status": "Finished"
            },
            "CYCLETASK-10": {
                "title": self.task_group_tasks_historical[4]["title"],
                "description":
                self.task_group_tasks_historical[4]["description"],
                "start_date": "2014-05-25",
                "end_date": "2014-05-30",
                "finished_date": "2014-05-01 00:00:00",
                "verified_date": "2014-05-01 00:00:00",
                "status": "Verified"
            }
        }

        # Expected cycle tasks which should be created in correct cycle task update
        # case. It is needed for most tests.
        self.expected_cycle_task_correct = {
            "CYCLETASK-1": {
                "title": self.task_group_tasks_active[0]["title"] + " one",
                "description":
                self.task_group_tasks_active[0]["description"] + " one",
                "start_date": "2016-06-01",
                "end_date": "2016-06-06",
                "finished_date": "None",
                "verified_date": "None",
                "status": "Assigned"
            },
            "CYCLETASK-2": {
                "title": self.task_group_tasks_active[1]["title"] + " two",
                "description":
                self.task_group_tasks_active[1]["description"] + " two",
                "start_date": "2016-06-07",
                "end_date": "2016-06-12",
                "finished_date": "None",
                "verified_date": "None",
                "status": "Declined"
            },
            "CYCLETASK-3": {
                "title": self.task_group_tasks_active[2]["title"] + " three",
                "description":
                self.task_group_tasks_active[2]["description"] + " three",
                "start_date": "2016-06-13",
                "end_date": "2016-06-18",
                "finished_date": "None",
                "verified_date": "None",
                "status": "InProgress"
            },
            "CYCLETASK-4": {
                "title": self.task_group_tasks_active[3]["title"] + " four",
                "description":
                self.task_group_tasks_active[3]["description"] + " four",
                "start_date": "2016-06-19",
                "end_date": "2016-06-24",
                "finished_date": "2016-07-19 00:00:00",
                "verified_date": "None",
                "status": "Finished"
            },
            "CYCLETASK-5": {
                "title": self.task_group_tasks_active[4]["title"] + " five",
                "description":
                self.task_group_tasks_active[4]["description"] + " five",
                "start_date": "2016-06-25",
                "end_date": "2016-06-30",
                "finished_date": "2016-07-25 00:00:00",
                "verified_date": "2016-08-30 00:00:00",
                "status": "Verified"
            },
            "CYCLETASK-6": {
                "title":
                self.task_group_tasks_historical[0]["title"] + " one",
                "description":
                self.task_group_tasks_historical[0]["description"] + " one",
                "start_date":
                "2014-04-01",
                "end_date":
                "2014-04-06",
                "finished_date":
                "2014-05-01 00:00:00",
                "verified_date":
                "2014-06-06 00:00:00",
                "status":
                "Assigned"
            },
            "CYCLETASK-7": {
                "title":
                self.task_group_tasks_historical[1]["title"] + " two",
                "description":
                self.task_group_tasks_historical[1]["description"] + " two",
                "start_date":
                "2014-04-07",
                "end_date":
                "2014-04-12",
                "finished_date":
                "2014-05-07 00:00:00",
                "verified_date":
                "None",
                "status":
                "Declined"
            },
            "CYCLETASK-8": {
                "title":
                self.task_group_tasks_historical[2]["title"] + " three",
                "description":
                self.task_group_tasks_historical[2]["description"] + " three",
                "start_date":
                "2014-04-13",
                "end_date":
                "2014-04-18",
                "finished_date":
                "2014-05-13 00:00:00",
                "verified_date":
                "2014-06-18 00:00:00",
                "status":
                "InProgress"
            },
            "CYCLETASK-9": {
                "title":
                self.task_group_tasks_historical[3]["title"] + " four",
                "description":
                self.task_group_tasks_historical[3]["description"] + " four",
                "start_date":
                "2014-04-19",
                "end_date":
                "2014-04-24",
                "finished_date":
                "2014-05-19 00:00:00",
                "verified_date":
                "None",
                "status":
                "Finished"
            },
            "CYCLETASK-10": {
                "title":
                self.task_group_tasks_historical[4]["title"] + " five",
                "description":
                self.task_group_tasks_historical[4]["description"] + " five",
                "start_date":
                "2014-04-25",
                "end_date":
                "2014-04-30",
                "finished_date":
                "2014-05-25 00:00:00",
                "verified_date":
                "2014-06-30 00:00:00",
                "status":
                "Verified"
            }
        }

        # Below is description of warning for non-importable columns. It is needed
        # for test_cycle_task_warnings.
        importable_column_names = []
        for field_name in CycleTaskGroupObjectTask.IMPORTABLE_FIELDS:
            if field_name == 'slug':
                continue
            # pylint: disable=protected-access
            importable_column_names.append(
                CycleTaskGroupObjectTask._aliases.get(field_name, field_name))
        self.expected_warnings = {
            'Cycle Task Group Object Task': {
                'block_warnings': {
                    errors.ONLY_IMPORTABLE_COLUMNS_WARNING.format(
                        line=2, columns=", ".join(importable_column_names))
                },
            }
        }

        # This is an error message which should be shown during
        # test_cycle_task_create_error test
        self.expected_create_error = {
            'Cycle Task Group Object Task': {
                'row_errors': {errors.CREATE_INSTANCE_ERROR.format(line=13)}
            }
        }

        # Below is expected date errors for test_cycle_task_date_error. They should
        # be shown during date validator's tests.
        self.expected_date_error = {
            'Cycle Task Group Object Task': {
                'row_errors': {
                    errors.INVALID_START_END_DATES.format(
                        line=3,
                        start_date="Start Date",
                        end_date="End Date",
                    ),
                    errors.INVALID_STATUS_DATE_CORRELATION.format(
                        line=4,
                        date="Actual Finish Date",
                        status="not Finished",
                    ),
                    errors.INVALID_STATUS_DATE_CORRELATION.format(
                        line=5,
                        date="Actual Verified Date",
                        status="not Verified",
                    ),
                    errors.INVALID_STATUS_DATE_CORRELATION.format(
                        line=6,
                        date="Actual Verified Date",
                        status="not Verified",
                    ),
                    errors.INVALID_START_END_DATES.format(
                        line=7,
                        start_date="Actual Finish Date",
                        end_date="Actual Verified Date",
                    ),
                    errors.INVALID_START_END_DATES.format(
                        line=8,
                        start_date="Start Date",
                        end_date="End Date",
                    ),
                    errors.MISSING_VALUE_ERROR.format(
                        line=9,
                        column_name="Actual Finish Date",
                    ),
                    errors.INVALID_START_END_DATES.format(
                        line=10,
                        start_date="Actual Finish Date",
                        end_date="Actual Verified Date",
                    ),
                },
            }
        }
        # Below is expected cycle-tasks data which should appear in test DB after
        # test_cycle_task_date_error run
        self.expected_cycle_task_date_error = dict()
        self.expected_cycle_task_date_error.update(
            self.generated_cycle_tasks_active)
        self.expected_cycle_task_date_error.update(
            self.generated_cycle_tasks_historical)
        self.expected_cycle_task_date_error["CYCLETASK-9"] = (
            self.expected_cycle_task_correct["CYCLETASK-9"])
        self.expected_cycle_task_date_error["CYCLETASK-10"] = (
            self.expected_cycle_task_correct["CYCLETASK-10"])

        # Expected error message which should be shown after
        # test_cycle_task_permission_error run
        self.expected_permission_error = {
            'Cycle Task Group Object Task': {
                'block_errors': {errors.PERMISSION_ERROR.format(line=2)}
            }
        }
class TestBasicWorkflowActions(TestCase):
    """
  Tests for basic workflow actions
  """
    def setUp(self):
        super(TestBasicWorkflowActions, self).setUp()
        self.api = Api()
        self.generator = WorkflowsGenerator()
        self.object_generator = ObjectGenerator()

        self.random_objects = self.object_generator.generate_random_objects()
        self.create_test_cases()

    def tearDown(self):
        pass

    def test_create_workflows(self):
        _, wflow = self.generator.generate_workflow(self.one_time_workflow_1)
        self.assertIsInstance(wflow, Workflow)

        task_groups = db.session.query(TaskGroup)\
            .filter(TaskGroup.workflow_id == wflow.id).all()

        self.assertEqual(len(task_groups),
                         len(self.one_time_workflow_1["task_groups"]))

    def test_workflows(self):
        for workflow in self.all_workflows:
            _, wflow = self.generator.generate_workflow(workflow)
            self.assertIsInstance(wflow, Workflow)

            task_groups = db.session.query(TaskGroup)\
                .filter(TaskGroup.workflow_id == wflow.id).all()

            self.assertEqual(len(task_groups), len(workflow["task_groups"]))

    def test_activate_wf(self):
        for workflow in self.all_workflows:
            _, wflow = self.generator.generate_workflow(workflow)
            response, wflow = self.generator.activate_workflow(wflow)

            self.assert200(response)

    def test_one_time_workflow_edits(self):
        _, wflow = self.generator.generate_workflow(self.one_time_workflow_1)

        wf_dict = {"title": "modified one time wf"}
        self.generator.modify_workflow(wflow, data=wf_dict)

        modified_wf = db.session.query(Workflow).filter(
            Workflow.id == wflow.id).one()
        self.assertEqual(wf_dict["title"], modified_wf.title)

    def test_one_time_wf_activate(self):
        _, wflow = self.generator.generate_workflow(self.one_time_workflow_1)
        self.generator.generate_cycle(wflow)
        self.generator.activate_workflow(wflow)

        tasks = [
            len(tg.get("task_group_tasks", []))
            for tg in self.one_time_workflow_1["task_groups"]
        ]

        cycle_tasks = db.session.query(CycleTaskGroupObjectTask).join(
            Cycle).join(Workflow).filter(Workflow.id == wflow.id).all()
        active_wf = db.session.query(Workflow).filter(
            Workflow.id == wflow.id).one()

        self.assertEqual(sum(tasks), len(cycle_tasks))
        self.assertEqual(active_wf.status, "Active")

    def test_one_time_wf_state_transition_dates(self):
        _, wflow = self.generator.generate_workflow(self.one_time_workflow_1)
        self.generator.generate_cycle(wflow)
        self.generator.activate_workflow(wflow)

        cycle_tasks = db.session.query(CycleTaskGroupObjectTask).join(
            Cycle).join(Workflow).filter(Workflow.id == wflow.id).all()
        with freeze_time("2015-6-9"):
            today = dtm.date.today()
            transitions = [
                ("In Progress", None, None),
                ("Finished", today, None),
                ("Declined", None, None),
                ("Finished", today, None),
                ("Verified", today, today),
                ("Finished", today, None),
            ]
            # Iterate over possible transitions and check if dates got set correctly
            for (status, expected_finished, expected_verified) in transitions:
                cycle_task = cycle_tasks[0]
                _, task = self.generator.modify_object(cycle_task,
                                                       {"status": status})
                self.assertEqual(task.finished_date, expected_finished)
                self.assertEqual(task.verified_date, expected_verified)

    def test_delete_calls(self):
        _, workflow = self.generator.generate_workflow()
        self.generator.generate_task_group(workflow)
        _, task_group = self.generator.generate_task_group(workflow)
        task_groups = db.session.query(TaskGroup).filter(
            TaskGroup.workflow_id == workflow.id).all()
        self.assertEqual(len(task_groups), 2)

        response = self.generator.api.delete(task_group)
        self.assert200(response)

        task_groups = db.session.query(TaskGroup).filter(
            TaskGroup.workflow_id == workflow.id).all()
        self.assertEqual(len(task_groups), 1)

    def create_test_cases(self):

        self.quarterly_wf_1 = {
            "title":
            "quarterly wf 1",
            "description":
            "",
            "unit":
            "month",
            "repeat_every":
            3,
            "task_groups": [
                {
                    "title":
                    "tg_1",
                    "task_group_tasks": [
                        {
                            "description": factories.random_str(100),
                        },
                        {
                            "description": factories.random_str(100),
                        },
                        {
                            "description": factories.random_str(100),
                        },
                    ],
                },
            ]
        }

        self.weekly_wf_1 = {
            "title":
            "weekly thingy",
            "description":
            "start this many a time",
            "unit":
            "week",
            "repeat_every":
            1,
            "task_groups": [
                {
                    "title":
                    "tg_2",
                    "task_group_tasks": [
                        {
                            "description": factories.random_str(100),
                        },
                        {
                            "title": "monday task",
                        },
                        {
                            "title": "weekend task",
                        },
                    ],
                    "task_group_objects":
                    self.random_objects
                },
            ]
        }

        self.one_time_workflow_1 = {
            "title":
            "one time wf test",
            "description":
            "some test workflow",
            "is_verification_needed":
            True,
            "task_groups": [{
                "title": "tg_1",
                "task_group_tasks": [{}, {}, {}]
            }, {
                "title":
                "tg_2",
                "task_group_tasks": [{
                    "description": factories.random_str(100)
                }, {}],
                "task_group_objects":
                self.random_objects[:2]
            }, {
                "title":
                "tg_3",
                "task_group_tasks": [{
                    "title": "simple task 1",
                    "description": factories.random_str(100)
                }, {
                    "title": factories.random_str(),
                    "description": factories.random_str(100)
                }, {
                    "title": factories.random_str(),
                    "description": factories.random_str(100)
                }],
                "task_group_objects":
                self.random_objects
            }]
        }
        self.one_time_workflow_2 = {
            "title":
            "test_wf_title",
            "description":
            "some test workflow",
            "task_groups": [{
                "title": "tg_1",
                "task_group_tasks": [{}, {}, {}]
            }, {
                "title":
                "tg_2",
                "task_group_tasks": [{
                    "description": factories.random_str(100)
                }, {}],
                "task_group_objects":
                self.random_objects[:2]
            }, {
                "title":
                "tg_3",
                "task_group_tasks": [{
                    "title": "simple task 1",
                    "description": factories.random_str(100)
                }, {
                    "title": factories.random_str(),
                    "description": factories.random_str(100)
                }, {
                    "title": factories.random_str(),
                    "description": factories.random_str(100)
                }],
                "task_group_objects": []
            }]
        }

        self.monthly_workflow_1 = {
            "title":
            "monthly test wf",
            "description":
            "start this many a time",
            "unit":
            "month",
            "repeat_every":
            1,
            "task_groups": [
                {
                    "title":
                    "tg_2",
                    "task_group_tasks": [{
                        "description":
                        factories.random_str(100),
                    }, {
                        "title": "monday task",
                    }, {
                        "title": "weekend task",
                    }],
                    "task_group_objects":
                    self.random_objects
                },
            ]
        }
        self.all_workflows = [
            self.one_time_workflow_1,
            self.one_time_workflow_2,
            self.weekly_wf_1,
            self.monthly_workflow_1,
            self.quarterly_wf_1,
        ]
Esempio n. 36
0
class TestWorkflowStatistic(TestCase):
  """Tests endpoint for getting workflow statistic."""
  def setUp(self):
    super(TestWorkflowStatistic, self).setUp()
    self.client.get("/login")
    self.api = Api()
    self.generator = WorkflowsGenerator()
    self.object_generator = ObjectGenerator()

  def _setup_data(self):
    """Help method to setup workflows configuration."""
    # pylint: disable=too-many-locals
    # pylint: disable=attribute-defined-outside-init
    first_wf_admin = all_models.Person.query.first()
    _, second_wf_admin = self.object_generator.generate_person(
        user_role="Administrator",
        data={"email": "*****@*****.**"})
    first_wf_admin_id = first_wf_admin.id
    role = all_models.AccessControlRole.query.filter(
        all_models.AccessControlRole.name == "Task Assignees",
        all_models.AccessControlRole.object_type == "TaskGroupTask",
    ).one()
    role_id = role.id
    workflow_template1 = {
        "title": "test wf 1",
        "is_verification_needed": True,
        "task_groups": [{
            "title": "task group 1",
            "contact": create_stub(first_wf_admin),
            "task_group_tasks": [{
                "title": "task 1",
                "description": "some task",
                "access_control_list": [
                    acl_helper.get_acl_json(role_id, first_wf_admin_id)],
                "start_date": date(2017, 5, 5),
                "end_date": date(2017, 8, 15),
            }, {
                "title": "task 2",
                "description": "some task",
                "access_control_list": [
                    acl_helper.get_acl_json(role_id, first_wf_admin_id)],
                "start_date": date(2017, 5, 5),
                "end_date": date(2017, 8, 16),
            }, {"title": "task 3",
                "description": "some task",
                "access_control_list": [
                    acl_helper.get_acl_json(role_id, first_wf_admin_id)],
                "start_date": date(2017, 5, 5),
                "end_date": date(2017, 8, 17),
                }
            ],
            "task_group_objects": []
        }, {
            "title": "task group 2",
            "contact": create_stub(first_wf_admin),
            "task_group_tasks": [{
                "title": "task 4",
                "description": "some task",
                "access_control_list": [
                    acl_helper.get_acl_json(role_id, first_wf_admin_id)],
                "start_date": date(2017, 5, 5),
                "end_date": date(2017, 8, 17),
            }],
            "task_group_objects": []
        }]
    }
    workflow_template2 = {
        "title": "test wf 2",
        "is_verification_needed": False,
        "task_groups": [{
            "title": "task group 1",
            "contact": create_stub(first_wf_admin),
            "task_group_tasks": [{
                "title": "task 5",
                "description": "some task",
                "access_control_list": [
                    acl_helper.get_acl_json(role_id, first_wf_admin_id)],
                "start_date": date(2017, 5, 5),
                "end_date": date(2017, 8, 17),
            }, {
                "title": "task 6",
                "description": "some task",
                "access_control_list": [
                    acl_helper.get_acl_json(role_id, first_wf_admin_id)],
                "start_date": date(2017, 5, 5),
                "end_date": date(2017, 8, 15)
            }, {
                "title": "task 7",
                "description": "some task",
                "access_control_list": [
                    acl_helper.get_acl_json(role_id, first_wf_admin_id)],
                "start_date": date(2017, 5, 5),
                "end_date": date(2017, 8, 17)
            }, {
                "title": "task 8",
                "description": "some task",
                "access_control_list": [
                    acl_helper.get_acl_json(role_id, first_wf_admin_id)],
                "start_date": date(2017, 5, 5),
                "end_date": date(2017, 8, 14)
            }],
            "task_group_objects": []
        }]
    }
    with factories.single_commit():
      _, workflow1 = self.generator.generate_workflow(workflow_template1)
      _, workflow2 = self.generator.generate_workflow(workflow_template2)
      _, cycle1 = self.generator.generate_cycle(workflow1)
      _, cycle2 = self.generator.generate_cycle(workflow2)
      _, workflow1 = self.generator.activate_workflow(workflow1)
      _, workflow2 = self.generator.activate_workflow(workflow2)
      cycle1_id = cycle1.id
      cycle2_id = cycle2.id

    workflow1 = all_models.Workflow.query.get(workflow1.id)
    self.workflow1_id = workflow1.id
    self.workflow2_id = workflow2.id

    # first workflow has 2 workflow admins, second has 1
    # workflow admin now
    factories.AccessControlPersonFactory(
        ac_list=workflow1.acr_name_acl_map["Admin"],
        person=second_wf_admin
    )

    with freeze_time("2017-8-16"):
      cycle1 = all_models.Cycle.query.get(cycle1_id)
      cycle2 = all_models.Cycle.query.get(cycle2_id)
      task2 = cycle1.cycle_task_group_object_tasks[1]
      task3 = cycle1.cycle_task_group_object_tasks[2]
      task4 = cycle1.cycle_task_group_object_tasks[3]
      task5 = cycle2.cycle_task_group_object_tasks[0]
      task7 = cycle2.cycle_task_group_object_tasks[2]
      task8 = cycle2.cycle_task_group_object_tasks[3]
      self.api.put(task2, {
          "status": "Finished"
      })
      self.api.put(task3, {
          "status": "Deprecated"
      })
      self.api.put(task4, {
          "status": "Verified"
      })
      self.api.put(task5, {
          "status": "Finished"
      })
      self.api.put(task7, {
          "status": "Deprecated"
      })
      self.api.put(task8, {
          "status": "Finished"
      })

  def _get_expected_result(self):
    """Help method to provide expected statistic."""
    first_wf_admin_email = "*****@*****.**"
    second_wf_admin_email = "*****@*****.**"
    workflow1_owners = sorted([first_wf_admin_email,
                               second_wf_admin_email])
    workflow1 = all_models.Workflow.query.get(self.workflow1_id)
    workflow2 = all_models.Workflow.query.get(self.workflow2_id)
    expected_result = {'workflows': [{
        'workflow': {
            'id': workflow2.id,
            'title': workflow2.title
        },
        'owners': [first_wf_admin_email],
        'task_stat': {
            'counts': {
                'completed': 2,
                'overdue': 1,
                'total': 4
            },
            'due_in_date': '2017-08-14'
        }}, {
        'workflow': {
            'id': workflow1.id,
            'title': workflow1.title
        },
        'owners': workflow1_owners,
        'task_stat': {
            'counts': {
                'completed': 1,
                'total': 4,
                'overdue': 1,
            },
            'due_in_date': '2017-08-15'
        }},
    ]}
    return expected_result

  def test_workflow_statistic(self):
    """Tests endpoint for getting workflow statistics."""
    with freeze_time("2017-8-16"):
      self.client.get('/login')
      self._setup_data()
      # first wf admin (logged user)
      first_wf_admin = all_models.Person.query.first()
      response = self.client.get(
          "/api/people/{}/my_workflows".format(first_wf_admin.id)
      )
      expected_result = self._get_expected_result()
      self.assertEqual(response.json, expected_result)
class TestTaskOverdueNotificationsUsingAPI(TestTaskOverdueNotifications):
  """Tests for overdue notifications when changing Tasks with an API."""

  # pylint: disable=invalid-name

  def setUp(self):
    super(TestTaskOverdueNotificationsUsingAPI, self).setUp()
    self.api = Api()
    self.wf_generator = WorkflowsGenerator()
    self.object_generator = ObjectGenerator()
    models.Notification.query.delete()

    self._fix_notification_init()

    self.random_objects = self.object_generator.generate_random_objects(2)
    _, self.user = self.object_generator.generate_person(
        user_role="Administrator")
    self._create_test_cases()

  @ddt.data(True, False)
  @patch("ggrc.notifications.common.send_email")
  def test_sending_overdue_notifications_for_tasks(self, is_vf_needed, _):
    """Overdue notifications should be sent for overdue tasks every day.

    Even if an overdue notification has already been sent, it should still be
    sent in every following daily digest f a task is still overdue.
    """
    with freeze_time("2017-05-15 14:25:36"):
      tmp = self.one_time_workflow.copy()
      tmp['is_verification_needed'] = is_vf_needed
      _, workflow = self.wf_generator.generate_workflow(tmp)
      self.wf_generator.generate_cycle(workflow)
      response, workflow = self.wf_generator.activate_workflow(workflow)
      self.assert200(response)

    tasks = workflow.cycles[0].cycle_task_group_object_tasks
    task1_id = tasks[0].id
    task2_id = tasks[1].id

    user = models.Person.query.get(self.user.id)

    with freeze_time("2017-05-14 08:09:10"):
      _, notif_data = common.get_daily_notifications()
      user_notifs = notif_data.get(user.email, {})
      self.assertNotIn("task_overdue", user_notifs)

    with freeze_time("2017-05-15 08:09:10"):  # task 1 due date
      _, notif_data = common.get_daily_notifications()
      user_notifs = notif_data.get(user.email, {})
      self.assertNotIn("task_overdue", user_notifs)

    with freeze_time("2017-05-16 08:09:10"):  # task 2 due date
      _, notif_data = common.get_daily_notifications()
      user_notifs = notif_data.get(user.email, {})
      self.assertIn("task_overdue", user_notifs)

      overdue_task_ids = sorted(user_notifs["task_overdue"].keys())
      self.assertEqual(overdue_task_ids, [task1_id])

    with freeze_time("2017-05-17 08:09:10"):  # after both tasks' due dates
      _, notif_data = common.get_daily_notifications()
      user_notifs = notif_data.get(user.email, {})
      self.assertIn("task_overdue", user_notifs)

      overdue_task_ids = sorted(user_notifs["task_overdue"].keys())
      self.assertEqual(overdue_task_ids, [task1_id, task2_id])

      common.send_daily_digest_notifications()

    # even after sending the overdue notifications, they are sent again the
    # day after, too
    with freeze_time("2017-05-18 08:09:10"):
      _, notif_data = common.get_daily_notifications()
      user_notifs = notif_data.get(user.email, {})
      self.assertIn("task_overdue", user_notifs)

      overdue_task_ids = sorted(user_notifs["task_overdue"].keys())
      self.assertEqual(overdue_task_ids, [task1_id, task2_id])

  @ddt.data(True, False)
  @patch("ggrc.notifications.common.send_email")
  def test_adjust_overdue_notifications_on_task_due_date_change(self,
                                                                is_vf_needed,
                                                                _):
    """Sending overdue notifications should adjust to task due date changes."""
    with freeze_time("2017-05-15 14:25:36"):
      tmp = self.one_time_workflow.copy()
      tmp['is_verification_needed'] = is_vf_needed
      _, workflow = self.wf_generator.generate_workflow(tmp)
      self.wf_generator.generate_cycle(workflow)
      response, workflow = self.wf_generator.activate_workflow(workflow)
      self.assert200(response)

      tasks = workflow.cycles[0].cycle_task_group_object_tasks
      task1, task2 = tasks
      self.wf_generator.modify_object(task2, {"end_date": date(2099, 12, 31)})

      user = models.Person.query.get(self.user.id)

    with freeze_time("2017-05-16 08:09:10"):  # a day after task1 due date
      _, notif_data = common.get_daily_notifications()
      user_notifs = notif_data.get(user.email, {})
      self.assertIn("task_overdue", user_notifs)
      self.assertEqual(len(user_notifs["task_overdue"]), 1)

      # change task1 due date, there should be no overdue notification anymore
      self.wf_generator.modify_object(task1, {"end_date": date(2017, 5, 16)})
      _, notif_data = common.get_daily_notifications()
      user_notifs = notif_data.get(user.email, {})
      self.assertNotIn("task_overdue", user_notifs)

      # change task1 due date to the past there should a notification again
      self.wf_generator.modify_object(task1, {"end_date": date(2017, 5, 14)})
      _, notif_data = common.get_daily_notifications()
      user_notifs = notif_data.get(user.email, {})
      self.assertIn("task_overdue", user_notifs)
      self.assertEqual(len(user_notifs["task_overdue"]), 1)

  @ddt.data(True, False)
  @patch("ggrc.notifications.common.send_email")
  def test_adjust_overdue_notifications_on_task_status_change(self,
                                                              is_vf_needed,
                                                              _):
    """Sending overdue notifications should take task status into account."""
    with freeze_time("2017-05-15 14:25:36"):
      tmp = self.one_time_workflow.copy()
      tmp['is_verification_needed'] = is_vf_needed
      _, workflow = self.wf_generator.generate_workflow(tmp)
      self.wf_generator.generate_cycle(workflow)
      response, workflow = self.wf_generator.activate_workflow(workflow)
      self.assert200(response)

      tasks = workflow.cycles[0].cycle_task_group_object_tasks
      task1, task2 = tasks
      self.wf_generator.modify_object(task2, {"end_date": date(2099, 12, 31)})

      user = models.Person.query.get(self.user.id)
      user_email = user.email
    if is_vf_needed:
      non_final_states = [CycleTaskGroupObjectTask.ASSIGNED,
                          CycleTaskGroupObjectTask.IN_PROGRESS,
                          CycleTaskGroupObjectTask.FINISHED,
                          CycleTaskGroupObjectTask.DECLINED]
      final_state = CycleTaskGroupObjectTask.VERIFIED
    else:
      non_final_states = [CycleTaskGroupObjectTask.ASSIGNED,
                          CycleTaskGroupObjectTask.IN_PROGRESS]
      final_state = CycleTaskGroupObjectTask.FINISHED

    with freeze_time("2017-05-16 08:09:10"):  # a day after task1 due date
      for state in non_final_states:
        # clear all notifications before before changing the task status
        models.Notification.query.delete()
        _, notif_data = common.get_daily_notifications()
        self.assertEqual(notif_data, {})

        self.wf_generator.modify_object(task1, {"status": state})

        _, notif_data = common.get_daily_notifications()
        user_notifs = notif_data.get(user_email, {})
        self.assertIn("task_overdue", user_notifs)
        self.assertEqual(len(user_notifs["task_overdue"]), 1)

      # WITHOUT clearing the overdue notifications, move the task to "verified"
      # state, and the overdue notification should disappear.

      self.wf_generator.modify_object(task1, {"status": final_state})
      _, notif_data = common.get_daily_notifications()
      user_notifs = notif_data.get(user_email, {})
      self.assertNotIn("task_overdue", user_notifs)

  @ddt.data(True, False)
  @patch("ggrc.notifications.common.send_email")
  def test_stop_sending_overdue_notification_if_task_gets_deleted(self,
                                                                  is_vf_needed,
                                                                  _):
    """Overdue notifications should not be sent for deleted tasks."""
    with freeze_time("2017-05-15 14:25:36"):
      tmp = self.one_time_workflow.copy()
      tmp['is_verification_needed'] = is_vf_needed
      _, workflow = self.wf_generator.generate_workflow(tmp)
      self.wf_generator.generate_cycle(workflow)
      response, workflow = self.wf_generator.activate_workflow(workflow)
      self.assert200(response)

    tasks = workflow.cycles[0].cycle_task_group_object_tasks
    task1, task2 = tasks

    user = models.Person.query.get(self.user.id)
    user_email = user.email

    with freeze_time("2017-10-16 08:09:10"):  # long after both task due dates
      _, notif_data = common.get_daily_notifications()
      user_notifs = notif_data.get(user_email, {})
      self.assertIn("task_overdue", user_notifs)
      self.assertEqual(len(user_notifs["task_overdue"]), 2)

      db.session.delete(task2)
      db.session.commit()

      _, notif_data = common.get_daily_notifications()
      user_notifs = notif_data.get(user_email, {})
      self.assertIn("task_overdue", user_notifs)
      self.assertEqual(len(user_notifs["task_overdue"]), 1)

      db.session.delete(task1)
      db.session.commit()

      _, notif_data = common.get_daily_notifications()
      user_notifs = notif_data.get(user.email, {})
      self.assertNotIn("task_overdue", user_notifs)

  def _create_test_cases(self):
    """Create configuration to use for generating a new workflow."""
    def person_dict(person_id):
      return {
          "href": "/api/people/" + str(person_id),
          "id": person_id,
          "type": "Person"
      }
    role_id = models.all_models.AccessControlRole.query.filter(
        models.all_models.AccessControlRole.name == "Task Assignees",
        models.all_models.AccessControlRole.object_type == "TaskGroupTask",
    ).one().id
    self.one_time_workflow = {
        "title": "one time test workflow",
        "notify_on_change": True,
        "description": "some test workflow",
        "owners": [person_dict(self.user.id)],
        "task_groups": [{
            "title": "one time task group",
            "contact": person_dict(self.user.id),
            "task_group_tasks": [{
                "title": "task 1",
                "description": "some task",
                "start_date": date(2017, 5, 5),  # Friday
                "end_date": date(2017, 5, 15),
                "access_control_list": [{
                    "person": {"id": self.user.id, },
                    "ac_role_id": role_id,
                }],
            }, {
                "title": "task 2",
                "description": "some task 2",
                "start_date": date(2017, 5, 5),  # Friday
                "end_date": date(2017, 5, 16),
                "access_control_list": [{
                    "person": {"id": self.user.id, },
                    "ac_role_id": role_id,
                }],
            }],
            "task_group_objects": self.random_objects
        }]
    }
Esempio n. 38
0
class TestOneTimeWorkflowNotification(TestCase):
    """ This class contains simple one time workflow tests that are not
  in the gsheet test grid
  """
    def setUp(self):
        super(TestOneTimeWorkflowNotification, self).setUp()
        self.api = Api()
        self.wf_generator = WorkflowsGenerator()
        self.object_generator = ObjectGenerator()

        self.random_objects = self.object_generator.generate_random_objects()
        self.person_1 = self.create_user_with_role(role="Administrator")
        self.person_2 = self.create_user_with_role(role="Administrator")
        self.secondary_assignee = self.create_user_with_role(role="Reader")
        self.create_test_cases()

        def init_decorator(init):
            def new_init(self, *args, **kwargs):
                init(self, *args, **kwargs)
                if hasattr(self, "created_at"):
                    self.created_at = datetime.now()

            return new_init

        all_models.Notification.__init__ = init_decorator(
            all_models.Notification.__init__)

    def test_one_time_wf_activate(self):
        """Test one time wf activation"""

        with freeze_time("2015-04-10"):
            _, wf = self.wf_generator.generate_workflow(
                self.one_time_workflow_1)
            _, cycle = self.wf_generator.generate_cycle(wf)
            self.wf_generator.activate_workflow(wf)

            person_1, person_2 = self.person_1, self.person_2
            secondary_assignee = self.secondary_assignee

        task_assignees = [person_1, person_2, secondary_assignee]
        with freeze_time("2015-04-11"):
            _, notif_data = common.get_daily_notifications()

            self.assertIn(person_2.email, notif_data)

            # cycle started notifs available only for contact
            self.assertIn("cycle_started", notif_data[person_2.email])
            self.assertIn(cycle.id,
                          notif_data[person_2.email]["cycle_started"])

            for user in task_assignees:
                self.assertIn("my_tasks",
                              notif_data[user.email]["cycle_data"][cycle.id])

        with freeze_time("2015-05-03"):  # two days before due date
            _, notif_data = common.get_daily_notifications()
            for user in task_assignees:
                self.assertIn(user.email, notif_data)
                self.assertNotIn("due_in", notif_data[user.email])
                self.assertNotIn("due_today", notif_data[user.email])

        with freeze_time("2015-05-04"):  # one day before due date
            _, notif_data = common.get_daily_notifications()
            for user in task_assignees:
                self.assertEqual(len(notif_data[user.email]["due_in"]), 1)

        with freeze_time("2015-05-05"):  # due date
            _, notif_data = common.get_daily_notifications()
            for user in task_assignees:
                self.assertEqual(len(notif_data[user.email]["due_today"]), 1)

        with freeze_time("2015-05-10"):
            _, notif_data = common.get_daily_notifications()
            self.assertEqual(
                len(notif_data[secondary_assignee.email]["due_in"]), 2)

    @patch("ggrc.notifications.common.send_email")
    def test_one_time_wf_activate_single_person(self, mock_mail):
        """Test one time wf activation with single person"""

        with freeze_time("2015-04-10"):
            user = "******"
            _, wf = self.wf_generator.generate_workflow(
                self.one_time_workflow_single_person)

            _, cycle = self.wf_generator.generate_cycle(wf)
            self.wf_generator.activate_workflow(wf)

        with freeze_time("2015-04-11"):
            _, notif_data = common.get_daily_notifications()
            self.assertIn("cycle_started", notif_data[user])
            self.assertIn(cycle.id, notif_data[user]["cycle_started"])
            self.assertIn("my_tasks", notif_data[user]["cycle_data"][cycle.id])
            self.assertIn("cycle_tasks",
                          notif_data[user]["cycle_data"][cycle.id])
            self.assertIn("my_task_groups",
                          notif_data[user]["cycle_data"][cycle.id])
            self.assertIn("cycle_url",
                          notif_data[user]["cycle_started"][cycle.id])

            cycle = Cycle.query.get(cycle.id)
            cycle_data = notif_data[user]["cycle_data"][cycle.id]
            for task in cycle.cycle_task_group_object_tasks:
                self.assertIn(task.id, cycle_data["my_tasks"])
                self.assertIn(task.id, cycle_data["cycle_tasks"])
                self.assertIn("title", cycle_data["my_tasks"][task.id])
                self.assertIn("title", cycle_data["cycle_tasks"][task.id])
                self.assertIn("cycle_task_url",
                              cycle_data["cycle_tasks"][task.id])

        with freeze_time("2015-05-03"):  # two days before due date
            _, notif_data = common.get_daily_notifications()
            self.assertIn(user, notif_data)
            self.assertNotIn("due_in", notif_data[user])
            self.assertNotIn("due_today", notif_data[user])

        with freeze_time("2015-05-04"):  # one day before due date
            _, notif_data = common.get_daily_notifications()
            self.assertEqual(len(notif_data[user]["due_in"]), 2)

        with freeze_time("2015-05-05"):  # due date
            _, notif_data = common.get_daily_notifications()
            self.assertEqual(len(notif_data[user]["due_today"]), 2)

            common.send_daily_digest_notifications()
            self.assertEqual(mock_mail.call_count, 1)

    def create_test_cases(self):
        def person_dict(person_id):
            return {
                "href": "/api/people/%d" % person_id,
                "id": person_id,
                "type": "Person"
            }

        task_assignee_role_id = all_models.AccessControlRole.query.filter(
            all_models.AccessControlRole.name == "Task Assignees",
            all_models.AccessControlRole.object_type == "TaskGroupTask",
        ).one().id

        secondary_task_assignee_id = all_models.AccessControlRole.query.filter(
            all_models.AccessControlRole.name == "Task Secondary Assignees",
            all_models.AccessControlRole.object_type == "TaskGroupTask",
        ).one().id
        self.one_time_workflow_1 = {
            "title":
            "one time test workflow",
            "description":
            "some test workflow",
            "notify_on_change":
            True,
            # admin will be current user with id == 1
            "task_groups": [
                {
                    "title":
                    "one time task group",
                    "contact":
                    person_dict(self.person_2.id),
                    "task_group_tasks": [
                        {
                            "title":
                            "task 1",
                            "description":
                            "some task",
                            "access_control_list": [
                                acl_helper.get_acl_json(
                                    task_assignee_role_id, self.person_1.id),
                                acl_helper.get_acl_json(
                                    secondary_task_assignee_id,
                                    self.secondary_assignee.id),
                            ],
                            "start_date":
                            date(2015, 5, 1),  # friday
                            "end_date":
                            date(2015, 5, 5),
                        },
                        {
                            "title":
                            "task 2",
                            "description":
                            "some task",
                            "access_control_list": [
                                acl_helper.get_acl_json(
                                    task_assignee_role_id, self.person_1.id),
                                acl_helper.get_acl_json(
                                    secondary_task_assignee_id,
                                    self.secondary_assignee.id),
                            ],
                            "start_date":
                            date(2015, 5, 4),
                            "end_date":
                            date(2015, 5, 7),
                        }
                    ],
                    "task_group_objects":
                    self.random_objects[:2]
                },
                {
                    "title":
                    "another one time task group",
                    "contact":
                    person_dict(self.person_2.id),
                    "task_group_tasks": [
                        {
                            "title":
                            "task 1 in tg 2",
                            "description":
                            "some task",
                            "access_control_list": [
                                acl_helper.get_acl_json(
                                    task_assignee_role_id, self.person_1.id)
                            ],
                            "start_date":
                            date(2015, 5, 8),  # friday
                            "end_date":
                            date(2015, 5, 12),
                        },
                        {
                            "title":
                            "task 2 in tg 2",
                            "description":
                            "some task",
                            "access_control_list": [
                                acl_helper.get_acl_json(
                                    task_assignee_role_id, self.person_2.id)
                            ],
                            "start_date":
                            date(2015, 5, 1),  # friday
                            "end_date":
                            date(2015, 5, 5),
                        }
                    ],
                    "task_group_objects": []
                }
            ]
        }

        user = all_models.Person.query.filter(
            all_models.Person.email == "*****@*****.**").one().id

        self.one_time_workflow_single_person = {
            "title":
            "one time test workflow",
            "notify_on_change":
            True,
            "description":
            "some test workflow",
            # admin will be current user with id == 1
            "task_groups": [
                {
                    "title":
                    "one time task group",
                    "contact":
                    person_dict(user),
                    "task_group_tasks": [
                        {
                            "title":
                            u"task 1 \u2062 WITH AN UMBRELLA ELLA ELLA. \u2062",
                            "description":
                            "some task. ",
                            "access_control_list": [
                                acl_helper.get_acl_json(
                                    task_assignee_role_id, user)
                            ],
                            "start_date":
                            date(2015, 5, 1),  # friday
                            "end_date":
                            date(2015, 5, 5),
                        },
                        {
                            "title":
                            "task 2",
                            "description":
                            "some task",
                            "access_control_list": [
                                acl_helper.get_acl_json(
                                    task_assignee_role_id, user)
                            ],
                            "start_date":
                            date(2015, 5, 4),
                            "end_date":
                            date(2015, 5, 7),
                        }
                    ],
                    "task_group_objects":
                    self.random_objects[:2]
                },
                {
                    "title":
                    "another one time task group",
                    "contact":
                    person_dict(user),
                    "task_group_tasks": [
                        {
                            "title":
                            u"task 1 \u2062 WITH AN UMBRELLA ELLA ELLA. \u2062",
                            "description":
                            "some task",
                            "access_control_list": [
                                acl_helper.get_acl_json(
                                    task_assignee_role_id, user)
                            ],
                            "start_date":
                            date(2015, 5, 8),  # friday
                            "end_date":
                            date(2015, 5, 12),
                        },
                        {
                            "title":
                            "task 2 in tg 2",
                            "description":
                            "some task",
                            "access_control_list": [
                                acl_helper.get_acl_json(
                                    task_assignee_role_id, user)
                            ],
                            "start_date":
                            date(2015, 5, 1),  # friday
                            "end_date":
                            date(2015, 5, 5),
                        }
                    ],
                    "task_group_objects": []
                }
            ]
        }
Esempio n. 39
0
class TestPersonResource(TestCase, WithQueryApi):
    """Tests for special people api endpoints."""
    def setUp(self):
        super(TestPersonResource, self).setUp()
        self.client.get("/login")
        self.api = Api()
        self.generator = WorkflowsGenerator()

    @staticmethod
    def _create_users_names_rbac(users):
        """Create name and Creator role for users, created vid PersonFactory"""
        if not users:
            return
        roles = {r.name: r for r in all_models.Role.query.all()}
        for user in users:
            user.name = user.email.split("@")[0]
            rbac_factories.UserRoleFactory(role=roles["Creator"], person=user)

    def assert_profile_get_successful(self, response, expected_datetime):
        """Verify assertions for successful GET profile method"""
        self.assert200(response)
        response_datetime = date_parser.parse(
            response.json["last_seen_whats_new"])
        self.assertEqual(expected_datetime, response_datetime)

    @freeze_time("2018-05-20 12:23:17")
    def test_profile_get_successful(self):
        """Test person_profile GET method successfully achieves correct data"""
        with factories.single_commit():
            user = factories.PersonFactory()
            self._create_users_names_rbac([user])
        self.api.set_user(person=user)

        response = self.api.client.get("/api/people/{}/profile".format(
            user.id))
        self.assert_profile_get_successful(response, default_date())

    def test_profile_get_no_profile(self):
        """Test person_profile GET method achieves data with missing profile"""
        with factories.single_commit():
            user = factories.PersonFactory()
            self._create_users_names_rbac([user])
        self.api.set_user(person=user)

        profiles_table = PersonProfile.__table__
        db_request = profiles_table.delete().where(
            profiles_table.c.person_id == user.id)
        db.engine.execute(db_request)
        with freeze_time("2018-05-28 23:30:10"):
            response = self.api.client.get("/api/people/{}/profile".format(
                user.id))
            self.assert_profile_get_successful(response, default_date())

    def test_profile_get_failed(self):
        """Test person_profiles GET method fails

    Now only logged user can request his profile
    """
        with factories.single_commit():
            valid_user = factories.PersonFactory()
            self._create_users_names_rbac([valid_user])

        response = self.client.get("/api/people/{}/profile".format(
            valid_user.id))
        # logged with default user during setUp
        self.assert403(response)
        response = self.api.client.get("/api/people/{}/profile".format(
            valid_user.id))
        # not authorized user
        self.assert403(response)

    @ddt.data("Creator", "Reader", "Editor", "Administrator")
    def test_profile_post_empty_body(self, role_name):
        """Test person_profile POST method with empty body - {}."""
        role = all_models.Role.query.filter(
            all_models.Role.name == role_name).one()
        with factories.single_commit():
            user = factories.PersonFactory()
            rbac_factories.UserRoleFactory(role=role, person=user)
        self.api.set_user(person=user)

        response = self.api.send_request(
            self.api.client.post,
            data={},
            api_link="/api/people/{}/profile".format(user.id))

        self.assert405(response)

    def test_profile_post_unauthorized(self):
        """Test person_profile POST method with empty body - No Access."""
        with factories.single_commit():
            user = factories.PersonFactory()

        response = self.api.send_request(
            self.api.client.post,
            data={},
            api_link="/api/people/{}/profile".format(user.id))
        # not authorized user
        self.assert405(response)

    def assert_profile_put_successful(self, response, correct_response, user,
                                      expected):
        """Verify assertions for successful PUT profile method"""
        self.assert200(response)
        self.assertEqual(response.json, correct_response)
        profile = PersonProfile.query.filter_by(person_id=user.id).first()
        self.assertEqual(profile.last_seen_whats_new,
                         date_parser.parse(expected))

    @ddt.data(["2018-05-20 16:38:17", "2018-05-20 16:38:17"],
              ["2018-07-05T14:11:31Z", "2018-07-05T14:11:31"])
    @ddt.unpack
    def test_profile_put_successful(self, new_date, expected_date):
        """Test person_profile PUT method for setting data and correct response"""
        with factories.single_commit():
            user = factories.PersonFactory()
            self._create_users_names_rbac([user])
        self.api.set_user(person=user)

        data = {"last_seen_whats_new": new_date}
        correct_response = {"Person": {"id": user.id, "profile": data}}
        response = self.api.client.put("/api/people/{}/profile".format(
            user.id),
                                       content_type='application/json',
                                       data=json.dumps(data),
                                       headers=[('X-Requested-By', 'Tests')])
        self.assert_profile_put_successful(response, correct_response, user,
                                           expected_date)

    def test_profile_put_no_profile(self):
        """Test person_profile PUT method for setting data for missing profile"""
        with factories.single_commit():
            user = factories.PersonFactory()
            self._create_users_names_rbac([user])
        self.api.set_user(person=user)

        new_date = "2018-05-20 22:05:17"
        data = {"last_seen_whats_new": new_date}
        correct_response = {"Person": {"id": user.id, "profile": data}}
        profiles_table = PersonProfile.__table__
        db_request = profiles_table.delete().where(
            profiles_table.c.person_id == user.id)
        db.engine.execute(db_request)
        response = self.api.client.put("/api/people/{}/profile".format(
            user.id),
                                       content_type='application/json',
                                       data=json.dumps(data),
                                       headers=[('X-Requested-By', 'Tests')])
        self.assert_profile_put_successful(response, correct_response, user,
                                           new_date)

    def test_profile_put_unauthorized(self):
        """Test person_profiles PUT method fails for unauthorized user"""
        with factories.single_commit():
            user = factories.PersonFactory()
            self._create_users_names_rbac([user])

        new_date = "2018-05-20 22:05:17"
        data = {"last_seen_whats_new": new_date}
        response = self.client.put("/api/people/{}/profile".format(user.id),
                                   content_type='application/json',
                                   data=json.dumps(data),
                                   headers=[('X-Requested-By', 'Tests')])
        # logged with default user during setUp
        self.assert403(response)
        response = self.api.client.put("/api/people/{}/profile".format(
            user.id),
                                       content_type='application/json',
                                       data=json.dumps(data),
                                       headers=[('X-Requested-By', 'Tests')])
        # not authorized user
        self.assert403(response)

    @ddt.data({"last_seen_whats_new": "NOT A 123 DAT456A"}, {
        "other_key": "2018-05-20 22:05:17",
        "one_more_key": 42
    })
    def test_profile_put_corrupted_data(self, data):
        """Test person_profiles PUT method fails via incorrect request data

    If request doesn't have "last_seen_whats_new" key or date is incorrect,
      response is code 400 "Bad Request"
    """
        with factories.single_commit():
            user = factories.PersonFactory()
            self._create_users_names_rbac([user])
        self.api.set_user(person=user)

        response = self.api.client.put("/api/people/{}/profile".format(
            user.id),
                                       content_type='application/json',
                                       data=json.dumps(data),
                                       headers=[('X-Requested-By', 'Tests')])
        # missed key in request
        self.assert400(response)

    def test_task_count_empty(self):
        """Test query count without any workflows and tasks."""
        user = all_models.Person.query.first()
        response = self.client.get("/api/people/{}/task_count".format(user.id))
        self.assertEqual(response.json, {
            "open_task_count": 0,
            "has_overdue": False
        })

    @ddt.data(
        (True, [
            ("task 1", "Finished", 3, True, 3),
            ("task 1", "Verified", 2, True, 3),
            ("task 2", "Declined", 2, True, 3),
            ("task 2", "Verified", 1, False, 3),
            ("task 2", "Finished", 2, True, 3),
            ("task 3", "Verified", 1, True, 3),
            ("task 2", "Verified", 0, False, 3),
        ]),
        (False, [
            ("task 1", "Finished", 2, True, 3),
            ("task 2", "In Progress", 2, True, 3),
            ("task 2", "Finished", 1, False, 3),
            ("task 3", "Finished", 0, False, 3),
        ]),
    )
    @ddt.unpack
    def test_task_count(self, is_verification_needed, transitions):
        """Test person task counts.

    This tests checks for correct task counts
     - with inactive workflows and
     - with overdue tasks
     - without overdue tasks
     - with finished overdue tasks

    The four checks are done in a single test due to complex differences
    between tests that make ddt cumbersome and the single test also improves
    integration test performance due to slow workflow setup stage.
    """
        # pylint: disable=too-many-locals

        user = all_models.Person.query.first()
        dummy_user = factories.PersonFactory()
        user_id = user.id
        role_id = all_models.AccessControlRole.query.filter(
            all_models.AccessControlRole.name == "Task Assignees",
            all_models.AccessControlRole.object_type == "TaskGroupTask",
        ).one().id
        secondary_role_id = all_models.AccessControlRole.query.filter(
            all_models.AccessControlRole.name == "Task Secondary Assignees",
            all_models.AccessControlRole.object_type == "TaskGroupTask",
        ).one().id

        one_time_workflow = {
            "title":
            "Person resource test workflow",
            "notify_on_change":
            True,
            "description":
            "some test workflow",
            "owners": [create_stub(user)],
            "is_verification_needed":
            is_verification_needed,
            "task_groups": [{
                "title":
                "one time task group",
                "contact":
                create_stub(user),
                "task_group_tasks": [
                    {
                        "title":
                        "task 1",
                        "description":
                        "some task",
                        "access_control_list": [
                            acl_helper.get_acl_json(role_id, user.id),
                            acl_helper.get_acl_json(secondary_role_id, user.id)
                        ],
                        "start_date":
                        date(2017, 5, 5),
                        "end_date":
                        date(2017, 8, 15),
                    },
                    {
                        "title":
                        "task 2",
                        "description":
                        "some task 3",
                        "access_control_list": [
                            acl_helper.get_acl_json(role_id, user.id),
                            acl_helper.get_acl_json(secondary_role_id,
                                                    user.id),
                            acl_helper.get_acl_json(secondary_role_id,
                                                    dummy_user.id)
                        ],
                        "start_date":
                        date(2017, 5, 5),
                        "end_date":
                        date(2017, 9, 16),
                    },
                    {
                        "title":
                        "task 3",
                        "description":
                        "some task 4",
                        "access_control_list": [
                            acl_helper.get_acl_json(role_id, user.id),
                            acl_helper.get_acl_json(role_id, dummy_user.id)
                        ],
                        "start_date":
                        date(2017, 6, 5),
                        "end_date":
                        date(2017, 10, 16),
                    },
                    {
                        "title":
                        "dummy task 4",  # task should not counted
                        "description":
                        "some task 4",
                        "access_control_list":
                        [acl_helper.get_acl_json(role_id, dummy_user.id)],
                        "start_date":
                        date(2017, 6, 5),
                        "end_date":
                        date(2017, 11, 17),
                    },
                    {
                        "title":
                        "dummy task 5",  # task should not counted
                        "description":
                        "some task 4",
                        "access_control_list":
                        [acl_helper.get_acl_json(role_id, dummy_user.id)],
                        "start_date":
                        date(2017, 6, 5),
                        "end_date":
                        date(2017, 11, 18),
                    }
                ],
                "task_group_objects": []
            }]
        }

        inactive_workflow = {
            "title":
            "Activated workflow with archived cycles",
            "notify_on_change":
            True,
            "description":
            "Extra test workflow",
            "owners": [create_stub(user)],
            "task_groups": [{
                "title":
                "Extra task group",
                "contact":
                create_stub(user),
                "task_group_tasks": [{
                    "title":
                    "not counted existing task",
                    "description":
                    "",
                    "access_control_list":
                    [acl_helper.get_acl_json(role_id, user.id)],
                    "start_date":
                    date(2017, 5, 5),
                    "end_date":
                    date(2017, 8, 15),
                }],
                "task_group_objects": []
            }]
        }

        with freeze_time("2017-10-16 05:09:10"):
            self.client.get("/login")
            # Activate normal one time workflow
            _, workflow = self.generator.generate_workflow(one_time_workflow)
            _, cycle = self.generator.generate_cycle(workflow)
            tasks = {t.title: t for t in cycle.cycle_task_group_object_tasks}
            _, workflow = self.generator.activate_workflow(workflow)

            # Activate and close the inactive workflow
            _, workflow = self.generator.generate_workflow(inactive_workflow)
            _, cycle = self.generator.generate_cycle(workflow)
            _, workflow = self.generator.activate_workflow(workflow)
            self.generator.modify_object(cycle, data={"is_current": False})

        with freeze_time("2017-7-16 07:09:10"):
            self.client.get("/login")
            response = self.client.get(
                "/api/people/{}/task_count".format(user_id))
            self.assertEqual(response.json, {
                "open_task_count": 3,
                "has_overdue": False
            })

        with freeze_time("2017-10-16 08:09:10"):  # same day as task 3 end date
            self.client.get("/login")
            response = self.client.get(
                "/api/people/{}/task_count".format(user_id))
            self.assertEqual(response.json, {
                "open_task_count": 3,
                "has_overdue": True
            })

            for task, status, count, overdue, my_work_count in transitions:
                self.generator.modify_object(tasks[task],
                                             data={"status": status})
                task_count_response = \
                    self.client.get("/api/people/{}/task_count".format(user_id))
                my_work_count_response = \
                    self.client.get("/api/people/{}/my_work_count".format(user_id))

                self.assertEqual(task_count_response.json, {
                    "open_task_count": count,
                    "has_overdue": overdue
                })

                self.assertEqual(
                    my_work_count_response.json["CycleTaskGroupObjectTask"],
                    my_work_count)

    def test_task_count_multiple_wfs(self):
        """Test task count with both verified and non verified workflows.

    This checks task counts with 4 tasks
        2017, 8, 15  - verification needed
        2017, 11, 18  - verification needed
        2017, 8, 15  - No verification needed
        2017, 11, 18  - No verification needed
    """

        user = all_models.Person.query.first()
        user_id = user.id
        role_id = all_models.AccessControlRole.query.filter(
            all_models.AccessControlRole.name == "Task Assignees",
            all_models.AccessControlRole.object_type == "TaskGroupTask",
        ).one().id
        workflow_template = {
            "title":
            "verified workflow",
            "owners": [create_stub(user)],
            "is_verification_needed":
            True,
            "task_groups": [{
                "title":
                "one time task group",
                "contact":
                create_stub(user),
                "task_group_tasks": [{
                    "title":
                    "task 1",
                    "description":
                    "some task",
                    "access_control_list":
                    [acl_helper.get_acl_json(role_id, user.id)],
                    "start_date":
                    date(2017, 5, 5),
                    "end_date":
                    date(2017, 8, 15),
                }, {
                    "title":
                    "dummy task 5",
                    "description":
                    "some task 4",
                    "access_control_list":
                    [acl_helper.get_acl_json(role_id, user.id)],
                    "start_date":
                    date(2017, 6, 5),
                    "end_date":
                    date(2017, 11, 18),
                }],
                "task_group_objects": []
            }]
        }

        with freeze_time("2017-10-16 05:09:10"):
            self.client.get("/login")
            verified_workflow = workflow_template.copy()
            verified_workflow["is_verification_needed"] = True
            _, workflow = self.generator.generate_workflow(verified_workflow)
            _, cycle = self.generator.generate_cycle(workflow)
            verified_tasks = {
                task.title: task
                for task in cycle.cycle_task_group_object_tasks
            }
            _, workflow = self.generator.activate_workflow(workflow)

            non_verified_workflow = workflow_template.copy()
            non_verified_workflow["is_verification_needed"] = False
            _, workflow = self.generator.generate_workflow(
                non_verified_workflow)
            _, cycle = self.generator.generate_cycle(workflow)
            non_verified_tasks = {
                task.title: task
                for task in cycle.cycle_task_group_object_tasks
            }
            _, workflow = self.generator.activate_workflow(workflow)

        with freeze_time("2017-7-16 07:09:10"):
            self.client.get("/login")
            response = self.client.get(
                "/api/people/{}/task_count".format(user_id))
            self.assertEqual(response.json, {
                "open_task_count": 4,
                "has_overdue": False
            })

        with freeze_time("2017-10-16 08:09:10"):
            self.client.get("/login")
            response = self.client.get(
                "/api/people/{}/task_count".format(user_id))
            self.assertEqual(response.json, {
                "open_task_count": 4,
                "has_overdue": True
            })

            # transition 1, task that needs verification goes to finished state. This
            # transition should not change anything
            self.generator.modify_object(verified_tasks["task 1"],
                                         data={"status": "Finished"})
            response = self.client.get(
                "/api/people/{}/task_count".format(user_id))
            self.assertEqual(response.json, {
                "open_task_count": 4,
                "has_overdue": True
            })

            # transition 2, task that needs verification goes to verified state. This
            # transition should reduce task count.
            self.generator.modify_object(verified_tasks["task 1"],
                                         data={"status": "Verified"})
            response = self.client.get(
                "/api/people/{}/task_count".format(user_id))
            self.assertEqual(response.json, {
                "open_task_count": 3,
                "has_overdue": True
            })

            # transition 3, task that does not need verification goes into Finished
            # state. This transition should reduce task count and remove all overdue
            # tasks
            self.generator.modify_object(non_verified_tasks["task 1"],
                                         data={"status": "Finished"})
            response = self.client.get(
                "/api/people/{}/task_count".format(user_id))
            self.assertEqual(response.json, {
                "open_task_count": 2,
                "has_overdue": False
            })

    @ddt.data(("Creator", 403), ("Reader", 403), ("Editor", 200),
              ("Administrator", 200))
    @ddt.unpack
    def test_person_editing(self, role_name, status):
        """{0} should receive {1} status code on edit Person."""
        role = all_models.Role.query.filter(
            all_models.Role.name == role_name).one()
        with factories.single_commit():
            client_user = factories.PersonFactory()
            rbac_factories.UserRoleFactory(role=role, person=client_user)
        self.api.set_user(client_user)
        self.client.get("/login")
        base_email = "*****@*****.**"
        person = factories.PersonFactory(email=base_email)
        person_id = person.id
        new_email = "new_{}".format(base_email)
        resp = self.api.put(person, {"email": new_email})
        self.assertEqual(status, resp.status_code)
        person = all_models.Person.query.get(person_id)
        if status == 200:
            self.assertEqual(new_email, person.email)
        else:
            self.assertEqual(base_email, person.email)
Esempio n. 40
0
class TestOneTimeWorkflowNotification(TestCase):
    """ This class contains simple one time workflow tests that are not
  in the gsheet test grid
  """
    def setUp(self):
        TestCase.setUp(self)
        self.api = Api()
        self.wf_generator = WorkflowsGenerator()
        self.object_generator = ObjectGenerator()

        self.random_objects = self.object_generator.generate_random_objects()
        self.random_people = self.object_generator.generate_random_people(
            user_role="Administrator")
        self.create_test_cases()

        def init_decorator(init):
            def new_init(self, *args, **kwargs):
                init(self, *args, **kwargs)
                if hasattr(self, "created_at"):
                    self.created_at = datetime.now()

            return new_init

        Notification.__init__ = init_decorator(Notification.__init__)

    def test_one_time_wf_activate(self):
        def get_person(person_id):
            return db.session.query(Person).filter(
                Person.id == person_id).one()

        with freeze_time("2015-04-10"):
            _, wf = self.wf_generator.generate_workflow(
                self.one_time_workflow_1)

            _, cycle = self.wf_generator.generate_cycle(wf)
            self.wf_generator.activate_workflow(wf)

            person_1 = get_person(self.random_people[0].id)

        with freeze_time("2015-04-11"):
            _, notif_data = common.get_daily_notifications()
            self.assertIn("cycle_started", notif_data[person_1.email])
            self.assertIn(cycle.id,
                          notif_data[person_1.email]["cycle_started"])
            self.assertIn("my_tasks",
                          notif_data[person_1.email]["cycle_data"][cycle.id])

        with freeze_time("2015-05-03"):  # two days befor due date
            _, notif_data = common.get_daily_notifications()
            self.assertIn(person_1.email, notif_data)
            self.assertNotIn("due_in", notif_data[person_1.email])
            self.assertNotIn("due_today", notif_data[person_1.email])

        with freeze_time("2015-05-04"):  # one day befor due date
            _, notif_data = common.get_daily_notifications()
            self.assertEqual(len(notif_data[person_1.email]["due_in"]), 1)

        with freeze_time("2015-05-05"):  # due date
            _, notif_data = common.get_daily_notifications()
            self.assertEqual(len(notif_data[person_1.email]["due_today"]), 1)

    @patch("ggrc.notifications.common.send_email")
    def test_one_time_wf_activate_single_person(self, mock_mail):

        with freeze_time("2015-04-10"):
            user = "******"
            _, wf = self.wf_generator.generate_workflow(
                self.one_time_workflow_single_person)

            _, cycle = self.wf_generator.generate_cycle(wf)
            self.wf_generator.activate_workflow(wf)

        with freeze_time("2015-04-11"):
            _, notif_data = common.get_daily_notifications()
            self.assertIn("cycle_started", notif_data[user])
            self.assertIn(cycle.id, notif_data[user]["cycle_started"])
            self.assertIn("my_tasks", notif_data[user]["cycle_data"][cycle.id])
            self.assertIn("cycle_tasks",
                          notif_data[user]["cycle_data"][cycle.id])
            self.assertIn("my_task_groups",
                          notif_data[user]["cycle_data"][cycle.id])
            self.assertIn("cycle_url",
                          notif_data[user]["cycle_started"][cycle.id])

            cycle = Cycle.query.get(cycle.id)
            cycle_data = notif_data[user]["cycle_data"][cycle.id]
            for task in cycle.cycle_task_group_object_tasks:
                self.assertIn(task.id, cycle_data["my_tasks"])
                self.assertIn(task.id, cycle_data["cycle_tasks"])
                self.assertIn("title", cycle_data["my_tasks"][task.id])
                self.assertIn("title", cycle_data["cycle_tasks"][task.id])
                self.assertIn("cycle_task_url",
                              cycle_data["cycle_tasks"][task.id])

        with freeze_time("2015-05-03"):  # two days before due date
            _, notif_data = common.get_daily_notifications()
            self.assertIn(user, notif_data)
            self.assertNotIn("due_in", notif_data[user])
            self.assertNotIn("due_today", notif_data[user])

        with freeze_time("2015-05-04"):  # one day before due date
            _, notif_data = common.get_daily_notifications()
            self.assertEqual(len(notif_data[user]["due_in"]), 2)

        with freeze_time("2015-05-05"):  # due date
            _, notif_data = common.get_daily_notifications()
            self.assertEqual(len(notif_data[user]["due_today"]), 2)

            common.send_daily_digest_notifications()
            self.assertEqual(mock_mail.call_count, 1)

    def create_test_cases(self):
        def person_dict(person_id):
            return {
                "href": "/api/people/%d" % person_id,
                "id": person_id,
                "type": "Person"
            }

        self.one_time_workflow_1 = {
            "title":
            "one time test workflow",
            "description":
            "some test workflow",
            "notify_on_change":
            True,
            "owners": [person_dict(self.random_people[3].id)],
            "task_groups": [
                {
                    "title":
                    "one time task group",
                    "contact":
                    person_dict(self.random_people[2].id),
                    "task_group_tasks": [
                        {
                            "title": "task 1",
                            "description": "some task",
                            "contact": person_dict(self.random_people[0].id),
                            "start_date": date(2015, 5, 1),  # friday
                            "end_date": date(2015, 5, 5),
                        },
                        {
                            "title": "task 2",
                            "description": "some task",
                            "contact": person_dict(self.random_people[1].id),
                            "start_date": date(2015, 5, 4),
                            "end_date": date(2015, 5, 7),
                        }
                    ],
                    "task_group_objects":
                    self.random_objects[:2]
                },
                {
                    "title":
                    "another one time task group",
                    "contact":
                    person_dict(self.random_people[2].id),
                    "task_group_tasks": [
                        {
                            "title": "task 1 in tg 2",
                            "description": "some task",
                            "contact": person_dict(self.random_people[0].id),
                            "start_date": date(2015, 5, 8),  # friday
                            "end_date": date(2015, 5, 12),
                        },
                        {
                            "title": "task 2 in tg 2",
                            "description": "some task",
                            "contact": person_dict(self.random_people[2].id),
                            "start_date": date(2015, 5, 1),  # friday
                            "end_date": date(2015, 5, 5),
                        }
                    ],
                    "task_group_objects": []
                }
            ]
        }

        user = Person.query.filter(Person.email == "*****@*****.**").one().id

        self.one_time_workflow_single_person = {
            "title":
            "one time test workflow",
            "notify_on_change":
            True,
            "description":
            "some test workflow",
            "owners": [person_dict(user)],
            "task_groups": [
                {
                    "title":
                    "one time task group",
                    "contact":
                    person_dict(user),
                    "task_group_tasks": [
                        {
                            "title":
                            u"task 1 \u2062 WITH AN UMBRELLA ELLA ELLA. \u2062",
                            "description": "some task. ",
                            "contact": person_dict(user),
                            "start_date": date(2015, 5, 1),  # friday
                            "end_date": date(2015, 5, 5),
                        },
                        {
                            "title": "task 2",
                            "description": "some task",
                            "contact": person_dict(user),
                            "start_date": date(2015, 5, 4),
                            "end_date": date(2015, 5, 7),
                        }
                    ],
                    "task_group_objects":
                    self.random_objects[:2]
                },
                {
                    "title":
                    "another one time task group",
                    "contact":
                    person_dict(user),
                    "task_group_tasks": [
                        {
                            "title":
                            u"task 1 \u2062 WITH AN UMBRELLA ELLA ELLA. \u2062",
                            "description": "some task",
                            "contact": person_dict(user),
                            "start_date": date(2015, 5, 8),  # friday
                            "end_date": date(2015, 5, 12),
                        },
                        {
                            "title": "task 2 in tg 2",
                            "description": "some task",
                            "contact": person_dict(user),
                            "start_date": date(2015, 5, 1),  # friday
                            "end_date": date(2015, 5, 5),
                        }
                    ],
                    "task_group_objects": []
                }
            ]
        }
class TestTaskOverdueNotificationsUsingImports(TestTaskOverdueNotifications):
  """Tests for overdue notifications when changing Tasks via imports."""

  # pylint: disable=invalid-name

  CSV_DIR = join(abspath(dirname(__file__)), "../converters/test_csvs/")

  def setUp(self):
    self.wf_generator = WorkflowsGenerator()
    self._fix_notification_init()

  @patch("ggrc.notifications.common.send_email")
  def test_creating_overdue_notifications_for_new_tasks(self, _):
    """Overdue notifications should be created for tasks created with imports.
    """
    Workflow.query.delete()
    models.Notification.query.delete()
    db.session.commit()

    filename = join(self.CSV_DIR, "workflow_small_sheet.csv")
    self.import_file(filename)

    workflow = Workflow.query.one()
    self.wf_generator.generate_cycle(workflow)
    response, workflow = self.wf_generator.activate_workflow(workflow)

    user = models.Person.query.filter(
        models.Person.email == '*****@*****.**').one()

    with freeze_time("2020-01-01 00:00:00"):  # afer all tasks' due dates
      _, notif_data = common.get_daily_notifications()
      user_notifs = notif_data.get(user.email, {})
      self.assertIn("task_overdue", user_notifs)
      self.assertEqual(len(user_notifs["task_overdue"]), 4)

  @patch("ggrc.notifications.common.send_email")
  def test_overdue_notifications_when_task_due_date_is_changed(self, _):
    """Overdue notifications should adjust to task due date changes."""
    Workflow.query.delete()
    models.Notification.query.delete()
    db.session.commit()

    filename = join(self.CSV_DIR, "workflow_small_sheet.csv")
    self.import_file(filename)

    workflow = Workflow.query.one()
    self.wf_generator.generate_cycle(workflow)
    response, workflow = self.wf_generator.activate_workflow(workflow)

    user = models.Person.query.filter(
        models.Person.email == '*****@*****.**').one()

    with freeze_time("2015-01-01 00:00:00"):  # before all tasks' due dates
      _, notif_data = common.get_daily_notifications()
      user_notifs = notif_data.get(user.email, {})
      self.assertNotIn("task_overdue", user_notifs)

      # now modify task's due date and check if overdue notification appears
      task = CycleTaskGroupObjectTask.query.filter(
          CycleTaskGroupObjectTask.title == "task for wf-2").one()
      task_id, task_code = task.id, task.slug

      response = self.import_data(OrderedDict((
          ("object_type", "CycleTask"),
          ("Code*", task_code),
          ("Start Date", "12/15/2014"),
          ("Due Date", "12/31/2014"),
      )))
      self._check_csv_response(response, expected_messages={})

      _, notif_data = common.get_daily_notifications()
      user_notifs = notif_data.get(user.email, {})
      self.assertIn("task_overdue", user_notifs)
      self.assertEqual(len(user_notifs["task_overdue"]), 1)
      self.assertIn(task_id, user_notifs["task_overdue"])
class TestCycleTaskImportUpdate(BaseTestCycleTaskImportUpdate):
    """ This class contains simple cycle task update tests using import
  functionality
  """

    CSV_DIR = join(abspath(dirname(__file__)), "test_csvs/")

    IMPORTABLE_COLUMN_NAMES = [
        "Summary",
        "Task Description",
        "Start Date",
        "Due Date",
        "State",
        "Task Assignees",
        "Task Secondary Assignees",
    ]

    def setUp(self):
        super(TestCycleTaskImportUpdate, self).setUp()
        self.wf_generator = WorkflowsGenerator()
        self.object_generator = ObjectGenerator()
        self.random_objects = self.object_generator.generate_random_objects(2)
        self.person_1 = ggrc_factories.PersonFactory()
        self.ftime_active = "2016-07-01"
        self.ftime_historical = "2014-05-01"
        self._create_test_cases_data()
        # It is needed because cycle-tasks are generated automatically with
        # 'slug' based on auto_increment 'id' field.
        # At start of each test we suppose that created cycle-task's 'slug'
        # lie in range from 1 to 10.
        db.session.execute('ALTER TABLE cycle_task_group_object_tasks '
                           'AUTO_INCREMENT = 1')

    def test_cycle_task_correct(self):
        """Test cycle task update via import with correct data"""
        self._generate_cycle_tasks()
        with freeze_time(self.ftime_active):
            cycle_task = wf_factories.CycleTaskGroupObjectTaskFactory(
                description="task active description 1")
            self.import_data(
                collections.OrderedDict([
                    ("object_type", "Cycle Task Group Object Task"),
                    ("Code*", cycle_task.slug),
                    ("description", "task active description 1"),
                ]))

        self._cmp_tasks(self.expected_cycle_task_correct)

    def test_cycle_task_warnings(self):
        """Test cycle task update via import with data which is the reason of
    warnings about non-importable columns."""
        self._generate_cycle_tasks()

        expected_warnings = {
            'Cycle Task': {
                'block_warnings': {
                    errors.ONLY_IMPORTABLE_COLUMNS_WARNING.format(
                        line=2,
                        columns=", ".join(self.IMPORTABLE_COLUMN_NAMES))
                },
                'row_warnings': []
            }
        }

        with freeze_time(self.ftime_active):
            person = ggrc_factories.PersonFactory()
            cycle_task = wf_factories.CycleTaskGroupObjectTaskFactory(
                title="task active title134567890")
            ct_data = collections.OrderedDict([
                ("object_type", "Cycle Task Group Object Task"),
                ("Code*", cycle_task.slug),
                ("Summary*", "task active title1-Updated"),
                ("Task Description", "task active title1 description"),
                ("Start Date", self.ftime_historical),
                ("Due Date", self.ftime_active),
                ("Actual Finish Date", self.ftime_active),
                ("Actual Verified Date", self.ftime_active),
                ("State", "Assigned"),
                ("Task Assignees", person.email),
                ("Task Secondary Assignees", person.email),
            ])

            response = self.import_data(ct_data)
            self._check_csv_response(response, expected_warnings)
            self._cmp_tasks(self.expected_cycle_task_correct)

    def test_cycle_task_permission_error(self):
        """Test cycle task update via import with non-admin user which is the
    reason of error. Only admin can update cycle tasks via import."""
        self._generate_cycle_tasks()
        with freeze_time(self.ftime_active):
            _, creator = self.object_generator.generate_person(
                user_role="Creator")

            cycle_task = wf_factories.CycleTaskGroupObjectTaskFactory(
                description="task active description 1")
            ct_data = collections.OrderedDict([
                ("object_type", "Cycle Task Group Object Task"),
                ("Code*", cycle_task.slug),
            ])
            response = self.import_data(ct_data, person=creator, safe=False)
            self._check_csv_response(response, self.expected_permission_error)
            # Cycle tasks' data shouldn't be changed in test DB after import run from
            # non-admin user

            expected_cycle_task_permission_error = {}
            expected_cycle_task_permission_error.update(
                self.generated_cycle_tasks_active)
            expected_cycle_task_permission_error.update(
                self.generated_cycle_tasks_historical)
            self._cmp_tasks(expected_cycle_task_permission_error)

    def _cmp_tasks(self, expected_ctasks):
        """Compare tasks values from argument's list and test DB."""
        for ctask in db.session.query(CycleTaskGroupObjectTask).all():
            if ctask.title not in expected_ctasks:
                continue
            exp_task = expected_ctasks[ctask.title]
            for attr, val in exp_task.iteritems():
                self.assertEqual(
                    str(getattr(ctask, attr, None)), val,
                    "attr {} value for {} not expected".format(
                        attr, ctask.title))

    # pylint: disable=too-many-arguments
    def _activate_workflow(self, ftime, workflow, task_group, task_group_tasks,
                           random_object, cycle_tasks):
        """Helper which is responsible for active cycle-tasks creation"""
        with freeze_time(ftime):
            _, wf = self.wf_generator.generate_workflow(workflow)
            _, tg = self.wf_generator.generate_task_group(wf, task_group)
            for task in task_group_tasks:
                self.wf_generator.generate_task_group_task(tg, task)
            self.wf_generator.generate_task_group_object(tg, random_object)
            _, cycle = self.wf_generator.generate_cycle(wf)
            self.wf_generator.activate_workflow(wf)
            for exp_slug, exp_task in cycle_tasks.iteritems():
                obj = db.session.query(CycleTaskGroupObjectTask).filter_by(
                    slug=exp_slug).first()
                if exp_task["status"] == "Verified":
                    self.wf_generator.modify_object(obj,
                                                    {"status": "Finished"})
                self.wf_generator.modify_object(obj,
                                                {"status": exp_task["status"]})
        self._cmp_tasks(cycle_tasks)
        return cycle

    def _generate_cycle_tasks(self):
        """Helper which is responsible for test data creation"""
        self._activate_workflow(self.ftime_active, self.workflow_active,
                                self.task_group_active,
                                self.task_group_tasks_active,
                                self.random_objects[0],
                                self.generated_cycle_tasks_active)
        cycle = self._activate_workflow(self.ftime_historical,
                                        self.workflow_historical,
                                        self.task_group_historical,
                                        self.task_group_tasks_historical,
                                        self.random_objects[1],
                                        self.generated_cycle_tasks_historical)
        with freeze_time(self.ftime_historical):
            cycle = Cycle.query.get(cycle.id)
            self.wf_generator.modify_object(cycle, data={"is_current": False})

    def _create_test_cases_data(self):
        """Create test cases data: for object generation,
    expected data for checks"""
        def person_dict(person_id):
            """Return person data"""
            return {
                "href": "/api/people/%d" % person_id,
                "id": person_id,
                "type": "Person"
            }

        wf_admin_role_id = {
            n: i
            for (
                i,
                n) in role.get_custom_roles_for(Workflow.__name__).iteritems()
        }['Admin']

        self.workflow_active = {
            "title":
            "workflow active title",
            "description":
            "workflow active description",
            "is_verification_needed":
            True,
            "access_control_list":
            [acl_helper.get_acl_json(wf_admin_role_id, self.person_1.id)],
            "notify_on_change":
            False,
        }

        self.task_group_active = {
            "title": "task group active title",
            "contact": person_dict(self.person_1.id),
        }

        self.task_group_tasks_active = [{
            "title":
            "task active title 1",
            "description":
            "task active description 1",
            "contact":
            person_dict(self.person_1.id),
            "start_date":
            "07/01/2016",
            "end_date":
            "07/06/2016",
        }, {
            "title":
            "task active title 2",
            "description":
            "task active description 2",
            "contact":
            person_dict(self.person_1.id),
            "start_date":
            "07/07/2016",
            "end_date":
            "07/12/2016",
        }, {
            "title":
            "task active title 3",
            "description":
            "task active description 3",
            "contact":
            person_dict(self.person_1.id),
            "start_date":
            "07/13/2016",
            "end_date":
            "07/18/2016",
        }, {
            "title":
            "task active title 4",
            "description":
            "task active description 4",
            "contact":
            person_dict(self.person_1.id),
            "start_date":
            "07/19/2016",
            "end_date":
            "07/24/2016",
        }, {
            "title":
            "task active title 5",
            "description":
            "task active description 5",
            "contact":
            person_dict(self.person_1.id),
            "start_date":
            "07/25/2016",
            "end_date":
            "07/30/2016",
        }]

        # Active cycle tasks which should be generated from previous structure
        # at the beginning of each test
        self.generated_cycle_tasks_active = {
            "CYCLETASK-1": {
                "title": self.task_group_tasks_active[0]["title"],
                "description": self.task_group_tasks_active[0]["description"],
                "start_date": "2016-07-01",
                "end_date": "2016-07-06",
                "finished_date": "None",
                "verified_date": "None",
                "status": "Assigned"
            },
            "CYCLETASK-2": {
                "title": self.task_group_tasks_active[1]["title"],
                "description": self.task_group_tasks_active[1]["description"],
                "start_date": "2016-07-07",
                "end_date": "2016-07-12",
                "finished_date": "None",
                "verified_date": "None",
                "status": "Declined"
            },
            "CYCLETASK-3": {
                "title": self.task_group_tasks_active[2]["title"],
                "description": self.task_group_tasks_active[2]["description"],
                "start_date": "2016-07-13",
                "end_date": "2016-07-18",
                "finished_date": "None",
                "verified_date": "None",
                "status": "In Progress"
            },
            "CYCLETASK-4": {
                "title": self.task_group_tasks_active[3]["title"],
                "description": self.task_group_tasks_active[3]["description"],
                "start_date": "2016-07-19",
                "end_date": "2016-07-22",
                "finished_date": "2016-07-01",
                "verified_date": "None",
                "status": "Finished"
            },
            "CYCLETASK-5": {
                "title": self.task_group_tasks_active[4]["title"],
                "description": self.task_group_tasks_active[4]["description"],
                "start_date": "2016-07-25",
                "end_date": "2016-07-29",
                "finished_date": "2016-07-01",
                "verified_date": "2016-07-01",
                "status": "Verified"
            }
        }

        self.workflow_historical = {
            "title":
            "workflow historical title",
            "description":
            "workflow historical description",
            "is_verification_needed":
            True,
            "access_control_list":
            [acl_helper.get_acl_json(wf_admin_role_id, self.person_1.id)],
            "notify_on_change":
            False,
        }

        self.task_group_historical = {
            "title": "task group historical title",
            "contact": person_dict(self.person_1.id),
        }

        self.task_group_tasks_historical = [
            {
                "title": "task historical title 1",
                "description": "task historical description 1",
                "contact": person_dict(self.person_1.id),
                "start_date": "05/01/2014",
                "end_date": "05/06/2014",
            },
            {
                "title": "task historical title 2",
                "description": "task historical description 2",
                "contact": person_dict(self.person_1.id),
                "start_date": "05/07/2014",
                "end_date": "05/12/2014",
            },
            {
                "title": "task historical title 3",
                "description": "task historical description 3",
                "contact": person_dict(self.person_1.id),
                "start_date": "05/13/2014",
                "end_date": "05/18/2014",
            },
            {
                "title": "task historical title 4",
                "description": "task historical description 4",
                "contact": person_dict(self.person_1.id),
                "start_date": "05/19/2014",
                "end_date": "05/24/2014",
            },
            {
                "title": "task historical title 5",
                "description": "task historical description 5",
                "contact": person_dict(self.person_1.id),
                "start_date": "05/25/2014",
                "end_date": "05/30/2014",
            },
        ]

        # Historical cycle tasks which should be generated from previous structure
        # at the beginning of each test.
        self.generated_cycle_tasks_historical = {
            "CYCLETASK-6": {
                "title": self.task_group_tasks_historical[0]["title"],
                "description":
                self.task_group_tasks_historical[0]["description"],
                "start_date": "2014-05-01",
                "end_date": "2014-05-06",
                "finished_date": "None",
                "verified_date": "None",
                "status": "Assigned"
            },
            "CYCLETASK-7": {
                "title": self.task_group_tasks_historical[1]["title"],
                "description":
                self.task_group_tasks_historical[1]["description"],
                "start_date": "2014-05-07",
                "end_date": "2014-05-12",
                "finished_date": "None",
                "verified_date": "None",
                "status": "Declined"
            },
            "CYCLETASK-8": {
                "title": self.task_group_tasks_historical[2]["title"],
                "description":
                self.task_group_tasks_historical[2]["description"],
                "start_date": "2014-05-13",
                "end_date": "2014-05-16",
                "finished_date": "None",
                "verified_date": "None",
                "status": "In Progress"
            },
            "CYCLETASK-9": {
                "title": self.task_group_tasks_historical[3]["title"],
                "description":
                self.task_group_tasks_historical[3]["description"],
                "start_date": "2014-05-19",
                "end_date": "2014-05-23",
                "finished_date": "2014-05-01",
                "verified_date": "None",
                "status": "Finished"
            },
            "CYCLETASK-10": {
                "title": self.task_group_tasks_historical[4]["title"],
                "description":
                self.task_group_tasks_historical[4]["description"],
                "start_date": "2014-05-23",
                "end_date": "2014-05-30",
                "finished_date": "2014-05-01",
                "verified_date": "2014-05-01",
                "status": "Verified"
            }
        }

        # Expected cycle tasks which should be created in correct cycle task update
        # case. It is needed for most tests.
        self.expected_cycle_task_correct = {
            "CYCLETASK-1": {
                "title": self.task_group_tasks_active[0]["title"] + " one",
                "description":
                self.task_group_tasks_active[0]["description"] + " one",
                "start_date": "2016-06-01",
                "end_date": "2016-06-06",
                "finished_date": "None",
                "verified_date": "None",
                "status": "Assigned"
            },
            "CYCLETASK-2": {
                "title": self.task_group_tasks_active[1]["title"] + " two",
                "description":
                self.task_group_tasks_active[1]["description"] + " two",
                "start_date": "2016-06-07",
                "end_date": "2016-06-12",
                "finished_date": "None",
                "verified_date": "None",
                "status": "Declined"
            },
            "CYCLETASK-3": {
                "title": self.task_group_tasks_active[2]["title"] + " three",
                "description":
                self.task_group_tasks_active[2]["description"] + " three",
                "start_date": "2016-06-13",
                "end_date": "2016-06-18",
                "finished_date": "None",
                "verified_date": "None",
                "status": "In Progress"
            },
            "CYCLETASK-4": {
                "title": self.task_group_tasks_active[3]["title"] + " four",
                "description":
                self.task_group_tasks_active[3]["description"] + " four",
                "start_date": "2016-06-19",
                "end_date": "2016-06-24",
                "status": "Finished"
            },
            "CYCLETASK-5": {
                "title": self.task_group_tasks_active[4]["title"] + " five",
                "description":
                self.task_group_tasks_active[4]["description"] + " five",
                "start_date": "2016-06-25",
                "end_date": "2016-06-30",
                "finished_date": "2016-07-01",
                "verified_date": "2016-07-01",
                "status": "Verified"
            },
            "CYCLETASK-6": {
                "title":
                self.task_group_tasks_historical[0]["title"] + " one",
                "description":
                self.task_group_tasks_historical[0]["description"] + " one",
                "start_date":
                "2014-04-01",
                "end_date":
                "2014-04-06",
                "finished_date":
                "None",
                "verified_date":
                "None",
                "status":
                "Assigned"
            },
            "CYCLETASK-7": {
                "title":
                self.task_group_tasks_historical[1]["title"] + " two",
                "description":
                self.task_group_tasks_historical[1]["description"] + " two",
                "start_date":
                "2014-04-07",
                "end_date":
                "2014-04-12",
                "finished_date":
                "None",
                "verified_date":
                "None",
                "status":
                "Declined"
            },
            "CYCLETASK-8": {
                "title":
                self.task_group_tasks_historical[2]["title"] + " three",
                "description":
                self.task_group_tasks_historical[2]["description"] + " three",
                "start_date":
                "2014-04-13",
                "end_date":
                "2014-04-18",
                "finished_date":
                "None",
                "verified_date":
                "None",
                "status":
                "In Progress"
            },
            "CYCLETASK-9": {
                "title":
                self.task_group_tasks_historical[3]["title"] + " four",
                "description":
                self.task_group_tasks_historical[3]["description"] + " four",
                "start_date":
                "2014-04-19",
                "end_date":
                "2014-04-24",
                "status":
                "Finished"
            },
            "CYCLETASK-10": {
                "title":
                self.task_group_tasks_historical[4]["title"] + " five",
                "description":
                self.task_group_tasks_historical[4]["description"] + " five",
                "start_date":
                "2014-04-25",
                "end_date":
                "2014-04-30",
                "finished_date":
                "2014-05-01",
                "verified_date":
                "2014-05-01",
                "status":
                "Verified"
            }
        }

        # This is an error message which should be shown during
        # test_cycle_task_create_error test
        self.expected_create_error = {
            'Cycle Task': {
                'row_errors': {errors.CREATE_INSTANCE_ERROR.format(line=13)}
            }
        }

        # Below is expected date errors for test_cycle_task_date_error. They should
        # be shown during date validator's tests.
        self.expected_date_error = {
            'Cycle Task': {
                'row_errors': {
                    errors.INVALID_START_END_DATES.format(
                        line=3,
                        start_date="Start Date",
                        end_date="End Date",
                    ),
                    errors.INVALID_START_END_DATES.format(
                        line=8,
                        start_date="Start Date",
                        end_date="End Date",
                    ),
                },
            }
        }
        # Below is expected cycle-tasks data which should appear in test DB after
        # test_cycle_task_date_error run
        self.expected_cycle_task_date_error = dict()
        self.expected_cycle_task_date_error.update(
            self.generated_cycle_tasks_active)
        self.expected_cycle_task_date_error.update(
            self.generated_cycle_tasks_historical)
        self.expected_cycle_task_date_error["CYCLETASK-9"] = (
            self.expected_cycle_task_correct["CYCLETASK-9"])
        self.expected_cycle_task_date_error["CYCLETASK-10"] = (
            self.expected_cycle_task_correct["CYCLETASK-10"])

        # Expected error message which should be shown after
        # test_cycle_task_permission_error run
        self.expected_permission_error = {
            'Cycle Task': {
                'block_errors': {errors.PERMISSION_ERROR.format(line=2)}
            }
        }
Esempio n. 43
0
class TestOneTimeWorkflowNotification(TestCase):

  """ This class contains simple one time workflow tests that are not
  in the gsheet test grid
  """

  def setUp(self):
    super(TestOneTimeWorkflowNotification, self).setUp()
    self.api = Api()
    self.wf_generator = WorkflowsGenerator()
    self.object_generator = ObjectGenerator()

    self.random_objects = self.object_generator.generate_random_objects()
    self.random_people = self.object_generator.generate_random_people(
        user_role="Administrator"
    )
    self.create_test_cases()

    def init_decorator(init):
      def new_init(self, *args, **kwargs):
        init(self, *args, **kwargs)
        if hasattr(self, "created_at"):
          self.created_at = datetime.now()
      return new_init

    Notification.__init__ = init_decorator(Notification.__init__)

  def test_one_time_wf_activate(self):
    def get_person(person_id):
      return db.session.query(Person).filter(Person.id == person_id).one()
    with freeze_time("2015-04-10"):
      _, wf = self.wf_generator.generate_workflow(self.one_time_workflow_1)

      _, cycle = self.wf_generator.generate_cycle(wf)
      self.wf_generator.activate_workflow(wf)

      person_2 = get_person(self.random_people[2].id)

    with freeze_time("2015-04-11"):
      _, notif_data = common.get_daily_notifications()
      self.assertIn(person_2.email, notif_data)
      self.assertIn("cycle_started", notif_data[person_2.email])
      self.assertIn(cycle.id, notif_data[person_2.email]["cycle_started"])
      self.assertIn("my_tasks",
                    notif_data[person_2.email]["cycle_data"][cycle.id])

      person_1 = get_person(self.random_people[0].id)

    with freeze_time("2015-05-03"):  # two days befor due date
      _, notif_data = common.get_daily_notifications()
      self.assertIn(person_1.email, notif_data)
      self.assertNotIn("due_in", notif_data[person_1.email])
      self.assertNotIn("due_today", notif_data[person_1.email])

    with freeze_time("2015-05-04"):  # one day befor due date
      _, notif_data = common.get_daily_notifications()
      self.assertEqual(len(notif_data[person_1.email]["due_in"]), 1)

    with freeze_time("2015-05-05"):  # due date
      _, notif_data = common.get_daily_notifications()
      self.assertEqual(len(notif_data[person_1.email]["due_today"]), 1)

  @patch("ggrc.notifications.common.send_email")
  def test_one_time_wf_activate_single_person(self, mock_mail):

    with freeze_time("2015-04-10"):
      user = "******"
      _, wf = self.wf_generator.generate_workflow(
          self.one_time_workflow_single_person)

      _, cycle = self.wf_generator.generate_cycle(wf)
      self.wf_generator.activate_workflow(wf)

    with freeze_time("2015-04-11"):
      _, notif_data = common.get_daily_notifications()
      self.assertIn("cycle_started", notif_data[user])
      self.assertIn(cycle.id, notif_data[user]["cycle_started"])
      self.assertIn("my_tasks", notif_data[user]["cycle_data"][cycle.id])
      self.assertIn("cycle_tasks", notif_data[user]["cycle_data"][cycle.id])
      self.assertIn(
          "my_task_groups", notif_data[user]["cycle_data"][cycle.id])
      self.assertIn("cycle_url", notif_data[user]["cycle_started"][cycle.id])

      cycle = Cycle.query.get(cycle.id)
      cycle_data = notif_data[user]["cycle_data"][cycle.id]
      for task in cycle.cycle_task_group_object_tasks:
        self.assertIn(task.id, cycle_data["my_tasks"])
        self.assertIn(task.id, cycle_data["cycle_tasks"])
        self.assertIn("title", cycle_data["my_tasks"][task.id])
        self.assertIn("title", cycle_data["cycle_tasks"][task.id])
        self.assertIn("cycle_task_url", cycle_data["cycle_tasks"][task.id])

    with freeze_time("2015-05-03"):  # two days before due date
      _, notif_data = common.get_daily_notifications()
      self.assertIn(user, notif_data)
      self.assertNotIn("due_in", notif_data[user])
      self.assertNotIn("due_today", notif_data[user])

    with freeze_time("2015-05-04"):  # one day before due date
      _, notif_data = common.get_daily_notifications()
      self.assertEqual(len(notif_data[user]["due_in"]), 2)

    with freeze_time("2015-05-05"):  # due date
      _, notif_data = common.get_daily_notifications()
      self.assertEqual(len(notif_data[user]["due_today"]), 2)

      common.send_daily_digest_notifications()
      self.assertEqual(mock_mail.call_count, 1)

  def create_test_cases(self):
    def person_dict(person_id):
      return {
          "href": "/api/people/%d" % person_id,
          "id": person_id,
          "type": "Person"
      }

    role_id = all_models.AccessControlRole.query.filter(
        all_models.AccessControlRole.name == "Task Assignees",
        all_models.AccessControlRole.object_type == "TaskGroupTask",
    ).one().id
    self.one_time_workflow_1 = {
        "title": "one time test workflow",
        "description": "some test workflow",
        "notify_on_change": True,
        # admin will be current user with id == 1
        "task_groups": [{
            "title": "one time task group",
            "contact": person_dict(self.random_people[2].id),
            "task_group_tasks": [{
                "title": "task 1",
                "description": "some task",
                "access_control_list": [
                    acl_helper.get_acl_json(role_id, self.random_people[0].id)
                ],
                "start_date": date(2015, 5, 1),  # friday
                "end_date": date(2015, 5, 5),
            }, {
                "title": "task 2",
                "description": "some task",
                "access_control_list": [
                    acl_helper.get_acl_json(role_id, self.random_people[1].id)
                ],
                "start_date": date(2015, 5, 4),
                "end_date": date(2015, 5, 7),
            }],
            "task_group_objects": self.random_objects[:2]
        }, {
            "title": "another one time task group",
            "contact": person_dict(self.random_people[2].id),
            "task_group_tasks": [{
                "title": "task 1 in tg 2",
                "description": "some task",
                "access_control_list": [
                    acl_helper.get_acl_json(role_id, self.random_people[0].id)
                ],
                "start_date": date(2015, 5, 8),  # friday
                "end_date": date(2015, 5, 12),
            }, {
                "title": "task 2 in tg 2",
                "description": "some task",
                "access_control_list": [
                    acl_helper.get_acl_json(role_id, self.random_people[2].id)
                ],
                "start_date": date(2015, 5, 1),  # friday
                "end_date": date(2015, 5, 5),
            }],
            "task_group_objects": []
        }]
    }

    user = Person.query.filter(Person.email == "*****@*****.**").one().id

    self.one_time_workflow_single_person = {
        "title": "one time test workflow",
        "notify_on_change": True,
        "description": "some test workflow",
        # admin will be current user with id == 1
        "task_groups": [{
            "title": "one time task group",
            "contact": person_dict(user),
            "task_group_tasks": [{
                "title": u"task 1 \u2062 WITH AN UMBRELLA ELLA ELLA. \u2062",
                "description": "some task. ",
                "access_control_list": [
                    acl_helper.get_acl_json(role_id, user)
                ],
                "start_date": date(2015, 5, 1),  # friday
                "end_date": date(2015, 5, 5),
            }, {
                "title": "task 2",
                "description": "some task",
                "access_control_list": [
                    acl_helper.get_acl_json(role_id, user)
                ],
                "start_date": date(2015, 5, 4),
                "end_date": date(2015, 5, 7),
            }],
            "task_group_objects": self.random_objects[:2]
        }, {
            "title": "another one time task group",
            "contact": person_dict(user),
            "task_group_tasks": [{
                "title": u"task 1 \u2062 WITH AN UMBRELLA ELLA ELLA. \u2062",
                "description": "some task",
                "access_control_list": [
                    acl_helper.get_acl_json(role_id, user)
                ],
                "start_date": date(2015, 5, 8),  # friday
                "end_date": date(2015, 5, 12),
            }, {
                "title": "task 2 in tg 2",
                "description": "some task",
                "access_control_list": [
                    acl_helper.get_acl_json(role_id, user)
                ],
                "start_date": date(2015, 5, 1),  # friday
                "end_date": date(2015, 5, 5),
            }],
            "task_group_objects": []
        }]
    }
class TestTaskOverdueNotificationsUsingAPI(TestTaskOverdueNotifications):
  """Tests for overdue notifications when changing Tasks with an API."""

  # pylint: disable=invalid-name

  def setUp(self):
    super(TestTaskOverdueNotificationsUsingAPI, self).setUp()
    self.api = Api()
    self.wf_generator = WorkflowsGenerator()
    self.object_generator = ObjectGenerator()
    models.Notification.query.delete()

    self._fix_notification_init()

    self.random_objects = self.object_generator.generate_random_objects(2)
    _, self.user = self.object_generator.generate_person(
        user_role="Administrator")
    self._create_test_cases()

  @ddt.data(True, False)
  @patch("ggrc.notifications.common.send_email")
  def test_sending_overdue_notifications_for_tasks(self, is_vf_needed, _):
    """Overdue notifications should be sent for overdue tasks every day.

    Even if an overdue notification has already been sent, it should still be
    sent in every following daily digest f a task is still overdue.
    """
    with freeze_time("2017-05-15 14:25:36"):
      tmp = self.one_time_workflow.copy()
      tmp['is_verification_needed'] = is_vf_needed
      _, workflow = self.wf_generator.generate_workflow(tmp)
      self.wf_generator.generate_cycle(workflow)
      response, workflow = self.wf_generator.activate_workflow(workflow)
      self.assert200(response)

    tasks = workflow.cycles[0].cycle_task_group_object_tasks
    task1_id = tasks[0].id
    task2_id = tasks[1].id

    user = models.Person.query.get(self.user.id)

    with freeze_time("2017-05-14 08:09:10"):
      _, notif_data = common.get_daily_notifications()
      user_notifs = notif_data.get(user.email, {})
      self.assertNotIn("task_overdue", user_notifs)

    with freeze_time("2017-05-15 08:09:10"):  # task 1 due date
      _, notif_data = common.get_daily_notifications()
      user_notifs = notif_data.get(user.email, {})
      self.assertNotIn("task_overdue", user_notifs)

    with freeze_time("2017-05-16 08:09:10"):  # task 2 due date
      _, notif_data = common.get_daily_notifications()
      user_notifs = notif_data.get(user.email, {})
      self.assertIn("task_overdue", user_notifs)

      overdue_task_ids = sorted(user_notifs["task_overdue"].keys())
      self.assertEqual(overdue_task_ids, [task1_id])

    with freeze_time("2017-05-17 08:09:10"):  # after both tasks' due dates
      _, notif_data = common.get_daily_notifications()
      user_notifs = notif_data.get(user.email, {})
      self.assertIn("task_overdue", user_notifs)

      overdue_task_ids = sorted(user_notifs["task_overdue"].keys())
      self.assertEqual(overdue_task_ids, [task1_id, task2_id])

      common.send_daily_digest_notifications()

    # even after sending the overdue notifications, they are sent again the
    # day after, too
    with freeze_time("2017-05-18 08:09:10"):
      _, notif_data = common.get_daily_notifications()
      user_notifs = notif_data.get(user.email, {})
      self.assertIn("task_overdue", user_notifs)

      overdue_task_ids = sorted(user_notifs["task_overdue"].keys())
      self.assertEqual(overdue_task_ids, [task1_id, task2_id])

  @ddt.data(True, False)
  @patch("ggrc.notifications.common.send_email")
  def test_adjust_overdue_notifications_on_task_due_date_change(self,
                                                                is_vf_needed,
                                                                _):
    """Sending overdue notifications should adjust to task due date changes."""
    with freeze_time("2017-05-15 14:25:36"):
      tmp = self.one_time_workflow.copy()
      tmp['is_verification_needed'] = is_vf_needed
      _, workflow = self.wf_generator.generate_workflow(tmp)
      self.wf_generator.generate_cycle(workflow)
      response, workflow = self.wf_generator.activate_workflow(workflow)
      self.assert200(response)

      tasks = workflow.cycles[0].cycle_task_group_object_tasks
      task1, task2 = tasks
      self.wf_generator.modify_object(task2, {"end_date": date(2099, 12, 31)})

      user = models.Person.query.get(self.user.id)

    with freeze_time("2017-05-16 08:09:10"):  # a day after task1 due date
      _, notif_data = common.get_daily_notifications()
      user_notifs = notif_data.get(user.email, {})
      self.assertIn("task_overdue", user_notifs)
      self.assertEqual(len(user_notifs["task_overdue"]), 1)

      # change task1 due date, there should be no overdue notification anymore
      self.wf_generator.modify_object(task1, {"end_date": date(2017, 5, 16)})
      _, notif_data = common.get_daily_notifications()
      user_notifs = notif_data.get(user.email, {})
      self.assertNotIn("task_overdue", user_notifs)

      # change task1 due date to the past there should a notification again
      self.wf_generator.modify_object(task1, {"end_date": date(2017, 5, 14)})
      _, notif_data = common.get_daily_notifications()
      user_notifs = notif_data.get(user.email, {})
      self.assertIn("task_overdue", user_notifs)
      self.assertEqual(len(user_notifs["task_overdue"]), 1)

  @ddt.data(True, False)
  @patch("ggrc.notifications.common.send_email")
  def test_adjust_overdue_notifications_on_task_status_change(self,
                                                              is_vf_needed,
                                                              _):
    """Sending overdue notifications should take task status into account."""
    with freeze_time("2017-05-15 14:25:36"):
      tmp = self.one_time_workflow.copy()
      tmp['is_verification_needed'] = is_vf_needed
      _, workflow = self.wf_generator.generate_workflow(tmp)
      self.wf_generator.generate_cycle(workflow)
      response, workflow = self.wf_generator.activate_workflow(workflow)
      self.assert200(response)

      tasks = workflow.cycles[0].cycle_task_group_object_tasks
      task1, task2 = tasks
      self.wf_generator.modify_object(task2, {"end_date": date(2099, 12, 31)})

      user = models.Person.query.get(self.user.id)
      user_email = user.email
    if is_vf_needed:
      non_final_states = [CycleTaskGroupObjectTask.ASSIGNED,
                          CycleTaskGroupObjectTask.IN_PROGRESS,
                          CycleTaskGroupObjectTask.FINISHED,
                          CycleTaskGroupObjectTask.DECLINED]
      final_state = CycleTaskGroupObjectTask.VERIFIED
    else:
      non_final_states = [CycleTaskGroupObjectTask.ASSIGNED,
                          CycleTaskGroupObjectTask.IN_PROGRESS]
      final_state = CycleTaskGroupObjectTask.FINISHED

    with freeze_time("2017-05-16 08:09:10"):  # a day after task1 due date
      for state in non_final_states:
        # clear all notifications before before changing the task status
        models.Notification.query.delete()
        _, notif_data = common.get_daily_notifications()
        self.assertEqual(notif_data, {})

        self.wf_generator.modify_object(task1, {"status": state})

        _, notif_data = common.get_daily_notifications()
        user_notifs = notif_data.get(user_email, {})
        self.assertIn("task_overdue", user_notifs)
        self.assertEqual(len(user_notifs["task_overdue"]), 1)

      # WITHOUT clearing the overdue notifications, move the task to "verified"
      # state, and the overdue notification should disappear.

      self.wf_generator.modify_object(task1, {"status": final_state})
      _, notif_data = common.get_daily_notifications()
      user_notifs = notif_data.get(user_email, {})
      self.assertNotIn("task_overdue", user_notifs)

  @ddt.data(True, False)
  @patch("ggrc.notifications.common.send_email")
  def test_stop_sending_overdue_notification_if_task_gets_deleted(self,
                                                                  is_vf_needed,
                                                                  _):
    """Overdue notifications should not be sent for deleted tasks."""
    with freeze_time("2017-05-15 14:25:36"):
      tmp = self.one_time_workflow.copy()
      tmp['is_verification_needed'] = is_vf_needed
      _, workflow = self.wf_generator.generate_workflow(tmp)
      self.wf_generator.generate_cycle(workflow)
      response, workflow = self.wf_generator.activate_workflow(workflow)
      self.assert200(response)

    tasks = workflow.cycles[0].cycle_task_group_object_tasks
    task1, task2 = tasks

    user = models.Person.query.get(self.user.id)
    user_email = user.email

    with freeze_time("2017-10-16 08:09:10"):  # long after both task due dates
      _, notif_data = common.get_daily_notifications()
      user_notifs = notif_data.get(user_email, {})
      self.assertIn("task_overdue", user_notifs)
      self.assertEqual(len(user_notifs["task_overdue"]), 2)

      db.session.delete(task2)
      db.session.commit()

      _, notif_data = common.get_daily_notifications()
      user_notifs = notif_data.get(user_email, {})
      self.assertIn("task_overdue", user_notifs)
      self.assertEqual(len(user_notifs["task_overdue"]), 1)

      db.session.delete(task1)
      db.session.commit()

      _, notif_data = common.get_daily_notifications()
      user_notifs = notif_data.get(user.email, {})
      self.assertNotIn("task_overdue", user_notifs)

  def _create_test_cases(self):
    """Create configuration to use for generating a new workflow."""
    def person_dict(person_id):
      return {
          "href": "/api/people/" + str(person_id),
          "id": person_id,
          "type": "Person"
      }
    role_id = models.all_models.AccessControlRole.query.filter(
        models.all_models.AccessControlRole.name == "Task Assignees",
        models.all_models.AccessControlRole.object_type == "TaskGroupTask",
    ).one().id
    self.one_time_workflow = {
        "title": "one time test workflow",
        "notify_on_change": True,
        "description": "some test workflow",
        # admin will be current user with id == 1
        "task_groups": [{
            "title": "one time task group",
            "contact": person_dict(self.user.id),
            "task_group_tasks": [{
                "title": "task 1",
                "description": "some task",
                "start_date": date(2017, 5, 5),  # Friday
                "end_date": date(2017, 5, 15),
                "access_control_list": [
                    acl_helper.get_acl_json(role_id, self.user.id)],
            }, {
                "title": "task 2",
                "description": "some task 2",
                "start_date": date(2017, 5, 5),  # Friday
                "end_date": date(2017, 5, 16),
                "access_control_list": [
                    acl_helper.get_acl_json(role_id, self.user.id)],
            }],
            "task_group_objects": self.random_objects
        }]
    }
Esempio n. 45
0
class TestOneTimeWfEndDateChange(TestCase):

  """ This class contains simple one time workflow tests that are not
  in the gsheet test grid
  """

  def setUp(self):
    super(TestOneTimeWfEndDateChange, self).setUp()
    self.api = Api()
    self.wf_generator = WorkflowsGenerator()
    self.object_generator = ObjectGenerator()
    Notification.query.delete()

    self.random_objects = self.object_generator.generate_random_objects(2)
    _, self.user = self.object_generator.generate_person(
        user_role="Administrator")
    self.create_test_cases()

    def init_decorator(init):
      def new_init(self, *args, **kwargs):
        init(self, *args, **kwargs)
        if hasattr(self, "created_at"):
          self.created_at = datetime.now()
      return new_init

    Notification.__init__ = init_decorator(Notification.__init__)

  @patch("ggrc.notifications.common.send_email")
  def test_no_date_change(self, mock_mail):
    def get_person(person_id):
      return db.session.query(Person).filter(Person.id == person_id).one()

    with freeze_time("2015-04-10 03:21:34"):
      _, workflow = self.wf_generator.generate_workflow(
          self.one_time_workflow_1)

      _, cycle = self.wf_generator.generate_cycle(workflow)
      self.wf_generator.activate_workflow(workflow)

    with freeze_time("2015-04-11 03:21:34"):
      user = get_person(self.user.id)
      _, notif_data = common.get_daily_notifications()
      self.assertIn("cycle_started", notif_data[user.email])

    with freeze_time("2015-05-02 03:21:34"):
      _, notif_data = common.get_daily_notifications()
      self.assertIn(user.email, notif_data)
      self.assertIn("cycle_started", notif_data[user.email])
      self.assertNotIn("due_in", notif_data[user.email])
      self.assertNotIn("due_today", notif_data[user.email])

    with freeze_time("2015-05-02 03:21:34"):
      common.send_daily_digest_notifications()
      _, notif_data = common.get_daily_notifications()
      self.assertEqual(notif_data, {})

      # one email to owner and one to assigne
      self.assertEqual(mock_mail.call_count, 2)

    with freeze_time("2015-05-04 03:21:34"):  # one day before due date
      _, notif_data = common.get_daily_notifications()
      user = get_person(self.user.id)
      self.assertIn("due_in", notif_data[user.email])
      self.assertEqual(len(notif_data[user.email]["due_in"]), 2)

    with freeze_time("2015-05-04 03:21:34"):  # one day before due date
      common.send_daily_digest_notifications()
      _, notif_data = common.get_daily_notifications()
      self.assertEqual(notif_data, {})

      # one email to owner and one to assigne
      self.assertEqual(mock_mail.call_count, 3)

    with freeze_time("2015-05-05 03:21:34"):  # due date
      _, notif_data = common.get_daily_notifications()
      self.assertIn("due_today", notif_data[user.email])
      self.assertEqual(len(notif_data[user.email]["due_today"]), 2)

  @patch("ggrc.notifications.common.send_email")
  def test_move_end_date_to_future(self, mock_mail):
    """
    test moving the end date to the future, befor due_in and due_today
    notifications have been sent
    """
    def get_person(person_id):
      return db.session.query(Person).filter(Person.id == person_id).one()

    with freeze_time("2015-04-10 03:21:34"):
      _, workflow = self.wf_generator.generate_workflow(
          self.one_time_workflow_1)

      _, cycle = self.wf_generator.generate_cycle(workflow)
      self.wf_generator.activate_workflow(workflow)

    with freeze_time("2015-04-11 03:21:34"):
      user = get_person(self.user.id)
      _, notif_data = common.get_daily_notifications()
      self.assertIn("cycle_started", notif_data[user.email])

    with freeze_time("2015-05-02 03:21:34"):
      _, notif_data = common.get_daily_notifications()
      self.assertIn(user.email, notif_data)
      self.assertIn("cycle_started", notif_data[user.email])
      self.assertNotIn("due_in", notif_data[user.email])
      self.assertNotIn("due_today", notif_data[user.email])

    with freeze_time("2015-05-02 03:21:34"):
      common.send_daily_digest_notifications()
      _, notif_data = common.get_daily_notifications()
      self.assertEqual(notif_data, {})

      # one email to owner and one to assigne
      self.assertEqual(mock_mail.call_count, 2)

    with freeze_time("2015-05-03 03:21:34"):
      cycle = Cycle.query.get(cycle.id)
      task1 = CycleTaskGroupObjectTask.query.get(
          cycle.cycle_task_group_object_tasks[0].id)
      task2 = CycleTaskGroupObjectTask.query.get(
          cycle.cycle_task_group_object_tasks[1].id)

      self.wf_generator.modify_object(
          task1, data={"end_date": date(2015, 5, 15)})
      self.wf_generator.modify_object(
          task2, data={"end_date": date(2015, 5, 15)})

    with freeze_time("2015-05-04 03:21:34"):  # one day befor due date
      _, notif_data = common.get_daily_notifications()
      user = get_person(self.user.id)
      self.assertEqual(notif_data, {})

    with freeze_time("2015-05-05 03:21:34"):  # due date
      _, notif_data = common.get_daily_notifications()
      self.assertEqual(notif_data, {})

    with freeze_time("2015-05-14 03:21:34"):  # due date
      _, notif_data = common.get_daily_notifications()
      self.assertIn(user.email, notif_data)
      self.assertIn("due_in", notif_data[user.email])
      self.assertEqual(len(notif_data[user.email]["due_in"]),
                       len(self.random_objects))

    with freeze_time("2015-05-15 03:21:34"):  # due date
      _, notif_data = common.get_daily_notifications()
      self.assertIn(user.email, notif_data)

      # yesterdays mail has not been sent
      self.assertIn("due_in", notif_data[user.email])

      self.assertIn("due_today", notif_data[user.email])
      self.assertEqual(len(notif_data[user.email]["due_today"]),
                       len(self.random_objects))

  @patch("ggrc.notifications.common.send_email")
  def test_move_end_date_to_past(self, mock_mail):
    def get_person(person_id):
      return db.session.query(Person).filter(Person.id == person_id).one()

    with freeze_time("2015-04-10 03:21:34"):
      _, workflow = self.wf_generator.generate_workflow(
          self.one_time_workflow_1)

      _, cycle = self.wf_generator.generate_cycle(workflow)
      self.wf_generator.activate_workflow(workflow)

    with freeze_time("2015-05-02 03:21:34"):
      common.send_daily_digest_notifications()
      _, notif_data = common.get_daily_notifications()
      self.assertEqual(notif_data, {})

      # one email to owner and one to assigne
      self.assertEqual(mock_mail.call_count, 2)

    with freeze_time("2015-05-03 03:21:34"):
      cycle = Cycle.query.get(cycle.id)
      task1 = CycleTaskGroupObjectTask.query.get(
          cycle.cycle_task_group_object_tasks[0].id)
      task2 = CycleTaskGroupObjectTask.query.get(
          cycle.cycle_task_group_object_tasks[1].id)

      self.wf_generator.modify_object(
          task1, data={"end_date": date(2015, 5, 1)})
      self.wf_generator.modify_object(
          task2, data={"end_date": date(2015, 5, 1)})

    with freeze_time("2015-05-03 03:21:34"):  # one day befor due date
      _, notif_data = common.get_daily_notifications()
      self.assertEqual(notif_data, {})

    with freeze_time("2015-05-04 03:21:34"):  # due date
      _, notif_data = common.get_daily_notifications()
      self.assertEqual(notif_data, {})

    with freeze_time("2015-05-05 03:21:34"):  # due date
      _, notif_data = common.get_daily_notifications()
      self.assertEqual(notif_data, {})

  @patch("ggrc.notifications.common.send_email")
  def test_move_end_date_to_today(self, mock_mail):
    def get_person(person_id):
      return db.session.query(Person).filter(Person.id == person_id).one()

    with freeze_time("2015-04-10 03:21:34"):
      _, workflow = self.wf_generator.generate_workflow(
          self.one_time_workflow_1)

      _, cycle = self.wf_generator.generate_cycle(workflow)
      self.wf_generator.activate_workflow(workflow)

    with freeze_time("2015-05-02 03:21:34"):
      common.send_daily_digest_notifications()
      _, notif_data = common.get_daily_notifications()
      self.assertEqual(notif_data, {})

      # one email to owner and one to assigne
      self.assertEqual(mock_mail.call_count, 2)

    with freeze_time("2015-05-03 03:21:34"):
      cycle = Cycle.query.get(cycle.id)
      task1 = CycleTaskGroupObjectTask.query.get(
          cycle.cycle_task_group_object_tasks[0].id)
      task2 = CycleTaskGroupObjectTask.query.get(
          cycle.cycle_task_group_object_tasks[1].id)

      self.wf_generator.modify_object(
          task1, data={"end_date": date(2015, 5, 3)})
      self.wf_generator.modify_object(
          task2, data={"end_date": date(2015, 5, 4)})

    with freeze_time("2015-05-03 03:21:34"):  # one day befor due date
      user = get_person(self.user.id)
      _, notif_data = common.get_daily_notifications()

      self.assertNotEquals(notif_data, {})
      self.assertIn(user.email, notif_data)
      self.assertIn("due_today", notif_data[user.email])
      self.assertIn("due_in", notif_data[user.email])
      self.assertEqual(len(notif_data[user.email]["due_today"]), 1)

      common.send_daily_digest_notifications()

    with freeze_time("2015-05-04 03:21:34"):  # due date
      user = get_person(self.user.id)
      _, notif_data = common.get_daily_notifications()
      self.assertIn(user.email, notif_data)
      self.assertIn("due_today", notif_data[user.email])
      self.assertNotIn("due_in", notif_data[user.email])
      common.send_daily_digest_notifications()

    with freeze_time("2015-05-05 03:21:34"):  # due date
      _, notif_data = common.get_daily_notifications()
      self.assertEqual(notif_data, {})

  def create_test_cases(self):
    def person_dict(person_id):
      return {
          "href": "/api/people/%d" % person_id,
          "id": person_id,
          "type": "Person"
      }

    self.one_time_workflow_1 = {
        "title": "one time test workflow",
        "notify_on_change": True,
        "description": "some test workflow",
        "owners": [person_dict(self.user.id)],
        "task_groups": [{
            "title": "one time task group",
            "contact": person_dict(self.user.id),
            "task_group_tasks": [{
                "title": "task 1",
                "description": "some task",
                "contact": person_dict(self.user.id),
                "start_date": date(2015, 5, 1),  # friday
                "end_date": date(2015, 5, 5),
            }, {
                "title": "task 2",
                "description": "some task 2",
                "contact": person_dict(self.user.id),
                "start_date": date(2015, 5, 1),  # friday
                "end_date": date(2015, 5, 5),
            }],
            "task_group_objects": self.random_objects
        }]
    }
Esempio n. 46
0
class TestMonthlyWorkflowNotification(TestCase):

  """ This class contains simple one time workflow tests that are not
  in the gsheet test grid
  """

  def setUp(self):
    super(TestMonthlyWorkflowNotification, self).setUp()
    self.api = Api()
    self.wf_generator = WorkflowsGenerator()
    self.object_generator = ObjectGenerator()

    self.random_objects = self.object_generator.generate_random_objects()
    _, self.person_1 = self.object_generator.generate_person(
        user_role="Administrator")
    _, self.person_2 = self.object_generator.generate_person(
        user_role="Administrator")
    self.create_test_cases()

    def init_decorator(init):
      def new_init(self, *args, **kwargs):
        init(self, *args, **kwargs)
        if hasattr(self, "created_at"):
          self.created_at = dt.datetime.now()
      return new_init

    Notification.__init__ = init_decorator(Notification.__init__)

  @patch("ggrc.notifications.common.send_email")
  def test_auto_generate_cycle(self, mock_mail):

    person_1_email = Person.query.get(self.person_1.id).email
    with freeze_time("2015-04-01"):
      _, wf = self.wf_generator.generate_workflow(self.monthly_workflow_1)
      self.wf_generator.activate_workflow(wf)
      _, notif_data = common.get_daily_notifications()
      self.assertNotIn(person_1_email, notif_data)

    with freeze_time("2015-04-02"):
      self.api.client.get("nightly_cron_endpoint")
      _, notif_data = common.get_daily_notifications()
      self.assertNotIn(person_1_email, notif_data)
      start_recurring_cycles()
      _, notif_data = common.get_daily_notifications()
      self.assertNotIn(person_1_email, notif_data)

    # cycle starts on monday - 6th, and not on 5th
    with freeze_time("2015-04-03"):
      start_recurring_cycles()
      _, notif_data = common.get_daily_notifications()
      self.assertIn(person_1_email, notif_data)
      self.assertIn("cycle_started", notif_data[person_1_email])

    with freeze_time("2015-04-15"):  # one day befor due date
      _, notif_data = common.get_daily_notifications()
      self.assertIn(person_1_email, notif_data)

    with freeze_time("2015-04-25"):  # due date
      _, notif_data = common.get_daily_notifications()
      self.assertIn(person_1_email, notif_data)

  @patch("ggrc.notifications.common.send_email")
  def test_manual_generate_cycle(self, mock_mail):

    with freeze_time("2015-04-01"):
      _, wf = self.wf_generator.generate_workflow(self.monthly_workflow_1)
      self.wf_generator.activate_workflow(wf)

      person_1 = Person.query.get(self.person_1.id)

    with freeze_time("2015-04-03"):
      _, notif_data = common.get_daily_notifications()

    with freeze_time("2015-04-03"):
      _, cycle = self.wf_generator.generate_cycle(wf)
      _, notif_data = common.get_daily_notifications()
      person_1 = Person.query.get(self.person_1.id)
      self.assertIn("cycle_started", notif_data[person_1.email])

    with freeze_time("2015-05-03"):  # two days befor due date
      _, notif_data = common.get_daily_notifications()
      person_1 = Person.query.get(self.person_1.id)
      self.assertIn(person_1.email, notif_data)

  def create_test_cases(self):
    def person_dict(person_id):
      return {
          "href": "/api/people/%d" % person_id,
          "id": person_id,
          "type": "Person"
      }

    self.monthly_workflow_1 = {
        "title": "test monthly wf notifications",
        "notify_on_change": True,
        "description": "some test workflow",
        # admin will be current user with id == 1
        "unit": "month",
        "recurrences": True,
        "repeat_every": 1,
        "task_groups": [{
            "title": "one time task group",
            "contact": person_dict(self.person_1.id),
            "task_group_tasks": [{
                "title": "task 1",
                "description": "some task",
                "contact": person_dict(self.person_1.id),
                "start_date": dt.date(2015, 4, 5),
                "end_date": dt.date(2015, 4, 25),
            }, {
                "title": "task 2",
                "description": "some task",
                "contact": person_dict(self.person_1.id),
                "start_date": dt.date(2015, 4, 10),
                "end_date": dt.date(2015, 4, 21),
            }],
            "task_group_objects": self.random_objects[:2]
        }, {
            "title": "another one time task group",
            "contact": person_dict(self.person_1.id),
            "task_group_tasks": [{
                "title": "task 1 in tg 2",
                "description": "some task",
                "contact": person_dict(self.person_1.id),
                "start_date": dt.date(2015, 4, 15),
                "end_date": dt.date(2015, 4, 15),
            }, {
                "title": "task 2 in tg 2",
                "description": "some task",
                "contact": person_dict(self.person_2.id),
                "start_date": dt.date(2015, 4, 15),
                "end_date": dt.date(2015, 4, 28),
            }],
            "task_group_objects": []
        }]
    }