コード例 #1
0
ファイル: test_archived.py プロジェクト: egorhm/ggrc-core
class TestArchived(WithQueryApi, TestCase):
  """Tests for filtering by Archived field."""
  def setUp(self):
    super(TestArchived, self).setUp()
    self.client.get("/login")
    self.api = Api()

  @ddt.data([1, 3])
  def test_archived_audits(self, archived_audits):
    """Test filtration by Archived Audits."""
    with factories.single_commit():
      audit_ids = [factories.AuditFactory().id for _ in range(5)]

    expected_ids = []
    for i in archived_audits:
      audit = all_models.Audit.query.get(audit_ids[i])
      response = self.api.put(audit, {"archived": True})
      self.assert200(response)
      expected_ids.append(audit_ids[i])

    ids = self.simple_query(
        "Audit",
        expression=["archived", "=", "true"],
        type_="ids",
        field="ids"
    )
    self.assertItemsEqual(ids, expected_ids)

  @ddt.data([2, 4])
  def test_archived_assessments(self, archived_audits):
    """Test filtration by Archived Assessments."""
    # Create 5 Audits, each of them has 3 Assessment
    with factories.single_commit():
      audit_ids = []
      for _ in range(5):
        audit = factories.AuditFactory()
        audit_ids.append(audit.id)
        for _ in range(3):
          factories.AssessmentFactory(audit=audit)

    # This list contain ids of assessments from audits in archived_audits
    expected_ids = []
    for i in archived_audits:
      audit = all_models.Audit.query.get(audit_ids[i])
      expected_ids += [a.id for a in audit.assessments]
      response = self.api.put(audit, {"archived": True})
      self.assert200(response)

    ids = self.simple_query(
        "Assessment",
        expression=["archived", "=", "true"],
        type_="ids",
        field="ids"
    )
    self.assertItemsEqual(ids, expected_ids)
コード例 #2
0
class TestPersonProfilePermissions(TestCase):
  """Test PersonProfile."""

  def setUp(self):
    super(TestPersonProfilePermissions, self).setUp()
    self.generator = Generator()
    self.api = Api()
    self.object_generator = ObjectGenerator()
    self.init_users()

  def init_users(self):
    """Init user with different roles."""
    users = [("reader", "Reader"),
             ("editor", "Editor"),
             ("admin", "Administrator"),
             ("creator", "Creator")]
    self.users = {}
    for (name, role) in users:
      _, user = self.object_generator.generate_person(
          data={"name": name},
          user_role=role
      )
      self.users[name] = user

  @ddt.data("reader", "editor", "admin", "creator")
  def test_permissions(self, name):
    """Test permissions for user roles."""
    user = all_models.Person.query.get(self.users[name].id)
    profile = all_models.PersonProfile.query.join(
        all_models.PersonProfile.person
    ).filter_by(
        email=user.email
    ).one()
    self.api.set_user(self.users[name])
    response = self.api.get(all_models.PersonProfile, profile.id)
    self.assert200(response)

    new_date = "2018-05-20 22:05:17"
    response = self.api.put(profile, {
        "people_profiles": {
            "id": profile.id,
            "last_seen_whats_new": new_date,
        },
    })
    self.assert200(response)

    response = self.api.delete(profile)
    if name == "admin":
      self.assert200(response)
    else:
      self.assert403(response)
コード例 #3
0
ファイル: test_proposal_acl.py プロジェクト: egorhm/ggrc-core
class TestACLPopulation(TestCase):
  """Test population permissions for controlproposals."""

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

  @ddt.data(
      {"read": 1, "update": 0},
      {"read": 0, "update": 1},
      {"read": 1, "update": 1},
      {"read": 0, "update": 0},
  )
  @ddt.unpack
  def test_proposal_acl_poulation(self, read, update):
    """Test proposal perms with read {read} and update {update}."""
    with factories.single_commit():
      control = factories.ControlFactory(title="1")
      role = factories.AccessControlRoleFactory(object_type=control.type,
                                                name="role",
                                                read=read,
                                                update=update)
      proposal = factories.ProposalFactory(instance=control,
                                           content={"field": "a"},
                                           agenda="agenda content")
      person = factories.PersonFactory()
    self.assertFalse(proposal.access_control_list)
    role_id = role.id
    control_id = control.id
    proposal_id = proposal.id
    control_content = control.log_json()
    control_content["access_control_list"] = [
        {"ac_role_id": role_id,
            "person": {"type": "Person", "id": person.id}}
    ]
    resp = self.api.put(control, {"control": control_content})
    self.assertEqual(200, resp.status_code)
    control = all_models.Control.query.get(control_id)
    proposal = all_models.Proposal.query.get(proposal_id)
    full_proposal_acls = all_models.AccessControlList.query.filter(
        all_models.AccessControlList.object_type == proposal.type,
        all_models.AccessControlList.object_id == proposal.id,
    ).all()
    self.assertEqual(int(read or update),
                     len(full_proposal_acls))
コード例 #4
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)
コード例 #5
0
class TestLastAssessmentDate(TestCase):
  """Integration test suite for WithLastAssessmentDate functionality.

  Setup stage for these tests consists of:
    - 5 controls (Control_0, Control_1, Control_2, Control_3, Control_4)
    - 2 Objectives (Objective_0, Objective_1)
    - 2 audits
      - Audit_0 with snapshots of
        - Control_0, Control_1, Objective_0
      - Audit_1 with snapshots of
        -Control_1, Control_2, Control_3, Objective_0, Objective_1
    - 2 Assessments
      - Assessment_0 on Audit_0 with snapshot mappings for
        - Control_1, Objective_0
      - Assessment_1 on Audit_1 with snapshot mappings for
        - Control_1, Control_2, Objective_0, Objective_1
  """

  def setUp(self):
    super(TestLastAssessmentDate, self).setUp()
    self.api = Api()
    self.generator = generator.ObjectGenerator()
    self.client.get("/login")
    person = models.Person.query.first()
    admin_control = models.AccessControlRole.query.filter_by(
        name="Admin", object_type="Control"
    ).first()
    admin_objective = models.AccessControlRole.query.filter_by(
        name="Admin", object_type="Objective"
    ).first()
    with factories.single_commit():
      controls = [
          factories.ControlFactory(slug="Control_{}".format(i),
                                   title="Control_{}".format(i))
          for i in range(5)
      ]
      objectives = [
          factories.ObjectiveFactory(slug="Objective_{}".format(i),
                                     title="Objective_{}".format(i))
          for i in range(2)
      ]

      for obj in itertools.chain(controls, objectives):
        acr = admin_control if obj.type == "Control" else admin_objective
        factories.AccessControlList(
            object=obj, person=person, ac_role=acr
        )

      audit_0 = factories.AuditFactory(title="Audit_0", contact=person)
      audit_1 = factories.AuditFactory(title="Audit_1", contact=person)
      audit_0_snapshots = self._create_snapshots(
          audit_0, controls[:2] + objectives[:1])
      audit_1_snapshots = self._create_snapshots(
          audit_1, controls[1:4] + objectives)
      assessment_0 = factories.AssessmentFactory(
          title="Assessment_0", audit=audit_0)
      assessment_1 = factories.AssessmentFactory(
          title="Assessment_1", audit=audit_1)
      factories.RelationshipFactory(source=assessment_0, destination=audit_0)
      factories.RelationshipFactory(source=audit_1, destination=assessment_1)

      # Audit 0 assessment mappings:
      factories.RelationshipFactory(
          source=assessment_0,
          destination=audit_0_snapshots[1],  # snapshot of control_1
      )
      factories.RelationshipFactory(
          source=assessment_0,
          destination=audit_0_snapshots[2],  # snapshot of objective_0
      )
      # Audit 1 assessment mappings:
      factories.RelationshipFactory(
          source=audit_1_snapshots[0],  # snapshot of control_1
          destination=assessment_1,
      )
      factories.RelationshipFactory(
          source=assessment_1,
          destination=audit_1_snapshots[1],  # snapshot of control_2
      )
      factories.RelationshipFactory(
          source=assessment_1,
          destination=audit_1_snapshots[3],  # snapshot of objective_0
      )
      factories.RelationshipFactory(
          source=audit_1_snapshots[4],  # snapshot of objective_1
          destination=assessment_1,
      )

  def test_no_finished_assessment(self):
    """Test empty last assessment dates."""
    controls = models.Control.query.all()
    for control in controls:
      self.assertEqual(control.last_assessment_date, None)

    snapshots = models.Snapshot.query.all()
    for snapshot in snapshots:
      self.assertEqual(snapshot.last_assessment_date, None)

  def test_single_finished_assessment(self):
    finish_date = datetime.datetime(2017, 2, 20, 13, 40, 0)
    related_objects = {"Control_1", "Objective_0"}

    with freezegun.freeze_time(finish_date):
      asmt = models.Assessment.query.filter_by(title="Assessment_0").first()
      self.api.put(asmt, {"status": "Completed"})

    for control in models.Control.query.all():
      if control.title in related_objects:
        self.assertEqual(control.last_assessment_date, finish_date)
      else:
        self.assertEqual(control.last_assessment_date, None)

    for snapshot in models.Snapshot.query.all():
      if snapshot.revision.content["title"] in related_objects:
        self.assertEqual(snapshot.last_assessment_date, finish_date)
      else:
        self.assertEqual(snapshot.last_assessment_date, None)

  def test_snapshot_lad_on_new_audits(self):
    """Test snapshot last assessment date for new audits."""

    finish_date = datetime.datetime(2017, 2, 20, 13, 40, 0)
    related_objects = {"Control_1", "Objective_0"}

    with freezegun.freeze_time(finish_date):
      asmt = models.Assessment.query.filter_by(title="Assessment_0").first()
      self.api.put(asmt, {"status": "Completed"})

      program = models.Program.query.first()
      for control in models.Control.query.all():
        factories.RelationshipFactory(source=program, destination=control)

      self.generator.generate_object(models.Audit, data={
          "title": "dummy",
          "program": self.generator.create_stub(program),
          "context": self.generator.create_stub(program.context),
      })

    audit = models.Audit.query.filter_by(title="dummy").first()
    snapshots = models.Snapshot.query.filter(
        models.Snapshot.parent_id == audit.id
    ).all()
    self.assertEqual(len(snapshots), 5)

    for snapshot in snapshots:
      if snapshot.revision.content["title"] in related_objects:
        self.assertEqual(snapshot.last_assessment_date, finish_date)
      else:
        self.assertEqual(snapshot.last_assessment_date, None)

  def test_export_lad_instance(self):
    """Check export Last Assessment Date."""
    finish_date = datetime.datetime(2017, 2, 20, 13, 40, 0)
    finish_date_str = finish_date.strftime("%m/%d/%Y")
    related_objects = {"Control_1", "Objective_0"}

    with freezegun.freeze_time(finish_date):
      asmt = models.Assessment.query.filter_by(title="Assessment_0").first()
      self.api.put(asmt, {"status": "Completed"})

    search_request = [{
        "object_name": "Control",
        "filters": {"expression": {}},
        "fields": ["title", "last_assessment_date"]
    }]
    query = self.export_parsed_csv(search_request)["Control"]

    for control in query:
      if control['Title*'] in related_objects:
        f_date = finish_date_str
      else:
        f_date = ""
      self.assertEqual(control["Last Assessment Date"], f_date)

  def test_import_lad_field(self):
    """Test Last Assessment Date field read only on import."""
    finish_date = datetime.datetime(2017, 2, 20, 13, 40, 0)
    with freezegun.freeze_time(finish_date):
      asmt = models.Assessment.query.filter_by(title="Assessment_0").first()
      self.api.put(asmt, {"status": "Completed"})
    resp = self.import_data(collections.OrderedDict([
        ("object_type", "Control"),
        ("code", "Control_1"),
        ("Last Assessment Date", "06/06/2017"),
    ]))
    self._check_csv_response(resp, {
        "Control": {
            "row_warnings": {
                errors.UNMODIFIABLE_COLUMN.format(
                    line=3, column_name="Last Assessment Date"
                )
            }
        }
    })

    control = models.Control.query.filter(
        models.Control.slug == "Control_1"
    ).one()
    self.assertEqual(1, len(resp))
    self.assertEqual(1, resp[0]["updated"])
    self.assertEqual(control.last_assessment_date, finish_date)
コード例 #6
0
class TestLastAssessmentDate(TestCase):
    """Integration test suite for WithLastAssessmentDate functionality.

  Setup stage for these tests consists of:
    - 5 controls (Control_0, Control_1, Control_2, Control_3, Control_4)
    - 2 Objectives (Objective_0, Objective_1)
    - 2 audits
      - Audit_0 with snapshots of
        - Control_0, Control_1, Objective_0
      - Audit_1 with snapshots of
        -Control_1, Control_2, Control_3, Objective_0, Objective_1
    - 2 Assessments
      - Assessment_0 on Audit_0 with snapshot mappings for
        - Control_1, Objective_0
      - Assessment_1 on Audit_1 with snapshot mappings for
        - Control_1, Control_2, Objective_0, Objective_1
  """
    def setUp(self):
        super(TestLastAssessmentDate, self).setUp()
        self.api = Api()
        self.generator = generator.ObjectGenerator()
        self.client.get("/login")
        person = models.Person.query.first()
        admin_control = models.AccessControlRole.query.filter_by(
            name="Admin", object_type="Control").first()
        admin_objective = models.AccessControlRole.query.filter_by(
            name="Admin", object_type="Objective").first()
        with factories.single_commit():
            controls = [
                factories.ControlFactory(slug="Control_{}".format(i),
                                         title="Control_{}".format(i))
                for i in range(5)
            ]
            objectives = [
                factories.ObjectiveFactory(slug="Objective_{}".format(i),
                                           title="Objective_{}".format(i))
                for i in range(2)
            ]

            for obj in itertools.chain(controls, objectives):
                acr = admin_control if obj.type == "Control" else admin_objective
                factories.AccessControlList(object=obj,
                                            person=person,
                                            ac_role=acr)

            audit_0 = factories.AuditFactory(title="Audit_0")
            audit_1 = factories.AuditFactory(title="Audit_1")
            audit_0_snapshots = self._create_snapshots(
                audit_0, controls[:2] + objectives[:1])
            audit_1_snapshots = self._create_snapshots(
                audit_1, controls[1:4] + objectives)
            assessment_0 = factories.AssessmentFactory(title="Assessment_0",
                                                       audit=audit_0)
            assessment_1 = factories.AssessmentFactory(title="Assessment_1",
                                                       audit=audit_1)
            factories.RelationshipFactory(source=assessment_0,
                                          destination=audit_0)
            factories.RelationshipFactory(source=audit_1,
                                          destination=assessment_1)

            # Audit 0 assessment mappings:
            factories.RelationshipFactory(
                source=assessment_0,
                destination=audit_0_snapshots[1],  # snapshot of control_1
            )
            factories.RelationshipFactory(
                source=assessment_0,
                destination=audit_0_snapshots[2],  # snapshot of objective_0
            )
            # Audit 1 assessment mappings:
            factories.RelationshipFactory(
                source=audit_1_snapshots[0],  # snapshot of control_1
                destination=assessment_1,
            )
            factories.RelationshipFactory(
                source=assessment_1,
                destination=audit_1_snapshots[1],  # snapshot of control_2
            )
            factories.RelationshipFactory(
                source=assessment_1,
                destination=audit_1_snapshots[3],  # snapshot of objective_0
            )
            factories.RelationshipFactory(
                source=audit_1_snapshots[4],  # snapshot of objective_1
                destination=assessment_1,
            )

    def test_no_finished_assessment(self):
        """Test empty last assessment dates."""
        controls = models.Control.query.all()
        for control in controls:
            self.assertEqual(control.last_assessment_date, None)

        snapshots = models.Snapshot.query.all()
        for snapshot in snapshots:
            self.assertEqual(snapshot.last_assessment_date, None)

    def test_single_finished_assessment(self):
        finish_date = datetime.datetime(2017, 2, 20, 13, 40, 0)
        related_objects = {"Control_1", "Objective_0"}

        with freezegun.freeze_time(finish_date):
            asmt = models.Assessment.query.filter_by(
                title="Assessment_0").first()
            self.api.put(asmt, {"status": "Completed"})

        for control in models.Control.query.all():
            if control.title in related_objects:
                self.assertEqual(control.last_assessment_date, finish_date)
            else:
                self.assertEqual(control.last_assessment_date, None)

        for snapshot in models.Snapshot.query.all():
            if snapshot.revision.content["title"] in related_objects:
                self.assertEqual(snapshot.last_assessment_date, finish_date)
            else:
                self.assertEqual(snapshot.last_assessment_date, None)

    def test_snapshot_lad_on_new_audits(self):
        """Test snapshot last assessment date for new audits."""

        finish_date = datetime.datetime(2017, 2, 20, 13, 40, 0)
        related_objects = {"Control_1", "Objective_0"}

        with freezegun.freeze_time(finish_date):
            asmt = models.Assessment.query.filter_by(
                title="Assessment_0").first()
            self.api.put(asmt, {"status": "Completed"})

            program = models.Program.query.first()
            for control in models.Control.query.all():
                factories.RelationshipFactory(source=program,
                                              destination=control)

            self.generator.generate_object(
                models.Audit,
                data={
                    "title": "dummy",
                    "program": self.generator.create_stub(program),
                    "context": self.generator.create_stub(program.context),
                })

        audit = models.Audit.query.filter_by(title="dummy").first()
        snapshots = models.Snapshot.query.filter(
            models.Snapshot.parent_id == audit.id).all()
        self.assertEqual(len(snapshots), 5)

        for snapshot in snapshots:
            if snapshot.revision.content["title"] in related_objects:
                self.assertEqual(snapshot.last_assessment_date, finish_date)
            else:
                self.assertEqual(snapshot.last_assessment_date, None)

    def test_export_lad_instance(self):
        """Check export Last Assessment Date."""
        finish_date = datetime.datetime(2017, 2, 20, 13, 40, 0)
        finish_date_str = finish_date.strftime("%m/%d/%Y")
        related_objects = {"Control_1", "Objective_0"}

        with freezegun.freeze_time(finish_date):
            asmt = models.Assessment.query.filter_by(
                title="Assessment_0").first()
            self.api.put(asmt, {"status": "Completed"})

        search_request = [{
            "object_name": "Control",
            "filters": {
                "expression": {}
            },
            "fields": ["title", "last_assessment_date"]
        }]
        query = self.export_parsed_csv(search_request)["Control"]

        for control in query:
            if control['Title*'] in related_objects:
                f_date = finish_date_str
            else:
                f_date = ""
            self.assertEqual(control["Last Assessment Date"], f_date)

    def test_import_lad_field(self):
        """Test Last Assessment Date field read only on import."""
        finish_date = datetime.datetime(2017, 2, 20, 13, 40, 0)
        with freezegun.freeze_time(finish_date):
            asmt = models.Assessment.query.filter_by(
                title="Assessment_0").first()
            self.api.put(asmt, {"status": "Completed"})
        resp = self.import_data(
            collections.OrderedDict([
                ("object_type", "Control"),
                ("code", "Control_1"),
                ("Last Assessment Date", "06/06/2017"),
            ]))
        self._check_csv_response(
            resp, {
                "Control": {
                    "row_warnings": {
                        errors.EXPORT_ONLY_WARNING.format(
                            line=3, column_name="Last Assessment Date")
                    }
                }
            })

        control = models.Control.query.filter(
            models.Control.slug == "Control_1").one()
        self.assertEqual(1, len(resp))
        self.assertEqual(1, resp[0]["updated"])
        self.assertEqual(control.last_assessment_date, finish_date)
コード例 #7
0
class TestDisabledIssueIntegration(ggrc.TestCase):
  """Tests for IssueTracker integration functionality with disabled sync."""

  # pylint: disable=invalid-name

  def setUp(self):
    # pylint: disable=super-on-old-class
    super(TestDisabledIssueIntegration, self).setUp()
    self.api = Api()
    self.client.get("/login")

  @mock.patch("ggrc.integrations.issues.Client.create_issue")
  def test_issue_creation(self, mock_create_issue):
    """Test creating Issue object with disabled integration."""
    with mock.patch.object(settings, "ISSUE_TRACKER_ENABLED", True):
      response = self.api.post(all_models.Issue, {
          "issue": {
              "title": "test title",
              "context": None,
              "issue_tracker": {
                  "enabled": False,
              }
          },
      })
    mock_create_issue.assert_not_called()
    self.assertEqual(response.status_code, 201)
    issue_id = response.json.get("issue").get("id")
    issue_tracker_issue = models.IssuetrackerIssue.get_issue("Issue",
                                                             issue_id)
    self.assertIsNone(issue_tracker_issue)

  @mock.patch("ggrc.integrations.issues.Client.update_issue")
  def test_issue_deletion(self, mock_update_issue):
    """Test deleting Issue object with disabled integration for issue."""
    iti = factories.IssueTrackerIssueFactory(
        enabled=False,
        issue_tracked_obj=factories.IssueFactory()
    )
    with mock.patch.object(settings, "ISSUE_TRACKER_ENABLED", True):
      self.api.delete(iti.issue_tracked_obj)
    mock_update_issue.assert_not_called()

  @ddt.data(
      {"description": "new description", "issue_tracker": {"enabled": False}},
      {"test_plan": "new test plan", "issue_tracker": {"enabled": False}},
      {"issue_tracker": {"component_id": "123", "enabled": False}},
      {"issue_tracker": {"hotlist_id": "321", "enabled": False}},
      {"issue_tracker": {"issue_priority": "P2", "enabled": False}},
      {"issue_tracker": {"issue_severity": "S2", "enabled": False}},
  )
  @mock.patch("ggrc.integrations.issues.Client.update_issue")
  def test_update_issue_object(self, issue_attrs, mock_update_issue):
    """Test updating issue object with disabled integration for issue."""
    iti = factories.IssueTrackerIssueFactory(
        enabled=False,
        issue_tracked_obj=factories.IssueFactory()
    )
    with mock.patch.object(settings, "ISSUE_TRACKER_ENABLED", True):
      self.api.put(iti.issue_tracked_obj, issue_attrs)
    mock_update_issue.assert_not_called()

  @mock.patch("ggrc.integrations.issues.Client.create_issue",
              side_effect=[integrations_errors.Error, {"issueId": "issueId"}])
  @mock.patch.object(settings, "ISSUE_TRACKER_ENABLED", True)
  def test_issue_recreation(self, mock_create_issue):
    """Test retrying to turn on integration after failed creation."""
    # Arrange data.
    component_id = "1234"
    hotlist_id = "4321"
    issue_type = "Default Issue type"
    issue_priority = "P2"
    issue_severity = "S1"
    title = "test title"
    issue_tracker_attrs = {
        "enabled": True,
        "component_id": int(component_id),
        "hotlist_id": int(hotlist_id),
        "issue_type": issue_type,
        "issue_priority": issue_priority,
        "issue_severity": issue_severity,
    }

    # Perform actions and assert results.
    with mock.patch.object(integration_utils, "exclude_auditor_emails",
                           return_value={u"*****@*****.**", }):

      # Try to create issue. create_issue should raise exception here.
      response = self.api.post(all_models.Issue, {
          "issue": {
              "title": title,
              "context": None,
              "issue_tracker": issue_tracker_attrs
          },
      })

      issue_id = response.json.get("issue").get("id")
      issue_tracker_issue = models.IssuetrackerIssue.get_issue("Issue",
                                                               issue_id)
      self.assertIsNone(issue_tracker_issue.issue_id)
      self.assertIsNone(issue_tracker_issue.issue_url)

      # Try to turn on integration on already created issue.
      self.api.put(
          issue_tracker_issue.issue_tracked_obj,
          {"issue_tracker": issue_tracker_attrs}
      )

      issue_id = issue_tracker_issue.issue_tracked_obj.id
      issue_tracker_issue = models.IssuetrackerIssue.get_issue("Issue",
                                                               issue_id)
      self.assertEqual(issue_tracker_issue.issue_url, "http://issue/issueId")

  @mock.patch("ggrc.integrations.issues.Client.update_issue")
  def test_adding_comment_to_issue(self, update_issue_mock):
    """Test not adding comment to issue when issue tracker disabled."""
    iti = factories.IssueTrackerIssueFactory(
        enabled=False,
        issue_tracked_obj=factories.IssueFactory()
    )
    comment = factories.CommentFactory(description="test comment")

    with mock.patch.object(settings, "ISSUE_TRACKER_ENABLED", True):
      self.api.post(all_models.Relationship, {
          "relationship": {
              "source": {"id": iti.issue_tracked_obj.id, "type": "Issue"},
              "destination": {"id": comment.id, "type": "comment"},
              "context": None
          },
      })
    update_issue_mock.assert_not_called()
コード例 #8
0
ファイル: test_api_calls.py プロジェクト: Smotko/ggrc-core
class TestWorkflowsApiPost(TestCase):

  def setUp(self):
    super(TestWorkflowsApiPost, self).setUp()
    self.api = Api()
    self.generator = wf_generator.WorkflowsGenerator()

  def tearDown(self):
    pass

  def test_send_invalid_data(self):
    data = self.get_workflow_dict()
    del data["workflow"]["title"]
    del data["workflow"]["context"]
    response = self.api.post(all_models.Workflow, data)
    self.assert400(response)
    # TODO: check why response.json["message"] is empty

  def test_create_one_time_workflows(self):
    data = self.get_workflow_dict()
    response = self.api.post(all_models.Workflow, data)
    self.assertEqual(response.status_code, 201)

  def test_create_weekly_workflow(self):
    """Test create valid weekly wf"""
    data = self.get_workflow_dict()
    data["workflow"]["repeat_every"] = 7
    data["workflow"]["unit"] = "day"
    data["workflow"]["title"] = "Weekly"
    response = self.api.post(all_models.Workflow, data)
    self.assertEqual(response.status_code, 201)

  def test_create_annually_workflow(self):
    """Test create valid annual wf"""
    data = self.get_workflow_dict()
    data["workflow"]["repeat_every"] = 12
    data["workflow"]["unit"] = "month"
    data["workflow"]["title"] = "Annually"
    response = self.api.post(all_models.Workflow, data)
    self.assertEqual(response.status_code, 201)

  @ddt.data("wrong value", 0, -4)
  def test_create_wrong_repeat_every_workflow(self, value):  # noqa pylint: disable=invalid-name
    """Test case for invalid repeat_every value"""
    data = self.get_workflow_dict()
    data["workflow"]["repeat_every"] = value
    data["workflow"]["unit"] = "month"
    data["workflow"]["title"] = "Wrong wf"
    response = self.api.post(all_models.Workflow, data)
    self.assertEqual(response.status_code, 400)

  def test_create_wrong_unit_workflow(self):
    """Test case for invalid unit value"""
    data = self.get_workflow_dict()
    data["workflow"]["repeat_every"] = 12
    data["workflow"]["unit"] = "wrong value"
    data["workflow"]["title"] = "Wrong wf"
    response = self.api.post(all_models.Workflow, data)
    self.assertEqual(response.status_code, 400)

  def test_create_task_group(self):
    wf_data = self.get_workflow_dict()
    wf_data["workflow"]["title"] = "Create_task_group"
    wf_response = self.api.post(all_models.Workflow, wf_data)

    data = self.get_task_group_dict(wf_response.json["workflow"])

    response = self.api.post(all_models.TaskGroup, data)
    self.assertEqual(response.status_code, 201)

  # TODO: Api should be able to handle invalid data
  @unittest.skip("Not implemented.")
  def test_create_task_group_invalid_workflow_data(self):  # noqa pylint: disable=invalid-name
    data = self.get_task_group_dict({"id": -1, "context": {"id": -1}})
    response = self.api.post(all_models.TaskGroup, data)
    self.assert400(response)

  @staticmethod
  def get_workflow_dict():
    data = {
        "workflow": {
            "custom_attribute_definitions": [],
            "custom_attributes": {},
            "title": "One_time",
            "description": "",
            "unit": None,
            "repeat_every": None,
            "notify_on_change": False,
            "task_group_title": "Task Group 1",
            "notify_custom_message": "",
            "is_verification_needed": True,
            "owners": None,
            "context": None,
        }
    }
    return data

  @staticmethod
  def get_task_group_dict(workflow):
    data = {
        "task_group": {
            "custom_attribute_definitions": [],
            "custom_attributes": {},
            "_transient": {},
            "contact": {
                "id": 1,
                "href": "/api/people/1",
                "type": "Person"
            },
            "workflow": {
                "id": workflow["id"],
                "href": "/api/workflows/%d" % workflow["id"],
                "type": "Workflow"
            },
            "context": {
                "id": workflow["context"]["id"],
                "href": "/api/contexts/%d" % workflow["context"]["id"],
                "type": "Context"
            },
            "modal_title": "Create Task Group",
            "title": "Create_task_group",
            "description": "",
        }
    }
    return data

  @ddt.data({},
            {"repeat_every": 5,
             "unit": "month"})
  def test_repeat_multiplier_field(self, data):
    """Check repeat_multiplier is set to 0 after wf creation.
    """
    with factories.single_commit():
      workflow = wf_factories.WorkflowFactory(**data)
    workflow_id = workflow.id
    self.assertEqual(
        0,
        all_models.Workflow.query.get(workflow_id).repeat_multiplier
    )

  # TODO: Unskip in the patch 2
  @unittest.skip("Will be activated in patch 2")
  def test_change_to_one_time_wf(self):
    """Check repeat_every and unit can be set to Null only together."""
    with factories.single_commit():
      workflow = wf_factories.WorkflowFactory(repeat_every=12,
                                              unit="day")
    resp = self.api.put(workflow, {"repeat_every": None,
                                   "unit": None})
    self.assert200(resp)

  @ddt.data({"repeat_every": 5},
            {"unit": "month"})
  def test_change_repeat_every(self, data):
    """Check repeat_every or unit can not be changed once set."""
    with factories.single_commit():
      workflow = wf_factories.WorkflowFactory()
    resp = self.api.put(workflow, data)
    self.assert400(resp)

  def test_not_change_to_one_time_wf(self):
    """Check repeat_every or unit can't be set to Null separately.
    This test will be useful in the 2nd patch, where we allow to change
    WF setup
    """
    with factories.single_commit():
      workflow = wf_factories.WorkflowFactory(repeat_every=12,
                                              unit="day")
    resp = self.api.put(workflow, {"repeat_every": None})
    self.assert400(resp)
    resp = self.api.put(workflow, {"unit": None})
    self.assert400(resp)

  @ddt.data(True, False)
  def test_autogen_verification_flag(self, flag):
    """Check is_verification_needed flag for activate WF action."""
    with factories.single_commit():
      workflow = wf_factories.WorkflowFactory(is_verification_needed=flag)
      group = wf_factories.TaskGroupFactory(workflow=workflow)
      wf_factories.TaskGroupTaskFactory(task_group=group)
    data = [{
        "cycle": {
            "autogenerate": True,
            "isOverdue": False,
            "workflow": {
                "id": workflow.id,
                "type": "Workflow",
            },
            "context": {
                "id": workflow.context_id,
                "type": "Context",
            },
        }
    }]
    resp = self.api.send_request(
        self.api.client.post,
        api_link="/api/cycles",
        data=data)
    cycle_id = resp.json[0][1]["cycle"]["id"]
    self.assertEqual(
        flag,
        all_models.Cycle.query.get(cycle_id).is_verification_needed)

  @ddt.data(True, False)
  def test_change_verification_flag_positive(self, flag):  # noqa pylint: disable=invalid-name
    """is_verification_needed flag is changeable for DRAFT workflow."""
    with factories.single_commit():
      workflow = wf_factories.WorkflowFactory(is_verification_needed=flag)
    self.assertEqual(workflow.status, all_models.Workflow.DRAFT)
    workflow_id = workflow.id
    resp = self.api.put(workflow, {"is_verification_needed": not flag})
    self.assert200(resp)
    self.assertEqual(
        all_models.Workflow.query.get(workflow_id).is_verification_needed,
        not flag)

  @ddt.data(True, False)
  def test_change_verification_flag_negative(self, flag):  # noqa pylint: disable=invalid-name
    with freezegun.freeze_time("2017-08-10"):
      with factories.single_commit():
        workflow = wf_factories.WorkflowFactory(
            unit=all_models.Workflow.WEEK_UNIT,
            is_verification_needed=flag,
            repeat_every=1)
        wf_factories.TaskGroupTaskFactory(
            task_group=wf_factories.TaskGroupFactory(
                context=factories.ContextFactory(),
                workflow=workflow
            ),
            # Two cycles should be created
            start_date=datetime.date(2017, 8, 3),
            end_date=datetime.date(2017, 8, 7))
      workflow_id = workflow.id
      self.assertEqual(workflow.status, all_models.Workflow.DRAFT)
      self.generator.activate_workflow(workflow)
      workflow = all_models.Workflow.query.get(workflow_id)
      self.assertEqual(workflow.status, all_models.Workflow.ACTIVE)
      resp = self.api.put(workflow, {"is_verification_needed": not flag})
      self.assert400(resp)
      workflow = all_models.Workflow.query.get(workflow_id)
      self.assertEqual(workflow.is_verification_needed, flag)

      # End all current cycles
      for cycle in workflow.cycles:
        self.generator.modify_object(cycle, {'is_current': False})
      workflow = all_models.Workflow.query.filter(
          all_models.Workflow.id == workflow_id).first()
      self.assertEqual(workflow.status, all_models.Workflow.INACTIVE)
      resp = self.api.put(workflow, {"is_verification_needed": not flag})
      self.assert400(resp)
      workflow = all_models.Workflow.query.get(workflow_id)
      self.assertEqual(workflow.is_verification_needed, flag)

  @ddt.data(True, False)
  def test_not_change_vf_flag(self, flag):
    """Check is_verification_needed not change on update."""
    with factories.single_commit():
      workflow = wf_factories.WorkflowFactory(is_verification_needed=flag)
    workflow_id = workflow.id
    resp = self.api.put(workflow, {"is_verification_needed": flag})
    self.assert200(resp)
    self.assertEqual(
        flag,
        all_models.Workflow.query.get(workflow_id).is_verification_needed)

  @ddt.data(True, False, None)
  def test_create_vf_flag(self, flag):
    """Check is_verification_needed flag setup on create."""
    data = self.get_workflow_dict()
    if flag is None:
      data['workflow'].pop('is_verification_needed', None)
    else:
      data['workflow']['is_verification_needed'] = flag
    resp = self.api.post(all_models.Workflow, data)
    self.assertEqual(201, resp.status_code)
    workflow_id = resp.json['workflow']['id']
    self.assertEqual(
        flag if flag is not None else True,
        all_models.Workflow.query.get(workflow_id).is_verification_needed)
コード例 #9
0
class TestPermissions(TestCase):
    """Test checks permissions for proposals."""
    def setUp(self):
        super(TestPermissions, self).setUp()
        self.api = Api()
        roles = {r.name: r for r in all_models.Role.query.all()}
        ac_roles = {
            r.name: r
            for r in all_models.AccessControlRole.query.all()
        }
        with factories.single_commit():
            self.control = factories.ControlFactory()
            acrs = {
                "ACL_Reader":
                factories.AccessControlRoleFactory(name="ACL_Reader",
                                                   object_type="Control",
                                                   update=0),
                "ACL_Editor":
                factories.AccessControlRoleFactory(name="ACL_Editor",
                                                   object_type="Control"),
                "ACL_Nobody":
                factories.AccessControlRoleFactory(
                    name="ACL_Nobody",
                    object_type="Control",
                    read=0,
                    update=0,
                    delete=0,
                ),
            }
            self.program = factories.ProgramFactory()
            self.program.context.related_object = self.program
            self.relationship = factories.RelationshipFactory(
                source=self.program,
                destination=self.control,
                context=self.program.context,
            )
            self.people = {
                "Creator": factories.PersonFactory(),
                "Reader": factories.PersonFactory(),
                "Editor": factories.PersonFactory(),
                "Administrator": factories.PersonFactory(),
                "ACL_Reader": factories.PersonFactory(),
                "ACL_Editor": factories.PersonFactory(),
                "ACL_Nobody": factories.PersonFactory(),
                "Program Editors": factories.PersonFactory(),
                "Program Managers": factories.PersonFactory(),
                "Program Readers": factories.PersonFactory(),
            }
            for role_name in ["Creator", "Reader", "Editor", "Administrator"]:
                rbac_factories.UserRoleFactory(role=roles[role_name],
                                               person=self.people[role_name])
            for role_name in [
                    "Program Editors", "Program Managers", "Program Readers"
            ]:
                person = self.people[role_name]
                rbac_factories.UserRoleFactory(role=roles["Creator"],
                                               person=person)
                factories.AccessControlListFactory(ac_role=ac_roles[role_name],
                                                   object=self.program,
                                                   person=person)
            self.proposal = factories.ProposalFactory(
                instance=self.control,
                content={
                    "access_control_list": {},
                    "custom_attribute_values": {},
                    "fields": {},
                    "mapping_fields": {},
                    "mapping_list_fields": {},
                })
            for role_name in ["ACL_Reader", "ACL_Editor", "ACL_Nobody"]:
                person = self.people[role_name]
                rbac_factories.UserRoleFactory(role=roles["Creator"],
                                               person=person)
                factories.AccessControlListFactory(ac_role=acrs[role_name],
                                                   object=self.control,
                                                   person=person)
        with factories.single_commit():
            proposal_model.set_acl_to_all_proposals_for(self.control)

    @ddt.data(
        ("Creator", 403),
        ("Reader", 200),
        ("Editor", 200),
        ("ACL_Reader", 200),
        ("ACL_Editor", 200),
        ("ACL_Nobody", 403),
        ("Administrator", 200),
        ("Program Editors", 200),
        ("Program Managers", 200),
        ("Program Readers", 200),
    )
    @ddt.unpack
    def test_permissions_on_get(self, role_name, status):
        """Test get proposals for {0}."""
        proposal_id = self.proposal.id
        self.api.set_user(self.people[role_name])
        self.client.get("/login")
        resp = self.api.get(all_models.Proposal, proposal_id)
        self.assertEqual(status, resp.status_code)

    def api_proposal_status_change(self, proposal_id, status):
        return self.api.put(all_models.Proposal.query.get(proposal_id),
                            {"proposal": {
                                "status": status
                            }})

    @ddt.data(("Creator", 403), ("Reader", 403), ("Editor", 200),
              ("Administrator", 200), ("ACL_Reader", 403), ("ACL_Editor", 200),
              ("ACL_Nobody", 403), ("Program Editors", 200),
              ("Program Managers", 200), ("Program Readers", 403))
    @ddt.unpack
    def test_permissions_on_apply(self, role_name, status):
        """Test apply proposals for {0}."""
        proposal_id = self.proposal.id
        self.api.set_user(self.people[role_name])
        self.client.get("/login")
        resp = self.api_proposal_status_change(
            proposal_id, all_models.Proposal.STATES.APPLIED)
        self.assertEqual(status, resp.status_code)

    @ddt.data(
        ("Creator", 403),
        ("Reader", 403),
        ("Editor", 200),
        ("ACL_Reader", 403),
        ("ACL_Editor", 200),
        ("ACL_Nobody", 403),
        ("Administrator", 200),
        ("Program Editors", 200),
        ("Program Managers", 200),
        ("Program Readers", 403),
    )
    @ddt.unpack
    def test_permissions_on_decline(self, role_name, status):
        """Test decline proposals for {0}."""
        proposal_id = self.proposal.id
        self.api.set_user(self.people[role_name])
        self.client.get("/login")
        resp = self.api_proposal_status_change(
            proposal_id, all_models.Proposal.STATES.DECLINED)
        self.assertEqual(status, resp.status_code)

    @ddt.data(
        ("Creator", 403),
        ("Reader", 201),
        ("Editor", 201),
        ("ACL_Reader", 201),
        ("ACL_Editor", 201),
        ("ACL_Nobody", 403),
        ("Administrator", 201),
        ("Program Editors", 201),
        ("Program Managers", 201),
        ("Program Readers", 201),
    )
    @ddt.unpack
    def test_permissions_on_create(self, role_name, status):
        """Test create proposal for {0}."""
        data = {
            "proposal": {
                "instance": {
                    "id": self.control.id,
                    "type": self.control.type,
                },
                "full_instance_content": {
                    "title": "new_title"
                },
                "agenda": "update cav",
                "context": None,
            }
        }
        self.api.set_user(self.people[role_name])
        self.client.get("/login")
        resp = self.api.post(all_models.Proposal, data)
        self.assertEqual(status, resp.status_code)

    @ddt.data(
        ("Creator", 0),
        ("Reader", 1),
        ("Editor", 1),
        ("ACL_Reader", 1),
        ("ACL_Editor", 1),
        ("ACL_Nobody", 0),
        ("Administrator", 1),
        ("Program Editors", 1),
        ("Program Managers", 1),
        ("Program Readers", 1),
    )
    @ddt.unpack
    def test_query_filter(self, role_name, expected_count):
        """Test query proposals for {0}.

    Args:
        role_name: string, unique key,
                   shows the position of user in generated infrustructure
        expected_count: int, number of proposals,
                        that should be filtered by query
    """
        control_id = self.control.id
        data = [{
            "limit": [0, 5],
            "object_name":
            all_models.Proposal.__name__,
            "order_by": [
                {
                    "name": "status",
                    "desc": True
                },
                {
                    "name": "created_at",
                    "desc": True
                },
            ],
            "filters": {
                "expression": {
                    "left": {
                        "left": "instance_type",
                        "op": {
                            "name": "="
                        },
                        "right": self.control.type,
                    },
                    "op": {
                        "name": "AND"
                    },
                    "right": {
                        "left": "instance_id",
                        "op": {
                            "name": "="
                        },
                        "right": control_id,
                    },
                },
            },
        }]
        self.api.set_user(self.people[role_name])
        self.client.get("/login")
        headers = {
            "Content-Type": "application/json",
        }
        resp = self.api.client.post("/query",
                                    data=json.dumps(data),
                                    headers=headers).json
        self.assertEqual(1, len(resp))
        self.assertEqual(expected_count, resp[0]["Proposal"]["count"])
コード例 #10
0
ファイル: test_document.py プロジェクト: egorhm/ggrc-core
class TestDocument(TestCase):
  """Document test cases"""
  # pylint: disable=invalid-name

  def setUp(self):
    super(TestDocument, self).setUp()
    self.api = Api()
    self.gen = generator.ObjectGenerator()

  def test_get_parent_obj_control_type(self):
    """Test mapping parent of Control type"""
    control = factories.ControlFactory()
    document = factories.DocumentFileFactory(
        parent_obj={
            'id': control.id,
            'type': 'Control'
        })
    expected_control = document.related_objects(_types=[control.type]).pop()
    self.assertEqual(expected_control, control)

  def test_parent_obj_validation_is_id_presents(self):
    """Validation parent_obj id should present."""
    with self.assertRaises(exceptions.ValidationError):
      factories.DocumentFileFactory(
          parent_obj={
              'type': 'Control'
          })

  def test_parent_obj_validation_is_type_presents(self):
    """Validation parent_obj type should present."""
    control = factories.ControlFactory()
    with self.assertRaises(exceptions.ValidationError):
      factories.DocumentFileFactory(
          parent_obj={
              'id': control.id
          })

  def test_parent_obj_validation_wrong_type(self):
    """Validation parent_obj type.

    Type should be in 'Control', 'Issue', 'RiskAssessment'.
    """
    control = factories.ControlFactory()
    with self.assertRaises(exceptions.ValidationError):
      factories.DocumentFileFactory(
          parent_obj={
              'id': control.id,
              'type': 'Program'
          })

  def test_update_title(self):
    """Test update document title."""
    create_title = "test_title"
    update_title = "update_test_title"
    document = factories.DocumentFactory(title=create_title)
    response = self.api.put(document, {"title": update_title})
    self.assert200(response)
    self.assertEqual(all_models.Document.query.get(document.id).title,
                     update_title)

  def create_document_by_type(self, kind):
    """Create document with sent type."""
    data = {
        "title": "test_title",
        "link": "test_link",
    }
    if kind is not None:
      data["kind"] = kind
    kind = kind or all_models.Document.REFERENCE_URL
    resp, doc = self.gen.generate_object(
        all_models.Document,
        data
    )
    self.assertTrue(
        all_models.Document.query.filter(
            all_models.Document.id == resp.json["document"]["id"],
            all_models.Document.kind == kind,
        ).all()
    )
    return (resp, doc)

  def test_create_reference_url(self):
    """Test create reference url."""
    self.create_document_by_type(all_models.Document.REFERENCE_URL)

  def test_create_reference_url_default(self):
    """Test create reference url(default)."""
    self.create_document_by_type(None)

  def test_create_evidence(self):
    """Test create evidence."""
    self.create_document_by_type(all_models.Document.FILE)

  def test_create_invalid_type(self):
    """Test validation kind."""
    data = {
        "kind": 3,
        "title": "test_title",
        "link": "test_link",
        "owners": [self.gen.create_stub(all_models.Person.query.first())],
    }
    obj_name = all_models.Document._inflector.table_singular
    obj = all_models.Document()
    obj_dict = self.gen.obj_to_dict(obj, obj_name)
    obj_dict[obj_name].update(data)
    resp = self.api.post(all_models.Document, obj_dict)
    self.assert400(resp)
    self.assertEqual('"Invalid value for attribute kind. '
                     'Expected options are `FILE`, '
                     '`REFERENCE_URL`"',
                     resp.data)

  def test_header_on_expected_error(self):
    """During authorization flow we have the expected 'Unauthorized'.

    To allow FE ignore the error popup we need to set
    'X-Expected-Error' header
    """
    control = factories.ControlFactory()
    response = self.api.post(all_models.Document, [{
        "document": {
            "kind": all_models.Document.FILE,
            "source_gdrive_id": "some link",
            "link": "some link",
            "title": "some title",
            "context": None,
            "parent_obj": {
                "id": control.id,
                "type": "Control"
            }
        }
    }])
    self.assertEqual(response.status_code, 401)
    self.assertIn('X-Expected-Error', response.headers)

  def test_header_on_unexpected_error(self):
    """During authorization flow we have the expected 'Unauthorized'.

    If error is unexpected we need to make sure that 'X-Expected-Error'
    header is not set.
    """
    # pylint: disable=unused-argument
    def side_effect_function(*args, **kwargs):
      raise Unauthorized("Unable to get valid credentials")

    with mock.patch("ggrc.gdrive.file_actions.get_gdrive_file_link") as mocked:
      mocked.side_effect = side_effect_function
      control = factories.ControlFactory()
      response = self.api.post(all_models.Document, [{
          "document": {
              "kind": all_models.Document.FILE,
              "source_gdrive_id": "some link",
              "link": "some link",
              "title": "some title",
              "context": None,
              "parent_obj": {
                  "id": control.id,
                  "type": "Control"
              }
          }
      }])
    self.assertEqual(response.status_code, 401)
    self.assertNotIn('X-Expected-Error', response.headers)

  def test_header_on_expected_error_batch(self):
    """During authorization flow we have the expected 'Unauthorized'.

    To allow FE ignore popup we need to set 'X-Expected-Error' header
    """
    # pylint: disable=unused-argument
    def side_effect_function(*args, **kwargs):
      raise GdriveUnauthorized("Unable to get valid credentials")

    with mock.patch("ggrc.gdrive.file_actions.get_gdrive_file_link") as mocked:
      mocked.side_effect = side_effect_function
      control = factories.ControlFactory()

      doc1 = {
          "document": {
              "kind": all_models.Document.FILE,
              "source_gdrive_id": "some link",
              "link": "some link",
              "title": "some title",
              "context": None,
              "parent_obj": {
                  "id": control.id,
                  "type": "Control"
              }
          }
      }
      doc2 = {
          "document": {
              "kind": all_models.Document.REFERENCE_URL,
              "link": "some link",
              "title": "some title",
              "context": None,
          }
      }

    response = self.api.post(all_models.Document, [doc1, doc2])
    self.assertEqual(response.status_code, 401)
    self.assertIn('X-Expected-Error', response.headers)

  def test_create_document_by_api(self,
                                  kind=all_models.Document.REFERENCE_URL):
    """Test crete document via POST"""
    document_data = dict(
        title='Simple title',
        kind=kind,
        link='some_url.com',
        description='mega description'
    )
    _, document = self.gen.generate_object(
        all_models.Document,
        document_data
    )

    result = all_models.Document.query.filter(
        all_models.Document.id == document.id).one()

    self.assertEqual(result.title, 'Simple title')
    self.assertEqual(result.kind, kind)
    self.assertEqual(result.link, 'some_url.com')
    self.assertEqual(result.description, 'mega description')

  @mock.patch('ggrc.gdrive.file_actions.get_gdrive_file_link',
              dummy_gdrive_response_link)
  def test_create_document_file_by_api(self, kind=all_models.Document.FILE):
    """Test crete document.FILE via POST"""
    document_data = dict(
        title='Simple title',
        kind=kind,
        source_gdrive_id='1234',
        description='mega description'
    )
    _, document = self.gen.generate_object(
        all_models.Document,
        document_data
    )

    result = all_models.Document.query.filter(
        all_models.Document.id == document.id).one()

    self.assertEqual(result.slug, 'DOCUMENT-{}'.format(result.id))
    self.assertEqual(result.title, 'Simple title')
    self.assertEqual(result.kind, kind)
    self.assertEqual(result.link, 'http://mega.doc')
    self.assertEqual(result.description, 'mega description')
    self.assertEqual(result.status, all_models.Document.START_STATE)

  def test_document_ref_url_type_with_parent(self):
    """Document of REFERENCE_URL type mapped to parent if parent specified"""
    control = factories.ControlFactory()
    document = factories.DocumentReferenceUrlFactory(
        description='mega description',
        parent_obj={
            'id': control.id,
            'type': 'Control'
        }
    )
    rel_evidences = control.related_objects(_types=[document.type])
    self.assertEqual(document, rel_evidences.pop())

  def test_document_make_admin_endpoint(self):
    """Test /api/document/make_admin endpoint

    should add current user as document admin
    """

    _, editor = self.gen.generate_person(
        user_role="Creator"
    )

    doc = factories.DocumentFileFactory(gdrive_id="123")
    doc_id = doc.id
    self.api.set_user(editor)
    request_data = json.dumps(dict(gdrive_ids=["123", "456"]))
    response = self.api.client.post("/api/document/make_admin",
                                    data=request_data,
                                    content_type="application/json")

    updated = [obj for obj in response.json if obj["updated"]]
    not_updated = [obj for obj in response.json if not obj["updated"]]

    self.assertEquals(len(updated), 1)
    self.assertEquals(updated[0]["object"]["id"], doc_id)
    self.assertEquals(len(not_updated), 1)

    doc = all_models.Document.query.filter_by(id=doc_id).one()
    self.assertEquals(len(doc.access_control_list), 1)
    control_user = all_models.Person.query.get(editor.id)
    self.assertIn(control_user.id,
                  [acr.person_id for acr in doc.access_control_list])

  def test_api_documents_exist(self):
    """Test /api/document/documents_exist"""
    with factories.single_commit():
      doc1 = factories.DocumentFileFactory(gdrive_id="123")
      doc1_id = doc1.id
      factories.DocumentFileFactory(gdrive_id="456")
    endpoint_uri = "/api/document/documents_exist"
    request_data1 = json.dumps(dict(gdrive_ids=["123", "456"]))
    response1 = self.api.client.post(endpoint_uri, data=request_data1,
                                     content_type="application/json")

    self.assertEquals(len(response1.json), 2)
    self.assertTrue(all([r["exists"] for r in response1.json]))

    request_data2 = json.dumps(dict(gdrive_ids=["123", "999"]))
    response2 = self.api.client.post(endpoint_uri, data=request_data2,
                                     content_type="application/json")
    self.assertEquals(len(response2.json), 2)
    existing = [obj for obj in response2.json if obj["exists"]]
    not_existing = [obj for obj in response2.json if not obj["exists"]]
    self.assertEquals(len(existing), 1)
    self.assertEquals(len(not_existing), 1)
    self.assertEquals(existing[0]["object"]["id"], doc1_id)

  def test_add_to_parent_folder(self):
    """If parent has folder => add document to that folder"""
    method_to_patch = 'ggrc.gdrive.file_actions.add_gdrive_file_folder'
    with mock.patch(method_to_patch) as mocked:
      mocked.return_value = 'http://mega.doc'
      with factories.single_commit():
        control = factories.ControlFactory(folder="gdrive_folder_id")
        factories.DocumentFileFactory(
            source_gdrive_id="source_gdrive_id",
            parent_obj={
                "id": control.id,
                "type": "Control"
            }
        )
    mocked.assert_called_with("source_gdrive_id", "gdrive_folder_id")

  def test_add_to_parent_folder_relationship(self):
    """If parent has folder => add document to that folder mapped via rel"""
    method_to_patch = 'ggrc.gdrive.file_actions.add_gdrive_file_folder'
    with mock.patch(method_to_patch) as mocked:
      mocked.return_value = 'http://mega.doc'
      with factories.single_commit():
        control = factories.ControlFactory(folder="gdrive_folder_id")
        control_id = control.id
        doc = factories.DocumentFileFactory(
            source_gdrive_id="source_gdrive_id",
            link='some link'
        )
        doc_id = doc.id

      response = self.api.post(all_models.Relationship, {
          "relationship": {
              "source": {"id": control_id, "type": control.type},
              "destination": {"id": doc_id, "type": doc.type},
              "context": None
          },
      })

    self.assertStatus(response, 201)
    mocked.assert_called_with("source_gdrive_id", "gdrive_folder_id")

  def test_add_to_parent_folder_not_specified(self):
    """If parent has not folder => just save gdrive link"""
    with mock.patch('ggrc.gdrive.file_actions.get_gdrive_file_link') as mocked:
      mocked.return_value = 'http://mega.doc'
      with factories.single_commit():
        control = factories.ControlFactory()
        factories.DocumentFileFactory(
            source_gdrive_id="source_gdrive_id",
            parent_obj={
                "id": control.id,
                "type": "Control"
            }
        )
    mocked.assert_called_with("source_gdrive_id")
コード例 #11
0
class TestAccessControlList(TestCase):
    """TestAccessControlList"""
    @classmethod
    def setUpClass(cls):
        """Sets objects common for all tests in TestCase."""
        super(TestAccessControlList, cls).setUpClass()
        cls.api = Api()

    def setUp(self):
        super(TestAccessControlList, self).setUp()
        self.api = Api()

        self.person = factories.PersonFactory(name="My Person")
        self.acr = factories.AccessControlRoleFactory(object_type="Control",
                                                      read=True)
        self.second_acr = factories.AccessControlRoleFactory(
            object_type="Control", read=True)
        self.control = factories.ControlFactory()
        factories.AccessControlPersonFactory(
            ac_list=self.control.acr_acl_map[self.acr],
            person=self.person,
        )

    def _acl_asserts(self, acl, acr_id, person_id):
        """Run asserts on a given acl list"""
        self.assertEqual(
            len(acl), 1,
            "Access control list did not get saved {}".format(acl))
        self.assertTrue(acl[0]["id"] > 0,
                        "Acces control list did not set an id")
        self.assertEqual(
            acl[0]["ac_role_id"], acr_id,
            "Access control list does not include the saved role id")
        self.assertEqual(
            acl[0]["person_id"], person_id,
            "Access control list does not include person id {}".format(acl[0]))

    def _post_control(self, acr_id, person_id, assertion, collection=False):
        """Helper function for posting a control"""
        title = factories.random_str(prefix="Control - ")
        control = {
            "control": {
                "title": title,
                "type": "Control",
                "context": None,
                "access_control_list":
                [acl_helper.get_acl_json(acr_id, person_id)],
                "assertions": '["{}"]'.format(assertion),
                "external_id": factories.SynchronizableExternalId.next(),
                "external_slug": factories.random_str(),
                "review_status": all_models.Review.STATES.UNREVIEWED,
                "review_status_display_name": "some status",
            },
        }
        with self.api.as_external():
            response = self.api.post(all_models.Control,
                                     [control] if collection else control)

        self.assertTrue(
            response.status_code == 200 or response.status_code == 201,
            "Control not created successfully {}".format(response.status))

        if collection:
            return response.json[0][1]
        return response.json

    def test_object_roles(self):
        """Test if roles are fetched with the object"""
        acr_id, person_id = self.acr.id, self.person.id
        response = self.api.get(all_models.Control, self.control.id)
        self.assert200(
            response,
            "Failed to fetch created control {}".format(response.status))
        self.assertIn(
            "access_control_list", response.json["control"],
            "Access Control List not present in {}".format(
                response.json["control"].keys()))

        acl = response.json["control"]["access_control_list"]
        self._acl_asserts(acl, acr_id, person_id)

    def test_post_object_roles(self):
        """Test if roles are stored correctly when POSTed with the object"""
        acr_id, person_id = self.acr.id, self.person.id
        response = self._post_control(acr_id, person_id, "test assertion")
        acl = response["control"]["access_control_list"]
        self._acl_asserts(acl, acr_id, person_id)

    def test_acl_revision_content(self):
        """Test if the access control list is added to revisions"""
        acr_id, person_id = self.acr.id, self.person.id
        response = self._post_control(acr_id, person_id, "test assertion")
        control_id = response["control"]["id"]
        rev = all_models.Revision.query.filter(
            all_models.Revision.resource_id == control_id,
            all_models.Revision.resource_type == "Control").first()

        acl = rev.content["access_control_list"]
        self._acl_asserts(acl, acr_id, person_id)

    def test_put_object_roles(self):
        """Test if PUTing object roles saves them correctly"""
        second_acr_id, person_id = self.second_acr.id, self.person.id
        response = self.api.get(all_models.Control, self.control.id)
        self.assert200(
            response,
            "Failed to fetch created control {}".format(response.status))

        control = response.json['control']
        control['access_control_list'].append(
            acl_helper.get_acl_json(second_acr_id, person_id))
        with self.api.as_external():
            response = self.api.put(self.control, {"control": control})
        self.assert200(response,
                       "PUTing control failed {}".format(response.status))

        acl = response.json['control']['access_control_list']
        self.assertEqual(len(acl), 2,
                         "Access control list not updated {}".format(acl))

    def test_put_removing_roles(self):
        """Test if PUTing an empty list removes object roles correct"""
        response = self.api.get(all_models.Control, self.control.id)
        self.assert200(
            response,
            "Failed to fetch created control {}".format(response.status))

        control = response.json['control']
        control['access_control_list'] = []
        with self.api.as_external():
            response = self.api.put(self.control, {"control": control})
        self.assert200(response,
                       "PUTing control failed {}".format(response.status))

        acl = response.json['control']['access_control_list']
        self.assertEqual(len(acl), 0,
                         "Access control list not empty {}".format(acl))

    def test_acl_indexing_on_post(self):
        """Test if roles are stored correctly when POSTed with the object"""
        acr_id, person_id = self.acr.id, self.person.id
        response = self._post_control(acr_id, person_id, "test assertion")
        control = response["control"]
        res = mysql.MysqlRecordProperty.query.filter(
            mysql.MysqlRecordProperty.type == "Control",
            mysql.MysqlRecordProperty.key == control["id"],
            mysql.MysqlRecordProperty.property == self.acr.name).all()

        self.assertTrue(
            len(res) > 0,
            "Full text record index not created for {}".format(self.acr.name))

        # email is presented in __sort__ subproperty as well
        self.assertEqual(
            len([r for r in res if r.content == self.person.email]), 2,
            "Person email not indexed {}".format(self.person.email))

        self.assertEqual(
            len([r for r in res if r.content == self.person.name]), 1,
            "Person name not indexed {}".format(self.person.name))

    def test_acl_revision_count(self):
        """Test if acl revision is created when object POSTed and PUTed"""
        acr_id, person_id = self.acr.id, self.person.id

        response = self._post_control(acr_id, person_id, "test assertion")
        # One ACL and Control created in setUp and on by POST
        self.assertEqual(
            all_models.Revision.query.filter_by(
                resource_type="AccessControlPerson").count(), 3)
        self.assertEqual(
            all_models.Revision.query.filter_by(
                resource_type="Control").count(), 2)

        # If content of "access_control_list" is changed,
        # new revision should be created for ACL
        control = response["control"]
        control["access_control_list"] = []
        with self.api.as_external():
            self.api.put(all_models.Control.query.get(control["id"]),
                         {"control": control})
        self.assertEqual(
            all_models.Revision.query.filter_by(
                resource_type="AccessControlPerson").count(), 4)
        self.assertEqual(
            all_models.Revision.query.filter_by(
                resource_type="Control").count(), 3)
コード例 #12
0
class TestAccessControlRole(TestCase):
  """TestAccessControlRole"""

  def setUp(self):
    self.clear_data()
    super(TestAccessControlRole, self).setUp()
    self.api = Api()
    self.object_generator = ObjectGenerator()
    self.people = {}
    for name in ["Creator", "Reader", "Editor"]:
      _, user = self.object_generator.generate_person(
          data={"name": name}, user_role=name)
      self.people[name] = user

  def _post_role(self):
    """Helper function for POSTing roles"""
    name = random_str(prefix="Access Control Role - ")
    return self.api.post(AccessControlRole, {
        "access_control_role": {
            "name": name,
            "object_type": "Control",
            "context": None,
            "read": True
        },
    })

  def test_create(self):
    """Test Access Control Role creation"""
    response = self._post_role()
    assert response.status_code == 201, \
        "Failed to create a new access control role, response was {}".format(
            response.status)

    id_ = response.json['access_control_role']['id']
    role = AccessControlRole.query.filter(AccessControlRole.id == id_).first()
    assert role.read == 1, \
        "Read permission not correctly saved {}".format(role.read)
    assert role.update == 1, \
        "Update permission not correctly saved {}".format(role.update)
    assert role.delete == 1, \
        "Update permission not correctly saved {}".format(role.delete)

  @ddt.data(
      {"mandatory": True, "exp_response": MANDATORY_ROLE_RESPONSE},
      {"mandatory": False, "exp_response": NON_MANDATORY_ROLE_RESPONSE},
  )
  @ddt.unpack
  def test_mandatory_delete(self, mandatory, exp_response):
    """Test set empty field via import if acr mandatory is {mandatory}"""
    with factories.single_commit():
      user = factories.PersonFactory()
      control = factories.ControlFactory()
      role = factories.AccessControlRoleFactory(
          name=ROLE_NAME,
          object_type="Control",
          mandatory=mandatory,
      )
      role_id = role.id
      factories.AccessControlListFactory(
          person=user,
          ac_role_id=role.id,
          object=control,
      )
    response = self.import_data(OrderedDict([
        ("object_type", "Control"),
        ("Code*", control.slug),
        (ROLE_NAME, "--"),
    ]))
    self._check_csv_response(response, exp_response)
    db_data = defaultdict(set)
    for acl in all_models.Control.query.get(control.id).access_control_list:
      db_data[acl.ac_role_id].add(acl.person_id)
    if mandatory:
      cur_user = all_models.Person.query.filter_by(
          email="*****@*****.**").first()
      self.assertEqual(set([cur_user.id]), db_data[role_id])
    else:
      self.assertFalse(db_data[role_id])

  def test_only_admin_can_post(self):
    """Only admin users should be able to POST access control roles"""
    for name in ("Creator", "Reader", "Editor"):
      person = self.people.get(name)
      self.api.set_user(person)
      response = self._post_role()
      assert response.status_code == 403, \
          "Non admins should get forbidden error when POSTing role. {}".format(
              response.status)

  @ddt.data(
      ("name", "New ACR"),
      ("read", False),
      ("mandatory", False),
      ("non_editable", False),
  )
  @ddt.unpack
  def test_modify_non_editable_role(self, field_name, field_value):
    """Test if user can modify non-editable role"""
    # Primary Contacts role of Control is non-editable
    ac_role = AccessControlRole.query.filter_by(
        object_type="Control",
        name="Primary Contacts",
    ).first()

    response = self.api.put(ac_role, {field_name: field_value})
    assert response.status_code == 403, \
        "Forbidden error should be thrown when non-editable " \
        "role {} updated.".format(ac_role.name)

  def test_delete_non_editable_role(self):
    """Test if user can delete non-editable role"""
    # Primary Contacts role of Control is non-editable
    ac_role = AccessControlRole.query.filter_by(
        object_type="Control",
        name="Primary Contacts",
    ).first()

    response = self.api.delete(ac_role)
    assert response.status_code == 403, \
        "Forbidden error should be thrown when non-editable " \
        "role {} deleted.".format(ac_role.name)
コード例 #13
0
class TestArchived(WithQueryApi, TestCase):
  """Tests for filtering by Archived field."""
  # pylint: disable=invalid-name
  def setUp(self):
    super(TestArchived, self).setUp()
    self.client.get("/login")
    self.api = Api()

  @ddt.data([1, 3])
  def test_archived_audits(self, archived_audits):
    """Test filtration by Archived Audits."""
    with factories.single_commit():
      audit_ids = [factories.AuditFactory().id for _ in range(5)]

    expected_ids = []
    for i in archived_audits:
      audit = all_models.Audit.query.get(audit_ids[i])
      response = self.api.put(audit, {"archived": True})
      self.assert200(response)
      expected_ids.append(audit_ids[i])

    ids = self.simple_query(
        "Audit",
        expression=["archived", "=", "true"],
        type_="ids",
        field="ids"
    )
    self.assertItemsEqual(ids, expected_ids)

  @ddt.data([2, 4])
  def test_archived_assessments(self, archived_audits):
    """Test filtration by Archived Assessments."""
    # Create 5 Audits, each of them has 3 Assessment
    with factories.single_commit():
      audit_ids = []
      for _ in range(5):
        audit = factories.AuditFactory()
        audit_ids.append(audit.id)
        for _ in range(3):
          factories.AssessmentFactory(audit=audit)

    # This list contain ids of assessments from audits in archived_audits
    expected_ids = []
    for i in archived_audits:
      audit = all_models.Audit.query.get(audit_ids[i])
      expected_ids += [a.id for a in audit.assessments]
      response = self.api.put(audit, {"archived": True})
      self.assert200(response)

    ids = self.simple_query(
        "Assessment",
        expression=["archived", "=", "true"],
        type_="ids",
        field="ids"
    )
    self.assertItemsEqual(ids, expected_ids)

  def _archive_audit_and_check_evidence(self, audit, evidence_ids):
    """Helper function archive audit and check evidences is archived"""
    response = self.api.put(audit, {"archived": True})
    self.assert200(response)
    ids = self.simple_query(
        "Evidence",
        expression=["archived", "=", "true"],
        type_="ids",
        field="ids"
    )
    self.assertItemsEqual(ids, evidence_ids)

  def test_archived_evidence_forward(self):
    """Test evidence archived with audit in audit -> assessment"""
    expected_evidence_ids = []
    with factories.single_commit():
      audit = factories.AuditFactory()
      assessment = factories.AssessmentFactory(audit=audit)
      evidence = factories.EvidenceUrlFactory()
      factories.RelationshipFactory(source=audit,
                                    destination=assessment)
      factories.RelationshipFactory(source=evidence,
                                    destination=assessment)
      expected_evidence_ids.append(evidence.id)
    self._archive_audit_and_check_evidence(audit, expected_evidence_ids)

  def test_archived_evidence_backward(self):
    """Test evidence archived with audit in assessment -> audit"""
    expected_evidence_ids = []
    with factories.single_commit():
      audit = factories.AuditFactory()
      assessment = factories.AssessmentFactory(audit=audit)
      evidence = factories.EvidenceUrlFactory()
      factories.RelationshipFactory(source=assessment,
                                    destination=audit)
      factories.RelationshipFactory(source=assessment,
                                    destination=evidence)
      expected_evidence_ids.append(evidence.id)
    self._archive_audit_and_check_evidence(audit, expected_evidence_ids)

  def test_archived_evidence_from_audit_forward(self):
    """Test evidence archived with audit in audit -> evidence"""
    expected_evidence_ids = []
    with factories.single_commit():
      audit = factories.AuditFactory()
      evidence = factories.EvidenceUrlFactory()
      factories.RelationshipFactory(source=audit,
                                    destination=evidence)
      expected_evidence_ids.append(evidence.id)
    self._archive_audit_and_check_evidence(audit, expected_evidence_ids)

  def test_archived_evidence_from_audit_backward(self):
    """Test evidence archived with audit in evidence -> audit"""
    expected_evidence_ids = []
    with factories.single_commit():
      audit = factories.AuditFactory()
      evidence = factories.EvidenceUrlFactory()
      factories.RelationshipFactory(source=evidence,
                                    destination=audit)
      expected_evidence_ids.append(evidence.id)
    self._archive_audit_and_check_evidence(audit, expected_evidence_ids)
コード例 #14
0
class TestWorkflowCycleStatePropagation(TestCase):
  """Test case for cycle task to cycle task group status propagation"""

  def setUp(self):
    super(TestWorkflowCycleStatePropagation, self).setUp()
    self.api = Api()
    self.generator = WorkflowsGenerator()
    self.object_generator = ObjectGenerator()

    self.weekly_wf = {
        "title": "weekly thingy",
        "description": "start this many a time",
        "unit": "week",
        "repeat_every": 1,
        "task_groups": [{
            "title": "weekly task group",
            "task_group_tasks": [{
                "title": "weekly task 1",
                "start_date": dtm.date(2016, 6, 10),
                "end_date": dtm.date(2016, 6, 13),
            }, {
                "title": "weekly task 1",
                "start_date": dtm.date(2016, 6, 10),
                "end_date": dtm.date(2016, 6, 13),
            }]},
        ]
    }

  def test_weekly_state_transitions_assigned_inprogress(self):
    """Test that starting one cycle task changes cycle task group"""

    with freeze_time("2016-6-10 13:00:00"):  # Friday, 6/10/2016
      _, wf = self.generator.generate_workflow(self.weekly_wf)
      self.generator.activate_workflow(wf)

      ctg = db.session.query(CycleTaskGroup).join(
          Cycle).join(Workflow).filter(Workflow.id == wf.id).all()[0]
      self.assertEqual(ctg.status, "Assigned")

      cycle_tasks = db.session.query(CycleTaskGroupObjectTask).join(
          Cycle).join(Workflow).filter(Workflow.id == wf.id).all()
      first_ct, second_ct = cycle_tasks

      for cycle_task in cycle_tasks:
        self.assertEqual(cycle_task.status, "Assigned")

      # Move one task to InProgress
      _, first_ct = self.generator.modify_object(
          first_ct, {"status": "InProgress"})

      self.assertEqual(first_ct.status, "InProgress")
      ctg = db.session.query(CycleTaskGroup).get(ctg.id)
      self.assertEqual(ctg.status, "InProgress")

      # Undo operation
      _, first_ct = self.generator.modify_object(
          first_ct, {"status": "Assigned"})

      self.assertEqual(first_ct.status, "Assigned")
      ctg = db.session.query(CycleTaskGroup).get(ctg.id)
      self.assertEqual(ctg.status, "Assigned")

      # Move both to in progress
      for cycle_task in cycle_tasks:
        self.generator.modify_object(
            cycle_task, {"status": "InProgress"})

      ctg = db.session.query(CycleTaskGroup).get(ctg.id)
      self.assertEqual(ctg.status, "InProgress")

      # Undo one cycle task
      _, first_ct = self.generator.modify_object(
          first_ct, {"status": "Assigned"})
      second_ct = db.session.query(CycleTaskGroupObjectTask).get(second_ct.id)
      ctg = db.session.query(CycleTaskGroup).get(ctg.id)

      self.assertEqual(first_ct.status, "Assigned")
      self.assertEqual(second_ct.status, "InProgress")
      self.assertEqual(ctg.status, "InProgress")

      # Undo second cycle task
      _, second_ct = self.generator.modify_object(
          second_ct, {"status": "Assigned"})
      first_ct = db.session.query(CycleTaskGroupObjectTask).get(first_ct.id)
      ctg = db.session.query(CycleTaskGroup).get(ctg.id)

      self.assertEqual(first_ct.status, "Assigned")
      self.assertEqual(second_ct.status, "Assigned")
      self.assertEqual(ctg.status, "Assigned")

  def test_weekly_state_transitions_inprogress_finished(self):
    """Test In Progress to Finished transitions"""

    with freeze_time("2016-6-10 13:00:00"):  # Friday, 6/10/2016
      _, wf = self.generator.generate_workflow(self.weekly_wf)
      self.generator.activate_workflow(wf)

      ctg = db.session.query(CycleTaskGroup).join(
          Cycle).join(Workflow).filter(Workflow.id == wf.id).all()[0]

      cycle_tasks = db.session.query(CycleTaskGroupObjectTask).join(
          Cycle).join(Workflow).filter(Workflow.id == wf.id).all()
      first_ct, second_ct = cycle_tasks

      # Move both tasks to InProgress
      for cycle_task in cycle_tasks:
        self.generator.modify_object(
            cycle_task, {"status": "InProgress"})

      # Test that moving one task to finished doesn't finish entire cycle
      _, first_ct = self.generator.modify_object(
          first_ct, {"status": "Finished"})
      second_ct = db.session.query(CycleTaskGroupObjectTask).get(second_ct.id)
      ctg = db.session.query(CycleTaskGroup).get(ctg.id)

      self.assertEqual(first_ct.status, "Finished")
      self.assertEqual(second_ct.status, "InProgress")
      self.assertEqual(ctg.status, "InProgress")

      # Test moving second task to Finished - entire cycle should be finished
      _, second_ct = self.generator.modify_object(
          second_ct, {"status": "Finished"})
      first_ct = db.session.query(CycleTaskGroupObjectTask).get(first_ct.id)
      ctg = db.session.query(CycleTaskGroup).get(ctg.id)

      self.assertEqual(second_ct.status, "Finished")
      self.assertEqual(first_ct.status, "Finished")
      self.assertEqual(ctg.status, "Finished")

      # Undo one task, cycle should be InProgress
      _, first_ct = self.generator.modify_object(
          first_ct, {"status": "InProgress"})
      second_ct = db.session.query(CycleTaskGroupObjectTask).get(second_ct.id)
      ctg = db.session.query(CycleTaskGroup).get(ctg.id)

      self.assertEqual(first_ct.status, "InProgress")
      self.assertEqual(second_ct.status, "Finished")
      self.assertEqual(ctg.status, "InProgress")

  def test_weekly_state_transitions_finished_verified(self):
    """Test Finished to Verified transitions"""

    with freeze_time("2016-6-10 13:00:00"):  # Friday, 6/10/2016
      _, wf = self.generator.generate_workflow(self.weekly_wf)
      self.generator.activate_workflow(wf)

      ctg = db.session.query(CycleTaskGroup).join(
          Cycle).join(Workflow).filter(Workflow.id == wf.id).all()[0]

      cycle_tasks = db.session.query(CycleTaskGroupObjectTask).join(
          Cycle).join(Workflow).filter(Workflow.id == wf.id).all()
      first_ct, second_ct = cycle_tasks

      # Move both tasks to InProgress
      for cycle_task in cycle_tasks:
        self.generator.modify_object(
            cycle_task, {"status": "InProgress"})
        self.generator.modify_object(
            cycle_task, {"status": "Finished"})

      ctg = db.session.query(CycleTaskGroup).get(ctg.id)
      self.assertEqual(ctg.status, "Finished")

      for cycle_task in cycle_tasks:
        cycle_task = db.session.query(CycleTaskGroupObjectTask).get(
            cycle_task.id)
        self.assertEqual(cycle_task.status, "Finished")

      # Verify first CT
      _, first_ct = self.generator.modify_object(
          first_ct, {"status": "Verified"})

      second_ct = db.session.query(CycleTaskGroupObjectTask).get(second_ct.id)
      ctg = db.session.query(CycleTaskGroup).get(ctg.id)

      self.assertEqual(first_ct.status, "Verified")
      self.assertEqual(second_ct.status, "Finished")
      self.assertEqual(ctg.status, "Finished")

      # Verify second CT
      _, second_ct = self.generator.modify_object(
          second_ct, {"status": "Verified"})

      first_ct = db.session.query(CycleTaskGroupObjectTask).get(first_ct.id)
      ctg = db.session.query(CycleTaskGroup).get(ctg.id)

      self.assertEqual(first_ct.status, "Verified")
      self.assertEqual(second_ct.status, "Verified")
      self.assertEqual(ctg.status, "Verified")

  def test_weekly_state_transitions_finished_declined(self):
    """Test Finished to Declined transitions"""

    with freeze_time("2016-6-10 13:00:00"):  # Friday, 6/10/2016
      _, wf = self.generator.generate_workflow(self.weekly_wf)
      self.generator.activate_workflow(wf)

      ctg = db.session.query(CycleTaskGroup).join(
          Cycle).join(Workflow).filter(Workflow.id == wf.id).all()[0]

      cycle_tasks = db.session.query(CycleTaskGroupObjectTask).join(
          Cycle).join(Workflow).filter(Workflow.id == wf.id).all()
      first_ct, second_ct = cycle_tasks

      # Move both tasks to InProgress
      for cycle_task in cycle_tasks:
        self.generator.modify_object(
            cycle_task, {"status": "InProgress"})
        self.generator.modify_object(
            cycle_task, {"status": "Finished"})

      ctg = db.session.query(CycleTaskGroup).get(ctg.id)
      self.assertEqual(ctg.status, "Finished")

      # Decline first CT
      _, first_ct = self.generator.modify_object(
          first_ct, {"status": "Declined"})

      second_ct = db.session.query(CycleTaskGroupObjectTask).get(second_ct.id)
      ctg = db.session.query(CycleTaskGroup).get(ctg.id)

      self.assertEqual(first_ct.status, "Declined")
      self.assertEqual(second_ct.status, "Finished")
      self.assertEqual(ctg.status, "InProgress")

  def test_deleted_task_state_transitions(self):
    """Test InProgress to Finished transition after task is deleted"""

    with freeze_time("2016-6-10 13:00:00"):  # Friday, 6/10/2016
      _, wf = self.generator.generate_workflow(self.weekly_wf)
      self.generator.activate_workflow(wf)

      ctg = db.session.query(CycleTaskGroup).join(
          Cycle).join(Workflow).filter(Workflow.id == wf.id).all()[0]
      first_ct, second_ct = db.session.query(CycleTaskGroupObjectTask).join(
          Cycle).join(Workflow).filter(Workflow.id == wf.id).all()

      # Move first task to InProgress
      self.generator.modify_object(first_ct, {"status": "InProgress"})
      self.generator.modify_object(first_ct, {"status": "Finished"})
      # Delete second task
      response = self.generator.api.delete(second_ct)
      self.assert200(response)

      ctg = db.session.query(CycleTaskGroup).get(ctg.id)
      self.assertEqual(ctg.status, "Finished")

  def test_cycle_change_on_ct_status_transition(self):
    """Test cycle is_current change on task Finished to InProgress transition
    """
    with freeze_time("2016-6-10 13:00:00"):  # Friday, 6/10/2016
      _, wf = self.generator.generate_workflow(self.weekly_wf)
      self.generator.activate_workflow(wf)

    ctg = db.session.query(
        CycleTaskGroup
    ).join(
        Cycle
    ).join(
        Workflow
    ).filter(
        Workflow.id == wf.id
    ).one()
    c_id = ctg.cycle.id
    first_ct, second_ct = db.session.query(CycleTaskGroupObjectTask).join(
        Cycle).join(Workflow).filter(Workflow.id == wf.id).all()
    self.api.put(first_ct, {"status": "Verified"})
    self.api.put(second_ct, {"status": "Verified"})
    # cycle now should have is_current == False
    cycle = db.session.query(Cycle).get(c_id)

    self.assertEqual(cycle.is_current, False)

    # Move second task back to InProgress
    self.api.put(second_ct, {"status": "InProgress"})
    # cycle now should have is_current == True

    cycle = db.session.query(Cycle).get(ctg.cycle.id)
    self.assertEqual(cycle.is_current, True)

  def test_async_request_state_transitions(self):
    """Test asynchronous transitions"""
    def change_state(cycle_task, status):
      self.generator.api.put(cycle_task, {"status": status})

    updated_wf = deepcopy(self.weekly_wf)
    updated_wf["task_groups"][0]["task_group_tasks"].extend(
        [{"title": "weekly task 1"} for _ in xrange(3)])

    with freeze_time("2016-6-10 13:00:00"):  # Friday, 6/10/2016
      _, wf = self.generator.generate_workflow(updated_wf)
      self.generator.activate_workflow(wf)

      ctg = db.session.query(CycleTaskGroup).join(
          Cycle).join(Workflow).filter(Workflow.id == wf.id).all()[0]

      cycle_tasks = db.session.query(CycleTaskGroupObjectTask).join(
          Cycle).join(Workflow).filter(Workflow.id == wf.id).all()

      # Move all tasks to InProgress
      threads = []
      for cycle_task in cycle_tasks:
        change_state(cycle_task, "InProgress")
        threads.append(Thread(target=change_state,
                              args=(cycle_task, "Finished")))

      for t in threads:
        t.start()
      for t in threads:
        t.join()

      db.session.commit()
      ctg = db.session.query(CycleTaskGroup).get(ctg.id)
      self.assertEqual(ctg.status, "Finished")
コード例 #15
0
ファイル: test_document.py プロジェクト: zidarsk8/ggrc-core
class TestDocument(TestCase):
  """Document test cases"""
  # pylint: disable=invalid-name

  def setUp(self):
    super(TestDocument, self).setUp()
    self.api = Api()
    self.gen = generator.ObjectGenerator()

  def test_update_title(self):
    """Test update document title."""
    create_title = "test_title"
    update_title = "update_test_title"
    document = factories.DocumentFactory(title=create_title)
    response = self.api.put(document, {"title": update_title})
    self.assert200(response)
    self.assertEqual(all_models.Document.query.get(document.id).title,
                     update_title)

  def create_document_by_type(self, doc_type):
    """Create docuemtn with sent type."""
    data = {
        "title": "test_title",
        "link": "test_link",
    }
    if doc_type is not None:
        data["document_type"] = doc_type
    doc_type = doc_type or all_models.Document.URL
    resp, doc = self.gen.generate_object(
        all_models.Document,
        data
    )
    self.assertTrue(
        all_models.Document.query.filter(
            all_models.Document.id == resp.json["document"]['id'],
            all_models.Document.document_type == doc_type,
        ).all()
    )
    return (resp, doc)

  def test_create_url(self):
    """Test create url."""
    self.create_document_by_type(all_models.Document.URL)

  def test_create_url_default(self):
    """Test create url(default)."""
    self.create_document_by_type(None)

  def test_create_evidence(self):
    """Test create evidence."""
    self.create_document_by_type(all_models.Document.ATTACHMENT)

  def test_create_invalid_type(self):
    """Test validation document_type."""
    data = {
        "document_type": 3,
        "title": "test_title",
        "link": "test_link",
        "owners": [self.gen.create_stub(all_models.Person.query.first())],
    }
    obj_name = all_models.Document._inflector.table_singular
    obj = all_models.Document()
    obj_dict = self.gen.obj_to_dict(obj, obj_name)
    obj_dict[obj_name].update(data)
    resp = self.api.post(all_models.Document, obj_dict)
    self.assert400(resp)
    self.assertEqual('"Invalid value for attribute document_type. '
                     'Expected options are `URL`, `EVIDENCE`."',
                     resp.data)
コード例 #16
0
ファイル: base.py プロジェクト: egorhm/ggrc-core
class BaseTestProposalApi(TestCase):
  """Base TestCase class proposal apip tests."""

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

  def create_proposal(self, instance, **context):
    """Create proposal."""
    data = context.copy()
    data["instance"] = {"id": instance.id, "type": instance.type}
    resp = self.api.post(all_models.Proposal, {"proposal": data})
    self.assertEqual(201, resp.status_code)
    return resp

  def update_proposal_status(self, proposal, status, resp_status, **context):
    """Update proposal status via api."""
    data = {"status": status}
    data.update(context)
    resp = self.api.put(proposal, {"proposal": data})
    self.assertEqual(resp_status, resp.status_code)
    return resp

  def decline_proposal(self, proposal, resp_status=200, **context):
    return self.update_proposal_status(proposal,
                                       proposal.STATES.DECLINED,
                                       resp_status,
                                       **context)

  def apply_proposal(self, proposal, resp_status=200, **context):
    return self.update_proposal_status(proposal,
                                       proposal.STATES.APPLIED,
                                       resp_status,
                                       **context)

  @staticmethod
  def revision_query_for(obj):
    return all_models.Revision.query.filter(
        all_models.Revision.resource_type == obj.type,
        all_models.Revision.resource_id == obj.id
    )

  def latest_revision_for(self, obj):
    return self.revision_query_for(
        obj
    ).order_by(
        all_models.Revision.id.desc()
    ).first()

  @contextlib.contextmanager
  def number_obj_revisions_for(self, obj, increase_on=1):
    """Context manager,

    checks the number of logged revisions after nested operations."""
    revision_query = self.revision_query_for(obj)
    start_count = revision_query.count()
    yield
    expected = start_count + increase_on
    current = revision_query.count()
    msg = ("Object change isn't logged correctly: "
           "expected number {expected} is not equal to {current}.")
    self.assertEqual(expected,
                     current,
                     msg.format(expected=expected, current=current))
コード例 #17
0
class TestAuditRBAC(TestCase):
    """Test audit RBAC"""
    # pylint: disable=too-many-instance-attributes

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

    def setUp(self):
        """Imports test_csvs/audit_rbac_snapshot_create.csv needed by the tests"""
        TestCase.clear_data()
        self.api = Api()
        self.objgen = integration.ggrc.generator.ObjectGenerator()

        self.csv_files = itertools.cycle([
            "audit_rbac_snapshot_create.csv", "audit_rbac_snapshot_update.csv"
        ])

        response = self._import_file(next(self.csv_files))
        self._check_csv_response(response, {})

        self.people = all_models.Person.eager_query().all()

        self.program = db.session.query(all_models.Program).filter(
            all_models.Program.slug == "PMRBACPROGRAM-1").one()

        sources = set(r.source for r in self.program.related_sources)
        destinations = set(r.destination
                           for r in self.program.related_destinations)
        related = [
            obj for obj in sources.union(destinations)
            if not isinstance(obj, all_models.Person)
        ]
        self.related_objects = related

        self.api = Api()
        self.client.get("/login")

        self.audit = self.create_audit()

        self.snapshots = all_models.Snapshot.eager_query().all()

    def create_audit(self):
        """Create default audit for audit snapshot RBAC tests"""
        _, audit = self.objgen.generate_object(
            all_models.Audit, {
                "title": "Snapshotable audit",
                "program": {
                    "id": self.program.id
                },
                "status": "Planned",
                "snapshots": {
                    "operation": "create",
                },
                "context": {
                    "type": "Context",
                    "id": self.program.context_id,
                    "href": "/api/contexts/{}".format(self.program.context_id)
                }
            })
        self.add_auditors(audit)
        return audit

    def add_auditors(self, audit):
        """Add auditors to audits via POST user_role call"""
        auditor_emails = [
            "*****@*****.**",
            "*****@*****.**",
            "*****@*****.**",
            "*****@*****.**",
        ]
        program_reader_emails = [
            "*****@*****.**",
        ]
        auditor_role = db.session.query(
            all_models.Role).filter(all_models.Role.name == "Auditor").one()
        program_reader_role = db.session.query(all_models.Role).filter(
            all_models.Role.name == "ProgramReader").one()

        program = db.session.query(all_models.Program).filter(
            all_models.Program.slug == "PMRBACPROGRAM-1").one()

        user_roles = [(auditor_emails, auditor_role, audit.context),
                      (program_reader_emails, program_reader_role,
                       program.context)]

        for emails, role, context in user_roles:
            for email in emails:
                auditor = all_models.Person.query.filter(
                    all_models.Person.email == email).one()
                self.objgen.generate_user_role(auditor, role, context)

    def update_audit(self):
        """Update default audit"""
        response = self._import_file(next(self.csv_files))
        self._check_csv_response(response, {})

        audit = all_models.Audit.query.filter(
            all_models.Audit.title == "Snapshotable audit").one()
        self.audit = audit

        self.api.modify_object(self.audit,
                               {"snapshots": {
                                   "operation": "upsert"
                               }})

    def read(self, objects):
        """Attempt to do a GET request for every object in the objects list"""
        responses = []
        for obj in objects:
            status_code = self.api.get(obj.__class__, obj.id).status_code
            responses.append((obj.type, status_code))
        return responses

    def update(self, objects):
        """Attempt to do a PUT request for every object in the objects list"""
        scope_response = self.api.get(self.audit.__class__, self.audit.id)
        if scope_response.status_code == 200:
            self.update_audit()

        responses = []
        for obj in objects:
            response = self.api.get(obj.__class__, obj.id)
            status_code = response.status_code
            if response.status_code == 200:
                data = response.json
                if obj.type == "Snapshot":
                    data.update({"update_revision": "latest"})
                put_call = self.api.put(obj, data)
                status_code = put_call.status_code
            responses.append((obj.type, status_code))
        return responses

    def call_api(self, method, expected_statuses):
        """Calls the REST api with a given method and returns a list of
       status_codes that do not match the expected_statuses dict"""
        all_errors = []
        for person in self.people:
            self.api.set_user(person)
            responses = method(self.snapshots + [self.audit])
            for type_, code in responses:
                if code != expected_statuses[person.email][type_]:
                    all_errors.append(
                        "{} does not have {} access to {} ({})".format(
                            person.email, method.__name__, type_, code))
        return all_errors

    def test_read_access_on_mapped(self):
        """Test READ access to snapshotted objects of default audit"""
        expected_statuses = defaultdict(lambda: defaultdict(lambda: 200))
        exceptional_users = (("*****@*****.**",
                              DEFAULT_LACK_OF_PERMISSIONS), )
        for user, exceptions in exceptional_users:
            for type_, status_code in exceptions.items():
                expected_statuses[user][type_] = status_code
        errors = self.call_api(self.read, expected_statuses)
        assert not errors, "\n".join(errors)

    def test_update_access_on_mapped(self):
        """Test UPDATE access to snapshotted objects of default audit"""
        expected_statuses = defaultdict(lambda: defaultdict(lambda: 200))

        exceptional_users = (
            ("*****@*****.**", DEFAULT_LACK_OF_PERMISSIONS),
            ("*****@*****.**", DEFAULT_LACK_OF_PERMISSIONS),
            ("*****@*****.**", DEFAULT_LACK_OF_PERMISSIONS),
            ("*****@*****.**", DEFAULT_LACK_OF_PERMISSIONS),
            # Auditor roles
            ("*****@*****.**", DEFAULT_AUDITOR_PERMISSIONS),
            ("*****@*****.**", DEFAULT_AUDITOR_PERMISSIONS))

        for user, exceptions in exceptional_users:
            for type_, status_code in exceptions.items():
                expected_statuses[user][type_] = status_code

        errors = self.call_api(self.update, expected_statuses)
        assert not errors, "\n".join(errors)
コード例 #18
0
class TestAccessControlRole(TestCase):
  """TestAccessControlRole"""

  def setUp(self):
    self.clear_data()
    super(TestAccessControlRole, self).setUp()
    self.api = Api()
    self.object_generator = ObjectGenerator()
    self.people = {}
    for name in ["Creator", "Reader", "Editor"]:
      _, user = self.object_generator.generate_person(
          data={"name": name}, user_role=name)
      self.people[name] = user

  def _post_role(self, name=None, object_type="Control"):
    """Helper function for POSTing roles"""
    if name is None:
      name = random_str(prefix="Access Control Role - ")
    return self.api.post(AccessControlRole, {
        "access_control_role": {
            "name": name,
            "object_type": object_type,
            "context": None,
            "read": True
        },
    })

  def test_create_after_objects(self):
    """Test eager creation of ACLs on existing objects with new ACR."""
    risk_id = factories.RiskFactory().id
    role_name = "New Custom Role"
    self._post_role(name=role_name, object_type="Risk")
    risk = all_models.Risk.query.get(risk_id)
    self.assertIn(role_name, risk.acr_name_acl_map.keys())
    self.assertIsNotNone(risk.acr_name_acl_map[role_name])

  def test_create(self):
    """Test Access Control Role creation"""
    response = self._post_role(object_type="Risk")
    assert response.status_code == 201, \
        "Failed to create a new access control role, response was {}".format(
            response.status)

    id_ = response.json['access_control_role']['id']
    role = AccessControlRole.query.filter(AccessControlRole.id == id_).first()
    assert role.read == 1, \
        "Read permission not correctly saved {}".format(role.read)
    assert role.update == 1, \
        "Update permission not correctly saved {}".format(role.update)
    assert role.delete == 1, \
        "Update permission not correctly saved {}".format(role.delete)

  @ddt.data(
      {"mandatory": True, "exp_response": MANDATORY_ROLE_RESPONSE},
      {"mandatory": False, "exp_response": NON_MANDATORY_ROLE_RESPONSE},
  )
  @ddt.unpack
  def test_mandatory_delete(self, mandatory, exp_response):
    """Test set empty field via import if acr mandatory is {mandatory}"""
    role = factories.AccessControlRoleFactory(
        name=ROLE_NAME,
        object_type="Risk",
        mandatory=mandatory,
    )
    with factories.single_commit():
      user = factories.PersonFactory()
      risk = factories.RiskFactory()
      role_id = role.id
      factories.AccessControlPersonFactory(
          ac_list=risk.acr_name_acl_map[ROLE_NAME],
          person=user,
      )
    response = self.import_data(OrderedDict([
        ("object_type", "Risk"),
        ("Code*", risk.slug),
        (ROLE_NAME, "--"),
    ]))
    self._check_csv_response(response, exp_response)
    db_data = defaultdict(set)
    risk = all_models.Risk.query.get(risk.id)
    for person, acl in risk.access_control_list:
      db_data[acl.ac_role_id].add(person.id)
    if mandatory:
      cur_user = all_models.Person.query.filter_by(
          email="*****@*****.**").first()
      self.assertEqual(set([cur_user.id]), db_data[role_id])
    else:
      self.assertFalse(db_data[role_id])

  def test_only_admin_can_post(self):
    """Only admin users should be able to POST access control roles"""
    for name in ("Creator", "Reader", "Editor"):
      person = self.people.get(name)
      self.api.set_user(person)
      response = self._post_role()
      assert response.status_code == 403, \
          "Non admins should get forbidden error when POSTing role. {}".format(
              response.status)

  @ddt.data(
      ("name", "New ACR"),
      ("read", False),
      ("mandatory", False),
      ("non_editable", False),
  )
  @ddt.unpack
  def test_modify_non_editable_role(self, field_name, field_value):
    """Test if user can modify non-editable role"""
    # Primary Contacts role of Control is non-editable
    ac_role = AccessControlRole.query.filter_by(
        object_type="Control",
        name="Control Operators",
    ).first()

    response = self.api.put(ac_role, {field_name: field_value})
    assert response.status_code == 403, \
        "Forbidden error should be thrown when non-editable " \
        "role {} updated.".format(ac_role.name)

  def test_delete_non_editable_role(self):
    """Test if user can delete non-editable role"""
    # Primary Contacts role of Control is non-editable
    ac_role = AccessControlRole.query.filter_by(
        object_type="Control",
        name="Control Operators",
    ).first()

    response = self.api.delete(ac_role)
    assert response.status_code == 403, \
        "Forbidden error should be thrown when non-editable " \
        "role {} deleted.".format(ac_role.name)

  @ddt.data("Control")
  def test_create_from_ggrcq(self, object_type):
    """Test that create action only for GGRCQ."""
    with self.api.as_external():
      response = self._post_role(object_type=object_type)
      self.assertEqual(response.status_code, 201)

  @ddt.data("Control")
  def test_create_from_ggrc(self, object_type):
    """Test create action not allowed for GGRC."""
    response = self._post_role(object_type=object_type)
    self.assertEqual(response.status_code, 405)

  @ddt.data("Control")
  def test_modify_from_ggrcq(self, object_type):
    """Test that modify action only for GGRCQ."""
    with factories.single_commit():
      acr_id = factories.AccessControlRoleFactory(object_type=object_type).id

    with self.api.as_external():
      acr = all_models.AccessControlRole.query.get(acr_id)
      response = self.api.put(acr, {"name": "new acr"})
      self.assertEqual(response.status_code, 200)

  @ddt.data("Control")
  def test_modify_from_ggrc(self, object_type):
    """Test modify action not allowed for GGRC."""
    with factories.single_commit():
      acr = factories.AccessControlRoleFactory(object_type=object_type)

    response = self.api.put(acr, {"name": "new acr"})
    self.assertEqual(response.status_code, 405)

  @ddt.data("Control")
  def test_delete_from_ggrcq(self, object_type):
    """Test that modify action only for GGRCQ."""
    with factories.single_commit():
      acr_id = factories.AccessControlRoleFactory(object_type=object_type).id

    with self.api.as_external():
      acr = all_models.AccessControlRole.query.get(acr_id)
      response = self.api.delete(acr)
      self.assertEqual(response.status_code, 200)

  @ddt.data("Control")
  def test_delete_from_ggrc(self, object_type):
    """Test modify action not allowed for GGRC."""
    with factories.single_commit():
      acr = factories.AccessControlRoleFactory(object_type=object_type)

    response = self.api.delete(acr)
    self.assertEqual(response.status_code, 405)
コード例 #19
0
class TestIssueIntegration(ggrc.TestCase):
    """Test set for IssueTracker integration functionality."""

    # pylint: disable=invalid-name

    def setUp(self):
        # pylint: disable=super-on-old-class
        super(TestIssueIntegration, self).setUp()
        self.api = Api()
        self.client.get("/login")

    @mock.patch("ggrc.integrations.issues.Client.create_issue",
                return_value={"issueId": "issueId"})
    @mock.patch.object(settings, "ISSUE_TRACKER_ENABLED", True)
    def test_create_issue_tracker_info(self, mock_create_issue):
        """Test creation issue tracker issue for Issue object."""
        component_id = "1234"
        hotlist_id = "4321"
        issue_type = "Default Issue type"
        issue_priority = "P2"
        issue_severity = "S1"
        title = "test title"

        with mock.patch.object(integration_utils,
                               "exclude_auditor_emails",
                               return_value={
                                   u"*****@*****.**",
                               }):
            response = self.api.post(
                all_models.Issue, {
                    "issue": {
                        "title": title,
                        "context": None,
                        "issue_tracker": {
                            "enabled": True,
                            "component_id": int(component_id),
                            "hotlist_id": int(hotlist_id),
                            "issue_type": issue_type,
                            "issue_priority": issue_priority,
                            "issue_severity": issue_severity,
                        }
                    },
                })
            mock_create_issue.assert_called_once()
            self.assertEqual(response.status_code, 201)
            issue_id = response.json.get("issue").get("id")
            issue_tracker_issue = models.IssuetrackerIssue.get_issue(
                "Issue", issue_id)
            self.assertTrue(issue_tracker_issue.enabled)
            self.assertEqual(issue_tracker_issue.title, title)
            self.assertEqual(issue_tracker_issue.component_id, component_id)
            self.assertEqual(issue_tracker_issue.hotlist_id, hotlist_id)
            self.assertEqual(issue_tracker_issue.issue_type, issue_type)
            self.assertEqual(issue_tracker_issue.issue_priority,
                             issue_priority)
            self.assertEqual(issue_tracker_issue.issue_severity,
                             issue_severity)

    def test_exclude_auditor(self):
        """Test 'exclude_auditor_emails' util."""
        audit = factories.AuditFactory()
        factories.AccessControlListFactory(
            ac_role=factories.AccessControlRoleFactory(name="Auditors"),
            person=factories.PersonFactory(email="*****@*****.**"),
            object_id=audit.id,
            object_type="Audit")

        result = integration_utils.exclude_auditor_emails(
            ["*****@*****.**", "*****@*****.**"])
        self.assertEqual(result, {
            "*****@*****.**",
        })

    @ddt.data(
        ({
            "description": "new description"
        }, {
            "comment": "Issue Description has been updated.\nnew description"
        }),
        ({
            "test_plan": "new test plan"
        }, {
            "comment":
            "Issue Remediation Plan has been updated.\nnew test plan"
        }),
        ({
            "issue_tracker": {
                "component_id": "123",
                "enabled": True
            }
        }, {
            "component_id": 123
        }),
        ({
            "issue_tracker": {
                "hotlist_id": "321",
                "enabled": True
            }
        }, {
            "hotlist_ids": [
                321,
            ]
        }),
        ({
            "issue_tracker": {
                "issue_priority": "P2",
                "enabled": True
            }
        }, {
            "priority": "P2"
        }),
        ({
            "issue_tracker": {
                "issue_severity": "S2",
                "enabled": True
            }
        }, {
            "severity": "S2"
        }),
        ({
            "issue_tracker": {
                "enabled": False
            }
        }, {
            "comment":
            "Changes to this GGRC object will no longer be "
            "tracked within this bug."
        }),
    )
    @ddt.unpack
    @mock.patch("ggrc.integrations.issues.Client.update_issue")
    def test_update_issue(self, issue_attrs, expected_query,
                          mock_update_issue):
        """Test updating issue tracker issue."""
        iti = factories.IssueTrackerIssueFactory(
            enabled=True, issue_tracked_obj=factories.IssueFactory())
        with mock.patch.object(settings, "ISSUE_TRACKER_ENABLED", True):
            self.api.put(iti.issue_tracked_obj, issue_attrs)
        mock_update_issue.assert_called_with(iti.issue_id, expected_query)

    @ddt.data(
        {"notes": "new notes"},
        {"end_date": "2018-07-15"},
        {"start_date": "2018-07-15"},
    )
    @mock.patch("ggrc.integrations.issues.Client.update_issue")
    def test_update_issue_with_untracked_fields(self, issue_attrs,
                                                mock_update_issue):
        """Test updating issue with fields which shouldn't be sync."""
        iti = factories.IssueTrackerIssueFactory(
            enabled=True, issue_tracked_obj=factories.IssueFactory())
        with mock.patch.object(settings, "ISSUE_TRACKER_ENABLED", True):
            self.api.put(iti.issue_tracked_obj, issue_attrs)
        mock_update_issue.assert_not_called()

    @mock.patch("ggrc.integrations.issues.Client.update_issue")
    def test_issue_tracker_error(self, update_issue_mock):
        """Test that issue tracker does not change state
       in case receiving an error."""
        iti = factories.IssueTrackerIssueFactory(
            enabled=True, issue_tracked_obj=factories.IssueFactory())
        update_issue_mock.side_effect = integrations_errors.HttpError("data")
        issue_attrs = {
            "issue_tracker": {
                "enabled": True,
                "hotlist_id": "123",
                "issue_id": iti.issue_id,
            }
        }
        with mock.patch.object(settings, "ISSUE_TRACKER_ENABLED", True),\
            mock.patch.object(all_models.IssuetrackerIssue,
                              "create_or_update_from_dict") as update_info_mock:
            self.api.put(iti.issue_tracked_obj, issue_attrs)

        # Check that "enabled" flag hasn't been changed.
        self.assertTrue("enabled" not in update_info_mock.call_args[0][1])

    @mock.patch("ggrc.integrations.issues.Client.update_issue")
    def test_delete_issue(self, mock_update_issue):
        """Test updating issue tracker issue when issue in GGRC has been deleted"""
        iti = factories.IssueTrackerIssueFactory(
            enabled=True, issue_tracked_obj=factories.IssueFactory())
        expected_query = {
            "comment":
            "GGRC object has been deleted. GGRC changes "
            "will no longer be tracked within this bug."
        }
        with mock.patch.object(settings, "ISSUE_TRACKER_ENABLED", True):
            self.api.delete(iti.issue_tracked_obj)
        mock_update_issue.assert_called_with(iti.issue_id, expected_query)

    @mock.patch.object(params_builder.BaseIssueTrackerParamsBuilder,
                       "get_ggrc_object_url",
                       return_value="http://issue_url.com")
    @mock.patch("ggrc.integrations.issues.Client.update_issue")
    def test_adding_comment_to_issue(self, update_issue_mock,
                                     url_builder_mock):
        """Test adding comment to issue."""
        iti = factories.IssueTrackerIssueFactory(
            enabled=True, issue_tracked_obj=factories.IssueFactory())
        comment = factories.CommentFactory(description="test comment")

        expected_result = {
            "comment":
            u"A new comment is added by 'Example User' to the 'Issue': "
            u"'test comment'.\nUse the following to link to get more "
            u"information from the GGRC 'Issue'. Link - "
            u"http://issue_url.com"
        }

        with mock.patch.object(settings, "ISSUE_TRACKER_ENABLED", True):
            self.api.post(
                all_models.Relationship, {
                    "relationship": {
                        "source": {
                            "id": iti.issue_tracked_obj.id,
                            "type": "Issue"
                        },
                        "destination": {
                            "id": comment.id,
                            "type": "comment"
                        },
                        "context": None
                    },
                })
        url_builder_mock.assert_called_once()
        update_issue_mock.assert_called_with(iti.issue_id, expected_result)
コード例 #20
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)
コード例 #21
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")
コード例 #22
0
class TestProposalRelationship(TestCase):
  """Test relationship for proposals."""

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

  @staticmethod
  def proposal_relationships(obj):
    """Get relationships between any Proposal and object.

    Args:
        obj: Instance of Proposalable object.

    Returns:
        Query which return relationship ids.
    """
    return db.session.query(all_models.Relationship.id).filter(
        sa.or_(
            sa.and_(
                all_models.Relationship.source_type == 'Proposal',
                all_models.Relationship.destination_type == obj.type,
            ),
            sa.and_(
                all_models.Relationship.source_type == obj.type,
                all_models.Relationship.destination_type == 'Proposal',
            )
        )
    )

  def create_proposal_for(self, obj):
    """Create Proposal for obj.

    Args:
        obj: Instance of Proposalable object.

    Returns:
        Response with result of Proposal creation.
    """
    response = self.api.post(
        all_models.Proposal,
        {
            "proposal": {
                "instance": {
                    "id": obj.id,
                    "type": obj.type,
                },
                "full_instance_content": obj.log_json(),
                "context": None,
            }
        }
    )
    self.assertEqual(201, response.status_code)
    return response

  @ddt.data("Risk", "Control")
  def test_create(self, model_name):
    """Test if relationship between {} and Proposal is created."""
    obj = factories.get_model_factory(model_name)()
    self.create_proposal_for(obj)
    self.assertEqual(1, self.proposal_relationships(obj).count())

  def test_create_on_post_only(self):
    """Test if proposal relationship is created on post only."""
    control = factories.ControlFactory()
    response = self.create_proposal_for(control)
    post_rels = self.proposal_relationships(control).all()

    proposal = all_models.Proposal.query.get(response.json["proposal"]["id"])
    # Invalidate ACR cache manually as after first post
    # it will in detached state
    flask.g.global_ac_roles = None
    response = self.api.put(
        proposal,
        {
            "proposal": {
                "status": all_models.Proposal.STATES.APPLIED,
                "apply_reason": "test"
            }
        }
    )
    self.assert200(response)
    put_rels = self.proposal_relationships(control).all()
    self.assertEqual(post_rels, put_rels)

  def test_rel_remove_parent(self):
    """Test if relationship will be removed if parent instance is removed."""
    control = factories.ControlFactory()
    self.create_proposal_for(control)
    self.assertEqual(1, self.proposal_relationships(control).count())

    response = self.api.delete(control)
    self.assert200(response)
    self.assertEqual(0, self.proposal_relationships(control).count())

  def test_rel_remove_proposal(self):
    """Test if relationship will be removed if proposal is removed."""
    control = factories.ControlFactory()
    response = self.create_proposal_for(control)
    self.assertEqual(1, self.proposal_relationships(control).count())

    proposal = all_models.Proposal.query.get(response.json["proposal"]["id"])
    response = self.api.delete(proposal)
    self.assert200(response)
    self.assertEqual(0, self.proposal_relationships(control).count())
コード例 #23
0
class TestReader(TestCase):
    """Test Assignable RBAC"""
    def setUp(self):
        super(TestReader, self).setUp()
        self.audit_id = factories.AuditFactory().id
        self.generator = Generator()
        self.api = Api()
        self.object_generator = ObjectGenerator()
        self.init_users()
        self.init_assignable()

    def init_users(self):
        """ Init users needed by the test cases """
        users = [("creator", "Creator"), ("reader", "Reader"),
                 ("editor", "Editor"), ("admin", "Administrator")]
        self.users = {}
        for (name, role) in users:
            _, user = self.object_generator.generate_person(
                data={"name": name}, user_role=role)
            self.users[name] = user

    def init_assignable(self):
        """Creates the assignable object used by all the tests"""
        self.api.set_user(self.users["editor"])
        response = self.api.post(
            all_models.Assessment, {
                "assessment": {
                    "title": "Assessment",
                    "context": None,
                    "audit": {
                        "id": self.audit_id,
                        "type": "Audit"
                    }
                }
            })
        obj_id = response.json.get("assessment").get("id")
        self.assertEqual(response.status_code, 201,
                         "Error setting up Assessment")
        self.obj_json = response.json
        self.obj = all_models.Assessment.query.get(obj_id)

    def _add_creator(self, asmnt, user):
        """Helper method for creating assignees on an object"""
        acr = all_models.AccessControlRole.query.filter(
            all_models.AccessControlRole.object_type == "Assessment",
            all_models.AccessControlRole.name == "Creators",
        ).first()
        return self.api.put(
            asmnt, {
                "access_control_list": [{
                    "ac_role_id": acr.id,
                    "person": {
                        "id": user.id
                    },
                    "type": "AccessControlList",
                }]
            })

    def test_basic_with_no_assignee(self):
        """Editor creates an Assessment, but doesn't assign Reader/Creator as
       assignee. Reader should have Read access, Creator should have no access
    """

        # Reader should have read access, but shouldn't be allowed to edit or
        # create another assingee
        self.api.set_user(self.users["reader"])
        response = self.api.get(all_models.Assessment, self.obj.id)
        self.assertEqual(response.status_code, 200)
        response = self.api.put(self.obj, self.obj_json)
        self.assertEqual(response.status_code, 403)
        response = self._add_creator(self.obj, self.users["reader"])
        self.assertEqual(response.status_code, 403)

        # Creator should have no access. We skip the put request because we can't
        # get the object etag.
        self.api.set_user(self.users["creator"])
        response = self.api.get(all_models.Assessment, self.obj.id)
        self.assertEqual(response.status_code, 403)
        response = self._add_creator(self.obj, self.users["reader"])
        self.assertEqual(response.status_code, 403)

    def test_basic_with_assignee(self):
        """Test if Reader/Creator have CRUD access once they become assignees"""

        # Admin adds reader as an assignee
        self.api.set_user(self.users["admin"])
        response = self._add_creator(self.obj, self.users["reader"])
        self.assertEqual(response.status_code, 200)

        # Reader is now allowed to update the object
        self.api.set_user(self.users["reader"])
        response = self.api.get(all_models.Assessment, self.obj.id)
        self.assertEqual(response.status_code, 200)
        response = self.api.put(self.obj, response.json)
        self.assertEqual(response.status_code, 200)

        # Reader adds creator as an assignee
        response = self._add_creator(self.obj, self.users["creator"])
        self.assertEqual(response.status_code, 200)

        # Creator now has CRUD access
        self.api.set_user(self.users["creator"])
        response = self.api.get(all_models.Assessment, self.obj.id)
        self.assertEqual(response.status_code, 200)
        response = self.api.put(self.obj, response.json)

        # Creator should even be allowed to add new assignees
        response = self._add_creator(self.obj, self.users["admin"])
        self.assertEqual(response.status_code, 200)

    def test_read_of_mapped_objects(self):
        """Test if assignees get Read access on all mapped objects"""

        # Editor creates a System object and maps it to the assignable object
        self.api.set_user(self.users["editor"])
        response = self.api.post(
            all_models.System,
            {"system": {
                "title": "System",
                "context": None,
            }})
        system_id = response.json.get("system").get("id")
        system = all_models.System.query.get(system_id)
        self.api.post(
            all_models.Relationship, {
                "relationship": {
                    "source": {
                        "id": self.obj.id,
                        "type": "Assessment"
                    },
                    "destination": {
                        "id": system_id,
                        "type": "System"
                    },
                    "context": None
                },
            })

        # Since creator is not an assignee she should not have access to any of the
        # two objects
        self.api.set_user(self.users["creator"])
        response = self.api.get(all_models.Assessment, self.obj.id)
        self.assertEqual(response.status_code, 403)
        response = self.api.get(all_models.System, system_id)
        self.assertEqual(response.status_code, 403)

        # Editor adds creator as an assignee
        self.api.set_user(self.users["editor"])
        response = self._add_creator(self.obj, self.users["creator"])
        self.assertEqual(response.status_code, 200)

        # Creator should now have read access on the mapped object
        self.api.set_user(self.users["creator"])
        response = self.api.get(all_models.System, system_id)
        self.assertEqual(response.status_code, 200)

        # But he should still not be allowed to update
        response = self.api.put(system, response.json)
        self.assertEqual(response.status_code, 403)
コード例 #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")
コード例 #25
0
class TestCreatorAudit(TestCase):
    """Set up necessary objects and test Creator role with Audit roles"""
    def setUp(self):
        super(TestCreatorAudit, self).setUp()
        self.generator = Generator()
        self.api = Api()
        self.object_generator = ObjectGenerator()
        self.init_users()
        self.init_roles()
        self.init_test_cases()
        self.objects = {}

    def init_test_cases(self):
        """Create a dict of all possible test cases."""
        self.test_cases = {
            "Auditor": {
                "audit_role": "Auditor",
                "objects": {
                    "audit": {
                        "get": 200,
                        "put": 403,
                        "delete": 403
                    },
                    "mapped_Issue": {
                        "get": 200,
                        "put": 200,
                        "delete": 200
                    },
                    "unrelated_Issue": {
                        "get": 403,
                        "put": 403,
                        "delete": 403,
                        "map": 403,
                    },
                    "mapped_Assessment": {
                        "get": 200,
                        "put": 200,
                        "delete": 200
                    },
                    "unrelated_Assessment": {
                        "get": 403,
                        "put": 403,
                        "delete": 403,
                        "map": 403,
                    }
                }
            },
        }

    def init_roles(self):
        """Create a delete request for the given object."""
        response = self.api.get_query(all_models.Role, "")
        self.roles = {}
        for role in response.json.get("roles_collection").get("roles"):
            self.roles[role.get("name")] = role

    def init_users(self):
        """Create users used by test cases."""
        self.people = {}
        for name in ["creator", "notmapped", "mapped", "Auditor"]:
            _, user = self.object_generator.generate_person(
                data={"name": name}, user_role="Creator")
            self.people[name] = user

        _, user = self.object_generator.generate_person(
            data={"name": "editor"}, user_role="Editor")
        self.people["editor"] = user

    def delete(self, obj):
        """Create a delete request for the given object.

    Args:
        obj (model instance): target object to delete
    Returns:
        int: http response status code
    """
        return self.api.delete(obj).status_code

    def get(self, obj):
        """Create a get request for the given object.

    Args:
        obj (model instance): target object to get
    Returns:
        int: http response status code
    """
        return self.api.get(obj.__class__, obj.id).status_code

    def put(self, obj):
        """Create a put request for the given object.

    Args:
        obj (model instance): target object to put
    Returns:
        int: http response status code
    """
        response = self.api.get(obj.__class__, obj.id)
        if response.status_code == 200:
            return self.api.put(obj, response.json).status_code
        else:
            return response.status_code

    def map(self, dest):
        """Map audit to dest.

    Args:
        dest (model instance): target object to map to the audit
    Returns:
        int: http response status code
    """
        response = self.api.post(
            all_models.Relationship, {
                "relationship": {
                    "source": {
                        "id": self.objects["audit"].id,
                        "type": self.objects["audit"].type,
                    },
                    "destination": {
                        "id": dest.id,
                        "type": dest.type
                    },
                    "context": None
                },
            })
        return response.status_code

    def init_objects(self, test_case_name):
        """Create a Program, an Audit, and a Mapped object for the test case.

    Args:
        test_case_name (string): test case to init for
    """
        # Create a program
        dummy_audit = factories.AuditFactory()
        unrelated_audit = {
            "type": "Audit",
            "context_id": dummy_audit.context.id,
            "id": dummy_audit.id,
        }
        test_case = self.test_cases[test_case_name]
        editor = self.people.get('editor')
        self.api.set_user(editor)
        random_title = factories.random_str()
        response = self.api.post(all_models.Program, {
            "program": {
                "title": random_title,
                "context": None
            },
        })
        self.assertEqual(response.status_code, 201)
        program_id = response.json.get("program").get("id")
        self.objects["program"] = all_models.Program.query.get(program_id)
        response = self.api.post(
            all_models.Audit, {
                "audit": {
                    "title": random_title + " audit",
                    'program': {
                        'id': program_id
                    },
                    "status": "Planned",
                    "context": None
                }
            })
        self.assertEqual(response.status_code, 201)
        context = response.json.get("audit").get("context")
        audit_id = response.json.get("audit").get("id")
        self.objects["audit"] = all_models.Audit.query.get(audit_id)
        audits = {
            "mapped": {
                "type": "Audit",
                "context_id": context["id"],
                "id": audit_id,
            },
            "unrelated": unrelated_audit,
        }

        for prefix, audit_dict in audits.items():
            random_title = factories.random_str()

            response = self.api.post(all_models.Issue, {
                "issue": {
                    "title": random_title,
                    "context": None,
                },
            })
            self.assertEqual(response.status_code, 201)
            issue_id = response.json.get("issue").get("id")
            self.objects[prefix +
                         "_Issue"] = all_models.Issue.query.get(issue_id)

            response = self.api.post(
                all_models.Assessment, {
                    "assessment": {
                        "title": random_title,
                        "context": {
                            "type": "Context",
                            "id": audit_dict["context_id"],
                        },
                        "audit": audit_dict,
                    },
                })
            self.assertEqual(response.status_code, 201)
            assessment_id = response.json.get("assessment").get("id")
            self.objects[prefix + "_Assessment"] = \
                all_models.Assessment.query.get(assessment_id)

        self.assertEqual(self.map(self.objects["mapped_Issue"]), 201)
        self.assertEqual(self.map(self.objects["mapped_Assessment"]), 201)

        # Add roles to mapped users:
        if "audit_role" in test_case:
            person = self.people.get(test_case_name)
            role = self.roles[test_case["audit_role"]]
            response = self.api.post(
                all_models.UserRole, {
                    "user_role": {
                        "person": {
                            "id": person.id,
                            "type": "Person",
                            "href": "/api/people/{}".format(person.id),
                        },
                        "role": {
                            "type": "Role",
                            "href": "/api/roles/{}".format(role["id"]),
                            "id": role["id"],
                        },
                        "context": {
                            "type": "Context",
                            "id": context.get("id"),
                            "href": "/api/contexts/{}".format(
                                context.get("id"))
                        }
                    }
                })
            self.assertEqual(response.status_code, 201)

    def test_creator_audit_roles(self):
        """ Test creator role with all audit scoped roles """
        # Check permissions based on test_cases:
        errors = []
        for test_case in self.test_cases:
            self.init_objects(test_case)
            person = self.people.get(test_case)
            objects = self.test_cases.get(test_case).get('objects')
            self.api.set_user(person)
            for obj, actions in objects.iteritems():
                for action in ("map", "get", "put", "delete"):
                    if action not in actions:
                        continue
                    # reset sesion:
                    db.session.commit()
                    func = getattr(self, action)
                    res = func(self.objects[obj])
                    if res != actions[action]:
                        errors.append(
                            "{}: Tried {} on {}, but received {} instead of {}"
                            .format(test_case, action, obj, res,
                                    actions[action]))

        self.assertEqual(errors, [])
コード例 #26
0
class TestReviewApi(TestCase):
  """Base TestCase class proposal api tests."""

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

  def test_simple_get(self):
    """Test simple get"""
    with factories.single_commit():
      risk = factories.RiskFactory()
      review = factories.ReviewFactory(
          email_message="test email message",
          notification_type="email",
          reviewable=risk,
          status=all_models.Review.STATES.UNREVIEWED,
      )
    resp = self.api.get(all_models.Review, review.id)
    self.assert200(resp)
    self.assertIn("review", resp.json)
    resp_review = resp.json["review"]
    self.assertEqual(all_models.Review.STATES.UNREVIEWED,
                     resp_review["status"])
    self.assertEqual(all_models.Review.NotificationTypes.EMAIL_TYPE,
                     resp_review["notification_type"])
    self.assertEqual("test email message",
                     resp_review["email_message"])

  def test_collection_get(self):
    """Test simple collection get"""
    with factories.single_commit():
      review1 = factories.ReviewFactory(
          status=all_models.Review.STATES.UNREVIEWED
      )
      review2 = factories.ReviewFactory(
          status=all_models.Review.STATES.REVIEWED
      )

    resp = self.api.get_collection(all_models.Review,
                                   [review1.id, review2.id])
    self.assert200(resp)
    self.assertIn("reviews_collection", resp.json)
    self.assertIn("reviews", resp.json["reviews_collection"])
    self.assertEquals(2, len(resp.json["reviews_collection"]["reviews"]))

  def test_create_review(self):
    """Create review via API, check that single relationship is created"""
    program = factories.ProgramFactory()
    program_id = program.id
    resp = self.api.post(
        all_models.Review,
        {
            "review": {
                "reviewable": {
                    "type": program.type,
                    "id": program.id,
                },
                "context": None,
                "notification_type": "email",
                "status": all_models.Review.STATES.UNREVIEWED,
                "access_control_list": build_reviewer_acl()
            },
        },
    )
    self.assertEqual(201, resp.status_code)
    review_id = resp.json["review"]["id"]
    review = all_models.Review.query.get(review_id)
    self.assertEqual(all_models.Review.STATES.UNREVIEWED, review.status)
    self.assertEqual(program.type, review.reviewable_type)
    self.assertEqual(program_id, review.reviewable_id)

    control_review_rel_count = all_models.Relationship.query.filter(
        all_models.Relationship.source_id == review.id,
        all_models.Relationship.source_type == review.type,
        all_models.Relationship.destination_id == program_id,
        all_models.Relationship.destination_type == program.type,
    ).union(
        all_models.Relationship.query.filter(
            all_models.Relationship.destination_id == review.id,
            all_models.Relationship.destination_type == review.type,
            all_models.Relationship.source_id == program_id,
            all_models.Relationship.source_type == program.type,
        )
    ).count()
    self.assertEqual(1, control_review_rel_count)

  def test_delete_review(self):
    """Test delete review via API"""
    with factories.single_commit():
      risk = factories.RiskFactory()
      risk_id = risk.id
      review = factories.ReviewFactory(reviewable=risk)
      review_id = review.id
    resp = self.api.delete(review)
    self.assert200(resp)
    review = all_models.Review.query.get(review_id)
    risk = all_models.Risk.query.get(risk_id)

    self.assertIsNone(review)
    self.assertEquals(0, len(risk.related_objects(_types=["Review"])))

  def test_last_reviewed(self):
    """last_reviewed_by, last_reviewed_by should be set if reviewed"""
    risk = factories.RiskFactory()
    resp, review = self.generator.generate_object(
        all_models.Review,
        {
            "reviewable": {
                "type": risk.type,
                "id": risk.id,
            },
            "context": None,
            "status": all_models.Review.STATES.UNREVIEWED,
            "access_control_list": build_reviewer_acl(),
            "notification_type": all_models.Review.NotificationTypes.EMAIL_TYPE
        },
    )
    review_id = review.id
    resp = self.api.put(
        review,
        {
            "status": all_models.Review.STATES.REVIEWED,
        },
    )
    self.assert200(resp)
    self.assertIsNotNone(resp.json["review"]["last_reviewed_by"])
    self.assertIsNotNone(resp.json["review"]["last_reviewed_at"])

    review = all_models.Review.query.get(review_id)
    self.assertIsNotNone(review.last_reviewed_by)
    self.assertIsNotNone(review.last_reviewed_at)

  def test_reviewable_revisions(self):
    """Check that proper revisions are created"""
    program = factories.ProgramFactory()
    resp, review = self.generator.generate_object(
        all_models.Review,
        {
            "reviewable": {
                "type": program.type,
                "id": program.id,
            },
            "context": None,
            "status": all_models.Review.STATES.UNREVIEWED,
            "access_control_list": build_reviewer_acl(),
            "notification_type": all_models.Review.NotificationTypes.EMAIL_TYPE
        },
    )
    program_id = program.id
    reviewable = review.reviewable

    program_revisions = all_models.Revision.query.filter_by(
        resource_id=program_id,
        resource_type=program.type
    ).order_by(
        all_models.Revision.id,
    ).all()
    self.assertEquals(2, len(program_revisions))
    self.assertEquals(all_models.Review.STATES.UNREVIEWED,
                      program_revisions[0].content["review_status"])
    self.assertEquals(all_models.Review.STATES.UNREVIEWED,
                      program_revisions[1].content["review_status"])
    resp = self.api.put(
        review,
        {
            "status": all_models.Review.STATES.REVIEWED,
        },
    )
    self.assert200(resp)

    program_revisions = all_models.Revision.query.filter_by(
        resource_id=program_id,
        resource_type=program.type
    ).order_by(
        all_models.Revision.id,
    ).all()
    self.assertEquals(3, len(program_revisions))
    self.assertEquals(all_models.Review.STATES.REVIEWED,
                      program_revisions[2].content["review_status"])

    resp = self.api.put(
        reviewable,
        {
            "description": "some new description"
        }
    )
    self.assert200(resp)

    program_revisions = all_models.Revision.query.filter_by(
        resource_id=program_id,
        resource_type=program.type
    ).order_by(
        all_models.Revision.id,
    ).all()
    self.assertEquals(4, len(program_revisions))
    self.assertEquals(all_models.Review.STATES.UNREVIEWED,
                      program_revisions[3].content["review_status"])
コード例 #27
0
class TestIssueIntegration(ggrc.TestCase):
    """Test set for IssueTracker integration functionality."""
    DEFAULT_ISSUE_ATTRS = {
        "title": "title1",
        "context": None,
        "status": "Draft",
        "enabled": True,
        "component_id": 1234,
        "hotlist_id": 4321,
        "issue_id": TICKET_ID,
        "issue_type": "Default Issue Type",
        "issue_priority": "P2",
        "issue_severity": "S1",
    }

    DEFAULT_TICKET_ATTRS = {
        "component_id": 1234,
        "hotlist_id": 4321,
        "issue_id": TICKET_ID,
        "status": "new",
        "issue_type": "Default Issue type",
        "issue_priority": "P1",
        "issue_severity": "S2",
        "title": "test title",
        "verifier": "*****@*****.**",
        "assignee": "*****@*****.**",
        "ccs": ["*****@*****.**"],
    }

    # pylint: disable=invalid-name

    def setUp(self):
        # pylint: disable=super-on-old-class
        super(TestIssueIntegration, self).setUp()
        self.api = Api()
        self.client.get("/login")

    @mock.patch("ggrc.integrations.issues.Client.create_issue",
                return_value={"issueId": "issueId"})
    @mock.patch.object(settings, "ISSUE_TRACKER_ENABLED", True)
    def test_create_issue_tracker_info(self, mock_create_issue):
        """Test creation issue tracker issue for Issue object."""
        component_id = "1234"
        hotlist_id = "4321"
        issue_type = "Default Issue type"
        issue_priority = "P2"
        issue_severity = "S1"
        title = "test title"

        with mock.patch.object(integration_utils,
                               "exclude_auditor_emails",
                               return_value={
                                   u"*****@*****.**",
                               }):
            response = self.api.post(
                all_models.Issue, {
                    "issue": {
                        "title": title,
                        "context": None,
                        "issue_tracker": {
                            "enabled": True,
                            "component_id": int(component_id),
                            "hotlist_id": int(hotlist_id),
                            "issue_type": issue_type,
                            "issue_priority": issue_priority,
                            "issue_severity": issue_severity,
                        }
                    },
                })
            mock_create_issue.assert_called_once()
            self.assertEqual(response.status_code, 201)
            issue_id = response.json.get("issue").get("id")
            issue_tracker_issue = models.IssuetrackerIssue.get_issue(
                "Issue", issue_id)
            self.assertTrue(issue_tracker_issue.enabled)
            self.assertEqual(issue_tracker_issue.title, title)
            self.assertEqual(issue_tracker_issue.component_id, component_id)
            self.assertEqual(issue_tracker_issue.hotlist_id, hotlist_id)
            self.assertEqual(issue_tracker_issue.issue_type, issue_type)
            self.assertEqual(issue_tracker_issue.issue_priority,
                             issue_priority)
            self.assertEqual(issue_tracker_issue.issue_severity,
                             issue_severity)

    def test_exclude_auditor(self):
        """Test 'exclude_auditor_emails' util."""
        audit = factories.AuditFactory()
        person = factories.PersonFactory(email="*****@*****.**")
        audit.add_person_with_role_name(person, "Auditors")
        db.session.commit()

        result = integration_utils.exclude_auditor_emails(
            ["*****@*****.**", "*****@*****.**"])
        self.assertEqual(result, {
            "*****@*****.**",
        })

    @ddt.data(
        ({
            "description": "new description"
        }, {
            "comment": "Issue Description has been updated.\nnew description"
        }),
        ({
            "test_plan": "new test plan"
        }, {
            "comment":
            "Issue Remediation Plan has been updated.\nnew test plan"
        }),
        ({
            "issue_tracker": {
                "component_id": "123",
                "enabled": True,
                "issue_id": TICKET_ID
            }
        }, {
            "component_id": 123
        }),
        ({
            "issue_tracker": {
                "hotlist_id": "321",
                "enabled": True,
                "issue_id": TICKET_ID
            }
        }, {
            "hotlist_ids": [
                321,
            ]
        }),
        ({
            "issue_tracker": {
                "issue_priority": "P2",
                "enabled": True,
                "issue_id": TICKET_ID
            }
        }, {
            "priority": "P2"
        }),
        ({
            "issue_tracker": {
                "issue_severity": "S2",
                "enabled": True,
                "issue_id": TICKET_ID
            }
        }, {
            "severity": "S2"
        }),
        ({
            "issue_tracker": {
                "enabled": False,
                "hotlist_ids": [
                    999,
                ],
                "issue_id": TICKET_ID
            }
        }, {
            "comment":
            "Changes to this GGRC object will no longer be "
            "tracked within this bug."
        }),
        ({
            "issue_tracker": {
                "title": "test_iti_title",
                "enabled": True,
                "issue_id": TICKET_ID
            }
        }, {
            "title": "test_iti_title"
        }),
    )
    @ddt.unpack
    @mock.patch("ggrc.integrations.issues.Client.update_issue")
    def test_update_issue(self, issue_attrs, expected_query,
                          mock_update_issue):
        """Test updating issue tracker issue."""
        iti = factories.IssueTrackerIssueFactory(
            enabled=True,
            issue_id=TICKET_ID,
            issue_tracked_obj=factories.IssueFactory())

        with mock.patch.object(settings, "ISSUE_TRACKER_ENABLED", True):
            self.api.put(iti.issue_tracked_obj, issue_attrs)
        mock_update_issue.assert_called_with(iti.issue_id, expected_query)

    @ddt.data(
        {"notes": "new notes"},
        {"end_date": "2018-07-15"},
        {"start_date": "2018-07-15"},
    )
    @mock.patch("ggrc.integrations.issues.Client.update_issue")
    def test_update_issue_with_untracked_fields(self, issue_attrs,
                                                mock_update_issue):
        """Test updating issue with fields which shouldn't be sync."""
        iti = factories.IssueTrackerIssueFactory(
            enabled=True, issue_tracked_obj=factories.IssueFactory())
        with mock.patch.object(settings, "ISSUE_TRACKER_ENABLED", True):
            self.api.put(iti.issue_tracked_obj, issue_attrs)
        mock_update_issue.assert_not_called()

    def request_payload_builder(self, issue_attrs):
        """Build payload for POST request to Issue Tracker"""
        payload_attrs = dict(self.DEFAULT_ISSUE_ATTRS, **issue_attrs)
        payload = {
            "issue": {
                "title": payload_attrs["title"],
                "context": payload_attrs["context"],
                "status": payload_attrs["status"],
                "issue_tracker": {
                    "enabled": payload_attrs["enabled"],
                    "component_id": payload_attrs["component_id"],
                    "hotlist_id": payload_attrs["hotlist_id"],
                    "issue_id": payload_attrs["issue_id"],
                    "issue_type": payload_attrs["issue_type"],
                    "issue_priority": payload_attrs["issue_priority"],
                    "issue_severity": payload_attrs["issue_severity"],
                    "title": payload_attrs["title"],
                }
            }
        }
        return payload

    def put_request_payload_builder(self, issue_attrs):
        """Build payload for PUT request to Issue Tracker"""
        payload_attrs = dict(self.DEFAULT_ISSUE_ATTRS, **issue_attrs)
        payload = {
            "issue_tracker": {
                "enabled": payload_attrs["enabled"],
                "component_id": payload_attrs["component_id"],
                "hotlist_id": payload_attrs["hotlist_id"],
                "issue_id": payload_attrs["issue_id"],
                "issue_type": payload_attrs["issue_type"],
                "issue_priority": payload_attrs["issue_priority"],
                "issue_severity": payload_attrs["issue_severity"],
                "title": payload_attrs["title"],
            }
        }
        return payload

    def response_payload_builder(self, ticket_attrs):
        """Build payload for response from Issue Tracker via get_issue method"""
        payload_attrs = dict(self.DEFAULT_TICKET_ATTRS, **ticket_attrs)
        payload = {
            "issueState": {
                "component_id": payload_attrs["component_id"],
                "hotlist_id": payload_attrs["hotlist_id"],
                "issue_id": payload_attrs["issue_id"],
                "status": payload_attrs["status"],
                "issue_type": payload_attrs["issue_type"],
                "issue_priority": payload_attrs["issue_priority"],
                "issue_severity": payload_attrs["issue_severity"],
                "title": payload_attrs["title"],
                "verifier": payload_attrs["verifier"],
                "assignee": payload_attrs["assignee"],
                "ccs": payload_attrs["ccs"],
            }
        }
        return payload

    def check_issuetracker_issue_fields(self, obj, issue_tracker_issue,
                                        issue_attrs,
                                        issue_tracker_ticket_attrs):
        """Checks issuetracker_issue were updated correctly.

    Make assertions to check if issue tracker fields were updated according
    our business logic.
    For Issue model we should get title, component_id, hotlist_id,
    priority, severity, issue_id and issue_type from GGRC and status from
    Issue Tracker.
    """

        self.assertTrue(issue_tracker_issue.enabled)

        # According to our business logic these attributes should be taken
        # from issue information
        self.assertEqual(issue_tracker_issue.title,
                         issue_attrs["issue"]["title"])
        self.assertEqual(int(issue_tracker_issue.component_id),
                         issue_attrs["issue"]["issue_tracker"]["component_id"])
        self.assertEqual(int(issue_tracker_issue.hotlist_id),
                         issue_attrs["issue"]["issue_tracker"]["hotlist_id"])
        self.assertEqual(
            issue_tracker_issue.issue_priority,
            issue_attrs["issue"]["issue_tracker"]["issue_priority"])
        self.assertEqual(
            issue_tracker_issue.issue_severity,
            issue_attrs["issue"]["issue_tracker"]["issue_severity"])
        self.assertEqual(int(issue_tracker_issue.issue_id),
                         issue_attrs["issue"]["issue_tracker"]["issue_id"])
        self.assertEqual(issue_tracker_issue.issue_type,
                         issue_attrs["issue"]["issue_tracker"]["issue_type"])

        # These attributes should be taken from ticket information
        ticket_status = issue_tracker_ticket_attrs["issueState"]["status"]
        ticket_mapped_status = ISSUE_STATUS_MAPPING[ticket_status]
        self.assertEqual(obj.status, ticket_mapped_status)

    @ddt.data(
        ({
            "title": "first_title"
        }, {
            "title": "other_title"
        }),
        ({
            "issue_type": "type1"
        }, {
            "issue_type": "process"
        }),
        ({
            "issue_severity": "S0"
        }, {
            "issue_severity": "S1"
        }),
        ({
            "issue_priority": "P0"
        }, {
            "issue_priority": "P1"
        }),
        ({
            "hotlist_id": 1234
        }, {
            "hotlist_id": 4321
        }),
        ({
            "component_id": 1234
        }, {
            "component_id": 4321
        }),
        ({
            "status": "Draft"
        }, {
            "status": "fixed"
        }),
    )
    @ddt.unpack
    @mock.patch("ggrc.integrations.issues.Client.update_issue")
    @mock.patch.object(settings, "ISSUE_TRACKER_ENABLED", True)
    def test_new_issue_linking(self, issue_attrs, ticket_attrs, update_mock):
        """Test linking new Issue to IssueTracker ticket sets correct fields"""
        issue_request_payload = self.request_payload_builder(issue_attrs)
        response_payload = self.response_payload_builder(ticket_attrs)
        with mock.patch("ggrc.integrations.issues.Client.get_issue",
                        return_value=response_payload) as get_mock:
            with mock.patch.object(integration_utils,
                                   "exclude_auditor_emails",
                                   return_value={
                                       u"*****@*****.**",
                                   }):
                response = self.api.post(all_models.Issue,
                                         issue_request_payload)
            get_mock.assert_called_once()
        update_mock.assert_called_once()

        self.assertEqual(response.status_code, 201)
        issue_id = response.json.get("issue").get("id")
        issue_tracker_issue = models.IssuetrackerIssue.get_issue(
            "Issue", issue_id)
        issue = all_models.Issue.query.filter_by(id=issue_id).first()
        self.check_issuetracker_issue_fields(issue, issue_tracker_issue,
                                             issue_request_payload,
                                             response_payload)

    @mock.patch("ggrc.integrations.issues.Client.update_issue")
    @mock.patch.object(settings, "ISSUE_TRACKER_ENABLED", True)
    def test_people_merge_after_linking(self, update_mock):
        """Test people roles were updated while linking new ticket"""
        ticket_attrs = {
            "verifier": "*****@*****.**",
            "assignee": "*****@*****.**",
            "ccs": ["*****@*****.**", "*****@*****.**"],
        }
        with factories.single_commit():
            factories.PersonFactory(email="*****@*****.**")
            factories.PersonFactory(email="*****@*****.**")
            for email in ["*****@*****.**", "*****@*****.**"]:
                factories.PersonFactory(email=email)

        issue_request_payload = self.request_payload_builder({})
        response_payload = self.response_payload_builder(ticket_attrs)
        with mock.patch("ggrc.integrations.issues.Client.get_issue",
                        return_value=response_payload) as get_mock:
            with mock.patch.object(integration_utils,
                                   "exclude_auditor_emails",
                                   return_value={
                                       u"*****@*****.**",
                                   }):
                response = self.api.post(all_models.Issue,
                                         issue_request_payload)
            get_mock.assert_called_once()
        update_mock.assert_called_once()

        self.assertEqual(response.status_code, 201)
        issue_id = response.json.get("issue").get("id")
        issue = all_models.Issue.query.filter_by(id=issue_id).first()

        admins = [
            person.email for person in issue.get_persons_for_rolename("Admin")
        ]
        primary = [
            person.email
            for person in issue.get_persons_for_rolename("Primary Contacts")
        ]
        secondary = [
            person.email
            for person in issue.get_persons_for_rolename("Secondary Contacts")
        ]
        # assert ticket roles were added to Issue
        self.assertIn("*****@*****.**", admins)
        self.assertIn("*****@*****.**", primary)
        for person in ["*****@*****.**", "*****@*****.**"]:
            self.assertIn(person, secondary)

    @mock.patch("ggrc.integrations.issues.Client.update_issue")
    @mock.patch.object(settings, "ISSUE_TRACKER_ENABLED", True)
    def test_existing_issue_link(self, update_mock):
        """Test Issue link to another ticket """
        iti = factories.IssueTrackerIssueFactory(
            enabled=True,
            issue_id=TICKET_ID,
            issue_tracked_obj=factories.IssueFactory())
        new_ticket_id = TICKET_ID + 1
        new_data = {"issue_id": new_ticket_id}
        issue_request_payload = self.put_request_payload_builder(new_data)
        response_payload = self.response_payload_builder(new_data)

        with mock.patch("ggrc.integrations.issues.Client.get_issue",
                        return_value=response_payload) as get_mock:
            with mock.patch.object(integration_utils,
                                   "exclude_auditor_emails",
                                   return_value={
                                       u"*****@*****.**",
                                   }):
                response = self.api.put(iti.issue_tracked_obj,
                                        issue_request_payload)
            get_mock.assert_called_once()

        self.assert200(response)

        # check if data was changed in our DB
        issue_id = response.json.get("issue").get("id")
        issue_tracker_issue = models.IssuetrackerIssue.get_issue(
            "Issue", issue_id)
        self.assertEqual(int(issue_tracker_issue.issue_id), new_ticket_id)

        # check detach comment was sent
        detach_comment_template = params_builder.IssueParamsBuilder.DETACH_TMPL
        comment = detach_comment_template.format(new_ticket_id=new_ticket_id)
        expected_args = (TICKET_ID, {"status": "OBSOLETE", "comment": comment})
        self.assertEqual(expected_args, update_mock.call_args[0])

    @mock.patch.object(settings, "ISSUE_TRACKER_ENABLED", True)
    def test_already_linked_ticket(self):
        """Test Issue without IT couldn't be linked to already linked ticket"""
        with factories.single_commit():
            factories.IssueTrackerIssueFactory(
                enabled=True,
                issue_id=TICKET_ID,
                issue_tracked_obj=factories.IssueFactory())
            new_issue = factories.IssueFactory()

        issue_data = {"issue_id": TICKET_ID}
        issue_request_payload = self.put_request_payload_builder(issue_data)

        response = self.api.put(new_issue, issue_request_payload)
        self.assert200(response)
        self.assertTrue(response.json["issue"]["issue_tracker"]["_warnings"])
        issue_id = response.json.get("issue").get("id")
        issue_tracker_issue = models.IssuetrackerIssue.get_issue(
            "Issue", issue_id)
        self.assertFalse(issue_tracker_issue)

    @mock.patch("ggrc.integrations.issues.Client.update_issue")
    @mock.patch.object(settings, "ISSUE_TRACKER_ENABLED", True)
    def test_creating_new_ticket_for_linked_issue(self, update_mock):
        """Test create new ticket for already linked issue"""
        iti = factories.IssueTrackerIssueFactory(
            enabled=True,
            issue_id=TICKET_ID,
            issue_tracked_obj=factories.IssueFactory())
        new_data = {"issue_id": ''}
        issue_request_payload = self.put_request_payload_builder(new_data)

        with mock.patch.object(integration_utils,
                               "exclude_auditor_emails",
                               return_value={
                                   u"*****@*****.**",
                               }):
            with mock.patch("ggrc.integrations.issues.Client.create_issue",
                            return_value={"issueId":
                                          TICKET_ID + 1}) as create_mock:
                response = self.api.put(iti.issue_tracked_obj,
                                        issue_request_payload)

        self.assert200(response)

        # Detach comment should be sent to previous ticket
        update_mock.assert_called_once()
        self.assertEqual(TICKET_ID, update_mock.call_args[0][0])
        create_mock.assert_called_once()

        # check if data was changed in our DB
        issue_id = response.json.get("issue").get("id")
        issue_tracker_issue = models.IssuetrackerIssue.get_issue(
            "Issue", issue_id)
        self.assertNotEqual(int(issue_tracker_issue.issue_id), TICKET_ID)

    @mock.patch("ggrc.integrations.issues.Client.update_issue")
    def test_issue_tracker_error(self, update_issue_mock):
        """Test issue tracker errors.

    Issue in Issue tracker doesn't change state
    in case receiving an error.
    """
        iti = factories.IssueTrackerIssueFactory(
            enabled=True, issue_tracked_obj=factories.IssueFactory())
        update_issue_mock.side_effect = integrations_errors.HttpError("data")
        issue_attrs = {
            "issue_tracker": {
                "enabled": True,
                "hotlist_id": "123",
                "issue_id": iti.issue_id,
            }
        }
        with mock.patch.object(settings, "ISSUE_TRACKER_ENABLED", True),\
            mock.patch.object(all_models.IssuetrackerIssue,
                              "create_or_update_from_dict") as update_info_mock:
            self.api.put(iti.issue_tracked_obj, issue_attrs)

        # Check that "enabled" flag hasn't been changed.
        self.assertTrue("enabled" not in update_info_mock.call_args[0][1])

    @mock.patch("ggrc.integrations.issues.Client.update_issue")
    def test_delete_issue(self, mock_update_issue):
        """Test updating issue tracker issue when issue in GGRC has been deleted"""
        iti = factories.IssueTrackerIssueFactory(
            enabled=True, issue_tracked_obj=factories.IssueFactory())
        expected_query = {
            "comment":
            "GGRC object has been deleted. GGRC changes "
            "will no longer be tracked within this bug."
        }
        with mock.patch.object(settings, "ISSUE_TRACKER_ENABLED", True):
            self.api.delete(iti.issue_tracked_obj)
        mock_update_issue.assert_called_with(iti.issue_id, expected_query)

    @ddt.data("test comment", "  \n\ntest comment\n\n" "  \n\n  \n\n")
    @mock.patch.object(params_builder.BaseIssueTrackerParamsBuilder,
                       "get_ggrc_object_url",
                       return_value="http://issue_url.com")
    @mock.patch("ggrc.integrations.issues.Client.update_issue")
    def test_adding_comment_to_issue(self, desc, update_issue_mock,
                                     url_builder_mock):
        """Test adding comment to issue."""
        role = all_models.Role.query.filter(
            all_models.Role.name == "Administrator").one()
        with factories.single_commit():
            client_user = factories.PersonFactory(name="Test User")
            rbac_factories.UserRoleFactory(role=role, person=client_user)
        self.api.set_user(client_user)
        self.client.get("/login")
        iti = factories.IssueTrackerIssueFactory(
            enabled=True, issue_tracked_obj=factories.IssueFactory())
        comment = factories.CommentFactory(description=desc)
        builder_class = params_builder.BaseIssueTrackerParamsBuilder
        expected_result = {
            "comment":
            builder_class.COMMENT_TMPL.format(
                author=client_user.name,
                comment="test comment",
                model="Issue",
                link="http://issue_url.com",
            )
        }

        with mock.patch.object(settings, "ISSUE_TRACKER_ENABLED", True):
            self.api.post(
                all_models.Relationship, {
                    "relationship": {
                        "source": {
                            "id": iti.issue_tracked_obj.id,
                            "type": "Issue"
                        },
                        "destination": {
                            "id": comment.id,
                            "type": "comment"
                        },
                        "context": None
                    },
                })
        url_builder_mock.assert_called_once()
        update_issue_mock.assert_called_with(iti.issue_id, expected_result)

    @mock.patch("ggrc.integrations.issues.Client.update_issue")
    @mock.patch.object(settings, "ISSUE_TRACKER_ENABLED", True)
    def test_mapping_document(self, update_issue_mock):
        """Test map document action on issue.

    Issue in Issue tracker shouldn't be updated when reference url has been
    added to issue.
    """
        iti = factories.IssueTrackerIssueFactory(
            enabled=True, issue_tracked_obj=factories.IssueFactory())
        document = factories.DocumentFactory()
        response = self.api.put(
            iti.issue_tracked_obj, {
                "actions": {
                    "add_related": [
                        {
                            "id": document.id,
                            "type": "Document",
                        },
                    ]
                }
            })
        self.assert200(response)

        relationship = all_models.Relationship.query.filter(
            all_models.Relationship.source_type == "Issue",
            all_models.Relationship.source_id == response.json["issue"]["id"],
        ).order_by(all_models.Relationship.id.desc()).first()

        self.assertEqual(relationship.destination_id, document.id)
        self.assertEqual(relationship.source_id, iti.issue_tracked_obj.id)

        # Check that issue in Issue Tracker hasn't been updated.
        update_issue_mock.assert_not_called()
コード例 #28
0
class TestWorkflowsApiPost(TestCase):
  """Test class for ggrc workflow api post action."""

  def setUp(self):
    super(TestWorkflowsApiPost, self).setUp()
    self.api = Api()
    self.generator = wf_generator.WorkflowsGenerator()
    self.wf_admin_id = all_models.Person.query.first().id
    with factories.single_commit():
      self.people_ids = [factories.PersonFactory().id for _ in xrange(6)]

  def tearDown(self):
    pass

  def _delete_and_check_related_acl(self, related_model, exp_acl_count,
                                    is_deleted):
    """Delete related model and check remaining ACL count.

    Args:
        related_model: related model class
        exp_acl_count: expected related ACL count after delete operation
        is_deleted: is related object already deleted
    """
    if is_deleted:
      related_count = related_model.query.count()
      self.assertEqual(related_count, 0)
    else:
      related = related_model.query.one()
      response = self.api.delete(related)
      self.assert200(response)

    related_acl_count = all_models.AccessControlList.query.filter(
        all_models.AccessControlList.object_type == related_model.__name__
    ).count()
    self.assertEqual(related_acl_count, 0)

    bg_task_count = all_models.AccessControlList.query.filter(
        all_models.AccessControlList.object_type == "BackgroundTask"
    ).count()

    all_acl_count = all_models.AccessControlList.query.count()
    self.assertEqual(all_acl_count - bg_task_count, exp_acl_count)

  def test_acl_on_object_deletion(self):
    """Test related ACL records removed on related object delete"""
    self._create_propagation_acl_test_data()
    acl_count = all_models.AccessControlList.query.count()
    self.assertNotEqual(acl_count, 0)

    admin = all_models.Person.query.get(1)
    self.api.set_user(admin)

    related_models = (
        (all_models.CycleTaskEntry, 18, False),
        (all_models.TaskGroup, 12, False),
        (all_models.TaskGroupTask, 12, True),
        (all_models.Cycle, 3, False),
        (all_models.CycleTaskGroup, 3, True),
        (all_models.CycleTaskGroupObjectTask, 3, True),
    )
    for related_model, acl_count, is_deleted in related_models:
      self._delete_and_check_related_acl(related_model, acl_count, is_deleted)

  def test_acl_on_workflow_delete(self):
    """Test related ACL records removed on Workflow delete"""
    self._create_propagation_acl_test_data()
    acl_count = all_models.AccessControlList.query.count()
    self.assertNotEqual(acl_count, 0)

    admin = all_models.Person.query.get(1)
    self.api.set_user(admin)
    workflow = all_models.Workflow.query.one()
    response = self.api.delete(workflow)
    self.assert200(response)

    acl_count = all_models.AccessControlList.query.count()
    bg_acl_count = all_models.AccessControlList.query.filter(
        all_models.AccessControlList.object_type == "BackgroundTask"
    ).count()
    self.assertEqual(acl_count, bg_acl_count)

  def test_acl_for_new_related_object(self):
    """Test Workflow ACL propagation for new related objects."""
    data = self.get_workflow_dict()
    acl_map = {
        self.people_ids[0]: WF_ROLES['Admin'],
        self.people_ids[1]: WF_ROLES['Workflow Member'],
    }
    data["workflow"]["access_control_list"] = acl_helper.get_acl_list(acl_map)
    data["workflow"]["unit"] = "week"
    data["workflow"]["repeat_every"] = 1
    response = self.api.post(all_models.Workflow, data)
    self.assertEqual(response.status_code, 201)

    data = self.get_task_group_dict(response.json["workflow"])
    data["task_group"]["contact"]["id"] = self.people_ids[2]
    data["task_group"]["contact"]["href"] = "/api/people/{}".format(
        self.people_ids[2])
    response = self.api.post(all_models.TaskGroup, data)
    self.assertEqual(response.status_code, 201)

    task_group = all_models.TaskGroup.eager_query().one()
    data = self.get_task_dict(task_group)
    data["task_group_task"]["start_date"] = "2018-01-04"
    data["task_group_task"]["end_date"] = "2018-01-05"
    response = self.api.post(all_models.TaskGroupTask, data)
    self.assertEqual(response.status_code, 201)

    workflow = all_models.Workflow.query.one()
    with freezegun.freeze_time("2018-01-05"):  # Generate 1 cycle
      self.generator.activate_workflow(workflow)

    cycle_task = all_models.CycleTaskGroupObjectTask.query.one()
    cycle = all_models.Cycle.query.one()
    data = self.get_comment_dict(cycle_task, cycle)
    response = self.api.post(all_models.CycleTaskEntry, data)
    self.assertEqual(response.status_code, 201)

    self._check_propagated_acl(3)

  @ddt.data('Admin', 'Workflow Member')
  def test_tg_assignee(self, role_name):
    """Test TaskGroup assignee already has {0} role."""
    data = self.get_workflow_dict()
    init_acl = {
        self.people_ids[0]: WF_ROLES['Admin'],
        self.people_ids[1]: WF_ROLES[role_name],
    }
    data['workflow']['access_control_list'] = acl_helper.get_acl_list(init_acl)
    response = self.api.post(all_models.Workflow, data)
    self.assertEqual(response.status_code, 201)

    data = self.get_task_group_dict(response.json["workflow"])
    data["task_group"]["contact"]["id"] = self.people_ids[1]
    data["task_group"]["contact"]["href"] = "/api/people/{}".format(
        self.people_ids[1])
    response = self.api.post(all_models.TaskGroup, data)
    self.assertEqual(response.status_code, 201)

    workflow = all_models.Workflow.query.one()
    task_group = all_models.TaskGroup.query.one()

    actual_acl = all_models.AccessControlList.eager_query().filter(
        all_models.AccessControlList.person_id == task_group.contact_id,
    ).all()
    self.assertEqual(len(actual_acl), 2)

    expected = {
        role_name: (workflow.type, workflow.id),
        "{} Mapped".format(role_name): (task_group.type, task_group.id)
    }
    actual = {
        acl.ac_role.name: (acl.object_type, acl.object_id)
        for acl in actual_acl
    }
    self.assertDictEqual(expected, actual)

  def test_task_group_assignee_gets_workflow_member(self):  # noqa pylint: disable=invalid-name
    """Test TaskGroup assignee gets WorkflowMember role."""
    data = self.get_workflow_dict()
    init_acl = {
        self.people_ids[0]: WF_ROLES['Admin'],
        self.people_ids[1]: WF_ROLES['Workflow Member'],
    }
    data['workflow']['access_control_list'] = acl_helper.get_acl_list(init_acl)
    response = self.api.post(all_models.Workflow, data)
    self.assertEqual(response.status_code, 201)

    data = self.get_task_group_dict(response.json["workflow"])
    data["task_group"]["contact"]["id"] = self.people_ids[2]
    data["task_group"]["contact"]["href"] = "/api/people/{}".format(
        self.people_ids[2])
    response = self.api.post(all_models.TaskGroup, data)
    self.assertEqual(response.status_code, 201)

    workflow = all_models.Workflow.query.one()
    task_group = all_models.TaskGroup.query.one()

    parent_acl = all_models.AccessControlList.eager_query().filter(
        all_models.AccessControlList.person_id == task_group.contact_id,
        all_models.AccessControlList.object_type == workflow.type,
        all_models.AccessControlList.object_id == workflow.id
    ).one()
    tg_acl = all_models.AccessControlList.eager_query().filter(
        all_models.AccessControlList.person_id == task_group.contact_id,
        all_models.AccessControlList.object_type == task_group.type,
        all_models.AccessControlList.object_id == task_group.id
    ).one()
    self.assertEqual(parent_acl.ac_role.name, "Workflow Member")
    self.assertEqual(tg_acl.parent_id, parent_acl.id)
    self.assertEqual(tg_acl.ac_role.name, "Workflow Member Mapped")

  def _create_propagation_acl_test_data(self):  # noqa pylint: disable=invalid-name
    """Create objects for Workflow ACL propagation test."""
    with freezegun.freeze_time("2017-08-9"):
      with factories.single_commit():
        workflow = wf_factories.WorkflowFactory(
            title='wf1',
            unit=all_models.Workflow.WEEK_UNIT,
            is_verification_needed=True,
            repeat_every=1)
        wf_factories.TaskGroupTaskFactory(
            title='tgt1',
            task_group=wf_factories.TaskGroupFactory(
                title='tg1',
                context=factories.ContextFactory(),
                workflow=workflow
            ),
            # One cycle should be created
            start_date=datetime.date(2017, 8, 3),
            end_date=datetime.date(2017, 8, 7)
        )
      self.generator.activate_workflow(workflow)
      cycle = all_models.Cycle.query.one()
      cycle_task = all_models.CycleTaskGroupObjectTask.query.one()
      wf_factories.CycleTaskEntryFactory(
          cycle=cycle,
          cycle_task_group_object_task=cycle_task,
          description="Cycle task comment",
      )
      workflow = all_models.Workflow.query.one()
      acl_map = {
          self.people_ids[0]: WF_ROLES['Admin'],
          self.people_ids[1]: WF_ROLES['Workflow Member'],
          self.people_ids[2]: WF_ROLES['Workflow Member'],
      }
      put_params = {'access_control_list': acl_helper.get_acl_list(acl_map)}
      response = self.api.put(workflow, put_params)
      self.assert200(response)

  def _check_propagated_acl(self, roles_count):
    """Check Workflow propagated ACL records.

    Args:
        roles_count: roles' count created in test
    """
    related_objects = (
        (all_models.TaskGroup.query.one().id, all_models.TaskGroup.__name__),
        (all_models.TaskGroupTask.query.one().id,
         all_models.TaskGroupTask.__name__),
        (all_models.Cycle.query.one().id, all_models.Cycle.__name__),
        (all_models.CycleTaskGroup.query.one().id,
         all_models.CycleTaskGroup.__name__),
        (all_models.CycleTaskGroupObjectTask.query.one().id,
         all_models.CycleTaskGroupObjectTask.__name__),
        (all_models.CycleTaskEntry.query.one().id,
         all_models.CycleTaskEntry.__name__)
    )
    related_count = len(related_objects)
    bd_tasks_count = all_models.BackgroundTask.query.count()

    all_acl = [acl for acl in all_models.AccessControlList.eager_query().all()]
    self.assertEqual(
        len(all_acl),
        bd_tasks_count + roles_count + roles_count * related_count
    )

    workflow = all_models.Workflow.query.one()
    parent_acl, related_acl = [], []
    for acl in all_acl:
      if (not acl.parent_id and acl.object_id == workflow.id and
              acl.object_type == workflow.type):
        parent_acl.append(acl)
      else:
        related_acl.append(acl)

    result = defaultdict(set)
    for p_acl in parent_acl:
      for r_acl in related_acl:
        if (r_acl.ac_role.name == '{} Mapped'.format(p_acl.ac_role.name) and
                r_acl.parent_id == p_acl.id and
                r_acl.person_id == p_acl.person_id):
          result[p_acl.id].add((r_acl.object_id, r_acl.object_type))
    self.assertEqual(roles_count, len(result))

  def test_assign_workflow_acl(self):
    """Test propagation Workflow ACL roles on Workflow's update ACL records."""
    self._create_propagation_acl_test_data()
    self._check_propagated_acl(3)

  def test_unassign_workflow_acl(self):
    """Test propagation Workflow ACL roles on person unassigned."""
    self._create_propagation_acl_test_data()
    with freezegun.freeze_time("2017-08-9"):
      workflow = all_models.Workflow.query.one()
      acl_map = {
          self.people_ids[0]: WF_ROLES['Admin'],
          self.people_ids[1]: WF_ROLES['Workflow Member'],
      }
      put_params = {'access_control_list': acl_helper.get_acl_list(acl_map)}
      response = self.api.put(workflow, put_params)
      self.assert200(response)

    self._check_propagated_acl(2)

  def test_post_workflow_with_acl(self):
    """Test PUT workflow with ACL."""
    data = self.get_workflow_dict()

    exp_res = {
        self.wf_admin_id: WF_ROLES['Admin'],
        self.people_ids[0]: WF_ROLES['Admin'],
        self.people_ids[1]: WF_ROLES['Workflow Member'],
        self.people_ids[2]: WF_ROLES['Workflow Member'],
        self.people_ids[3]: WF_ROLES['Workflow Member']
    }
    data['workflow']['access_control_list'] = acl_helper.get_acl_list(exp_res)
    response = self.api.post(all_models.Workflow, data)
    self.assertEqual(response.status_code, 201)
    workflow = all_models.Workflow.eager_query().one()
    act_res = {acl.person_id: acl.ac_role_id
               for acl in workflow.access_control_list}
    self.assertDictEqual(exp_res, act_res)

  def test_update_workflow_acl_people(self):
    """Test PUT workflow with updated ACL."""
    data = self.get_workflow_dict()
    init_map = {
        self.wf_admin_id: WF_ROLES['Admin'],
        self.people_ids[0]: WF_ROLES['Workflow Member'],
    }
    data['workflow']['access_control_list'] = acl_helper.get_acl_list(init_map)
    response = self.api.post(all_models.Workflow, data)
    self.assertEqual(response.status_code, 201)
    exp_res = {
        self.people_ids[0]: WF_ROLES['Admin'],
        self.people_ids[1]: WF_ROLES['Admin'],
        self.people_ids[2]: WF_ROLES['Workflow Member'],
        self.people_ids[3]: WF_ROLES['Workflow Member'],
        self.people_ids[4]: WF_ROLES['Workflow Member']
    }
    workflow = all_models.Workflow.eager_query().one()
    put_params = {'access_control_list': acl_helper.get_acl_list(exp_res)}
    response = self.api.put(workflow, put_params)
    self.assert200(response)
    workflow = all_models.Workflow.eager_query().one()
    act_res = {acl.person_id: acl.ac_role_id
               for acl in workflow.access_control_list}
    self.assertDictEqual(exp_res, act_res)

  def test_send_invalid_data(self):
    """Test send invalid data on Workflow post."""
    data = self.get_workflow_dict()
    del data["workflow"]["title"]
    del data["workflow"]["context"]
    response = self.api.post(all_models.Workflow, data)
    self.assert400(response)
    # TODO: check why response.json["message"] is empty

  def test_create_one_time_workflows(self):
    """Test simple create one time Workflow over api."""
    data = self.get_workflow_dict()
    response = self.api.post(all_models.Workflow, data)
    self.assertEqual(response.status_code, 201)

  def test_create_weekly_workflow(self):
    """Test create valid weekly wf"""
    data = self.get_workflow_dict()
    data["workflow"]["repeat_every"] = 7
    data["workflow"]["unit"] = "day"
    data["workflow"]["title"] = "Weekly"
    response = self.api.post(all_models.Workflow, data)
    self.assertEqual(response.status_code, 201)

  def test_create_annually_workflow(self):
    """Test create valid annual wf"""
    data = self.get_workflow_dict()
    data["workflow"]["repeat_every"] = 12
    data["workflow"]["unit"] = "month"
    data["workflow"]["title"] = "Annually"
    response = self.api.post(all_models.Workflow, data)
    self.assertEqual(response.status_code, 201)

  @ddt.data("wrong value", 0, -4)
  def test_create_wrong_repeat_every_workflow(self, value):  # noqa pylint: disable=invalid-name
    """Test case for invalid repeat_every value"""
    data = self.get_workflow_dict()
    data["workflow"]["repeat_every"] = value
    data["workflow"]["unit"] = "month"
    data["workflow"]["title"] = "Wrong wf"
    response = self.api.post(all_models.Workflow, data)
    self.assertEqual(response.status_code, 400)

  def test_create_wrong_unit_workflow(self):
    """Test case for invalid unit value"""
    data = self.get_workflow_dict()
    data["workflow"]["repeat_every"] = 12
    data["workflow"]["unit"] = "wrong value"
    data["workflow"]["title"] = "Wrong wf"
    response = self.api.post(all_models.Workflow, data)
    self.assertEqual(response.status_code, 400)

  def test_create_task_group(self):
    """Test create task group over api."""
    wf_data = self.get_workflow_dict()
    wf_data["workflow"]["title"] = "Create_task_group"
    wf_response = self.api.post(all_models.Workflow, wf_data)

    data = self.get_task_group_dict(wf_response.json["workflow"])

    response = self.api.post(all_models.TaskGroup, data)
    self.assertEqual(response.status_code, 201)

  @staticmethod
  def get_workflow_dict():
    return {
        "workflow": {
            "custom_attribute_definitions": [],
            "custom_attributes": {},
            "title": "One_time",
            "description": "",
            "unit": None,
            "repeat_every": None,
            "notify_on_change": False,
            "task_group_title": "Task Group 1",
            "notify_custom_message": "",
            "is_verification_needed": True,
            "context": None,
        }
    }

  def get_task_group_dict(self, workflow):
    return {
        "task_group": {
            "custom_attribute_definitions": [],
            "custom_attributes": {},
            "_transient": {},
            "contact": {
                "id": self.wf_admin_id,
                "href": "/api/people/{}".format(self.wf_admin_id),
                "type": "Person"
            },
            "workflow": {
                "id": workflow["id"],
                "href": "/api/workflows/%d" % workflow["id"],
                "type": "Workflow"
            },
            "context": {
                "id": workflow["context"]["id"],
                "href": "/api/contexts/%d" % workflow["context"]["id"],
                "type": "Context"
            },
            "modal_title": "Create Task Group",
            "title": "Create_task_group",
            "description": "",
        }
    }

  def get_task_dict(self, task_group):
    return {
        "task_group_task": {
            "start_date": "2017-12-25",
            "end_date": "2017-12-31",
            "custom_attributes": {},
            "contact": {
                "id": self.wf_admin_id,
                "href": "/api/people/{}".format(self.wf_admin_id),
                "type": "Person"
            },
            "task_group": {
                "id": task_group.id,
                "href": "/api/task_groups/{}".format(task_group.id),
                "type": "TaskGroup"
            },
            "context": {
                "id": task_group.context_id,
                "href": "/api/contexts/{}".format(task_group.context_id),
                "type": "Context"
            },
            "title": "Create_task",
            "task_type": "text",
            "description": ""
        }
    }

  @staticmethod
  def get_comment_dict(cycle_task, cycle):
    return {
        "cycle_task_entry": {
            "custom_attributes": {},
            "cycle_task_group_object_task": {
                "id": cycle_task.id,
                "href": "/api/cycle_task_group_object_tasks/{}".format(
                    cycle_task.id),
                "type": "CycleTaskGroupObjectTask"
            },
            "cycle": {
                "id": cycle.id,
                "href": "/api/cycles/{}".format(cycle.id),
                "type": "Cycle"
            },
            "context": {
                "id": cycle.context_id,
                "href": "/api/contexts/{}".format(cycle.context_id),
                "type": "Context"
            },
            "is_declining_review": "",
            "description": "CT comment"
        }
    }

  @ddt.data({},
            {"repeat_every": 5, "unit": "month"})
  def test_repeat_multiplier_field(self, data):
    """Check repeat_multiplier is set to 0 after wf creation."""
    with factories.single_commit():
      workflow = wf_factories.WorkflowFactory(**data)
    workflow_id = workflow.id
    self.assertEqual(
        0, all_models.Workflow.query.get(workflow_id).repeat_multiplier)

  # TODO: Unskip in the patch 2
  @unittest.skip("Will be activated in patch 2")
  def test_change_to_one_time_wf(self):
    """Check repeat_every and unit can be set to Null only together."""
    with factories.single_commit():
      workflow = wf_factories.WorkflowFactory(repeat_every=12,
                                              unit="day")
    resp = self.api.put(workflow, {"repeat_every": None,
                                   "unit": None})
    self.assert200(resp)

  @ddt.data({"repeat_every": 5},
            {"unit": "month"})
  def test_change_repeat_every(self, data):
    """Check repeat_every or unit can not be changed once set."""
    with factories.single_commit():
      workflow = wf_factories.WorkflowFactory()
    resp = self.api.put(workflow, data)
    self.assert400(resp)

  def test_not_change_to_one_time_wf(self):
    """Check repeat_every or unit can't be set to Null separately.
    This test will be useful in the 2nd patch, where we allow to change
    WF setup
    """
    with factories.single_commit():
      workflow = wf_factories.WorkflowFactory(repeat_every=12,
                                              unit="day")
    resp = self.api.put(workflow, {"repeat_every": None})
    self.assert400(resp)
    resp = self.api.put(workflow, {"unit": None})
    self.assert400(resp)

  @ddt.data(True, False)
  def test_autogen_verification_flag(self, flag):
    """Check is_verification_needed flag for activate WF action."""
    with factories.single_commit():
      workflow = wf_factories.WorkflowFactory(is_verification_needed=flag)
      group = wf_factories.TaskGroupFactory(workflow=workflow)
      wf_factories.TaskGroupTaskFactory(task_group=group)
    data = [{
        "cycle": {
            "autogenerate": True,
            "isOverdue": False,
            "workflow": {
                "id": workflow.id,
                "type": "Workflow",
            },
            "context": {
                "id": workflow.context_id,
                "type": "Context",
            },
        }
    }]
    resp = self.api.send_request(
        self.api.client.post,
        api_link="/api/cycles",
        data=data)
    cycle_id = resp.json[0][1]["cycle"]["id"]
    self.assertEqual(
        flag, all_models.Cycle.query.get(cycle_id).is_verification_needed)

  @ddt.data(True, False)
  def test_verification_flag_positive(self, flag):  # noqa pylint: disable=invalid-name
    """is_verification_needed flag is changeable for DRAFT workflow."""
    with factories.single_commit():
      workflow = wf_factories.WorkflowFactory(is_verification_needed=flag)
    self.assertEqual(workflow.status, all_models.Workflow.DRAFT)
    workflow_id = workflow.id
    resp = self.api.put(workflow, {"is_verification_needed": not flag})
    self.assert200(resp)
    self.assertEqual(
        all_models.Workflow.query.get(workflow_id).is_verification_needed,
        not flag)

  @ddt.data(True, False)
  def test_verification_flag_negative(self, flag):
    """Test immutable verification flag on active workflows."""
    with freezegun.freeze_time("2017-08-10"):
      with factories.single_commit():
        workflow = wf_factories.WorkflowFactory(
            unit=all_models.Workflow.WEEK_UNIT,
            is_verification_needed=flag,
            repeat_every=1)
        wf_factories.TaskGroupTaskFactory(
            task_group=wf_factories.TaskGroupFactory(
                context=factories.ContextFactory(),
                workflow=workflow
            ),
            # Two cycles should be created
            start_date=datetime.date(2017, 8, 3),
            end_date=datetime.date(2017, 8, 7))
      workflow_id = workflow.id
      self.assertEqual(workflow.status, all_models.Workflow.DRAFT)
      self.generator.activate_workflow(workflow)
      workflow = all_models.Workflow.query.get(workflow_id)
      self.assertEqual(workflow.status, all_models.Workflow.ACTIVE)
      resp = self.api.put(workflow, {"is_verification_needed": not flag})
      self.assert400(resp)
      workflow = all_models.Workflow.query.get(workflow_id)
      self.assertEqual(workflow.is_verification_needed, flag)

      # End all current cycles
      for cycle in workflow.cycles:
        self.generator.modify_object(cycle, {'is_current': False})
      workflow = all_models.Workflow.query.filter(
          all_models.Workflow.id == workflow_id).first()
      self.assertEqual(workflow.status, all_models.Workflow.INACTIVE)
      resp = self.api.put(workflow, {"is_verification_needed": not flag})
      self.assert400(resp)
      workflow = all_models.Workflow.query.get(workflow_id)
      self.assertEqual(workflow.is_verification_needed, flag)

  @ddt.data(True, False)
  def test_not_change_vf_flag(self, flag):
    """Check is_verification_needed not change on update."""
    with factories.single_commit():
      workflow = wf_factories.WorkflowFactory(is_verification_needed=flag)
    workflow_id = workflow.id
    resp = self.api.put(workflow, {"is_verification_needed": flag})
    self.assert200(resp)
    self.assertEqual(
        flag,
        all_models.Workflow.query.get(workflow_id).is_verification_needed)

  @ddt.data(True, False, None)
  def test_create_vf_flag(self, flag):
    """Check is_verification_needed flag setup on create."""
    data = self.get_workflow_dict()
    if flag is None:
      data['workflow'].pop('is_verification_needed', None)
    else:
      data['workflow']['is_verification_needed'] = flag
    resp = self.api.post(all_models.Workflow, data)
    self.assertEqual(201, resp.status_code)
    workflow_id = resp.json['workflow']['id']
    self.assertEqual(
        flag if flag is not None else True,
        all_models.Workflow.query.get(workflow_id).is_verification_needed)
コード例 #29
0
ファイル: test_api_calls.py プロジェクト: zidarsk8/ggrc-core
class TestWorkflowsApiPost(TestCase):

  def setUp(self):
    super(TestWorkflowsApiPost, self).setUp()
    self.api = Api()

  def tearDown(self):
    pass

  def test_send_invalid_data(self):
    data = self.get_workflow_dict()
    del data["workflow"]["title"]
    del data["workflow"]["context"]
    response = self.api.post(all_models.Workflow, data)
    self.assert400(response)
    # TODO: check why response.json["message"] is empty

  def test_create_one_time_workflows(self):
    data = self.get_workflow_dict()
    response = self.api.post(all_models.Workflow, data)
    self.assertEqual(response.status_code, 201)

  def test_create_weekly_workflows(self):
    data = self.get_workflow_dict()
    data["workflow"]["frequency"] = "weekly"
    data["workflow"]["title"] = "Weekly"
    response = self.api.post(all_models.Workflow, data)
    self.assertEqual(response.status_code, 201)

  def test_create_monthly_workflows(self):
    data = self.get_workflow_dict()
    data["workflow"]["frequency"] = "monthly"
    data["workflow"]["title"] = "Monthly"
    response = self.api.post(all_models.Workflow, data)
    self.assertEqual(response.status_code, 201)

  def test_create_quarterly_workflows(self):
    data = self.get_workflow_dict()
    data["workflow"]["frequency"] = "quarterly"
    data["workflow"]["title"] = "Quarterly"
    response = self.api.post(all_models.Workflow, data)
    self.assertEqual(response.status_code, 201)

  def test_create_annually_workflows(self):
    data = self.get_workflow_dict()
    data["workflow"]["frequency"] = "annually"
    data["workflow"]["title"] = "Annually"
    response = self.api.post(all_models.Workflow, data)
    self.assertEqual(response.status_code, 201)

  def test_create_task_group(self):
    wf_data = self.get_workflow_dict()
    wf_data["workflow"]["title"] = "Create_task_group"
    wf_response = self.api.post(all_models.Workflow, wf_data)

    data = self.get_task_group_dict(wf_response.json["workflow"])

    response = self.api.post(all_models.TaskGroup, data)
    self.assertEqual(response.status_code, 201)

  # TODO: Api should be able to handle invalid data
  @unittest.skip("Not implemented.")
  def test_create_task_group_invalid_workflow_data(self):
    data = self.get_task_group_dict({"id": -1, "context": {"id": -1}})
    response = self.api.post(all_models.TaskGroup, data)
    self.assert400(response)

  @staticmethod
  def get_workflow_dict():
    data = {
        "workflow": {
            "custom_attribute_definitions": [],
            "custom_attributes": {},
            "title": "One_time",
            "description": "",
            "frequency": "one_time",
            "notify_on_change": False,
            "task_group_title": "Task Group 1",
            "notify_custom_message": "",
            "is_verification_needed": True,
            "owners": None,
            "context": None,
        }
    }
    return data

  @staticmethod
  def get_task_group_dict(workflow):
    data = {
        "task_group": {
            "custom_attribute_definitions": [],
            "custom_attributes": {},
            "_transient": {},
            "contact": {
                "id": 1,
                "href": "/api/people/1",
                "type": "Person"
            },
            "workflow": {
                "id": workflow["id"],
                "href": "/api/workflows/%d" % workflow["id"],
                "type": "Workflow"
            },
            "context": {
                "id": workflow["context"]["id"],
                "href": "/api/contexts/%d" % workflow["context"]["id"],
                "type": "Context"
            },
            "modal_title": "Create Task Group",
            "title": "Create_task_group",
            "description": "",
        }
    }
    return data

  @ddt.data(True, False)
  def test_autogen_verification_flag(self, flag):
    """Check is_verification_needed flag for activate WF action."""
    with factories.single_commit():
      workflow = wf_factories.WorkflowFactory(is_verification_needed=flag)
      group = wf_factories.TaskGroupFactory(workflow=workflow)
      wf_factories.TaskGroupTaskFactory(task_group=group)
    data = [{
        "cycle": {
            "autogenerate": True,
            "isOverdue": False,
            "workflow": {
                "id": workflow.id,
                "type": "Workflow",
            },
            "context": {
                "id": workflow.context_id,
                "type": "Context",
            },
        }
    }]
    resp = self.api.send_request(
        self.api.client.post,
        api_link="/api/cycles",
        data=data)
    cycle_id = resp.json[0][1]["cycle"]["id"]
    self.assertEqual(
        flag,
        all_models.Cycle.query.get(cycle_id).is_verification_needed)

  @ddt.data(True, False)
  def test_change_verification_flag(self, flag):
    """Check is_verification_needed flag isn't changeable."""
    with factories.single_commit():
      workflow = wf_factories.WorkflowFactory(is_verification_needed=flag)
    workflow_id = workflow.id
    resp = self.api.put(workflow, {"is_verification_needed": not flag})
    self.assert400(resp)
    self.assertEqual(
        flag,
        all_models.Workflow.query.get(workflow_id).is_verification_needed)

  @ddt.data(True, False)
  def test_not_change_vf_flag(self, flag):
    """Check is_verification_needed not change on update."""
    with factories.single_commit():
      workflow = wf_factories.WorkflowFactory(is_verification_needed=flag)
    workflow_id = workflow.id
    resp = self.api.put(workflow, {"is_verification_needed": flag})
    self.assert200(resp)
    self.assertEqual(
        flag,
        all_models.Workflow.query.get(workflow_id).is_verification_needed)

  @ddt.data(True, False, None)
  def test_create_vf_flag(self, flag):
    """Check is_verification_needed flag setup on create."""
    data = self.get_workflow_dict()
    if flag is None:
      data['workflow'].pop('is_verification_needed', None)
    else:
      data['workflow']['is_verification_needed'] = flag
    resp = self.api.post(all_models.Workflow, data)
    self.assertEqual(201, resp.status_code)
    workflow_id = resp.json['workflow']['id']
    self.assertEqual(
        flag if flag is not None else True,
        all_models.Workflow.query.get(workflow_id).is_verification_needed)
コード例 #30
0
class TestCreatorProgram(TestCase):
  """Set up necessary objects and test Creator role with Program roles"""

  def setUp(self):
    super(TestCreatorProgram, self).setUp()
    self.generator = Generator()
    self.api = Api()
    self.object_generator = ObjectGenerator()
    self.init_users()
    self.init_roles()
    self.init_test_cases()
    self.objects = {}

  def init_test_cases(self):
    """ Create a dict of all possible test cases """
    self.test_cases = {
        "notmapped": {
            "objects": {
                "program": {
                    "get": 403,
                    "put": 403,
                    "delete": 403
                },
                "mapped_object": {
                    "get": 403,
                    "put": 403,
                    "delete": 403
                },
                "unrelated": {
                    "get": 403,
                    "put": 403,
                    "delete": 403,
                    "map": 403,
                }
            },
        },
        "mapped": {
            "objects": {
                "program": {
                    "get": 403,
                    "put": 403,
                    "delete": 403
                },
                "mapped_object": {
                    "get": 403,
                    "put": 403,
                    "delete": 403
                },
                "unrelated": {
                    "get": 403,
                    "put": 403,
                    "delete": 403,
                    "map": 403,
                }
            }
        },
        "ProgramReader": {
            "program_role": "ProgramReader",
            "objects": {
                "program": {
                    "get": 200,
                    "put": 403,
                    "delete": 403
                },
                "mapped_object": {
                    "get": 200,
                    "put": 403,
                    "delete": 403
                },
                "unrelated": {
                    "get": 403,
                    "put": 403,
                    "delete": 403,
                    "map": 403,
                }
            }
        },
        "ProgramOwner": {
            "program_role": "ProgramOwner",
            "objects": {
                "program": {
                    "get": 200,
                    "put": 200,
                    "delete": 200
                },
                "mapped_object": {
                    "get": 200,
                    "put": 200,
                    "delete": 200,
                },
                "unrelated": {
                    "get": 403,
                    "put": 403,
                    "delete": 403,
                    "map": 403,
                }
            }
        },
        "ProgramEditor": {
            "program_role": "ProgramEditor",
            "objects": {
                "program": {
                    "get": 200,
                    "put": 200,
                    "delete": 200
                },
                "mapped_object": {
                    "get": 200,
                    "put": 200,
                    "delete": 200
                },
                "unrelated": {
                    "get": 403,
                    "put": 403,
                    "delete": 403,
                    "map": 403,
                }
            }
        },
    }

  def init_roles(self):
    """ Create a delete request for the given object """
    response = self.api.get_query(all_models.Role, "")
    self.roles = {}
    for role in response.json.get("roles_collection").get("roles"):
      self.roles[role.get("name")] = role

  def init_users(self):
    """ Create users used by test cases """
    users = [
        ("creator", "Creator"),
        ("notmapped", "Creator"),
        ("mapped", "Creator"),
        ("ProgramReader", "Creator"),
        ("ProgramEditor", "Creator"),
        ("ProgramOwner", "Creator")]
    self.people = {}
    for (name, role) in users:
      _, user = self.object_generator.generate_person(
          data={"name": name}, user_role=role)
      self.people[name] = user

  def delete(self, obj):
    """ Create a delete request for the given object """
    return self.api.delete(obj).status_code

  def get(self, obj):
    """ Create a get request for the given object """
    return self.api.get(obj.__class__, obj.id).status_code

  def put(self, obj):
    """ Create a put request for the given object """
    response = self.api.get(obj.__class__, obj.id)
    if response.status_code == 200:
      return self.api.put(obj, response.json).status_code
    else:
      return response.status_code

  def map(self, dest):
    """ Map src to dest """
    response = self.api.post(all_models.Relationship, {
        "relationship": {"source": {
            "id": self.objects["program"].id,
            "type": self.objects["program"].type,
        }, "destination": {
            "id": dest.id,
            "type": dest.type
        }, "context": None},
    })
    return response.status_code

  def init_objects(self, test_case_name):
    """ Create a Program and a Mapped object for a given test case """
    # Create a program
    test_case = self.test_cases[test_case_name]
    creator = self.people.get('creator')
    self.api.set_user(creator)
    random_title = factories.random_str()
    response = self.api.post(all_models.Program, {
        "program": {"title": random_title, "context": None},
    })
    self.assertEqual(response.status_code, 201)
    context_id = response.json.get("program").get("context").get("id")
    program_id = response.json.get("program").get("id")

    # Use admin owner role to map it with system
    acr_id = all_models.AccessControlRole.query.filter_by(
        object_type="System",
        name="Admin"
    ).first().id
    self.objects["program"] = all_models.Program.query.get(program_id)

    # Create an object:
    for obj in ("mapped_object", "unrelated"):
      random_title = factories.random_str()
      response = self.api.post(all_models.System, {
          "system": {
              "title": random_title,
              "context": None,
              "access_control_list": [{
                  "person": {
                      "id": creator.id,
                      "type": "Person",
                  },
                  "ac_role_id": acr_id,
                  "context": None
              }],
          },
      })
      self.assertEqual(response.status_code, 201)
      system_id = response.json.get("system").get("id")
      self.objects[obj] = all_models.System.query.get(system_id)

    # Map Object to Program
    response = self.api.post(all_models.Relationship, {
        "relationship": {"source": {
            "id": program_id,
            "type": "Program"
        }, "destination": {
            "id": self.objects["mapped_object"].id,
            "type": "System"
        }, "context": None},
    })
    self.assertEqual(response.status_code, 201)

    # Map people to Program:
    if test_case_name != "notmapped":
      person = self.people.get(test_case_name)
      response = self.api.post(all_models.ObjectPerson, {"object_person": {
          "person": {
              "id": person.id,
              "type": "Person",
              "href": "/api/people/{}".format(person.id),
          }, "personable": {
              "type": "Program",
              "href": "/api/programs/{}".format(program_id),
              "id": program_id,
          }, "context": {
              "type": "Context",
              "id": context_id,
              "href": "/api/contexts/{}".format(context_id)
          }}})

    # Add roles to mapped users:
    if "program_role" in test_case:
      person = self.people.get(test_case_name)
      role = self.roles[test_case["program_role"]]
      response = self.api.post(all_models.UserRole, {"user_role": {
          "person": {
              "id": person.id,
              "type": "Person",
              "href": "/api/people/{}".format(person.id),
          }, "role": {
              "type": "Role",
              "href": "/api/roles/{}".format(role["id"]),
              "id": role["id"],
          }, "context": {
              "type": "Context",
              "id": context_id,
              "href": "/api/contexts/{}".format(context_id)
          }}})
      self.assertEqual(response.status_code, 201)

  def test_creator_program_roles(self):
    """ Test creator role with all program scoped roles """
    # Check permissions based on test_cases:
    errors = []
    for test_case in self.test_cases:
      self.init_objects(test_case)
      person = self.people.get(test_case)
      objects = self.test_cases.get(test_case).get('objects')
      self.api.set_user(person)
      for obj in ("unrelated", "mapped_object", "program"):
        actions = objects[obj]
        for action in ("map", "get", "put", "delete"):
          # reset sesion:
          db.session.commit()
          if action not in actions:
            continue
          func = getattr(self, action)
          res = func(self.objects[obj])
          if res != actions[action]:
            errors.append(
                "{}: Tried {} on {}, but received {} instead of {}".format(
                    test_case, action, obj, res, actions[action]))
      # Try mapping
    self.assertEqual(errors, [])
コード例 #31
0
class TestPermissions(TestCase):
    """Test checks permissions for proposals."""
    def setUp(self):
        super(TestPermissions, self).setUp()
        self.api = Api()
        roles = {r.name: r for r in all_models.Role.query.all()}
        ac_roles = {
            r.name: r
            for r in all_models.AccessControlRole.query.all()
        }
        with factories.single_commit():
            self.control = factories.ControlFactory()
            acrs = {
                "ACL_Reader":
                factories.AccessControlRoleFactory(name="ACL_Reader",
                                                   object_type="Control",
                                                   update=0),
                "ACL_Editor":
                factories.AccessControlRoleFactory(name="ACL_Editor",
                                                   object_type="Control"),
            }
            self.program = factories.ProgramFactory()
            self.program.context.related_object = self.program
            self.relationship = factories.RelationshipFactory(
                source=self.program,
                destination=self.control,
                context=self.program.context,
            )
            self.people = {
                "Creator": factories.PersonFactory(),
                "Reader": factories.PersonFactory(),
                "Editor": factories.PersonFactory(),
                "Administrator": factories.PersonFactory(),
                "ACL_Reader": factories.PersonFactory(),
                "ACL_Editor": factories.PersonFactory(),
                "Program Editors": factories.PersonFactory(),
                "Program Managers": factories.PersonFactory(),
                "Program Readers": factories.PersonFactory(),
            }
            for role_name in ["Creator", "Reader", "Editor", "Administrator"]:
                rbac_factories.UserRoleFactory(role=roles[role_name],
                                               person=self.people[role_name])
            for role_name in [
                    "Program Editors", "Program Managers", "Program Readers"
            ]:
                person = self.people[role_name]
                rbac_factories.UserRoleFactory(role=roles["Creator"],
                                               person=person)
                factories.AccessControlListFactory(
                    ac_role=ac_roles[role_name],
                    object=self.program,
                    person=self.people[role_name])
            self.proposal = factories.ProposalFactory(
                instance=self.control,
                content={
                    "access_control_list": {},
                    "custom_attribute_values": {},
                    "fields": {},
                    "mapping_fields": {},
                    "mapping_list_fields": {},
                })
            for role_name in ["ACL_Reader", "ACL_Editor"]:
                rbac_factories.UserRoleFactory(role=roles["Creator"],
                                               person=self.people[role_name])
                factories.AccessControlListFactory(
                    ac_role=acrs[role_name],
                    object=self.control,
                    person=self.people[role_name])
            proposal_model.set_acl_to_all_proposals_for(self.control)

    @ddt.data(
        ("Creator", 403),
        ("Reader", 200),
        ("Editor", 200),
        ("ACL_Reader", 200),
        ("ACL_Editor", 200),
        ("Administrator", 200),
        ("Program Editors", 200),
        ("Program Managers", 200),
        ("Program Readers", 200),
    )
    @ddt.unpack
    def test_permissions_on_get(self, role_name, status):
        """Test get proposals for {0}."""
        proposal_id = self.proposal.id
        self.api.set_user(self.people[role_name])
        self.client.get("/login")
        resp = self.api.get(all_models.Proposal, proposal_id)
        self.assertEqual(status, resp.status_code)

    def api_proposal_status_change(self, proposal_id, status):
        return self.api.put(all_models.Proposal.query.get(proposal_id),
                            {"proposal": {
                                "status": status
                            }})

    @ddt.data(("Creator", 403), ("Reader", 403), ("Editor", 200),
              ("Administrator", 200), ("ACL_Reader", 403), ("ACL_Editor", 200),
              ("Program Editors", 200), ("Program Managers", 200),
              ("Program Readers", 403))
    @ddt.unpack
    def test_permissions_on_apply(self, role_name, status):
        """Test apply proposals for {0}."""
        proposal_id = self.proposal.id
        self.api.set_user(self.people[role_name])
        self.client.get("/login")
        resp = self.api_proposal_status_change(
            proposal_id, all_models.Proposal.STATES.APPLIED)
        self.assertEqual(status, resp.status_code)

    @ddt.data(
        ("Creator", 403),
        ("Reader", 403),
        ("Editor", 200),
        ("ACL_Reader", 403),
        ("ACL_Editor", 200),
        ("Administrator", 200),
        ("Program Editors", 200),
        ("Program Managers", 200),
        ("Program Readers", 403),
    )
    @ddt.unpack
    def test_permissions_on_decline(self, role_name, status):
        """Test decline proposals for {0}."""
        proposal_id = self.proposal.id
        self.api.set_user(self.people[role_name])
        self.client.get("/login")
        resp = self.api_proposal_status_change(
            proposal_id, all_models.Proposal.STATES.DECLINED)
        self.assertEqual(status, resp.status_code)

    @ddt.data(
        ("Creator", 403),
        ("Reader", 201),
        ("Editor", 201),
        ("ACL_Reader", 201),
        ("ACL_Editor", 201),
        ("Administrator", 201),
        ("Program Editors", 201),
        ("Program Managers", 201),
        ("Program Readers", 201),
    )
    @ddt.unpack
    def test_permissions_on_create(self, role_name, status):
        """Test create proposal for {0}."""
        data = {
            "proposal": {
                "instance": {
                    "id": self.control.id,
                    "type": self.control.type,
                },
                "full_instance_content": {
                    "title": "new_title"
                },
                "agenda": "update cav",
                "context": None,
            }
        }
        self.api.set_user(self.people[role_name])
        self.client.get("/login")
        resp = self.api.post(all_models.Proposal, data)
        self.assertEqual(status, resp.status_code)
コード例 #32
0
ファイル: test_api.py プロジェクト: inesp/ggrc-core
class TestReviewApi(TestCase):
    """Base TestCase class proposal api tests."""
    def setUp(self):
        super(TestReviewApi, self).setUp()
        self.api = Api()
        self.api.client.get("/login")
        self.generator = generator.ObjectGenerator()

    def test_simple_get(self):
        """Test simple get"""
        with factories.single_commit():
            control = factories.ControlFactory()
            review = factories.ReviewFactory(
                email_message="test email message",
                notification_type="email",
                reviewable=control,
                status=all_models.Review.STATES.UNREVIEWED,
            )
        resp = self.api.get(all_models.Review, review.id)
        self.assert200(resp)
        self.assertIn("review", resp.json)
        resp_review = resp.json["review"]
        self.assertEqual(all_models.Review.STATES.UNREVIEWED,
                         resp_review["status"])
        self.assertEqual(all_models.Review.NotificationTypes.EMAIL_TYPE,
                         resp_review["notification_type"])
        self.assertEqual("test email message", resp_review["email_message"])

    def test_collection_get(self):
        """Test simple collection get"""
        with factories.single_commit():
            review1 = factories.ReviewFactory(
                status=all_models.Review.STATES.UNREVIEWED)
            review2 = factories.ReviewFactory(
                status=all_models.Review.STATES.REVIEWED)

        resp = self.api.get_collection(all_models.Review,
                                       [review1.id, review2.id])
        self.assert200(resp)
        self.assertIn("reviews_collection", resp.json)
        self.assertIn("reviews", resp.json["reviews_collection"])
        self.assertEquals(2, len(resp.json["reviews_collection"]["reviews"]))

    def test_create_review(self):
        """Create review via API, check that single relationship is created"""
        control = factories.ControlFactory()
        control_id = control.id
        resp = self.api.post(
            all_models.Review,
            {
                "review": {
                    "reviewable": {
                        "type": control.type,
                        "id": control.id,
                    },
                    "context": None,
                    "notification_type": "email",
                    "status": all_models.Review.STATES.UNREVIEWED,
                    "access_control_list": build_reviewer_acl()
                },
            },
        )
        self.assertEqual(201, resp.status_code)
        review_id = resp.json["review"]["id"]
        review = all_models.Review.query.get(review_id)
        self.assertEqual(all_models.Review.STATES.UNREVIEWED, review.status)
        self.assertEqual(control.type, review.reviewable_type)
        self.assertEqual(control_id, review.reviewable_id)

        control_review_rel_count = all_models.Relationship.query.filter(
            all_models.Relationship.source_id == review.id,
            all_models.Relationship.source_type == review.type,
            all_models.Relationship.destination_id == control_id,
            all_models.Relationship.destination_type == control.type,
        ).union(
            all_models.Relationship.query.filter(
                all_models.Relationship.destination_id == review.id,
                all_models.Relationship.destination_type == review.type,
                all_models.Relationship.source_id == control_id,
                all_models.Relationship.source_type == control.type,
            )).count()
        self.assertEqual(1, control_review_rel_count)

    def test_delete_review(self):
        """Test delete review via API"""
        with factories.single_commit():
            control = factories.ControlFactory()
            control_id = control.id
            review = factories.ReviewFactory(reviewable=control)
            review_id = review.id
        resp = self.api.delete(review)
        self.assert200(resp)
        review = all_models.Review.query.get(review_id)
        control = all_models.Control.query.get(control_id)

        self.assertIsNone(review)
        self.assertEquals(0, len(control.related_objects(_types=["Review"])))

    def test_last_reviewed(self):
        """last_reviewed_by, last_reviewed_by should be set if reviewed"""
        control = factories.ControlFactory()
        resp, review = self.generator.generate_object(
            all_models.Review,
            {
                "reviewable": {
                    "type": control.type,
                    "id": control.id,
                },
                "context": None,
                "status": all_models.Review.STATES.UNREVIEWED,
                "access_control_list": build_reviewer_acl(),
                "notification_type":
                all_models.Review.NotificationTypes.EMAIL_TYPE
            },
        )
        review_id = review.id
        resp = self.api.put(
            review,
            {
                "status": all_models.Review.STATES.REVIEWED,
            },
        )
        self.assert200(resp)
        self.assertIsNotNone(resp.json["review"]["last_reviewed_by"])
        self.assertIsNotNone(resp.json["review"]["last_reviewed_at"])

        review = all_models.Review.query.get(review_id)
        self.assertIsNotNone(review.last_reviewed_by)
        self.assertIsNotNone(review.last_reviewed_at)

    def test_reviewable_revisions(self):
        """Check that proper revisions are created"""
        control = factories.ControlFactory()
        resp, review = self.generator.generate_object(
            all_models.Review,
            {
                "reviewable": {
                    "type": control.type,
                    "id": control.id,
                },
                "context": None,
                "status": all_models.Review.STATES.UNREVIEWED,
                "access_control_list": build_reviewer_acl(),
                "notification_type":
                all_models.Review.NotificationTypes.EMAIL_TYPE
            },
        )
        control_id = control.id
        reviewable = review.reviewable

        control_revisions = all_models.Revision.query.filter_by(
            resource_id=control_id, resource_type=control.type).order_by(
                all_models.Revision.created_at, ).all()
        self.assertEquals(2, len(control_revisions))
        self.assertEquals(all_models.Review.STATES.UNREVIEWED,
                          control_revisions[0].content["review_status"])
        self.assertEquals(all_models.Review.STATES.UNREVIEWED,
                          control_revisions[1].content["review_status"])
        resp = self.api.put(
            review,
            {
                "status": all_models.Review.STATES.REVIEWED,
            },
        )
        self.assert200(resp)

        control_revisions = all_models.Revision.query.filter_by(
            resource_id=control_id, resource_type=control.type).order_by(
                all_models.Revision.created_at, ).all()
        self.assertEquals(3, len(control_revisions))
        self.assertEquals(all_models.Review.STATES.REVIEWED,
                          control_revisions[2].content["review_status"])

        resp = self.api.put(reviewable,
                            {"description": "some new description"})
        self.assert200(resp)

        control_revisions = all_models.Revision.query.filter_by(
            resource_id=control_id, resource_type=control.type).order_by(
                all_models.Revision.created_at, ).all()
        self.assertEquals(4, len(control_revisions))
        self.assertEquals(all_models.Review.STATES.UNREVIEWED,
                          control_revisions[3].content["review_status"])
コード例 #33
0
class TestAssessment(TestCase):
    """Assessment test cases"""

    # pylint: disable=invalid-name

    def setUp(self):
        super(TestAssessment, self).setUp()
        self.api = Api()

    def test_auto_slug_generation(self):
        """Test auto slug generation"""
        factories.AssessmentFactory(title="Some title")
        ca = Assessment.query.first()
        self.assertEqual("ASSESSMENT-{}".format(ca.id), ca.slug)

    def test_enabling_comment_notifications_by_default(self):
        """New Assessments should have comment notifications enabled by default."""
        asmt = factories.AssessmentFactory()

        self.assertTrue(asmt.send_by_default)
        recipients = asmt.recipients.split(",") if asmt.recipients else []
        self.assertEqual(sorted(recipients),
                         ["Assessor", "Creator", "Verifier"])

    def test_audit_changes_api(self):
        """Test that users can't change the audit mapped to an assessment."""
        audit_id = factories.AuditFactory().id
        asmt = factories.AssessmentFactory()
        correct_audit_id = asmt.audit_id
        response = self.api.put(asmt,
                                {"audit": {
                                    "type": "Audit",
                                    "id": audit_id
                                }})
        self.assert400(response)
        assessment = Assessment.query.first()
        self.assertEqual(assessment.audit_id, correct_audit_id)

    def test_put_no_audit_change(self):
        """Test that put requests works without audit changes"""
        asmt = factories.AssessmentFactory()
        correct_audit_id = asmt.audit_id
        response = self.api.put(
            asmt, {"audit": {
                "type": "Audit",
                "id": correct_audit_id
            }})
        self.assert200(response)
        assessment = Assessment.query.first()
        self.assertEqual(assessment.audit_id, correct_audit_id)

    def test_audit_changes_import(self):
        """Test that users can't change the audit mapped to an assessment."""
        audit = factories.AuditFactory()
        asmt = factories.AssessmentFactory()
        correct_audit_id = asmt.audit_id
        response = self.import_data(
            OrderedDict([
                ("object_type", "Assessment"),
                ("Code*", asmt.slug),
                ("audit", audit.slug),
            ]))
        self._check_csv_response(
            response, {
                "Assessment": {
                    "row_warnings": {
                        errors.UNMODIFIABLE_COLUMN.format(line=3,
                                                          column_name="Audit")
                    }
                }
            })
        assessment = Assessment.query.first()
        self.assertEqual(assessment.audit_id, correct_audit_id)

    def test_no_audit_change_imports(self):
        """Test that imports work if audit field does not contain changes."""
        factories.AuditFactory()
        asmt = factories.AssessmentFactory()
        correct_audit_id = asmt.audit_id
        response = self.import_data(
            OrderedDict([
                ("object_type", "Assessment"),
                ("Code*", asmt.slug),
                ("audit", asmt.audit.slug),
            ]))
        self._check_csv_response(response, {})
        assessment = Assessment.query.first()
        self.assertEqual(assessment.audit_id, correct_audit_id)

    def test_empty_audit_import(self):
        """Test empty audit import"""
        factories.AuditFactory()
        asmt = factories.AssessmentFactory()
        correct_audit_id = asmt.audit_id
        response = self.import_data(
            OrderedDict([
                ("object_type", "Assessment"),
                ("Code*", asmt.slug),
                ("audit", ""),
            ]))
        self._check_csv_response(response, {})
        assessment = Assessment.query.first()
        self.assertEqual(assessment.audit_id, correct_audit_id)
コード例 #34
0
ファイル: test_audit_rbac.py プロジェクト: zidarsk8/ggrc-core
class TestPermissionsOnAssessmentTemplate(TestCase):
  """ Test check permissions for ProgramEditor on

  get and post assessment_temaplte action"""

  def setUp(self):
    super(TestPermissionsOnAssessmentTemplate, self).setUp()
    self.api = Api()
    self.generator = ObjectGenerator()
    _, program = self.generator.generate_object(all_models.Program)
    program_id = program.id
    _, self.editor = self.generator.generate_person(
        user_role="Creator"
    )
    role = perms.all_models.Role.query.filter(
        perms.all_models.Role.name == "ProgramEditor"
    ).first()
    self.generator.generate_user_role(
        self.editor,
        role,
        all_models.Program.query.get(program_id)
    )
    _, audit = self.generator.generate_object(
        all_models.Audit,
        {
            "title": "Audit",
            "program": {"id": program_id},
            "status": "Planned"
        },
    )
    audit_id = audit.id

    generated_at = self.generator.generate_object(
        all_models.AssessmentTemplate,
        {
            "title": "Template",
            "_NON_RELEVANT_OBJ_TYPES": {},
            "_objectTypes": {},
            "audit": {"id": audit.id},
            "audit_title": audit.title,
            "people_value": [],
            "default_people": {
                "assessors": "Object Owners",
                "verifiers": "Object Owners",
            },
            "context": {"id": audit.context.id},
        }
    )
    self.assessment_template_resp, assessment_template = generated_at
    assessment_template_id = assessment_template.id
    self.api.set_user(self.editor)
    self.perms_data = self.api.client.get("/permissions").json
    self.audit = all_models.Audit.query.get(audit_id)
    self.assessment_template = all_models.AssessmentTemplate.query.get(
        assessment_template_id)

  def test_post_action(self):
    """Test create action on AssessmentTemplate created by api"""
    data = [{
        "assessment_template": {
            "_NON_RELEVANT_OBJ_TYPES": {},
            "_objectTypes": {},
            "audit": {"id": self.audit.id},
            "audit_title": self.audit.title,
            "people_value": [],
            "default_people": {
                "assessors": "Object Owners",
                "verifiers": "Object Owners",
            },
            "context": {"id": self.audit.context.id},
            "title": "123",
        }
    }]
    self.api.set_user(self.editor)
    resp = self.api.post(all_models.AssessmentTemplate, data)
    self.assert200(resp)

  def test_get_action(self):
    """Test read action on AssessmentTemplate created by api"""
    resp = self.api.get(all_models.AssessmentTemplate,
                        self.assessment_template.id)
    self.assert200(resp)

  def test_put_action(self):
    """Test update action on AssessmentTemplate created by api"""
    to_update = copy.deepcopy(self.assessment_template_resp.json)
    new_title = "new_{}".format(self.assessment_template.title)
    to_update['assessment_template']['title'] = new_title
    resp = self.api.put(self.assessment_template, to_update)
    self.assert200(resp)
    assessment_tmpl = all_models.AssessmentTemplate.query.get(
        self.assessment_template.id
    )
    self.assertEqual(new_title, assessment_tmpl.title)

  def test_delete_action(self):
    """Test delete action on AssessmentTemplate created by api"""
    resp = self.api.delete(self.assessment_template)
    self.assert200(resp)
    self.assertFalse(all_models.AssessmentTemplate.query.filter(
        all_models.AssessmentTemplate == self.assessment_template.id).all())
コード例 #35
0
class TestRevisionHistory(TestCase):
    """Test checks permissions for revision history."""
    def setUp(self):
        super(TestRevisionHistory, self).setUp()
        self.api = Api()
        roles = {r.name: r for r in all_models.Role.query.all()}
        ac_roles = {
            r.name: r
            for r in all_models.AccessControlRole.query.all()
        }
        with factories.single_commit():
            self.control = factories.ControlFactory()
            acrs = {
                "ACL_Reader":
                factories.AccessControlRoleFactory(name="ACL_Reader",
                                                   object_type="Control",
                                                   update=0),
                "ACL_Editor":
                factories.AccessControlRoleFactory(name="ACL_Editor",
                                                   object_type="Control"),
            }
            self.program = factories.ProgramFactory()
            self.program.context.related_object = self.program
            self.relationship = factories.RelationshipFactory(
                source=self.program,
                destination=self.control,
                context=self.program.context,
            )
            self.people = {
                "Creator": factories.PersonFactory(),
                "Reader": factories.PersonFactory(),
                "Editor": factories.PersonFactory(),
                "Administrator": factories.PersonFactory(),
                "ACL_Reader": factories.PersonFactory(),
                "ACL_Editor": factories.PersonFactory(),
                "Program Editors": factories.PersonFactory(),
                "Program Managers": factories.PersonFactory(),
                "Program Readers": factories.PersonFactory(),
            }
            for role_name in ["Creator", "Reader", "Editor", "Administrator"]:
                rbac_factories.UserRoleFactory(role=roles[role_name],
                                               person=self.people[role_name])
            for role_name in [
                    "Program Editors", "Program Managers", "Program Readers"
            ]:
                person = self.people[role_name]
                rbac_factories.UserRoleFactory(role=roles["Creator"],
                                               person=person)
                factories.AccessControlListFactory(
                    ac_role=ac_roles[role_name],
                    object=self.program,
                    person=self.people[role_name])
        with factories.single_commit():
            for role_name in ["ACL_Reader", "ACL_Editor"]:
                rbac_factories.UserRoleFactory(role=roles["Creator"],
                                               person=self.people[role_name])
                factories.AccessControlListFactory(
                    ac_role=acrs[role_name],
                    object=self.control,
                    person=self.people[role_name])

    @ddt.data(
        ("Creator", True),
        ("Reader", False),
        ("Editor", False),
        ("ACL_Reader", False),
        ("ACL_Editor", False),
        ("Administrator", False),
        ("Program Editors", False),
        ("Program Managers", False),
        ("Program Readers", False),
    )
    @ddt.unpack
    def test_get(self, role_name, empty):
        """Test get revision history for {0}."""
        control_id = self.control.id
        query = all_models.Revision.query.filter(
            all_models.Revision.resource_id == control_id,
            all_models.Revision.resource_type == self.control.type,
        )
        if empty:
            ids = []
        else:
            ids = [i.id for i in query]
        self.api.set_user(self.people[role_name])
        self.client.get("/login")
        resp = self.api.client.get(
            "/api/revisions"
            "?resource_type=Control&resource_id={}".format(control_id))
        self.assertEqual(
            ids,
            [i["id"] for i in resp.json["revisions_collection"]["revisions"]])

    def update_revisions(self, obj):
        """Assert revision diff between api and calculated in test.."""
        query = all_models.Revision.query.filter(
            all_models.Revision.resource_id == obj.id,
            all_models.Revision.resource_type == obj.type,
        ).order_by(sa.desc(all_models.Revision.updated_at))
        diffs = [(i.id, json.loads(json.dumps(i.diff_with_current())))
                 for i in query]
        for i in range(3):
            resp = self.api.client.get(
                "/api/revisions"
                "?__sort=-updated_at"
                "&resource_type={}"
                "&resource_id={}"
                "&_={}".format(obj.type, obj.id,
                               random.randint(1100000000, 1153513123412)))
            resp_diffs = [
                (i["id"], i["diff_with_current"])
                for i in resp.json["revisions_collection"]["revisions"]
            ]
            self.assertDictEqual(dict(diffs), dict(resp_diffs))
        self.assertFalse(any(resp_diffs[0][1].values()))

    def test_control_memcache(self):
        """Test get updated control revisions."""
        control_id = self.control.id
        self.api.put(self.control, {"title": "new_title"})
        self.control = all_models.Control.eager_query().get(control_id)
        self.update_revisions(self.control)
        db.session.expire_all()
        self.control = all_models.Control.eager_query().get(control_id)
        self.api.put(self.control, {"description": "new test description BLA"})
        self.control = all_models.Control.eager_query().get(control_id)
        self.update_revisions(self.control)
        self.api.put(self.control, {"description": "bla bla bla"})
        self.control = all_models.Control.eager_query().get(control_id)
        self.update_revisions(self.control)

    def test_risk_memcache(self):
        """Test get updated risk revisions."""
        risk = factories.RiskFactory()
        risk_id = risk.id
        self.api.put(risk, {"title": "new_title"})
        self.update_revisions(risk)
        db.session.expire_all()
        risk = all_models.Risk.eager_query().get(risk_id)
        self.api.put(risk, {"description": "new test description BLA"})
        risk = all_models.Risk.eager_query().get(risk_id)
        self.update_revisions(risk)
        risk = all_models.Risk.eager_query().get(risk_id)
        self.api.put(risk, {"description": "BLA bla bla"})
        risk = all_models.Risk.eager_query().get(risk_id)
        self.update_revisions(risk)

    @ddt.data(True, False)
    def test_get_mandatory_acrs(self, mandatory):
        """ACR and mandatory meta info if mandatory flag is {0}."""
        control_id = self.control.id
        acr = factories.AccessControlRoleFactory(name="test_name",
                                                 object_type=self.control.type,
                                                 mandatory=mandatory)
        acr_id = acr.id
        resp = self.api.client.get(
            "/api/revisions"
            "?resource_type=Control&resource_id={}".format(control_id))
        collection = resp.json["revisions_collection"]["revisions"]
        self.assertTrue(collection)
        self.assertIn("meta", collection[0])
        self.assertIn("mandatory", collection[0]["meta"])
        self.assertIn("access_control_roles",
                      collection[0]["meta"]["mandatory"])
        mandatory_acrs = collection[0]["meta"]["mandatory"][
            "access_control_roles"]
        self.assertEqual(mandatory, acr_id in mandatory_acrs)

    @ddt.data(True, False)
    def test_get_mandatory_cads(self, mandatory):
        """CAD and mandatory meta info if mandatory flag is {0}."""
        control_id = self.control.id
        cad = factories.CustomAttributeDefinitionFactory(
            title="test_name", definition_type="control", mandatory=mandatory)
        cad_id = cad.id
        resp = self.api.client.get(
            "/api/revisions"
            "?resource_type=Control&resource_id={}".format(control_id))
        collection = resp.json["revisions_collection"]["revisions"]
        self.assertTrue(collection)
        self.assertIn("meta", collection[0])
        self.assertIn("mandatory", collection[0]["meta"])
        mandatory_meta = collection[0]["meta"]["mandatory"]
        self.assertIn("custom_attribute_definitions", mandatory_meta)
        mandatory_cads = mandatory_meta["custom_attribute_definitions"]
        self.assertEqual(mandatory, cad_id in mandatory_cads)

    @ddt.data(
        {
            "factory":
            factories.ControlFactory,
            "fields": [
                'test_plan', 'status', 'notes', 'description', 'title', 'slug',
                'folder'
            ]
        },
        {
            "factory": factories.RiskFactory,
            "fields": ['test_plan', 'status', 'notes', 'title', 'slug']
        },
    )
    @ddt.unpack
    def test_get_mandatory_fields(self, factory, fields):
        """Fields mandatory meta info for {factory._meta.model}."""
        instance = factory()
        resp = self.api.client.get("/api/revisions"
                                   "?resource_type={}&resource_id={}".format(
                                       instance.type, instance.id))
        collection = resp.json["revisions_collection"]["revisions"]
        self.assertTrue(collection)
        self.assertIn("meta", collection[0])
        self.assertIn("mandatory", collection[0]["meta"])
        mandatory_meta = collection[0]["meta"]["mandatory"]
        self.assertIn("fields", mandatory_meta)
        self.assertEqual(fields, mandatory_meta["fields"])

    @ddt.data(
        {
            "factory": factories.ControlFactory,
            "fields": []
        },
        {
            "factory": factories.RiskFactory,
            "fields": []
        },
        {
            "factory": factories.AssessmentFactory,
            "fields": []
        },
    )
    @ddt.unpack
    def test_mandatory_mapping_list(self, factory, fields):
        """Mapping List mandatory meta info for {factory._meta.model}."""
        instance = factory()
        resp = self.api.client.get("/api/revisions"
                                   "?resource_type={}&resource_id={}".format(
                                       instance.type, instance.id))
        collection = resp.json["revisions_collection"]["revisions"]
        self.assertTrue(collection)
        self.assertIn("meta", collection[0])
        self.assertIn("mandatory", collection[0]["meta"])
        mandatory_meta = collection[0]["meta"]["mandatory"]
        self.assertIn("mapping_list_fields", mandatory_meta)
        self.assertEqual(fields, mandatory_meta["mapping_list_fields"])

    @ddt.data(
        {
            "factory": factories.ControlFactory,
            "fields": []
        },
        {
            "factory": factories.RiskFactory,
            "fields": []
        },
        {
            "factory": factories.AssessmentFactory,
            "fields": ["audit"]
        },
    )
    @ddt.unpack
    def test_mandatory_mappings(self, factory, fields):
        """Mapping fields mandatory meta info for {factory._meta.model}."""
        instance = factory()
        resp = self.api.client.get("/api/revisions"
                                   "?resource_type={}&resource_id={}".format(
                                       instance.type, instance.id))
        collection = resp.json["revisions_collection"]["revisions"]
        self.assertTrue(collection)
        self.assertIn("meta", collection[0])
        self.assertIn("mandatory", collection[0]["meta"])
        mandatory_meta = collection[0]["meta"]["mandatory"]
        self.assertIn("mapping_fields", mandatory_meta)
        self.assertEqual(fields, mandatory_meta["mapping_fields"])
コード例 #36
0
ファイル: test_audit_rbac.py プロジェクト: zidarsk8/ggrc-core
class TestAuditRBAC(TestCase):
  """Test audit RBAC"""

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

  @classmethod
  def setUpClass(cls):
    TestCase.clear_data()
    cls.response = cls._import_file("audit_rbac.csv")
    cls.people = all_models.Person.eager_query().all()
    cls.audit = all_models.Audit.eager_query().first()
    sources = set(r.source for r in cls.audit.related_sources)
    destinations = set(r.destination for r in cls.audit.related_destinations)
    related = [obj for obj in sources.union(destinations)
               if not isinstance(obj, all_models.Person)]
    cls.related_objects = related

  def setUp(self):
    """Imports test_csvs/audit_rbac.csv needed by the tests"""
    self._check_csv_response(self.response, {})
    self.api = Api()
    self.client.get("/login")

  def read(self, objects):
    """Attempt to do a GET request for every object in the objects list"""
    responses = []
    for obj in objects:
      status_code = self.api.get(obj.__class__, obj.id).status_code
      responses.append((obj.type, status_code))
    return responses

  def update(self, objects):
    """Attempt to do a PUT request for every object in the objects list"""
    responses = []
    for obj in objects:
      response = self.api.get(obj.__class__, obj.id)
      status_code = response.status_code
      if response.status_code == 200:
        status_code = self.api.put(obj, response.json).status_code
      responses.append((obj.type, status_code))
    return responses

  def call_api(self, method, expected_statuses):
    """Calls the REST api with a given method and returns a list of
       status_codes that do not match the expected_statuses dict"""
    all_errors = []
    for person in self.people:
      self.api.set_user(person)
      responses = method(self.related_objects + [self.audit])
      for type_, code in responses:
        if code != expected_statuses[person.email]:
          all_errors.append("{} does not have {} access to {} ({})".format(
              person.email, method.__name__, type_, code))
    return all_errors

  def test_read_access_on_mapped(self):
    """Test if people have read access to mapped objects.

    All users except [email protected] should have read access."""
    expected_statuses = defaultdict(lambda: 200)
    for exception in ("*****@*****.**",):
      expected_statuses[exception] = 403
    errors = self.call_api(self.read, expected_statuses)
    assert not errors, "\n".join(errors)

  def test_update_access_on_mapped(self):
    """Test if people have upate access to mapped objects.

    All users except [email protected], [email protected], [email protected],
    [email protected] should have update access."""
    expected_statuses = defaultdict(lambda: 200)
    for exception in ("*****@*****.**", "*****@*****.**",
                      "*****@*****.**", "*****@*****.**"):
      expected_statuses[exception] = 403
    errors = self.call_api(self.update, expected_statuses)
    assert not errors, "\n".join(errors)
コード例 #37
0
class TestWorkflowCycleStatePropagation(TestCase):
  """Test case for cycle task to cycle task group status propagation"""

  def setUp(self):
    super(TestWorkflowCycleStatePropagation, self).setUp()
    self.api = Api()
    self.generator = WorkflowsGenerator()
    self.object_generator = ObjectGenerator()

    self.weekly_wf = {
        "title": "weekly thingy",
        "description": "start this many a time",
        "unit": "week",
        "repeat_every": 1,
        "task_groups": [{
            "title": "weekly task group",
            "task_group_tasks": [{
                "title": "weekly task 1",
                "start_date": dtm.date(2016, 6, 10),
                "end_date": dtm.date(2016, 6, 13),
            }, {
                "title": "weekly task 1",
                "start_date": dtm.date(2016, 6, 10),
                "end_date": dtm.date(2016, 6, 13),
            }]},
        ]
    }

  def test_weekly_state_transitions_assigned_inprogress(self):
    """Test that starting one cycle task changes cycle task group"""

    with freeze_time("2016-6-10 13:00:00"):  # Friday, 6/10/2016
      _, wf = self.generator.generate_workflow(self.weekly_wf)
      self.generator.activate_workflow(wf)

      ctg = db.session.query(CycleTaskGroup).join(
          Cycle).join(Workflow).filter(Workflow.id == wf.id).all()[0]
      self.assertEqual(ctg.status, "Assigned")

      cycle_tasks = db.session.query(CycleTaskGroupObjectTask).join(
          Cycle).join(Workflow).filter(Workflow.id == wf.id).all()
      first_ct, second_ct = cycle_tasks

      for cycle_task in cycle_tasks:
        self.assertEqual(cycle_task.status, "Assigned")

      # Move one task to In Progress
      _, first_ct = self.generator.modify_object(
          first_ct, {"status": "In Progress"})

      self.assertEqual(first_ct.status, "In Progress")
      ctg = db.session.query(CycleTaskGroup).get(ctg.id)
      self.assertEqual(ctg.status, "In Progress")

      # Undo operation
      _, first_ct = self.generator.modify_object(
          first_ct, {"status": "Assigned"})

      self.assertEqual(first_ct.status, "Assigned")
      ctg = db.session.query(CycleTaskGroup).get(ctg.id)
      self.assertEqual(ctg.status, "Assigned")

      # Move both to in progress
      for cycle_task in cycle_tasks:
        self.generator.modify_object(
            cycle_task, {"status": "In Progress"})

      ctg = db.session.query(CycleTaskGroup).get(ctg.id)
      self.assertEqual(ctg.status, "In Progress")

      # Undo one cycle task
      _, first_ct = self.generator.modify_object(
          first_ct, {"status": "Assigned"})
      second_ct = db.session.query(CycleTaskGroupObjectTask).get(second_ct.id)
      ctg = db.session.query(CycleTaskGroup).get(ctg.id)

      self.assertEqual(first_ct.status, "Assigned")
      self.assertEqual(second_ct.status, "In Progress")
      self.assertEqual(ctg.status, "In Progress")

      # Undo second cycle task
      _, second_ct = self.generator.modify_object(
          second_ct, {"status": "Assigned"})
      first_ct = db.session.query(CycleTaskGroupObjectTask).get(first_ct.id)
      ctg = db.session.query(CycleTaskGroup).get(ctg.id)

      self.assertEqual(first_ct.status, "Assigned")
      self.assertEqual(second_ct.status, "Assigned")
      self.assertEqual(ctg.status, "Assigned")

  def test_weekly_state_transitions_inprogress_finished(self):
    """Test In Progress to Finished transitions"""

    with freeze_time("2016-6-10 13:00:00"):  # Friday, 6/10/2016
      _, wf = self.generator.generate_workflow(self.weekly_wf)
      self.generator.activate_workflow(wf)

      ctg = db.session.query(CycleTaskGroup).join(
          Cycle).join(Workflow).filter(Workflow.id == wf.id).all()[0]

      cycle_tasks = db.session.query(CycleTaskGroupObjectTask).join(
          Cycle).join(Workflow).filter(Workflow.id == wf.id).all()
      first_ct, second_ct = cycle_tasks

      # Move both tasks to In Progress
      for cycle_task in cycle_tasks:
        self.generator.modify_object(
            cycle_task, {"status": "In Progress"})

      # Test that moving one task to finished doesn't finish entire cycle
      _, first_ct = self.generator.modify_object(
          first_ct, {"status": "Finished"})
      second_ct = db.session.query(CycleTaskGroupObjectTask).get(second_ct.id)
      ctg = db.session.query(CycleTaskGroup).get(ctg.id)

      self.assertEqual(first_ct.status, "Finished")
      self.assertEqual(second_ct.status, "In Progress")
      self.assertEqual(ctg.status, "In Progress")

      # Test moving second task to Finished - entire cycle should be finished
      _, second_ct = self.generator.modify_object(
          second_ct, {"status": "Finished"})
      first_ct = db.session.query(CycleTaskGroupObjectTask).get(first_ct.id)
      ctg = db.session.query(CycleTaskGroup).get(ctg.id)

      self.assertEqual(second_ct.status, "Finished")
      self.assertEqual(first_ct.status, "Finished")
      self.assertEqual(ctg.status, "Finished")

      # Undo one task, cycle should be In Progress
      _, first_ct = self.generator.modify_object(
          first_ct, {"status": "In Progress"})
      second_ct = db.session.query(CycleTaskGroupObjectTask).get(second_ct.id)
      ctg = db.session.query(CycleTaskGroup).get(ctg.id)

      self.assertEqual(first_ct.status, "In Progress")
      self.assertEqual(second_ct.status, "Finished")
      self.assertEqual(ctg.status, "In Progress")

  def test_weekly_state_transitions_finished_verified(self):
    """Test Finished to Verified transitions"""

    with freeze_time("2016-6-10 13:00:00"):  # Friday, 6/10/2016
      _, wf = self.generator.generate_workflow(self.weekly_wf)
      self.generator.activate_workflow(wf)

      ctg = db.session.query(CycleTaskGroup).join(
          Cycle).join(Workflow).filter(Workflow.id == wf.id).all()[0]

      cycle_tasks = db.session.query(CycleTaskGroupObjectTask).join(
          Cycle).join(Workflow).filter(Workflow.id == wf.id).all()
      first_ct, second_ct = cycle_tasks

      # Move both tasks to In Progress
      for cycle_task in cycle_tasks:
        self.generator.modify_object(
            cycle_task, {"status": "In Progress"})
        self.generator.modify_object(
            cycle_task, {"status": "Finished"})

      ctg = db.session.query(CycleTaskGroup).get(ctg.id)
      self.assertEqual(ctg.status, "Finished")

      for cycle_task in cycle_tasks:
        cycle_task = db.session.query(CycleTaskGroupObjectTask).get(
            cycle_task.id)
        self.assertEqual(cycle_task.status, "Finished")

      # Verify first CT
      _, first_ct = self.generator.modify_object(
          first_ct, {"status": "Verified"})

      second_ct = db.session.query(CycleTaskGroupObjectTask).get(second_ct.id)
      ctg = db.session.query(CycleTaskGroup).get(ctg.id)

      self.assertEqual(first_ct.status, "Verified")
      self.assertEqual(second_ct.status, "Finished")
      self.assertEqual(ctg.status, "Finished")

      # Verify second CT
      _, second_ct = self.generator.modify_object(
          second_ct, {"status": "Verified"})

      first_ct = db.session.query(CycleTaskGroupObjectTask).get(first_ct.id)
      ctg = db.session.query(CycleTaskGroup).get(ctg.id)

      self.assertEqual(first_ct.status, "Verified")
      self.assertEqual(second_ct.status, "Verified")
      self.assertEqual(ctg.status, "Verified")

  def test_weekly_state_transitions_finished_declined(self):
    """Test Finished to Declined transitions"""

    with freeze_time("2016-6-10 13:00:00"):  # Friday, 6/10/2016
      _, wf = self.generator.generate_workflow(self.weekly_wf)
      self.generator.activate_workflow(wf)

      ctg = db.session.query(CycleTaskGroup).join(
          Cycle).join(Workflow).filter(Workflow.id == wf.id).all()[0]

      cycle_tasks = db.session.query(CycleTaskGroupObjectTask).join(
          Cycle).join(Workflow).filter(Workflow.id == wf.id).all()
      first_ct, second_ct = cycle_tasks

      # Move both tasks to In Progress
      for cycle_task in cycle_tasks:
        self.generator.modify_object(
            cycle_task, {"status": "In Progress"})
        self.generator.modify_object(
            cycle_task, {"status": "Finished"})

      ctg = db.session.query(CycleTaskGroup).get(ctg.id)
      self.assertEqual(ctg.status, "Finished")

      # Decline first CT
      _, first_ct = self.generator.modify_object(
          first_ct, {"status": "Declined"})

      second_ct = db.session.query(CycleTaskGroupObjectTask).get(second_ct.id)
      ctg = db.session.query(CycleTaskGroup).get(ctg.id)

      self.assertEqual(first_ct.status, "Declined")
      self.assertEqual(second_ct.status, "Finished")
      self.assertEqual(ctg.status, "In Progress")

  def test_deleted_task_state_transitions(self):
    """Test In Progress to Finished transition after task is deleted"""

    with freeze_time("2016-6-10 13:00:00"):  # Friday, 6/10/2016
      _, wf = self.generator.generate_workflow(self.weekly_wf)
      self.generator.activate_workflow(wf)

      ctg = db.session.query(CycleTaskGroup).join(
          Cycle).join(Workflow).filter(Workflow.id == wf.id).all()[0]
      first_ct, second_ct = db.session.query(CycleTaskGroupObjectTask).join(
          Cycle).join(Workflow).filter(Workflow.id == wf.id).all()

      # Move first task to In Progress
      self.generator.modify_object(first_ct, {"status": "In Progress"})
      self.generator.modify_object(first_ct, {"status": "Finished"})
      # Delete second task
      response = self.generator.api.delete(second_ct)
      self.assert200(response)

      ctg = db.session.query(CycleTaskGroup).get(ctg.id)
      self.assertEqual(ctg.status, "Finished")

  def test_cycle_change_on_ct_status_transition(self):
    """Test cycle is_current change on task Finished to In Progress transition
    """
    with freeze_time("2016-6-10 13:00:00"):  # Friday, 6/10/2016
      _, wf = self.generator.generate_workflow(self.weekly_wf)
      self.generator.activate_workflow(wf)

    ctg = db.session.query(
        CycleTaskGroup
    ).join(
        Cycle
    ).join(
        Workflow
    ).filter(
        Workflow.id == wf.id
    ).one()
    c_id = ctg.cycle.id
    first_ct, second_ct = db.session.query(CycleTaskGroupObjectTask).join(
        Cycle).join(Workflow).filter(Workflow.id == wf.id).all()
    self.api.put(first_ct, {"status": "Verified"})
    self.api.put(second_ct, {"status": "Verified"})
    # cycle now should have is_current == False
    cycle = db.session.query(Cycle).get(c_id)

    self.assertEqual(cycle.is_current, False)

    # Move second task back to In Progress
    self.api.put(second_ct, {"status": "In Progress"})
    # cycle now should have is_current == True

    cycle = db.session.query(Cycle).get(ctg.cycle.id)
    self.assertEqual(cycle.is_current, True)

  @staticmethod
  def _get_obj(model, title):
    return db.session.query(model).filter(model.title == title).first()

  def test_cycle_task_group_dates(self):
    """Test status and dates when task status is changed """
    wf_data = {
        "title": "test workflow",
        "unit": "week",
        "repeat_every": 1,
        "task_groups": [{
            "title": "test group",
            "task_group_tasks": [{
                "title": "task1",
                "start_date": dtm.date(2016, 6, 10),
                "end_date": dtm.date(2016, 6, 14),
            }, {
                "title": "task2",
                "start_date": dtm.date(2016, 6, 13),
                "end_date": dtm.date(2016, 6, 15),
            }]
        }]
    }

    with freeze_time("2016-6-10 13:00:00"):  # Friday, 6/10/2016
      _, wf = self.generator.generate_workflow(wf_data)
      self.generator.activate_workflow(wf)

      # check task group status and dates
      tg = self._get_obj(CycleTaskGroup, "test group")
      self.assertEqual(tg.end_date, dtm.date(2016, 6, 15))
      self.assertEqual(tg.next_due_date, dtm.date(2016, 6, 14))
      self.assertEqual(tg.status, "Assigned")

      # move task1 to Verified
      task1 = self._get_obj(CycleTaskGroupObjectTask, "task1")
      self.api.put(task1, {"status": "In Progress"})
      self.api.put(task1, {"status": "Finished"})
      self.api.put(task1, {"status": "Verified"})

      # # check task group status and dates
      tg = self._get_obj(CycleTaskGroup, "test group")
      self.assertEqual(tg.end_date, dtm.date(2016, 6, 15))
      self.assertEqual(tg.next_due_date, dtm.date(2016, 6, 15))
      self.assertEqual(tg.status, "In Progress")

      # move task2 to Verified
      task2 = self._get_obj(CycleTaskGroupObjectTask, "task2")
      self.api.put(task2, {"status": "In Progress"})
      self.api.put(task2, {"status": "Finished"})
      self.api.put(task2, {"status": "Verified"})

      # # check task group status and dates
      tg = self._get_obj(CycleTaskGroup, "test group")
      self.assertIsNone(tg.next_due_date)
      self.assertEqual(tg.status, "Verified")

  def test_empty_group_status(self):
    """Test status and dates when task group is empty """
    wf_data = {
        "title": "test workflow",
        "unit": "week",
        "repeat_every": 1,
        "task_groups": [{
            "title": "test group1",
            "task_group_tasks": [{
                "title": "task1",
                "start_date": dtm.date(2016, 6, 10),
                "end_date": dtm.date(2016, 6, 13),
            }]
        }, {
            # second task group prevents from moving workflow to history
            "title": "test group2",
            "task_group_tasks": [{
                "title": "task2",
                "start_date": dtm.date(2016, 6, 14),
                "end_date": dtm.date(2016, 6, 16),
            }]
        }]
    }

    with freeze_time("2016-6-10 13:00:00"):  # Friday, 6/10/2016
      _, wf = self.generator.generate_workflow(wf_data)
      self.generator.activate_workflow(wf)

      # check task group status and dates
      tg = self._get_obj(CycleTaskGroup, "test group1")
      self.assertEqual(tg.end_date, dtm.date(2016, 6, 13))
      self.assertEqual(tg.next_due_date, dtm.date(2016, 6, 13))
      self.assertEqual(tg.status, "Assigned")

      # move task2 to Verified
      task2 = self._get_obj(CycleTaskGroupObjectTask, "task2")
      self.api.put(task2, {"status": "In Progress"})
      self.api.put(task2, {"status": "Finished"})
      self.api.put(task2, {"status": "Verified"})

      # check task group status
      cycle = self._get_obj(Cycle, "test workflow")
      self.assertEqual(cycle.status, "In Progress")

      # delete task1
      task = self._get_obj(CycleTaskGroupObjectTask, "task1")
      self.api.delete(task)

      # # check task group status and dates
      tg = self._get_obj(CycleTaskGroup, "test group1")
      self.assertIsNone(tg.end_date)
      self.assertIsNone(tg.next_due_date)
      self.assertEqual(tg.status, "Deprecated")

      # # check cycle status
      cycle = self._get_obj(Cycle, "test workflow")
      self.assertEqual(cycle.status, "Verified")
コード例 #38
0
class TestProposalApi(TestCase):
    """Test case for proposal api."""
    def setUp(self):
        super(TestProposalApi, self).setUp()
        self.api = Api()
        self.client.get("/login")

    def test_simple_get_proposal(self):
        """Test simple get proposal."""
        with factories.single_commit():
            control = factories.ControlFactory()
            proposal = factories.ProposalFactory(instance=control,
                                                 content={"field": "a"},
                                                 agenda="agenda content")
        instance_dict = {"id": control.id, "type": control.type}
        resp = self.api.get(all_models.Proposal, proposal.id)
        self.assert200(resp)
        self.assertIn("proposal", resp.json)
        data = resp.json["proposal"]
        self.assertIn("content", data)
        self.assertIn("instance", data)
        self.assertIn("agenda", data)
        self.assertDictEqual(instance_dict, data["instance"])
        self.assertDictEqual({"field": "a"}, data["content"])
        self.assertEqual("agenda content", data["agenda"])

    def test_simple_create_proposal(self):
        """Test simple create proposal."""
        new_title = "2"
        control = factories.ControlFactory(title="1")
        control_id = control.id
        control.title = new_title
        self.assertEqual(0, len(control.comments))
        resp = self.api.post(
            all_models.Proposal,
            {
                "proposal": {
                    "instance": {
                        "id": control.id,
                        "type": control.type,
                    },
                    # "content": {"123": 123},
                    "full_instance_content": control.log_json(),
                    "agenda": "update title from 1 to 2",
                    "context": None,
                }
            })
        self.assertEqual(201, resp.status_code)
        control = all_models.Control.query.get(control_id)
        self.assertEqual(1, len(control.proposals))
        self.assertIn("fields", control.proposals[0].content)
        self.assertEqual({"title": "2"},
                         control.proposals[0].content["fields"])
        self.assertEqual(1, len(control.comments))

    def test_simple_apply_status(self):
        """Test simple apply status."""
        with factories.single_commit():
            control = factories.ControlFactory(title="1")
            proposal = factories.ProposalFactory(
                instance=control,
                content={"fields": {
                    "title": "2"
                }},
                agenda="agenda content")
        control_id = control.id
        proposal_id = proposal.id
        self.assertEqual(proposal.STATES.PROPOSED, proposal.status)
        revisions = all_models.Revision.query.filter(
            all_models.Revision.resource_type == control.type,
            all_models.Revision.resource_id == control.id).all()
        self.assertEqual(1, len(revisions))
        self.assertEqual(0, len(control.comments))
        resp = self.api.put(
            proposal, {
                "proposal": {
                    "status": proposal.STATES.APPLIED,
                    "apply_reason": "approved",
                }
            })
        self.assert200(resp)
        control = all_models.Control.query.get(control_id)
        proposal = all_models.Proposal.query.get(proposal_id)
        self.assertEqual(proposal.STATES.APPLIED, proposal.status)
        self.assertEqual("2", control.title)
        revisions = all_models.Revision.query.filter(
            all_models.Revision.resource_type == control.type,
            all_models.Revision.resource_id == control.id).all()
        self.assertEqual(2, len(revisions))
        self.assertEqual("2", revisions[-1].content['title'])
        self.assertEqual(1, len(control.comments))

    def test_simple_decline_status(self):
        """Test simple decline status."""
        with factories.single_commit():
            control = factories.ControlFactory(title="1")
            proposal = factories.ProposalFactory(
                instance=control,
                content={"fields": {
                    "title": "2"
                }},
                agenda="agenda content")
        control_id = control.id
        proposal_id = proposal.id
        revisions = all_models.Revision.query.filter(
            all_models.Revision.resource_type == control.type,
            all_models.Revision.resource_id == control.id).all()
        self.assertEqual(proposal.STATES.PROPOSED, proposal.status)
        self.assertEqual(1, len(revisions))
        self.assertEqual(0, len(control.comments))
        resp = self.api.put(
            proposal, {
                "proposal": {
                    "status": proposal.STATES.DECLINED,
                    "decline_reason": "declined bla",
                }
            })
        self.assert200(resp)
        control = all_models.Control.query.get(control_id)
        proposal = all_models.Proposal.query.get(proposal_id)
        self.assertEqual(proposal.STATES.DECLINED, proposal.status)
        self.assertEqual("1", control.title)
        revisions = all_models.Revision.query.filter(
            all_models.Revision.resource_type == control.type,
            all_models.Revision.resource_id == control.id).all()
        self.assertEqual(1, len(revisions))
        self.assertEqual(1, len(control.comments))

    def test_proposal_for_acl(self):
        """Test simple add acl proposal."""
        with factories.single_commit():
            control = factories.ControlFactory(title="1")
            role = factories.AccessControlRoleFactory(name="role")
            person = factories.PersonFactory()
        control_id = control.id
        role_id = unicode(role.id)
        person_id = person.id
        control_content = control.log_json()
        control_content["access_control_list"] = [{
            "ac_role_id": role_id,
            "person": {
                "type": "Person",
                "id": person.id
            }
        }]
        resp = self.api.post(
            all_models.Proposal,
            {
                "proposal": {
                    "instance": {
                        "id": control.id,
                        "type": control.type,
                    },
                    # "content": {"123": 123},
                    "full_instance_content": control_content,
                    "agenda": "update access control roles",
                    "context": None,
                }
            })
        self.assertEqual(201, resp.status_code)
        control = all_models.Control.query.get(control_id)
        self.assertEqual(1, len(control.proposals))
        self.assertIn("access_control_list", control.proposals[0].content)
        acl = control.proposals[0].content["access_control_list"]
        self.assertIn(role_id, acl)
        role = control.proposals[0].content["access_control_list"][role_id]
        person = all_models.Person.query.get(person_id)
        self.assertEqual(
            {
                "added": [{
                    "id": person_id,
                    "email": person.email
                }],
                "deleted": [],
            }, role)
        self.assertEqual(1, len(control.comments))

    def test_proposal_delete_acl(self):
        """Test simple delete acl proposal."""
        with factories.single_commit():
            control = factories.ControlFactory(title="1")
            role = factories.AccessControlRoleFactory(name="role")
            person = factories.PersonFactory()
            factories.AccessControlListFactory(
                person=person,
                ac_role=role,
                object=control,
            )
        with factories.single_commit():
            latest_revision = all_models.Revision.query.filter(
                all_models.Revision.resource_id == control.id,
                all_models.Revision.resource_type == control.type).order_by(
                    all_models.Revision.created_at.desc()).first()
            latest_revision.content = control.log_json()

        control_id = control.id
        role_id = unicode(role.id)
        person_id = person.id
        control_content = control.log_json()
        control_content["access_control_list"] = []
        resp = self.api.post(
            all_models.Proposal,
            {
                "proposal": {
                    "instance": {
                        "id": control.id,
                        "type": control.type,
                    },
                    # "content": {"123": 123},
                    "full_instance_content": control_content,
                    "agenda": "delete access control roles",
                    "context": None,
                }
            })
        self.assertEqual(201, resp.status_code)
        control = all_models.Control.query.get(control_id)
        self.assertEqual(1, len(control.proposals))
        self.assertIn("access_control_list", control.proposals[0].content)
        acl = control.proposals[0].content["access_control_list"]
        self.assertIn(role_id, acl)
        role = control.proposals[0].content["access_control_list"][role_id]
        person = all_models.Person.query.get(person_id)
        self.assertEqual(
            {
                "added": [],
                "deleted": [{
                    "id": person_id,
                    "email": person.email
                }],
            }, role)
        self.assertEqual(1, len(control.comments))

    def test_apply_acl(self):  # pylint: disable=too-many-locals
        """Test simple apply acl proposal."""
        with factories.single_commit():
            control = factories.ControlFactory(title="1")
            role_1 = factories.AccessControlRoleFactory(name="role_1",
                                                        object_type="Control")
            role_2 = factories.AccessControlRoleFactory(name="role_2",
                                                        object_type="Control")
            role_3 = factories.AccessControlRoleFactory(name="role_3",
                                                        object_type="Control")
            role_4 = factories.AccessControlRoleFactory(name="role_4",
                                                        object_type="Control")
            role_5 = factories.AccessControlRoleFactory(name="role_5",
                                                        object_type="Control")
            person_1 = factories.PersonFactory()
            person_2 = factories.PersonFactory()
            person_3 = factories.PersonFactory()
            factories.AccessControlListFactory(
                person=person_1,
                ac_role=role_1,
                object=control,
            )
            factories.AccessControlListFactory(
                person=person_2,
                ac_role=role_2,
                object=control,
            )
            factories.AccessControlListFactory(
                person=person_3,
                ac_role=role_3,
                object=control,
            )
            for person in [person_1, person_2, person_3]:
                factories.AccessControlListFactory(
                    person=person,
                    ac_role=role_4,
                    object=control,
                )

        with factories.single_commit():
            proposal = factories.ProposalFactory(instance=control,
                                                 content={
                                                     "access_control_list": {
                                                         role_1.id: {
                                                             "added": [{
                                                                 "id":
                                                                 person_2.id,
                                                                 "email":
                                                                 person_2.email
                                                             }],
                                                             "deleted": []
                                                         },
                                                         role_2.id: {
                                                             "added": [{
                                                                 "id":
                                                                 person_1.id,
                                                                 "email":
                                                                 person_1.email
                                                             }],
                                                             "deleted": [{
                                                                 "id":
                                                                 person_2.id,
                                                                 "email":
                                                                 person_2.email
                                                             }]
                                                         },
                                                         role_3.id: {
                                                             "added": [{
                                                                 "id":
                                                                 person_3.id,
                                                                 "email":
                                                                 person_3.email
                                                             }],
                                                             "deleted": [{
                                                                 "id":
                                                                 person_2.id,
                                                                 "email":
                                                                 person_2.email
                                                             }]
                                                         },
                                                         role_4.id: {
                                                             "added": [],
                                                             "deleted": [{
                                                                 "id":
                                                                 person_1.id,
                                                                 "email":
                                                                 person_1.email
                                                             }, {
                                                                 "id":
                                                                 person_2.id,
                                                                 "email":
                                                                 person_2.email
                                                             }, {
                                                                 "id":
                                                                 person_3.id,
                                                                 "email":
                                                                 person_3.email
                                                             }]
                                                         },
                                                         role_5.id: {
                                                             "added": [{
                                                                 "id":
                                                                 person_1.id,
                                                                 "email":
                                                                 person_1.email
                                                             }, {
                                                                 "id":
                                                                 person_2.id,
                                                                 "email":
                                                                 person_2.email
                                                             }, {
                                                                 "id":
                                                                 person_3.id,
                                                                 "email":
                                                                 person_3.email
                                                             }],
                                                             "deleted": [],
                                                         },
                                                     }
                                                 },
                                                 agenda="agenda content")
        control_id = control.id
        person_1_id = person_1.id
        person_2_id = person_2.id
        person_3_id = person_3.id
        role_1_id = role_1.id
        role_2_id = role_2.id
        role_3_id = role_3.id
        role_4_id = role_4.id
        role_5_id = role_5.id
        self.assertEqual(proposal.STATES.PROPOSED, proposal.status)
        revisions = all_models.Revision.query.filter(
            all_models.Revision.resource_type == control.type,
            all_models.Revision.resource_id == control.id).all()
        self.assertEqual(1, len(revisions))
        resp = self.api.put(proposal,
                            {"proposal": {
                                "status": proposal.STATES.APPLIED
                            }})
        self.assert200(resp)
        control = all_models.Control.query.get(control_id)
        result_dict = collections.defaultdict(set)
        for acl in control.access_control_list:
            result_dict[acl.ac_role_id].add(acl.person_id)
        self.assertEqual({person_1_id, person_2_id}, result_dict[role_1_id])
        self.assertEqual({person_1_id}, result_dict[role_2_id])
        self.assertEqual({person_3_id}, result_dict[role_3_id])
        self.assertEqual(set([]), result_dict[role_4_id])
        self.assertEqual({person_1_id, person_2_id, person_3_id},
                         result_dict[role_5_id])

    def test_change_cad(self):
        """Test create proposal with change CAVs."""
        with factories.single_commit():
            control = factories.ControlFactory(title="1")
            cad = factories.CustomAttributeDefinitionFactory(
                definition_type="control")
            factories.CustomAttributeValueFactory(custom_attribute=cad,
                                                  attributable=control,
                                                  attribute_value="123")
        control_id = control.id
        cad_id = cad.id
        data = control.log_json()
        del data["custom_attributes"]
        data["custom_attribute_values"][0]["attribute_value"] = "321"
        resp = self.api.post(
            all_models.Proposal,
            {
                "proposal": {
                    "instance": {
                        "id": control.id,
                        "type": control.type,
                    },
                    # "content": {"123": 123},
                    "full_instance_content": data,
                    "agenda": "update cav",
                    "context": None,
                }
            })
        self.assertEqual(201, resp.status_code)
        control = all_models.Control.query.get(control_id)
        self.assertEqual(1, len(control.proposals))
        self.assertIn("custom_attribute_values", control.proposals[0].content)
        self.assertEqual(
            {
                unicode(cad_id): {
                    "attribute_value": u"321",
                    "attribute_object": None,
                    "remove_cav": False
                }
            }, control.proposals[0].content["custom_attribute_values"])
        self.assertEqual(1, len(control.comments))

    @ddt.data(True, False)
    def test_apply_cad(self, remove_cav):
        """Test apply proposal with change CAVs."""
        with factories.single_commit():
            control = factories.ControlFactory(title="1")
            cad = factories.CustomAttributeDefinitionFactory(
                definition_type="control")
        control_id = control.id
        proposal = factories.ProposalFactory(instance=control,
                                             content={
                                                 "custom_attribute_values": {
                                                     cad.id: {
                                                         "attribute_value":
                                                         "321",
                                                         "attribute_object":
                                                         None,
                                                         "remove_cav":
                                                         remove_cav,
                                                     },
                                                 },
                                             },
                                             agenda="agenda content")
        revisions = all_models.Revision.query.filter(
            all_models.Revision.resource_type == control.type,
            all_models.Revision.resource_id == control.id).all()
        self.assertEqual(1, len(revisions))
        resp = self.api.put(proposal,
                            {"proposal": {
                                "status": proposal.STATES.APPLIED
                            }})
        self.assert200(resp)
        control = all_models.Control.query.get(control_id)
        revisions = all_models.Revision.query.filter(
            all_models.Revision.resource_type == control.type,
            all_models.Revision.resource_id == control.id).all()
        self.assertEqual(2, len(revisions))
        self.assertEqual("321",
                         control.custom_attribute_values[0].attribute_value)

    @ddt.data(True, False)
    def test_apply_mapping_cad(self, remove_cav):
        """Test apply mapping CAVs proposal."""
        with factories.single_commit():
            control = factories.ControlFactory(title="1")
            cad = factories.CustomAttributeDefinitionFactory(
                definition_type="control", attribute_type="Map:Person")
            person = factories.PersonFactory()
            cav = factories.CustomAttributeValueFactory(
                custom_attribute=cad,
                attributable=control,
                attribute_object_id=person.id,
                attribute_value="Person",
            )
        self.assertEqual(person,
                         control.custom_attribute_values[0].attribute_object)
        control_id = control.id
        proposal = factories.ProposalFactory(instance=control,
                                             content={
                                                 "custom_attribute_values": {
                                                     cad.id: {
                                                         "attribute_value":
                                                         "Person",
                                                         "attribute_object":
                                                         None,
                                                         "remove_cav":
                                                         remove_cav,
                                                     },
                                                 },
                                             },
                                             agenda="agenda content")
        resp = self.api.put(proposal,
                            {"proposal": {
                                "status": proposal.STATES.APPLIED
                            }})
        self.assert200(resp)
        control = all_models.Control.query.get(control_id)
        if remove_cav:
            self.assertFalse(control.custom_attribute_values)
        else:
            cav = control.custom_attribute_values[0]
            self.assertEqual("Person", cav.attribute_value)
            self.assertIsNone(cav.attribute_object_id)

    def test_change_cad_oldstile(self):
        """Test create CAVs proposal in old style."""
        with factories.single_commit():
            control = factories.ControlFactory(title="1")
            cad = factories.CustomAttributeDefinitionFactory(
                definition_type="control")
            factories.CustomAttributeValueFactory(custom_attribute=cad,
                                                  attributable=control,
                                                  attribute_value="123")
        control_id = control.id
        cad_id = cad.id
        data = control.log_json()
        data["custom_attributes"] = {cad.id: "321"}
        resp = self.api.post(
            all_models.Proposal,
            {
                "proposal": {
                    "instance": {
                        "id": control.id,
                        "type": control.type,
                    },
                    # "content": {"123": 123},
                    "full_instance_content": data,
                    "agenda": "update cav",
                    "context": None,
                }
            })
        self.assertEqual(201, resp.status_code)
        control = all_models.Control.query.get(control_id)
        self.assertEqual(1, len(control.proposals))
        self.assertIn("custom_attribute_values", control.proposals[0].content)
        self.assertEqual(
            {
                unicode(cad_id): {
                    "attribute_value": u"321",
                    "attribute_object": None,
                    "remove_cav": True
                }
            }, control.proposals[0].content["custom_attribute_values"])
        self.assertEqual(1, len(control.comments))

    def test_change_mapping(self):
        """Test create mapping proposal."""
        setuped_kind, update_kind = all_models.Option.query.filter(
            all_models.Option.role == "control_kind")[:2]
        control = factories.ControlFactory(title="1", kind=setuped_kind)
        control_id = control.id
        data = control.log_json()
        update_kind_json = update_kind.log_json()
        data["kind"] = update_kind_json
        resp = self.api.post(
            all_models.Proposal,
            {
                "proposal": {
                    "instance": {
                        "id": control.id,
                        "type": control.type,
                    },
                    # "content": {"123": 123},
                    "full_instance_content": data,
                    "agenda": "update kind",
                    "context": None,
                }
            })
        self.assertEqual(201, resp.status_code)
        control = all_models.Control.query.get(control_id)
        self.assertEqual(1, len(control.proposals))
        self.assertIn("mapping_fields", control.proposals[0].content)
        self.assertIn("kind", control.proposals[0].content["mapping_fields"])
        self.assertEqual(
            json.loads(utils.as_json(update_kind_json)),
            control.proposals[0].content["mapping_fields"]["kind"])
        self.assertEqual(1, len(control.comments))

    def test_apply_mapping(self):
        """Test apply mapping proposal."""
        setuped_kind, update_kind = all_models.Option.query.filter(
            all_models.Option.role == "control_kind")[:2]
        with factories.single_commit():
            control = factories.ControlFactory(title="1", kind=setuped_kind)
            proposal = factories.ProposalFactory(instance=control,
                                                 content={
                                                     "mapping_fields": {
                                                         "kind": {
                                                             "id":
                                                             update_kind.id,
                                                             "type":
                                                             update_kind.type,
                                                         },
                                                     },
                                                 },
                                                 agenda="agenda content")
        control_id = control.id
        proposal_id = proposal.id
        update_kind_id = update_kind.id
        self.assertEqual(proposal.STATES.PROPOSED, proposal.status)
        revisions = all_models.Revision.query.filter(
            all_models.Revision.resource_type == control.type,
            all_models.Revision.resource_id == control.id).all()
        self.assertEqual(1, len(revisions))
        self.assertEqual(0, len(control.comments))
        resp = self.api.put(
            proposal, {
                "proposal": {
                    "status": proposal.STATES.APPLIED,
                    "apply_reason": "approved",
                }
            })
        self.assert200(resp)
        control = all_models.Control.query.get(control_id)
        self.assertEqual(all_models.Option.query.get(update_kind_id),
                         control.kind)
        proposal = all_models.Proposal.query.get(proposal_id)
        self.assertEqual(proposal.STATES.APPLIED, proposal.status)
        revisions = all_models.Revision.query.filter(
            all_models.Revision.resource_type == control.type,
            all_models.Revision.resource_id == control.id).all()
        self.assertEqual(2, len(revisions))
        self.assertIn("kind", revisions[-1].content)
        self.assertIn("id", revisions[-1].content["kind"])
        self.assertEqual(update_kind_id, revisions[-1].content["kind"]["id"])
        self.assertEqual(1, len(control.comments))

    def test_change_mapping_to_empty(self):
        """Test create empty mapping proposal."""
        setuped_kind = all_models.Option.query.filter(
            all_models.Option.role == "control_kind").first()
        control = factories.ControlFactory(title="1", kind=setuped_kind)
        control.kind = None
        control_id = control.id
        resp = self.api.post(
            all_models.Proposal,
            {
                "proposal": {
                    "instance": {
                        "id": control.id,
                        "type": control.type,
                    },
                    # "content": {"123": 123},
                    "full_instance_content": control.log_json(),
                    "agenda": "update kind",
                    "context": None,
                }
            })
        self.assertEqual(201, resp.status_code)
        control = all_models.Control.query.get(control_id)
        self.assertEqual(1, len(control.proposals))
        self.assertIn("mapping_fields", control.proposals[0].content)
        self.assertIn("kind", control.proposals[0].content["mapping_fields"])
        self.assertIsNone(
            control.proposals[0].content["mapping_fields"]["kind"])
        self.assertEqual(1, len(control.comments))

    def test_apply_empty_mapping(self):
        """Test apply empty mapping proposal."""
        setuped_kind = all_models.Option.query.filter(
            all_models.Option.role == "control_kind").first()
        with factories.single_commit():
            control = factories.ControlFactory(title="1", kind=setuped_kind)
            proposal = factories.ProposalFactory(
                instance=control,
                content={"mapping_fields": {
                    "kind": None
                }},
                agenda="agenda content")
        control_id = control.id
        proposal_id = proposal.id
        self.assertEqual(proposal.STATES.PROPOSED, proposal.status)
        revisions = all_models.Revision.query.filter(
            all_models.Revision.resource_type == control.type,
            all_models.Revision.resource_id == control.id).all()
        self.assertEqual(1, len(revisions))
        self.assertEqual(0, len(control.comments))
        resp = self.api.put(
            proposal, {
                "proposal": {
                    "status": proposal.STATES.APPLIED,
                    "apply_reason": "approved",
                }
            })
        self.assert200(resp)
        control = all_models.Control.query.get(control_id)
        self.assertIsNone(control.kind)
        proposal = all_models.Proposal.query.get(proposal_id)
        self.assertEqual(proposal.STATES.APPLIED, proposal.status)
        revisions = all_models.Revision.query.filter(
            all_models.Revision.resource_type == control.type,
            all_models.Revision.resource_id == control.id).all()
        self.assertEqual(2, len(revisions))
        self.assertIn("kind", revisions[-1].content)
        self.assertIsNone(revisions[-1].content["kind"])
        self.assertEqual(1, len(control.comments))

    def test_change_mapping_list(self):
        """Test create mapping list proposal."""
        with factories.single_commit():
            cat = factories.ControlCategoryFactory()
            control = factories.ControlFactory(title="1")
        data = control.log_json()
        control_id = control.id
        data["categories"] = [{"id": cat.id, "type": cat.type}]
        cat_id = cat.id
        resp = self.api.post(
            all_models.Proposal,
            {
                "proposal": {
                    "instance": {
                        "id": control.id,
                        "type": control.type,
                    },
                    # "content": {"123": 123},
                    "full_instance_content": data,
                    "agenda": "update categories",
                    "context": None,
                }
            })
        self.assertEqual(201, resp.status_code)
        control = all_models.Control.query.get(control_id)
        self.assertEqual(1, len(control.proposals))
        self.assertIn("mapping_list_fields", control.proposals[0].content)
        fields = control.proposals[0].content["mapping_list_fields"]
        self.assertIn("categories", fields)
        self.assertEqual(
            {
                "added": [{
                    "id": cat_id,
                    "type": "ControlCategory"
                }],
                "deleted": []
            }, fields["categories"])
        self.assertEqual(1, len(control.comments))

    def test_change_empty_mapping_list(self):
        """Test create mapping list proposal to empty."""
        with factories.single_commit():
            category = factories.ControlCategoryFactory()
            control = factories.ControlFactory(categories=[category])
        data = control.log_json()
        category_id = category.id
        control_id = control.id
        data["categories"] = []
        resp = self.api.post(
            all_models.Proposal,
            {
                "proposal": {
                    "instance": {
                        "id": control.id,
                        "type": control.type,
                    },
                    # "content": {"123": 123},
                    "full_instance_content": data,
                    "agenda": "update categories",
                    "context": None,
                }
            })
        self.assertEqual(201, resp.status_code)
        control = all_models.Control.query.get(control_id)
        category = all_models.ControlCategory.query.get(category_id)
        self.assertEqual(1, len(control.proposals))
        self.assertIn("mapping_list_fields", control.proposals[0].content)
        fields = control.proposals[0].content["mapping_list_fields"]
        self.assertIn("categories", fields)
        self.assertEqual(
            {
                "added": [],
                "deleted": [json.loads(utils.as_json(category.log_json()))]
            }, fields["categories"])
        self.assertEqual(1, len(control.comments))

    def test_apply_empty_mapping_list(self):
        """Test apply mapping list proposal to empty."""
        with factories.single_commit():
            category = factories.ControlCategoryFactory()
            control = factories.ControlFactory()
            control.categories.append(category)
        control_id = control.id
        category_id = category.id
        with factories.single_commit():
            proposal = factories.ProposalFactory(
                instance=control,
                content={
                    "mapping_list_fields": {
                        "categories": {
                            "added": [],
                            "deleted": [
                                {
                                    "id": category_id,
                                    "type": "ControlCategory"
                                },
                            ]
                        }
                    }
                },
                agenda="agenda content")
        proposal_id = proposal.id
        revisions = all_models.Revision.query.filter(
            all_models.Revision.resource_type == control.type,
            all_models.Revision.resource_id == control.id).all()
        self.assertEqual(1, len(revisions))
        resp = self.api.put(
            proposal, {
                "proposal": {
                    "status": proposal.STATES.APPLIED,
                    "apply_reason": "approved",
                }
            })
        self.assertEqual(200, resp.status_code)
        control = all_models.Control.query.get(control_id)
        proposal = all_models.Proposal.query.get(proposal_id)
        self.assertEqual(proposal.STATES.APPLIED, proposal.status)
        control = all_models.Control.query.get(control_id)
        self.assertEqual([], control.categories)
        revisions = all_models.Revision.query.filter(
            all_models.Revision.resource_type == control.type,
            all_models.Revision.resource_id == control.id).all()
        self.assertEqual(2, len(revisions))
        self.assertEqual(1, len(control.comments))

    def test_apply_mapping_list(self):
        """Test apply mapping list proposal."""
        with factories.single_commit():
            category = factories.ControlCategoryFactory()
            control = factories.ControlFactory()
        control_id = control.id
        category_id = category.id
        with factories.single_commit():
            proposal = factories.ProposalFactory(
                instance=control,
                content={
                    "mapping_list_fields": {
                        "categories": {
                            "deleted": [],
                            "added": [
                                {
                                    "id": category_id,
                                    "type": "ControlCategory"
                                },
                            ]
                        }
                    }
                },
                agenda="agenda content")
        proposal_id = proposal.id
        revisions = all_models.Revision.query.filter(
            all_models.Revision.resource_type == control.type,
            all_models.Revision.resource_id == control.id).all()
        self.assertEqual(1, len(revisions))
        resp = self.api.put(
            proposal, {
                "proposal": {
                    "status": proposal.STATES.APPLIED,
                    "apply_reason": "approved",
                }
            })
        self.assertEqual(200, resp.status_code)
        control = all_models.Control.query.get(control_id)
        proposal = all_models.Proposal.query.get(proposal_id)
        self.assertEqual(proposal.STATES.APPLIED, proposal.status)
        control = all_models.Control.query.get(control_id)
        self.assertEqual([all_models.ControlCategory.query.get(category_id)],
                         control.categories)
        revisions = all_models.Revision.query.filter(
            all_models.Revision.resource_type == control.type,
            all_models.Revision.resource_id == control.id).all()
        self.assertEqual(2, len(revisions))
        self.assertEqual(1, len(control.comments))
        comment = control.comments[0]
        self.assertEqual(proposal, comment.initiator_instance)
コード例 #39
0
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)
コード例 #40
0
class TestRevisionHistory(TestCase):
  """Test checks permissions for revision history."""

  def setUp(self):
    super(TestRevisionHistory, self).setUp()
    self.api = Api()
    roles = {r.name: r for r in all_models.Role.query.all()}

    with factories.single_commit():
      factories.AccessControlRoleFactory(
          name="ACL_Reader",
          object_type="Control",
          update=0,
      )
      factories.AccessControlRoleFactory(
          name="ACL_Editor",
          object_type="Control"
      ),

    with factories.single_commit():
      self.control = factories.ControlFactory()
      self.program = factories.ProgramFactory()
      self.program.context.related_object = self.program
      self.relationship = factories.RelationshipFactory(
          source=self.program,
          destination=self.control,
          context=self.program.context,
      )
      self.people = {
          "Creator": factories.PersonFactory(),
          "Reader": factories.PersonFactory(),
          "Editor": factories.PersonFactory(),
          "Administrator": factories.PersonFactory(),
          "ACL_Reader": factories.PersonFactory(),
          "ACL_Editor": factories.PersonFactory(),
          "Program Editors": factories.PersonFactory(),
          "Program Managers": factories.PersonFactory(),
          "Program Readers": factories.PersonFactory(),
      }
      for role_name in ["Creator", "Reader", "Editor", "Administrator"]:
        rbac_factories.UserRoleFactory(role=roles[role_name],
                                       person=self.people[role_name])
      for role_name in ["Program Editors",
                        "Program Managers",
                        "Program Readers"]:
        person = self.people[role_name]
        rbac_factories.UserRoleFactory(role=roles["Creator"], person=person)
        factories.AccessControlPersonFactory(
            ac_list=self.program.acr_name_acl_map[role_name],
            person=self.people[role_name],
        )
    with factories.single_commit():
      for role_name in ["ACL_Reader", "ACL_Editor"]:
        rbac_factories.UserRoleFactory(role=roles["Creator"],
                                       person=self.people[role_name])
        factories.AccessControlPersonFactory(
            ac_list=self.control.acr_name_acl_map[role_name],
            person=self.people[role_name],
        )

  @ddt.data(
      ("Creator", True),
      ("Reader", False),
      ("Editor", False),
      ("ACL_Reader", False),
      ("ACL_Editor", False),
      ("Administrator", False),
      ("Program Editors", False),
      ("Program Managers", False),
      ("Program Readers", False),
  )
  @ddt.unpack
  def test_get(self, role_name, empty):
    """Test get revision history for {0}."""
    control_id = self.control.id
    query = all_models.Revision.query.filter(
        all_models.Revision.resource_id == control_id,
        all_models.Revision.resource_type == self.control.type,
    )
    if empty:
      ids = []
    else:
      ids = [i.id for i in query]
    self.api.set_user(self.people[role_name])
    self.client.get("/login")
    resp = self.api.client.get(
        "/api/revisions"
        "?resource_type=Control&resource_id={}".format(control_id)
    )
    self.assertEqual(
        ids,
        [i["id"] for i in resp.json["revisions_collection"]["revisions"]])

  def update_revisions(self, obj):
    """Assert revision diff between api and calculated in test.."""
    query = all_models.Revision.query.filter(
        all_models.Revision.resource_id == obj.id,
        all_models.Revision.resource_type == obj.type,
    ).order_by(
        sa.desc(all_models.Revision.updated_at)
    )
    diffs = [(i.id, json.loads(json.dumps(i.diff_with_current())))
             for i in query]
    for i in range(3):
      resp = self.api.client.get(
          "/api/revisions"
          "?__sort=-updated_at"
          "&resource_type={}"
          "&resource_id={}"
          "&_={}".format(
              obj.type,
              obj.id,
              random.randint(1100000000, 1153513123412)
          )
      )
      resp_diffs = [(i["id"], i["diff_with_current"])
                    for i in resp.json["revisions_collection"]["revisions"]]
      self.assertDictEqual(dict(diffs), dict(resp_diffs))
    self.assertFalse(any(resp_diffs[0][1].values()))

  def test_control_memcache(self):
    """Test get updated control revisions."""
    control_id = self.control.id
    self.api.put(self.control, {"title": "new_title"})
    self.control = all_models.Control.eager_query().get(control_id)
    self.update_revisions(self.control)
    db.session.expire_all()
    self.control = all_models.Control.eager_query().get(control_id)
    self.api.put(self.control, {"description": "new test description BLA"})
    self.control = all_models.Control.eager_query().get(control_id)
    self.update_revisions(self.control)
    self.api.put(self.control, {"description": "bla bla bla"})
    self.control = all_models.Control.eager_query().get(control_id)
    self.update_revisions(self.control)

  def test_risk_memcache(self):
    """Test get updated risk revisions."""
    risk = factories.RiskFactory()
    risk_id = risk.id
    self.api.put(risk, {"title": "new_title"})
    self.update_revisions(risk)
    db.session.expire_all()
    risk = all_models.Risk.eager_query().get(risk_id)
    self.api.put(risk, {"description": "new test description BLA"})
    risk = all_models.Risk.eager_query().get(risk_id)
    self.update_revisions(risk)
    risk = all_models.Risk.eager_query().get(risk_id)
    self.api.put(risk, {"description": "BLA bla bla"})
    risk = all_models.Risk.eager_query().get(risk_id)
    self.update_revisions(risk)

  def test_change_log(self):
    """Test Change log where CAV attribute_object_id is str in revision
     content"""
    person = factories.PersonFactory()
    cad = factories.CustomAttributeDefinitionFactory(
        definition_type="control",
        definition_id=None,
        attribute_type="Map:Person",
        title="Global Person CA",
    )
    factories.CustomAttributeValueFactory(
        attributable=self.control,
        custom_attribute=cad,
        attribute_value=person.type,
        attribute_object_id=str(person.id),
    )
    self.api.put(self.control, {"title": "new_title"})
    control = all_models.Control.eager_query().get(self.control.id)
    self.update_revisions(control)

  @ddt.data(True, False)
  def test_get_mandatory_acrs(self, mandatory):
    """ACR and mandatory meta info if mandatory flag is {0}."""
    control_id = self.control.id
    acr = factories.AccessControlRoleFactory(name="test_name",
                                             object_type=self.control.type,
                                             mandatory=mandatory)
    acr_id = acr.id
    resp = self.api.client.get(
        "/api/revisions"
        "?resource_type=Control&resource_id={}".format(control_id)
    )
    collection = resp.json["revisions_collection"]["revisions"]
    self.assertTrue(collection)
    self.assertIn("meta", collection[0])
    self.assertIn("mandatory", collection[0]["meta"])
    self.assertIn("access_control_roles", collection[0]["meta"]["mandatory"])
    mandatory_acrs = collection[0]["meta"]["mandatory"]["access_control_roles"]
    self.assertEqual(mandatory, acr_id in mandatory_acrs)

  @ddt.data(True, False)
  def test_get_mandatory_cads(self, mandatory):
    """CAD and mandatory meta info if mandatory flag is {0}."""
    control_id = self.control.id
    cad = factories.CustomAttributeDefinitionFactory(
        title="test_name",
        definition_type="control",
        mandatory=mandatory)
    cad_id = cad.id
    resp = self.api.client.get(
        "/api/revisions"
        "?resource_type=Control&resource_id={}".format(control_id)
    )
    collection = resp.json["revisions_collection"]["revisions"]
    self.assertTrue(collection)
    self.assertIn("meta", collection[0])
    self.assertIn("mandatory", collection[0]["meta"])
    mandatory_meta = collection[0]["meta"]["mandatory"]
    self.assertIn("custom_attribute_definitions", mandatory_meta)
    mandatory_cads = mandatory_meta["custom_attribute_definitions"]
    self.assertEqual(mandatory, cad_id in mandatory_cads)

  @ddt.data(
      {"factory": factories.ControlFactory,
       "fields": ['test_plan', 'status', 'notes',
                  'description', 'title', 'slug', 'folder']},
      {"factory": factories.RiskFactory,
       "fields": ['test_plan', 'status', 'description',
                  'notes', 'title', 'slug', 'folder', 'risk_type']},
  )
  @ddt.unpack
  def test_get_mandatory_fields(self, factory, fields):
    """Fields mandatory meta info for {factory._meta.model}."""
    instance = factory()
    resp = self.api.client.get(
        "/api/revisions"
        "?resource_type={}&resource_id={}".format(instance.type, instance.id)
    )
    collection = resp.json["revisions_collection"]["revisions"]
    self.assertTrue(collection)
    self.assertIn("meta", collection[0])
    self.assertIn("mandatory", collection[0]["meta"])
    mandatory_meta = collection[0]["meta"]["mandatory"]
    self.assertIn("fields", mandatory_meta)
    self.assertItemsEqual(fields, mandatory_meta["fields"])

  @ddt.data(
      {"factory": factories.ControlFactory, "fields": []},
      {"factory": factories.RiskFactory, "fields": []},
      {"factory": factories.AssessmentFactory, "fields": []},
  )
  @ddt.unpack
  def test_mandatory_mapping_list(self, factory, fields):
    """Mapping List mandatory meta info for {factory._meta.model}."""
    instance = factory()
    resp = self.api.client.get(
        "/api/revisions"
        "?resource_type={}&resource_id={}".format(instance.type, instance.id)
    )
    collection = resp.json["revisions_collection"]["revisions"]
    self.assertTrue(collection)
    self.assertIn("meta", collection[0])
    self.assertIn("mandatory", collection[0]["meta"])
    mandatory_meta = collection[0]["meta"]["mandatory"]
    self.assertIn("mapping_list_fields", mandatory_meta)
    self.assertEqual(fields, mandatory_meta["mapping_list_fields"])

  @ddt.data(
      {"factory": factories.ControlFactory, "fields": []},
      {"factory": factories.RiskFactory, "fields": []},
      {"factory": factories.AssessmentFactory, "fields": ["audit"]},
  )
  @ddt.unpack
  def test_mandatory_mappings(self, factory, fields):
    """Mapping fields mandatory meta info for {factory._meta.model}."""
    instance = factory()
    resp = self.api.client.get(
        "/api/revisions"
        "?resource_type={}&resource_id={}".format(instance.type, instance.id)
    )
    collection = resp.json["revisions_collection"]["revisions"]
    self.assertTrue(collection)
    self.assertIn("meta", collection[0])
    self.assertIn("mandatory", collection[0]["meta"])
    mandatory_meta = collection[0]["meta"]["mandatory"]
    self.assertIn("mapping_fields", mandatory_meta)
    self.assertEqual(fields, mandatory_meta["mapping_fields"])
コード例 #41
0
class TestReviewStatusUpdate(TestCase):
  """Base TestCase class automatic review status update."""

  def setUp(self):
    super(TestReviewStatusUpdate, self).setUp()
    self.api = Api()
    self.api.login_as_external()

    self.generator = generator.ObjectGenerator()

  def test_gca(self):
    """if GCA of reviewable is changed review -> unreviewed"""
    with factories.single_commit():
      ca_factory = factories.CustomAttributeDefinitionFactory
      gca = ca_factory(
          definition_type="risk",
          title="rich_test_gca",
          attribute_type="Rich Text"
      )
      risk = factories.RiskFactory()
      review = factories.ReviewFactory(
          status=all_models.Review.STATES.REVIEWED, reviewable=risk
      )
    review_id = review.id
    reviewable = review.reviewable

    review = all_models.Review.query.get(review_id)

    self.assertEqual(review.status, all_models.Review.STATES.REVIEWED)

    self.api.modify_object(
        reviewable, {
            "custom_attribute_values":
            [{
                "custom_attribute_id": gca.id,
                "attribute_value": "new_value",
            }],
        }
    )

    review = all_models.Review.query.get(review_id)
    self.assertEqual(review.status, all_models.Review.STATES.UNREVIEWED)

  def test_update_gca(self):
    """if existing GCA value changed review -> unreviewed"""
    with factories.single_commit():
      ca_factory = factories.CustomAttributeDefinitionFactory
      gca = ca_factory(
          definition_type="risk",
          title="rich_test_gca",
          attribute_type="Rich Text"
      )
      risk = factories.RiskFactory()

      risk.custom_attribute_values = [{
          "attribute_value": "starting_value",
          "custom_attribute_id": gca.id
      }]
      review = factories.ReviewFactory(
          status=all_models.Review.STATES.REVIEWED, reviewable=risk
      )
    review_id = review.id
    reviewable = review.reviewable

    review = all_models.Review.query.get(review_id)

    self.assertEqual(review.status, all_models.Review.STATES.REVIEWED)

    self.api.modify_object(
        reviewable, {
            "custom_attribute_values":
            [{
                "custom_attribute_id": gca.id,
                "attribute_value": "new_value",
            }],
        }
    )

    review = all_models.Review.query.get(review_id)
    self.assertEqual(review.status, all_models.Review.STATES.UNREVIEWED)

  @ddt.data("custom attr", "slug", "self")
  def test_gca_with_varying_titles(self, title):
    """if GCA with any title is changed review -> unreviewed"""
    with factories.single_commit():
      ca_factory = factories.CustomAttributeDefinitionFactory
      gca = ca_factory(
          definition_type="risk",
          title=title,
          attribute_type="Rich Text"
      )
      risk = factories.RiskFactory()

      risk.custom_attribute_values = [{
          "attribute_value": "starting_value",
          "custom_attribute_id": gca.id
      }]
      review = factories.ReviewFactory(
          status=all_models.Review.STATES.REVIEWED, reviewable=risk
      )
    review_id = review.id
    reviewable = review.reviewable

    review = all_models.Review.query.get(review_id)

    self.assertEqual(review.status, all_models.Review.STATES.REVIEWED)

    self.api.modify_object(
        reviewable, {
            "custom_attribute_values":
            [{
                "custom_attribute_id": gca.id,
                "attribute_value": "new_value",
            }],
        }
    )

    review = all_models.Review.query.get(review_id)
    self.assertEqual(review.status, all_models.Review.STATES.UNREVIEWED)

  def test_map_person_gca(self):
    """if Map:Person GCA value added review -> unreviewed"""
    with factories.single_commit():
      ca_factory = factories.CustomAttributeDefinitionFactory
      gca = ca_factory(
          definition_type="risk",
          title="map_test_gca",
          attribute_type="Map:Person"
      )

      user_id = all_models.Person.query.filter_by(
          email="*****@*****.**"
      ).one().id

      risk = factories.RiskFactory()
      review = factories.ReviewFactory(
          status=all_models.Review.STATES.REVIEWED, reviewable=risk
      )

    review_id = review.id
    reviewable = review.reviewable
    review = all_models.Review.query.get(review_id)

    self.assertEqual(review.status, all_models.Review.STATES.REVIEWED)

    self.api.modify_object(
        reviewable, {
            "custom_attribute_values":
            [{
                "custom_attribute_id": gca.id,
                "attribute_object_id": user_id,
                "attribute_value": "Person",
            }],
        }
    )

    review = all_models.Review.query.get(review_id)
    self.assertEqual(review.status, all_models.Review.STATES.UNREVIEWED)

  def test_update_map_person_gca(self):
    """if existing Map:Person GCA value changed review -> unreviewed"""
    with factories.single_commit():
      ca_factory = factories.CustomAttributeDefinitionFactory
      gca = ca_factory(
          definition_type="risk",
          title="map_test_gca",
          attribute_type="Map:Person"
      )

      first_user_id = all_models.Person.query.filter_by(
          email="*****@*****.**"
      ).one().id
      second_user_id = factories.PersonFactory().id

      risk = factories.RiskFactory()
      risk.custom_attribute_values = [{
          "attribute_object_id": first_user_id,
          "custom_attribute_id": gca.id,
          "attribute_value": "Person"
      }]
      review = factories.ReviewFactory(
          status=all_models.Review.STATES.REVIEWED, reviewable=risk
      )

    review_id = review.id
    reviewable = review.reviewable
    review = all_models.Review.query.get(review_id)

    self.assertEqual(review.status, all_models.Review.STATES.REVIEWED)

    self.api.modify_object(
        reviewable, {
            "custom_attribute_values":
            [{
                "custom_attribute_id": gca.id,
                "attribute_object_id": second_user_id,
                "attribute_value": "Person",
            }],
        }
    )

    review = all_models.Review.query.get(review_id)
    self.assertEqual(review.status, all_models.Review.STATES.UNREVIEWED)

  def test_delete_map_person_gca(self):
    """if existing Map:Person GCA value deleted review -> unreviewed"""
    with factories.single_commit():
      ca_factory = factories.CustomAttributeDefinitionFactory
      gca = ca_factory(
          definition_type="risk",
          title="map_test_gca",
          attribute_type="Map:Person"
      )

      user_id = all_models.Person.query.filter_by(
          email="*****@*****.**"
      ).one().id

      risk = factories.RiskFactory()
      risk.custom_attribute_values = [{
          "attribute_object_id": user_id,
          "custom_attribute_id": gca.id,
          "attribute_value": "Person"
      }]
      review = factories.ReviewFactory(
          status=all_models.Review.STATES.REVIEWED, reviewable=risk
      )

    review_id = review.id
    reviewable = review.reviewable
    review = all_models.Review.query.get(review_id)

    self.assertEqual(review.status, all_models.Review.STATES.REVIEWED)

    self.api.modify_object(
        reviewable, {
            "custom_attribute_values":
            [{
                "custom_attribute_id": gca.id,
                "attribute_object_id": None,
                "attribute_object": None,
                "attribute_value": "Person",
            }],
        }
    )

    review = all_models.Review.query.get(review_id)
    self.assertEqual(review.status, all_models.Review.STATES.UNREVIEWED)

  def test_reference_url(self):
    """If reference url is updated state should not updated"""
    with factories.single_commit():
      risk = factories.RiskFactory()
      doc = factories.DocumentReferenceUrlFactory(
          title="Simple title",
          link="some_url.com",
          description="mega description",
          parent_obj={
              "id": risk.id,
              "type": "Risk"
          }
      )
      review = factories.ReviewFactory(
          status=all_models.Review.STATES.REVIEWED, reviewable=risk
      )
    review_id = review.id

    self.api.modify_object(doc, {"link": "new_link.com"})
    review = all_models.Review.query.get(review_id)
    self.assertEqual(review.status, all_models.Review.STATES.REVIEWED)

  def test_acl_roles(self):
    """Update of reviewable ACL shouldn't change review status"""
    with factories.single_commit():
      risk = factories.RiskFactory()
      review = factories.ReviewFactory(
          status=all_models.Review.STATES.REVIEWED, reviewable=risk
      )
    review_id = review.id

    ac_role_id = all_models.AccessControlRole.query.filter_by(
        name="Admin", object_type="Risk"
    ).one().id

    user_id = all_models.Person.query.filter_by(
        email="*****@*****.**"
    ).one().id

    self.api.modify_object(
        risk, {
            "access_control_list":
            [{
                "ac_role_id": ac_role_id,
                "person": {
                    "id": user_id
                },
            }],
        }
    )
    review = all_models.Review.query.get(review_id)
    self.assertEqual(review.status, all_models.Review.STATES.REVIEWED)

  def test_comments(self):
    """Add comment to reviewable shouldn't update review state"""
    with factories.single_commit():
      risk = factories.RiskFactory()
      review = factories.ReviewFactory(
          status=all_models.Review.STATES.REVIEWED, reviewable=risk
      )
    review_id = review.id

    self.generator.generate_comment(
        risk, "Verifiers", "some comment", send_notification="false"
    )

    review = all_models.Review.query.get(review_id)
    self.assertEqual(review.status, all_models.Review.STATES.REVIEWED)

  def test_mapping_non_snapshotable(self):
    """Map non-snapshotable shouldn't change review status"""
    with factories.single_commit():
      risk = factories.RiskFactory()
      review = factories.ReviewFactory(
          status=all_models.Review.STATES.REVIEWED, reviewable=risk
      )
      review_id = review.id

    factories.RelationshipFactory(
        source=risk, destination=factories.IssueFactory()
    )

    review = all_models.Review.query.get(review_id)
    self.assertEqual(review.status, all_models.Review.STATES.REVIEWED)

  @ddt.data(
      "Standard",
      "Regulation",
      "Requirement",
      "Objective",
      "Control",
      "Product",
      "System",
      "Process",
      "AccessGroup",
      "Contract",
      "DataAsset",
      "Facility",
      "Market",
      "OrgGroup",
      "Policy",
      "Risk",
      "Threat",
      "Vendor"
  )
  def test_map_snapshotable(self, snapshotable):
    """Map '{}' should change review status"""
    with factories.single_commit():
      risk = factories.RiskFactory()
      review = factories.ReviewFactory(
          status=all_models.Review.STATES.REVIEWED, reviewable=risk
      )
      review_id = review.id

    self.generator.generate_relationship(
        source=risk,
        destination=factories.get_model_factory(snapshotable)(),
        context=None,
    )

    review = all_models.Review.query.get(review_id)
    self.assertEqual(review.status, all_models.Review.STATES.UNREVIEWED)

  def test_unmap_snapshotable(self):
    """Unmap snapshotable should change review status"""
    self.api.login_as_normal()

    risk = factories.RiskFactory()
    resp, review = generate_review_object(risk)
    review_id = review.id

    _, rel = self.generator.generate_relationship(
        source=risk,
        destination=factories.ProductFactory(),
        context=None,
    )

    review = all_models.Review.query.get(review_id)
    resp = self.api.modify_object(
        review, {"status": all_models.Review.STATES.REVIEWED}
    )
    self.assert200(resp)

    resp = self.api.delete(rel)
    self.assert200(resp)
    review = all_models.Review.query.get(review_id)
    self.assertEqual(review.status, all_models.Review.STATES.UNREVIEWED)

  @ddt.data(
      "Assessment",
      "Issue",
      "Program",
      "Project",
      "Audit",
      "RiskAssessment",
      "AssessmentTemplate",
      "Person",
  )
  def test_map_nonsnapshotable(self, nonsnapshotable):
    """Map '{}' shouldn't change review status"""
    risk = factories.RiskFactory()
    _, review = generate_review_object(
        risk, state=all_models.Review.STATES.REVIEWED)
    review_id = review.id

    review = all_models.Review.query.get(review_id)

    self.assertEqual(review.status, all_models.Review.STATES.REVIEWED)

    self.generator.generate_relationship(
        source=risk,
        destination=factories.get_model_factory(nonsnapshotable)(),
        context=None,
    )

    review = all_models.Review.query.get(review_id)
    self.assertEqual(review.status, all_models.Review.STATES.REVIEWED)

  def test_unmap_nonsnapshotable(self):
    """Unmap nonsnapshotable shouldn't change review status"""
    self.api.login_as_normal()

    risk = factories.RiskFactory()
    resp, review = generate_review_object(
        risk, state=all_models.Review.STATES.REVIEWED)
    review_id = review.id
    _, rel = self.generator.generate_relationship(
        source=risk,
        destination=factories.ProgramFactory(),
        context=None,
    )

    review = all_models.Review.query.get(review_id)
    resp = self.api.modify_object(
        review, {"status": all_models.Review.STATES.REVIEWED}
    )
    self.assert200(resp)
    review = all_models.Review.query.get(review_id)
    self.assertEqual(review.status, all_models.Review.STATES.REVIEWED)

    resp = self.api.delete(rel)
    self.assert200(resp)
    review = all_models.Review.query.get(review_id)
    self.assertEqual(review.status, all_models.Review.STATES.REVIEWED)

  def test_proposal_apply(self):
    """Reviewable object changed via proposal -> review.state-> UNREVIEWED"""
    risk = factories.RiskFactory()
    _, review = generate_review_object(risk)

    review_id = review.id

    proposal_content = {
        "fields": {
            "title": "new title"
        },
    }
    proposal = factories.ProposalFactory(
        instance=risk, content=proposal_content, agenda="agenda content"
    )
    self.api.modify_object(proposal, {"status": proposal.STATES.APPLIED})

    review = all_models.Review.query.get(review_id)
    self.assertEqual(review.status, all_models.Review.STATES.UNREVIEWED)

  def test_review_status_update(self):
    """Test updating folder preserves review status"""
    risk = factories.RiskFactory()
    factories.ReviewFactory(
        reviewable=risk,
        status=all_models.Review.STATES.REVIEWED,
    )
    self.api.put(risk, {"folder": factories.random_str()})
    risk = all_models.Risk.query.get(risk.id)
    self.assertEqual(risk.review.status, all_models.Review.STATES.REVIEWED)
コード例 #42
0
class TestReader(TestCase):
  """Test Assignable RBAC"""

  def setUp(self):
    super(TestReader, self).setUp()
    self.audit_id = factories.AuditFactory().id
    self.generator = Generator()
    self.api = Api()
    self.object_generator = ObjectGenerator()
    self.init_users()
    self.init_assignable()

  def init_users(self):
    """ Init users needed by the test cases """
    users = [("creator", "Creator"), ("reader", "Reader"),
             ("editor", "Editor"), ("admin", "Administrator")]
    self.users = {}
    for (name, role) in users:
      _, user = self.object_generator.generate_person(
          data={"name": name}, user_role=role)
      self.users[name] = user

  def init_assignable(self):
    """Creates the assignable object used by all the tests"""
    self.api.set_user(self.users["editor"])
    response = self.api.post(all_models.Assessment, {
        "assessment": {
            "title": "Assessment",
            "context": None,
            "audit": {
                "id": self.audit_id,
                "type": "Audit"
            }
        }
    })
    obj_id = response.json.get("assessment").get("id")
    self.assertEqual(response.status_code, 201, "Error setting up Assessment")
    self.obj_json = response.json
    self.obj = all_models.Assessment.query.get(obj_id)

  def _add_creator(self, asmnt, user):
    """Helper method for creating assignees on an object"""
    acr = all_models.AccessControlRole.query.filter(
        all_models.AccessControlRole.object_type == "Assessment",
        all_models.AccessControlRole.name == "Creators",
    ).first()
    return self.api.put(asmnt, {
        "access_control_list": [acl_helper.get_acl_json(acr.id, user.id)]
    })

  def test_basic_with_no_assignee(self):
    """Editor creates an Assessment, but doesn't assign Reader/Creator as
       assignee. Reader should have Read access, Creator should have no access
    """

    # Reader should have read access, but shouldn't be allowed to edit or
    # create another assingee
    self.api.set_user(self.users["reader"])
    response = self.api.get(all_models.Assessment, self.obj.id)
    self.assertEqual(response.status_code, 200)
    response = self.api.put(self.obj, self.obj_json)
    self.assertEqual(response.status_code, 403)
    response = self._add_creator(self.obj, self.users["reader"])
    self.assertEqual(response.status_code, 403)

    # Creator should have no access. We skip the put request because we can't
    # get the object etag.
    self.api.set_user(self.users["creator"])
    response = self.api.get(all_models.Assessment, self.obj.id)
    self.assertEqual(response.status_code, 403)
    response = self._add_creator(self.obj, self.users["reader"])
    self.assertEqual(response.status_code, 403)

  def test_basic_with_assignee(self):
    """Test if Reader/Creator have CRUD access once they become assignees"""

    # Admin adds reader as an assignee
    self.api.set_user(self.users["admin"])
    response = self._add_creator(self.obj, self.users["reader"])
    self.assertEqual(response.status_code, 200)

    # Reader is now allowed to update the object
    self.api.set_user(self.users["reader"])
    response = self.api.get(all_models.Assessment, self.obj.id)
    self.assertEqual(response.status_code, 200)
    response = self.api.put(self.obj, response.json)
    self.assertEqual(response.status_code, 200)

    # Reader adds creator as an assignee
    response = self._add_creator(self.obj, self.users["creator"])
    self.assertEqual(response.status_code, 200)

    # Creator now has CRUD access
    self.api.set_user(self.users["creator"])
    response = self.api.get(all_models.Assessment, self.obj.id)
    self.assertEqual(response.status_code, 200)
    response = self.api.put(self.obj, response.json)

    # Creator should even be allowed to add new assignees
    response = self._add_creator(self.obj, self.users["admin"])
    self.assertEqual(response.status_code, 200)

  def test_read_of_mapped_objects(self):
    """Test if assignees get Read access on all mapped objects"""

    # Editor creates a System object and maps it to the assignable object
    self.api.set_user(self.users["editor"])
    response = self.api.post(all_models.System, {
        "system": {
            "title": "System",
            "context": None,
        }
    })
    system_id = response.json.get("system").get("id")
    system = all_models.System.query.get(system_id)
    self.api.post(all_models.Relationship, {
        "relationship": {"source": {
            "id": self.obj.id,
            "type": "Assessment"
        }, "destination": {
            "id": system_id,
            "type": "System"
        }, "context": None},
    })

    # Since creator is not an assignee she should not have access to any of the
    # two objects
    self.api.set_user(self.users["creator"])
    response = self.api.get(all_models.Assessment, self.obj.id)
    self.assertEqual(response.status_code, 403)
    response = self.api.get(all_models.System, system_id)
    self.assertEqual(response.status_code, 403)

    # Editor adds creator as an assignee
    self.api.set_user(self.users["editor"])
    response = self._add_creator(self.obj, self.users["creator"])
    self.assertEqual(response.status_code, 200)

    # Creator should now have read access on the mapped object
    self.api.set_user(self.users["creator"])
    response = self.api.get(all_models.System, system_id)
    self.assertEqual(response.status_code, 403)

    # But he should still not be allowed to update
    response = self.api.put(system, response.json)
    self.assertEqual(response.status_code, 403)
コード例 #43
0
class TestStatusApiPost(TestCase):
    """Test for api calls changes states of Cycle, Tasks and Groups."""
    def setUp(self):
        super(TestStatusApiPost, self).setUp()
        self.api = Api()
        with factories.single_commit():
            self.workflow = wf_factories.WorkflowFactory()
            self.cycle = wf_factories.CycleFactory(workflow=self.workflow)
            self.group = wf_factories.CycleTaskGroupFactory(
                cycle=self.cycle, context=self.cycle.workflow.context)
            self.task = wf_factories.CycleTaskFactory(
                cycle=self.cycle,
                cycle_task_group=self.group,
                context=self.cycle.workflow.context)

    def setup_cycle_state(self, is_verification_needed):
        """Setup cycle is_verification_needed state."""
        self.cycle.is_verification_needed = is_verification_needed
        db.session.add(self.cycle)
        db.session.commit()

    @ddt.data(u"Assigned", u"In Progress", u"Finished", u"Verified",
              u"Declined")
    def test_set_state_verified_task(self, state):
        """Check state for verification required task."""
        self.setup_cycle_state(True)
        resp = self.api.put(self.task, data={"status": state})
        task = all_models.CycleTaskGroupObjectTask.query.get(
            resp.json["cycle_task_group_object_task"]["id"])
        self.assertEqual(state, task.status)

    @ddt.data((u"Assigned", True), (u"In Progress", True), (u"Finished", True),
              (u"Verified", False), (u"Declined", False))
    @ddt.unpack
    def test_state_non_verified_task(self, state, is_valid):
        """Check state for verification non required task."""
        self.setup_cycle_state(False)
        resp = self.api.put(self.task, data={"status": state})
        if is_valid:
            task = all_models.CycleTaskGroupObjectTask.query.get(
                resp.json["cycle_task_group_object_task"]["id"])
            self.assertEqual(state, task.status)
        else:
            self.assert400(resp)

    @ddt.data(u"Assigned", u"In Progress", u"Finished", u"Verified")
    def test_state_verified_group(self, state):
        """Check state for verification required group."""
        self.setup_cycle_state(True)
        resp = self.api.put(self.group, data={"status": state})
        group = all_models.CycleTaskGroup.query.get(
            resp.json["cycle_task_group"]["id"])
        self.assertEqual(state, group.status)

    @ddt.data((u"Assigned", True), (u"In Progress", True), (u"Finished", True),
              (u"Verified", False))
    @ddt.unpack
    def test_state_non_verified_group(self, state, is_valid):
        """Check state for verification non required group."""
        self.setup_cycle_state(False)
        resp = self.api.put(self.group, data={"status": state})
        if is_valid:
            group = all_models.CycleTaskGroup.query.get(
                resp.json["cycle_task_group"]["id"])
            self.assertEqual(state, group.status)
        else:
            self.assert400(resp)

    @ddt.data(u"Assigned", u"In Progress", u"Finished", u"Verified")
    def test_state_verified_cycle(self, state):
        """Check state for verification required cycle."""
        self.setup_cycle_state(True)
        resp = self.api.put(self.cycle, data={"status": state})
        cycle = all_models.Cycle.query.get(resp.json["cycle"]["id"])
        self.assertEqual(state, cycle.status)

    @ddt.data((u"Assigned", True), (u"In Progress", True), (u"Finished", True),
              (u"Verified", False))
    @ddt.unpack
    def test_state_non_verified_cycle(self, state, is_valid):
        """Check state for verification non required cycle."""
        self.setup_cycle_state(False)
        resp = self.api.put(self.cycle, data={"status": state})
        if is_valid:
            cycle = all_models.Cycle.query.get(resp.json["cycle"]["id"])
            self.assertEqual(state, cycle.status)
        else:
            self.assert400(resp)

    @ddt.data(True, False)
    def test_change_is_verification(self, flag):
        """Try to change cycle is_verification_needed."""
        self.setup_cycle_state(flag)
        resp = self.api.put(self.cycle, data={"is_verification_needed": flag})
        self.assert200(resp)
        cycle = all_models.Cycle.query.get(resp.json["cycle"]["id"])
        self.assertEqual(flag, cycle.is_verification_needed)

    @ddt.data(True, False)
    def test_change_is_vf_wrong(self, flag):
        """Try to change cycle is_verification_needed not changed."""
        self.setup_cycle_state(flag)
        resp = self.api.put(self.cycle,
                            data={"is_verification_needed": not flag})
        self.assert200(resp)
        cycle = all_models.Cycle.query.get(resp.json["cycle"]["id"])
        self.assertEqual(flag, cycle.is_verification_needed)

    @ddt.data(True, False)
    def test_change_cycle_none_flag(self, flag):
        """Try to change cycle is_verification_needed not changed
    by not sending is_verification_flag."""
        self.setup_cycle_state(flag)
        resp = self.api.put(self.cycle,
                            not_send_fields=["is_verification_needed"])
        self.assert200(resp)
        cycle = all_models.Cycle.query.get(resp.json["cycle"]["id"])
        self.assertEqual(flag, cycle.is_verification_needed)

    @ddt.data(True, False)
    def test_change_wf_none_flag(self, flag):
        """Try to change workflow is_verification_needed not changed by
    not sending is_verification_flag."""
        db.engine.execute(
            "update workflows set is_verification_needed={} where id={}".
            format(flag, self.workflow.id))
        resp = self.api.put(self.workflow,
                            not_send_fields=["is_verification_needed"])
        self.assert200(resp)
        workflow = all_models.Workflow.query.get(resp.json["workflow"]["id"])
        self.assertEqual(flag, workflow.is_verification_needed)

    @ddt.data((u"In Progress", True, True), (u"In Progress", False, True),
              (u"Declined", True, True), (u"Verified", True, False),
              (u"Finished", True, True), (u"Finished", False, False))
    @ddt.unpack
    def test_move_to_history(self, state, is_vf_needed, is_current):
        """Moved to history if state changed."""
        self.setup_cycle_state(is_vf_needed)
        resp = self.api.put(self.task, data={"status": state})
        task = all_models.CycleTaskGroupObjectTask.query.get(
            resp.json["cycle_task_group_object_task"]["id"])
        self.assertEqual(is_current, task.cycle.is_current)
コード例 #44
0
class TestPermissionsOnAssessmentTemplate(TestCase):
    """ Test check permissions for ProgramEditor on

  get and post assessment_temaplte action"""
    def setUp(self):
        super(TestPermissionsOnAssessmentTemplate, self).setUp()
        self.api = Api()
        self.generator = ObjectGenerator()
        _, program = self.generator.generate_object(all_models.Program)
        program_id = program.id
        _, self.editor = self.generator.generate_person(user_role="Creator")
        role = perms.all_models.Role.query.filter(
            perms.all_models.Role.name == "ProgramEditor").first()
        self.generator.generate_user_role(
            self.editor, role, all_models.Program.query.get(program_id))
        _, audit = self.generator.generate_object(
            all_models.Audit,
            {
                "title": "Audit",
                "program": {
                    "id": program_id
                },
                "status": "Planned"
            },
        )
        audit_id = audit.id

        generated_at = self.generator.generate_object(
            all_models.AssessmentTemplate, {
                "title": "Template",
                "_NON_RELEVANT_OBJ_TYPES": {},
                "_objectTypes": {},
                "audit": {
                    "id": audit.id
                },
                "audit_title": audit.title,
                "people_value": [],
                "default_people": {
                    "assignees": "Admin",
                    "verifiers": "Admin",
                },
                "context": {
                    "id": audit.context.id
                },
            })
        self.assessment_template_resp, assessment_template = generated_at
        assessment_template_id = assessment_template.id
        self.api.set_user(self.editor)
        self.perms_data = self.api.client.get("/permissions").json
        self.audit = all_models.Audit.query.get(audit_id)
        self.assessment_template = all_models.AssessmentTemplate.query.get(
            assessment_template_id)

    def test_post_action(self):
        """Test create action on AssessmentTemplate created by api"""
        data = [{
            "assessment_template": {
                "_NON_RELEVANT_OBJ_TYPES": {},
                "_objectTypes": {},
                "audit": {
                    "id": self.audit.id
                },
                "audit_title": self.audit.title,
                "people_value": [],
                "default_people": {
                    "assignees": "Admin",
                    "verifiers": "Admin",
                },
                "context": {
                    "id": self.audit.context.id
                },
                "title": "123",
            }
        }]
        self.api.set_user(self.editor)
        resp = self.api.post(all_models.AssessmentTemplate, data)
        self.assert200(resp)

    def test_get_action(self):
        """Test read action on AssessmentTemplate created by api"""
        resp = self.api.get(all_models.AssessmentTemplate,
                            self.assessment_template.id)
        self.assert200(resp)

    def test_put_action(self):
        """Test update action on AssessmentTemplate created by api"""
        to_update = copy.deepcopy(self.assessment_template_resp.json)
        new_title = "new_{}".format(self.assessment_template.title)
        to_update['assessment_template']['title'] = new_title
        resp = self.api.put(self.assessment_template, to_update)
        self.assert200(resp)
        assessment_tmpl = all_models.AssessmentTemplate.query.get(
            self.assessment_template.id)
        self.assertEqual(new_title, assessment_tmpl.title)

    def test_delete_action(self):
        """Test delete action on AssessmentTemplate created by api"""
        resp = self.api.delete(self.assessment_template)
        self.assert200(resp)
        self.assertFalse(
            all_models.AssessmentTemplate.query.filter(
                all_models.AssessmentTemplate ==
                self.assessment_template.id).all())
コード例 #45
0
class TestProposalRelationship(TestCase):
  """Test relationship for proposals."""

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

  @staticmethod
  def proposal_relationships(obj):
    """Get relationships between any Proposal and object.

    Args:
        obj: Instance of Proposalable object.

    Returns:
        Query which return relationship ids.
    """
    return db.session.query(all_models.Relationship.id).filter(
        sa.or_(
            sa.and_(
                all_models.Relationship.source_type == 'Proposal',
                all_models.Relationship.destination_type == obj.type,
            ),
            sa.and_(
                all_models.Relationship.source_type == obj.type,
                all_models.Relationship.destination_type == 'Proposal',
            )
        )
    )

  def create_proposal_for(self, obj):
    """Create Proposal for obj.

    Args:
        obj: Instance of Proposalable object.

    Returns:
        Response with result of Proposal creation.
    """
    response = self.api.post(
        all_models.Proposal,
        {
            "proposal": {
                "instance": {
                    "id": obj.id,
                    "type": obj.type,
                },
                "full_instance_content": obj.log_json(),
                "context": None,
            }
        }
    )
    self.assertEqual(201, response.status_code)
    return response

  @ddt.data("Risk", "Control")
  def test_create(self, model_name):
    """Test if relationship between {} and Proposal is created."""
    obj = factories.get_model_factory(model_name)()
    self.create_proposal_for(obj)
    self.assertEqual(1, self.proposal_relationships(obj).count())

  def test_create_on_post_only(self):
    """Test if proposal relationship is created on post only."""
    control = factories.ControlFactory()
    response = self.create_proposal_for(control)
    post_rels = self.proposal_relationships(control).all()

    proposal = all_models.Proposal.query.get(response.json["proposal"]["id"])
    # Invalidate ACR cache manually as after first post
    # it will in detached state
    flask.g.global_ac_roles = None
    response = self.api.put(
        proposal,
        {
            "proposal": {
                "status": all_models.Proposal.STATES.APPLIED,
                "apply_reason": "test"
            }
        }
    )
    self.assert200(response)
    put_rels = self.proposal_relationships(control).all()
    self.assertEqual(post_rels, put_rels)

  def test_rel_remove_parent(self):
    """Test if relationship will be removed if parent instance is removed."""
    control = factories.ControlFactory()
    self.create_proposal_for(control)
    self.assertEqual(1, self.proposal_relationships(control).count())

    response = self.api.delete(control)
    self.assert200(response)
    self.assertEqual(0, self.proposal_relationships(control).count())

  def test_rel_remove_proposal(self):
    """Test if relationship will be removed if proposal is removed."""
    control = factories.ControlFactory()
    response = self.create_proposal_for(control)
    self.assertEqual(1, self.proposal_relationships(control).count())

    proposal = all_models.Proposal.query.get(response.json["proposal"]["id"])
    response = self.api.delete(proposal)
    self.assert200(response)
    self.assertEqual(0, self.proposal_relationships(control).count())
コード例 #46
0
class TestAuditRBAC(TestCase):
    """Test audit RBAC"""

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

    @classmethod
    def setUpClass(cls):
        TestCase.clear_data()
        cls.response = cls._import_file("audit_rbac.csv")
        cls.people = all_models.Person.eager_query().all()
        cls.audit = all_models.Audit.eager_query().first()
        sources = set(r.source for r in cls.audit.related_sources)
        destinations = set(r.destination
                           for r in cls.audit.related_destinations)
        related = [
            obj for obj in sources.union(destinations)
            if not isinstance(obj, all_models.Person)
        ]
        cls.related_objects = related

    def setUp(self):
        """Imports test_csvs/audit_rbac.csv needed by the tests"""
        self._check_csv_response(self.response, {})
        self.api = Api()
        self.client.get("/login")

    def read(self, objects):
        """Attempt to do a GET request for every object in the objects list"""
        responses = []
        for obj in objects:
            status_code = self.api.get(obj.__class__, obj.id).status_code
            responses.append((obj.type, status_code))
        return responses

    def update(self, objects):
        """Attempt to do a PUT request for every object in the objects list"""
        responses = []
        for obj in objects:
            response = self.api.get(obj.__class__, obj.id)
            status_code = response.status_code
            if response.status_code == 200:
                status_code = self.api.put(obj, response.json).status_code
            responses.append((obj.type, status_code))
        return responses

    def call_api(self, method, expected_statuses):
        """Calls the REST api with a given method and returns a list of
       status_codes that do not match the expected_statuses dict"""
        all_errors = []
        for person in self.people:
            self.api.set_user(person)
            responses = method(self.related_objects + [self.audit])
            for type_, code in responses:
                if code != expected_statuses[person.email]:
                    all_errors.append(
                        "{} does not have {} access to {} ({})".format(
                            person.email, method.__name__, type_, code))
        return all_errors

    def test_read_access_on_mapped(self):
        """Test if people have read access to mapped objects.

    All users except [email protected] should have read access."""
        expected_statuses = defaultdict(lambda: 200)
        for exception in ("*****@*****.**", ):
            expected_statuses[exception] = 403
        errors = self.call_api(self.read, expected_statuses)
        assert not errors, "\n".join(errors)

    def test_update_access_on_mapped(self):
        """Test if people have upate access to mapped objects.

    All users except [email protected], [email protected], [email protected],
    [email protected] should have update access."""
        expected_statuses = defaultdict(lambda: 200)
        for exception in ("*****@*****.**", "*****@*****.**",
                          "*****@*****.**", "*****@*****.**"):
            expected_statuses[exception] = 403
        errors = self.call_api(self.update, expected_statuses)
        assert not errors, "\n".join(errors)
コード例 #47
0
class TestAccessControlList(TestCase):
    """TestAccessControlList"""
    def setUp(self):
        super(TestAccessControlList, self).setUp()
        self.api = Api()
        self.person = factories.PersonFactory(name="My Person")
        self.control = factories.ControlFactory()
        self.acr = factories.AccessControlRoleFactory(object_type="Control",
                                                      read=True)
        self.second_acr = factories.AccessControlRoleFactory(
            object_type="Control", read=True)
        self.acl = factories.AccessControlListFactory(object=self.control,
                                                      ac_role_id=self.acr.id,
                                                      person=self.person)

    def _post_control(self, id_, person_id, collection=False):
        """Helper function for posting a control"""
        title = random_str(prefix="Control - ")
        control = {
            "control": {
                "title": title,
                "type": "Control",
                "context": None,
                "access_control_list": [_acl_json(id_, person_id)]
            },
        }
        response = self.api.post(all_models.Control,
                                 [control] if collection else control)
        assert response.status_code == 200 or response.status_code == 201, \
            "Control with acl not created successfully {}".format(response.status)

        if collection:
            return response.json[0][1]
        return response.json

    def test_object_roles(self):
        """Test if roles are fetched with the object"""
        id_, person_id = self.acr.id, self.person.id
        response = self.api.get(all_models.Control, self.control.id)
        assert response.status_code == 200, \
            "Failed to fetch created control {}".format(response.status)

        assert "access_control_list" in response.json["control"], \
            "Access Control List not a property in {}".format(
                response.json["control"].keys())

        ac_list = response.json["control"]["access_control_list"]
        assert len(ac_list) == 1, "{}".format(len(ac_list))

        assert ac_list[0]["ac_role_id"] == id_, \
            "ac_role_id not properly set {}".format(ac_list[0].get("ac_role_id"))
        assert ac_list[0]["person"]["id"] == person_id, \
            "Person stub not properly set {}".format(ac_list[0]["person"])

    def test_post_object_roles(self):
        """Test if roles are stored correctly when POSTed with the object"""
        id_, person_id = self.acr.id, self.person.id
        response = self._post_control(id_, person_id)

        acl = response["control"]["access_control_list"]
        _acl_asserts(acl, id_, person_id)

    def test_acl_revision_content(self):
        """Test if the access control list is added to revisions"""
        id_, person_id = self.acr.id, self.person.id
        response = self._post_control(id_, person_id)
        control_id = response["control"]["id"]
        rev = all_models.Revision.query.filter(
            all_models.Revision.resource_id == control_id,
            all_models.Revision.resource_type == "Control").first()

        acl = rev.content["access_control_list"]
        _acl_asserts(acl, id_, person_id)

    def test_put_object_roles(self):
        """Test if PUTing object roles saves them correctly"""
        id_2, person_id = self.second_acr.id, self.person.id

        response = self.api.get(all_models.Control, self.control.id)
        assert response.status_code == 200, \
            "Failed to fetch created control {}".format(response.status)
        control = response.json['control']
        control['access_control_list'].append(_acl_json(id_2, person_id))
        response = self.api.put(self.control, {"control": control})
        assert response.status_code == 200, \
            "PUTing control failed {}".format(response.status)
        acl = response.json['control']['access_control_list']
        assert len(acl) == 2, \
            "Access control list not correctly updated {}".format(acl)

    def test_put_removing_roles(self):
        """Test if PUTing an empty list removes object roles correct"""
        response = self.api.get(all_models.Control, self.control.id)
        assert response.status_code == 200, \
            "Failed to fetch created control {}".format(response.status)
        control = response.json['control']
        control['access_control_list'] = []
        response = self.api.put(self.control, {"control": control})
        assert response.status_code == 200, \
            "PUTing control failed {}".format(response.status)
        acl = response.json['control']['access_control_list']
        assert len(acl) == 0, \
            "Access control list not empty {}".format(acl)

    def test_acl_indexing_on_post(self):
        """Test if roles are stored correctly when POSTed with the object"""
        id_, person_id = self.acr.id, self.person.id
        response = self._post_control(id_, person_id, True)
        control = response["control"]
        res = mysql.MysqlRecordProperty.query.filter(
            mysql.MysqlRecordProperty.type == "Control",
            mysql.MysqlRecordProperty.key == control["id"],
            mysql.MysqlRecordProperty.property == self.acr.name).all()
        assert len(res) > 0, \
            "Full text record index not created for {}".format(self.acr.name)
        assert len([r for r in res if r.content == self.person.email]) == 1, \
            "Person email not indexed {}".format(self.person.email)
        assert len([r for r in res if r.content == self.person.name]) == 1, \
            "Person name not indexed {}".format(self.person.name)

    def test_acl_revision_count(self):
        """Test if acl revision is created when object POSTed and PUTed"""
        id_, person_id = self.acr.id, self.person.id

        response = self._post_control(id_, person_id)
        # One ACL and Control created in setUp and on by POST
        self.assertEqual(
            all_models.Revision.query.filter_by(
                resource_type="AccessControlList").count(), 2)
        self.assertEqual(
            all_models.Revision.query.filter_by(
                resource_type="Control").count(), 2)

        # If content of "access_control_list" is changed,
        # new revision should be created for ACL
        control = response["control"]
        control["access_control_list"] = []
        self.api.put(all_models.Control.query.get(control["id"]),
                     {"control": control})
        self.assertEqual(
            all_models.Revision.query.filter_by(
                resource_type="AccessControlList").count(), 3)
        self.assertEqual(
            all_models.Revision.query.filter_by(
                resource_type="Control").count(), 3)
コード例 #48
0
class TestAuditRBAC(TestCase):
    """Test audit RBAC"""

    # pylint: disable=too-many-instance-attributes

    def setUp(self):
        """Imports test_csvs/audit_rbac_snapshot_create.csv needed by the tests"""
        TestCase.clear_data()
        self.api = Api()
        self.objgen = integration.ggrc.generator.ObjectGenerator()

    def create_audit(self, audit_role, userid):
        """Create default audit for audit snapshot RBAC tests"""
        is_auditor = False
        if audit_role == "Auditors":
            is_auditor = True
            auditor_role = all_models.AccessControlRole.query.filter(
                all_models.AccessControlRole.name == "Auditors").one()

        program = db.session.query(all_models.Program).get(self.program_id)
        _, audit = self.objgen.generate_object(
            all_models.Audit, {
                "title":
                "Snapshotable audit",
                "program": {
                    "id": self.program_id
                },
                "status":
                "Planned",
                "snapshots": {
                    "operation": "create",
                },
                "access_control_list":
                [acl_helper.get_acl_json(auditor_role.id, userid)]
                if is_auditor else None,
                "context": {
                    "type": "Context",
                    "id": program.context_id,
                    "href": "/api/contexts/{}".format(program.context_id)
                }
            })
        return audit

    def create_program(self, program_role, userid):
        """Create default program for audit snapshot RBAC tests"""
        if program_role and program_role != "Auditors":
            program_role = all_models.AccessControlRole.query.filter(
                all_models.AccessControlRole.name == program_role).one()
        else:
            program_role = None

        _, program = self.objgen.generate_object(
            all_models.Program, {
                "title":
                "test program",
                "access_control_list":
                [acl_helper.get_acl_json(program_role.id, userid)]
                if program_role else None
            })

        for model_type in Types.all - Types.external:
            model = get_model(model_type)
            _, model_object = self.objgen.generate_object(
                model, {
                    "title": "Test Snapshot - {}".format(model_type),
                })
            self.objgen.generate_relationship(program, model_object)

        return program

    def update_audit(self):
        """Update default audit"""
        for model_type in Types.all - Types.external:
            model = get_model(model_type)
            obj = model.query.filter_by(
                title="Test Snapshot - {}".format(model_type)).first()
            self.api.modify_object(
                obj, {"title": "Test Snapshot - {} EDIT".format(model_type)})

        audit = all_models.Audit.query.filter(
            all_models.Audit.title == "Snapshotable audit").one()

        self.api.modify_object(audit, {"snapshots": {"operation": "upsert"}})

    def read(self, objects):
        """Attempt to do a GET request for every object in the objects list"""
        responses = []
        for obj in objects:
            status_code = self.api.get(obj.__class__, obj.id).status_code
            responses.append((obj.type, status_code))
        return responses

    def update(self, objects):
        """Attempt to do a PUT request for every object in the objects list"""
        scope_response = self.api.get(all_models.Audit, self.audit_id)
        if scope_response.status_code == 200:
            self.update_audit()

        responses = []
        for obj in objects:
            response = self.api.get(obj.__class__, obj.id)
            status_code = response.status_code
            if response.status_code == 200:
                data = response.json
                if obj.type == "Snapshot":
                    data.update({"update_revision": "latest"})
                put_call = self.api.put(obj, data)
                status_code = put_call.status_code
            responses.append((obj.type, status_code))
        return responses

    # pylint: disable=attribute-defined-outside-init
    @ddt.data(
        ("Administrator", "", DEFAULT_PERMISSIONS),
        ("Creator", "", DEFAULT_LACK_OF_PERMISSIONS),
        ("Reader", "", DEFAULT_PERMISSIONS),
        ("Editor", "", DEFAULT_PERMISSIONS),
        ("Administrator", "Program Managers", DEFAULT_PERMISSIONS),
        ("Creator", "Program Managers", DEFAULT_PERMISSIONS),
        ("Reader", "Program Managers", DEFAULT_PERMISSIONS),
        ("Editor", "Program Managers", DEFAULT_PERMISSIONS),
        ("Administrator", "Program Editors", DEFAULT_PERMISSIONS),
        ("Creator", "Program Editors", DEFAULT_PERMISSIONS),
        ("Reader", "Program Editors", DEFAULT_PERMISSIONS),
        ("Editor", "Program Editors", DEFAULT_PERMISSIONS),
        ("Administrator", "Program Readers", DEFAULT_PERMISSIONS),
        ("Creator", "Program Readers", DEFAULT_PERMISSIONS),
        ("Reader", "Program Readers", DEFAULT_PERMISSIONS),
        ("Editor", "Program Readers", DEFAULT_PERMISSIONS),
        ("Administrator", "Auditors", DEFAULT_PERMISSIONS),
        ("Creator", "Auditors", DEFAULT_PERMISSIONS),
        ("Reader", "Auditors", DEFAULT_PERMISSIONS),
        ("Editor", "Auditors", DEFAULT_PERMISSIONS),
    )
    @ddt.unpack
    def test_read_access_on_mapped(self, rolename, object_role,
                                   expected_status):
        """Test if {0} with {1} role, has READ access to
       snapshotted objects of default audit"""

        user = self.create_user_with_role(rolename)
        user_id = user.id

        program = self.create_program(object_role, user_id)
        self.program_id = program.id

        audit = self.create_audit(object_role, user_id)

        snapshots = all_models.Snapshot.eager_query().all()

        objects = snapshots + [audit]

        user = all_models.Person.query.get(user_id)
        self.api.set_user(user)

        responses = self.read(objects)
        all_errors = []
        for type_, code in responses:
            if code != expected_status[type_]:
                all_errors.append(
                    "{} does not have read access to {} ({})".format(
                        user.email, type_, code))
        assert not all_errors, "\n".join(all_errors)

    # pylint: disable=attribute-defined-outside-init
    @ddt.data(
        ("Administrator", "", DEFAULT_PERMISSIONS),
        ("Creator", "", DEFAULT_LACK_OF_PERMISSIONS),
        ("Reader", "", DEFAULT_LACK_OF_PERMISSIONS),
        ("Editor", "", DEFAULT_PERMISSIONS),
        ("Administrator", "Program Managers", DEFAULT_PERMISSIONS),
        ("Creator", "Program Managers", DEFAULT_PERMISSIONS),
        ("Reader", "Program Managers", DEFAULT_PERMISSIONS),
        ("Editor", "Program Managers", DEFAULT_PERMISSIONS),
        ("Administrator", "Program Editors", DEFAULT_PERMISSIONS),
        ("Creator", "Program Editors", DEFAULT_PERMISSIONS),
        ("Reader", "Program Editors", DEFAULT_PERMISSIONS),
        ("Editor", "Program Editors", DEFAULT_PERMISSIONS),
        ("Administrator", "Program Readers", DEFAULT_PERMISSIONS),
        ("Creator", "Program Readers", DEFAULT_LACK_OF_PERMISSIONS),
        ("Reader", "Program Readers", DEFAULT_LACK_OF_PERMISSIONS),
        ("Editor", "Program Readers", DEFAULT_PERMISSIONS),
        ("Administrator", "Auditors", DEFAULT_PERMISSIONS),
        ("Creator", "Auditors", DEFAULT_AUDITOR_PERMISSIONS),
        ("Reader", "Auditors", DEFAULT_AUDITOR_PERMISSIONS),
        ("Editor", "Auditors", DEFAULT_PERMISSIONS),
    )
    @ddt.unpack
    def test_update_access_on_mapped(self, rolename, object_role,
                                     expected_status):
        """Test if {0} with {1} role, has UPDATE access to
       snapshotted objects of default audit"""

        user = self.create_user_with_role(rolename)
        user_id = user.id

        program = self.create_program(object_role, user_id)
        self.program_id = program.id

        audit = self.create_audit(object_role, user_id)
        self.audit_id = audit.id

        snapshots = all_models.Snapshot.eager_query().all()

        objects = snapshots + [audit]

        user = all_models.Person.query.get(user_id)
        self.api.set_user(user)

        responses = self.update(objects)
        all_errors = []
        for type_, code in responses:
            if code != expected_status[type_]:
                all_errors.append(
                    "{} does not have update access to {} ({})".format(
                        user.email, type_, code))
        assert not all_errors, "\n".join(all_errors)
コード例 #49
0
class TestAccessControlRole(TestCase):
    """TestAccessControlRole"""
    def setUp(self):
        self.clear_data()
        super(TestAccessControlRole, self).setUp()
        self.api = Api()
        self.object_generator = ObjectGenerator()
        self.people = {}
        for name in ["Creator", "Reader", "Editor"]:
            _, user = self.object_generator.generate_person(
                data={"name": name}, user_role=name)
            self.people[name] = user

    def _post_role(self, name=None, object_type="Control"):
        """Helper function for POSTing roles"""
        if name is None:
            name = random_str(prefix="Access Control Role - ")
        return self.api.post(
            AccessControlRole, {
                "access_control_role": {
                    "name": name,
                    "object_type": object_type,
                    "context": None,
                    "read": True
                },
            })

    def test_create_after_objects(self):
        """Test eager creation of ACLs on existing objects with new ACR."""
        program_id = factories.ProgramFactory().id
        role_name = "New Custom Role"
        self._post_role(name=role_name, object_type="Program")
        program = all_models.Program.query.get(program_id)
        self.assertIn(role_name, program.acr_name_acl_map.keys())
        self.assertIsNotNone(program.acr_name_acl_map[role_name])

    def test_create(self):
        """Test Access Control Role creation"""
        response = self._post_role(object_type="Program")
        assert response.status_code == 201, \
            "Failed to create a new access control role, response was {}".format(
                response.status)

        id_ = response.json['access_control_role']['id']
        role = AccessControlRole.query.filter(
            AccessControlRole.id == id_).first()
        assert role.read == 1, \
            "Read permission not correctly saved {}".format(role.read)
        assert role.update == 1, \
            "Update permission not correctly saved {}".format(role.update)
        assert role.delete == 1, \
            "Update permission not correctly saved {}".format(role.delete)

    @ddt.data(
        {
            "mandatory": True,
            "exp_response": MANDATORY_ROLE_RESPONSE
        },
        {
            "mandatory": False,
            "exp_response": NON_MANDATORY_ROLE_RESPONSE
        },
    )
    @ddt.unpack
    def test_mandatory_delete(self, mandatory, exp_response):
        """Test set empty field via import if acr mandatory is {mandatory}"""
        role = factories.AccessControlRoleFactory(
            name=ROLE_NAME,
            object_type="Program",
            mandatory=mandatory,
        )
        with factories.single_commit():
            user = factories.PersonFactory()
            program = factories.ProgramFactory()
            role_id = role.id
            factories.AccessControlPersonFactory(
                ac_list=program.acr_name_acl_map[ROLE_NAME],
                person=user,
            )
        response = self.import_data(
            OrderedDict([
                ("object_type", "Program"),
                ("Code*", program.slug),
                (ROLE_NAME, "--"),
            ]))
        self._check_csv_response(response, exp_response)
        db_data = defaultdict(set)
        program = all_models.Program.query.get(program.id)
        for person, acl in program.access_control_list:
            db_data[acl.ac_role_id].add(person.id)
        if mandatory:
            cur_user = all_models.Person.query.filter_by(
                email="*****@*****.**").first()
            self.assertEqual(set([cur_user.id]), db_data[role_id])
        else:
            self.assertFalse(db_data[role_id])

    def test_only_admin_can_post(self):
        """Only admin users should be able to POST access control roles"""
        for name in ("Creator", "Reader", "Editor"):
            person = self.people.get(name)
            self.api.set_user(person)
            response = self._post_role()
            assert response.status_code == 403, \
                "Non admins should get forbidden error when POSTing role. {}".format(
                    response.status)

    @ddt.data(
        ("name", "New ACR"),
        ("read", False),
        ("mandatory", False),
        ("non_editable", False),
    )
    @ddt.unpack
    def test_modify_non_editable_role(self, field_name, field_value):
        """Test if user can modify non-editable role"""
        # Primary Contacts role of Control is non-editable
        ac_role = AccessControlRole.query.filter_by(
            object_type="Control",
            name="Control Operators",
        ).first()

        response = self.api.put(ac_role, {field_name: field_value})
        assert response.status_code == 403, \
            "Forbidden error should be thrown when non-editable " \
            "role {} updated.".format(ac_role.name)

    def test_delete_non_editable_role(self):
        """Test if user can delete non-editable role"""
        # Primary Contacts role of Control is non-editable
        ac_role = AccessControlRole.query.filter_by(
            object_type="Control",
            name="Control Operators",
        ).first()

        response = self.api.delete(ac_role)
        assert response.status_code == 403, \
            "Forbidden error should be thrown when non-editable " \
            "role {} deleted.".format(ac_role.name)

    @ddt.data("Control")
    def test_create_from_ggrcq(self, object_type):
        """Test that create action only for GGRCQ."""
        with self.api.as_external():
            response = self._post_role(object_type=object_type)
            self.assertEqual(response.status_code, 201)

    @ddt.data("Control")
    def test_create_from_ggrc(self, object_type):
        """Test create action not allowed for GGRC."""
        response = self._post_role(object_type=object_type)
        self.assertEqual(response.status_code, 405)

    @ddt.data("Control")
    def test_modify_from_ggrcq(self, object_type):
        """Test that modify action only for GGRCQ."""
        with factories.single_commit():
            acr_id = factories.AccessControlRoleFactory(
                object_type=object_type).id

        with self.api.as_external():
            acr = all_models.AccessControlRole.query.get(acr_id)
            response = self.api.put(acr, {"name": "new acr"})
            self.assertEqual(response.status_code, 200)

    @ddt.data("Control")
    def test_modify_from_ggrc(self, object_type):
        """Test modify action not allowed for GGRC."""
        with factories.single_commit():
            acr = factories.AccessControlRoleFactory(object_type=object_type)

        response = self.api.put(acr, {"name": "new acr"})
        self.assertEqual(response.status_code, 405)

    @ddt.data("Control")
    def test_delete_from_ggrcq(self, object_type):
        """Test that modify action only for GGRCQ."""
        with factories.single_commit():
            acr_id = factories.AccessControlRoleFactory(
                object_type=object_type).id

        with self.api.as_external():
            acr = all_models.AccessControlRole.query.get(acr_id)
            response = self.api.delete(acr)
            self.assertEqual(response.status_code, 200)

    @ddt.data("Control")
    def test_delete_from_ggrc(self, object_type):
        """Test modify action not allowed for GGRC."""
        with factories.single_commit():
            acr = factories.AccessControlRoleFactory(object_type=object_type)

        response = self.api.delete(acr)
        self.assertEqual(response.status_code, 405)

    def test_create_with_wrong_options(self):
        """ Test if user create ACR with wrong options."""
        options = [{
            'access_control_role': {
                'name': 'Test',
                'modal_title': 'Add Custom Role to type Regulation',
                'read': False,
                'object_type': 'Regulation',
                'parent_type': 'Regulation',
                'update': True,
                'context': {
                    'id': None
                },
                'delete': True
            }
        }]
        response = self.api.post(AccessControlRole, options)
        self.assert400(response)
コード例 #50
0
class TestAccessControlList(TestCase):
  """TestAccessControlList"""

  def setUp(self):
    super(TestAccessControlList, self).setUp()
    self.api = Api()
    self.person = factories.PersonFactory(name="My Person")
    self.control = factories.ControlFactory()
    self.acr = factories.AccessControlRoleFactory(
        object_type="Control",
        read=True
    )
    self.second_acr = factories.AccessControlRoleFactory(
        object_type="Control",
        read=True
    )
    self.acl = factories.AccessControlListFactory(
        object=self.control,
        ac_role_id=self.acr.id,
        person=self.person
    )

  def _post_control(self, id_, person_id, collection=False):
    """Helper function for posting a control"""
    title = random_str(prefix="Control - ")
    control = {
        "control": {
            "title": title,
            "type": "Control",
            "context": None,
            "access_control_list": [
                _acl_json(id_, person_id)
            ]
        },
    }
    response = self.api.post(
        all_models.Control, [control] if collection else control)
    assert response.status_code == 200 or response.status_code == 201, \
        "Control with acl not created successfully {}".format(response.status)

    if collection:
      return response.json[0][1]
    return response.json

  def test_object_roles(self):
    """Test if roles are fetched with the object"""
    id_, person_id = self.acr.id, self.person.id
    response = self.api.get(all_models.Control, self.control.id)
    assert response.status_code == 200, \
        "Failed to fetch created control {}".format(response.status)

    assert "access_control_list" in response.json["control"], \
        "Access Control List not a property in {}".format(
            response.json["control"].keys())

    ac_list = response.json["control"]["access_control_list"]
    assert len(ac_list) == 1, "{}".format(len(ac_list))

    assert ac_list[0]["ac_role_id"] == id_, \
        "ac_role_id not properly set {}".format(ac_list[0].get("ac_role_id"))
    assert ac_list[0]["person"]["id"] == person_id, \
        "Person stub not properly set {}".format(ac_list[0]["person"])

  def test_post_object_roles(self):
    """Test if roles are stored correctly when POSTed with the object"""
    id_, person_id = self.acr.id, self.person.id
    response = self._post_control(id_, person_id)

    acl = response["control"]["access_control_list"]
    _acl_asserts(acl, id_, person_id)

  def test_acl_revision_content(self):
    """Test if the access control list is added to revisions"""
    id_, person_id = self.acr.id, self.person.id
    response = self._post_control(id_, person_id)
    control_id = response["control"]["id"]
    rev = all_models.Revision.query.filter(
        all_models.Revision.resource_id == control_id,
        all_models.Revision.resource_type == "Control"
    ).first()

    acl = rev.content["access_control_list"]
    _acl_asserts(acl, id_, person_id)

  def test_put_object_roles(self):
    """Test if PUTing object roles saves them correctly"""
    id_2, person_id = self.second_acr.id, self.person.id

    response = self.api.get(all_models.Control, self.control.id)
    assert response.status_code == 200, \
        "Failed to fetch created control {}".format(response.status)
    control = response.json['control']
    control['access_control_list'].append(_acl_json(id_2, person_id))
    response = self.api.put(self.control, {"control": control})
    assert response.status_code == 200, \
        "PUTing control failed {}".format(response.status)
    acl = response.json['control']['access_control_list']
    assert len(acl) == 2, \
        "Access control list not correctly updated {}".format(acl)

  def test_put_removing_roles(self):
    """Test if PUTing an empty list removes object roles correct"""
    response = self.api.get(all_models.Control, self.control.id)
    assert response.status_code == 200, \
        "Failed to fetch created control {}".format(response.status)
    control = response.json['control']
    control['access_control_list'] = []
    response = self.api.put(self.control, {"control": control})
    assert response.status_code == 200, \
        "PUTing control failed {}".format(response.status)
    acl = response.json['control']['access_control_list']
    assert len(acl) == 0, \
        "Access control list not empty {}".format(acl)

  def test_acl_indexing_on_post(self):
    """Test if roles are stored correctly when POSTed with the object"""
    id_, person_id = self.acr.id, self.person.id
    response = self._post_control(id_, person_id, True)
    control = response["control"]
    res = mysql.MysqlRecordProperty.query.filter(
        mysql.MysqlRecordProperty.type == "Control",
        mysql.MysqlRecordProperty.key == control["id"],
        mysql.MysqlRecordProperty.property == self.acr.name
    ).all()
    assert len(res) > 0, \
        "Full text record index not created for {}".format(self.acr.name)
    # email is presented in __sort__ subproperty as well
    assert len([r for r in res if r.content == self.person.email]) == 2, \
        "Person email not indexed {}".format(self.person.email)
    assert len([r for r in res if r.content == self.person.name]) == 1, \
        "Person name not indexed {}".format(self.person.name)

  def test_acl_revision_count(self):
    """Test if acl revision is created when object POSTed and PUTed"""
    id_, person_id = self.acr.id, self.person.id

    response = self._post_control(id_, person_id)
    # One ACL and Control created in setUp and on by POST
    self.assertEqual(
        all_models.Revision.query.filter_by(
            resource_type="AccessControlList"
        ).count(),
        2
    )
    self.assertEqual(
        all_models.Revision.query.filter_by(
            resource_type="Control"
        ).count(),
        2
    )

    # If content of "access_control_list" is changed,
    # new revision should be created for ACL
    control = response["control"]
    control["access_control_list"] = []
    self.api.put(
        all_models.Control.query.get(control["id"]),
        {"control": control}
    )
    self.assertEqual(
        all_models.Revision.query.filter_by(
            resource_type="AccessControlList"
        ).count(),
        3
    )
    self.assertEqual(
        all_models.Revision.query.filter_by(
            resource_type="Control"
        ).count(),
        3
    )
コード例 #51
0
class TestCollection(TestCase, WithQueryApi):
    """Test for collection assessment objects."""
    def setUp(self):
        super(TestCollection, self).setUp()
        self.client.get("/login")
        self.clear_data()
        self.api = Api()
        self.generator = ObjectGenerator()

    @ddt.data(True, False)
    def test_order_by_test(self, desc):
        """Order by fultext attr"""
        expected_ids = []
        with factories.single_commit():
            assessments = [factories.AssessmentFactory() for _ in range(10)]
        random.shuffle(assessments)
        with factories.single_commit():
            for idx, assessment in enumerate(assessments):
                comment = factories.CommentFactory(description=str(idx))
                factories.RelationshipFactory(source=assessment,
                                              destination=comment)
                expected_ids.append(assessment.id)
        query = self._make_query_dict("Assessment",
                                      order_by=[{
                                          "name": "comment",
                                          "desc": desc
                                      }])
        if desc:
            expected_ids = expected_ids[::-1]
        results = self._get_first_result_set(query, "Assessment", "values")
        self.assertEqual(expected_ids, [i['id'] for i in results])

    @ddt.data("Assessor", "Creator", "Verifier")
    def test_delete_assessment_by_role(self, role_name):
        """Delete assessment not allowed for based on Assignee Type."""
        with factories.single_commit():
            assessment = factories.AssessmentFactory()
            context = factories.ContextFactory(related_object=assessment)
            assessment.context = context
            person = factories.PersonFactory()
            object_person_rel = factories.RelationshipFactory(
                source=assessment, destination=person)
            factories.RelationshipAttrFactory(
                relationship_id=object_person_rel.id,
                attr_name="AssigneeType",
                attr_value=role_name,
            )
        assessment_id = assessment.id
        role = all_models.Role.query.filter(
            all_models.Role.name == "Creator").first()
        self.generator.generate_user_role(person, role, context)
        self.api.set_user(person)
        assessment = all_models.Assessment.query.get(assessment_id)
        resp = self.api.delete(assessment)
        self.assert403(resp)
        self.assertTrue(
            all_models.Assessment.query.filter(
                all_models.Assessment.id == assessment_id).one())

    @ddt.data(
        (all_models.Assessment.REWORK_NEEDED, True),
        (all_models.Assessment.DONE_STATE, True),
        (all_models.Assessment.FINAL_STATE, True),
        (all_models.Assessment.START_STATE, False),
    )
    @ddt.unpack
    def test_update_status_need_rework(self, status, is_valid):
        """Update assessment state from need rework to valid or invalid states."""
        with factories.single_commit():
            assessment = factories.AssessmentFactory(
                status=all_models.Assessment.REWORK_NEEDED)
        assessment_id = assessment.id
        resp = self.api.put(assessment, {"status": status})
        if is_valid:
            self.assert200(resp)
            check_status = status
        else:
            self.assert400(resp)
            check_status = all_models.Assessment.REWORK_NEEDED
        self.assertEqual(check_status,
                         all_models.Assessment.query.get(assessment_id).status)
コード例 #52
0
class TestArchived(WithQueryApi, TestCase):
    """Tests for filtering by Archived field."""

    # pylint: disable=invalid-name
    def setUp(self):
        super(TestArchived, self).setUp()
        self.client.get("/login")
        self.api = Api()

    @ddt.data([1, 3])
    def test_archived_audits(self, archived_audits):
        """Test filtration by Archived Audits."""
        with factories.single_commit():
            audit_ids = [factories.AuditFactory().id for _ in range(5)]

        expected_ids = []
        for i in archived_audits:
            audit = all_models.Audit.query.get(audit_ids[i])
            response = self.api.put(audit, {"archived": True})
            self.assert200(response)
            expected_ids.append(audit_ids[i])

        ids = self.simple_query("Audit",
                                expression=["archived", "=", "true"],
                                type_="ids",
                                field="ids")
        self.assertItemsEqual(ids, expected_ids)

    @ddt.data([2, 4])
    def test_archived_assessments(self, archived_audits):
        """Test filtration by Archived Assessments."""
        # Create 5 Audits, each of them has 3 Assessment
        with factories.single_commit():
            audit_ids = []
            for _ in range(5):
                audit = factories.AuditFactory()
                audit_ids.append(audit.id)
                for _ in range(3):
                    factories.AssessmentFactory(audit=audit)

        # This list contain ids of assessments from audits in archived_audits
        expected_ids = []
        for i in archived_audits:
            audit = all_models.Audit.query.get(audit_ids[i])
            expected_ids += [a.id for a in audit.assessments]
            response = self.api.put(audit, {"archived": True})
            self.assert200(response)

        ids = self.simple_query("Assessment",
                                expression=["archived", "=", "true"],
                                type_="ids",
                                field="ids")
        self.assertItemsEqual(ids, expected_ids)

    def _archive_audit_and_check_evidence(self, audit, evidence_ids):
        """Helper function archive audit and check evidences is archived"""
        response = self.api.put(audit, {"archived": True})
        self.assert200(response)
        ids = self.simple_query("Evidence",
                                expression=["archived", "=", "true"],
                                type_="ids",
                                field="ids")
        self.assertItemsEqual(ids, evidence_ids)

    def test_archived_evidence_forward(self):
        """Test evidence archived with audit in audit -> assessment"""
        expected_evidence_ids = []
        with factories.single_commit():
            audit = factories.AuditFactory()
            assessment = factories.AssessmentFactory(audit=audit)
            evidence = factories.EvidenceUrlFactory()
            factories.RelationshipFactory(source=audit, destination=assessment)
            factories.RelationshipFactory(source=evidence,
                                          destination=assessment)
            expected_evidence_ids.append(evidence.id)
        self._archive_audit_and_check_evidence(audit, expected_evidence_ids)

    def test_archived_evidence_backward(self):
        """Test evidence archived with audit in assessment -> audit"""
        expected_evidence_ids = []
        with factories.single_commit():
            audit = factories.AuditFactory()
            assessment = factories.AssessmentFactory(audit=audit)
            evidence = factories.EvidenceUrlFactory()
            factories.RelationshipFactory(source=assessment, destination=audit)
            factories.RelationshipFactory(source=assessment,
                                          destination=evidence)
            expected_evidence_ids.append(evidence.id)
        self._archive_audit_and_check_evidence(audit, expected_evidence_ids)

    def test_archived_evidence_from_audit_forward(self):
        """Test evidence archived with audit in audit -> evidence"""
        expected_evidence_ids = []
        with factories.single_commit():
            audit = factories.AuditFactory()
            evidence = factories.EvidenceUrlFactory()
            factories.RelationshipFactory(source=audit, destination=evidence)
            expected_evidence_ids.append(evidence.id)
        self._archive_audit_and_check_evidence(audit, expected_evidence_ids)

    def test_archived_evidence_from_audit_backward(self):
        """Test evidence archived with audit in evidence -> audit"""
        expected_evidence_ids = []
        with factories.single_commit():
            audit = factories.AuditFactory()
            evidence = factories.EvidenceUrlFactory()
            factories.RelationshipFactory(source=evidence, destination=audit)
            expected_evidence_ids.append(evidence.id)
        self._archive_audit_and_check_evidence(audit, expected_evidence_ids)
コード例 #53
0
class TestReviewStatusUpdate(TestCase):
    """Base TestCase class automatic review status update."""
    def setUp(self):
        super(TestReviewStatusUpdate, self).setUp()
        self.api = Api()
        self.api.client.get("/login")
        self.generator = generator.ObjectGenerator()

    @ddt.data(
        ("title", "new title"),
        ("description", "new description"),
        ("test_plan", "new test_plan"),
        ("notes", "new notes"),
        ("fraud_related", 1),
        ("key_control", 1),
        ("start_date", "2020-01-01"),
        ("status", "Active"),
        ("kind", get_kind_data),
        ("means", get_mean_data),
        ("verify_frequency", get_verify_frequency_data),
        ("assertions", get_assertions_data),
        ("categories", get_categories_data),
    )
    @ddt.unpack
    def test_reviewable_attributes(self, attr_to_modify, new_value):
        """If attribute '{0}' modified move review to Unreviewed state"""
        with factories.single_commit():
            control = factories.ControlFactory()
            review = factories.ReviewFactory(
                status=all_models.Review.STATES.REVIEWED, reviewable=control)
        review_id = review.id
        reviewable = review.reviewable

        self.api.modify_object(reviewable, {
            attr_to_modify:
            new_value() if callable(new_value) else new_value
        })

        review = all_models.Review.query.get(review_id)
        self.assertEqual(review.status, all_models.Review.STATES.UNREVIEWED)

    def test_gca(self):
        """if GCA of reviewable is changed review -> unreviewed"""
        with factories.single_commit():
            ca_factory = factories.CustomAttributeDefinitionFactory
            gca = ca_factory(definition_type="control",
                             title="rich_test_gca",
                             attribute_type="Rich Text")
            control = factories.ControlFactory()
            review = factories.ReviewFactory(
                status=all_models.Review.STATES.REVIEWED, reviewable=control)
        review_id = review.id
        reviewable = review.reviewable

        review = all_models.Review.query.get(review_id)

        self.assertEqual(review.status, all_models.Review.STATES.REVIEWED)

        self.api.modify_object(
            reviewable, {
                "custom_attribute_values": [{
                    "custom_attribute_id": gca.id,
                    "attribute_value": "new_value",
                }],
            })

        review = all_models.Review.query.get(review_id)
        self.assertEqual(review.status, all_models.Review.STATES.UNREVIEWED)

    def test_update_gca(self):
        """if existing GCA value changed review -> unreviewed"""
        with factories.single_commit():
            ca_factory = factories.CustomAttributeDefinitionFactory
            gca = ca_factory(definition_type="control",
                             title="rich_test_gca",
                             attribute_type="Rich Text")
            control = factories.ControlFactory()

            control.custom_attribute_values = [{
                "attribute_value": "starting_value",
                "custom_attribute_id": gca.id
            }]
            review = factories.ReviewFactory(
                status=all_models.Review.STATES.REVIEWED, reviewable=control)
        review_id = review.id
        reviewable = review.reviewable

        review = all_models.Review.query.get(review_id)

        self.assertEqual(review.status, all_models.Review.STATES.REVIEWED)

        self.api.modify_object(
            reviewable, {
                "custom_attribute_values": [{
                    "custom_attribute_id": gca.id,
                    "attribute_value": "new_value",
                }],
            })

        review = all_models.Review.query.get(review_id)
        self.assertEqual(review.status, all_models.Review.STATES.UNREVIEWED)

    @ddt.data("custom attr", "slug", "self")
    def test_gca_with_varying_titles(self, title):
        """if GCA with any title is changed review -> unreviewed"""
        with factories.single_commit():
            ca_factory = factories.CustomAttributeDefinitionFactory
            gca = ca_factory(definition_type="control",
                             title=title,
                             attribute_type="Rich Text")
            control = factories.ControlFactory()

            control.custom_attribute_values = [{
                "attribute_value": "starting_value",
                "custom_attribute_id": gca.id
            }]
            review = factories.ReviewFactory(
                status=all_models.Review.STATES.REVIEWED, reviewable=control)
        review_id = review.id
        reviewable = review.reviewable

        review = all_models.Review.query.get(review_id)

        self.assertEqual(review.status, all_models.Review.STATES.REVIEWED)

        self.api.modify_object(
            reviewable, {
                "custom_attribute_values": [{
                    "custom_attribute_id": gca.id,
                    "attribute_value": "new_value",
                }],
            })

        review = all_models.Review.query.get(review_id)
        self.assertEqual(review.status, all_models.Review.STATES.UNREVIEWED)

    def test_map_person_gca(self):
        """if Map:Person GCA value added review -> unreviewed"""
        with factories.single_commit():
            ca_factory = factories.CustomAttributeDefinitionFactory
            gca = ca_factory(definition_type="control",
                             title="map_test_gca",
                             attribute_type="Map:Person")

            user_id = all_models.Person.query.filter_by(
                email="*****@*****.**").one().id

            control = factories.ControlFactory()
            review = factories.ReviewFactory(
                status=all_models.Review.STATES.REVIEWED, reviewable=control)

        review_id = review.id
        reviewable = review.reviewable
        review = all_models.Review.query.get(review_id)

        self.assertEqual(review.status, all_models.Review.STATES.REVIEWED)

        self.api.modify_object(
            reviewable, {
                "custom_attribute_values": [{
                    "custom_attribute_id": gca.id,
                    "attribute_object_id": user_id,
                    "attribute_value": "Person",
                }],
            })

        review = all_models.Review.query.get(review_id)
        self.assertEqual(review.status, all_models.Review.STATES.UNREVIEWED)

    def test_update_map_person_gca(self):
        """if existing Map:Person GCA value changed review -> unreviewed"""
        with factories.single_commit():
            ca_factory = factories.CustomAttributeDefinitionFactory
            gca = ca_factory(definition_type="control",
                             title="map_test_gca",
                             attribute_type="Map:Person")

            first_user_id = all_models.Person.query.filter_by(
                email="*****@*****.**").one().id
            second_user_id = factories.PersonFactory().id

            control = factories.ControlFactory()
            control.custom_attribute_values = [{
                "attribute_object_id": first_user_id,
                "custom_attribute_id": gca.id,
                "attribute_value": "Person"
            }]
            review = factories.ReviewFactory(
                status=all_models.Review.STATES.REVIEWED, reviewable=control)

        review_id = review.id
        reviewable = review.reviewable
        review = all_models.Review.query.get(review_id)

        self.assertEqual(review.status, all_models.Review.STATES.REVIEWED)

        self.api.modify_object(
            reviewable, {
                "custom_attribute_values": [{
                    "custom_attribute_id": gca.id,
                    "attribute_object_id": second_user_id,
                    "attribute_value": "Person",
                }],
            })

        review = all_models.Review.query.get(review_id)
        self.assertEqual(review.status, all_models.Review.STATES.UNREVIEWED)

    def test_delete_map_person_gca(self):
        """if existing Map:Person GCA value deleted review -> unreviewed"""
        with factories.single_commit():
            ca_factory = factories.CustomAttributeDefinitionFactory
            gca = ca_factory(definition_type="control",
                             title="map_test_gca",
                             attribute_type="Map:Person")

            user_id = all_models.Person.query.filter_by(
                email="*****@*****.**").one().id

            control = factories.ControlFactory()
            control.custom_attribute_values = [{
                "attribute_object_id": user_id,
                "custom_attribute_id": gca.id,
                "attribute_value": "Person"
            }]
            review = factories.ReviewFactory(
                status=all_models.Review.STATES.REVIEWED, reviewable=control)

        review_id = review.id
        reviewable = review.reviewable
        review = all_models.Review.query.get(review_id)

        self.assertEqual(review.status, all_models.Review.STATES.REVIEWED)

        self.api.modify_object(
            reviewable, {
                "custom_attribute_values": [{
                    "custom_attribute_id": gca.id,
                    "attribute_object_id": None,
                    "attribute_object": None,
                    "attribute_value": "Person",
                }],
            })

        review = all_models.Review.query.get(review_id)
        self.assertEqual(review.status, all_models.Review.STATES.UNREVIEWED)

    def test_reference_url(self):
        """If reference url is updated state should not updated"""
        with factories.single_commit():
            control = factories.ControlFactory()
            doc = factories.DocumentReferenceUrlFactory(
                title="Simple title",
                link="some_url.com",
                description="mega description",
                parent_obj={
                    "id": control.id,
                    "type": "Control"
                })
            review = factories.ReviewFactory(
                status=all_models.Review.STATES.REVIEWED, reviewable=control)
        review_id = review.id

        self.api.modify_object(doc, {"link": "new_link.com"})
        review = all_models.Review.query.get(review_id)
        self.assertEqual(review.status, all_models.Review.STATES.REVIEWED)

    def test_acl_roles(self):
        """Update of reviewable ACL shouldn't change review status"""
        with factories.single_commit():
            control = factories.ControlFactory()
            review = factories.ReviewFactory(
                status=all_models.Review.STATES.REVIEWED, reviewable=control)
        review_id = review.id

        ac_role_id = all_models.AccessControlRole.query.filter_by(
            name="Control Operators", object_type="Control").one().id

        user_id = all_models.Person.query.filter_by(
            email="*****@*****.**").one().id

        self.api.modify_object(
            control, {
                "access_control_list": [{
                    "ac_role_id": ac_role_id,
                    "person": {
                        "id": user_id
                    },
                }],
            })
        review = all_models.Review.query.get(review_id)
        self.assertEqual(review.status, all_models.Review.STATES.REVIEWED)

    def test_comments(self):
        """Add comment to reviewable shouldn't update review state"""
        with factories.single_commit():
            control = factories.ControlFactory()
            review = factories.ReviewFactory(
                status=all_models.Review.STATES.REVIEWED, reviewable=control)
        review_id = review.id

        self.generator.generate_comment(control,
                                        "Verifiers",
                                        "some comment",
                                        send_notification="false")

        review = all_models.Review.query.get(review_id)
        self.assertEqual(review.status, all_models.Review.STATES.REVIEWED)

    def test_mapping_non_snapshotable(self):
        """Map non-snapshotable shouldn't change review status"""
        with factories.single_commit():
            control = factories.ControlFactory()
            review = factories.ReviewFactory(
                status=all_models.Review.STATES.REVIEWED, reviewable=control)
            review_id = review.id

        factories.RelationshipFactory(source=control,
                                      destination=factories.IssueFactory())

        review = all_models.Review.query.get(review_id)
        self.assertEqual(review.status, all_models.Review.STATES.REVIEWED)

    @ddt.data("Standard", "Regulation", "Requirement", "Objective", "Control",
              "Product", "System", "Process", "AccessGroup", "Contract",
              "DataAsset", "Facility", "Market", "OrgGroup", "Policy", "Risk",
              "Threat", "Vendor")
    def test_map_snapshotable(self, snapshotable):
        """Map '{}' should change review status"""
        with factories.single_commit():
            control = factories.ControlFactory()
            review = factories.ReviewFactory(
                status=all_models.Review.STATES.REVIEWED, reviewable=control)
            review_id = review.id

        self.generator.generate_relationship(
            source=control,
            destination=factories.get_model_factory(snapshotable)(),
            context=None,
        )

        review = all_models.Review.query.get(review_id)
        self.assertEqual(review.status, all_models.Review.STATES.UNREVIEWED)

    def test_unmap_snapshotable(self):
        """Unmap snapshotable should change review status"""
        control = factories.ControlFactory()
        resp, review = generate_review_object(control)
        review_id = review.id

        _, rel = self.generator.generate_relationship(
            source=control,
            destination=factories.ProductFactory(),
            context=None,
        )

        review = all_models.Review.query.get(review_id)
        resp = self.api.modify_object(
            review, {"status": all_models.Review.STATES.REVIEWED})
        self.assert200(resp)

        resp = self.api.delete(rel)
        self.assert200(resp)
        review = all_models.Review.query.get(review_id)
        self.assertEqual(review.status, all_models.Review.STATES.UNREVIEWED)

    @ddt.data(
        "Assessment",
        "Issue",
        "Program",
        "Project",
        "Audit",
        "RiskAssessment",
        "AssessmentTemplate",
        "Person",
    )
    def test_map_nonsnapshotable(self, nonsnapshotable):
        """Map '{}' shouldn't change review status"""
        control = factories.ControlFactory()
        _, review = generate_review_object(
            control, state=all_models.Review.STATES.REVIEWED)
        review_id = review.id

        review = all_models.Review.query.get(review_id)

        self.assertEqual(review.status, all_models.Review.STATES.REVIEWED)

        self.generator.generate_relationship(
            source=control,
            destination=factories.get_model_factory(nonsnapshotable)(),
            context=None,
        )

        review = all_models.Review.query.get(review_id)
        self.assertEqual(review.status, all_models.Review.STATES.REVIEWED)

    def test_unmap_nonsnapshotable(self):
        """Unmap nonsnapshotable shouldn't change review status"""
        control = factories.ControlFactory()
        resp, review = generate_review_object(
            control, state=all_models.Review.STATES.REVIEWED)
        review_id = review.id
        _, rel = self.generator.generate_relationship(
            source=control,
            destination=factories.ProgramFactory(),
            context=None,
        )

        review = all_models.Review.query.get(review_id)
        resp = self.api.modify_object(
            review, {"status": all_models.Review.STATES.REVIEWED})
        self.assert200(resp)
        review = all_models.Review.query.get(review_id)
        self.assertEqual(review.status, all_models.Review.STATES.REVIEWED)

        resp = self.api.delete(rel)
        self.assert200(resp)
        review = all_models.Review.query.get(review_id)
        self.assertEqual(review.status, all_models.Review.STATES.REVIEWED)

    def test_proposal_apply(self):
        """Reviewable object changed via proposal -> review.state-> UNREVIEWED"""
        control = factories.ControlFactory()
        _, review = generate_review_object(control)

        review_id = review.id

        proposal_content = {
            "fields": {
                "title": "new title"
            },
        }
        proposal = factories.ProposalFactory(instance=control,
                                             content=proposal_content,
                                             agenda="agenda content")
        self.api.modify_object(proposal, {"status": proposal.STATES.APPLIED})

        review = all_models.Review.query.get(review_id)
        self.assertEqual(review.status, all_models.Review.STATES.UNREVIEWED)

    def test_review_status_update(self):
        """Test updating folder preserves review status"""
        control = factories.ControlFactory()
        factories.ReviewFactory(
            reviewable=control,
            status=all_models.Review.STATES.REVIEWED,
        )
        self.api.put(control, {"folder": factories.random_str()})
        control = all_models.Control.query.get(control.id)
        self.assertEqual(control.review.status,
                         all_models.Review.STATES.REVIEWED)
コード例 #54
0
class TestCreatorAudit(TestCase):
  """Set up necessary objects and test Creator role with Audit roles"""

  def setUp(self):
    super(TestCreatorAudit, self).setUp()
    self.generator = Generator()
    self.api = Api()
    self.object_generator = ObjectGenerator()
    self.init_users()
    self.init_roles()
    self.init_test_cases()
    self.objects = {}

  def init_test_cases(self):
    """Create a dict of all possible test cases."""
    self.test_cases = {
        "Auditor": {
            "audit_role": "Auditor",
            "objects": {
                "audit": {
                    "get": 200,
                    "put": 403,
                    "delete": 403
                },
                "mapped_Issue": {
                    "get": 200,
                    "put": 200,
                    "delete": 200
                },
                "unrelated_Issue": {
                    "get": 403,
                    "put": 403,
                    "delete": 403,
                    "map": 403,
                },
                "mapped_Assessment": {
                    "get": 200,
                    "put": 200,
                    "delete": 200
                },
                "unrelated_Assessment": {
                    "get": 403,
                    "put": 403,
                    "delete": 403,
                    "map": 403,
                }
            }
        },
    }

  def init_roles(self):
    """Create a delete request for the given object."""
    response = self.api.get_query(all_models.Role, "")
    self.roles = {}
    for role in response.json.get("roles_collection").get("roles"):
      self.roles[role.get("name")] = role

  def init_users(self):
    """Create users used by test cases."""
    self.people = {}
    for name in ["creator", "notmapped", "mapped", "Auditor"]:
      _, user = self.object_generator.generate_person(
          data={"name": name}, user_role="Creator")
      self.people[name] = user

    _, user = self.object_generator.generate_person(
        data={"name": "editor"}, user_role="Editor")
    self.people["editor"] = user

  def delete(self, obj):
    """Create a delete request for the given object.

    Args:
        obj (model instance): target object to delete
    Returns:
        int: http response status code
    """
    return self.api.delete(obj).status_code

  def get(self, obj):
    """Create a get request for the given object.

    Args:
        obj (model instance): target object to get
    Returns:
        int: http response status code
    """
    return self.api.get(obj.__class__, obj.id).status_code

  def put(self, obj):
    """Create a put request for the given object.

    Args:
        obj (model instance): target object to put
    Returns:
        int: http response status code
    """
    response = self.api.get(obj.__class__, obj.id)
    if response.status_code == 200:
      return self.api.put(obj, response.json).status_code
    else:
      return response.status_code

  def map(self, dest):
    """Map audit to dest.

    Args:
        dest (model instance): target object to map to the audit
    Returns:
        int: http response status code
    """
    response = self.api.post(all_models.Relationship, {
        "relationship": {"source": {
            "id": self.objects["audit"].id,
            "type": self.objects["audit"].type,
        }, "destination": {
            "id": dest.id,
            "type": dest.type
        }, "context": None},
    })
    return response.status_code

  def init_objects(self, test_case_name):
    """Create a Program, an Audit, and a Mapped object for the test case.

    Args:
        test_case_name (string): test case to init for
    """
    # Create a program
    dummy_audit = factories.AuditFactory()
    unrelated_audit = {
        "type": "Audit",
        "context_id": dummy_audit.context.id,
        "id": dummy_audit.id,
    }
    test_case = self.test_cases[test_case_name]
    editor = self.people.get('editor')
    self.api.set_user(editor)
    random_title = factories.random_str()
    response = self.api.post(all_models.Program, {
        "program": {"title": random_title, "context": None},
    })
    self.assertEqual(response.status_code, 201)
    program_id = response.json.get("program").get("id")
    self.objects["program"] = all_models.Program.query.get(program_id)
    response = self.api.post(all_models.Audit, {
        "audit": {
            "title": random_title + " audit",
            'program': {'id': program_id},
            "status": "Planned",
            "context": None
        }
    })
    self.assertEqual(response.status_code, 201)
    context = response.json.get("audit").get("context")
    audit_id = response.json.get("audit").get("id")
    self.objects["audit"] = all_models.Audit.query.get(audit_id)
    audits = {
        "mapped": {
            "type": "Audit",
            "context_id": context["id"],
            "id": audit_id,
        },
        "unrelated": unrelated_audit,
    }

    for prefix, audit_dict in audits.items():
      random_title = factories.random_str()

      response = self.api.post(all_models.Issue, {
          "issue": {
              "title": random_title,
              "context": {
                  "type": "Context",
                  "id": audit_dict["context_id"],
              },
              "audit": audit_dict,
          },
      })
      self.assertEqual(response.status_code, 201)
      issue_id = response.json.get("issue").get("id")
      self.objects[prefix + "_Issue"] = all_models.Issue.query.get(issue_id)

      response = self.api.post(all_models.Assessment, {
          "assessment": {
              "title": random_title,
              "context": {
                  "type": "Context",
                  "id": audit_dict["context_id"],
              },
              "audit": audit_dict,
          },
      })
      self.assertEqual(response.status_code, 201)
      assessment_id = response.json.get("assessment").get("id")
      self.objects[prefix + "_Assessment"] = \
          all_models.Assessment.query.get(assessment_id)

    self.assertEqual(self.map(self.objects["mapped_Issue"]), 201)
    self.assertEqual(self.map(self.objects["mapped_Assessment"]), 201)

    # Add roles to mapped users:
    if "audit_role" in test_case:
      person = self.people.get(test_case_name)
      role = self.roles[test_case["audit_role"]]
      response = self.api.post(all_models.UserRole, {"user_role": {
          "person": {
              "id": person.id,
              "type": "Person",
              "href": "/api/people/{}".format(person.id),
          }, "role": {
              "type": "Role",
              "href": "/api/roles/{}".format(role["id"]),
              "id": role["id"],
          }, "context": {
              "type": "Context",
              "id": context.get("id"),
              "href": "/api/contexts/{}".format(context.get("id"))
          }}})
      self.assertEqual(response.status_code, 201)

  def test_creator_audit_roles(self):
    """ Test creator role with all audit scoped roles """
    # Check permissions based on test_cases:
    errors = []
    for test_case in self.test_cases:
      self.init_objects(test_case)
      person = self.people.get(test_case)
      objects = self.test_cases.get(test_case).get('objects')
      self.api.set_user(person)
      for obj, actions in objects.iteritems():
        for action in ("map", "get", "put", "delete"):
          if action not in actions:
            continue
          # reset sesion:
          db.session.commit()
          func = getattr(self, action)
          res = func(self.objects[obj])
          if res != actions[action]:
            errors.append(
                "{}: Tried {} on {}, but received {} instead of {}".format(
                    test_case, action, obj, res, actions[action]))

    self.assertEqual(errors, [])
コード例 #55
0
class TestDisabledIssueIntegration(ggrc.TestCase):
    """Tests for IssueTracker integration functionality with disabled sync."""

    # pylint: disable=invalid-name

    def setUp(self):
        # pylint: disable=super-on-old-class
        super(TestDisabledIssueIntegration, self).setUp()
        self.api = Api()
        self.client.get("/login")

    @mock.patch("ggrc.integrations.issues.Client.create_issue")
    def test_issue_creation(self, mock_create_issue):
        """Test creating Issue object with disabled integration."""
        with mock.patch.object(settings, "ISSUE_TRACKER_ENABLED", True):
            response = self.api.post(
                all_models.Issue, {
                    "issue": {
                        "title": "test title",
                        "context": None,
                        "issue_tracker": {
                            "enabled": False,
                        }
                    },
                })
        mock_create_issue.assert_not_called()
        self.assertEqual(response.status_code, 201)
        issue_id = response.json.get("issue").get("id")
        issue_tracker_issue = models.IssuetrackerIssue.get_issue(
            "Issue", issue_id)
        self.assertIsNone(issue_tracker_issue)

    @mock.patch("ggrc.integrations.issues.Client.update_issue")
    def test_issue_deletion(self, mock_update_issue):
        """Test deleting Issue object with disabled integration for issue."""
        iti = factories.IssueTrackerIssueFactory(
            enabled=False, issue_tracked_obj=factories.IssueFactory())
        with mock.patch.object(settings, "ISSUE_TRACKER_ENABLED", True):
            self.api.delete(iti.issue_tracked_obj)
        mock_update_issue.assert_not_called()

    @ddt.data(
        {
            "description": "new description",
            "issue_tracker": {
                "issue_id": TICKET_ID,
                "enabled": False
            }
        },
        {
            "test_plan": "new test plan",
            "issue_tracker": {
                "issue_id": TICKET_ID,
                "enabled": False
            }
        },
        {
            "issue_tracker": {
                "issue_id": TICKET_ID,
                "component_id": "123",
                "enabled": False
            }
        },
        {
            "issue_tracker": {
                "issue_id": TICKET_ID,
                "hotlist_id": "321",
                "enabled": False
            }
        },
        {
            "issue_tracker": {
                "issue_id": TICKET_ID,
                "issue_priority": "P2",
                "enabled": False
            }
        },
        {
            "issue_tracker": {
                "issue_id": TICKET_ID,
                "issue_severity": "S2",
                "enabled": False
            }
        },
        {
            "issue_tracker": {
                "issue_id": TICKET_ID,
                "title": "title1",
                "enabled": False
            }
        },
    )
    @mock.patch("ggrc.integrations.issues.Client.update_issue")
    def test_update_issue_object(self, issue_attrs, mock_update_issue):
        """Test updating issue object with disabled integration for issue."""
        iti = factories.IssueTrackerIssueFactory(
            enabled=False,
            issue_id=TICKET_ID,
            issue_tracked_obj=factories.IssueFactory())
        with mock.patch.object(settings, "ISSUE_TRACKER_ENABLED", True):
            self.api.put(iti.issue_tracked_obj, issue_attrs)
        mock_update_issue.assert_not_called()

    @mock.patch(
        "ggrc.integrations.issues.Client.create_issue",
        side_effect=[integrations_errors.Error, {
            "issueId": "issueId"
        }])
    @mock.patch.object(settings, "ISSUE_TRACKER_ENABLED", True)
    def test_issue_recreation(self, mock_create_issue):
        """Test retrying to turn on integration after failed creation."""
        # Arrange data.
        component_id = "1234"
        hotlist_id = "4321"
        issue_type = "Default Issue type"
        issue_priority = "P2"
        issue_severity = "S1"
        title = "test title"
        issue_tracker_attrs = {
            "enabled": True,
            "component_id": int(component_id),
            "hotlist_id": int(hotlist_id),
            "issue_type": issue_type,
            "issue_priority": issue_priority,
            "issue_severity": issue_severity,
        }

        # Perform actions and assert results.
        with mock.patch.object(integration_utils,
                               "exclude_auditor_emails",
                               return_value={
                                   u"*****@*****.**",
                               }):

            # Try to create issue. create_issue should raise exception here.
            response = self.api.post(
                all_models.Issue, {
                    "issue": {
                        "title": title,
                        "context": None,
                        "issue_tracker": issue_tracker_attrs
                    },
                })

            issue_id = response.json.get("issue").get("id")
            issue_tracker_issue = models.IssuetrackerIssue.get_issue(
                "Issue", issue_id)
            self.assertIsNone(issue_tracker_issue.issue_id)
            self.assertIsNone(issue_tracker_issue.issue_url)

            # Try to turn on integration on already created issue.
            self.api.put(issue_tracker_issue.issue_tracked_obj,
                         {"issue_tracker": issue_tracker_attrs})

            issue_id = issue_tracker_issue.issue_tracked_obj.id
            issue_tracker_issue = models.IssuetrackerIssue.get_issue(
                "Issue", issue_id)
            self.assertEqual(issue_tracker_issue.issue_url,
                             "http://issue/issueId")

    @mock.patch("ggrc.integrations.issues.Client.update_issue")
    def test_adding_comment_to_issue(self, update_issue_mock):
        """Test not adding comment to issue when issue tracker disabled."""
        iti = factories.IssueTrackerIssueFactory(
            enabled=False, issue_tracked_obj=factories.IssueFactory())
        comment = factories.CommentFactory(description="test comment")

        with mock.patch.object(settings, "ISSUE_TRACKER_ENABLED", True):
            self.api.post(
                all_models.Relationship, {
                    "relationship": {
                        "source": {
                            "id": iti.issue_tracked_obj.id,
                            "type": "Issue"
                        },
                        "destination": {
                            "id": comment.id,
                            "type": "comment"
                        },
                        "context": None
                    },
                })
        update_issue_mock.assert_not_called()

    def test_prepare_update_json(self):
        """Test prepare_update_json method for Issue."""
        with factories.single_commit():
            issue = factories.IssueFactory()
            iti = factories.IssueTrackerIssueFactory(
                enabled=True,
                issue_tracked_obj=issue,
                title='title',
                component_id=123,
                hotlist_id=321,
                issue_type="PROCESS",
                issue_priority="P3",
                issue_severity="S3",
            )
        without_info = issue_integration.prepare_issue_update_json(issue)
        issue_info = issue.issue_tracker
        with_info = issue_integration.prepare_issue_update_json(
            issue, issue_info)

        expected_info = {
            'component_id': 123,
            'severity': u'S3',
            'title': iti.title,
            'hotlist_ids': [
                321,
            ],
            'priority': u'P3',
            'type': u'PROCESS',
        }
        self.assertEqual(expected_info, with_info)
        self.assertEqual(without_info, with_info)
コード例 #56
0
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)
コード例 #57
0
class TestAuditRBAC(TestCase):
  """Test audit RBAC"""
  # pylint: disable=too-many-instance-attributes

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

  def setUp(self):
    """Imports test_csvs/audit_rbac_snapshot_create.csv needed by the tests"""
    TestCase.clear_data()
    self.api = Api()
    self.objgen = integration.ggrc.generator.ObjectGenerator()

    self.csv_files = itertools.cycle([
        "audit_rbac_snapshot_create.csv",
        "audit_rbac_snapshot_update.csv"
    ])

    self._import_file(next(self.csv_files))
    self.people = all_models.Person.eager_query().all()

    self.program = db.session.query(all_models.Program).filter(
        all_models.Program.slug == "PMRBACPROGRAM-1"
    ).one()

    sources = set(r.source for r in self.program.related_sources)
    destinations = set(r.destination
                       for r in self.program.related_destinations)
    related = [obj for obj in sources.union(destinations)
               if not isinstance(obj, all_models.Person)]
    self.related_objects = related

    self.api = Api()
    self.client.get("/login")

    self.audit = self.create_audit()

    self.snapshots = all_models.Snapshot.eager_query().all()

    self.sanity_check()

  def create_audit(self):
    """Create default audit for audit snapshot RBAC tests"""
    _, audit = self.objgen.generate_object(all_models.Audit, {
        "title": "Snapshotable audit",
        "program": {"id": self.program.id},
        "status": "Planned",
        "snapshots": {
            "operation": "create",
        },
        "context": {
            "type": "Context",
            "id": self.program.context_id,
            "href": "/api/contexts/{}".format(self.program.context_id)
        }
    })
    self.add_auditors(audit)
    return audit

  def add_auditors(self, audit):
    """Add auditors to audits via POST user_role call"""
    auditor_emails = [
        "*****@*****.**",
        "*****@*****.**",
        "*****@*****.**",
        "*****@*****.**",
    ]
    program_reader_emails = [
        "*****@*****.**",
    ]
    auditor_role = db.session.query(all_models.Role).filter(
        all_models.Role.name == "Auditor"
    ).one()
    program_reader_role = db.session.query(all_models.Role).filter(
        all_models.Role.name == "ProgramReader"
    ).one()

    program = db.session.query(all_models.Program).filter(
        all_models.Program.slug == "PMRBACPROGRAM-1"
    ).one()

    user_roles = [
        (auditor_emails, auditor_role, audit.context),
        (program_reader_emails, program_reader_role, program.context)
    ]

    for emails, role, context in user_roles:
      for email in emails:
        auditor = all_models.Person.query.filter(
            all_models.Person.email == email).one()
        self.objgen.generate_user_role(auditor, role, context)

  def update_audit(self):
    """Update default audit"""
    self._import_file(next(self.csv_files))

    audit = all_models.Audit.query.filter(
        all_models.Audit.title == "Snapshotable audit"
    ).one()
    self.audit = audit

    self.api.modify_object(self.audit, {
        "snapshots": {
            "operation": "upsert"
        }
    })

  def sanity_check(self):
    """Sanity check if the audit_rbac.csv was imported correctly"""
    assert len(self.people) == 21, \
        "Expecting 21 people not {}.".format(len(self.people))
    assert len(self.related_objects) == 19, \
        "Expecting 19 objects mapped to program not {}.".format(
            len(self.related_objects))
    assert len(self.snapshots) == 19, \
        "Expecting 19 snapshots for default audit not {}.".format(
            len(self.snapshots))
    assert all(ss.parent_id == self.audit.id for ss in self.snapshots), \
        "All snapshots should be in default audit scope!"

  def read(self, objects):
    """Attempt to do a GET request for every object in the objects list"""
    responses = []
    for obj in objects:
      status_code = self.api.get(obj.__class__, obj.id).status_code
      responses.append((obj.type, status_code))
    return responses

  def update(self, objects):
    """Attempt to do a PUT request for every object in the objects list"""
    scope_response = self.api.get(self.audit.__class__, self.audit.id)
    if scope_response.status_code == 200:
      self.update_audit()

    responses = []
    for obj in objects:
      response = self.api.get(obj.__class__, obj.id)
      status_code = response.status_code
      if response.status_code == 200:
        data = response.json
        if obj.type == "Snapshot":
          data.update({
              "update_revision": "latest"
          })
        put_call = self.api.put(obj, data)
        status_code = put_call.status_code
      responses.append((obj.type, status_code))
    return responses

  def call_api(self, method, expected_statuses):
    """Calls the REST api with a given method and returns a list of
       status_codes that do not match the expected_statuses dict"""
    all_errors = []
    for person in self.people:
      self.api.set_user(person)
      responses = method(self.snapshots + [self.audit])
      for type_, code in responses:
        if code != expected_statuses[person.email][type_]:
          all_errors.append("{} does not have {} access to {} ({})".format(
              person.email, method.__name__, type_, code))
    return all_errors

  def test_read_access_on_mapped(self):
    """Test READ access to snapshotted objects of default audit"""
    expected_statuses = defaultdict(lambda: defaultdict(lambda: 200))
    exceptional_users = (
        ("*****@*****.**", DEFAULT_LACK_OF_PERMISSIONS),
    )
    for user, exceptions in exceptional_users:
      for type_, status_code in exceptions.items():
        expected_statuses[user][type_] = status_code
    errors = self.call_api(self.read, expected_statuses)
    assert not errors, "\n".join(errors)

  def test_update_access_on_mapped(self):
    """Test UPDATE access to snapshotted objects of default audit"""
    expected_statuses = defaultdict(lambda: defaultdict(lambda: 200))

    exceptional_users = (
        ("*****@*****.**", DEFAULT_LACK_OF_PERMISSIONS),
        ("*****@*****.**", DEFAULT_LACK_OF_PERMISSIONS),
        ("*****@*****.**", DEFAULT_LACK_OF_PERMISSIONS),
        ("*****@*****.**", DEFAULT_LACK_OF_PERMISSIONS),
        # Auditor roles
        ("*****@*****.**", DEFAULT_AUDITOR_PERMISSIONS),
        ("*****@*****.**", DEFAULT_AUDITOR_PERMISSIONS)
    )

    for user, exceptions in exceptional_users:
      for type_, status_code in exceptions.items():
        expected_statuses[user][type_] = status_code

    errors = self.call_api(self.update, expected_statuses)
    assert not errors, "\n".join(errors)
コード例 #58
0
class TestWorkflowsApiPost(TestCase):
    """Test class for ggrc workflow api post action."""
    def setUp(self):
        super(TestWorkflowsApiPost, self).setUp()
        self.api = Api()
        self.generator = wf_generator.WorkflowsGenerator()

    def tearDown(self):
        pass

    def test_send_invalid_data(self):
        """Test send invalid data on Workflow post."""
        data = self.get_workflow_dict()
        del data["workflow"]["title"]
        del data["workflow"]["context"]
        response = self.api.post(all_models.Workflow, data)
        self.assert400(response)
        # TODO: check why response.json["message"] is empty

    def test_create_one_time_workflows(self):
        """Test simple create one time Workflow over api."""
        data = self.get_workflow_dict()
        response = self.api.post(all_models.Workflow, data)
        self.assertEqual(response.status_code, 201)

    def test_create_weekly_workflow(self):
        """Test create valid weekly wf"""
        data = self.get_workflow_dict()
        data["workflow"]["repeat_every"] = 7
        data["workflow"]["unit"] = "day"
        data["workflow"]["title"] = "Weekly"
        response = self.api.post(all_models.Workflow, data)
        self.assertEqual(response.status_code, 201)

    def test_create_annually_workflow(self):
        """Test create valid annual wf"""
        data = self.get_workflow_dict()
        data["workflow"]["repeat_every"] = 12
        data["workflow"]["unit"] = "month"
        data["workflow"]["title"] = "Annually"
        response = self.api.post(all_models.Workflow, data)
        self.assertEqual(response.status_code, 201)

    @ddt.data("wrong value", 0, -4)
    def test_create_wrong_repeat_every_workflow(self, value):  # noqa pylint: disable=invalid-name
        """Test case for invalid repeat_every value"""
        data = self.get_workflow_dict()
        data["workflow"]["repeat_every"] = value
        data["workflow"]["unit"] = "month"
        data["workflow"]["title"] = "Wrong wf"
        response = self.api.post(all_models.Workflow, data)
        self.assertEqual(response.status_code, 400)

    def test_create_wrong_unit_workflow(self):
        """Test case for invalid unit value"""
        data = self.get_workflow_dict()
        data["workflow"]["repeat_every"] = 12
        data["workflow"]["unit"] = "wrong value"
        data["workflow"]["title"] = "Wrong wf"
        response = self.api.post(all_models.Workflow, data)
        self.assertEqual(response.status_code, 400)

    def test_create_task_group(self):
        """Test create task group over api."""
        wf_data = self.get_workflow_dict()
        wf_data["workflow"]["title"] = "Create_task_group"
        wf_response = self.api.post(all_models.Workflow, wf_data)

        data = self.get_task_group_dict(wf_response.json["workflow"])

        response = self.api.post(all_models.TaskGroup, data)
        self.assertEqual(response.status_code, 201)

    # TODO: Api should be able to handle invalid data
    @unittest.skip("Not implemented.")
    def test_create_task_group_invalid_workflow_data(self):  # noqa pylint: disable=invalid-name
        """Test create task group with invalid data."""
        data = self.get_task_group_dict({"id": -1, "context": {"id": -1}})
        response = self.api.post(all_models.TaskGroup, data)
        self.assert400(response)

    @staticmethod
    def get_workflow_dict():
        return {
            "workflow": {
                "custom_attribute_definitions": [],
                "custom_attributes": {},
                "title": "One_time",
                "description": "",
                "unit": None,
                "repeat_every": None,
                "notify_on_change": False,
                "task_group_title": "Task Group 1",
                "notify_custom_message": "",
                "is_verification_needed": True,
                "owners": None,
                "context": None,
            }
        }

    @staticmethod
    def get_task_group_dict(workflow):
        return {
            "task_group": {
                "custom_attribute_definitions": [],
                "custom_attributes": {},
                "_transient": {},
                "contact": {
                    "id": 1,
                    "href": "/api/people/1",
                    "type": "Person"
                },
                "workflow": {
                    "id": workflow["id"],
                    "href": "/api/workflows/%d" % workflow["id"],
                    "type": "Workflow"
                },
                "context": {
                    "id": workflow["context"]["id"],
                    "href": "/api/contexts/%d" % workflow["context"]["id"],
                    "type": "Context"
                },
                "modal_title": "Create Task Group",
                "title": "Create_task_group",
                "description": "",
            }
        }

    @ddt.data({}, {"repeat_every": 5, "unit": "month"})
    def test_repeat_multiplier_field(self, data):
        """Check repeat_multiplier is set to 0 after wf creation."""
        with factories.single_commit():
            workflow = wf_factories.WorkflowFactory(**data)
        workflow_id = workflow.id
        self.assertEqual(
            0,
            all_models.Workflow.query.get(workflow_id).repeat_multiplier)

    # TODO: Unskip in the patch 2
    @unittest.skip("Will be activated in patch 2")
    def test_change_to_one_time_wf(self):
        """Check repeat_every and unit can be set to Null only together."""
        with factories.single_commit():
            workflow = wf_factories.WorkflowFactory(repeat_every=12,
                                                    unit="day")
        resp = self.api.put(workflow, {"repeat_every": None, "unit": None})
        self.assert200(resp)

    @ddt.data({"repeat_every": 5}, {"unit": "month"})
    def test_change_repeat_every(self, data):
        """Check repeat_every or unit can not be changed once set."""
        with factories.single_commit():
            workflow = wf_factories.WorkflowFactory()
        resp = self.api.put(workflow, data)
        self.assert400(resp)

    def test_not_change_to_one_time_wf(self):
        """Check repeat_every or unit can't be set to Null separately.
    This test will be useful in the 2nd patch, where we allow to change
    WF setup
    """
        with factories.single_commit():
            workflow = wf_factories.WorkflowFactory(repeat_every=12,
                                                    unit="day")
        resp = self.api.put(workflow, {"repeat_every": None})
        self.assert400(resp)
        resp = self.api.put(workflow, {"unit": None})
        self.assert400(resp)

    @ddt.data(True, False)
    def test_autogen_verification_flag(self, flag):
        """Check is_verification_needed flag for activate WF action."""
        with factories.single_commit():
            workflow = wf_factories.WorkflowFactory(
                is_verification_needed=flag)
            group = wf_factories.TaskGroupFactory(workflow=workflow)
            wf_factories.TaskGroupTaskFactory(task_group=group)
        data = [{
            "cycle": {
                "autogenerate": True,
                "isOverdue": False,
                "workflow": {
                    "id": workflow.id,
                    "type": "Workflow",
                },
                "context": {
                    "id": workflow.context_id,
                    "type": "Context",
                },
            }
        }]
        resp = self.api.send_request(self.api.client.post,
                                     api_link="/api/cycles",
                                     data=data)
        cycle_id = resp.json[0][1]["cycle"]["id"]
        self.assertEqual(
            flag,
            all_models.Cycle.query.get(cycle_id).is_verification_needed)

    @ddt.data(True, False)
    def test_change_verification_flag_positive(self, flag):  # noqa pylint: disable=invalid-name
        """is_verification_needed flag is changeable for DRAFT workflow."""
        with factories.single_commit():
            workflow = wf_factories.WorkflowFactory(
                is_verification_needed=flag)
        self.assertEqual(workflow.status, all_models.Workflow.DRAFT)
        workflow_id = workflow.id
        resp = self.api.put(workflow, {"is_verification_needed": not flag})
        self.assert200(resp)
        self.assertEqual(
            all_models.Workflow.query.get(workflow_id).is_verification_needed,
            not flag)

    @ddt.data(True, False)
    def test_change_verification_flag_negative(self, flag):  # noqa pylint: disable=invalid-name
        with freezegun.freeze_time("2017-08-10"):
            with factories.single_commit():
                workflow = wf_factories.WorkflowFactory(
                    unit=all_models.Workflow.WEEK_UNIT,
                    is_verification_needed=flag,
                    repeat_every=1)
                wf_factories.TaskGroupTaskFactory(
                    task_group=wf_factories.TaskGroupFactory(
                        context=factories.ContextFactory(), workflow=workflow),
                    # Two cycles should be created
                    start_date=datetime.date(2017, 8, 3),
                    end_date=datetime.date(2017, 8, 7))
            workflow_id = workflow.id
            self.assertEqual(workflow.status, all_models.Workflow.DRAFT)
            self.generator.activate_workflow(workflow)
            workflow = all_models.Workflow.query.get(workflow_id)
            self.assertEqual(workflow.status, all_models.Workflow.ACTIVE)
            resp = self.api.put(workflow, {"is_verification_needed": not flag})
            self.assert400(resp)
            workflow = all_models.Workflow.query.get(workflow_id)
            self.assertEqual(workflow.is_verification_needed, flag)

            # End all current cycles
            for cycle in workflow.cycles:
                self.generator.modify_object(cycle, {'is_current': False})
            workflow = all_models.Workflow.query.filter(
                all_models.Workflow.id == workflow_id).first()
            self.assertEqual(workflow.status, all_models.Workflow.INACTIVE)
            resp = self.api.put(workflow, {"is_verification_needed": not flag})
            self.assert400(resp)
            workflow = all_models.Workflow.query.get(workflow_id)
            self.assertEqual(workflow.is_verification_needed, flag)

    @ddt.data(True, False)
    def test_not_change_vf_flag(self, flag):
        """Check is_verification_needed not change on update."""
        with factories.single_commit():
            workflow = wf_factories.WorkflowFactory(
                is_verification_needed=flag)
        workflow_id = workflow.id
        resp = self.api.put(workflow, {"is_verification_needed": flag})
        self.assert200(resp)
        self.assertEqual(
            flag,
            all_models.Workflow.query.get(workflow_id).is_verification_needed)

    @ddt.data(True, False, None)
    def test_create_vf_flag(self, flag):
        """Check is_verification_needed flag setup on create."""
        data = self.get_workflow_dict()
        if flag is None:
            data['workflow'].pop('is_verification_needed', None)
        else:
            data['workflow']['is_verification_needed'] = flag
        resp = self.api.post(all_models.Workflow, data)
        self.assertEqual(201, resp.status_code)
        workflow_id = resp.json['workflow']['id']
        self.assertEqual(
            flag if flag is not None else True,
            all_models.Workflow.query.get(workflow_id).is_verification_needed)
コード例 #59
0
ファイル: test_api_calls.py プロジェクト: zidarsk8/ggrc-core
class TestStatusApiPost(TestCase):
  """Test for api calls changes states of Cycle, Tasks and Groups."""

  def setUp(self):
    super(TestStatusApiPost, self).setUp()
    self.api = Api()
    with factories.single_commit():
      self.workflow = wf_factories.WorkflowFactory()
      self.cycle = wf_factories.CycleFactory(workflow=self.workflow)
      self.group = wf_factories.CycleTaskGroupFactory(
          cycle=self.cycle,
          context=self.cycle.workflow.context
      )
      self.task = wf_factories.CycleTaskFactory(
          cycle=self.cycle,
          cycle_task_group=self.group,
          context=self.cycle.workflow.context
      )

  def setup_cycle_state(self, is_verification_needed):
    """Setup cycle is_verification_needed state."""
    self.cycle.is_verification_needed = is_verification_needed
    db.session.add(self.cycle)
    db.session.commit()

  @ddt.data(u"Assigned",
            u"InProgress",
            u"Finished",
            u"Verified",
            u"Declined")
  def test_set_state_verified_task(self, state):
    """Check state for verification required task."""
    self.setup_cycle_state(True)
    resp = self.api.put(self.task, data={"status": state})
    task = all_models.CycleTaskGroupObjectTask.query.get(
        resp.json["cycle_task_group_object_task"]["id"]
    )
    self.assertEqual(state, task.status)

  @ddt.data((u"Assigned", True),
            (u"InProgress", True),
            (u"Finished", True),
            (u"Verified", False),
            (u"Declined", False))
  @ddt.unpack
  def test_state_non_verified_task(self, state, is_valid):
    """Check state for verification non required task."""
    self.setup_cycle_state(False)
    resp = self.api.put(self.task, data={"status": state})
    if is_valid:
      task = all_models.CycleTaskGroupObjectTask.query.get(
          resp.json["cycle_task_group_object_task"]["id"]
      )
      self.assertEqual(state, task.status)
    else:
      self.assert400(resp)

  @ddt.data(u"Assigned",
            u"InProgress",
            u"Finished",
            u"Verified")
  def test_state_verified_group(self, state):
    """Check state for verification required group."""
    self.setup_cycle_state(True)
    resp = self.api.put(self.group, data={"status": state})
    group = all_models.CycleTaskGroup.query.get(
        resp.json["cycle_task_group"]["id"]
    )
    self.assertEqual(state, group.status)

  @ddt.data((u"Assigned", True),
            (u"InProgress", True),
            (u"Finished", True),
            (u"Verified", False))
  @ddt.unpack
  def test_state_non_verified_group(self, state, is_valid):
    """Check state for verification non required group."""
    self.setup_cycle_state(False)
    resp = self.api.put(self.group, data={"status": state})
    if is_valid:
      group = all_models.CycleTaskGroup.query.get(
          resp.json["cycle_task_group"]["id"]
      )
      self.assertEqual(state, group.status)
    else:
      self.assert400(resp)

  @ddt.data(u"Assigned",
            u"InProgress",
            u"Finished",
            u"Verified")
  def test_state_verified_cycle(self, state):
    """Check state for verification required cycle."""
    self.setup_cycle_state(True)
    resp = self.api.put(self.cycle, data={"status": state})
    cycle = all_models.Cycle.query.get(resp.json["cycle"]["id"])
    self.assertEqual(state, cycle.status)

  @ddt.data((u"Assigned", True),
            (u"InProgress", True),
            (u"Finished", True),
            (u"Verified", False))
  @ddt.unpack
  def test_state_non_verified_cycle(self, state, is_valid):
    """Check state for verification non required cycle."""
    self.setup_cycle_state(False)
    resp = self.api.put(self.cycle, data={"status": state})
    if is_valid:
      cycle = all_models.Cycle.query.get(resp.json["cycle"]["id"])
      self.assertEqual(state, cycle.status)
    else:
      self.assert400(resp)

  @ddt.data(True, False)
  def test_change_is_verification(self, flag):
    """Try to change cycle is_verification_needed."""
    self.setup_cycle_state(flag)
    resp = self.api.put(self.cycle, data={"is_verification_needed": flag})
    self.assert200(resp)
    cycle = all_models.Cycle.query.get(resp.json["cycle"]["id"])
    self.assertEqual(flag, cycle.is_verification_needed)

  @ddt.data(True, False)
  def test_change_is_vf_wrong(self, flag):
    """Try to change cycle is_verification_needed not changed."""
    self.setup_cycle_state(flag)
    resp = self.api.put(self.cycle, data={"is_verification_needed": not flag})
    self.assert200(resp)
    cycle = all_models.Cycle.query.get(resp.json["cycle"]["id"])
    self.assertEqual(flag, cycle.is_verification_needed)

  @ddt.data(True, False)
  def test_change_cycle_none_flag(self, flag):
    """Try to change cycle is_verification_needed not changed
    by not sending is_verification_flag."""
    self.setup_cycle_state(flag)
    resp = self.api.put(self.cycle, not_send_fields=["is_verification_needed"])
    self.assert200(resp)
    cycle = all_models.Cycle.query.get(resp.json["cycle"]["id"])
    self.assertEqual(flag, cycle.is_verification_needed)

  @ddt.data(True, False)
  def test_change_wf_none_flag(self, flag):
    """Try to change workflow is_verification_needed not changed by
    not sending is_verification_flag."""
    db.engine.execute(
        "update workflows set is_verification_needed={} where id={}".format(
            flag, self.workflow.id
        )
    )
    resp = self.api.put(self.workflow,
                        not_send_fields=["is_verification_needed"])
    self.assert200(resp)
    workflow = all_models.Workflow.query.get(resp.json["workflow"]["id"])
    self.assertEqual(flag, workflow.is_verification_needed)
コード例 #60
0
class TestWorkflowsApiPost(TestCase):
    """Test class for ggrc workflow api post action."""
    def setUp(self):
        super(TestWorkflowsApiPost, self).setUp()
        self.api = Api()
        self.generator = wf_generator.WorkflowsGenerator()
        self.wf_admin_id = all_models.Person.query.first().id
        with factories.single_commit():
            self.people_ids = [factories.PersonFactory().id for _ in xrange(6)]

    def tearDown(self):
        pass

    def _delete_and_check_related_acl(self, related_model, exp_acl_count,
                                      is_deleted):
        """Delete related model and check remaining ACL count.

    Args:
        related_model: related model class
        exp_acl_count: expected related ACL count after delete operation
        is_deleted: is related object already deleted
    """
        if is_deleted:
            related_count = related_model.query.count()
            self.assertEqual(related_count, 0)
        else:
            related = related_model.query.one()
            response = self.api.delete(related)
            self.assert200(response)

        related_acl_count = all_models.AccessControlList.query.filter(
            all_models.AccessControlList.object_type ==
            related_model.__name__).count()
        self.assertEqual(related_acl_count, 0)

        bg_task_count = all_models.AccessControlList.query.filter(
            all_models.AccessControlList.object_type ==
            "BackgroundTask").count()

        all_acl_count = all_models.AccessControlList.query.count()
        self.assertEqual(all_acl_count - bg_task_count, exp_acl_count)

    def test_acl_on_object_deletion(self):
        """Test related ACL records removed on related object delete"""
        self._create_propagation_acl_test_data()
        acl_count = all_models.AccessControlList.query.count()
        self.assertNotEqual(acl_count, 0)

        admin = all_models.Person.query.get(1)
        self.api.set_user(admin)

        related_models = (
            (all_models.TaskGroup, 16, False),
            (all_models.TaskGroupTask, 16, True),
            (all_models.Cycle, 2, False),
            (all_models.CycleTaskGroup, 2, True),
            (all_models.CycleTaskGroupObjectTask, 2, True),
        )
        for related_model, acl_count, is_deleted in related_models:
            self._delete_and_check_related_acl(related_model, acl_count,
                                               is_deleted)

    def test_acl_on_workflow_delete(self):
        """Test related ACL records removed on Workflow delete"""
        self._create_propagation_acl_test_data()
        acl_count = all_models.AccessControlList.query.count()
        self.assertNotEqual(acl_count, 0)

        admin = all_models.Person.query.get(1)
        self.api.set_user(admin)
        workflow = all_models.Workflow.query.one()
        response = self.api.delete(workflow)
        self.assert200(response)

        acl_count = all_models.AccessControlList.query.count()
        bg_acl_count = all_models.AccessControlList.query.filter(
            all_models.AccessControlList.object_type ==
            "BackgroundTask").count()
        self.assertEqual(acl_count, bg_acl_count)

    def test_acl_for_new_related_object(self):
        """Test Workflow ACL propagation for new related objects."""
        data = self.get_workflow_dict()
        acl_map = {
            self.people_ids[0]: WF_ROLES['Admin'],
            self.people_ids[1]: WF_ROLES['Workflow Member'],
        }
        data["workflow"]["access_control_list"] = acl_helper.get_acl_list(
            acl_map)
        data["workflow"]["unit"] = "week"
        data["workflow"]["repeat_every"] = 1
        response = self.api.post(all_models.Workflow, data)
        self.assertEqual(response.status_code, 201)

        data = self.get_task_group_dict(response.json["workflow"])
        data["task_group"]["contact"]["id"] = self.people_ids[2]
        data["task_group"]["contact"]["href"] = "/api/people/{}".format(
            self.people_ids[2])
        response = self.api.post(all_models.TaskGroup, data)
        self.assertEqual(response.status_code, 201)

        task_group = all_models.TaskGroup.eager_query().one()
        data = self.get_task_dict(task_group)
        data["task_group_task"]["start_date"] = "2018-01-04"
        data["task_group_task"]["end_date"] = "2018-01-05"
        response = self.api.post(all_models.TaskGroupTask, data)
        self.assertEqual(response.status_code, 201)

        workflow = all_models.Workflow.query.one()
        with freezegun.freeze_time("2018-01-05"):  # Generate 1 cycle
            self.generator.activate_workflow(workflow)

        cycle_task = all_models.CycleTaskGroupObjectTask.query.one()
        with factories.single_commit():
            comment = factories.CommentFactory()
            factories.RelationshipFactory(source=comment,
                                          destination=cycle_task)

        self._check_propagated_acl(2, has_comment=True)

    @ddt.data('Admin', 'Workflow Member')
    def test_tg_assignee(self, role_name):
        """Test TaskGroup assignee already has {0} role."""
        data = self.get_workflow_dict()
        init_acl = {
            self.people_ids[0]: WF_ROLES['Admin'],
            self.people_ids[1]: WF_ROLES[role_name],
        }
        data['workflow']['access_control_list'] = acl_helper.get_acl_list(
            init_acl)
        response = self.api.post(all_models.Workflow, data)
        self.assertEqual(response.status_code, 201)

        data = self.get_task_group_dict(response.json["workflow"])
        data["task_group"]["contact"]["id"] = self.people_ids[1]
        data["task_group"]["contact"]["href"] = "/api/people/{}".format(
            self.people_ids[1])
        response = self.api.post(all_models.TaskGroup, data)
        self.assertEqual(response.status_code, 201)

        workflow = all_models.Workflow.query.one()
        task_group = all_models.TaskGroup.query.one()

        ac_people = all_models.AccessControlPerson.query.filter(
            all_models.AccessControlPerson.person_id == task_group.contact_id,
        ).all()
        self.assertEqual(len(ac_people), 1)

        actual = {(acp.ac_list.object_type, acp.ac_list.object_id)
                  for acp in ac_people}
        self.assertIn((workflow.type, workflow.id), actual)
        self.assertNotIn((task_group.type, task_group.id), actual)

    def test_task_group_assignee_gets_workflow_member(self):  # noqa pylint: disable=invalid-name
        """Test TaskGroup assignee gets WorkflowMember role."""
        data = self.get_workflow_dict()
        init_acl = {
            self.people_ids[0]: WF_ROLES['Admin'],
            self.people_ids[1]: WF_ROLES['Workflow Member'],
        }
        data['workflow']['access_control_list'] = acl_helper.get_acl_list(
            init_acl)
        response = self.api.post(all_models.Workflow, data)
        self.assertEqual(response.status_code, 201)

        data = self.get_task_group_dict(response.json["workflow"])
        data["task_group"]["contact"]["id"] = self.people_ids[2]
        data["task_group"]["contact"]["href"] = "/api/people/{}".format(
            self.people_ids[2])
        response = self.api.post(all_models.TaskGroup, data)
        self.assertEqual(response.status_code, 201)

        workflow = all_models.Workflow.query.one()

        wf_members = [
            acp.person.id for acp in
            workflow.acr_name_acl_map["Workflow Member"].access_control_people
        ]
        self.assertIn(self.people_ids[2], wf_members)

    def _create_propagation_acl_test_data(self):  # noqa pylint: disable=invalid-name
        """Create objects for Workflow ACL propagation test."""
        with freezegun.freeze_time("2017-08-9"):
            with factories.single_commit():
                workflow = wf_factories.WorkflowFactory(
                    title='wf1',
                    unit=all_models.Workflow.WEEK_UNIT,
                    is_verification_needed=True,
                    repeat_every=1)
                wf_factories.TaskGroupTaskFactory(
                    title='tgt1',
                    task_group=wf_factories.TaskGroupFactory(
                        title='tg1',
                        context=factories.ContextFactory(),
                        workflow=workflow),
                    # One cycle should be created
                    start_date=datetime.date(2017, 8, 3),
                    end_date=datetime.date(2017, 8, 7))
            self.generator.activate_workflow(workflow)
            workflow = all_models.Workflow.query.one()
            acl_map = {
                self.people_ids[0]: WF_ROLES['Admin'],
                self.people_ids[1]: WF_ROLES['Workflow Member'],
                self.people_ids[2]: WF_ROLES['Workflow Member'],
            }
            put_params = {
                'access_control_list': acl_helper.get_acl_list(acl_map)
            }
            response = self.api.put(workflow, put_params)
            self.assert200(response)

    def _check_propagated_acl(self, roles_count, has_comment=False):
        """ Check Workflow propagated ACL records.

    Args:
        roles_count: roles' count created in test
        has_comment: indicator that related objects contain comments
    """
        related_objects = [
            (all_models.TaskGroup.query.one().id,
             all_models.TaskGroup.__name__),
            (all_models.TaskGroupTask.query.one().id,
             all_models.TaskGroupTask.__name__),
            (all_models.Cycle.query.one().id, all_models.Cycle.__name__),
            (all_models.CycleTaskGroup.query.one().id,
             all_models.CycleTaskGroup.__name__),
            (all_models.CycleTaskGroupObjectTask.query.one().id,
             all_models.CycleTaskGroupObjectTask.__name__),
        ]
        if has_comment:
            related_objects.append((all_models.Comment.query.one().id,
                                    all_models.Comment.__name__), )

        # *2 is for relationships
        related_count = roles_count * len(related_objects) * 2

        # additional acl count for Relationship and Comment propagation of
        # Task Assignees/Task Secondary Assignees access control roles
        if has_comment:
            related_count += roles_count * 2

        all_acls = all_models.AccessControlList.query.filter(
            all_models.AccessControlList.parent_id_nn != 0).count()
        self.assertEqual(all_acls, related_count)

    def test_assign_workflow_acl(self):
        """Test propagation Workflow ACL roles on Workflow's update ACL records."""
        self._create_propagation_acl_test_data()
        self._check_propagated_acl(2)

    def test_unassign_workflow_acl(self):
        """Test propagation Workflow ACL roles on person unassigned."""
        self._create_propagation_acl_test_data()
        with freezegun.freeze_time("2017-08-9"):
            workflow = all_models.Workflow.query.one()
            acl_map = {
                self.people_ids[0]: WF_ROLES['Admin'],
                self.people_ids[1]: WF_ROLES['Workflow Member'],
            }
            put_params = {
                'access_control_list': acl_helper.get_acl_list(acl_map)
            }
            response = self.api.put(workflow, put_params)
            self.assert200(response)

        self._check_propagated_acl(2)

    def test_post_workflow_with_acl(self):
        """Test PUT workflow with ACL."""
        data = self.get_workflow_dict()

        exp_res = {
            self.wf_admin_id: WF_ROLES['Admin'],
            self.people_ids[0]: WF_ROLES['Admin'],
            self.people_ids[1]: WF_ROLES['Workflow Member'],
            self.people_ids[2]: WF_ROLES['Workflow Member'],
            self.people_ids[3]: WF_ROLES['Workflow Member']
        }
        data['workflow']['access_control_list'] = acl_helper.get_acl_list(
            exp_res)
        response = self.api.post(all_models.Workflow, data)
        self.assertEqual(response.status_code, 201)
        workflow = all_models.Workflow.eager_query().one()
        act_res = {
            person.id: acl.ac_role_id
            for person, acl in workflow.access_control_list
        }
        self.assertDictEqual(exp_res, act_res)

    def test_update_workflow_acl_people(self):
        """Test PUT workflow with updated ACL."""
        data = self.get_workflow_dict()
        init_map = {
            self.wf_admin_id: WF_ROLES['Admin'],
            self.people_ids[0]: WF_ROLES['Workflow Member'],
        }
        data['workflow']['access_control_list'] = acl_helper.get_acl_list(
            init_map)
        response = self.api.post(all_models.Workflow, data)
        self.assertEqual(response.status_code, 201)
        exp_res = {
            self.people_ids[0]: WF_ROLES['Admin'],
            self.people_ids[1]: WF_ROLES['Admin'],
            self.people_ids[2]: WF_ROLES['Workflow Member'],
            self.people_ids[3]: WF_ROLES['Workflow Member'],
            self.people_ids[4]: WF_ROLES['Workflow Member']
        }
        workflow = all_models.Workflow.eager_query().one()
        put_params = {'access_control_list': acl_helper.get_acl_list(exp_res)}
        response = self.api.put(workflow, put_params)
        self.assert200(response)
        workflow = all_models.Workflow.eager_query().one()
        act_res = {
            person.id: acl.ac_role_id
            for person, acl in workflow.access_control_list
        }
        self.assertDictEqual(exp_res, act_res)

    def test_send_invalid_data(self):
        """Test send invalid data on Workflow post."""
        data = self.get_workflow_dict()
        del data["workflow"]["title"]
        response = self.api.post(all_models.Workflow, data)
        self.assert400(response)

    def test_create_one_time_workflows(self):
        """Test simple create one time Workflow over api."""
        data = self.get_workflow_dict()
        response = self.api.post(all_models.Workflow, data)
        self.assertEqual(response.status_code, 201)

    def test_create_weekly_workflow(self):
        """Test create valid weekly wf"""
        data = self.get_workflow_dict()
        data["workflow"]["repeat_every"] = 7
        data["workflow"]["unit"] = "day"
        data["workflow"]["title"] = "Weekly"
        response = self.api.post(all_models.Workflow, data)
        self.assertEqual(response.status_code, 201)

    def test_create_annually_workflow(self):
        """Test create valid annual wf"""
        data = self.get_workflow_dict()
        data["workflow"]["repeat_every"] = 12
        data["workflow"]["unit"] = "month"
        data["workflow"]["title"] = "Annually"
        response = self.api.post(all_models.Workflow, data)
        self.assertEqual(response.status_code, 201)

    @ddt.data("wrong value", 0, -4)
    def test_create_wrong_repeat_every_workflow(self, value):  # noqa pylint: disable=invalid-name
        """Test case for invalid repeat_every value"""
        data = self.get_workflow_dict()
        data["workflow"]["repeat_every"] = value
        data["workflow"]["unit"] = "month"
        data["workflow"]["title"] = "Wrong wf"
        response = self.api.post(all_models.Workflow, data)
        self.assertEqual(response.status_code, 400)

    def test_create_wrong_unit_workflow(self):
        """Test case for invalid unit value"""
        data = self.get_workflow_dict()
        data["workflow"]["repeat_every"] = 12
        data["workflow"]["unit"] = "wrong value"
        data["workflow"]["title"] = "Wrong wf"
        response = self.api.post(all_models.Workflow, data)
        self.assertEqual(response.status_code, 400)

    def test_create_task_group(self):
        """Test create task group over api."""
        wf_data = self.get_workflow_dict()
        wf_data["workflow"]["title"] = "Create_task_group"
        wf_response = self.api.post(all_models.Workflow, wf_data)

        data = self.get_task_group_dict(wf_response.json["workflow"])

        response = self.api.post(all_models.TaskGroup, data)
        self.assertEqual(response.status_code, 201)

    def test_incorrect_wf_id_on_tg_post(self):
        """Tests incorrect id in tg post payload.

    Tests that 400 is raised on tg post if id in
    payload has incorrect type."""
        wf_data = self.get_workflow_dict()
        wf_response = self.api.post(all_models.Workflow, wf_data)
        data = {
            "workflow": {
                "id": {
                    "id": wf_response.json["workflow"]["id"]
                },
                "type": "Workflow"
            }
        }
        tg_response = self.api.post(all_models.TaskGroup, data)
        self.assertEqual(tg_response.status_code, 400)
        self.assertEqual(tg_response.json["message"],
                         ("Either type or id are specified "
                          "incorrectly in the request payload."))

    @staticmethod
    def get_workflow_dict():
        return {
            "workflow": {
                "custom_attribute_definitions": [],
                "custom_attributes": {},
                "title": "One_time",
                "description": "",
                "unit": None,
                "repeat_every": None,
                "notify_on_change": False,
                "task_group_title": "Task Group 1",
                "notify_custom_message": "",
                "is_verification_needed": True,
                "context": None,
            }
        }

    def get_task_group_dict(self, workflow):
        return {
            "task_group": {
                "custom_attribute_definitions": [],
                "custom_attributes": {},
                "_transient": {},
                "contact": {
                    "id": self.wf_admin_id,
                    "href": "/api/people/{}".format(self.wf_admin_id),
                    "type": "Person"
                },
                "workflow": {
                    "id": workflow["id"],
                    "href": "/api/workflows/%d" % workflow["id"],
                    "type": "Workflow"
                },
                "context": {
                    "id": workflow["context"]["id"],
                    "href": "/api/contexts/%d" % workflow["context"]["id"],
                    "type": "Context"
                },
                "modal_title": "Create Task Group",
                "title": "Create_task_group",
                "description": "",
            }
        }

    def get_task_dict(self, task_group):
        return {
            "task_group_task": {
                "start_date": "2017-12-25",
                "end_date": "2017-12-31",
                "custom_attributes": {},
                "contact": {
                    "id": self.wf_admin_id,
                    "href": "/api/people/{}".format(self.wf_admin_id),
                    "type": "Person"
                },
                "task_group": {
                    "id": task_group.id,
                    "href": "/api/task_groups/{}".format(task_group.id),
                    "type": "TaskGroup"
                },
                "context": {
                    "id": task_group.context_id,
                    "href": "/api/contexts/{}".format(task_group.context_id),
                    "type": "Context"
                },
                "title": "Create_task",
                "task_type": "text",
                "description": ""
            }
        }

    @ddt.data({}, {"repeat_every": 5, "unit": "month"})
    def test_repeat_multiplier_field(self, data):
        """Check repeat_multiplier is set to 0 after wf creation."""
        with factories.single_commit():
            workflow = wf_factories.WorkflowFactory(**data)
        workflow_id = workflow.id
        self.assertEqual(
            0,
            all_models.Workflow.query.get(workflow_id).repeat_multiplier)

    # TODO: Unskip in the patch 2
    @unittest.skip("Will be activated in patch 2")
    def test_change_to_one_time_wf(self):
        """Check repeat_every and unit can be set to Null only together."""
        with factories.single_commit():
            workflow = wf_factories.WorkflowFactory(repeat_every=12,
                                                    unit="day")
        resp = self.api.put(workflow, {"repeat_every": None, "unit": None})
        self.assert200(resp)

    @ddt.data({"repeat_every": 5}, {"unit": "month"})
    def test_change_repeat_every(self, data):
        """Check repeat_every or unit can not be changed once set."""
        with factories.single_commit():
            workflow = wf_factories.WorkflowFactory()
        resp = self.api.put(workflow, data)
        self.assert400(resp)

    def test_not_change_to_one_time_wf(self):
        """Check repeat_every or unit can't be set to Null separately.
    This test will be useful in the 2nd patch, where we allow to change
    WF setup
    """
        with factories.single_commit():
            workflow = wf_factories.WorkflowFactory(repeat_every=12,
                                                    unit="day")
        resp = self.api.put(workflow, {"repeat_every": None})
        self.assert400(resp)
        resp = self.api.put(workflow, {"unit": None})
        self.assert400(resp)

    @ddt.data(True, False)
    def test_autogen_verification_flag(self, flag):
        """Check is_verification_needed flag for activate WF action."""
        with factories.single_commit():
            workflow = wf_factories.WorkflowFactory(
                is_verification_needed=flag)
            group = wf_factories.TaskGroupFactory(workflow=workflow)
            wf_factories.TaskGroupTaskFactory(task_group=group)
        data = [{
            "cycle": {
                "autogenerate": True,
                "isOverdue": False,
                "title": factories.random_str(prefix='cycle - '),
                "workflow": {
                    "id": workflow.id,
                    "type": "Workflow",
                },
                "context": {
                    "id": workflow.context_id,
                    "type": "Context",
                },
            }
        }]
        resp = self.api.send_request(self.api.client.post,
                                     api_link="/api/cycles",
                                     data=data)
        cycle_id = resp.json[0][1]["cycle"]["id"]
        self.assertEqual(
            flag,
            all_models.Cycle.query.get(cycle_id).is_verification_needed)

    @ddt.data(True, False)
    def test_verification_flag_positive(self, flag):  # noqa pylint: disable=invalid-name
        """is_verification_needed flag is changeable for DRAFT workflow."""
        with factories.single_commit():
            workflow = wf_factories.WorkflowFactory(
                is_verification_needed=flag)
        self.assertEqual(workflow.status, all_models.Workflow.DRAFT)
        workflow_id = workflow.id
        resp = self.api.put(workflow, {"is_verification_needed": not flag})
        self.assert200(resp)
        self.assertEqual(
            all_models.Workflow.query.get(workflow_id).is_verification_needed,
            not flag)

    @ddt.data(True, False)
    def test_verification_flag_negative(self, flag):
        """Test immutable verification flag on active workflows."""
        with freezegun.freeze_time("2017-08-10"):
            with factories.single_commit():
                workflow = wf_factories.WorkflowFactory(
                    unit=all_models.Workflow.WEEK_UNIT,
                    is_verification_needed=flag,
                    repeat_every=1)
                wf_factories.TaskGroupTaskFactory(
                    task_group=wf_factories.TaskGroupFactory(
                        context=factories.ContextFactory(), workflow=workflow),
                    # Two cycles should be created
                    start_date=datetime.date(2017, 8, 3),
                    end_date=datetime.date(2017, 8, 7))
            workflow_id = workflow.id
            self.assertEqual(workflow.status, all_models.Workflow.DRAFT)
            self.generator.activate_workflow(workflow)
            workflow = all_models.Workflow.query.get(workflow_id)
            self.assertEqual(workflow.status, all_models.Workflow.ACTIVE)
            resp = self.api.put(workflow, {"is_verification_needed": not flag})
            self.assert400(resp)
            workflow = all_models.Workflow.query.get(workflow_id)
            self.assertEqual(workflow.is_verification_needed, flag)

            # End all current cycles
            for cycle in workflow.cycles:
                self.generator.modify_object(cycle, {'is_current': False})
            workflow = all_models.Workflow.query.filter(
                all_models.Workflow.id == workflow_id).first()
            self.assertEqual(workflow.status, all_models.Workflow.INACTIVE)
            resp = self.api.put(workflow, {"is_verification_needed": not flag})
            self.assert400(resp)
            workflow = all_models.Workflow.query.get(workflow_id)
            self.assertEqual(workflow.is_verification_needed, flag)

    @ddt.data(True, False)
    def test_not_change_vf_flag(self, flag):
        """Check is_verification_needed not change on update."""
        with factories.single_commit():
            workflow = wf_factories.WorkflowFactory(
                is_verification_needed=flag)
        workflow_id = workflow.id
        resp = self.api.put(workflow, {"is_verification_needed": flag})
        self.assert200(resp)
        self.assertEqual(
            flag,
            all_models.Workflow.query.get(workflow_id).is_verification_needed)

    @ddt.data(True, False, None)
    def test_create_vf_flag(self, flag):
        """Check is_verification_needed flag setup on create."""
        data = self.get_workflow_dict()
        if flag is None:
            data['workflow'].pop('is_verification_needed', None)
        else:
            data['workflow']['is_verification_needed'] = flag
        resp = self.api.post(all_models.Workflow, data)
        self.assertEqual(201, resp.status_code)
        workflow_id = resp.json['workflow']['id']
        self.assertEqual(
            flag if flag is not None else False,
            all_models.Workflow.query.get(workflow_id).is_verification_needed)