class TestTitledMixin(TestCase): """Test cases for Labeled mixin""" def setUp(self): super(TestTitledMixin, self).setUp() self.api = Api() self.api.login_as_normal() def test_post_no_title(self): """Test object creation request without title key""" response = self.api.post(all_models.Product, {'product': { "description": "desc" }}) self.assert400(response) self.assertEqual(response.json, "'title' must be specified") def test_post_title_is_null(self): """Test object creation request title=null""" response = self.api.post( all_models.Product, {'product': { "description": "desc", "title": None }}) self.assert400(response) self.assertEqual(response.json, "'title' must be specified") @ddt.data( ('a', 'a'), (' a ', 'a'), ('', ''), (' ', ''), ) @ddt.unpack def test_post_title_is_valid(self, title, expected): """Test object creation request title={0!r}""" response = self.api.post( all_models.Product, {'product': { "description": "desc", "title": title }}) self.assertStatus(response, 201) product = all_models.Product.query.get(response.json['product']['id']) self.assertEqual(product.title, expected)
class TestTitledMixin(TestCase): """Test cases for Labeled mixin""" def setUp(self): super(TestTitledMixin, self).setUp() self.api = Api() self.api.login_as_normal() def test_post_no_title(self): """Test object creation request without title key""" response = self.api.post(all_models.Product, {'product': {"description": "desc"}}) self.assert400(response) self.assertEqual(response.json, "'title' must be specified") def test_post_title_is_null(self): """Test object creation request title=null""" response = self.api.post( all_models.Product, {'product': {"description": "desc", "title": None}} ) self.assert400(response) self.assertEqual(response.json, "'title' must be specified") @ddt.data( ('a', 'a'), (' a ', 'a'), ('', ''), (' ', ''), ) @ddt.unpack def test_post_title_is_valid(self, title, expected): """Test object creation request title={0!r}""" response = self.api.post( all_models.Product, {'product': {"description": "desc", "title": title}} ) self.assertStatus(response, 201) product = all_models.Product.query.get(response.json['product']['id']) self.assertEqual(product.title, expected)
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))
class TestArchivedAuditObjectCreation(TestCase): """Test creation permissions in audit""" def setUp(self): """Prepare data needed to run the tests""" TestCase.clear_data() self.api = Api() self.client.get("/login") self.archived_audit = factories.AuditFactory( archived=True ) self.archived_audit.context = factories.ContextFactory( name="Audit context", related_object=self.archived_audit, ) self.audit = factories.AuditFactory() self.assessment = factories.AssessmentFactory() @data( (all_models.Assessment, 403), (all_models.AssessmentTemplate, 403), (all_models.Issue, 201), (all_models.Relationship, 403), ) @unpack def test_object_creation(self, obj, archived_status): """Test object creation in audit and archived audit""" audit = self.audit.id, self.audit.context.id archived_audit = self.archived_audit.id, self.archived_audit.context.id assessment_id = self.assessment.id response = self.api.post( obj, _create_obj_dict(obj, audit[0], audit[1], assessment_id)) assert response.status_code == 201, \ "201 not returned for {} on audit, received {} instead".format( obj._inflector.model_singular, response.status_code) response = self.api.post(obj, _create_obj_dict( obj, archived_audit[0], archived_audit[1], assessment_id)) assert response.status_code == archived_status, \ "403 not raised for {} on archived audit, received {} instead".format( obj._inflector.model_singular, response.status_code)
class TestAccessControlRole(TestCase): """TestAccessControlRole""" def setUp(self): 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) 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)
class TestAccessControlRole(TestCase): """TestAccessControlRole""" def setUp(self): 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) 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)
def test_create_from_ggrc(self, definition_type, title): """Test create definition not allowed for GGRC.""" api = Api() payload = [{ "custom_attribute_definition": { "attribute_type": "Text", "context": { "id": None }, "definition_type": definition_type, "helptext": "Some text", "mandatory": False, "modal_title": "Modal title", "placeholder": "Placeholder", "title": title } }] response = api.post(all_models.CustomAttributeDefinition, payload) self.assertEqual(response.status_code, 405)
def test_create_from_ggrc(self, definition_type, title): """Test create definition not allowed for GGRC.""" api = Api() payload = [ { "custom_attribute_definition": { "attribute_type": "Text", "context": {"id": None}, "definition_type": definition_type, "helptext": "Some text", "mandatory": False, "modal_title": "Modal title", "placeholder": "Placeholder", "title": title } } ] response = api.post(all_models.CustomAttributeDefinition, payload) self.assertEqual(response.status_code, 405)
def test_create_from_ggrcq(self, definition_type, title): """Test create definition only for GGRCQ.""" api = Api() payload = [ { "custom_attribute_definition": { "attribute_type": "Text", "context": {"id": None}, "definition_type": definition_type, "helptext": "", "mandatory": False, "modal_title": "Title", "placeholder": "", "title": title } } ] with api.as_external(): response = api.post(all_models.CustomAttributeDefinition, payload) self.assertEqual(response.status_code, 200)
def test_create_from_ggrcq(self, definition_type, title): """Test create definition only for GGRCQ.""" api = Api() payload = [{ "custom_attribute_definition": { "attribute_type": "Text", "context": { "id": None }, "definition_type": definition_type, "helptext": "", "mandatory": False, "modal_title": "Title", "placeholder": "", "title": title } }] with api.as_external(): response = api.post(all_models.CustomAttributeDefinition, payload) self.assertEqual(response.status_code, 200)
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)
class TestCreator(TestCase): """ TestCreator """ def setUp(self): super(TestCreator, self).setUp() self.generator = Generator() self.api = Api() self.object_generator = ObjectGenerator() self.init_users() def init_users(self): """ Init users needed by the test cases """ users = [("creator", "Creator"), ("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 test_admin_page_access(self): """Permissions to admin page.""" for role, code in (("creator", 403), ("admin", 200)): self.api.set_user(self.users[role]) self.assertEqual(self.api.client.get("/admin").status_code, code) def test_creator_can_crud(self): """ Test Basic create/read,update/delete operations """ self.api.set_user(self.users["creator"]) creator_id = self.users["creator"].id audit_id = factories.AuditFactory().id all_errors = [] base_models = set([ "Control", "DataAsset", "Contract", "Policy", "Regulation", "Standard", "Document", "Facility", "Market", "Objective", "OrgGroup", "Vendor", "Product", "Clause", "System", "Process", "Project", "AccessGroup", "Metric" ]) for model_singular in base_models: try: model = get_model(model_singular) table_singular = model._inflector.table_singular table_plural = model._inflector.table_plural # Test POST creation response = self.api.post(model, { table_singular: { "title": model_singular, "context": None, "documents_reference_url": "ref", "link": "https://example.com", # ignored except for Document "contact": { "type": "Person", "id": creator_id, }, "audit": { # this is ignored on everything but Issues "id": audit_id, "type": "Audit", } }, }) if response.status_code != 201: all_errors.append("{} post creation failed {} {}".format( model_singular, response.status, response.data)) continue # Test GET when not owner obj_id = response.json.get(table_singular).get("id") response = self.api.get(model, obj_id) if response.status_code != 403: # we are not onwers yet all_errors.append( "{} can retrieve object if not owner".format(model_singular)) continue response = self.api.get_collection(model, obj_id) collection = response.json.get( "{}_collection".format(table_plural)).get(table_plural) if collection: all_errors.append( "{} can retrieve object if not owner (collection)" .format(model_singular)) continue # Test GET when owner acr = all_models.AccessControlRole.query.filter_by( object_type=model_singular, name="Admin" ).first() factories.AccessControlListFactory( object_id=obj_id, object_type=model_singular, ac_role=acr, person_id=creator_id ) response = self.api.get(model, obj_id) if response.status_code != 200: all_errors.append("{} can't GET object {}".format( model_singular, response.status)) continue # Test GET collection when owner response = self.api.get_collection(model, obj_id) collection = response.json.get( "{}_collection".format(table_plural)).get(table_plural) if not collection: all_errors.append( "{} cannot retrieve object even if owner (collection)" .format(model_singular)) continue except: all_errors.append("{} exception thrown".format(model_singular)) raise self.assertEqual(all_errors, []) def test_creator_search(self): """Test if creator can see the correct object while using the search api""" self.api.set_user(self.users['admin']) self.api.post(all_models.Regulation, { "regulation": {"title": "Admin regulation", "context": None}, }) self.api.set_user(self.users['creator']) acr_id = all_models.AccessControlRole.query.filter_by( object_type="Policy", name="Admin" ).first().id response = self.api.post(all_models.Policy, { "policy": { "title": "Creator Policy", "context": None, "access_control_list": [ acl_helper.get_acl_json(acr_id, self.users["creator"].id)], }, }) response.json.get("policy").get("id") response, _ = self.api.search("Regulation,Policy") entries = response.json["results"]["entries"] self.assertEqual(len(entries), 1) self.assertEqual(entries[0]["type"], "Policy") response, _ = self.api.search("Regulation,Policy", counts=True) self.assertEqual(response.json["results"]["counts"]["Policy"], 1) self.assertEqual( response.json["results"]["counts"].get("Regulation"), None) def _get_count(self, obj): """ Return the number of counts for the given object from search """ response, _ = self.api.search(obj, counts=True) return response.json["results"]["counts"].get(obj) def test_creator_should_see_users(self): """ Test if creator can see all the users in the system """ self.api.set_user(self.users['admin']) admin_count = self._get_count("Person") self.api.set_user(self.users['creator']) creator_count = self._get_count("Person") self.assertEqual(admin_count, creator_count) def test_relationships_access(self): """Check if creator cannot access relationship objects""" self.api.set_user(self.users['admin']) _, obj_0 = self.generator.generate(all_models.Regulation, "regulation", { "regulation": {"title": "Test regulation", "context": None}, }) _, obj_1 = self.generator.generate(all_models.Regulation, "regulation", { "regulation": {"title": "Test regulation 2", "context": None}, }) response, rel = self.generator.generate( all_models.Relationship, "relationship", { "relationship": {"source": { "id": obj_0.id, "type": "Regulation" }, "destination": { "id": obj_1.id, "type": "Regulation" }, "context": None}, } ) relationship_id = rel.id self.assertEqual(response.status_code, 201) self.api.set_user(self.users['creator']) response = self.api.get_collection(all_models.Relationship, relationship_id) self.assertEqual(response.status_code, 200) num = len(response.json["relationships_collection"]["relationships"]) self.assertEqual(num, 0) def test_revision_access(self): """Check if creator can access the right revision objects.""" def gen(title, extra_data=None): """Generates section.""" section_content = {"title": title, "context": None} if extra_data: section_content.update(**extra_data) return self.generator.generate(all_models.Section, "section", { "section": section_content })[1] def check(obj, expected): """Check that how many revisions of an object current user can see.""" response = self.api.get_query( all_models.Revision, "resource_type={}&resource_id={}".format(obj.type, obj.id) ) self.assertEqual(response.status_code, 200) self.assertEqual( len(response.json['revisions_collection']['revisions']), expected ) self.api.set_user(self.users["admin"]) obj_1 = gen("Test Section 1") self.api.set_user(self.users["creator"]) acr_id = all_models.AccessControlRole.query.filter_by( object_type="Section", name="Admin" ).first().id linked_acl = { "access_control_list": [ acl_helper.get_acl_json(acr_id, self.users["creator"].id)], } check(obj_1, 0) obj_2 = gen("Test Section 2", linked_acl) obj2_acl = obj_2.access_control_list[0] check(obj_2, 1) check(obj2_acl, 1) @ddt.data("creator", "admin") def test_count_type_in_accordion(self, glob_role): """Return count of Persons in DB for side accordion.""" self.api.set_user(self.users[glob_role]) ocordion_api_person_count_link = ( "/search?" "q=&types=Program%2CWorkflow_All%2C" "Audit%2CAssessment%2CIssue%2CRegulation%2C" "Policy%2CStandard%2CContract%2CClause%2CSection%2CControl%2C" "Objective%2CPerson%2COrgGroup%2CVendor%2CAccessGroup%2CSystem%2C" "Process%2CDataAsset%2CProduct%2CProject%2CFacility%2C" "Market%2CRisk%2CThreat&counts_only=true&" "extra_columns=Workflow_All%3DWorkflow%2C" "Workflow_Active%3DWorkflow%2CWorkflow_Draft%3D" "Workflow%2CWorkflow_Inactive%3DWorkflow&contact_id=1&" "extra_params=Workflow%3Astatus%3DActive%3BWorkflow_Active" "%3Astatus%3DActive%3BWorkflow_Inactive%3Astatus%3D" "Inactive%3BWorkflow_Draft%3Astatus%3DDraft" ) resp = self.api.client.get(ocordion_api_person_count_link) self.assertIn("Person", resp.json["results"]["counts"]) self.assertEqual(all_models.Person.query.count(), resp.json["results"]["counts"]["Person"]) @ddt.data( ("/api/revisions?resource_type={}&resource_id={}", 1), ("/api/revisions?source_type={}&source_id={}", 0), ("/api/revisions?destination_type={}&destination_id={}", 1), ) @ddt.unpack def test_changelog_access(self, link, revision_count): """Test accessing changelog under GC user who is assigned to object""" with factories.single_commit(): audit = factories.AuditFactory() asmnt = factories.AssessmentFactory(audit=audit) asmnt_id = asmnt.id factories.RelationshipFactory(source=audit, destination=asmnt) verifier_role = all_models.AccessControlRole.query.filter_by( object_type="Assessment", name="Verifiers", ).first() factories.AccessControlListFactory( person=self.users["creator"], ac_role=verifier_role, object=asmnt, ) self.api.set_user(self.users["creator"]) response = self.api.client.get(link.format("Assessment", asmnt_id)) self.assert200(response) self.assertEqual( len(response.json.get("revisions_collection", {}).get("revisions")), revision_count ) @ddt.data(all_models.ControlCategory, all_models.ControlAssertion) def test_permissions_for_categories(self, category_model): """Test get collection for {0}.""" self.api.set_user(self.users["creator"]) resp = self.api.get_collection(category_model, None) plural_name = category_model._inflector.table_plural key_name = "{}_collection".format(plural_name) self.assertIn(key_name, resp.json) self.assertIn(plural_name, resp.json[key_name]) collection = resp.json[key_name][plural_name] self.assertTrue(collection, "Collection shouldn't be empty")
class TestReader(TestCase): """ Test reader role """ def setUp(self): super(TestReader, self).setUp() self.generator = Generator() self.api = Api() self.object_generator = ObjectGenerator() self.init_users() def init_users(self): """ Init users needed by the test cases """ users = [("reader", "Reader"), ("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 test_admin_page_access(self): for role, code in (("reader", 403), ("admin", 200)): self.api.set_user(self.users[role]) self.assertEqual(self.api.client.get("/admin").status_code, code) def test_reader_can_crud(self): """ Test Basic create/read,update/delete operations """ self.api.set_user(self.users["reader"]) all_errors = [] base_models = set([ "Control", "DataAsset", "Contract", "Policy", "Regulation", "Standard", "Document", "Facility", "Market", "Objective", "OrgGroup", "Vendor", "Product", "Clause", "System", "Process", "Project", "AccessGroup", ]) for model_singular in base_models: try: model = get_model(model_singular) table_singular = model._inflector.table_singular table_plural = model._inflector.table_plural # Test POST creation response = self.api.post( model, { table_singular: { "title": model_singular, "context": None, "reference_url": "ref", "contact": { "type": "Person", "id": self.users["reader"].id, }, }, }) if response.status_code != 201: all_errors.append("{} post creation failed {} {}".format( model_singular, response.status, response.data)) continue obj_id = response.json.get(table_singular).get("id") # Test GET when owner response = self.api.get(model, obj_id) if response.status_code != 200: all_errors.append("{} can't GET object {}".format( model_singular, response.status)) continue # Test GET collection when owner response = self.api.get_collection(model, obj_id) collection = response.json.get( "{}_collection".format(table_plural)).get(table_plural) if len(collection) == 0: all_errors.append( "{} cannot retrieve object even if owner (collection)". format(model_singular)) continue except: all_errors.append("{} exception thrown".format(model_singular)) raise self.assertEqual(all_errors, []) def test_reader_search(self): """ Test if reader can see the correct object while using search api """ self.api.set_user(self.users['admin']) self.api.post(all_models.Regulation, { "regulation": { "title": "Admin regulation", "context": None }, }) self.api.set_user(self.users['reader']) response = self.api.post(all_models.Policy, { "policy": { "title": "reader Policy", "context": None }, }) response, _ = self.api.search("Regulation,Policy") entries = response.json["results"]["entries"] self.assertEqual(len(entries), 2) response, _ = self.api.search("Regulation,Policy", counts=True) self.assertEqual(response.json["results"]["counts"]["Policy"], 1) self.assertEqual(response.json["results"]["counts"]["Regulation"], 1) def _get_count(self, obj): """ Return the number of counts for the given object from search """ response, _ = self.api.search(obj, counts=True) return response.json["results"]["counts"].get(obj) def test_reader_should_see_users(self): """ Test if creator can see all the users in the system """ self.api.set_user(self.users['admin']) admin_count = self._get_count("Person") self.api.set_user(self.users['reader']) reader_count = self._get_count("Person") self.assertEqual(admin_count, reader_count) def test_relationships_access(self): """Check if reader can access relationship objects""" self.api.set_user(self.users['admin']) _, obj_0 = self.generator.generate(all_models.Regulation, "regulation", { "regulation": { "title": "Test regulation", "context": None }, }) _, obj_1 = self.generator.generate( all_models.Regulation, "regulation", { "regulation": { "title": "Test regulation 2", "context": None }, }) response, rel = self.generator.generate( all_models.Relationship, "relationship", { "relationship": { "source": { "id": obj_0.id, "type": "Regulation" }, "destination": { "id": obj_1.id, "type": "Regulation" }, "context": None }, }) relationship_id = rel.id self.assertEqual(response.status_code, 201) self.api.set_user(self.users['reader']) response = self.api.get_collection(all_models.Relationship, relationship_id) self.assertEqual(response.status_code, 200) num = len(response.json["relationships_collection"]["relationships"]) self.assertEqual(num, 1) def test_creation_of_mappings(self): """Check if reader can't create mappings""" self.generator.api.set_user(self.users["admin"]) _, control = self.generator.generate(all_models.Control, "control", { "control": { "title": "Test Control", "context": None }, }) self.generator.api.set_user(self.users['reader']) _, program = self.generator.generate(all_models.Program, "program", { "program": { "title": "Test Program", "context": None }, }) response, _ = self.generator.generate( all_models.Relationship, "relationship", { "relationship": { "destination": { "id": program.id, "type": "Program" }, "source": { "id": control.id, "type": "Control" }, "context": { "id": program.context.id, "type": "context" } }, }) self.assertEqual(response.status_code, 403)
class TestReader(TestCase): """ Test reader role """ def setUp(self): super(TestReader, self).setUp() self.generator = Generator() self.api = Api() self.object_generator = ObjectGenerator() self.init_users() def init_users(self): """ Init users needed by the test cases """ users = [("reader", "Reader"), ("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 def test_admin_page_access(self): """Only admin can use admin section""" for role, code in (("reader", 403), ("admin", 200)): self.api.set_user(self.users[role]) self.assertEqual(self.api.client.get("/admin").status_code, code) def test_reader_can_crud(self): """ Test Basic create/read,update/delete operations """ self.api.set_user(self.users["reader"]) all_errors = [] base_models = set([ "Control", "DataAsset", "Contract", "Policy", "Regulation", "Standard", "Document", "Facility", "Market", "Objective", "OrgGroup", "Vendor", "Product", "Clause", "System", "Process", "Project", "AccessGroup", "Metric" ]) for model_singular in base_models: try: model = get_model(model_singular) table_singular = model._inflector.table_singular table_plural = model._inflector.table_plural # Test POST creation response = self.api.post( model, { table_singular: { "title": model_singular, "context": None, "documents_reference_url": "ref", "link": "https://example.com", # ignored except for Document "contact": { "type": "Person", "id": self.users["reader"].id, }, }, }) if response.status_code != 201: all_errors.append("{} post creation failed {} {}".format( model_singular, response.status, response.data)) continue obj_id = response.json.get(table_singular).get("id") # Test GET when owner response = self.api.get(model, obj_id) if response.status_code != 200: all_errors.append("{} can't GET object {}".format( model_singular, response.status)) continue # Test GET collection when owner response = self.api.get_collection(model, obj_id) collection = response.json.get( "{}_collection".format(table_plural)).get(table_plural) if not collection: all_errors.append( "{} cannot retrieve object even if owner (collection)". format(model_singular)) continue except: all_errors.append("{} exception thrown".format(model_singular)) raise self.assertEqual(all_errors, []) def test_reader_search(self): """ Test if reader can see the correct object while using search api """ self.api.set_user(self.users['admin']) self.api.post(all_models.Regulation, { "regulation": { "title": "Admin regulation", "context": None }, }) self.api.set_user(self.users['reader']) response = self.api.post(all_models.Policy, { "policy": { "title": "reader Policy", "context": None }, }) response, _ = self.api.search("Regulation,Policy") entries = response.json["results"]["entries"] self.assertEqual(len(entries), 2) response, _ = self.api.search("Regulation,Policy", counts=True) self.assertEqual(response.json["results"]["counts"]["Policy"], 1) self.assertEqual(response.json["results"]["counts"]["Regulation"], 1) def _get_count(self, obj): """ Return the number of counts for the given object from search """ response, _ = self.api.search(obj, counts=True) return response.json["results"]["counts"].get(obj) def test_reader_should_see_users(self): """ Test if creator can see all the users in the system """ self.api.set_user(self.users['admin']) admin_count = self._get_count("Person") self.api.set_user(self.users['reader']) reader_count = self._get_count("Person") self.assertEqual(admin_count, reader_count) def test_relationships_access(self): """Check if reader can access relationship objects""" self.api.set_user(self.users['admin']) _, obj_0 = self.generator.generate(all_models.Regulation, "regulation", { "regulation": { "title": "Test regulation", "context": None }, }) _, obj_1 = self.generator.generate( all_models.Regulation, "regulation", { "regulation": { "title": "Test regulation 2", "context": None }, }) response, rel = self.generator.generate( all_models.Relationship, "relationship", { "relationship": { "source": { "id": obj_0.id, "type": "Regulation" }, "destination": { "id": obj_1.id, "type": "Regulation" }, "context": None }, }) relationship_id = rel.id self.assertEqual(response.status_code, 201) self.api.set_user(self.users['reader']) response = self.api.get_collection(all_models.Relationship, relationship_id) self.assertEqual(response.status_code, 200) num = len(response.json["relationships_collection"]["relationships"]) self.assertEqual(num, 1) def test_creation_of_mappings(self): """Check if reader can't create mappings""" self.generator.api.set_user(self.users["admin"]) _, control = self.generator.generate(all_models.Control, "control", { "control": { "title": "Test Control", "context": None }, }) self.generator.api.set_user(self.users['reader']) _, program = self.generator.generate(all_models.Program, "program", { "program": { "title": "Test Program", "context": None }, }) response, _ = self.generator.generate( all_models.Relationship, "relationship", { "relationship": { "destination": { "id": program.id, "type": "Program" }, "source": { "id": control.id, "type": "Control" }, "context": { "id": program.context.id, "type": "context" } }, }) self.assertEqual(response.status_code, 403) @ddt.data("creator", "reader") def test_unmap_people(self, user_role): """Test that global reader/creator can't unmap people from program""" user = self.users[user_role] with factories.single_commit(): program = factories.ProgramFactory() mapped_person = factories.ObjectPersonFactory( personable=program, person=user, context=program.context) self.api.set_user(user) db.session.add(mapped_person) response = self.api.delete(mapped_person) self.assertEqual(response.status_code, 403) def test_read_evidence_revision(self): """Global Read can read Evidence revision content""" user = self.users["reader"] link = "google.com" evidence = factories.EvidenceUrlFactory(link=link) evidence_id = evidence.id self.api.set_user(user) resp = self.api.client.get( "/api/revisions?resource_type={}&resource_id={}".format( evidence.type, evidence_id)) rev_content = resp.json["revisions_collection"]["revisions"][0][ "content"] self.assertTrue(rev_content) self.assertEquals(link, rev_content["link"])
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, "hotlist_ids": [999, ]}}, {"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) @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()
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)
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"])
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))
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")
class TestEnableDisableNotifications(TestCase): """Test enable / disable notifications by user with specific global role. """ def setUp(self): super(TestEnableDisableNotifications, self).setUp() self.api = Api() @ddt.data("Reader", "Creator", "Editor", "Administrator") def test_default_notif_settings(self, role_name): """Test setting 'Daily email digest' checkbox to False by the user with global role {0}.""" with factories.single_commit(): name = "Test Name" email = "*****@*****.**" person = factories.PersonFactory(name=name, email=email) role = all_models.Role.query.filter( all_models.Role.name == role_name ).one() rbac_factories.UserRoleFactory(role=role, person=person) person = all_models.Person.query.get(person.id) self.api.set_user(person=person) response = self.api.post( all_models.NotificationConfig, data={ "notification_config": { "person_id": person.id, "notif_type": "Email_Digest", "enable_flag": True, "context": { "id": None, "type": "Context" } } }, ) self.assertEqual(response.status_code, 201) @ddt.data("Reader", "Creator", "Editor", "Administrator") def test_get_notif_settings(self, role_name): """Tests notification configs list is returned after it has been established by the user with role {0}.""" with factories.single_commit(): name = "Test Name" email = "*****@*****.**" person = factories.PersonFactory(name=name, email=email) role = all_models.Role.query.filter( all_models.Role.name == role_name ).one() rbac_factories.UserRoleFactory(role=role, person=person) person = all_models.Person.query.get(person.id) self.api.set_user(person=person) response = self.api.post( all_models.NotificationConfig, data={ "notification_config": { "person_id": person.id, "notif_type": "Email_Digest", "enable_flag": True, "context": { "id": None, "type": "Context" } } }, ) self.assertEqual(response.status_code, 201) response = self.api.get_query(all_models.NotificationConfig, "person_id=%s" % person.id) result_configs_list = response.json['notification_configs_collection'] self.assertTrue(result_configs_list['notification_configs'] != [])
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()
class TestReader(TestCase): """ Test reader role """ def setUp(self): super(TestReader, self).setUp() self.generator = Generator() self.api = Api() self.object_generator = ObjectGenerator() self.init_users() def init_users(self): """ Init users needed by the test cases """ users = [("reader", "Reader"), ("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 test_admin_page_access(self): return for role, code in (("reader", 403), ("admin", 200)): self.api.set_user(self.users[role]) self.assertEqual(self.api.client.get("/admin").status_code, code) def test_reader_can_crud(self): return """ Test Basic create/read,update/delete operations """ self.api.set_user(self.users["reader"]) all_errors = [] base_models = set([ "Control", "Assessment", "DataAsset", "Contract", "Policy", "Regulation", "Standard", "Document", "Facility", "Market", "Objective", "OrgGroup", "Vendor", "Product", "Clause", "System", "Process", "Issue", "Project", "AccessGroup", ]) for model_singular in base_models: try: model = get_model(model_singular) table_singular = model._inflector.table_singular table_plural = model._inflector.table_plural # Test POST creation response = self.api.post(model, { table_singular: { "title": model_singular, "context": None, "reference_url": "ref", "contact": { "type": "Person", "id": self.users["reader"].id, }, }, }) if response.status_code != 201: all_errors.append("{} post creation failed {} {}".format( model_singular, response.status, response.data)) continue obj_id = response.json.get(table_singular).get("id") # Test GET when owner response = self.api.get(model, obj_id) if response.status_code != 200: all_errors.append("{} can't GET object {}".format( model_singular, response.status)) continue # Test GET collection when owner response = self.api.get_collection(model, obj_id) collection = response.json.get( "{}_collection".format(table_plural)).get(table_plural) if len(collection) == 0: all_errors.append( "{} cannot retrieve object even if owner (collection)".format( model_singular)) continue except: all_errors.append("{} exception thrown".format(model_singular)) raise self.assertEqual(all_errors, []) def test_reader_search(self): return """ Test if reader can see the correct object while using search api """ self.api.set_user(self.users['admin']) self.api.post(all_models.Regulation, { "regulation": {"title": "Admin regulation", "context": None}, }) self.api.set_user(self.users['reader']) response = self.api.post(all_models.Policy, { "policy": {"title": "reader Policy", "context": None}, }) obj_id = response.json.get("policy").get("id") self.api.post(all_models.ObjectOwner, {"object_owner": { "person": { "id": self.users['reader'].id, "type": "Person", }, "ownable": { "type": "Policy", "id": obj_id, }, "context": None}}) response, _ = self.api.search("Regulation,Policy") entries = response.json["results"]["entries"] self.assertEqual(len(entries), 2) response, _ = self.api.search("Regulation,Policy", counts=True) self.assertEqual(response.json["results"]["counts"]["Policy"], 1) self.assertEqual(response.json["results"]["counts"]["Regulation"], 1) def _get_count(self, obj): """ Return the number of counts for the given object from search """ response, _ = self.api.search(obj, counts=True) return response.json["results"]["counts"].get(obj) def test_reader_should_see_users(self): """ Test if creator can see all the users in the system """ self.api.set_user(self.users['admin']) admin_count = self._get_count("Person") self.api.set_user(self.users['reader']) reader_count = self._get_count("Person") self.assertEqual(admin_count, reader_count) def test_relationships_access(self): """Check if reader can access relationship objects""" self.api.set_user(self.users['admin']) _, obj_0 = self.generator.generate(all_models.Regulation, "regulation", { "regulation": {"title": "Test regulation", "context": None}, }) _, obj_1 = self.generator.generate(all_models.Regulation, "regulation", { "regulation": {"title": "Test regulation 2", "context": None}, }) response, rel = self.generator.generate( all_models.Relationship, "relationship", { "relationship": {"source": { "id": obj_0.id, "type": "Regulation" }, "destination": { "id": obj_1.id, "type": "Regulation" }, "context": None}, } ) relationship_id = rel.id self.assertEqual(response.status_code, 201) self.api.set_user(self.users['reader']) response = self.api.get_collection(all_models.Relationship, relationship_id) self.assertEqual(response.status_code, 200) num = len(response.json["relationships_collection"]["relationships"]) self.assertEqual(num, 1) def test_creation_of_mappings(self): self.generator.api.set_user(self.users["admin"]) _, control = self.generator.generate(all_models.Control, "control", { "control": {"title": "Test Control", "context": None}, }) self.generator.api.set_user(self.users['reader']) _, program = self.generator.generate(all_models.Program, "program", { "program": {"title": "Test Program", "context": None}, }) response, _ = self.generator.generate( all_models.Relationship, "relationship", { "relationship": {"destination": { "id": program.id, "type": "Program" }, "source": { "id": control.id, "type": "Control" }, "context": { "id": program.context.id, "type": "context" }}, }) self.assertEqual(response.status_code, 403)
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)
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", }] } def test_async_request_state_transitions(self): """Test asynchronous transitions""" # Due to complexity of the test and the actual need for all variables # pylint: disable=too-many-locals tgt_roles = role.get_ac_roles_for("TaskGroupTask") queue = Queue.Queue() wf = self.generator.generate_workflow(self.weekly_wf)[1] context_id = wf.context.id tg_id = all_models.TaskGroup.query.first().id obj_count = 10 # actual thread count is 20, 10 tgt + 10 tgo with factories.single_commit(): person_id = factories.PersonFactory().id controls = [ factories.ControlFactory().id for _ in range(obj_count) ] def create_tgo(context_id, tg_id, control_id): queue.put( self.api.post(all_models.TaskGroupObject, [{ "task_group_object": { "context": { "id": context_id, "type": "Context" }, "task_group": { "id": tg_id, "type": "TaskGroup" }, "object": { "id": control_id, "type": "Control" }, }, }])) def create_tgt(context_id, tg_id, tgt_roles, person_id): queue.put( self.api.post(all_models.TaskGroupTask, [{ "task_group_task": { "response_options": [], "start_date": "2018-03-29", "end_date": "2018-04-05T00:23:41.000Z", "minStartDate": "2018-03-29T00:23:41.576Z", "access_control_list": [{ "ac_role_id": tgt_roles["Task Assignees"].id, "person": { "id": person_id, "type": "Person" } }], "contact": { "id": person_id, "type": "Person" }, "task_group": { "id": tg_id, "type": "TaskGroup" }, "context": { "id": context_id, "type": "Context" }, "sort_index": "1", "modal_title": "Create New Task", "title": "Dummy task title", "task_type": "text", "description": "", "slug": "", } }])) # Move all tasks to In Progress threads = [] for control_id in controls: threads.append( Thread(target=create_tgo, args=(context_id, tg_id, control_id))) threads.append( Thread(target=create_tgt, args=(context_id, tg_id, tgt_roles, person_id))) for t in threads: t.start() for t in threads: t.join() while not queue.empty(): response = queue.get() self.assert200(response, response.data) internal_acl_count = all_models.AccessControlList.query.filter( all_models.AccessControlList.parent_id.isnot(None)).count() self.assertEqual( internal_acl_count, (1 + 2 * obj_count) * 2, # wf role on 1 task group + number of tgo and tgt # times 2 is for all relationships that belong to each object )
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)
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)
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, [])
class TestWfNotifsGenerator(TestCase): """Test wf cycle tasks notifications generation.""" def setUp(self): """Set up.""" super(TestWfNotifsGenerator, self).setUp() self.api = Api() self.wf_generator = WorkflowsGenerator() self.object_generator = ObjectGenerator() Notification.query.delete() with freeze_time("2015-05-01 14:29:00"): wf_slug = "wf1" with factories.single_commit(): wf = wf_factories.WorkflowFactory(slug=wf_slug) task_group = wf_factories.TaskGroupFactory(workflow=wf) wf_factories.TaskGroupTaskFactory( task_group=task_group, title='task1', start_date=datetime.now(), end_date=datetime.now() + timedelta(7) ) data = workflow_api.get_cycle_post_dict(wf) self.api.post(all_models.Cycle, data) wf = all_models.Workflow.query.filter_by(slug=wf_slug).one() self.cycle = all_models.Cycle.query.filter_by(workflow_id=wf.id).one() self.ctask = all_models.CycleTaskGroupObjectTask.query.filter_by( cycle_id=self.cycle.id).first() def test_ctasks_notifs_generator_daily_digest(self): """Test cycle tasks notifications generation job.""" with freeze_time("2015-05-01 14:29:00"): self.assert_notifications_for_object(self.cycle, "manual_cycle_created") self.assert_notifications_for_object(self.ctask, "manual_cycle_created", "cycle_task_due_in", "cycle_task_due_today", "cycle_task_overdue") # Move task to Finished self.wf_generator.modify_object( self.ctask, data={"status": "Verified"}) generate_cycle_tasks_notifs() self.assert_notifications_for_object(self.cycle, "all_cycle_tasks_completed", "manual_cycle_created") # Undo finish self.wf_generator.modify_object( self.ctask, data={"status": "In Progress"}) generate_cycle_tasks_notifs() self.assert_notifications_for_object(self.cycle, "manual_cycle_created") self.assert_notifications_for_object(self.ctask, "cycle_task_due_in", "cycle_task_due_today", "cycle_task_overdue") self.wf_generator.modify_object( self.ctask, data={"status": "Declined"}) self.assert_notifications_for_object(self.ctask, "cycle_task_due_in", "cycle_task_due_today", "cycle_task_overdue", "cycle_task_declined") @ddt.data(("2015-05-01 14:29:00", ("all_cycle_tasks_completed", "manual_cycle_created")), ("2015-05-02 07:29:00", ("all_cycle_tasks_completed", "manual_cycle_created")), ("2015-05-02 14:29:00", ("all_cycle_tasks_completed", "manual_cycle_created")), ("2015-05-03 07:29:00", ("manual_cycle_created",))) @ddt.unpack def test_cycle_task_update_timelines(self, _datetime, notifications): """Test cycle task has been updated: 1) the day before job is called; 2) the same day job is called before 08:00 AM UTC; 3) the same day job is called after 08:00 AM UTC; 4) two days before job is called. """ with freeze_time("2015-05-01 14:29:00"): # Move task to Finished self.wf_generator.modify_object( self.ctask, data={"status": "Verified"}) with freeze_time(_datetime): generate_cycle_tasks_notifs() self.assert_notifications_for_object(self.cycle, *notifications) def test_ctasks_notifs_generator_daily_digest_called_twice(self): """No duplicated notifications should be generated""" with freeze_time("2015-05-01 14:29:00"): generate_cycle_tasks_notifs() self.assert_notifications_for_object(self.cycle, "manual_cycle_created") self.assert_notifications_for_object(self.ctask, "manual_cycle_created", "cycle_task_due_in", "cycle_task_due_today", "cycle_task_overdue") # Move task to Finished self.wf_generator.modify_object( self.ctask, data={"status": "Verified"}) generate_cycle_tasks_notifs() generate_cycle_tasks_notifs() self.assert_notifications_for_object(self.cycle, "all_cycle_tasks_completed", "manual_cycle_created") def test_ctasks_notifs_generator_cron_job(self): """Test cycle tasks notifications generation cron job.""" with freeze_time("2015-05-2 08:00:00"): generate_cycle_tasks_notifs() self.assert_notifications_for_object(self.cycle, "manual_cycle_created") self.assert_notifications_for_object(self.ctask, "manual_cycle_created", "cycle_task_due_in", "cycle_task_due_today", "cycle_task_overdue")
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())
class TestIssueMapping(TestCase): """Test Issue mapping""" def setup_roles(self): """Setup necessary roles needed by the tests""" query = all_models.Role.query acr_query = all_models.AccessControlRole.query self.roles = { 'creator': query.filter_by(name="Creator").first(), 'auditors': acr_query.filter_by(name="Auditors").first(), 'program_editors': acr_query.filter_by( name="Program Editors").first() } def setup_users(self): """Creates two creator users""" self.users = {} for user_name in ('auditor', 'programeditor'): user = factories.PersonFactory() rbac_factories.UserRoleFactory( role=self.roles['creator'], person=user) self.users[user_name] = user def setup_audits(self): """Create an audit and an archived audit""" self.audits = { False: self.create_audit(archived=False), True: self.create_audit(archived=True) } def setup_snapshots_and_issue(self): """Create snapshot & issue objects""" self.snapshots = {} self.issues = {} self.control = factories.ControlFactory() for is_archived in (False, True): audit = self.audits[is_archived] self.snapshots[is_archived] = self._create_snapshots( audit, [self.control], )[0] factories.RelationshipFactory( source=audit, destination=self.snapshots[is_archived], ) # Create an issue issue = factories.IssueFactory() self.issues[is_archived] = issue # Map issue to audit factories.RelationshipFactory( source=audit, destination=issue, context=audit.context ) def create_audit(self, archived=False): """Create an audit object and fix the it's context""" audit = factories.AuditFactory(archived=archived) factories.RelationshipFactory( source=audit, destination=audit.program, ) # Add auditor & program editor roles factories.AccessControlListFactory( ac_role=self.roles['auditors'], object=audit, person=self.users['auditor'] ) factories.AccessControlListFactory( ac_role=self.roles['program_editors'], object=audit.program, person=self.users['programeditor'] ) return audit def setUp(self): """Prepare data needed to run the tests""" super(TestIssueMapping, self).setUp() self.api = Api() self.setup_roles() self.setup_users() self.setup_audits() self.setup_snapshots_and_issue() @data( # user_name, is_archived ('auditor', True), ('programeditor', True), ('auditor', False), ('programeditor', False), ) @unpack def test_mapping_to_issue(self, user_name, is_archived): """Test mapping snapshots to issue for {0} ({1})""" user = self.users[user_name] payload = _get_map_dict( self.snapshots[is_archived], self.issues[is_archived]) self.api.set_user(user) # Try to map to audit response = self.api.post(all_models.Relationship, payload) self.assertStatus(response, 201) rel_id = response.json['relationship']['id'] relationship = all_models.Relationship.query.filter_by(id=rel_id).first() response = self.api.delete(relationship) self.assertStatus(response, 200)
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)
class TestAssessmentGeneration(TestCase): """Test assessment generation""" def setUp(self): super(TestAssessmentGeneration, self).setUp() self.api = Api() self.audit = factories.AuditFactory() self.control = factories.ControlFactory(test_plan="Control Test Plan") revision = Revision.query.filter( Revision.resource_id == self.control.id, Revision.resource_type == self.control.__class__.__name__).order_by( Revision.id.desc()).first() self.snapshot = factories.SnapshotFactory( parent=self.audit, child_id=self.control.id, child_type=self.control.__class__.__name__, revision_id=revision.id) def assessment_post(self, template=None): """Helper function to POST an assessment""" assessment_dict = { "_generated": True, "audit": { "id": self.audit.id, "type": "Audit" }, "object": { "id": self.snapshot.id, "type": "Snapshot" }, "context": { "id": self.audit.context.id, "type": "Context" }, "title": "Temp title" } if template: assessment_dict["template"] = { "id": template.id, "type": "AssessmentTemplate" } return self.api.post(Assessment, {"assessment": assessment_dict}) def test_autogenerated_title(self): """Test autogenerated assessment title""" control_title = self.control.title audit_title = self.audit.title response = self.assessment_post() title = response.json["assessment"]["title"] self.assertIn(audit_title, title) self.assertIn(control_title, title) def test_template_test_plan(self): """Test if generating assessments from template sets default test plan""" template = factories.AssessmentTemplateFactory( test_plan_procedure=False, procedure_description="Assessment Template Test Plan") response = self.assessment_post(template) self.assertEqual(response.json["assessment"]["test_plan"], template.procedure_description) def test_control_test_plan(self): """Test test_plan from control""" test_plan = self.control.test_plan template = factories.AssessmentTemplateFactory( test_plan_procedure=True) response = self.assessment_post(template) self.assertEqual(response.json["assessment"]["test_plan"], test_plan)
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())
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)
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 )
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)
class TestCreator(TestCase): """ TestCreator """ def setUp(self): super(TestCreator, self).setUp() self.generator = Generator() self.api = Api() self.object_generator = ObjectGenerator() self.init_users() def init_users(self): """ Init users needed by the test cases """ users = [("creator", "Creator"), ("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 test_admin_page_access(self): for role, code in (("creator", 403), ("admin", 200)): self.api.set_user(self.users[role]) self.assertEqual(self.api.tc.get("/admin").status_code, code) def test_creator_can_crud(self): """ Test Basic create/read,update/delete operations """ self.api.set_user(self.users["creator"]) all_errors = [] base_models = set([ "Control", "DataAsset", "Contract", "Policy", "Regulation", "Standard", "Document", "Facility", "Market", "Objective", "OrgGroup", "Vendor", "Product", "Clause", "System", "Process", "Issue", "Project", "AccessGroup" ]) for model_singular in base_models: try: model = get_model(model_singular) table_singular = model._inflector.table_singular table_plural = model._inflector.table_plural # Test POST creation response = self.api.post( model, { table_singular: { "title": model_singular, "context": None, "reference_url": "ref", "contact": { "type": "Person", "id": self.users["creator"].id, }, }, }) if response.status_code != 201: all_errors.append("{} post creation failed {} {}".format( model_singular, response.status, response.data)) continue # Test GET when not owner obj_id = response.json.get(table_singular).get("id") response = self.api.get(model, obj_id) if response.status_code != 403: # we are not onwers yet all_errors.append( "{} can retrieve object if not owner".format( model_singular)) continue response = self.api.get_collection(model, obj_id) collection = response.json.get( "{}_collection".format(table_plural)).get(table_plural) if len(collection) != 0: all_errors.append( "{} can retrieve object if not owner (collection)". format(model_singular)) continue # Become an owner response = self.api.post( all_models.ObjectOwner, { "object_owner": { "person": { "id": self.users['creator'].id, "type": "Person", }, "ownable": { "type": model_singular, "id": obj_id }, "context": None } }) if response.status_code != 201: all_errors.append("{} can't create owner {}.".format( model_singular, response.status)) continue # Test GET when owner response = self.api.get(model, obj_id) if response.status_code != 200: all_errors.append("{} can't GET object {}".format( model_singular, response.status)) continue # Test GET collection when owner response = self.api.get_collection(model, obj_id) collection = response.json.get( "{}_collection".format(table_plural)).get(table_plural) if len(collection) == 0: all_errors.append( "{} cannot retrieve object even if owner (collection)". format(model_singular)) continue except: all_errors.append("{} exception thrown".format(model_singular)) raise self.assertEqual(all_errors, []) def test_creator_search(self): """Test if creator can see the correct object while using the search api""" self.api.set_user(self.users['admin']) self.api.post(all_models.Regulation, { "regulation": { "title": "Admin regulation", "context": None }, }) self.api.set_user(self.users['creator']) response = self.api.post(all_models.Policy, { "policy": { "title": "Creator Policy", "context": None }, }) obj_id = response.json.get("policy").get("id") self.api.post( all_models.ObjectOwner, { "object_owner": { "person": { "id": self.users['creator'].id, "type": "Person", }, "ownable": { "type": "Policy", "id": obj_id, }, "context": None } }) response, _ = self.api.search("Regulation,Policy") entries = response.json["results"]["entries"] self.assertEqual(len(entries), 1) self.assertEqual(entries[0]["type"], "Policy") response, _ = self.api.search("Regulation,Policy", counts=True) self.assertEqual(response.json["results"]["counts"]["Policy"], 1) self.assertEqual(response.json["results"]["counts"].get("Regulation"), None) def _get_count(self, obj): """ Return the number of counts for the given object from search """ response, _ = self.api.search(obj, counts=True) return response.json["results"]["counts"].get(obj) def test_creator_should_see_users(self): """ Test if creator can see all the users in the system """ self.api.set_user(self.users['admin']) admin_count = self._get_count("Person") self.api.set_user(self.users['creator']) creator_count = self._get_count("Person") self.assertEqual(admin_count, creator_count) def test_creator_cannot_be_owner(self): """Test if creator cannot become owner of the object he has not created""" self.api.set_user(self.users['admin']) _, obj = self.generator.generate(all_models.Regulation, "regulation", { "regulation": { "title": "Test regulation", "context": None }, }) self.api.set_user(self.users['creator']) response = self.api.post( all_models.ObjectOwner, { "object_owner": { "person": { "id": self.users['creator'].id, "type": "Person", }, "ownable": { "type": "Regulation", "id": obj.id, }, "context": None } }) self.assertEqual(response.status_code, 403) def test_relationships_access(self): """Check if creator cannot access relationship objects""" self.api.set_user(self.users['admin']) _, obj_0 = self.generator.generate(all_models.Regulation, "regulation", { "regulation": { "title": "Test regulation", "context": None }, }) _, obj_1 = self.generator.generate( all_models.Regulation, "regulation", { "regulation": { "title": "Test regulation 2", "context": None }, }) response, rel = self.generator.generate( all_models.Relationship, "relationship", { "relationship": { "source": { "id": obj_0.id, "type": "Regulation" }, "destination": { "id": obj_1.id, "type": "Regulation" }, "context": None }, }) relationship_id = rel.id self.assertEqual(response.status_code, 201) self.api.set_user(self.users['creator']) response = self.api.get_collection(all_models.Relationship, relationship_id) self.assertEqual(response.status_code, 200) num = len(response.json["relationships_collection"]["relationships"]) self.assertEqual(num, 0) def test_revision_access(self): """Check if creator can access the right revision objects.""" def gen(title): return self.generator.generate(all_models.Section, "section", { "section": { "title": title, "context": None }, })[1] def check(obj, expected): """Check that how many revisions of an object current user can see.""" response = self.api.get_query( all_models.Revision, "resource_type={}&resource_id={}".format(obj.type, obj.id)) self.assertEqual(response.status_code, 200) self.assertEqual( len(response.json['revisions_collection']['revisions']), expected) self.api.set_user(self.users["admin"]) obj_1 = gen("Test Section 1") obj_2 = gen("Test Section 2") self.api.post( all_models.ObjectOwner, { "object_owner": { "person": { "id": self.users['creator'].id, "type": "Person", }, "ownable": { "type": "Section", "id": obj_2.id, }, "context": None } }) self.api.set_user(self.users["creator"]) check(obj_1, 0) check(obj_2, 2)
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())
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()
class TestIssueMapping(TestCase): """Test Issue mapping""" def setup_roles(self): """Setup necessary roles needed by the tests""" query = all_models.Role.query self.roles = { 'creator': query.filter_by(name="Creator").first(), 'auditor': query.filter_by(name="Auditor").first(), 'program_editor': query.filter_by(name="ProgramEditor").first() } def setup_users(self): """Creates two creator users""" self.users = {} for user_name in ('auditor', 'programeditor'): user = factories.PersonFactory() rbac_factories.UserRoleFactory(role=self.roles['creator'], person=user) self.users[user_name] = user def setup_audits(self): """Create an audit and an archived audit""" self.audits = { False: self.create_audit(archived=False), True: self.create_audit(archived=True) } def setup_snapshots_and_issue(self): """Create snapshot & issue objects""" self.snapshots = {} self.issues = {} self.control = factories.ControlFactory() revision = all_models.Revision.query.filter( all_models.Revision.resource_type == self.control.type).first() for is_archived in (False, True): audit = self.audits[is_archived] # Create a snapshot self.snapshots[is_archived] = factories.SnapshotFactory( child_id=revision.resource_id, child_type=revision.resource_type, revision=revision, parent=audit, context=audit.context, ) # Create an issue issue = factories.IssueFactory() self.issues[is_archived] = issue # Map issue to audit factories.RelationshipFactory(source=audit, destination=issue, context=audit.context) def create_audit(self, archived=False): """Create an audit object and fix the it's context""" audit = factories.AuditFactory(archived=archived) # Add auditor & program editor roles rbac_factories.UserRoleFactory(context=audit.context, role=self.roles['auditor'], person=self.users['auditor']) rbac_factories.UserRoleFactory(context=audit.program.context, role=self.roles['program_editor'], person=self.users['programeditor']) return audit def setUp(self): """Prepare data needed to run the tests""" self.api = Api() self.setup_roles() self.setup_users() self.setup_audits() self.setup_snapshots_and_issue() @data( # user_name, is_archived ('auditor', True), ('programeditor', True), ('auditor', False), ('programeditor', False), ) @unpack def test_mapping_to_issue(self, user_name, is_archived): """Test mapping snapshots to issue""" user = self.users[user_name] payload = _get_map_dict(self.snapshots[is_archived], self.issues[is_archived]) self.api.set_user(user) # Try to map to audit response = self.api.post(all_models.Relationship, payload) self.assertStatus(response, 201) rel_id = response.json['relationship']['id'] relationship = all_models.Relationship.query.filter_by( id=rel_id).first() response = self.api.delete(relationship) self.assertStatus(response, 200)
class TestWfNotifsGenerator(TestCase): """Test wf cycle tasks notifications generation.""" def setUp(self): """Set up.""" super(TestWfNotifsGenerator, self).setUp() self.api = Api() self.wf_generator = WorkflowsGenerator() self.object_generator = ObjectGenerator() Notification.query.delete() with freeze_time("2015-05-01 14:29:00"): wf_slug = "wf1" with factories.single_commit(): wf = wf_factories.WorkflowFactory(slug=wf_slug, is_verification_needed=True) task_group = wf_factories.TaskGroupFactory(workflow=wf) wf_factories.TaskGroupTaskFactory(task_group=task_group, title='task1', start_date=datetime.now(), end_date=datetime.now() + timedelta(7)) data = workflow_api.get_cycle_post_dict(wf) self.api.post(all_models.Cycle, data) wf = all_models.Workflow.query.filter_by(slug=wf_slug).one() self.cycle = all_models.Cycle.query.filter_by( workflow_id=wf.id).one() self.ctask = all_models.CycleTaskGroupObjectTask.query.filter_by( cycle_id=self.cycle.id).first() def test_ctasks_notifs_generator_daily_digest(self): """Test cycle tasks notifications generation job.""" with freeze_time("2015-05-01 14:29:00"): self.assert_notifications_for_object(self.cycle, "manual_cycle_created") self.assert_notifications_for_object(self.ctask, "manual_cycle_created", "cycle_task_due_in", "cycle_task_due_today", "cycle_task_overdue") # Move task to Finished self.wf_generator.modify_object(self.ctask, data={"status": "Verified"}) generate_cycle_tasks_notifs() self.assert_notifications_for_object(self.cycle, "all_cycle_tasks_completed", "manual_cycle_created") # Undo finish self.wf_generator.modify_object(self.ctask, data={"status": "In Progress"}) generate_cycle_tasks_notifs() self.assert_notifications_for_object(self.cycle, "manual_cycle_created") self.assert_notifications_for_object(self.ctask, "cycle_task_due_in", "cycle_task_due_today", "cycle_task_overdue") self.wf_generator.modify_object(self.ctask, data={"status": "Declined"}) self.assert_notifications_for_object(self.ctask, "cycle_task_due_in", "cycle_task_due_today", "cycle_task_overdue", "cycle_task_declined") @ddt.data(("2015-05-01 14:29:00", ("all_cycle_tasks_completed", "manual_cycle_created")), ("2015-05-02 07:29:00", ("all_cycle_tasks_completed", "manual_cycle_created")), ("2015-05-02 14:29:00", ("all_cycle_tasks_completed", "manual_cycle_created")), ("2015-05-03 07:29:00", ("manual_cycle_created", ))) @ddt.unpack def test_cycle_task_update_timelines(self, _datetime, notifications): """Test cycle task has been updated: 1) the day before job is called; 2) the same day job is called before 08:00 AM UTC; 3) the same day job is called after 08:00 AM UTC; 4) two days before job is called. """ with freeze_time("2015-05-01 14:29:00"): # Move task to Finished self.wf_generator.modify_object(self.ctask, data={"status": "Verified"}) with freeze_time(_datetime): generate_cycle_tasks_notifs() self.assert_notifications_for_object(self.cycle, *notifications) def test_ctasks_notifs_generator_daily_digest_called_twice(self): """No duplicated notifications should be generated""" with freeze_time("2015-05-01 14:29:00"): generate_cycle_tasks_notifs() self.assert_notifications_for_object(self.cycle, "manual_cycle_created") self.assert_notifications_for_object(self.ctask, "manual_cycle_created", "cycle_task_due_in", "cycle_task_due_today", "cycle_task_overdue") # Move task to Finished self.wf_generator.modify_object(self.ctask, data={"status": "Verified"}) generate_cycle_tasks_notifs() generate_cycle_tasks_notifs() self.assert_notifications_for_object(self.cycle, "all_cycle_tasks_completed", "manual_cycle_created") def test_ctasks_notifs_generator_cron_job(self): """Test cycle tasks notifications generation cron job.""" with freeze_time("2015-05-2 08:00:00"): generate_cycle_tasks_notifs() self.assert_notifications_for_object(self.cycle, "manual_cycle_created") self.assert_notifications_for_object(self.ctask, "manual_cycle_created", "cycle_task_due_in", "cycle_task_due_today", "cycle_task_overdue")
class TestCreator(TestCase): """ TestCreator """ def setUp(self): super(TestCreator, self).setUp() self.generator = Generator() self.api = Api() self.object_generator = ObjectGenerator() self.init_users() def init_users(self): """ Init users needed by the test cases """ users = [("creator", "Creator"), ("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 test_admin_page_access(self): """Permissions to admin page.""" for role, code in (("creator", 403), ("admin", 200)): self.api.set_user(self.users[role]) self.assertEqual(self.api.client.get("/admin").status_code, code) def test_creator_can_crud(self): """ Test Basic create/read,update/delete operations """ self.api.set_user(self.users["creator"]) creator_id = self.users["creator"].id audit_id = factories.AuditFactory().id all_errors = [] base_models = { "Control", "DataAsset", "Contract", "Policy", "Regulation", "Standard", "Document", "Facility", "Market", "Objective", "OrgGroup", "Vendor", "Product", "Clause", "System", "Process", "Project", "AccessGroup", "Metric", "ProductGroup", "TechnologyEnvironment" } for model_singular in base_models: try: model = get_model(model_singular) table_singular = model._inflector.table_singular table_plural = model._inflector.table_plural # Test POST creation response = self.api.post(model, { table_singular: { "title": model_singular, "context": None, "documents_reference_url": "ref", "link": "https://example.com", # ignored except for Document "contact": { "type": "Person", "id": creator_id, }, "audit": { # this is ignored on everything but Issues "id": audit_id, "type": "Audit", } }, }) if response.status_code != 201: all_errors.append("{} post creation failed {} {}".format( model_singular, response.status, response.data)) continue # Test GET when not owner obj_id = response.json.get(table_singular).get("id") response = self.api.get(model, obj_id) if response.status_code != 403: # we are not onwers yet all_errors.append( "{} can retrieve object if not owner".format( model_singular)) continue response = self.api.get_collection(model, obj_id) collection = response.json.get( "{}_collection".format(table_plural)).get(table_plural) if collection: all_errors.append( "{} can retrieve object if not owner (collection)". format(model_singular)) continue # Test GET when owner acr = all_models.AccessControlRole.query.filter_by( object_type=model_singular, name="Admin").first() factories.AccessControlListFactory(object_id=obj_id, object_type=model_singular, ac_role=acr, person_id=creator_id) response = self.api.get(model, obj_id) if response.status_code != 200: all_errors.append("{} can't GET object {}".format( model_singular, response.status)) continue # Test GET collection when owner response = self.api.get_collection(model, obj_id) collection = response.json.get( "{}_collection".format(table_plural)).get(table_plural) if not collection: all_errors.append( "{} cannot retrieve object even if owner (collection)". format(model_singular)) continue except: all_errors.append("{} exception thrown".format(model_singular)) raise self.assertEqual(all_errors, []) def test_creator_search(self): """Test if creator can see the correct object while using the search api""" self.api.set_user(self.users['admin']) self.api.post(all_models.Regulation, { "regulation": { "title": "Admin regulation", "context": None }, }) self.api.set_user(self.users['creator']) acr_id = all_models.AccessControlRole.query.filter_by( object_type="Policy", name="Admin").first().id response = self.api.post( all_models.Policy, { "policy": { "title": "Creator Policy", "context": None, "access_control_list": [ acl_helper.get_acl_json(acr_id, self.users["creator"].id) ], }, }) response.json.get("policy").get("id") response, _ = self.api.search("Regulation,Policy") entries = response.json["results"]["entries"] self.assertEqual(len(entries), 1) self.assertEqual(entries[0]["type"], "Policy") response, _ = self.api.search("Regulation,Policy", counts=True) self.assertEqual(response.json["results"]["counts"]["Policy"], 1) self.assertEqual(response.json["results"]["counts"].get("Regulation"), None) def _get_count(self, obj): """ Return the number of counts for the given object from search """ response, _ = self.api.search(obj, counts=True) return response.json["results"]["counts"].get(obj) def test_creator_should_see_users(self): """ Test if creator can see all the users in the system """ self.api.set_user(self.users['admin']) admin_count = self._get_count("Person") self.api.set_user(self.users['creator']) creator_count = self._get_count("Person") self.assertEqual(admin_count, creator_count) def test_relationships_access(self): """Check if creator cannot access relationship objects""" self.api.set_user(self.users['admin']) _, obj_0 = self.generator.generate(all_models.Regulation, "regulation", { "regulation": { "title": "Test regulation", "context": None }, }) _, obj_1 = self.generator.generate( all_models.Regulation, "regulation", { "regulation": { "title": "Test regulation 2", "context": None }, }) response, rel = self.generator.generate( all_models.Relationship, "relationship", { "relationship": { "source": { "id": obj_0.id, "type": "Regulation" }, "destination": { "id": obj_1.id, "type": "Regulation" }, "context": None }, }) relationship_id = rel.id self.assertEqual(response.status_code, 201) self.api.set_user(self.users['creator']) response = self.api.get_collection(all_models.Relationship, relationship_id) self.assertEqual(response.status_code, 200) num = len(response.json["relationships_collection"]["relationships"]) self.assertEqual(num, 0) def test_revision_access(self): """Check if creator can access the right revision objects.""" def gen(title, extra_data=None): """Generates requirement.""" requirement_content = {"title": title, "context": None} if extra_data: requirement_content.update(**extra_data) return self.generator.generate( all_models.Requirement, "requirement", {"requirement": requirement_content})[1] def check(obj, expected): """Check that how many revisions of an object current user can see.""" response = self.api.get_query( all_models.Revision, "resource_type={}&resource_id={}".format(obj.type, obj.id)) self.assertEqual(response.status_code, 200) self.assertEqual( len(response.json['revisions_collection']['revisions']), expected) self.api.set_user(self.users["admin"]) obj_1 = gen("Test Requirement 1") self.api.set_user(self.users["creator"]) acr_id = all_models.AccessControlRole.query.filter_by( object_type="Requirement", name="Admin").first().id linked_acl = { "access_control_list": [acl_helper.get_acl_json(acr_id, self.users["creator"].id)], } check(obj_1, 0) obj_2 = gen("Test Requirement 2", linked_acl) obj2_acl = obj_2.access_control_list[0] check(obj_2, 1) check(obj2_acl, 1) @ddt.data("creator", "admin") def test_count_type_in_accordion(self, glob_role): """Return count of Persons in DB for side accordion.""" self.api.set_user(self.users[glob_role]) ocordion_api_person_count_link = ( "/search?" "q=&types=Program%2CWorkflow_All%2C" "Audit%2CAssessment%2CIssue%2CRegulation%2C" "Policy%2CStandard%2CContract%2CClause%2CRequirement%2CControl%2C" "Objective%2CPerson%2COrgGroup%2CVendor%2CAccessGroup%2CSystem%2C" "Process%2CDataAsset%2CProduct%2CProject%2CFacility%2C" "Market%2CRisk%2CThreat&counts_only=true&" "extra_columns=Workflow_All%3DWorkflow%2C" "Workflow_Active%3DWorkflow%2CWorkflow_Draft%3D" "Workflow%2CWorkflow_Inactive%3DWorkflow&contact_id=1&" "extra_params=Workflow%3Astatus%3DActive%3BWorkflow_Active" "%3Astatus%3DActive%3BWorkflow_Inactive%3Astatus%3D" "Inactive%3BWorkflow_Draft%3Astatus%3DDraft") resp = self.api.client.get(ocordion_api_person_count_link) self.assertIn("Person", resp.json["results"]["counts"]) self.assertEqual(all_models.Person.query.count(), resp.json["results"]["counts"]["Person"]) @ddt.data( ("/api/revisions?resource_type={}&resource_id={}", 1), ("/api/revisions?source_type={}&source_id={}", 0), ("/api/revisions?destination_type={}&destination_id={}", 1), ) @ddt.unpack def test_changelog_access(self, link, revision_count): """Test accessing changelog under GC user who is assigned to object""" with factories.single_commit(): audit = factories.AuditFactory() asmnt = factories.AssessmentFactory(audit=audit) asmnt_id = asmnt.id factories.RelationshipFactory(source=audit, destination=asmnt) verifier_role = all_models.AccessControlRole.query.filter_by( object_type="Assessment", name="Verifiers", ).first() factories.AccessControlListFactory( person=self.users["creator"], ac_role=verifier_role, object=asmnt, ) self.api.set_user(self.users["creator"]) response = self.api.client.get(link.format("Assessment", asmnt_id)) self.assert200(response) self.assertEqual( len( response.json.get("revisions_collection", {}).get("revisions")), revision_count) @ddt.data(all_models.ControlCategory, all_models.ControlAssertion) def test_permissions_for_categories(self, category_model): """Test get collection for {0}.""" self.api.set_user(self.users["creator"]) resp = self.api.get_collection(category_model, None) plural_name = category_model._inflector.table_plural key_name = "{}_collection".format(plural_name) self.assertIn(key_name, resp.json) self.assertIn(plural_name, resp.json[key_name]) collection = resp.json[key_name][plural_name] self.assertTrue(collection, "Collection shouldn't be empty")
class TestReader(TestCase): """ Test reader role """ def setUp(self): super(TestReader, self).setUp() self.api = Api() self.object_generator = ObjectGenerator() self.init_users() def init_users(self): """ Init users needed by the test cases """ users = [("reader", "Reader"), ("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 self.users["external"] = self.object_generator.generate_person( data={"email": "*****@*****.**"})[1] def test_admin_page_access(self): """Only admin can use admin requirement""" for role, code in (("reader", 403), ("admin", 200)): self.api.set_user(self.users[role]) self.assertEqual(self.api.client.get("/admin").status_code, code) def test_reader_can_crud(self): """ Test Basic create/read,update/delete operations """ self.api.set_user(self.users["reader"]) all_errors = [] base_models = set([ "DataAsset", "Contract", "Policy", "Regulation", "Standard", "Document", "Facility", "Market", "Objective", "OrgGroup", "Vendor", "Product", "System", "Process", "Project", "AccessGroup", "Metric", "TechnologyEnvironment", "ProductGroup", "KeyReport", "AccountBalance", ]) for model_singular in base_models: try: model = get_model(model_singular) table_singular = model._inflector.table_singular table_plural = model._inflector.table_plural # Test POST creation response, _ = self.object_generator.generate_object( model, data={ table_singular: { "title": model_singular, "context": None, "documents_reference_url": "ref", "link": "https://example.com", # only for Document "contact": { "type": "Person", "id": self.users["reader"].id, } }, } ) if response.status_code != 201: all_errors.append("{} post creation failed {} {}".format( model_singular, response.status, response.data)) continue obj_id = response.json.get(table_singular).get("id") # Test GET when owner response = self.api.get(model, obj_id) if response.status_code != 200: all_errors.append("{} can't GET object {}".format( model_singular, response.status)) continue # Test GET collection when owner response = self.api.get_collection(model, obj_id) collection = response.json.get( "{}_collection".format(table_plural)).get(table_plural) if not collection: all_errors.append( "{} cannot retrieve object even if owner (collection)".format( model_singular)) continue except: all_errors.append("{} exception thrown".format(model_singular)) raise self.assertEqual(all_errors, []) def test_reader_search(self): """ Test if reader can see the correct object while using search api """ self.api.set_user(self.users['admin']) self.api.post(all_models.Regulation, { "regulation": {"title": "Admin regulation", "context": None}, }) self.api.set_user(self.users['reader']) response = self.api.post(all_models.Policy, { "policy": {"title": "reader Policy", "context": None}, }) response, _ = self.api.search("Regulation,Policy") entries = response.json["results"]["entries"] self.assertEqual(len(entries), 2) response, _ = self.api.search("Regulation,Policy", counts=True) self.assertEqual(response.json["results"]["counts"]["Policy"], 1) self.assertEqual(response.json["results"]["counts"]["Regulation"], 1) def _get_count(self, obj): """ Return the number of counts for the given object from search """ response, _ = self.api.search(obj, counts=True) return response.json["results"]["counts"].get(obj) def test_reader_should_see_users(self): """ Test if creator can see all the users in the system """ self.api.set_user(self.users['admin']) admin_count = self._get_count("Person") self.api.set_user(self.users['reader']) reader_count = self._get_count("Person") self.assertEqual(admin_count, reader_count) def test_relationships_access(self): """Check if reader can access relationship objects""" self.api.set_user(self.users['admin']) _, first_regulation = self.object_generator.generate_object( all_models.Regulation, data={"regulation": {"title": "Test regulation", "context": None}} ) _, second_regulation = self.object_generator.generate_object( all_models.Regulation, data={"regulation": {"title": "Test regulation 2", "context": None}} ) response, rel = self.object_generator.generate_relationship( first_regulation, second_regulation ) self.assertStatus(response, 201) self.api.set_user(self.users['reader']) response = self.api.get_collection(all_models.Relationship, rel.id) self.assert200(response) num = len(response.json["relationships_collection"]["relationships"]) self.assertEqual(num, 1) def test_creation_of_mappings(self): """Check if reader can't create mappings""" self.object_generator.api.set_user(self.users["external"]) _, control = self.object_generator.generate_object( all_models.Control, data={"control": {"title": "Test Control", "context": None}} ) self.object_generator.api.set_user(self.users['reader']) _, program = self.object_generator.generate_object( all_models.Program, data={"program": {"title": "Test Program", "context": None}} ) response, _ = self.object_generator.generate_relationship( control, program, program.context ) self.assert403(response) @ddt.data("creator", "reader") def test_unmap_people(self, user_role): """Test that global reader/creator can't unmap people from program""" user = self.users[user_role] with factories.single_commit(): program = factories.ProgramFactory() mapped_person = factories.ObjectPersonFactory( personable=program, person=user, context=program.context ) self.api.set_user(user) db.session.add(mapped_person) response = self.api.delete(mapped_person) self.assert403(response) def test_read_evidence_revision(self): """Global Read can read Evidence revision content""" user = self.users["reader"] link = "google.com" evidence = factories.EvidenceUrlFactory(link=link) evidence_id = evidence.id self.api.set_user(user) resp = self.api.client.get( "/api/revisions?resource_type={}&resource_id={}".format(evidence.type, evidence_id)) rev_content = resp.json["revisions_collection"]["revisions"][0]["content"] self.assertTrue(rev_content) self.assertEquals(link, rev_content["link"])
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)
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, [])
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) @ddt.data( { "name": "Test 1", "update": False, "read": False, "delete": True }, { "name": "Test 2", "update": True, "read": False, "delete": False }, { "name": "Test 3", "update": True, "read": False, "delete": True }, ) @ddt.unpack def test_create_with_wrong_options(self, name, update, read, delete): """ Test if user create ACR with wrong options.""" options = [{ 'access_control_role': { 'modal_title': 'Add Custom Role to type Regulation', 'object_type': 'Regulation', 'parent_type': 'Regulation', 'context': { 'id': None }, 'delete': delete, 'update': update, 'read': read, 'name': name } }] response = self.api.post(AccessControlRole, options) self.assert400(response)
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)
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)
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"])
class TestCreator(TestCase): """ TestCreator """ def setUp(self): super(TestCreator, self).setUp() self.generator = Generator() self.api = Api() self.object_generator = ObjectGenerator() self.init_users() def init_users(self): """ Init users needed by the test cases """ users = [("creator", "Creator"), ("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 test_admin_page_access(self): for role, code in (("creator", 403), ("admin", 200)): self.api.set_user(self.users[role]) self.assertEqual(self.api.client.get("/admin").status_code, code) def test_creator_can_crud(self): """ Test Basic create/read,update/delete operations """ self.api.set_user(self.users["creator"]) audit_id = factories.AuditFactory().id all_errors = [] base_models = set([ "Control", "DataAsset", "Contract", "Policy", "Regulation", "Standard", "Document", "Facility", "Market", "Objective", "OrgGroup", "Vendor", "Product", "Clause", "System", "Process", "Project", "AccessGroup" ]) for model_singular in base_models: try: model = get_model(model_singular) table_singular = model._inflector.table_singular table_plural = model._inflector.table_plural # Test POST creation response = self.api.post(model, { table_singular: { "title": model_singular, "context": None, "reference_url": "ref", "contact": { "type": "Person", "id": self.users["creator"].id, }, "audit": { # this is ignored on everything but Issues "id": audit_id, "type": "Audit", } }, }) if response.status_code != 201: all_errors.append("{} post creation failed {} {}".format( model_singular, response.status, response.data)) continue # Test GET when not owner obj_id = response.json.get(table_singular).get("id") response = self.api.get(model, obj_id) if response.status_code != 403: # we are not onwers yet all_errors.append( "{} can retrieve object if not owner".format(model_singular)) continue response = self.api.get_collection(model, obj_id) collection = response.json.get( "{}_collection".format(table_plural)).get(table_plural) if len(collection) != 0: all_errors.append( "{} can retrieve object if not owner (collection)" .format(model_singular)) continue # Become an owner response = self.api.post(all_models.ObjectOwner, {"object_owner": { "person": { "id": self.users['creator'].id, "type": "Person", }, "ownable": { "type": model_singular, "id": obj_id }, "context": None}}) if response.status_code != 201: all_errors.append("{} can't create owner {}.".format( model_singular, response.status)) continue # Test GET when owner response = self.api.get(model, obj_id) if response.status_code != 200: all_errors.append("{} can't GET object {}".format( model_singular, response.status)) continue # Test GET collection when owner response = self.api.get_collection(model, obj_id) collection = response.json.get( "{}_collection".format(table_plural)).get(table_plural) if len(collection) == 0: all_errors.append( "{} cannot retrieve object even if owner (collection)" .format(model_singular)) continue except: all_errors.append("{} exception thrown".format(model_singular)) raise self.assertEqual(all_errors, []) def test_creator_search(self): """Test if creator can see the correct object while using the search api""" self.api.set_user(self.users['admin']) self.api.post(all_models.Regulation, { "regulation": {"title": "Admin regulation", "context": None}, }) self.api.set_user(self.users['creator']) response = self.api.post(all_models.Policy, { "policy": {"title": "Creator Policy", "context": None}, }) obj_id = response.json.get("policy").get("id") self.api.post(all_models.ObjectOwner, {"object_owner": { "person": { "id": self.users['creator'].id, "type": "Person", }, "ownable": { "type": "Policy", "id": obj_id, }, "context": None}}) response, _ = self.api.search("Regulation,Policy") entries = response.json["results"]["entries"] self.assertEqual(len(entries), 1) self.assertEqual(entries[0]["type"], "Policy") response, _ = self.api.search("Regulation,Policy", counts=True) self.assertEqual(response.json["results"]["counts"]["Policy"], 1) self.assertEqual( response.json["results"]["counts"].get("Regulation"), None) def _get_count(self, obj): """ Return the number of counts for the given object from search """ response, _ = self.api.search(obj, counts=True) return response.json["results"]["counts"].get(obj) def test_creator_should_see_users(self): """ Test if creator can see all the users in the system """ self.api.set_user(self.users['admin']) admin_count = self._get_count("Person") self.api.set_user(self.users['creator']) creator_count = self._get_count("Person") self.assertEqual(admin_count, creator_count) def test_creator_cannot_be_owner(self): """Test if creator cannot become owner of the object he has not created""" self.api.set_user(self.users['admin']) _, obj = self.generator.generate(all_models.Regulation, "regulation", { "regulation": {"title": "Test regulation", "context": None}, }) self.api.set_user(self.users['creator']) response = self.api.post(all_models.ObjectOwner, {"object_owner": { "person": { "id": self.users['creator'].id, "type": "Person", }, "ownable": { "type": "Regulation", "id": obj.id, }, "context": None}}) self.assertEqual(response.status_code, 403) def test_relationships_access(self): """Check if creator cannot access relationship objects""" self.api.set_user(self.users['admin']) _, obj_0 = self.generator.generate(all_models.Regulation, "regulation", { "regulation": {"title": "Test regulation", "context": None}, }) _, obj_1 = self.generator.generate(all_models.Regulation, "regulation", { "regulation": {"title": "Test regulation 2", "context": None}, }) response, rel = self.generator.generate( all_models.Relationship, "relationship", { "relationship": {"source": { "id": obj_0.id, "type": "Regulation" }, "destination": { "id": obj_1.id, "type": "Regulation" }, "context": None}, } ) relationship_id = rel.id self.assertEqual(response.status_code, 201) self.api.set_user(self.users['creator']) response = self.api.get_collection(all_models.Relationship, relationship_id) self.assertEqual(response.status_code, 200) num = len(response.json["relationships_collection"]["relationships"]) self.assertEqual(num, 0) def test_revision_access(self): """Check if creator can access the right revision objects.""" def gen(title): return self.generator.generate(all_models.Section, "section", { "section": {"title": title, "context": None}, })[1] def check(obj, expected): """Check that how many revisions of an object current user can see.""" response = self.api.get_query( all_models.Revision, "resource_type={}&resource_id={}".format(obj.type, obj.id) ) self.assertEqual(response.status_code, 200) self.assertEqual( len(response.json['revisions_collection']['revisions']), expected ) self.api.set_user(self.users["admin"]) obj_1 = gen("Test Section 1") obj_2 = gen("Test Section 2") self.api.post(all_models.ObjectOwner, {"object_owner": { "person": { "id": self.users['creator'].id, "type": "Person", }, "ownable": { "type": "Section", "id": obj_2.id, }, "context": None}}) self.api.set_user(self.users["creator"]) check(obj_1, 0) check(obj_2, 2)
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)
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, [])
class TestWorkflowsApiPost(TestCase): def setUp(self): TestCase.setUp(self) 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(Workflow, data) self.assert400(response) def test_create_one_time_workflows(self): data = self.get_workflow_dict() response = self.api.post(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(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(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(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(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(Workflow, wf_data) wf = wf_response.json["workflow"] data = self.get_task_group_dict(wf) response = self.api.post(TaskGroup, data) self.assertEqual(response.status_code, 201) # TODO: Api should be able to handle invalid data @SkipTest def test_create_task_group_invalid_workflow_data(self): wf = {"id": -1, "context": {"id": -1}} data = self.get_task_group_dict(wf) response = self.api.post(TaskGroup, data) self.assert400(response) def get_workflow_dict(self): 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": "", "owners": None, "context": None, } } return data def get_task_group_dict(self, wf): data = { "task_group": { "custom_attribute_definitions": [], "custom_attributes": {}, "_transient": {}, "contact": { "id": 1, "href": "/api/people/1", "type": "Person" }, "workflow": { "id": wf["id"], "href": "/api/workflows/%d" % wf["id"], "type": "Workflow" }, "context": { "id": wf["context"]["id"], "href": "/api/contexts/%d" % wf["context"]["id"], "type": "Context" }, "modal_title": "Create Task Group", "title": "Create_task_group", "description": "", } } return data
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)
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)
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)
class TestCommentsForProposals(TestCase): """Test generate comments for proposals.""" def setUp(self): super(TestCommentsForProposals, self).setUp() self.api = Api() self.client.get("/login") TMPLS = all_models.Proposal.CommentTemplatesTextBuilder @ddt.data({"agenda": "", "comment": TMPLS.PROPOSED_WITHOUT_AGENDA}, {"agenda": "tmp", "comment": TMPLS.PROPOSED_WITH_AGENDA.format(text="tmp")}, {"agenda": "bla", "comment": TMPLS.PROPOSED_WITH_AGENDA.format(text="bla")}, {"agenda": "<p>bla</p>", "comment": TMPLS.PROPOSED_WITH_AGENDA.format(text="bla")}) @ddt.unpack def test_create(self, agenda, comment): """Test case create proposal with agenda {agenda}.""" risk = factories.RiskFactory() risk_id = risk.id self.assertEqual(0, len(risk.comments)) resp = self.api.post( all_models.Proposal, {"proposal": { "instance": { "id": risk.id, "type": risk.type, }, # "content": {"123": 123}, "full_instance_content": risk.log_json(), "agenda": agenda, "context": None, }}) self.assertEqual(201, resp.status_code) risk = all_models.Risk.query.get(risk_id) self.assertEqual(1, len(risk.comments)) self.assertEqual(comment, risk.comments[0].description) @ddt.data({"agenda": "", "comment_agenda": "", "status": all_models.Proposal.STATES.APPLIED, "tmpl": TMPLS.APPLIED_WITHOUT_COMMENT}, {"agenda": "tmp", "comment_agenda": "tmp", "status": all_models.Proposal.STATES.APPLIED, "tmpl": TMPLS.APPLIED_WITH_COMMENT}, {"agenda": "<p>tmp</p>", "comment_agenda": "tmp", "status": all_models.Proposal.STATES.APPLIED, "tmpl": TMPLS.APPLIED_WITH_COMMENT}, {"agenda": "<p> </p>", "comment_agenda": "", "status": all_models.Proposal.STATES.APPLIED, "tmpl": TMPLS.APPLIED_WITHOUT_COMMENT}, {"agenda": "bla", "comment_agenda": "bla", "status": all_models.Proposal.STATES.APPLIED, "tmpl": TMPLS.APPLIED_WITH_COMMENT}, {"agenda": "tmp", "comment_agenda": "tmp", "status": all_models.Proposal.STATES.DECLINED, "tmpl": TMPLS.DECLINED_WITH_COMMENT}, {"agenda": " <p>tmp</p> ", "comment_agenda": "tmp", "status": all_models.Proposal.STATES.DECLINED, "tmpl": TMPLS.DECLINED_WITH_COMMENT}, {"agenda": "BLa", "comment_agenda": "BLa", "status": all_models.Proposal.STATES.DECLINED, "tmpl": TMPLS.DECLINED_WITH_COMMENT}, {"agenda": "", "comment_agenda": "", "status": all_models.Proposal.STATES.DECLINED, "tmpl": TMPLS.DECLINED_WITHOUT_COMMENT}) @ddt.unpack def test_change_status(self, agenda, comment_agenda, status, tmpl): """Test comment proposal status move to {status} with agenda {agenda}.""" test_email = "*****@*****.**" with factories.single_commit(): risk = factories.RiskFactory() proposer = factories.PersonFactory(email=test_email) with factories.single_commit(): proposal = factories.ProposalFactory( instance=risk, content={"field": "a"}, agenda="agenda content", proposed_by=proposer) risk_id = risk.id if status == all_models.Proposal.STATES.APPLIED: resp = self.api.put( proposal, {"proposal": {"status": status, "apply_reason": agenda}}) else: resp = self.api.put( proposal, {"proposal": {"status": status, "decline_reason": agenda}}) self.assertEqual(200, resp.status_code) risk = all_models.Risk.query.get(risk_id) self.assertEqual(1, len(risk.comments)) comment = tmpl.format(user=test_email, text=comment_agenda) self.assertEqual(comment, risk.comments[0].description)
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()} factories.AccessControlRoleFactory( name="ACL_Reader", object_type="Risk", update=0 ) factories.AccessControlRoleFactory( name="ACL_Editor", object_type="Risk" ) factories.AccessControlRoleFactory( name="ACL_Nobody", object_type="Risk", read=0, update=0, delete=0, ) with factories.single_commit(): self.risk = factories.RiskFactory() self.program = factories.ProgramFactory() self.program.context.related_object = self.program self.relationship = factories.RelationshipFactory( source=self.program, destination=self.risk, 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.AccessControlPersonFactory( ac_list=self.program.acr_name_acl_map[role_name], person=person, ) self.proposal = factories.ProposalFactory( instance=self.risk, content={ "access_control_list": {}, "custom_attribute_values": {}, "fields": {}, "mapping_fields": {}, "mapping_list_fields": {}, } ) factories.RelationshipFactory( source=self.risk, destination=self.proposal, ) for role_name in ["ACL_Reader", "ACL_Editor", "ACL_Nobody"]: person = self.people[role_name] rbac_factories.UserRoleFactory(role=roles["Creator"], person=person) factories.AccessControlPersonFactory( ac_list=self.risk.acr_name_acl_map[role_name], person=person, ) @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), {"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.risk.id, "type": self.risk.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), # Following two tests have been commented out as the functionality for # custom role propagation has been temporarily removed This test should # be enabled back in scope of ticket GGRC-4991 # ("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 """ risk_id = self.risk.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.risk.type, }, "op": {"name": "AND"}, "right": { "left": "instance_id", "op": {"name": "="}, "right": risk_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"])