def test_ext_app_recreate_normal_relationship(self):
        """If ext app create already created relationship
    it has to be with is_external=False"""
        with factories.single_commit():
            market1 = factories.MarketFactory()
            market2 = factories.MarketFactory()
            relationship = factories.RelationshipFactory(
                source=market1,
                destination=market2,
                is_external=False,
            )
            relationship_id = relationship.id
        ext_api = ExternalApiClient(use_ggrcq_service_account=True)

        response = ext_api.post(all_models.Relationship,
                                data={
                                    "relationship": {
                                        "source": {
                                            "id": market1.id,
                                            "type": market1.type
                                        },
                                        "destination": {
                                            "id": market2.id,
                                            "type": market2.type
                                        },
                                        "is_external": True,
                                        "context": None,
                                    },
                                })

        self.assert200(response)
        relationship = all_models.Relationship.query.get(relationship_id)
        self.assertFalse(relationship.is_external)
    def test_ext_app_delete_normal_relationship(self):
        """External app can't delete normal relationships"""

        with factories.single_commit():
            issue = factories.IssueFactory()
            objective = factories.ObjectiveFactory()

            relationship = factories.RelationshipFactory(source=issue,
                                                         destination=objective,
                                                         is_external=False)
            relationship_id = relationship.id
        ext_api = ExternalApiClient(use_ggrcq_service_account=True)
        resp = ext_api.delete("relationship", relationship_id)
        self.assertStatus(resp, 400)
Beispiel #3
0
class TestRiskSnapshotting(TestCase):
  """Risk snapshot tests"""
  def setUp(self):
    """setUp, nothing else to add."""
    super(TestRiskSnapshotting, self).setUp()
    self.api = ExternalApiClient(use_ggrcq_service_account=True)
    self.objgen = ObjectGenerator()

  def test_update_risk_snapshot(self):
    """Update risk snapshot to the latest version"""
    with factories.single_commit():
      program = factories.ProgramFactory(title="P1")
      risk = factories.RiskFactory(title="R1")
      risk_id = risk.id
      factories.RelationshipFactory(source=program, destination=risk)
    # Risk snapshot created for audit during mapping audit to program
    self.objgen.generate_object(all_models.Audit, {
        "title": "A1",
        "program": {"id": program.id},
        "status": "Planned",
        "snapshots": {
            "operation": "create",
        },
    })
    # Update risk to get outdated snapshot (new risk revision)
    risk = all_models.Risk.query.get(risk_id)
    self.api.put(risk, risk.id, {
        "title": "New risk title",
    })
    audit = all_models.Audit.query.filter_by(title="A1").one()
    snapshot = all_models.Snapshot.query.first()
    self.assertEquals(audit, snapshot.parent)

    # Update snapshot to the latest revision
    response = self.api.put(snapshot, snapshot.id, {
        "update_revision": "latest",
    })

    self.assert200(response)
    self.assertTrue(response.json["snapshot"]["is_latest_revision"])
class TestExternalRelationshipNew(TestCase):
    """Integration test suite for External Relationship."""

    # pylint: disable=invalid-name

    def setUp(self):
        """Init API helper"""
        super(TestExternalRelationshipNew, self).setUp()
        self.ext_api = ExternalApiClient()

    def test_ext_app_delete_normal_relationship(self):
        """External app can't delete normal relationships"""

        with factories.single_commit():
            issue = factories.IssueFactory()
            objective = factories.ObjectiveFactory()

            relationship = factories.RelationshipFactory(source=issue,
                                                         destination=objective,
                                                         is_external=False)
            relationship_id = relationship.id
        ext_api = ExternalApiClient(use_ggrcq_service_account=True)
        resp = ext_api.delete("relationship", relationship_id)
        self.assertStatus(resp, 400)

    def test_sync_service_delete_normal_relationship(self):
        """Sync service can delete normal relationships via unmap endpoint"""

        with factories.single_commit():
            issue = factories.IssueFactory()
            objective = factories.ObjectiveFactory()

            relationship = factories.RelationshipFactory(source=issue,
                                                         destination=objective,
                                                         is_external=False)
            relationship_id = relationship.id
        resp = self.ext_api.unmap(issue, objective)
        self.assert200(resp)
        rel = all_models.Relationship.query.get(relationship_id)
        self.assertIsNone(rel)
Beispiel #5
0
 def setUp(self):
   """setUp, nothing else to add."""
   super(TestRiskSnapshotting, self).setUp()
   self.api = ExternalApiClient(use_ggrcq_service_account=True)
   self.objgen = ObjectGenerator()
Beispiel #6
0
 def setUp(self):
   """setUp, nothing else to add."""
   super(TestSyncServiceRisk, self).setUp()
   self.api = ExternalApiClient()
