diff --git a/spiffworkflow-backend/poetry.lock b/spiffworkflow-backend/poetry.lock index ee51f2ace..a67570020 100644 --- a/spiffworkflow-backend/poetry.lock +++ b/spiffworkflow-backend/poetry.lock @@ -1651,44 +1651,44 @@ yaml = ["pyyaml"] [[package]] name = "mypy" -version = "1.10.1" +version = "1.11.1" description = "Optional static typing for Python" optional = false python-versions = ">=3.8" files = [ - {file = "mypy-1.10.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e36f229acfe250dc660790840916eb49726c928e8ce10fbdf90715090fe4ae02"}, - {file = "mypy-1.10.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:51a46974340baaa4145363b9e051812a2446cf583dfaeba124af966fa44593f7"}, - {file = "mypy-1.10.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:901c89c2d67bba57aaaca91ccdb659aa3a312de67f23b9dfb059727cce2e2e0a"}, - {file = "mypy-1.10.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:0cd62192a4a32b77ceb31272d9e74d23cd88c8060c34d1d3622db3267679a5d9"}, - {file = "mypy-1.10.1-cp310-cp310-win_amd64.whl", hash = "sha256:a2cbc68cb9e943ac0814c13e2452d2046c2f2b23ff0278e26599224cf164e78d"}, - {file = "mypy-1.10.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:bd6f629b67bb43dc0d9211ee98b96d8dabc97b1ad38b9b25f5e4c4d7569a0c6a"}, - {file = "mypy-1.10.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:a1bbb3a6f5ff319d2b9d40b4080d46cd639abe3516d5a62c070cf0114a457d84"}, - {file = "mypy-1.10.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b8edd4e9bbbc9d7b79502eb9592cab808585516ae1bcc1446eb9122656c6066f"}, - {file = "mypy-1.10.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:6166a88b15f1759f94a46fa474c7b1b05d134b1b61fca627dd7335454cc9aa6b"}, - {file = "mypy-1.10.1-cp311-cp311-win_amd64.whl", hash = "sha256:5bb9cd11c01c8606a9d0b83ffa91d0b236a0e91bc4126d9ba9ce62906ada868e"}, - {file = "mypy-1.10.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:d8681909f7b44d0b7b86e653ca152d6dff0eb5eb41694e163c6092124f8246d7"}, - {file = "mypy-1.10.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:378c03f53f10bbdd55ca94e46ec3ba255279706a6aacaecac52ad248f98205d3"}, - {file = "mypy-1.10.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6bacf8f3a3d7d849f40ca6caea5c055122efe70e81480c8328ad29c55c69e93e"}, - {file = "mypy-1.10.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:701b5f71413f1e9855566a34d6e9d12624e9e0a8818a5704d74d6b0402e66c04"}, - {file = "mypy-1.10.1-cp312-cp312-win_amd64.whl", hash = "sha256:3c4c2992f6ea46ff7fce0072642cfb62af7a2484efe69017ed8b095f7b39ef31"}, - {file = "mypy-1.10.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:604282c886497645ffb87b8f35a57ec773a4a2721161e709a4422c1636ddde5c"}, - {file = "mypy-1.10.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:37fd87cab83f09842653f08de066ee68f1182b9b5282e4634cdb4b407266bade"}, - {file = "mypy-1.10.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8addf6313777dbb92e9564c5d32ec122bf2c6c39d683ea64de6a1fd98b90fe37"}, - {file = "mypy-1.10.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:5cc3ca0a244eb9a5249c7c583ad9a7e881aa5d7b73c35652296ddcdb33b2b9c7"}, - {file = "mypy-1.10.1-cp38-cp38-win_amd64.whl", hash = "sha256:1b3a2ffce52cc4dbaeee4df762f20a2905aa171ef157b82192f2e2f368eec05d"}, - {file = "mypy-1.10.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:fe85ed6836165d52ae8b88f99527d3d1b2362e0cb90b005409b8bed90e9059b3"}, - {file = "mypy-1.10.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:c2ae450d60d7d020d67ab440c6e3fae375809988119817214440033f26ddf7bf"}, - {file = "mypy-1.10.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6be84c06e6abd72f960ba9a71561c14137a583093ffcf9bbfaf5e613d63fa531"}, - {file = "mypy-1.10.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:2189ff1e39db399f08205e22a797383613ce1cb0cb3b13d8bcf0170e45b96cc3"}, - {file = "mypy-1.10.1-cp39-cp39-win_amd64.whl", hash = "sha256:97a131ee36ac37ce9581f4220311247ab6cba896b4395b9c87af0675a13a755f"}, - {file = "mypy-1.10.1-py3-none-any.whl", hash = "sha256:71d8ac0b906354ebda8ef1673e5fde785936ac1f29ff6987c7483cfbd5a4235a"}, - {file = "mypy-1.10.1.tar.gz", hash = "sha256:1f8f492d7db9e3593ef42d4f115f04e556130f2819ad33ab84551403e97dd4c0"}, + {file = "mypy-1.11.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:a32fc80b63de4b5b3e65f4be82b4cfa362a46702672aa6a0f443b4689af7008c"}, + {file = "mypy-1.11.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c1952f5ea8a5a959b05ed5f16452fddadbaae48b5d39235ab4c3fc444d5fd411"}, + {file = "mypy-1.11.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e1e30dc3bfa4e157e53c1d17a0dad20f89dc433393e7702b813c10e200843b03"}, + {file = "mypy-1.11.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:2c63350af88f43a66d3dfeeeb8d77af34a4f07d760b9eb3a8697f0386c7590b4"}, + {file = "mypy-1.11.1-cp310-cp310-win_amd64.whl", hash = "sha256:a831671bad47186603872a3abc19634f3011d7f83b083762c942442d51c58d58"}, + {file = "mypy-1.11.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:7b6343d338390bb946d449677726edf60102a1c96079b4f002dedff375953fc5"}, + {file = "mypy-1.11.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:e4fe9f4e5e521b458d8feb52547f4bade7ef8c93238dfb5bbc790d9ff2d770ca"}, + {file = "mypy-1.11.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:886c9dbecc87b9516eff294541bf7f3655722bf22bb898ee06985cd7269898de"}, + {file = "mypy-1.11.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:fca4a60e1dd9fd0193ae0067eaeeb962f2d79e0d9f0f66223a0682f26ffcc809"}, + {file = "mypy-1.11.1-cp311-cp311-win_amd64.whl", hash = "sha256:0bd53faf56de9643336aeea1c925012837432b5faf1701ccca7fde70166ccf72"}, + {file = "mypy-1.11.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:f39918a50f74dc5969807dcfaecafa804fa7f90c9d60506835036cc1bc891dc8"}, + {file = "mypy-1.11.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:0bc71d1fb27a428139dd78621953effe0d208aed9857cb08d002280b0422003a"}, + {file = "mypy-1.11.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b868d3bcff720dd7217c383474008ddabaf048fad8d78ed948bb4b624870a417"}, + {file = "mypy-1.11.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:a707ec1527ffcdd1c784d0924bf5cb15cd7f22683b919668a04d2b9c34549d2e"}, + {file = "mypy-1.11.1-cp312-cp312-win_amd64.whl", hash = "sha256:64f4a90e3ea07f590c5bcf9029035cf0efeae5ba8be511a8caada1a4893f5525"}, + {file = "mypy-1.11.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:749fd3213916f1751fff995fccf20c6195cae941dc968f3aaadf9bb4e430e5a2"}, + {file = "mypy-1.11.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:b639dce63a0b19085213ec5fdd8cffd1d81988f47a2dec7100e93564f3e8fb3b"}, + {file = "mypy-1.11.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4c956b49c5d865394d62941b109728c5c596a415e9c5b2be663dd26a1ff07bc0"}, + {file = "mypy-1.11.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:45df906e8b6804ef4b666af29a87ad9f5921aad091c79cc38e12198e220beabd"}, + {file = "mypy-1.11.1-cp38-cp38-win_amd64.whl", hash = "sha256:d44be7551689d9d47b7abc27c71257adfdb53f03880841a5db15ddb22dc63edb"}, + {file = "mypy-1.11.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:2684d3f693073ab89d76da8e3921883019ea8a3ec20fa5d8ecca6a2db4c54bbe"}, + {file = "mypy-1.11.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:79c07eb282cb457473add5052b63925e5cc97dfab9812ee65a7c7ab5e3cb551c"}, + {file = "mypy-1.11.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:11965c2f571ded6239977b14deebd3f4c3abd9a92398712d6da3a772974fad69"}, + {file = "mypy-1.11.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:a2b43895a0f8154df6519706d9bca8280cda52d3d9d1514b2d9c3e26792a0b74"}, + {file = "mypy-1.11.1-cp39-cp39-win_amd64.whl", hash = "sha256:1a81cf05975fd61aec5ae16501a091cfb9f605dc3e3c878c0da32f250b74760b"}, + {file = "mypy-1.11.1-py3-none-any.whl", hash = "sha256:0624bdb940255d2dd24e829d99a13cfeb72e4e9031f9492148f410ed30bcab54"}, + {file = "mypy-1.11.1.tar.gz", hash = "sha256:f404a0b069709f18bbdb702eb3dcfe51910602995de00bd39cea3050b5772d08"}, ] [package.dependencies] mypy-extensions = ">=1.0.0" tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} -typing-extensions = ">=4.1.0" +typing-extensions = ">=4.6.0" [package.extras] dmypy = ["psutil (>=4.0)"] diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/services/authorization_service.py b/spiffworkflow-backend/src/spiffworkflow_backend/services/authorization_service.py index 7b7de044c..a11a5361a 100644 --- a/spiffworkflow-backend/src/spiffworkflow_backend/services/authorization_service.py +++ b/spiffworkflow-backend/src/spiffworkflow_backend/services/authorization_service.py @@ -468,6 +468,7 @@ class AuthorizationService: desired_group_identifiers = None if current_app.config["SPIFFWORKFLOW_BACKEND_OPEN_ID_IS_AUTHORITY_FOR_USER_GROUPS"]: + desired_group_identifiers = [] if "groups" in user_info: desired_group_identifiers = user_info["groups"] @@ -502,7 +503,7 @@ class AuthorizationService: if desired_group_identifiers is not None: if not isinstance(desired_group_identifiers, list): - current_app.logger.error( + current_app.logger.error( # type: ignore f"Invalid groups property in token: {desired_group_identifiers}.If groups is specified, it must be a list" ) else: diff --git a/spiffworkflow-backend/src/spiffworkflow_backend/services/user_service.py b/spiffworkflow-backend/src/spiffworkflow_backend/services/user_service.py index 72fa6ecca..8cc4837e4 100644 --- a/spiffworkflow-backend/src/spiffworkflow_backend/services/user_service.py +++ b/spiffworkflow-backend/src/spiffworkflow_backend/services/user_service.py @@ -4,6 +4,7 @@ from typing import Any from flask import current_app from flask import g from sqlalchemy import and_ +from sqlalchemy import or_ from spiffworkflow_backend.exceptions.api_error import ApiError from spiffworkflow_backend.interfaces import UserToGroupDict @@ -183,7 +184,22 @@ class UserService: def update_human_task_assignments_for_user(cls, user: UserModel, new_group_ids: set[int], old_group_ids: set[int]) -> None: current_assignments = HumanTaskUserModel.query.filter_by(user_id=user.id).all() current_human_task_ids = [ca.human_task_id for ca in current_assignments] - human_tasks = HumanTaskModel.query.filter(HumanTaskModel.lane_assignment_id.in_(new_group_ids)).all() # type: ignore + human_tasks = ( + HumanTaskModel.query.outerjoin(HumanTaskUserModel) + .filter( + HumanTaskModel.lane_assignment_id.in_(new_group_ids), # type: ignore + HumanTaskModel.completed == False, # noqa: E712 + or_( + and_( + HumanTaskUserModel.user_id != user.id, + HumanTaskUserModel.added_by == HumanTaskUserAddedBy.lane_assignment.value, + ), + HumanTaskUserModel.user_id == None, # noqa: E711 + ), + ) + .all() + ) + for human_task in human_tasks: if human_task.id not in current_human_task_ids: human_task_user = HumanTaskUserModel( @@ -196,6 +212,7 @@ class UserService: HumanTaskUserModel.user_id == user.id, HumanTaskUserModel.added_by == HumanTaskUserAddedBy.lane_assignment.value, HumanTaskModel.lane_assignment_id.in_(old_group_ids), # type: ignore + HumanTaskModel.completed == False, # noqa: E712 ) .all() ) diff --git a/spiffworkflow-backend/tests/data/model_with_lanes/lanes_with_extra_tasks.bpmn b/spiffworkflow-backend/tests/data/model_with_lanes/lanes_with_extra_tasks.bpmn new file mode 100644 index 000000000..cec189025 --- /dev/null +++ b/spiffworkflow-backend/tests/data/model_with_lanes/lanes_with_extra_tasks.bpmn @@ -0,0 +1,114 @@ + + + + + + + + + StartEvent_1 + initiator_one + Event_06f4e68 + initiator_two + + + finance_approval + finance_confirmation + + + + Flow_1tbyols + + + + + + This is for the initiator user + + Flow_1tbyols + Flow_16ppta1 + + + Flow_0x92f7d + + + + + This is initiator again + + + Flow_1q487da + Flow_0x92f7d + + + + This is for a Finance Team user + + Flow_16ppta1 + Flow_0rug230 + + + + + Flow_0rug230 + Flow_1q487da + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/spiffworkflow-backend/tests/data/model_with_lanes/lanes_with_lane_owners.bpmn b/spiffworkflow-backend/tests/data/model_with_lanes/lanes_with_lane_owners.bpmn new file mode 100644 index 000000000..f70a288d9 --- /dev/null +++ b/spiffworkflow-backend/tests/data/model_with_lanes/lanes_with_lane_owners.bpmn @@ -0,0 +1,266 @@ + + + + + + + + + Activity_GetMoreData + Gateway_1chm5qn + Event_09rkmlh + Event_0t6257a + Activity_Review + Event_12mvls1 + Event_0er19np + Gateway_1y0gjbb + Gateway_1v7vc1z + + + StartEvent_1 + Activity_GetData + Activity_GetReviewers + Gateway_0l5x84t + + + + + Flow_1msk5h9 + + + + + + + + + Flow_1loqmjv + Flow_0rydrx7 + Flow_0c8hg1n + + + Flow_1msk5h9 + Flow_1loqmjv + #lane_owners = {"Reviewer": ["core.contributor@status.im", "madhurya@sartography.com"]} + +current_approver_role = "/Infra" +sme_group_members = get_group_members(current_approver_role) +lane_owners = {"/Infra": sme_group_members} + + + + + + + + + + data = {"item_id": itemId, "item_desc": itemName} + User entered data : {{data}} + + Flow_0c8hg1n + Flow_1xtmg0t + + + Flow_10rvmdc + Flow_1efovhl + Flow_19tmelr + Flow_0l3zw86 + + + + Flow_0q4r19g + + + approval == "Approve" + + + Flow_1efovhl + Flow_05xruvm + + + approval == "Reject" + + + + ## Approve +### Data +{{ data }} + + + + + + Flow_1xtmg0t + Flow_10rvmdc + + + Flow_0l3zw86 + Flow_12ty548 + + + + Flow_19tmelr + Flow_0zg89xt + + + + + + + Flow_05xruvm + Flow_0zg89xt + Flow_12ty548 + Flow_1ohb8ht + + + Flow_1ohb8ht + Flow_042vla2 + Flow_0q4r19g + + + Flow_042vla2 + Flow_0rydrx7 + + + approval == "Needmoreinfo" + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/spiffworkflow-backend/tests/spiffworkflow_backend/helpers/base_test.py b/spiffworkflow-backend/tests/spiffworkflow_backend/helpers/base_test.py index 31ca36386..7e41c9d2e 100644 --- a/spiffworkflow-backend/tests/spiffworkflow_backend/helpers/base_test.py +++ b/spiffworkflow-backend/tests/spiffworkflow_backend/helpers/base_test.py @@ -603,14 +603,17 @@ class BaseTest: processor: ProcessInstanceProcessor, execution_mode: str | None = None, data: dict | None = None, + user: UserModel | None = None, ) -> None: user_task = processor.get_ready_user_tasks()[0] human_task = HumanTaskModel.query.filter_by(task_guid=str(user_task.id)).first() + if user is None: + user = processor.process_instance_model.process_initiator ProcessInstanceService.complete_form_task( processor=processor, spiff_task=user_task, data=data or {}, - user=processor.process_instance_model.process_initiator, + user=user, human_task=human_task, execution_mode=execution_mode, ) diff --git a/spiffworkflow-backend/tests/spiffworkflow_backend/unit/test_authorization_service.py b/spiffworkflow-backend/tests/spiffworkflow_backend/unit/test_authorization_service.py index c13536cf9..7da253b31 100644 --- a/spiffworkflow-backend/tests/spiffworkflow_backend/unit/test_authorization_service.py +++ b/spiffworkflow-backend/tests/spiffworkflow_backend/unit/test_authorization_service.py @@ -3,6 +3,7 @@ from flask import Flask from flask.testing import FlaskClient from spiffworkflow_backend.exceptions.error import InvalidPermissionError from spiffworkflow_backend.models.group import GroupModel +from spiffworkflow_backend.models.human_task import HumanTaskModel from spiffworkflow_backend.models.human_task_user import HumanTaskUserModel from spiffworkflow_backend.models.user_group_assignment_waiting import UserGroupAssignmentWaitingModel from spiffworkflow_backend.services.authorization_service import AuthorizationService @@ -733,3 +734,128 @@ class TestAuthorizationService(BaseTest): assert len(human_task_users) == 0 tmp_group = GroupModel.query.filter_by(identifier="tmp_group").first() assert tmp_group is not None + + ##### run test again but this time without the groups key at all to remove groups + user_two = AuthorizationService.create_user_from_sign_in( + { + "username": "user_two", + "sub": "user_two", + "iss": "https://test.stuff", + "email": "user_two@example.com", + "groups": ["Finance Team", "tmp_group"], + } + ) + assert len(user_two.groups) == 3 + assert sorted([g.identifier for g in user_two.groups]) == sorted(["Finance Team", "everybody", "tmp_group"]) + tmp_group = GroupModel.query.filter_by(identifier="tmp_group").first() + assert tmp_group is not None + assert tmp_group.source_is_open_id is True + human_task_users = HumanTaskUserModel.query.filter_by(user_id=user_two.id).all() + assert len(human_task_users) == 1 + user_two = AuthorizationService.create_user_from_sign_in( + { + "username": "user_two", + "sub": "user_two", + "iss": "https://test.stuff", + "email": "user_two@example.com", + } + ) + assert len(user_two.groups) == 1 + assert user_two.groups[0].identifier == "everybody" + human_task_users = HumanTaskUserModel.query.filter_by(user_id=user_two.id).all() + assert len(human_task_users) == 0 + tmp_group = GroupModel.query.filter_by(identifier="tmp_group").first() + assert tmp_group is not None + + def test_user_can_complete_all_tasks_after_assignment( + self, + app: Flask, + client: FlaskClient, + with_db_and_bpmn_file_cleanup: None, + ) -> None: + process_model = load_test_spec( + process_model_id="test_group/model_with_lanes", + process_model_source_directory="model_with_lanes", + bpmn_file_name="lanes_with_extra_tasks.bpmn", + ) + process_instance = self.create_process_instance_from_process_model(process_model) + user_one = self.find_or_create_user(username="user_one") + user_group = UserService.find_or_create_group("Finance Team") + UserService.add_user_to_group(user_one, user_group) + processor = ProcessInstanceProcessor(process_instance) + processor.do_engine_steps(save=True) + self.complete_next_manual_task(processor, data={"itemId": "item1", "itemName": "Item One"}) + + with self.app_config_mock(app, "SPIFFWORKFLOW_BACKEND_OPEN_ID_IS_AUTHORITY_FOR_USER_GROUPS", True): + user_two = AuthorizationService.create_user_from_sign_in( + { + "username": "user_two", + "sub": "user_two", + "iss": "https://test.stuff", + "email": "user_two@example.com", + "groups": ["Finance Team"], + } + ) + human_task_users = ( + HumanTaskUserModel.query.filter_by(user_id=user_two.id) + .join(HumanTaskModel) + .filter(HumanTaskModel.completed == False) # noqa: E712 + .all() + ) + assert len(human_task_users) == 1 + self.complete_next_manual_task(processor, user=user_two) + human_task_users = ( + HumanTaskUserModel.query.filter_by(user_id=user_two.id) + .join(HumanTaskModel) + .filter(HumanTaskModel.completed == False) # noqa: E712 + .all() + ) + assert len(human_task_users) == 1 + self.complete_next_manual_task(processor, user=user_two) + + user_two = AuthorizationService.create_user_from_sign_in( + { + "username": "user_two", + "sub": "user_two", + "iss": "https://test.stuff", + "email": "user_two@example.com", + } + ) + human_task_count = ( + HumanTaskUserModel.query.join(HumanTaskModel) + .filter(HumanTaskUserModel.user_id == user_two.id, HumanTaskModel.completed == True) # noqa: E712 + .count() + ) + assert human_task_count == 2 + + def test_user_can_is_not_assigned_task_if_lane_owners_in_use( + self, + app: Flask, + client: FlaskClient, + with_db_and_bpmn_file_cleanup: None, + ) -> None: + process_model = load_test_spec( + process_model_id="test_group/model_with_lanes", + process_model_source_directory="model_with_lanes", + bpmn_file_name="lanes_with_lane_owners.bpmn", + ) + process_instance = self.create_process_instance_from_process_model(process_model) + user_one = self.find_or_create_user(username="user_one") + user_group = UserService.find_or_create_group("/Infra") + UserService.add_user_to_group(user_one, user_group) + processor = ProcessInstanceProcessor(process_instance) + processor.do_engine_steps(save=True) + self.complete_next_manual_task(processor, data={"itemId": "item1", "itemName": "Item One"}) + + with self.app_config_mock(app, "SPIFFWORKFLOW_BACKEND_OPEN_ID_IS_AUTHORITY_FOR_USER_GROUPS", True): + user_two = AuthorizationService.create_user_from_sign_in( + { + "username": "user_two", + "sub": "user_two", + "iss": "https://test.stuff", + "email": "user_two@example.com", + "groups": ["/Infra"], + } + ) + human_task_users = HumanTaskUserModel.query.filter_by(user_id=user_two.id).all() + assert len(human_task_users) == 0