Beispiel #7
0
class TestSyncServiceRisk(TestCase):
  """Tests for risk model for GGRCQ users."""

  def setUp(self):
    """setUp, nothing else to add."""
    super(TestSyncServiceRisk, self).setUp()
    self.api = ExternalApiClient()

  @staticmethod
  def generate_risk_body():
    """Generate JSON body for Risk."""
    test_date = datetime.datetime.utcnow().strftime("%Y-%m-%d")
    body = {
        "id": 10,
        "title": "External risk",
        "risk_type": "External risk",
        "created_at": test_date,
        "updated_at": test_date,
        "external_id": 10,
        "external_slug": "external_slug",
        "review_status": all_models.Review.STATES.UNREVIEWED,
        "review_status_display_name": "some status",
    }

    return body

  @staticmethod
  def generate_comment_body():
    """Generate JSON body for Risk comment."""
    body = {
        "external_id": 1,
        "external_slug": factories.random_str(),
        "description": "External comment",
        "context": None,
    }

    return body

  def assert_instance(self, expected, risk):
    """Compare expected response body with actual."""
    risk_values = {}
    expected_values = {}

    for field, value in expected.items():
      expected_values[field] = value
      attr = getattr(risk, field, None)
      if isinstance(attr, datetime.datetime):
        # this is datetime object
        attr = attr.strftime("%Y-%m-%d")
      risk_values[field] = attr

    self.assertEqual(expected_values, risk_values)

  def test_create_risk(self):
    """Test risk create with external user."""
    risk_body = self.generate_risk_body()

    response = self.api.post(
        all_models.Risk,
        data={"risk": risk_body},
    )

    self.assert201(response)
    risk = all_models.Risk.query.get(risk_body["id"])
    self.assert_instance(risk_body, risk)

  @ddt.data('review_status',
            'review_status_display_name')
  # pylint: disable=invalid-name
  def test_create_risk_without_field(self, field):
    """Check risk creation without review_status"""
    risk_body = self.generate_risk_body()
    del risk_body[field]

    response = self.api.post(all_models.Risk, data=risk_body)

    self.assert400(response)

  @ddt.data('review_status',
            'review_status_display_name')
  # pylint: disable=invalid-name
  def test_create_risk_with_empty_field(self, field):
    """Check risk creation with empty review_status"""
    risk_body = self.generate_risk_body()
    risk_body[field] = None

    response = self.api.post(all_models.Risk, data=risk_body)

    self.assert400(response)

  def test_update_risk(self):
    """Test risk update with external user."""
    test_date = datetime.datetime.utcnow().strftime("%Y-%m-%d")
    with factories.single_commit():
      risk_id = factories.RiskFactory().id
    created_at = test_date
    updated_at = test_date
    new_values = {
        "title": "New risk",
        "created_at": created_at,
        "updated_at": updated_at,
        "review_status": all_models.Review.STATES.UNREVIEWED,
        "review_status_display_name": "some status",
    }
    risk = all_models.Risk.query.get(risk_id)

    response = self.api.put(risk, risk.id, new_values)

    self.assert200(response)
    risk = all_models.Risk.query.get(risk_id)
    self.assert_instance(new_values, risk)

  @ddt.data(
      ("review_status", "Review status"),
      ("review_status_display_name", "Review status display")
  )
  @ddt.unpack
  # pylint: disable=invalid-name
  def test_update_risk_field_to_null(self, field, field_name):
    """Test review_status is not set to None"""
    risk = factories.RiskFactory()

    response = self.api.put(risk, risk.id, {
        field: None,
    })

    self.assert400(response)
    self.assertEqual(response.json["message"],
                     field_name + " for the object is not specified")
    risk = db.session.query(all_models.Risk).get(risk.id)
    self.assertIsNotNone(risk.external_id)

  # pylint: disable=invalid-name
  def test_update_review_status(self):
    """Test review_status is updated"""
    risk = factories.RiskFactory()
    new_value = all_models.Review.STATES.REVIEWED

    self.api.put(risk, risk.id, {
        "review_status": new_value,
        "review_status_display_name": "some status"
    })

    risk = db.session.query(all_models.Risk).get(risk.id)
    self.assertEquals(risk.review_status, new_value)

  # pylint: disable=invalid-name
  def test_update_review_status_display_name(self):
    """Test review_status_display_name is updated"""
    risk = factories.RiskFactory()
    new_value = "test123"

    self.api.put(risk, risk.id, {
        "review_status_display_name": new_value,
        "review_status": all_models.Review.STATES.UNREVIEWED
    })

    risk = db.session.query(all_models.Risk).get(risk.id)
    self.assertEquals(risk.review_status_display_name, new_value)

  def test_create_risk_comments(self):
    """Test external comments creation for risk."""
    risk_body = self.generate_risk_body()
    response = self.api.post(all_models.Risk, data={
        "risk": risk_body,
    })
    self.assert201(response)
    comment_body = self.generate_comment_body()

    response_ext_comment = self.api.post(all_models.ExternalComment, data={
        "external_comment": comment_body,
    })

    self.assert201(response_ext_comment)
    comment = db.session.query(all_models.ExternalComment.description).one()
    self.assertEqual(comment, (comment_body["description"],))
    risk_id = db.session.query(all_models.Risk.id).one()[0]
    comment_id = db.session.query(all_models.ExternalComment.id).one()[0]

    response_relationship = self.api.post(all_models.Relationship, data={
        "relationship": {
            "source": {"id": risk_id, "type": "Risk"},
            "destination": {"id": comment_id, "type": "ExternalComment"},
            "context": None,
            "is_external": True
        },
    })

    self.assert201(response_relationship)
    rels = all_models.Relationship.query.filter_by(
        source_type="Risk",
        source_id=risk_id,
        destination_type="ExternalComment",
        destination_id=comment_id
    )
    self.assertEqual(rels.count(), 1)

  def test_get_risk_external_comment(self):
    """Test query endpoint for risk ExternalComments."""
    with factories.single_commit():
      risk = factories.RiskFactory()
      comment = factories.ExternalCommentFactory(description="comment")
      factories.RelationshipFactory(source=risk, destination=comment)
    request_data = [{
        "filters": {
            "expression": {
                "object_name": "Risk",
                "op": {
                    "name": "relevant"
                },
                "ids": [risk.id]
            },
        },
        "object_name":"ExternalComment",
        "order_by": [{"name": "created_at", "desc": "true"}],
    }]

    response = self.api.post(
        all_models.Risk,
        data=request_data,
        url="/query",
    )

    self.assert200(response)
    response_data = response.json[0]["ExternalComment"]
    self.assertEqual(response_data["count"], 1)
    self.assertEqual(response_data["values"][0]["description"], "comment")

  @ddt.data(
      ("Due Date", "due_date"),
      ("Last Owner Reviewed Date", "last_submitted_at"),
      ("Last Compliance Reviewed Date", "last_verified_at"),
  )
  @ddt.unpack
  def test_search_risk_by_dates(self, field, attr):
    """Test query endpoint for risk by dates."""
    current_date = datetime.date.today()
    with factories.single_commit():
      factories.RiskFactory(**{attr: current_date})
    request_data = [{
        "filters": {
            "expression": {
                "left": {"left": field,
                         "op": {"name": "~"},
                         "right": current_date.strftime("%Y-%m-%d")},
                "op": {"name": "AND"},
                "right": {"left": "Status",
                          "op": {"name": "IN"},
                          "right": ["Active", "Draft", "Deprecated"]}
            }
        },
        "object_name": "Risk",
        "order_by": [{"name": "updated_at", "desc": "true"}],
    }]

    response = self.api.post(
        all_models.Risk,
        data=request_data,
        url="/query",
    )

    self.assert200(response)
    response_data = response.json[0]["Risk"]
    self.assertEqual(response_data["count"], 1)
    self.assertEqual(response_data["values"][0][attr],
                     current_date.strftime("%Y-%m-%d"))

  @ddt.data(
      ("Created By", "created_by"),
      ("Last Owner Reviewed By", "last_submitted_by"),
      ("Last Compliance Reviewed By", "last_verified_by"),
  )
  @ddt.unpack
  def test_search_risk_by_users(self, field, attr):
    """Test query endpoint for risk by users."""
    with factories.single_commit():
      person = factories.PersonFactory()
      factories.RiskFactory(**{attr: person})
    request_data = [{
        "filters": {
            "expression": {
                "left": {"left": field,
                         "op": {"name": "~"},
                         "right": person.email},
                "op": {"name": "AND"},
                "right": {"left": "Status",
                          "op": {"name": "IN"},
                          "right": ["Active", "Draft", "Deprecated"]}
            }
        },
        "object_name": "Risk",
        "order_by": [{"name": "updated_at", "desc": "true"}],
    }]

    response = self.api.post(
        all_models.Risk,
        data=request_data,
        url="/query",
    )

    self.assert200(response)
    response_data = response.json[0]["Risk"]
    self.assertEqual(response_data["count"], 1)
    self.assertEqual(response_data["values"][0][attr]['email'],
                     person.email)
class TestSyncServiceControl(TestCase):
    """Tests for control model using sync service as external user."""

    # pylint: disable=too-many-public-methods

    def setUp(self):
        """setUp, nothing else to add."""
        super(TestSyncServiceControl, self).setUp()
        self.api = ExternalApiClient()

    @staticmethod
    def prepare_control_request_body():
        """Create payload for control creation."""
        test_date = datetime.datetime.utcnow().strftime("%Y-%m-%d")
        return {
            "id": 123,
            "title": "new_control",
            "context": None,
            "created_at": test_date,
            "updated_at": test_date,
            "slug": "CONTROL-01",
            "external_id": factories.SynchronizableExternalId.next(),
            "external_slug": factories.random_str(),
            "kind": "test kind",
            "means": "test means",
            "verify_frequency": "test frequency",
            "assertions": '["test assertion"]',
            "categories": '["test category"]',
            "review_status": all_models.Review.STATES.UNREVIEWED,
            "review_status_display_name": "some status",
            "due_date": test_date,
            "created_by": {
                "email": "*****@*****.**",
                "name": "External Creator",
            },
            "last_submitted_at": test_date,
            "last_submitted_by": {
                "email": "*****@*****.**",
                "name": "External Owner",
            },
            "last_verified_at": test_date,
            "last_verified_by": {
                "email": "*****@*****.**",
                "name": "External Compliance",
            },
        }

    @staticmethod
    def generate_minimal_control_body():
        """Generate minimal control body"""
        return {
            "title": factories.random_str(),
            "external_id": factories.SynchronizableExternalId.next(),
            "external_slug": factories.random_str(),
            "context": None,
            "review_status": all_models.Review.STATES.UNREVIEWED,
            "review_status_display_name": "some status",
        }

    @staticmethod
    def setup_people(access_control_list):
        """Create Person objects specified in access_control_list."""
        all_users = set()
        for users in access_control_list.values():
            all_users.update({(user["email"], user["name"]) for user in users})

        with factories.single_commit():
            for email, name in all_users:
                factories.PersonFactory(email=email, name=name)

    def assert_obj_acl(self, obj, access_control_list):
        """Validate correctness of object access_control_list.

    Args:
        obj: Object for which acl should be checked.
        access_control_list: Dict of format
          {<role name>:[{"name": <user name>, "email": <user email>}.
    """
        actual_acl = {(user.acl_item.ac_role.name, user.person.email)
                      for user in obj.access_control_list}
        expected_acl = {(role, person["email"])
                        for role, people in access_control_list.items()
                        for person in people}
        self.assertEqual(actual_acl, expected_acl)

    @mock.patch("ggrc.settings.INTEGRATION_SERVICE_URL", "mock")
    def test_control_create(self):
        """Test control creation using sync service."""
        control_body = self.prepare_control_request_body()
        app_user_email = "*****@*****.**"
        ext_owner_email = "*****@*****.**"
        ext_compliance_email = "*****@*****.**"

        response = self.api.post(all_models.Control,
                                 data={
                                     "control": control_body,
                                 })

        self.assertEqual(response.status_code, 201)
        id_ = response.json.get("control").get("id")
        self.assertEqual(control_body["id"], id_)
        control = db.session.query(all_models.Control).get(id_)
        app_user = db.session.query(all_models.Person).filter(
            all_models.Person.email == app_user_email).one()
        ext_owner_user = db.session.query(all_models.Person).filter(
            all_models.Person.email == ext_owner_email).one()
        ext_compliance_user = db.session.query(all_models.Person).filter(
            all_models.Person.email == ext_compliance_email).one()
        self.assertEqual(control.modified_by_id, app_user.id)
        self.assertEqual(control.last_submitted_by_id, ext_owner_user.id)
        self.assertEqual(control.last_verified_by_id, ext_compliance_user.id)
        expected_assertions = control_body.pop("assertions")
        expected_categories = control_body.pop("categories")
        self.assertEqual(response.json["control"].get("assertions"),
                         json.loads(expected_assertions))
        self.assertEqual(response.json["control"].get("categories"),
                         json.loads(expected_categories))
        self.assert_response_fields(response.json.get("control"), control_body)
        self.assert_object_fields(control, control_body)
        self.assertEqual(control.assertions, expected_assertions)
        self.assertEqual(control.categories, expected_categories)
        revision = db.session.query(all_models.Revision).filter(
            all_models.Revision.resource_type == "Control",
            all_models.Revision.resource_id == control.id,
            all_models.Revision.action == "created",
            all_models.Revision.created_at == control.updated_at,
            all_models.Revision.updated_at == control.updated_at,
            all_models.Revision.modified_by_id == control.modified_by_id,
        ).one()
        self.assertIsNotNone(revision)

    @mock.patch("ggrc.settings.INTEGRATION_SERVICE_URL", "mock")
    def test_control_update(self):
        """Test control update using sync service."""
        test_date = datetime.datetime.utcnow().strftime("%Y-%m-%d")
        _, email = parseaddr(settings.SYNC_SERVICE_USER)
        person = factories.PersonFactory(email=email)
        control = factories.ControlFactory(modified_by=person)
        response = self.api.get(control, control.id)
        response.json["control"].pop("selfLink")
        response.json["control"].pop("viewLink")
        control_body = response.json["control"]
        control_body.update({
            "title": "updated_title",
            "created_at": test_date,
            "updated_at": test_date,
            "kind": "test kind",
            "means": "test means",
            "verify_frequency": "test frequency",
            "assertions": '["test assertions"]',
            "categories": '["test categories"]',
        })

        response = self.api.put(
            control,
            control.id,
            data=response.json,
        )

        expected_assertions = control_body.pop("assertions")
        expected_categories = control_body.pop("categories")
        self.assertEqual(
            response.json["control"].get("assertions"),
            json.loads(expected_assertions),
        )
        self.assertEqual(
            response.json["control"].get("categories"),
            json.loads(expected_categories),
        )
        self.assert_response_fields(response.json["control"], control_body)
        control = all_models.Control.query.get(control.id)
        self.assert_object_fields(control, control_body)
        self.assertEqual(control.assertions, expected_assertions)
        self.assertEqual(control.categories, expected_categories)
        revision = db.session.query(all_models.Revision).filter(
            all_models.Revision.resource_type == "Control",
            all_models.Revision.resource_id == control.id,
            all_models.Revision.action == "modified",
            all_models.Revision.created_at == control.updated_at,
            all_models.Revision.updated_at == control.updated_at,
            all_models.Revision.modified_by_id == control.modified_by_id,
        ).one()
        self.assertIsNotNone(revision)

    def test_create_with_asserts(self):
        """Check control creation with assertions pass"""
        control_body = self.prepare_control_request_body()

        response = self.api.post(all_models.Control,
                                 data={
                                     "control": control_body,
                                 })

        self.assertEqual(response.status_code, 201)
        control = all_models.Control.query.first()
        self.assertIsNotNone(control)
        self.assertEqual('["test assertion"]', control.assertions)

    def test_set_control_end_date(self):
        """End_date can't to be updated."""
        control = factories.ControlFactory()

        self.api.put(control, control.id, {"end_date": "2015-10-10"})

        control = db.session.query(all_models.Control).get(control.id)
        self.assertIsNone(control.end_date)

    def test_set_deprecated_status(self):
        """Deprecated status setup end_date."""
        control = factories.ControlFactory()
        self.assertIsNone(control.end_date)

        self.api.put(control, control.id, {
            "status": all_models.Control.DEPRECATED,
        })

        control = db.session.query(all_models.Control).get(control.id)
        self.assertIsNotNone(control.end_date)

    def test_create_control_comments(self):
        """Test external comments creation for control."""
        control_body = self.prepare_control_request_body()
        response = self.api.post(all_models.Control,
                                 data={
                                     "control": control_body,
                                 })
        self.assertEqual(response.status_code, 201)

        response_ext_comment = self.api.post(all_models.ExternalComment,
                                             data={
                                                 "external_comment": {
                                                     "id":
                                                     1,
                                                     "external_id":
                                                     1,
                                                     "external_slug":
                                                     factories.random_str(),
                                                     "description":
                                                     "test comment",
                                                     "context":
                                                     None,
                                                 },
                                             })
        self.assertEqual(response_ext_comment.status_code, 201)
        response_relationship = self.api.post(all_models.Relationship,
                                              data={
                                                  "relationship": {
                                                      "source": {
                                                          "id": 123,
                                                          "type": "Control"
                                                      },
                                                      "destination": {
                                                          "id": 1,
                                                          "type":
                                                          "ExternalComment"
                                                      },
                                                      "context": None,
                                                      "is_external": True,
                                                  },
                                              })
        self.assertEqual(response_relationship.status_code, 201)

        comments = db.session.query(
            all_models.ExternalComment.description).all()
        self.assertEqual(comments, [("test comment", )])
        rels = all_models.Relationship.query.filter_by(
            source_type="Control",
            source_id=123,
            destination_type="ExternalComment",
            destination_id=1)
        self.assertEqual(rels.count(), 1)

    def test_query_external_comment(self):
        """Test query endpoint for ExternalComments collection."""
        with factories.single_commit():
            control = factories.ControlFactory()
            comment = factories.ExternalCommentFactory(
                description="test comment")
            factories.RelationshipFactory(source=control, destination=comment)
        request_data = [{
            "filters": {
                "expression": {
                    "object_name": "Control",
                    "op": {
                        "name": "relevant"
                    },
                    "ids": [control.id]
                },
            },
            "object_name": "ExternalComment",
            "order_by": [{
                "name": "created_at",
                "desc": "true"
            }],
        }]

        response = self.api.post(comment, data=request_data, url="/query")

        self.assert200(response)
        response_data = response.json[0]["ExternalComment"]
        self.assertEqual(response_data["count"], 1)
        self.assertEqual(response_data["values"][0]["description"],
                         "test comment")

    @ddt.data("created_at", "description")
    def test_external_comments_order(self, order_by_attr):
        """Test order of ExternalComments returned by /query."""
        with factories.single_commit():
            control = factories.ControlFactory()
            for _ in range(5):
                comment = factories.ExternalCommentFactory(
                    description=factories.random_str())
                factories.RelationshipFactory(source=control,
                                              destination=comment)
        request_data = [{
            "filters": {
                "expression": {
                    "object_name": "Control",
                    "op": {
                        "name": "relevant"
                    },
                    "ids": [control.id]
                },
            },
            "object_name": "ExternalComment",
            "order_by": [{
                "name": order_by_attr,
                "desc": "true"
            }],
        }]

        response = self.api.post(comment, data=request_data, url="/query")

        self.assert200(response)
        response_data = response.json[0]["ExternalComment"]
        comments = [val["description"] for val in response_data["values"]]
        expected_comments = db.session.query(
            all_models.ExternalComment.description).order_by(
                getattr(all_models.ExternalComment, order_by_attr).desc(),
                all_models.ExternalComment.id.desc(),
            )
        self.assertEqual(comments, [i[0] for i in expected_comments])

    def test_create_unique_external_id(self):
        """Check control creation with non-unique external_id"""
        request1 = self.prepare_control_request_body()
        request2 = self.prepare_control_request_body()

        response1 = self.api.post(all_models.Control,
                                  data={'control': request1})
        prev_external_id = response1.json['control']['external_id']
        request2['external_id'] = prev_external_id
        response2 = self.api.post(all_models.Control,
                                  data={'control': request2})

        self.assert400(response2)

    @ddt.data(
        "external_id",
        "review_status",
        "review_status_display_name",
    )
    def test_create_without_field(self, field):
        """Check control creation without 'field'"""
        request = self.prepare_control_request_body()
        del request[field]

        response = self.api.post(all_models.Control, data=request)

        self.assert400(response)

    @ddt.data(
        "external_id",
        "review_status",
        "review_status_display_name",
    )
    # pylint: disable=invalid-name
    def test_control_create_with_empty_field(self, field):
        """Check control creation with empty 'field'"""
        request = self.prepare_control_request_body()
        request[field] = None

        response = self.api.post(all_models.Control, data=request)

        self.assert400(response)

    @ddt.data(("external_id", factories.SynchronizableExternalId.next()),
              ("review_status", all_models.Review.STATES.REVIEWED),
              ("review_status_display_name", "value12345"))
    @ddt.unpack
    def test_control_update_field(self, field, new_value):
        """Test field is updated"""
        control = factories.ControlFactory()

        self.api.put(control, control.id, {field: new_value})

        control = db.session.query(all_models.Control).get(control.id)
        self.assertEquals(getattr(control, field), new_value)

    @ddt.data(
        ("external_id", "External ID"),
        ("review_status", "review_status"),
        ("review_status_display_name", "review_status_display_name"),
    )
    @ddt.unpack
    def test_update_field_to_null(self, field, field_name):
        """Test external_id is not set to None"""
        control = factories.ControlFactory()

        response = self.api.put(control, control.id, {field: None})

        self.assert400(response)
        self.assertEqual(response.json["message"],
                         field_name + " for the object is not specified")
        control = db.session.query(all_models.Control).get(control.id)
        self.assertIsNotNone(control.external_id)

    @ddt.data(
        ("kind", ["1", "2", "3"], "2"),
        ("means", ["1", "1", "1"], "1"),
        ("verify_frequency", ["3", "2", "3"], "3"),
    )
    @ddt.unpack
    def test_control_query_string(self, field, possible_values, search_value):
        """Test querying '{0}' field for control."""
        with factories.single_commit():
            for val in possible_values:
                factories.ControlFactory(**{field: val})
        request_data = [{
            'fields': [],
            'filters': {
                'expression': {
                    'left': field,
                    'op': {
                        'name': '='
                    },
                    'right': search_value,
                },
            },
            'object_name': 'Control',
            'type': 'values',
        }]

        response = self.api.post(all_models.Control,
                                 data=request_data,
                                 url="/query")

        self.assert200(response)
        response_data = response.json[0]["Control"]
        expected_controls = all_models.Control.query.filter_by(
            **{field: search_value})
        self.assertEqual(expected_controls.count(), response_data.get("count"))
        expected_values = [getattr(i, field) for i in expected_controls]
        actual_values = [val.get(field) for val in response_data.get("values")]
        self.assertEqual(expected_values, actual_values)

    @ddt.data(
        ("assertions", ['["a", "b", "c"]', '["1", "2", "3"]'], "c"),
        ("categories", ['["a"]', '["1", "2", "3"]'], "1"),
        ("assertions", ['["a", "b"]', '["1", "2"]'], "3"),
    )
    @ddt.unpack
    def test_control_query_json(self, field, possible_values, search_value):
        """Test querying '{0}' field for control."""
        with factories.single_commit():
            for val in possible_values:
                factories.ControlFactory(**{field: val})
        request_data = [{
            "fields": [],
            "object_name": "Control",
            "type": "values",
            "filters": {
                "expression": {
                    "left": field,
                    "op": {
                        "name": "="
                    },
                    "right": search_value,
                },
            },
        }]

        response = self.api.post(all_models.Control,
                                 data=request_data,
                                 url="/query")

        self.assert200(response)
        response_data = response.json[0]["Control"]
        model_field = getattr(all_models.Control, field)
        expected_controls = all_models.Control.query.filter(
            model_field.like("%{}%".format(search_value)))
        self.assertEqual(expected_controls.count(), response_data.get("count"))
        expected_values = [
            json.loads(getattr(i, field)) for i in expected_controls
        ]
        actual_values = [val.get(field) for val in response_data.get("values")]
        self.assertEqual(expected_values, actual_values)

    @mock.patch("ggrc.settings.INTEGRATION_SERVICE_URL", "mock")
    @ddt.data(
        {
            "Admin": {
                "email": "*****@*****.**",
                "name": "user1",
            },
        },
        {
            "Admin": "*****@*****.**",
        },
    )
    def test_control_invalid_acl_format(self, access_control_list):
        """Test creation of new object with acl in invalid format."""
        control_body = self.prepare_control_request_body()
        control_body.update({
            "access_control_list": access_control_list,
        })

        response = self.api.post(all_models.Control,
                                 data={"control": control_body})

        self.assert400(response)
        expected_err = synchronizable.RoleableSynchronizable.INVALID_ACL_ERROR
        self.assertEqual(response.json, expected_err)
        control = all_models.Control.query.filter_by(id=123)
        self.assertEqual(control.count(), 0)

    def test_control_with_tg_update(self):
        """Test updating of Control mapped to TaskGroup."""
        with factories.single_commit():
            control = factories.ControlFactory()
            task_group = wf_factories.TaskGroupFactory()
            factories.RelationshipFactory(source=task_group,
                                          destination=control)

        response = self.api.put(control, control.id, {
            "title": "new title",
            "task_groups": [],
        })

        self.assert200(response)
        control = all_models.Control.query.get(control.id)
        self.assertEqual(control.title, "new title")
        tg_ids = [id_[0] for id_ in db.session.query(all_models.TaskGroup.id)]
        self.assertEqual(len(tg_ids), 1)
        self.assertEqual([tg.source_id for tg in control.related_sources],
                         tg_ids)
        tg_mapped_obj_ids = [
            id_[0] for id_ in db.session.query(
                all_models.Relationship.destination_id).filter(
                    all_models.Relationship.source_type == 'TaskGroup',
                    all_models.Relationship.source_id.in_(tg_ids),
                )
        ]
        self.assertEqual(len(tg_mapped_obj_ids), 1)

    # pylint: disable=invalid-name
    def test_control_with_duplicated_title(self):
        """Test control with duplicated title."""
        control_1 = self.generate_minimal_control_body()
        control_2 = self.generate_minimal_control_body()
        control_2["title"] = control_1["title"]

        response = self.api.post(all_models.Control,
                                 data={"control": control_1})
        self.assert201(response)
        response = self.api.post(all_models.Control,
                                 data={"control": control_2})
        self.assert201(response)

    def test_external_comment_acl(self):
        """Test automatic assigning current user to ExternalComment Admin."""
        response = self.api.post(all_models.ExternalComment,
                                 data={
                                     "external_comment": {
                                         "id": 1,
                                         "external_id": 1,
                                         "external_slug":
                                         factories.random_str(),
                                         "description": "test comment",
                                         "context": None,
                                         "access_control_list": {
                                             "Admin": [
                                                 {
                                                     "email":
                                                     "*****@*****.**",
                                                     "name": "user1",
                                                 },
                                             ],
                                         },
                                     }
                                 })

        self.assert201(response)
        comment = all_models.ExternalComment.query.get(1)
        comment_admin = comment.get_persons_for_rolename("Admin")
        self.assertEqual([i.email for i in comment_admin],
                         ["*****@*****.**"])

    @mock.patch("ggrc.settings.INTEGRATION_SERVICE_URL", "mock")
    @ddt.data(
        {
            "Admin": [
                {
                    "email": "*****@*****.**",
                    "name": "user1",
                },
            ],
        }, {
            "Admin": [
                {
                    "email": "*****@*****.**",
                    "name": "user1",
                },
                {
                    "email": "*****@*****.**",
                    "name": "user2",
                },
            ],
            "Principal Assignees": [
                {
                    "email": "*****@*****.**",
                    "name": "user2",
                },
                {
                    "email": "*****@*****.**",
                    "name": "user3",
                },
            ]
        }, {})
    def test_control_acl_create(self, access_control_list):
        """Test creation of control with non empty acl."""
        control_body = self.prepare_control_request_body()
        control_body.update({
            "access_control_list": access_control_list,
        })
        self.setup_people(access_control_list)

        response = self.api.post(all_models.Control,
                                 data={"control": control_body})

        self.assert201(response)
        control = all_models.Control.query.get(123)
        self.assert_obj_acl(control, access_control_list)

    def test_control_acl_new_people_create(self):
        """Test creation of control with acl which contain new people."""
        control_body = self.prepare_control_request_body()
        access_control_list = {
            "Admin": [
                {
                    "email": "*****@*****.**",
                    "name": "user1",
                },
                {
                    "email": "*****@*****.**",
                    "name": "user2",
                },
            ]
        }
        control_body.update({
            "access_control_list": access_control_list,
        })

        response = self.api.post(all_models.Control,
                                 data={"control": control_body})

        self.assert201(response)
        for expected_person in access_control_list["Admin"]:
            user = all_models.Person.query.filter_by(
                email=expected_person["email"]).one()
            self.assertEqual(user.name, expected_person["name"])
            self.assertEqual([ur.role.name for ur in user.user_roles],
                             ["Creator"])
        control = all_models.Control.query.get(123)
        self.assert_obj_acl(control, access_control_list)

    def test_control_acl_update(self):
        """Test updating of control with non empty acl."""
        with factories.single_commit():
            control = factories.ControlFactory()
            person = factories.PersonFactory()
            control.add_person_with_role_name(person, "Admin")
        access_control_list = {
            "Admin": [
                {
                    "email": "*****@*****.**",
                    "name": "user1",
                },
                {
                    "email": "*****@*****.**",
                    "name": "user2",
                },
            ]
        }
        self.setup_people(access_control_list)

        response = self.api.put(control, control.id, {
            "access_control_list": access_control_list,
        })

        self.assert200(response)
        control = all_models.Control.query.get(control.id)
        self.assert_obj_acl(control, access_control_list)

    def test_control_acl_new_people_update(self):
        """Test updating of control with acl which contain new people."""
        person = factories.PersonFactory()
        add_person_global_role(person, 'Creator')
        with factories.single_commit():
            control = factories.ControlFactory()
            control.add_person_with_role_name(person, "Admin")
        access_control_list = {
            "Admin": [{
                "email": person.email,
                "name": person.name,
            }],
            "Principal Assignees": [
                {
                    "email": person.email,
                    "name": person.name,
                },
                {
                    "email": "*****@*****.**",
                    "name": "user2",
                },
                {
                    "email": "*****@*****.**",
                    "name": "user3",
                },
            ]
        }

        response = self.api.put(control, control.id, {
            "access_control_list": access_control_list,
        })

        self.assert200(response)
        for expected_person in access_control_list["Admin"]:
            user = all_models.Person.query.filter_by(
                email=expected_person["email"]).one()
            self.assertEqual(user.name, expected_person["name"])
            self.assertEqual([ur.role.name for ur in user.user_roles],
                             ["Creator"])
        control = all_models.Control.query.get(control.id)
        self.assert_obj_acl(control, access_control_list)

    def test_wrong_role_controle_acl_update(self):
        """Test updating of control with non empty acl."""
        with factories.single_commit():
            control = factories.ControlFactory()
            person = factories.PersonFactory(name="user1",
                                             email="*****@*****.**")
            control.add_person_with_role_name(person, "Admin")
        access_control_list = {
            "Non-existing role": [
                {
                    "email": "*****@*****.**",
                    "name": "user2",
                },
            ]
        }

        response = self.api.put(control, control.id, {
            "access_control_list": access_control_list,
        })

        self.assert400(response)
        self.assertEqual(response.json["message"],
                         "Role 'Non-existing role' does not exist")
        control = all_models.Control.query.get(control.id)
        self.assert_obj_acl(
            control,
            {"Admin": [{
                "name": "user1",
                "email": "*****@*****.**"
            }]})
 def setUp(self):
     """Init API helper"""
     super(TestExternalRelationshipNew, self).setUp()
     self.ext_api = ExternalApiClient()
class TestExternalRelationshipNew(TestCase):
    """Integration test suite for External Relationship."""

    # pylint: disable=invalid-name

    def setUp(self):
        """Init API helper"""
        super(TestExternalRelationshipNew, self).setUp()
        self.ext_api = ExternalApiClient()

    def test_ext_app_delete_normal_relationship(self):
        """External app can't delete normal relationships"""

        with factories.single_commit():
            issue = factories.IssueFactory()
            objective = factories.ObjectiveFactory()

            relationship = factories.RelationshipFactory(source=issue,
                                                         destination=objective,
                                                         is_external=False)
            relationship_id = relationship.id
        ext_api = ExternalApiClient(use_ggrcq_service_account=True)
        resp = ext_api.delete("relationship", relationship_id)
        self.assertStatus(resp, 400)

    def test_ext_app_recreate_normal_relationship(self):
        """If ext app create already created relationship
    it has to be with is_external=False"""
        with factories.single_commit():
            market1 = factories.MarketFactory()
            market2 = factories.MarketFactory()
            relationship = factories.RelationshipFactory(
                source=market1,
                destination=market2,
                is_external=False,
            )
            relationship_id = relationship.id
        ext_api = ExternalApiClient(use_ggrcq_service_account=True)

        response = ext_api.post(all_models.Relationship,
                                data={
                                    "relationship": {
                                        "source": {
                                            "id": market1.id,
                                            "type": market1.type
                                        },
                                        "destination": {
                                            "id": market2.id,
                                            "type": market2.type
                                        },
                                        "is_external": True,
                                        "context": None,
                                    },
                                })

        self.assert200(response)
        relationship = all_models.Relationship.query.get(relationship_id)
        self.assertFalse(relationship.is_external)

    def test_sync_service_delete_normal_relationship(self):
        """Sync service can delete normal relationships via unmap endpoint"""

        with factories.single_commit():
            issue = factories.IssueFactory()
            objective = factories.ObjectiveFactory()

            relationship = factories.RelationshipFactory(source=issue,
                                                         destination=objective,
                                                         is_external=False)
            relationship_id = relationship.id
        resp = self.ext_api.unmap(issue, objective)
        self.assert200(resp)
        rel = all_models.Relationship.query.get(relationship_id)
        self.assertIsNone(rel)