From 856e51ff0995691347835fca40bf1406843ae758 Mon Sep 17 00:00:00 2001 From: Charles Date: Wed, 4 Feb 2026 15:42:55 +0100 Subject: [PATCH] update tests --- .phpunit.cache/test-results | 2 +- config/reference.php | 1783 +++++++++++++++++ phpunit.xml.dist | 1 + src/Controller/ActionController.php | 1 + src/Controller/ApplicationController.php | 13 +- src/Controller/UserController.php | 2 +- src/Entity/Organizations.php | 3 +- src/Form/OrganizationForm.php | 8 +- src/Form/UserForm.php | 6 +- src/Service/UserService.php | 1 - .../application/InformationCard.html.twig | 2 +- templates/application/appSmall.html.twig | 2 +- tests/Controller/ActionController.php | 63 + tests/Controller/ActionControllerTest.php | 106 - ...llerTest.php => ApplicationController.php} | 6 +- ...ControllerTest.php => IndexController.php} | 4 +- ...lerTest.php => NotificationController.php} | 8 +- ...lerTest.php => OrganizationController.php} | 34 +- ...rControllerTest.php => UserController.php} | 8 +- ...ctionalTest.php => AbstractFunctional.php} | 4 +- tests/Service/CguUserServiceTest.php | 5 +- tests/Service/NotificationServiceTest.php | 2 +- tests/Service/OrganizationsServiceTest.php | 162 +- tests/Service/UserServiceTest.php | 72 +- .../EditedOrganization_040226145244.txt | 1 + .../EditedOrganization_040226145519.txt | 1 + .../EditedOrganization_040226145546.txt | 1 + .../EditedOrganization_040226150040.txt | 1 + .../EditedOrganization_040226150142.txt | 1 + .../EditedOrganization_040226150152.txt | 1 + .../EditedOrganization_040226150232.txt | 1 + .../EditedOrganization_040226151241.txt | 1 + .../EditedOrganization_040226151311.txt | 1 + .../EditedOrganization_040226151509.txt | 1 + .../EditedOrganization_040226152128.txt | 1 + .../EditedOrganization_040226152350.txt | 1 + .../EditedOrganization_040226152401.txt | 1 + .../EditedOrganization_040226152500.txt | 1 + .../EditedOrganization_040226152559.txt | 1 + .../EditedOrganization_040226152709.txt | 1 + .../EditedOrganization_040226152724.txt | 1 + .../EditedOrganization_040226153016.txt | 1 + .../NewOrganization_040226145244.txt | 1 + .../NewOrganization_040226145518.txt | 1 + .../NewOrganization_040226145546.txt | 1 + .../NewOrganization_040226150040.txt | 1 + .../NewOrganization_040226150141.txt | 1 + .../NewOrganization_040226150152.txt | 1 + .../NewOrganization_040226150232.txt | 1 + .../NewOrganization_040226151240.txt | 1 + .../NewOrganization_040226151310.txt | 1 + .../NewOrganization_040226151508.txt | 1 + .../NewOrganization_040226152127.txt | 1 + .../NewOrganization_040226152349.txt | 1 + .../NewOrganization_040226152401.txt | 1 + .../NewOrganization_040226152459.txt | 1 + .../NewOrganization_040226152559.txt | 1 + .../NewOrganization_040226152708.txt | 1 + .../NewOrganization_040226152723.txt | 1 + .../NewOrganization_040226153015.txt | 1 + .../NewnameUser_040226151528.txt | 1 + .../NewnameUser_040226151746.txt | 1 + .../NewnameUser_040226151755.txt | 1 + .../NewnameUser_040226152120.txt | 1 + .../NewnameUser_040226152129.txt | 1 + .../NewnameUser_040226152351.txt | 1 + .../NewnameUser_040226152402.txt | 1 + .../NewnameUser_040226152403.txt | 1 + .../NewnameUser_040226152501.txt | 1 + .../NewnameUser_040226152601.txt | 1 + .../NewnameUser_040226152710.txt | 1 + .../NewnameUser_040226152725.txt | 1 + .../NewnameUser_040226153017.txt | 1 + 73 files changed, 2127 insertions(+), 220 deletions(-) create mode 100644 config/reference.php create mode 100644 tests/Controller/ActionController.php delete mode 100644 tests/Controller/ActionControllerTest.php rename tests/Controller/{ApplicationControllerTest.php => ApplicationController.php} (98%) rename tests/Controller/{IndexControllerTest.php => IndexController.php} (96%) rename tests/Controller/{NotificationControllerTest.php => NotificationController.php} (94%) rename tests/Controller/{OrganizationControllerTest.php => OrganizationController.php} (95%) rename tests/Controller/{UserControllerTest.php => UserController.php} (99%) rename tests/Functional/{AbstractFunctionalTest.php => AbstractFunctional.php} (96%) create mode 100644 uploads/organization_logos/EditedOrganization_040226145244.txt create mode 100644 uploads/organization_logos/EditedOrganization_040226145519.txt create mode 100644 uploads/organization_logos/EditedOrganization_040226145546.txt create mode 100644 uploads/organization_logos/EditedOrganization_040226150040.txt create mode 100644 uploads/organization_logos/EditedOrganization_040226150142.txt create mode 100644 uploads/organization_logos/EditedOrganization_040226150152.txt create mode 100644 uploads/organization_logos/EditedOrganization_040226150232.txt create mode 100644 uploads/organization_logos/EditedOrganization_040226151241.txt create mode 100644 uploads/organization_logos/EditedOrganization_040226151311.txt create mode 100644 uploads/organization_logos/EditedOrganization_040226151509.txt create mode 100644 uploads/organization_logos/EditedOrganization_040226152128.txt create mode 100644 uploads/organization_logos/EditedOrganization_040226152350.txt create mode 100644 uploads/organization_logos/EditedOrganization_040226152401.txt create mode 100644 uploads/organization_logos/EditedOrganization_040226152500.txt create mode 100644 uploads/organization_logos/EditedOrganization_040226152559.txt create mode 100644 uploads/organization_logos/EditedOrganization_040226152709.txt create mode 100644 uploads/organization_logos/EditedOrganization_040226152724.txt create mode 100644 uploads/organization_logos/EditedOrganization_040226153016.txt create mode 100644 uploads/organization_logos/NewOrganization_040226145244.txt create mode 100644 uploads/organization_logos/NewOrganization_040226145518.txt create mode 100644 uploads/organization_logos/NewOrganization_040226145546.txt create mode 100644 uploads/organization_logos/NewOrganization_040226150040.txt create mode 100644 uploads/organization_logos/NewOrganization_040226150141.txt create mode 100644 uploads/organization_logos/NewOrganization_040226150152.txt create mode 100644 uploads/organization_logos/NewOrganization_040226150232.txt create mode 100644 uploads/organization_logos/NewOrganization_040226151240.txt create mode 100644 uploads/organization_logos/NewOrganization_040226151310.txt create mode 100644 uploads/organization_logos/NewOrganization_040226151508.txt create mode 100644 uploads/organization_logos/NewOrganization_040226152127.txt create mode 100644 uploads/organization_logos/NewOrganization_040226152349.txt create mode 100644 uploads/organization_logos/NewOrganization_040226152401.txt create mode 100644 uploads/organization_logos/NewOrganization_040226152459.txt create mode 100644 uploads/organization_logos/NewOrganization_040226152559.txt create mode 100644 uploads/organization_logos/NewOrganization_040226152708.txt create mode 100644 uploads/organization_logos/NewOrganization_040226152723.txt create mode 100644 uploads/organization_logos/NewOrganization_040226153015.txt create mode 100644 uploads/profile_pictures/NewnameUser_040226151528.txt create mode 100644 uploads/profile_pictures/NewnameUser_040226151746.txt create mode 100644 uploads/profile_pictures/NewnameUser_040226151755.txt create mode 100644 uploads/profile_pictures/NewnameUser_040226152120.txt create mode 100644 uploads/profile_pictures/NewnameUser_040226152129.txt create mode 100644 uploads/profile_pictures/NewnameUser_040226152351.txt create mode 100644 uploads/profile_pictures/NewnameUser_040226152402.txt create mode 100644 uploads/profile_pictures/NewnameUser_040226152403.txt create mode 100644 uploads/profile_pictures/NewnameUser_040226152501.txt create mode 100644 uploads/profile_pictures/NewnameUser_040226152601.txt create mode 100644 uploads/profile_pictures/NewnameUser_040226152710.txt create mode 100644 uploads/profile_pictures/NewnameUser_040226152725.txt create mode 100644 uploads/profile_pictures/NewnameUser_040226153017.txt diff --git a/.phpunit.cache/test-results b/.phpunit.cache/test-results index 09141c6..c282d8b 100644 --- a/.phpunit.cache/test-results +++ b/.phpunit.cache/test-results @@ -1 +1 @@ -{"version":2,"defects":{"App\\Tests\\Service\\UserServiceTest::testCreateNewUserSuccess":7,"App\\Tests\\Service\\UserServiceTest::testLinkUserToOrganization":7,"App\\Tests\\Service\\UserOrganizationAppServiceTest::testDeactivateHandlesException":8,"App\\Tests\\Service\\UserOrganizationAppServiceTest::testSyncRolesHandlesSuperAdminLogic":8,"App\\Tests\\Service\\CguUserServiceTest::testAcceptLatestCguCreatesNewRelation":7,"App\\Tests\\Service\\CguUserServiceTest::testIsLatestCguAcceptedReturnsFalseIfNoCguExists":7,"App\\Tests\\Service\\AwsServiceTest::testGenerateUUIDv4":1,"App\\Tests\\Service\\AwsServiceTest::testCreateBucketSuccess":8,"App\\Tests\\Service\\AwsServiceTest::testCreateBucketFailure":8,"App\\Tests\\Service\\AwsServiceTest::testDeleteBucket":8,"App\\Tests\\Service\\AwsServiceTest::testGetListObjectReturnsContents":8,"App\\Tests\\Service\\AwsServiceTest::testGetListObjectReturnsNullIfEmpty":8,"App\\Tests\\Service\\AwsServiceTest::testPutDocObj":8,"App\\Tests\\Service\\AwsServiceTest::testDeleteDocObj":8,"App\\Tests\\Service\\AwsServiceTest::testRenameDocObj":8,"App\\Tests\\Service\\AwsServiceTest::testMoveDocObj":8,"App\\Tests\\Service\\AwsServiceTest::testGetPublicUrl":8,"App\\Tests\\Service\\ActionServiceTest::testFormatActivities":8,"App\\Tests\\Controller\\ActionControllerTest::fetch_activities_ajax_returns_json_response":8,"App\\Tests\\Controller\\ActionControllerTest::fetch_activities_returns_404_for_invalid_organization":8,"App\\Tests\\Controller\\UserControllerTest::view_user_displays_details_with_full_context":7,"App\\Tests\\Controller\\UserControllerTest::active_status_deactivates_user_via_ajax":7,"App\\Tests\\Controller\\UserControllerTest::data_returns_json_list_of_users":7,"App\\Tests\\Controller\\UserControllerTest::delete_soft_deletes_user":7,"App\\Tests\\Controller\\UserControllerTest::new_user_creation_redirects_correctly":7,"App\\Tests\\Controller\\ApplicationControllerTest::index_redirects_unauthenticated_user":7,"App\\Tests\\Controller\\ApplicationControllerTest::index_lists_applications_for_authenticated_user":8,"App\\Tests\\Controller\\ApplicationControllerTest::edit_page_denies_access_to_regular_users":8,"App\\Tests\\Controller\\ApplicationControllerTest::edit_page_loads_for_super_admin":7,"App\\Tests\\Controller\\ApplicationControllerTest::edit_submits_changes_successfully":7,"App\\Tests\\Controller\\ApplicationControllerTest::edit_handles_non_existent_id":7,"App\\Tests\\Controller\\ApplicationControllerTest::authorize_adds_organization_successfully":7,"App\\Tests\\Controller\\ApplicationControllerTest::edit_fails_gracefully_on_exception_with_mocked_logger":7,"App\\Tests\\Controller\\ApplicationControllerTest::edit_handles_form_submission_with_missing_data":7,"App\\Tests\\Controller\\ApplicationControllerTest::revoke_denies_access_to_admins":7,"App\\Tests\\Controller\\ApplicationControllerTest::authorize_fails_on_invalid_organization":7,"App\\Tests\\Controller\\ApplicationControllerTest::authorize_fails_on_invalid_application":7,"App\\Tests\\Controller\\ApplicationControllerTest::revoke_denies_access_to_user":7,"App\\Tests\\Controller\\ApplicationControllerTest::revoke_removes_organization_successfully":7,"App\\Tests\\Controller\\IndexControllerTest::test_index_successful_super_admin":7,"App\\Tests\\Controller\\IndexControllerTest::test_index_successful_admin_single_app":7,"App\\Tests\\Controller\\IndexControllerTest::test_index_successful_admin_mutiple_org":7,"App\\Tests\\Controller\\NotificationControllerTest::test_markAsRead_unauthenticated_user_forbidden":7,"App\\Tests\\Controller\\OrganizationControllerTest::test_index_super_admin_success":7,"App\\Tests\\Controller\\OrganizationControllerTest::test_index_regular_user_forbidden":8,"App\\Tests\\Controller\\OrganizationControllerTest::test_create_super_admin_success":7,"App\\Tests\\Controller\\IndexControllerTest::test_index_successful_admin_single_org":7,"App\\Tests\\Controller\\NotificationControllerTest::test_unread_unauthenticated_user_forbidden":7,"App\\Tests\\Controller\\OrganizationControllerTest::test_create_super_admin_invalid_data":7,"App\\Tests\\Controller\\OrganizationControllerTest::test_edit_super_admin_success":8,"App\\Tests\\Controller\\OrganizationControllerTest::test_edit_super_admin_invalid_data":7,"App\\Tests\\Controller\\OrganizationControllerTest::test_edit_nonexistent_organization_not_found":7,"App\\Tests\\Controller\\OrganizationControllerTest::test_delete_super_admin_success":7,"App\\Tests\\Controller\\OrganizationControllerTest::test_delete_regular_user_forbidden":8,"App\\Tests\\Controller\\OrganizationControllerTest::test_delete_nonexistent_organization_not_found":7,"App\\Tests\\Controller\\OrganizationControllerTest::test_delete_organization_with_dependencies":8,"App\\Tests\\Controller\\SecurityControllerTest::test_login_page_contains_login_form":7,"App\\Tests\\Controller\\SecurityControllerTest::test_login_with_invalid_credentials_shows_error":7,"App\\Tests\\Controller\\SecurityControllerTest::test_login_with_valid_credentials_redirects":7,"App\\Tests\\Controller\\SecurityControllerTest::test_logout_redirects_to_login":7,"App\\Tests\\Controller\\UserControllerTest::test_index_super_admin_success":7,"App\\Tests\\Controller\\UserControllerTest::test_index_no_organizations":7,"App\\Tests\\Controller\\UserControllerTest::test_view_super_admin":7,"App\\Tests\\Controller\\UserControllerTest::test_index_no_users_found":7,"App\\Tests\\Controller\\UserControllerTest::test_view_regular_user_forbidden":7,"App\\Tests\\Controller\\UserControllerTest::test_view_admin":7,"App\\Tests\\Controller\\UserControllerTest::test_view_user_not_found":7,"App\\Tests\\Controller\\UserControllerTest::test_edit_regular_user_forbidden":7,"App\\Tests\\Controller\\UserControllerTest::test_edit_other_user_forbidden":7,"App\\Tests\\Controller\\UserControllerTest::test_edit_user_not_found":7,"App\\Tests\\Controller\\UserControllerTest::test_edit_admin_edit_other_user_success":7,"App\\Tests\\Controller\\UserControllerTest::test_edit_admin_edit_other_user_different_organization_forbidden":8,"App\\Tests\\Controller\\UserControllerTest::test_create_super_admin_success":7,"App\\Tests\\Controller\\UserControllerTest::test_create_admin_forbidden":7,"App\\Tests\\Controller\\UserControllerTest::test_create_super_admin_invalid_data":7,"App\\Tests\\Controller\\UserControllerTest::test_create_super_admin_forbidden":7,"App\\Tests\\Controller\\UserControllerTest::test_create_super_admin_valid":8,"App\\Tests\\Controller\\UserControllerTest::test_delete_super_admin_success":7,"App\\Tests\\Controller\\UserControllerTest::test_delete_admin_forbidden":7,"App\\Tests\\Controller\\UserControllerTest::test_deactivate_super_admin_success":7,"App\\Tests\\Controller\\UserControllerTest::test_tabulator_super_admin_success":7},"times":{"App\\Tests\\Service\\UserServiceTest::testGenerateRandomPassword":0.001,"App\\Tests\\Service\\UserServiceTest::testIsUserConnectedReturnsTrueIfTokenValid":0.001,"App\\Tests\\Service\\UserServiceTest::testIsUserConnectedReturnsFalseIfTokenExpired":0.001,"App\\Tests\\Service\\UserServiceTest::testGetUserByIdentifierFound":0.001,"App\\Tests\\Service\\UserServiceTest::testGetUserByIdentifierNotFound":0.002,"App\\Tests\\Service\\UserServiceTest::testHasAccessToReturnsTrueForSuperAdmin":0.001,"App\\Tests\\Service\\UserServiceTest::testHasAccessToReturnsTrueForSelf":0.001,"App\\Tests\\Service\\UserServiceTest::testHandleProfilePictureUploadsAndLogs":0.001,"App\\Tests\\Service\\UserServiceTest::testSyncUserRolesAddsRole":0,"App\\Tests\\Service\\UserServiceTest::testSyncUserRolesRemovesRole":0.001,"App\\Tests\\Service\\UserServiceTest::testIsPasswordStrong":0,"App\\Tests\\Service\\UserServiceTest::testCreateNewUserSuccess":0.001,"App\\Tests\\Service\\UserServiceTest::testLinkUserToOrganization":0.002,"App\\Tests\\Service\\UserServiceTest::testIsAdminOfOrganizationReturnsTrue":0.001,"App\\Tests\\Service\\UserOrganizationServiceTest::testDeactivateAllLinksByUser":0.001,"App\\Tests\\Service\\UserOrganizationServiceTest::testDeactivateAllLinksByOrganization":0.001,"App\\Tests\\Service\\UserOrganizationServiceTest::testDeactivateDoesNothingIfNoLinksFound":0,"App\\Tests\\Service\\UserOrganizationAppServiceTest::testGroupUserOrganizationAppsByApplication":0.001,"App\\Tests\\Service\\UserOrganizationAppServiceTest::testDeactivateAllLinksSuccess":0.001,"App\\Tests\\Service\\UserOrganizationAppServiceTest::testDeactivateHandlesException":0.001,"App\\Tests\\Service\\UserOrganizationAppServiceTest::testSyncRolesAddsNewRole":0.002,"App\\Tests\\Service\\UserOrganizationAppServiceTest::testSyncRolesDeactivatesUnselectedRole":0.001,"App\\Tests\\Service\\UserOrganizationAppServiceTest::testSyncRolesHandlesSuperAdminLogic":0.001,"App\\Tests\\Service\\OrganizationsServiceTest::testHandleLogoSuccess":0.004,"App\\Tests\\Service\\OrganizationsServiceTest::testHandleLogoThrowsException":0.001,"App\\Tests\\Service\\OrganizationsServiceTest::testAppsAccess":0,"App\\Tests\\Service\\OrganizationsServiceTest::testNotifyOrganizationAdminsUserAccepted":0.001,"App\\Tests\\Service\\OrganizationsServiceTest::testNotifyOrganizationAdminsSkipsSelf":0.001,"App\\Tests\\Service\\NotificationServiceTest::testNotifyUserInvited":0,"App\\Tests\\Service\\NotificationServiceTest::testNotifyUserAcceptedInvite":0,"App\\Tests\\Service\\NotificationServiceTest::testNotifyUserDeactivated":0,"App\\Tests\\Service\\NotificationServiceTest::testNotifyUserActivated":0,"App\\Tests\\Service\\NotificationServiceTest::testNotifyOrganizationUpdate":0,"App\\Tests\\Service\\NotificationServiceTest::testNotifyAppAccessChangedGranted":0,"App\\Tests\\Service\\NotificationServiceTest::testNotifyAppAccessChangedRevoked":0,"App\\Tests\\Service\\NotificationServiceTest::testNotifyRoleChanged":0,"App\\Tests\\Service\\NotificationServiceTest::testNotifyUserDeleted":0,"App\\Tests\\Service\\LoggerServiceTest::testLogUserCreated":0.006,"App\\Tests\\Service\\LoggerServiceTest::testLogCGUAcceptanceLogsToTwoChannels":0.001,"App\\Tests\\Service\\LoggerServiceTest::testLogUserOrganizationLinkCreated":0,"App\\Tests\\Service\\LoggerServiceTest::testLogError":0,"App\\Tests\\Service\\LoggerServiceTest::testLogEntityNotFoundHandlesGlobals":0,"App\\Tests\\Service\\LoggerServiceTest::testLogAccessDenied":0,"App\\Tests\\Service\\LoggerServiceTest::testLogTokenRevocation":0,"App\\Tests\\Service\\LoggerServiceTest::testLogSuperAdmin":0,"App\\Tests\\Service\\LoggerServiceTest::testLogAWSAction":0,"App\\Tests\\Service\\LoggerServiceTest::testLogRoleEntityAssignment":0,"App\\Tests\\Service\\EmailServiceTest::testSendPasswordSetupEmailSuccess":0.004,"App\\Tests\\Service\\EmailServiceTest::testSendPasswordSetupEmailWithoutOrgIdInToken":0.001,"App\\Tests\\Service\\EmailServiceTest::testSendPasswordSetupEmailHandlesException":0.001,"App\\Tests\\Service\\EmailServiceTest::testSendExistingUserNotificationEmailSuccess":0.001,"App\\Tests\\Service\\EmailServiceTest::testSendExistingUserNotificationEmailHandlesException":0.001,"App\\Tests\\Service\\CguUserServiceTest::testIsLatestCguAcceptedReturnsFalseIfNoCguExists":0.004,"App\\Tests\\Service\\CguUserServiceTest::testIsLatestCguAcceptedReturnsFalseIfRelationDoesNotExist":0.001,"App\\Tests\\Service\\CguUserServiceTest::testIsLatestCguAcceptedReturnsTrueIfAccepted":0.001,"App\\Tests\\Service\\CguUserServiceTest::testAcceptLatestCguDoNothingIfNoCgu":0,"App\\Tests\\Service\\CguUserServiceTest::testAcceptLatestCguCreatesNewRelation":0.001,"App\\Tests\\Service\\CguUserServiceTest::testAcceptLatestCguUpdatesExistingRelation":0,"App\\Tests\\Service\\CguUserServiceTest::testDeclineCguSuccess":0,"App\\Tests\\Service\\CguUserServiceTest::testDeclineCguThrowsExceptionIfNotFound":0.001,"App\\Tests\\Service\\AwsServiceTest::testGenerateUUIDv4":0,"App\\Tests\\Service\\AwsServiceTest::testGetPublicUrl":0,"App\\Tests\\Service\\AwsServiceTest::testCreateBucketSuccess":0.005,"App\\Tests\\Service\\AwsServiceTest::testCreateBucketFailure":0.002,"App\\Tests\\Service\\AwsServiceTest::testDeleteBucket":0.002,"App\\Tests\\Service\\AwsServiceTest::testGetListObjectReturnsContents":0.002,"App\\Tests\\Service\\AwsServiceTest::testGetListObjectReturnsNullIfEmpty":0,"App\\Tests\\Service\\AwsServiceTest::testPutDocObj":0.003,"App\\Tests\\Service\\AwsServiceTest::testDeleteDocObj":0,"App\\Tests\\Service\\AwsServiceTest::testRenameDocObj":0.003,"App\\Tests\\Service\\AwsServiceTest::testMoveDocObj":0.003,"App\\Tests\\Service\\ActionServiceTest::testGetActivityColorRecent":0.002,"App\\Tests\\Service\\ActionServiceTest::testGetActivityColorMedium":0,"App\\Tests\\Service\\ActionServiceTest::testGetActivityColorOld":0,"App\\Tests\\Service\\ActionServiceTest::testFormatActivities":0.001,"App\\Tests\\Service\\ActionServiceTest::testCreateActionBasic":0.002,"App\\Tests\\Service\\ActionServiceTest::testCreateActionWithOrganizationAndTarget":0,"App\\Tests\\Service\\ActionServiceTest::testDescriptionActionSuccess":0,"App\\Tests\\Service\\ActionServiceTest::testDescriptionActionThrowsIfNoUser":0.001,"App\\Tests\\Service\\ActionServiceTest::testDescriptionActionThrowsIfInvalidType":0,"App\\Tests\\Service\\AccessTokenServiceTest::testRevokeUserTokensSuccess":0.006,"App\\Tests\\Service\\AccessTokenServiceTest::testRevokeUserTokensHandlesException":0.001,"App\\Tests\\Service\\AccessTokenServiceTest::testRevokeUserTokensDoesNothingIfNoneFound":0,"App\\Tests\\Controller\\ActionControllerTest::fetch_activities_ajax_returns_json_response":0.129,"App\\Tests\\Controller\\ActionControllerTest::fetch_activities_returns_404_for_invalid_organization":0.124,"App\\Tests\\Controller\\ActionControllerTest::fetch_activities_returns_404_for_non_existent_organization":0.115,"App\\Tests\\Controller\\ActionControllerTest::fetch_activities_access_denied_for_anonymous_user":0.023,"App\\Tests\\Controller\\UserControllerTest::view_user_displays_details_with_full_context":0.095,"App\\Tests\\Controller\\UserControllerTest::active_status_deactivates_user_via_ajax":0.016,"App\\Tests\\Controller\\UserControllerTest::data_returns_json_list_of_users":0.125,"App\\Tests\\Controller\\UserControllerTest::delete_soft_deletes_user":0.015,"App\\Tests\\Controller\\UserControllerTest::new_user_creation_redirects_correctly":0.37,"App\\Tests\\Controller\\ApplicationControllerTest::index_redirects_unauthenticated_user":0.014,"App\\Tests\\Controller\\ApplicationControllerTest::index_lists_applications_for_authenticated_user":0.086,"App\\Tests\\Controller\\ApplicationControllerTest::edit_page_denies_access_to_regular_users":0.2,"App\\Tests\\Controller\\ApplicationControllerTest::edit_page_loads_for_super_admin":0.038,"App\\Tests\\Controller\\ApplicationControllerTest::edit_submits_changes_successfully":0.054,"App\\Tests\\Controller\\ApplicationControllerTest::edit_handles_non_existent_id":0.087,"App\\Tests\\Controller\\ApplicationControllerTest::authorize_adds_organization_successfully":0.019,"App\\Tests\\Controller\\ApplicationControllerTest::edit_fails_gracefully_on_exception_with_mocked_logger":0.138,"App\\Tests\\Controller\\ApplicationControllerTest::index_denies_access_to_anonymous_users":0.008,"App\\Tests\\Controller\\ApplicationControllerTest::index_no_application_found":0.03,"App\\Tests\\Controller\\ApplicationControllerTest::edit_page_denies_access_to_admin_users":0.193,"App\\Tests\\Controller\\ApplicationControllerTest::authorize_fails_on_invalid_organization":0.018,"App\\Tests\\Controller\\ApplicationControllerTest::edit_handles_form_submission_with_missing_data":0.085,"App\\Tests\\Controller\\ApplicationControllerTest::authorize_fails_on_invalid_application":0.012,"App\\Tests\\Controller\\ApplicationControllerTest::revoke_denies_access_to_admins":0.195,"App\\Tests\\Controller\\ApplicationControllerTest::edit_handles_non_existent_id_get":0.048,"App\\Tests\\Controller\\ApplicationControllerTest::edit_handles_non_existent_id_post":0.048,"App\\Tests\\Controller\\ApplicationControllerTest::revoke_denies_access_to_user":0.197,"App\\Tests\\Controller\\ApplicationControllerTest::revoke_removes_organization_successfully":0.014,"App\\Tests\\Controller\\ApplicationControllerTest::revoke_fails_on_invalid_organization":0.108,"App\\Tests\\Controller\\ApplicationControllerTest::revoke_fails_on_invalid_application":0.102,"App\\Tests\\Controller\\IndexControllerTest::test_index_successful_super_admin":0.047,"App\\Tests\\Controller\\IndexControllerTest::test_index_successful_admin_single_app":0.197,"App\\Tests\\Controller\\IndexControllerTest::test_index_successful_admin_mutiple_org":0.053,"App\\Tests\\Controller\\IndexControllerTest::test_index_successful_user":0.049,"App\\Tests\\Controller\\IndexControllerTest::test_index_unauthenticated":0.01,"App\\Tests\\Controller\\IndexControllerTest::test_index_no_organization":0.143,"App\\Tests\\Controller\\NotificationControllerTest::test_index_super_admin_success":0.037,"App\\Tests\\Controller\\NotificationControllerTest::test_index_non_super_admin_forbidden":0.391,"App\\Tests\\Controller\\NotificationControllerTest::test_unread_authenticated_user_success":0.013,"App\\Tests\\Controller\\NotificationControllerTest::test_markAsRead_authenticated_user_success":0.013,"App\\Tests\\Controller\\NotificationControllerTest::test_markAsRead_notification_not_found":0.012,"App\\Tests\\Controller\\NotificationControllerTest::test_markAsRead_unauthenticated_user_forbidden":0.035,"App\\Tests\\Controller\\OrganizationControllerTest::test_index_super_admin_success":0.146,"App\\Tests\\Controller\\OrganizationControllerTest::test_index_regular_user_forbidden":0.21,"App\\Tests\\Controller\\OrganizationControllerTest::test_index_no_organizations":0.034,"App\\Tests\\Controller\\OrganizationControllerTest::test_create_super_admin_success":0.236,"App\\Tests\\Controller\\IndexControllerTest::test_index_successful_admin_single_org":0.06,"App\\Tests\\Controller\\NotificationControllerTest::test_unread_unauthenticated_user_forbidden":0.009,"App\\Tests\\Controller\\OrganizationControllerTest::test_create_regular_user_forbidden":0.195,"App\\Tests\\Controller\\OrganizationControllerTest::test_create_super_admin_invalid_data":0.204,"App\\Tests\\Controller\\OrganizationControllerTest::test_edit_super_admin_success":0.422,"App\\Tests\\Controller\\OrganizationControllerTest::test_edit_regular_user_forbidden":0.404,"App\\Tests\\Controller\\OrganizationControllerTest::test_edit_super_admin_invalid_data":0.354,"App\\Tests\\Controller\\OrganizationControllerTest::test_edit_nonexistent_organization_not_found":0.013,"App\\Tests\\Controller\\OrganizationControllerTest::test_create_super_admin_duplicate_email":0.239,"App\\Tests\\Controller\\OrganizationControllerTest::test_delete_super_admin_success":0.052,"App\\Tests\\Controller\\OrganizationControllerTest::test_delete_regular_user_forbidden":0.407,"App\\Tests\\Controller\\OrganizationControllerTest::test_delete_nonexistent_organization_not_found":0.283,"App\\Tests\\Controller\\OrganizationControllerTest::test_delete_organization_with_dependencies":0.154,"App\\Tests\\Controller\\SecurityControllerTest::test_login_page_is_accessible":0.077,"App\\Tests\\Controller\\SecurityControllerTest::test_login_page_contains_login_form":0.029,"App\\Tests\\Controller\\SecurityControllerTest::test_login_with_invalid_credentials_shows_error":0.065,"App\\Tests\\Controller\\SecurityControllerTest::test_login_with_valid_credentials_redirects":0.481,"App\\Tests\\Controller\\SecurityControllerTest::test_logout_redirects_to_login":0.105,"App\\Tests\\Controller\\UserControllerTest::test_index_super_admin_success":0.091,"App\\Tests\\Controller\\UserControllerTest::test_index_regular_user_forbidden":0.211,"App\\Tests\\Controller\\UserControllerTest::test_index_no_organizations":0.111,"App\\Tests\\Controller\\UserControllerTest::test_view_super_admin":0.062,"App\\Tests\\Controller\\UserControllerTest::test_index_no_users_found":0.114,"App\\Tests\\Controller\\UserControllerTest::test_view_regular_user_forbidden":0.105,"App\\Tests\\Controller\\UserControllerTest::test_view_admin":0.053,"App\\Tests\\Controller\\UserControllerTest::test_view_admin_different_organization_forbidden":0.119,"App\\Tests\\Controller\\UserControllerTest::test_view_user_self_success":0.039,"App\\Tests\\Controller\\UserControllerTest::test_view_user_self_with_organization_success":0.039,"App\\Tests\\Controller\\UserControllerTest::test_view_user_self_with_organization_forbidden":0.116,"App\\Tests\\Controller\\UserControllerTest::test_view_user_not_found":0.106,"App\\Tests\\Controller\\UserControllerTest::test_edit_super_admin_success":0.055,"App\\Tests\\Controller\\UserControllerTest::test_edit_regular_user_forbidden":0.056,"App\\Tests\\Controller\\UserControllerTest::test_edit_other_user_forbidden":0.196,"App\\Tests\\Controller\\UserControllerTest::test_edit_user_not_found":0.097,"App\\Tests\\Controller\\UserControllerTest::test_edit_admin_edit_other_user_success":0.218,"App\\Tests\\Controller\\UserControllerTest::test_edit_admin_user_not_found":0.1,"App\\Tests\\Controller\\UserControllerTest::test_edit_admin_edit_other_user_different_organization_forbidden":0.198,"App\\Tests\\Controller\\UserControllerTest::test_edit_user_not_found_admin":0.101,"App\\Tests\\Controller\\UserControllerTest::test_edit_super_admin_edit_other_user_success":0.222,"App\\Tests\\Controller\\UserControllerTest::test_edit_user_self_success":0.213,"App\\Tests\\Controller\\UserControllerTest::test_edit_user_self_with_organization_success":0.217,"App\\Tests\\Controller\\UserControllerTest::test_create_super_admin_success":0.137,"App\\Tests\\Controller\\UserControllerTest::test_create_regular_user_forbidden":0.193,"App\\Tests\\Controller\\UserControllerTest::test_create_admin_forbidden":0.226,"App\\Tests\\Controller\\UserControllerTest::test_create_super_admin_invalid_data":0.168,"App\\Tests\\Controller\\UserControllerTest::test_create_super_admin_forbidden":0.153,"App\\Tests\\Controller\\UserControllerTest::test_create_super_admin_valid":0.56,"App\\Tests\\Controller\\UserControllerTest::test_create_admin_valid":0.674,"App\\Tests\\Controller\\UserControllerTest::test_create_admin_no_organization_forbidden":0.298,"App\\Tests\\Controller\\UserControllerTest::test_delete_super_admin_success":0.162,"App\\Tests\\Controller\\UserControllerTest::test_delete_admin_forbidden":0.273,"App\\Tests\\Controller\\UserControllerTest::test_delete_not_found":0.203,"App\\Tests\\Controller\\UserControllerTest::test_deactivate_super_admin_success":0.166,"App\\Tests\\Controller\\UserControllerTest::test_tabulator_super_admin_success":0.293}} \ No newline at end of file +{"version":2,"defects":{"App\\Tests\\Service\\UserServiceTest::testCreateNewUserSuccess":7,"App\\Tests\\Service\\UserServiceTest::testLinkUserToOrganization":8,"App\\Tests\\Service\\UserOrganizationAppServiceTest::testDeactivateHandlesException":8,"App\\Tests\\Service\\UserOrganizationAppServiceTest::testSyncRolesHandlesSuperAdminLogic":8,"App\\Tests\\Service\\CguUserServiceTest::testAcceptLatestCguCreatesNewRelation":8,"App\\Tests\\Service\\CguUserServiceTest::testIsLatestCguAcceptedReturnsFalseIfNoCguExists":8,"App\\Tests\\Service\\AwsServiceTest::testGenerateUUIDv4":1,"App\\Tests\\Service\\AwsServiceTest::testCreateBucketSuccess":8,"App\\Tests\\Service\\AwsServiceTest::testCreateBucketFailure":8,"App\\Tests\\Service\\AwsServiceTest::testDeleteBucket":8,"App\\Tests\\Service\\AwsServiceTest::testGetListObjectReturnsContents":8,"App\\Tests\\Service\\AwsServiceTest::testGetListObjectReturnsNullIfEmpty":8,"App\\Tests\\Service\\AwsServiceTest::testPutDocObj":8,"App\\Tests\\Service\\AwsServiceTest::testDeleteDocObj":8,"App\\Tests\\Service\\AwsServiceTest::testRenameDocObj":8,"App\\Tests\\Service\\AwsServiceTest::testMoveDocObj":8,"App\\Tests\\Service\\AwsServiceTest::testGetPublicUrl":8,"App\\Tests\\Service\\ActionServiceTest::testFormatActivities":8,"App\\Tests\\Controller\\ActionControllerTest::fetch_activities_ajax_returns_json_response":8,"App\\Tests\\Controller\\ActionControllerTest::fetch_activities_returns_404_for_invalid_organization":7,"App\\Tests\\Controller\\UserControllerTest::view_user_displays_details_with_full_context":7,"App\\Tests\\Controller\\UserControllerTest::active_status_deactivates_user_via_ajax":7,"App\\Tests\\Controller\\UserControllerTest::data_returns_json_list_of_users":7,"App\\Tests\\Controller\\UserControllerTest::delete_soft_deletes_user":7,"App\\Tests\\Controller\\UserControllerTest::new_user_creation_redirects_correctly":7,"App\\Tests\\Controller\\ApplicationControllerTest::index_redirects_unauthenticated_user":8,"App\\Tests\\Controller\\ApplicationControllerTest::index_lists_applications_for_authenticated_user":7,"App\\Tests\\Controller\\ApplicationControllerTest::edit_page_denies_access_to_regular_users":8,"App\\Tests\\Controller\\ApplicationControllerTest::edit_page_loads_for_super_admin":7,"App\\Tests\\Controller\\ApplicationControllerTest::edit_submits_changes_successfully":7,"App\\Tests\\Controller\\ApplicationControllerTest::edit_handles_non_existent_id":7,"App\\Tests\\Controller\\ApplicationControllerTest::authorize_adds_organization_successfully":8,"App\\Tests\\Controller\\ApplicationControllerTest::edit_fails_gracefully_on_exception_with_mocked_logger":7,"App\\Tests\\Controller\\ApplicationControllerTest::edit_handles_form_submission_with_missing_data":7,"App\\Tests\\Controller\\ApplicationControllerTest::revoke_denies_access_to_admins":8,"App\\Tests\\Controller\\ApplicationControllerTest::authorize_fails_on_invalid_organization":7,"App\\Tests\\Controller\\ApplicationControllerTest::authorize_fails_on_invalid_application":7,"App\\Tests\\Controller\\ApplicationControllerTest::revoke_denies_access_to_user":8,"App\\Tests\\Controller\\ApplicationControllerTest::revoke_removes_organization_successfully":8,"App\\Tests\\Controller\\IndexControllerTest::test_index_successful_super_admin":8,"App\\Tests\\Controller\\IndexControllerTest::test_index_successful_admin_single_app":7,"App\\Tests\\Controller\\IndexControllerTest::test_index_successful_admin_mutiple_org":7,"App\\Tests\\Controller\\NotificationControllerTest::test_markAsRead_unauthenticated_user_forbidden":8,"App\\Tests\\Controller\\OrganizationControllerTest::test_index_super_admin_success":8,"App\\Tests\\Controller\\OrganizationControllerTest::test_index_regular_user_forbidden":8,"App\\Tests\\Controller\\OrganizationControllerTest::test_create_super_admin_success":8,"App\\Tests\\Controller\\IndexControllerTest::test_index_successful_admin_single_org":7,"App\\Tests\\Controller\\NotificationControllerTest::test_unread_unauthenticated_user_forbidden":7,"App\\Tests\\Controller\\OrganizationControllerTest::test_create_super_admin_invalid_data":8,"App\\Tests\\Controller\\OrganizationControllerTest::test_edit_super_admin_success":8,"App\\Tests\\Controller\\OrganizationControllerTest::test_edit_super_admin_invalid_data":7,"App\\Tests\\Controller\\OrganizationControllerTest::test_edit_nonexistent_organization_not_found":8,"App\\Tests\\Controller\\OrganizationControllerTest::test_delete_super_admin_success":8,"App\\Tests\\Controller\\OrganizationControllerTest::test_delete_regular_user_forbidden":8,"App\\Tests\\Controller\\OrganizationControllerTest::test_delete_nonexistent_organization_not_found":8,"App\\Tests\\Controller\\OrganizationControllerTest::test_delete_organization_with_dependencies":8,"App\\Tests\\Controller\\SecurityControllerTest::test_login_page_contains_login_form":7,"App\\Tests\\Controller\\SecurityControllerTest::test_login_with_invalid_credentials_shows_error":7,"App\\Tests\\Controller\\SecurityControllerTest::test_login_with_valid_credentials_redirects":7,"App\\Tests\\Controller\\SecurityControllerTest::test_logout_redirects_to_login":7,"App\\Tests\\Controller\\UserControllerTest::test_index_super_admin_success":8,"App\\Tests\\Controller\\UserControllerTest::test_index_no_organizations":7,"App\\Tests\\Controller\\UserControllerTest::test_view_super_admin":8,"App\\Tests\\Controller\\UserControllerTest::test_index_no_users_found":7,"App\\Tests\\Controller\\UserControllerTest::test_view_regular_user_forbidden":8,"App\\Tests\\Controller\\UserControllerTest::test_view_admin":8,"App\\Tests\\Controller\\UserControllerTest::test_view_user_not_found":8,"App\\Tests\\Controller\\UserControllerTest::test_edit_regular_user_forbidden":8,"App\\Tests\\Controller\\UserControllerTest::test_edit_other_user_forbidden":8,"App\\Tests\\Controller\\UserControllerTest::test_edit_user_not_found":8,"App\\Tests\\Controller\\UserControllerTest::test_edit_admin_edit_other_user_success":8,"App\\Tests\\Controller\\UserControllerTest::test_edit_admin_edit_other_user_different_organization_forbidden":8,"App\\Tests\\Controller\\UserControllerTest::test_create_super_admin_success":7,"App\\Tests\\Controller\\UserControllerTest::test_create_admin_forbidden":8,"App\\Tests\\Controller\\UserControllerTest::test_create_super_admin_invalid_data":7,"App\\Tests\\Controller\\UserControllerTest::test_create_super_admin_forbidden":7,"App\\Tests\\Controller\\UserControllerTest::test_create_super_admin_valid":8,"App\\Tests\\Controller\\UserControllerTest::test_delete_super_admin_success":8,"App\\Tests\\Controller\\UserControllerTest::test_delete_admin_forbidden":8,"App\\Tests\\Controller\\UserControllerTest::test_deactivate_super_admin_success":7,"App\\Tests\\Controller\\UserControllerTest::test_tabulator_super_admin_success":7,"App\\Tests\\Controller\\ApplicationControllerTest::index_no_application_found":7,"App\\Tests\\Controller\\ApplicationControllerTest::edit_page_denies_access_to_admin_users":8,"App\\Tests\\Controller\\ApplicationControllerTest::edit_handles_non_existent_id_get":7,"App\\Tests\\Controller\\ApplicationControllerTest::edit_handles_non_existent_id_post":7,"App\\Tests\\Controller\\ApplicationControllerTest::revoke_fails_on_invalid_organization":8,"App\\Tests\\Controller\\ApplicationControllerTest::revoke_fails_on_invalid_application":8,"App\\Tests\\Controller\\IndexControllerTest::test_index_successful_user":8,"App\\Tests\\Controller\\IndexControllerTest::test_index_unauthenticated":8,"App\\Tests\\Controller\\NotificationControllerTest::test_index_super_admin_success":8,"App\\Tests\\Controller\\NotificationControllerTest::test_index_non_super_admin_forbidden":8,"App\\Tests\\Controller\\NotificationControllerTest::test_unread_authenticated_user_success":8,"App\\Tests\\Controller\\NotificationControllerTest::test_markAsRead_authenticated_user_success":8,"App\\Tests\\Controller\\NotificationControllerTest::test_markAsRead_notification_not_found":8,"App\\Tests\\Controller\\OrganizationControllerTest::test_index_no_organizations":8,"App\\Tests\\Controller\\OrganizationControllerTest::test_create_regular_user_forbidden":8,"App\\Tests\\Controller\\OrganizationControllerTest::test_create_super_admin_duplicate_email":8,"App\\Tests\\Controller\\OrganizationControllerTest::test_edit_regular_user_forbidden":8,"App\\Tests\\Controller\\UserControllerTest::test_index_regular_user_forbidden":8,"App\\Tests\\Controller\\UserControllerTest::test_view_admin_different_organization_forbidden":8,"App\\Tests\\Controller\\UserControllerTest::test_view_user_self_success":8,"App\\Tests\\Controller\\UserControllerTest::test_view_user_self_with_organization_success":8,"App\\Tests\\Controller\\UserControllerTest::test_edit_super_admin_success":8,"App\\Tests\\Controller\\UserControllerTest::test_edit_super_admin_edit_other_user_success":8,"App\\Tests\\Controller\\UserControllerTest::test_edit_admin_user_not_found":8,"App\\Tests\\Controller\\UserControllerTest::test_edit_user_not_found_admin":8,"App\\Tests\\Controller\\UserControllerTest::test_edit_user_self_success":8,"App\\Tests\\Controller\\UserControllerTest::test_edit_user_self_with_organization_success":8,"App\\Tests\\Controller\\UserControllerTest::test_create_regular_user_forbidden":8,"App\\Tests\\Controller\\UserControllerTest::test_create_admin_valid":8,"App\\Tests\\Controller\\UserControllerTest::test_create_admin_no_organization_forbidden":8,"App\\Tests\\Controller\\UserControllerTest::test_delete_not_found":8,"App\\Tests\\Service\\CguUserServiceTest::testIsLatestCguAcceptedReturnsFalseIfRelationDoesNotExist":8,"App\\Tests\\Service\\CguUserServiceTest::testIsLatestCguAcceptedReturnsTrueIfAccepted":8,"App\\Tests\\Service\\CguUserServiceTest::testAcceptLatestCguDoNothingIfNoCgu":8,"App\\Tests\\Service\\CguUserServiceTest::testAcceptLatestCguUpdatesExistingRelation":8,"App\\Tests\\Service\\CguUserServiceTest::testDeclineCguSuccess":8,"App\\Tests\\Service\\CguUserServiceTest::testDeclineCguThrowsExceptionIfNotFound":8,"App\\Tests\\Service\\NotificationServiceTest::testNotifyUserDeactivated":7,"App\\Tests\\Service\\OrganizationsServiceTest::testHandleLogoSuccess":7,"App\\Tests\\Service\\OrganizationsServiceTest::testHandleLogoThrowsException":7,"App\\Tests\\Service\\OrganizationsServiceTest::testNotifyOrganizationAdminsUserAccepted":7,"App\\Tests\\Service\\UserServiceTest::testGenerateRandomPassword":7,"App\\Tests\\Service\\UserServiceTest::testIsUserConnectedReturnsTrueIfTokenValid":8,"App\\Tests\\Service\\UserServiceTest::testIsUserConnectedReturnsFalseIfTokenExpired":8,"App\\Tests\\Service\\UserServiceTest::testGetUserByIdentifierFound":8,"App\\Tests\\Service\\UserServiceTest::testGetUserByIdentifierNotFound":8,"App\\Tests\\Service\\UserServiceTest::testHasAccessToReturnsTrueForSuperAdmin":8,"App\\Tests\\Service\\UserServiceTest::testHasAccessToReturnsTrueForSelf":8,"App\\Tests\\Service\\UserServiceTest::testHandleProfilePictureUploadsAndLogs":8,"App\\Tests\\Service\\UserServiceTest::testSyncUserRolesAddsRole":8,"App\\Tests\\Service\\UserServiceTest::testSyncUserRolesRemovesRole":8,"App\\Tests\\Service\\UserServiceTest::testIsPasswordStrong":8,"App\\Tests\\Service\\UserServiceTest::testIsAdminOfOrganizationReturnsTrue":8,"App\\Tests\\Service\\OrganizationsServiceTest::testNotifyOrganizationAdminsHandlesAllCases":8},"times":{"App\\Tests\\Service\\UserServiceTest::testGenerateRandomPassword":0,"App\\Tests\\Service\\UserServiceTest::testIsUserConnectedReturnsTrueIfTokenValid":0,"App\\Tests\\Service\\UserServiceTest::testIsUserConnectedReturnsFalseIfTokenExpired":0,"App\\Tests\\Service\\UserServiceTest::testGetUserByIdentifierFound":0,"App\\Tests\\Service\\UserServiceTest::testGetUserByIdentifierNotFound":0.001,"App\\Tests\\Service\\UserServiceTest::testHasAccessToReturnsTrueForSuperAdmin":0,"App\\Tests\\Service\\UserServiceTest::testHasAccessToReturnsTrueForSelf":0,"App\\Tests\\Service\\UserServiceTest::testHandleProfilePictureUploadsAndLogs":0,"App\\Tests\\Service\\UserServiceTest::testSyncUserRolesAddsRole":0,"App\\Tests\\Service\\UserServiceTest::testSyncUserRolesRemovesRole":0,"App\\Tests\\Service\\UserServiceTest::testIsPasswordStrong":0,"App\\Tests\\Service\\UserServiceTest::testCreateNewUserSuccess":0.001,"App\\Tests\\Service\\UserServiceTest::testLinkUserToOrganization":0.001,"App\\Tests\\Service\\UserServiceTest::testIsAdminOfOrganizationReturnsTrue":0.001,"App\\Tests\\Service\\UserOrganizationServiceTest::testDeactivateAllLinksByUser":0.001,"App\\Tests\\Service\\UserOrganizationServiceTest::testDeactivateAllLinksByOrganization":0.001,"App\\Tests\\Service\\UserOrganizationServiceTest::testDeactivateDoesNothingIfNoLinksFound":0,"App\\Tests\\Service\\UserOrganizationAppServiceTest::testGroupUserOrganizationAppsByApplication":0,"App\\Tests\\Service\\UserOrganizationAppServiceTest::testDeactivateAllLinksSuccess":0.001,"App\\Tests\\Service\\UserOrganizationAppServiceTest::testDeactivateHandlesException":0.001,"App\\Tests\\Service\\UserOrganizationAppServiceTest::testSyncRolesAddsNewRole":0.001,"App\\Tests\\Service\\UserOrganizationAppServiceTest::testSyncRolesDeactivatesUnselectedRole":0,"App\\Tests\\Service\\UserOrganizationAppServiceTest::testSyncRolesHandlesSuperAdminLogic":0.001,"App\\Tests\\Service\\OrganizationsServiceTest::testHandleLogoSuccess":0.005,"App\\Tests\\Service\\OrganizationsServiceTest::testHandleLogoThrowsException":0.001,"App\\Tests\\Service\\OrganizationsServiceTest::testAppsAccess":0,"App\\Tests\\Service\\OrganizationsServiceTest::testNotifyOrganizationAdminsUserAccepted":0.001,"App\\Tests\\Service\\OrganizationsServiceTest::testNotifyOrganizationAdminsSkipsSelf":0.001,"App\\Tests\\Service\\NotificationServiceTest::testNotifyUserInvited":0,"App\\Tests\\Service\\NotificationServiceTest::testNotifyUserAcceptedInvite":0,"App\\Tests\\Service\\NotificationServiceTest::testNotifyUserDeactivated":0,"App\\Tests\\Service\\NotificationServiceTest::testNotifyUserActivated":0,"App\\Tests\\Service\\NotificationServiceTest::testNotifyOrganizationUpdate":0,"App\\Tests\\Service\\NotificationServiceTest::testNotifyAppAccessChangedGranted":0,"App\\Tests\\Service\\NotificationServiceTest::testNotifyAppAccessChangedRevoked":0,"App\\Tests\\Service\\NotificationServiceTest::testNotifyRoleChanged":0,"App\\Tests\\Service\\NotificationServiceTest::testNotifyUserDeleted":0,"App\\Tests\\Service\\LoggerServiceTest::testLogUserCreated":0.007,"App\\Tests\\Service\\LoggerServiceTest::testLogCGUAcceptanceLogsToTwoChannels":0.001,"App\\Tests\\Service\\LoggerServiceTest::testLogUserOrganizationLinkCreated":0.001,"App\\Tests\\Service\\LoggerServiceTest::testLogError":0,"App\\Tests\\Service\\LoggerServiceTest::testLogEntityNotFoundHandlesGlobals":0.001,"App\\Tests\\Service\\LoggerServiceTest::testLogAccessDenied":0,"App\\Tests\\Service\\LoggerServiceTest::testLogTokenRevocation":0,"App\\Tests\\Service\\LoggerServiceTest::testLogSuperAdmin":0,"App\\Tests\\Service\\LoggerServiceTest::testLogAWSAction":0,"App\\Tests\\Service\\LoggerServiceTest::testLogRoleEntityAssignment":0,"App\\Tests\\Service\\EmailServiceTest::testSendPasswordSetupEmailSuccess":0.005,"App\\Tests\\Service\\EmailServiceTest::testSendPasswordSetupEmailWithoutOrgIdInToken":0.001,"App\\Tests\\Service\\EmailServiceTest::testSendPasswordSetupEmailHandlesException":0.001,"App\\Tests\\Service\\EmailServiceTest::testSendExistingUserNotificationEmailSuccess":0.001,"App\\Tests\\Service\\EmailServiceTest::testSendExistingUserNotificationEmailHandlesException":0.001,"App\\Tests\\Service\\CguUserServiceTest::testIsLatestCguAcceptedReturnsFalseIfNoCguExists":0.004,"App\\Tests\\Service\\CguUserServiceTest::testIsLatestCguAcceptedReturnsFalseIfRelationDoesNotExist":0.001,"App\\Tests\\Service\\CguUserServiceTest::testIsLatestCguAcceptedReturnsTrueIfAccepted":0.001,"App\\Tests\\Service\\CguUserServiceTest::testAcceptLatestCguDoNothingIfNoCgu":0,"App\\Tests\\Service\\CguUserServiceTest::testAcceptLatestCguCreatesNewRelation":0.001,"App\\Tests\\Service\\CguUserServiceTest::testAcceptLatestCguUpdatesExistingRelation":0,"App\\Tests\\Service\\CguUserServiceTest::testDeclineCguSuccess":0,"App\\Tests\\Service\\CguUserServiceTest::testDeclineCguThrowsExceptionIfNotFound":0.001,"App\\Tests\\Service\\AwsServiceTest::testGenerateUUIDv4":0,"App\\Tests\\Service\\AwsServiceTest::testGetPublicUrl":0,"App\\Tests\\Service\\AwsServiceTest::testCreateBucketSuccess":0.005,"App\\Tests\\Service\\AwsServiceTest::testCreateBucketFailure":0.002,"App\\Tests\\Service\\AwsServiceTest::testDeleteBucket":0.002,"App\\Tests\\Service\\AwsServiceTest::testGetListObjectReturnsContents":0.002,"App\\Tests\\Service\\AwsServiceTest::testGetListObjectReturnsNullIfEmpty":0,"App\\Tests\\Service\\AwsServiceTest::testPutDocObj":0.003,"App\\Tests\\Service\\AwsServiceTest::testDeleteDocObj":0,"App\\Tests\\Service\\AwsServiceTest::testRenameDocObj":0.003,"App\\Tests\\Service\\AwsServiceTest::testMoveDocObj":0.003,"App\\Tests\\Service\\ActionServiceTest::testGetActivityColorRecent":0,"App\\Tests\\Service\\ActionServiceTest::testGetActivityColorMedium":0,"App\\Tests\\Service\\ActionServiceTest::testGetActivityColorOld":0,"App\\Tests\\Service\\ActionServiceTest::testFormatActivities":0.001,"App\\Tests\\Service\\ActionServiceTest::testCreateActionBasic":0,"App\\Tests\\Service\\ActionServiceTest::testCreateActionWithOrganizationAndTarget":0,"App\\Tests\\Service\\ActionServiceTest::testDescriptionActionSuccess":0,"App\\Tests\\Service\\ActionServiceTest::testDescriptionActionThrowsIfNoUser":0.001,"App\\Tests\\Service\\ActionServiceTest::testDescriptionActionThrowsIfInvalidType":0,"App\\Tests\\Service\\AccessTokenServiceTest::testRevokeUserTokensSuccess":0.005,"App\\Tests\\Service\\AccessTokenServiceTest::testRevokeUserTokensHandlesException":0.001,"App\\Tests\\Service\\AccessTokenServiceTest::testRevokeUserTokensDoesNothingIfNoneFound":0,"App\\Tests\\Controller\\ActionControllerTest::fetch_activities_ajax_returns_json_response":0.072,"App\\Tests\\Controller\\ActionControllerTest::fetch_activities_returns_404_for_invalid_organization":0.028,"App\\Tests\\Controller\\ActionControllerTest::fetch_activities_returns_404_for_non_existent_organization":0.115,"App\\Tests\\Controller\\ActionControllerTest::fetch_activities_access_denied_for_anonymous_user":0.023,"App\\Tests\\Controller\\UserControllerTest::view_user_displays_details_with_full_context":0.095,"App\\Tests\\Controller\\UserControllerTest::active_status_deactivates_user_via_ajax":0.016,"App\\Tests\\Controller\\UserControllerTest::data_returns_json_list_of_users":0.125,"App\\Tests\\Controller\\UserControllerTest::delete_soft_deletes_user":0.015,"App\\Tests\\Controller\\UserControllerTest::new_user_creation_redirects_correctly":0.37,"App\\Tests\\Controller\\ApplicationControllerTest::index_redirects_unauthenticated_user":0.012,"App\\Tests\\Controller\\ApplicationControllerTest::index_lists_applications_for_authenticated_user":0.048,"App\\Tests\\Controller\\ApplicationControllerTest::edit_page_denies_access_to_regular_users":0.027,"App\\Tests\\Controller\\ApplicationControllerTest::edit_page_loads_for_super_admin":0.046,"App\\Tests\\Controller\\ApplicationControllerTest::edit_submits_changes_successfully":0.046,"App\\Tests\\Controller\\ApplicationControllerTest::edit_handles_non_existent_id":0.087,"App\\Tests\\Controller\\ApplicationControllerTest::authorize_adds_organization_successfully":0.016,"App\\Tests\\Controller\\ApplicationControllerTest::edit_fails_gracefully_on_exception_with_mocked_logger":0.138,"App\\Tests\\Controller\\ApplicationControllerTest::index_denies_access_to_anonymous_users":0.008,"App\\Tests\\Controller\\ApplicationControllerTest::index_no_application_found":0.029,"App\\Tests\\Controller\\ApplicationControllerTest::edit_page_denies_access_to_admin_users":0.016,"App\\Tests\\Controller\\ApplicationControllerTest::authorize_fails_on_invalid_organization":0.023,"App\\Tests\\Controller\\ApplicationControllerTest::edit_handles_form_submission_with_missing_data":0.085,"App\\Tests\\Controller\\ApplicationControllerTest::authorize_fails_on_invalid_application":0.015,"App\\Tests\\Controller\\ApplicationControllerTest::revoke_denies_access_to_admins":0.017,"App\\Tests\\Controller\\ApplicationControllerTest::edit_handles_non_existent_id_get":0.043,"App\\Tests\\Controller\\ApplicationControllerTest::edit_handles_non_existent_id_post":0.046,"App\\Tests\\Controller\\ApplicationControllerTest::revoke_denies_access_to_user":0.017,"App\\Tests\\Controller\\ApplicationControllerTest::revoke_removes_organization_successfully":0.014,"App\\Tests\\Controller\\ApplicationControllerTest::revoke_fails_on_invalid_organization":0.017,"App\\Tests\\Controller\\ApplicationControllerTest::revoke_fails_on_invalid_application":0.016,"App\\Tests\\Controller\\IndexControllerTest::test_index_successful_super_admin":0.043,"App\\Tests\\Controller\\IndexControllerTest::test_index_successful_admin_single_app":0.197,"App\\Tests\\Controller\\IndexControllerTest::test_index_successful_admin_mutiple_org":0.055,"App\\Tests\\Controller\\IndexControllerTest::test_index_successful_user":0.046,"App\\Tests\\Controller\\IndexControllerTest::test_index_unauthenticated":0.007,"App\\Tests\\Controller\\IndexControllerTest::test_index_no_organization":0.143,"App\\Tests\\Controller\\NotificationControllerTest::test_index_super_admin_success":0.023,"App\\Tests\\Controller\\NotificationControllerTest::test_index_non_super_admin_forbidden":0.042,"App\\Tests\\Controller\\NotificationControllerTest::test_unread_authenticated_user_success":0.012,"App\\Tests\\Controller\\NotificationControllerTest::test_markAsRead_authenticated_user_success":0.011,"App\\Tests\\Controller\\NotificationControllerTest::test_markAsRead_notification_not_found":0.011,"App\\Tests\\Controller\\NotificationControllerTest::test_markAsRead_unauthenticated_user_forbidden":0.031,"App\\Tests\\Controller\\OrganizationControllerTest::test_index_super_admin_success":0.034,"App\\Tests\\Controller\\OrganizationControllerTest::test_index_regular_user_forbidden":0.017,"App\\Tests\\Controller\\OrganizationControllerTest::test_index_no_organizations":0.032,"App\\Tests\\Controller\\OrganizationControllerTest::test_create_super_admin_success":0.223,"App\\Tests\\Controller\\IndexControllerTest::test_index_successful_admin_single_org":0.08,"App\\Tests\\Controller\\NotificationControllerTest::test_unread_unauthenticated_user_forbidden":0.035,"App\\Tests\\Controller\\OrganizationControllerTest::test_create_regular_user_forbidden":0.021,"App\\Tests\\Controller\\OrganizationControllerTest::test_create_super_admin_invalid_data":0.201,"App\\Tests\\Controller\\OrganizationControllerTest::test_edit_super_admin_success":0.217,"App\\Tests\\Controller\\OrganizationControllerTest::test_edit_regular_user_forbidden":0.021,"App\\Tests\\Controller\\OrganizationControllerTest::test_edit_super_admin_invalid_data":0.208,"App\\Tests\\Controller\\OrganizationControllerTest::test_edit_nonexistent_organization_not_found":0.014,"App\\Tests\\Controller\\OrganizationControllerTest::test_create_super_admin_duplicate_email":0.212,"App\\Tests\\Controller\\OrganizationControllerTest::test_delete_super_admin_success":0.054,"App\\Tests\\Controller\\OrganizationControllerTest::test_delete_regular_user_forbidden":0.02,"App\\Tests\\Controller\\OrganizationControllerTest::test_delete_nonexistent_organization_not_found":0.017,"App\\Tests\\Controller\\OrganizationControllerTest::test_delete_organization_with_dependencies":0.071,"App\\Tests\\Controller\\SecurityControllerTest::test_login_page_is_accessible":0.077,"App\\Tests\\Controller\\SecurityControllerTest::test_login_page_contains_login_form":0.029,"App\\Tests\\Controller\\SecurityControllerTest::test_login_with_invalid_credentials_shows_error":0.065,"App\\Tests\\Controller\\SecurityControllerTest::test_login_with_valid_credentials_redirects":0.481,"App\\Tests\\Controller\\SecurityControllerTest::test_logout_redirects_to_login":0.105,"App\\Tests\\Controller\\UserControllerTest::test_index_super_admin_success":0.033,"App\\Tests\\Controller\\UserControllerTest::test_index_regular_user_forbidden":0.019,"App\\Tests\\Controller\\UserControllerTest::test_index_no_organizations":0.111,"App\\Tests\\Controller\\UserControllerTest::test_view_super_admin":0.042,"App\\Tests\\Controller\\UserControllerTest::test_index_no_users_found":0.114,"App\\Tests\\Controller\\UserControllerTest::test_view_regular_user_forbidden":0.017,"App\\Tests\\Controller\\UserControllerTest::test_view_admin":0.05,"App\\Tests\\Controller\\UserControllerTest::test_view_admin_different_organization_forbidden":0.025,"App\\Tests\\Controller\\UserControllerTest::test_view_user_self_success":0.033,"App\\Tests\\Controller\\UserControllerTest::test_view_user_self_with_organization_success":0.044,"App\\Tests\\Controller\\UserControllerTest::test_view_user_self_with_organization_forbidden":0.116,"App\\Tests\\Controller\\UserControllerTest::test_view_user_not_found":0.015,"App\\Tests\\Controller\\UserControllerTest::test_edit_super_admin_success":0.04,"App\\Tests\\Controller\\UserControllerTest::test_edit_regular_user_forbidden":0.038,"App\\Tests\\Controller\\UserControllerTest::test_edit_other_user_forbidden":0.018,"App\\Tests\\Controller\\UserControllerTest::test_edit_user_not_found":0.015,"App\\Tests\\Controller\\UserControllerTest::test_edit_admin_edit_other_user_success":0.221,"App\\Tests\\Controller\\UserControllerTest::test_edit_admin_user_not_found":0.016,"App\\Tests\\Controller\\UserControllerTest::test_edit_admin_edit_other_user_different_organization_forbidden":0.029,"App\\Tests\\Controller\\UserControllerTest::test_edit_user_not_found_admin":0.015,"App\\Tests\\Controller\\UserControllerTest::test_edit_super_admin_edit_other_user_success":0.209,"App\\Tests\\Controller\\UserControllerTest::test_edit_user_self_success":0.225,"App\\Tests\\Controller\\UserControllerTest::test_edit_user_self_with_organization_success":0.23,"App\\Tests\\Controller\\UserControllerTest::test_create_super_admin_success":0.137,"App\\Tests\\Controller\\UserControllerTest::test_create_regular_user_forbidden":0.017,"App\\Tests\\Controller\\UserControllerTest::test_create_admin_forbidden":0.042,"App\\Tests\\Controller\\UserControllerTest::test_create_super_admin_invalid_data":0.168,"App\\Tests\\Controller\\UserControllerTest::test_create_super_admin_forbidden":0.046,"App\\Tests\\Controller\\UserControllerTest::test_create_super_admin_valid":0.353,"App\\Tests\\Controller\\UserControllerTest::test_create_admin_valid":0.342,"App\\Tests\\Controller\\UserControllerTest::test_create_admin_no_organization_forbidden":0.033,"App\\Tests\\Controller\\UserControllerTest::test_delete_super_admin_success":0.082,"App\\Tests\\Controller\\UserControllerTest::test_delete_admin_forbidden":0.019,"App\\Tests\\Controller\\UserControllerTest::test_delete_not_found":0.016,"App\\Tests\\Controller\\UserControllerTest::test_deactivate_super_admin_success":0.166,"App\\Tests\\Controller\\UserControllerTest::test_tabulator_super_admin_success":0.293,"App\\Tests\\Service\\OrganizationsServiceTest::testNotifyOrganizationAdminsSkipsNonAdmins":0.001,"App\\Tests\\Service\\OrganizationsServiceTest::testNotifyOrganizationAdminsHandlesAllCases with data set \"Invited Case\"":0.001,"App\\Tests\\Service\\OrganizationsServiceTest::testNotifyOrganizationAdminsHandlesAllCases with data set \"Deactivated Case\"":0.001,"App\\Tests\\Service\\OrganizationsServiceTest::testNotifyOrganizationAdminsHandlesAllCases with data set \"Deleted Case\"":0.001,"App\\Tests\\Service\\OrganizationsServiceTest::testNotifyOrganizationAdminsHandlesAllCases with data set \"Activated Case\"":0.001,"App\\Tests\\Service\\OrganizationsServiceTest::testNotifyOrganizationAdminsHandlesAllCases":0.004}} \ No newline at end of file diff --git a/config/reference.php b/config/reference.php new file mode 100644 index 0000000..6cab768 --- /dev/null +++ b/config/reference.php @@ -0,0 +1,1783 @@ + [ + * 'App\\' => [ + * 'resource' => '../src/', + * ], + * ], + * ]); + * ``` + * + * @psalm-type ImportsConfig = list + * @psalm-type ParametersConfig = array|Param|null>|Param|null> + * @psalm-type ArgumentsType = list|array + * @psalm-type CallType = array|array{0:string, 1?:ArgumentsType, 2?:bool}|array{method:string, arguments?:ArgumentsType, returns_clone?:bool} + * @psalm-type TagsType = list>> // arrays inside the list must have only one element, with the tag name as the key + * @psalm-type CallbackType = string|array{0:string|ReferenceConfigurator,1:string}|\Closure|ReferenceConfigurator|ExpressionConfigurator + * @psalm-type DeprecationType = array{package: string, version: string, message?: string} + * @psalm-type DefaultsType = array{ + * public?: bool, + * tags?: TagsType, + * resource_tags?: TagsType, + * autowire?: bool, + * autoconfigure?: bool, + * bind?: array, + * } + * @psalm-type InstanceofType = array{ + * shared?: bool, + * lazy?: bool|string, + * public?: bool, + * properties?: array, + * configurator?: CallbackType, + * calls?: list, + * tags?: TagsType, + * resource_tags?: TagsType, + * autowire?: bool, + * bind?: array, + * constructor?: string, + * } + * @psalm-type DefinitionType = array{ + * class?: string, + * file?: string, + * parent?: string, + * shared?: bool, + * synthetic?: bool, + * lazy?: bool|string, + * public?: bool, + * abstract?: bool, + * deprecated?: DeprecationType, + * factory?: CallbackType, + * configurator?: CallbackType, + * arguments?: ArgumentsType, + * properties?: array, + * calls?: list, + * tags?: TagsType, + * resource_tags?: TagsType, + * decorates?: string, + * decoration_inner_name?: string, + * decoration_priority?: int, + * decoration_on_invalid?: 'exception'|'ignore'|null, + * autowire?: bool, + * autoconfigure?: bool, + * bind?: array, + * constructor?: string, + * from_callable?: CallbackType, + * } + * @psalm-type AliasType = string|array{ + * alias: string, + * public?: bool, + * deprecated?: DeprecationType, + * } + * @psalm-type PrototypeType = array{ + * resource: string, + * namespace?: string, + * exclude?: string|list, + * parent?: string, + * shared?: bool, + * lazy?: bool|string, + * public?: bool, + * abstract?: bool, + * deprecated?: DeprecationType, + * factory?: CallbackType, + * arguments?: ArgumentsType, + * properties?: array, + * configurator?: CallbackType, + * calls?: list, + * tags?: TagsType, + * resource_tags?: TagsType, + * autowire?: bool, + * autoconfigure?: bool, + * bind?: array, + * constructor?: string, + * } + * @psalm-type StackType = array{ + * stack: list>, + * public?: bool, + * deprecated?: DeprecationType, + * } + * @psalm-type ServicesConfig = array{ + * _defaults?: DefaultsType, + * _instanceof?: InstanceofType, + * ... + * } + * @psalm-type ExtensionType = array + * @psalm-type FrameworkConfig = array{ + * secret?: scalar|Param|null, + * http_method_override?: bool|Param, // Set true to enable support for the '_method' request parameter to determine the intended HTTP method on POST requests. // Default: false + * allowed_http_method_override?: list|null, + * trust_x_sendfile_type_header?: scalar|Param|null, // Set true to enable support for xsendfile in binary file responses. // Default: "%env(bool:default::SYMFONY_TRUST_X_SENDFILE_TYPE_HEADER)%" + * ide?: scalar|Param|null, // Default: "%env(default::SYMFONY_IDE)%" + * test?: bool|Param, + * default_locale?: scalar|Param|null, // Default: "en" + * set_locale_from_accept_language?: bool|Param, // Whether to use the Accept-Language HTTP header to set the Request locale (only when the "_locale" request attribute is not passed). // Default: false + * set_content_language_from_locale?: bool|Param, // Whether to set the Content-Language HTTP header on the Response using the Request locale. // Default: false + * enabled_locales?: list, + * trusted_hosts?: list, + * trusted_proxies?: mixed, // Default: ["%env(default::SYMFONY_TRUSTED_PROXIES)%"] + * trusted_headers?: list, + * error_controller?: scalar|Param|null, // Default: "error_controller" + * handle_all_throwables?: bool|Param, // HttpKernel will handle all kinds of \Throwable. // Default: true + * csrf_protection?: bool|array{ + * enabled?: scalar|Param|null, // Default: null + * stateless_token_ids?: list, + * check_header?: scalar|Param|null, // Whether to check the CSRF token in a header in addition to a cookie when using stateless protection. // Default: false + * cookie_name?: scalar|Param|null, // The name of the cookie to use when using stateless protection. // Default: "csrf-token" + * }, + * form?: bool|array{ // Form configuration + * enabled?: bool|Param, // Default: true + * csrf_protection?: bool|array{ + * enabled?: scalar|Param|null, // Default: null + * token_id?: scalar|Param|null, // Default: null + * field_name?: scalar|Param|null, // Default: "_token" + * field_attr?: array, + * }, + * }, + * http_cache?: bool|array{ // HTTP cache configuration + * enabled?: bool|Param, // Default: false + * debug?: bool|Param, // Default: "%kernel.debug%" + * trace_level?: "none"|"short"|"full"|Param, + * trace_header?: scalar|Param|null, + * default_ttl?: int|Param, + * private_headers?: list, + * skip_response_headers?: list, + * allow_reload?: bool|Param, + * allow_revalidate?: bool|Param, + * stale_while_revalidate?: int|Param, + * stale_if_error?: int|Param, + * terminate_on_cache_hit?: bool|Param, + * }, + * esi?: bool|array{ // ESI configuration + * enabled?: bool|Param, // Default: false + * }, + * ssi?: bool|array{ // SSI configuration + * enabled?: bool|Param, // Default: false + * }, + * fragments?: bool|array{ // Fragments configuration + * enabled?: bool|Param, // Default: false + * hinclude_default_template?: scalar|Param|null, // Default: null + * path?: scalar|Param|null, // Default: "/_fragment" + * }, + * profiler?: bool|array{ // Profiler configuration + * enabled?: bool|Param, // Default: false + * collect?: bool|Param, // Default: true + * collect_parameter?: scalar|Param|null, // The name of the parameter to use to enable or disable collection on a per request basis. // Default: null + * only_exceptions?: bool|Param, // Default: false + * only_main_requests?: bool|Param, // Default: false + * dsn?: scalar|Param|null, // Default: "file:%kernel.cache_dir%/profiler" + * collect_serializer_data?: bool|Param, // Enables the serializer data collector and profiler panel. // Default: false + * }, + * workflows?: bool|array{ + * enabled?: bool|Param, // Default: false + * workflows?: array, + * definition_validators?: list, + * support_strategy?: scalar|Param|null, + * initial_marking?: list, + * events_to_dispatch?: list|null, + * places?: list, + * }>, + * transitions: list, + * to?: list, + * weight?: int|Param, // Default: 1 + * metadata?: list, + * }>, + * metadata?: list, + * }>, + * }, + * router?: bool|array{ // Router configuration + * enabled?: bool|Param, // Default: false + * resource: scalar|Param|null, + * type?: scalar|Param|null, + * cache_dir?: scalar|Param|null, // Deprecated: Setting the "framework.router.cache_dir.cache_dir" configuration option is deprecated. It will be removed in version 8.0. // Default: "%kernel.build_dir%" + * default_uri?: scalar|Param|null, // The default URI used to generate URLs in a non-HTTP context. // Default: null + * http_port?: scalar|Param|null, // Default: 80 + * https_port?: scalar|Param|null, // Default: 443 + * strict_requirements?: scalar|Param|null, // set to true to throw an exception when a parameter does not match the requirements set to false to disable exceptions when a parameter does not match the requirements (and return null instead) set to null to disable parameter checks against requirements 'true' is the preferred configuration in development mode, while 'false' or 'null' might be preferred in production // Default: true + * utf8?: bool|Param, // Default: true + * }, + * session?: bool|array{ // Session configuration + * enabled?: bool|Param, // Default: false + * storage_factory_id?: scalar|Param|null, // Default: "session.storage.factory.native" + * handler_id?: scalar|Param|null, // Defaults to using the native session handler, or to the native *file* session handler if "save_path" is not null. + * name?: scalar|Param|null, + * cookie_lifetime?: scalar|Param|null, + * cookie_path?: scalar|Param|null, + * cookie_domain?: scalar|Param|null, + * cookie_secure?: true|false|"auto"|Param, // Default: "auto" + * cookie_httponly?: bool|Param, // Default: true + * cookie_samesite?: null|"lax"|"strict"|"none"|Param, // Default: "lax" + * use_cookies?: bool|Param, + * gc_divisor?: scalar|Param|null, + * gc_probability?: scalar|Param|null, + * gc_maxlifetime?: scalar|Param|null, + * save_path?: scalar|Param|null, // Defaults to "%kernel.cache_dir%/sessions" if the "handler_id" option is not null. + * metadata_update_threshold?: int|Param, // Seconds to wait between 2 session metadata updates. // Default: 0 + * sid_length?: int|Param, // Deprecated: Setting the "framework.session.sid_length.sid_length" configuration option is deprecated. It will be removed in version 8.0. No alternative is provided as PHP 8.4 has deprecated the related option. + * sid_bits_per_character?: int|Param, // Deprecated: Setting the "framework.session.sid_bits_per_character.sid_bits_per_character" configuration option is deprecated. It will be removed in version 8.0. No alternative is provided as PHP 8.4 has deprecated the related option. + * }, + * request?: bool|array{ // Request configuration + * enabled?: bool|Param, // Default: false + * formats?: array>, + * }, + * assets?: bool|array{ // Assets configuration + * enabled?: bool|Param, // Default: true + * strict_mode?: bool|Param, // Throw an exception if an entry is missing from the manifest.json. // Default: false + * version_strategy?: scalar|Param|null, // Default: null + * version?: scalar|Param|null, // Default: null + * version_format?: scalar|Param|null, // Default: "%%s?%%s" + * json_manifest_path?: scalar|Param|null, // Default: null + * base_path?: scalar|Param|null, // Default: "" + * base_urls?: list, + * packages?: array, + * }>, + * }, + * asset_mapper?: bool|array{ // Asset Mapper configuration + * enabled?: bool|Param, // Default: true + * paths?: array, + * excluded_patterns?: list, + * exclude_dotfiles?: bool|Param, // If true, any files starting with "." will be excluded from the asset mapper. // Default: true + * server?: bool|Param, // If true, a "dev server" will return the assets from the public directory (true in "debug" mode only by default). // Default: true + * public_prefix?: scalar|Param|null, // The public path where the assets will be written to (and served from when "server" is true). // Default: "/assets/" + * missing_import_mode?: "strict"|"warn"|"ignore"|Param, // Behavior if an asset cannot be found when imported from JavaScript or CSS files - e.g. "import './non-existent.js'". "strict" means an exception is thrown, "warn" means a warning is logged, "ignore" means the import is left as-is. // Default: "warn" + * extensions?: array, + * importmap_path?: scalar|Param|null, // The path of the importmap.php file. // Default: "%kernel.project_dir%/importmap.php" + * importmap_polyfill?: scalar|Param|null, // The importmap name that will be used to load the polyfill. Set to false to disable. // Default: "es-module-shims" + * importmap_script_attributes?: array, + * vendor_dir?: scalar|Param|null, // The directory to store JavaScript vendors. // Default: "%kernel.project_dir%/assets/vendor" + * precompress?: bool|array{ // Precompress assets with Brotli, Zstandard and gzip. + * enabled?: bool|Param, // Default: false + * formats?: list, + * extensions?: list, + * }, + * }, + * translator?: bool|array{ // Translator configuration + * enabled?: bool|Param, // Default: true + * fallbacks?: list, + * logging?: bool|Param, // Default: false + * formatter?: scalar|Param|null, // Default: "translator.formatter.default" + * cache_dir?: scalar|Param|null, // Default: "%kernel.cache_dir%/translations" + * default_path?: scalar|Param|null, // The default path used to load translations. // Default: "%kernel.project_dir%/translations" + * paths?: list, + * pseudo_localization?: bool|array{ + * enabled?: bool|Param, // Default: false + * accents?: bool|Param, // Default: true + * expansion_factor?: float|Param, // Default: 1.0 + * brackets?: bool|Param, // Default: true + * parse_html?: bool|Param, // Default: false + * localizable_html_attributes?: list, + * }, + * providers?: array, + * locales?: list, + * }>, + * globals?: array, + * domain?: string|Param, + * }>, + * }, + * validation?: bool|array{ // Validation configuration + * enabled?: bool|Param, // Default: true + * cache?: scalar|Param|null, // Deprecated: Setting the "framework.validation.cache.cache" configuration option is deprecated. It will be removed in version 8.0. + * enable_attributes?: bool|Param, // Default: true + * static_method?: list, + * translation_domain?: scalar|Param|null, // Default: "validators" + * email_validation_mode?: "html5"|"html5-allow-no-tld"|"strict"|"loose"|Param, // Default: "html5" + * mapping?: array{ + * paths?: list, + * }, + * not_compromised_password?: bool|array{ + * enabled?: bool|Param, // When disabled, compromised passwords will be accepted as valid. // Default: true + * endpoint?: scalar|Param|null, // API endpoint for the NotCompromisedPassword Validator. // Default: null + * }, + * disable_translation?: bool|Param, // Default: false + * auto_mapping?: array, + * }>, + * }, + * annotations?: bool|array{ + * enabled?: bool|Param, // Default: false + * }, + * serializer?: bool|array{ // Serializer configuration + * enabled?: bool|Param, // Default: true + * enable_attributes?: bool|Param, // Default: true + * name_converter?: scalar|Param|null, + * circular_reference_handler?: scalar|Param|null, + * max_depth_handler?: scalar|Param|null, + * mapping?: array{ + * paths?: list, + * }, + * default_context?: list, + * named_serializers?: array, + * include_built_in_normalizers?: bool|Param, // Whether to include the built-in normalizers // Default: true + * include_built_in_encoders?: bool|Param, // Whether to include the built-in encoders // Default: true + * }>, + * }, + * property_access?: bool|array{ // Property access configuration + * enabled?: bool|Param, // Default: true + * magic_call?: bool|Param, // Default: false + * magic_get?: bool|Param, // Default: true + * magic_set?: bool|Param, // Default: true + * throw_exception_on_invalid_index?: bool|Param, // Default: false + * throw_exception_on_invalid_property_path?: bool|Param, // Default: true + * }, + * type_info?: bool|array{ // Type info configuration + * enabled?: bool|Param, // Default: true + * aliases?: array, + * }, + * property_info?: bool|array{ // Property info configuration + * enabled?: bool|Param, // Default: true + * with_constructor_extractor?: bool|Param, // Registers the constructor extractor. + * }, + * cache?: array{ // Cache configuration + * prefix_seed?: scalar|Param|null, // Used to namespace cache keys when using several apps with the same shared backend. // Default: "_%kernel.project_dir%.%kernel.container_class%" + * app?: scalar|Param|null, // App related cache pools configuration. // Default: "cache.adapter.filesystem" + * system?: scalar|Param|null, // System related cache pools configuration. // Default: "cache.adapter.system" + * directory?: scalar|Param|null, // Default: "%kernel.share_dir%/pools/app" + * default_psr6_provider?: scalar|Param|null, + * default_redis_provider?: scalar|Param|null, // Default: "redis://localhost" + * default_valkey_provider?: scalar|Param|null, // Default: "valkey://localhost" + * default_memcached_provider?: scalar|Param|null, // Default: "memcached://localhost" + * default_doctrine_dbal_provider?: scalar|Param|null, // Default: "database_connection" + * default_pdo_provider?: scalar|Param|null, // Default: null + * pools?: array, + * tags?: scalar|Param|null, // Default: null + * public?: bool|Param, // Default: false + * default_lifetime?: scalar|Param|null, // Default lifetime of the pool. + * provider?: scalar|Param|null, // Overwrite the setting from the default provider for this adapter. + * early_expiration_message_bus?: scalar|Param|null, + * clearer?: scalar|Param|null, + * }>, + * }, + * php_errors?: array{ // PHP errors handling configuration + * log?: mixed, // Use the application logger instead of the PHP logger for logging PHP errors. // Default: true + * throw?: bool|Param, // Throw PHP errors as \ErrorException instances. // Default: true + * }, + * exceptions?: array, + * web_link?: bool|array{ // Web links configuration + * enabled?: bool|Param, // Default: true + * }, + * lock?: bool|string|array{ // Lock configuration + * enabled?: bool|Param, // Default: false + * resources?: array>, + * }, + * semaphore?: bool|string|array{ // Semaphore configuration + * enabled?: bool|Param, // Default: false + * resources?: array, + * }, + * messenger?: bool|array{ // Messenger configuration + * enabled?: bool|Param, // Default: true + * routing?: array, + * }>, + * serializer?: array{ + * default_serializer?: scalar|Param|null, // Service id to use as the default serializer for the transports. // Default: "messenger.transport.native_php_serializer" + * symfony_serializer?: array{ + * format?: scalar|Param|null, // Serialization format for the messenger.transport.symfony_serializer service (which is not the serializer used by default). // Default: "json" + * context?: array, + * }, + * }, + * transports?: array, + * failure_transport?: scalar|Param|null, // Transport name to send failed messages to (after all retries have failed). // Default: null + * retry_strategy?: string|array{ + * service?: scalar|Param|null, // Service id to override the retry strategy entirely. // Default: null + * max_retries?: int|Param, // Default: 3 + * delay?: int|Param, // Time in ms to delay (or the initial value when multiplier is used). // Default: 1000 + * multiplier?: float|Param, // If greater than 1, delay will grow exponentially for each retry: this delay = (delay * (multiple ^ retries)). // Default: 2 + * max_delay?: int|Param, // Max time in ms that a retry should ever be delayed (0 = infinite). // Default: 0 + * jitter?: float|Param, // Randomness to apply to the delay (between 0 and 1). // Default: 0.1 + * }, + * rate_limiter?: scalar|Param|null, // Rate limiter name to use when processing messages. // Default: null + * }>, + * failure_transport?: scalar|Param|null, // Transport name to send failed messages to (after all retries have failed). // Default: null + * stop_worker_on_signals?: list, + * default_bus?: scalar|Param|null, // Default: null + * buses?: array, + * }>, + * }>, + * }, + * scheduler?: bool|array{ // Scheduler configuration + * enabled?: bool|Param, // Default: false + * }, + * disallow_search_engine_index?: bool|Param, // Enabled by default when debug is enabled. // Default: true + * http_client?: bool|array{ // HTTP Client configuration + * enabled?: bool|Param, // Default: true + * max_host_connections?: int|Param, // The maximum number of connections to a single host. + * default_options?: array{ + * headers?: array, + * vars?: array, + * max_redirects?: int|Param, // The maximum number of redirects to follow. + * http_version?: scalar|Param|null, // The default HTTP version, typically 1.1 or 2.0, leave to null for the best version. + * resolve?: array, + * proxy?: scalar|Param|null, // The URL of the proxy to pass requests through or null for automatic detection. + * no_proxy?: scalar|Param|null, // A comma separated list of hosts that do not require a proxy to be reached. + * timeout?: float|Param, // The idle timeout, defaults to the "default_socket_timeout" ini parameter. + * max_duration?: float|Param, // The maximum execution time for the request+response as a whole. + * bindto?: scalar|Param|null, // A network interface name, IP address, a host name or a UNIX socket to bind to. + * verify_peer?: bool|Param, // Indicates if the peer should be verified in a TLS context. + * verify_host?: bool|Param, // Indicates if the host should exist as a certificate common name. + * cafile?: scalar|Param|null, // A certificate authority file. + * capath?: scalar|Param|null, // A directory that contains multiple certificate authority files. + * local_cert?: scalar|Param|null, // A PEM formatted certificate file. + * local_pk?: scalar|Param|null, // A private key file. + * passphrase?: scalar|Param|null, // The passphrase used to encrypt the "local_pk" file. + * ciphers?: scalar|Param|null, // A list of TLS ciphers separated by colons, commas or spaces (e.g. "RC3-SHA:TLS13-AES-128-GCM-SHA256"...) + * peer_fingerprint?: array{ // Associative array: hashing algorithm => hash(es). + * sha1?: mixed, + * pin-sha256?: mixed, + * md5?: mixed, + * }, + * crypto_method?: scalar|Param|null, // The minimum version of TLS to accept; must be one of STREAM_CRYPTO_METHOD_TLSv*_CLIENT constants. + * extra?: array, + * rate_limiter?: scalar|Param|null, // Rate limiter name to use for throttling requests. // Default: null + * caching?: bool|array{ // Caching configuration. + * enabled?: bool|Param, // Default: false + * cache_pool?: string|Param, // The taggable cache pool to use for storing the responses. // Default: "cache.http_client" + * shared?: bool|Param, // Indicates whether the cache is shared (public) or private. // Default: true + * max_ttl?: int|Param, // The maximum TTL (in seconds) allowed for cached responses. Null means no cap. // Default: null + * }, + * retry_failed?: bool|array{ + * enabled?: bool|Param, // Default: false + * retry_strategy?: scalar|Param|null, // service id to override the retry strategy. // Default: null + * http_codes?: array, + * }>, + * max_retries?: int|Param, // Default: 3 + * delay?: int|Param, // Time in ms to delay (or the initial value when multiplier is used). // Default: 1000 + * multiplier?: float|Param, // If greater than 1, delay will grow exponentially for each retry: delay * (multiple ^ retries). // Default: 2 + * max_delay?: int|Param, // Max time in ms that a retry should ever be delayed (0 = infinite). // Default: 0 + * jitter?: float|Param, // Randomness in percent (between 0 and 1) to apply to the delay. // Default: 0.1 + * }, + * }, + * mock_response_factory?: scalar|Param|null, // The id of the service that should generate mock responses. It should be either an invokable or an iterable. + * scoped_clients?: array, + * headers?: array, + * max_redirects?: int|Param, // The maximum number of redirects to follow. + * http_version?: scalar|Param|null, // The default HTTP version, typically 1.1 or 2.0, leave to null for the best version. + * resolve?: array, + * proxy?: scalar|Param|null, // The URL of the proxy to pass requests through or null for automatic detection. + * no_proxy?: scalar|Param|null, // A comma separated list of hosts that do not require a proxy to be reached. + * timeout?: float|Param, // The idle timeout, defaults to the "default_socket_timeout" ini parameter. + * max_duration?: float|Param, // The maximum execution time for the request+response as a whole. + * bindto?: scalar|Param|null, // A network interface name, IP address, a host name or a UNIX socket to bind to. + * verify_peer?: bool|Param, // Indicates if the peer should be verified in a TLS context. + * verify_host?: bool|Param, // Indicates if the host should exist as a certificate common name. + * cafile?: scalar|Param|null, // A certificate authority file. + * capath?: scalar|Param|null, // A directory that contains multiple certificate authority files. + * local_cert?: scalar|Param|null, // A PEM formatted certificate file. + * local_pk?: scalar|Param|null, // A private key file. + * passphrase?: scalar|Param|null, // The passphrase used to encrypt the "local_pk" file. + * ciphers?: scalar|Param|null, // A list of TLS ciphers separated by colons, commas or spaces (e.g. "RC3-SHA:TLS13-AES-128-GCM-SHA256"...). + * peer_fingerprint?: array{ // Associative array: hashing algorithm => hash(es). + * sha1?: mixed, + * pin-sha256?: mixed, + * md5?: mixed, + * }, + * crypto_method?: scalar|Param|null, // The minimum version of TLS to accept; must be one of STREAM_CRYPTO_METHOD_TLSv*_CLIENT constants. + * extra?: array, + * rate_limiter?: scalar|Param|null, // Rate limiter name to use for throttling requests. // Default: null + * caching?: bool|array{ // Caching configuration. + * enabled?: bool|Param, // Default: false + * cache_pool?: string|Param, // The taggable cache pool to use for storing the responses. // Default: "cache.http_client" + * shared?: bool|Param, // Indicates whether the cache is shared (public) or private. // Default: true + * max_ttl?: int|Param, // The maximum TTL (in seconds) allowed for cached responses. Null means no cap. // Default: null + * }, + * retry_failed?: bool|array{ + * enabled?: bool|Param, // Default: false + * retry_strategy?: scalar|Param|null, // service id to override the retry strategy. // Default: null + * http_codes?: array, + * }>, + * max_retries?: int|Param, // Default: 3 + * delay?: int|Param, // Time in ms to delay (or the initial value when multiplier is used). // Default: 1000 + * multiplier?: float|Param, // If greater than 1, delay will grow exponentially for each retry: delay * (multiple ^ retries). // Default: 2 + * max_delay?: int|Param, // Max time in ms that a retry should ever be delayed (0 = infinite). // Default: 0 + * jitter?: float|Param, // Randomness in percent (between 0 and 1) to apply to the delay. // Default: 0.1 + * }, + * }>, + * }, + * mailer?: bool|array{ // Mailer configuration + * enabled?: bool|Param, // Default: true + * message_bus?: scalar|Param|null, // The message bus to use. Defaults to the default bus if the Messenger component is installed. // Default: null + * dsn?: scalar|Param|null, // Default: null + * transports?: array, + * envelope?: array{ // Mailer Envelope configuration + * sender?: scalar|Param|null, + * recipients?: list, + * allowed_recipients?: list, + * }, + * headers?: array, + * dkim_signer?: bool|array{ // DKIM signer configuration + * enabled?: bool|Param, // Default: false + * key?: scalar|Param|null, // Key content, or path to key (in PEM format with the `file://` prefix) // Default: "" + * domain?: scalar|Param|null, // Default: "" + * select?: scalar|Param|null, // Default: "" + * passphrase?: scalar|Param|null, // The private key passphrase // Default: "" + * options?: array, + * }, + * smime_signer?: bool|array{ // S/MIME signer configuration + * enabled?: bool|Param, // Default: false + * key?: scalar|Param|null, // Path to key (in PEM format) // Default: "" + * certificate?: scalar|Param|null, // Path to certificate (in PEM format without the `file://` prefix) // Default: "" + * passphrase?: scalar|Param|null, // The private key passphrase // Default: null + * extra_certificates?: scalar|Param|null, // Default: null + * sign_options?: int|Param, // Default: null + * }, + * smime_encrypter?: bool|array{ // S/MIME encrypter configuration + * enabled?: bool|Param, // Default: false + * repository?: scalar|Param|null, // S/MIME certificate repository service. This service shall implement the `Symfony\Component\Mailer\EventListener\SmimeCertificateRepositoryInterface`. // Default: "" + * cipher?: int|Param, // A set of algorithms used to encrypt the message // Default: null + * }, + * }, + * secrets?: bool|array{ + * enabled?: bool|Param, // Default: true + * vault_directory?: scalar|Param|null, // Default: "%kernel.project_dir%/config/secrets/%kernel.runtime_environment%" + * local_dotenv_file?: scalar|Param|null, // Default: "%kernel.project_dir%/.env.%kernel.environment%.local" + * decryption_env_var?: scalar|Param|null, // Default: "base64:default::SYMFONY_DECRYPTION_SECRET" + * }, + * notifier?: bool|array{ // Notifier configuration + * enabled?: bool|Param, // Default: true + * message_bus?: scalar|Param|null, // The message bus to use. Defaults to the default bus if the Messenger component is installed. // Default: null + * chatter_transports?: array, + * texter_transports?: array, + * notification_on_failed_messages?: bool|Param, // Default: false + * channel_policy?: array>, + * admin_recipients?: list, + * }, + * rate_limiter?: bool|array{ // Rate limiter configuration + * enabled?: bool|Param, // Default: true + * limiters?: array, + * limit?: int|Param, // The maximum allowed hits in a fixed interval or burst. + * interval?: scalar|Param|null, // Configures the fixed interval if "policy" is set to "fixed_window" or "sliding_window". The value must be a number followed by "second", "minute", "hour", "day", "week" or "month" (or their plural equivalent). + * rate?: array{ // Configures the fill rate if "policy" is set to "token_bucket". + * interval?: scalar|Param|null, // Configures the rate interval. The value must be a number followed by "second", "minute", "hour", "day", "week" or "month" (or their plural equivalent). + * amount?: int|Param, // Amount of tokens to add each interval. // Default: 1 + * }, + * }>, + * }, + * uid?: bool|array{ // Uid configuration + * enabled?: bool|Param, // Default: false + * default_uuid_version?: 7|6|4|1|Param, // Default: 7 + * name_based_uuid_version?: 5|3|Param, // Default: 5 + * name_based_uuid_namespace?: scalar|Param|null, + * time_based_uuid_version?: 7|6|1|Param, // Default: 7 + * time_based_uuid_node?: scalar|Param|null, + * }, + * html_sanitizer?: bool|array{ // HtmlSanitizer configuration + * enabled?: bool|Param, // Default: false + * sanitizers?: array, + * block_elements?: list, + * drop_elements?: list, + * allow_attributes?: array, + * drop_attributes?: array, + * force_attributes?: array>, + * force_https_urls?: bool|Param, // Transforms URLs using the HTTP scheme to use the HTTPS scheme instead. // Default: false + * allowed_link_schemes?: list, + * allowed_link_hosts?: list|null, + * allow_relative_links?: bool|Param, // Allows relative URLs to be used in links href attributes. // Default: false + * allowed_media_schemes?: list, + * allowed_media_hosts?: list|null, + * allow_relative_medias?: bool|Param, // Allows relative URLs to be used in media source attributes (img, audio, video, ...). // Default: false + * with_attribute_sanitizers?: list, + * without_attribute_sanitizers?: list, + * max_input_length?: int|Param, // The maximum length allowed for the sanitized input. // Default: 0 + * }>, + * }, + * webhook?: bool|array{ // Webhook configuration + * enabled?: bool|Param, // Default: false + * message_bus?: scalar|Param|null, // The message bus to use. // Default: "messenger.default_bus" + * routing?: array, + * }, + * remote-event?: bool|array{ // RemoteEvent configuration + * enabled?: bool|Param, // Default: false + * }, + * json_streamer?: bool|array{ // JSON streamer configuration + * enabled?: bool|Param, // Default: false + * }, + * } + * @psalm-type DoctrineConfig = array{ + * dbal?: array{ + * default_connection?: scalar|Param|null, + * types?: array, + * driver_schemes?: array, + * connections?: array, + * mapping_types?: array, + * default_table_options?: array, + * schema_manager_factory?: scalar|Param|null, // Default: "doctrine.dbal.legacy_schema_manager_factory" + * result_cache?: scalar|Param|null, + * slaves?: array, + * replicas?: array, + * }>, + * }, + * orm?: array{ + * default_entity_manager?: scalar|Param|null, + * auto_generate_proxy_classes?: scalar|Param|null, // Auto generate mode possible values are: "NEVER", "ALWAYS", "FILE_NOT_EXISTS", "EVAL", "FILE_NOT_EXISTS_OR_CHANGED", this option is ignored when the "enable_native_lazy_objects" option is true // Default: false + * enable_lazy_ghost_objects?: bool|Param, // Enables the new implementation of proxies based on lazy ghosts instead of using the legacy implementation // Default: true + * enable_native_lazy_objects?: bool|Param, // Enables the new native implementation of PHP lazy objects instead of generated proxies // Default: false + * proxy_dir?: scalar|Param|null, // Configures the path where generated proxy classes are saved when using non-native lazy objects, this option is ignored when the "enable_native_lazy_objects" option is true // Default: "%kernel.build_dir%/doctrine/orm/Proxies" + * proxy_namespace?: scalar|Param|null, // Defines the root namespace for generated proxy classes when using non-native lazy objects, this option is ignored when the "enable_native_lazy_objects" option is true // Default: "Proxies" + * controller_resolver?: bool|array{ + * enabled?: bool|Param, // Default: true + * auto_mapping?: bool|Param|null, // Set to false to disable using route placeholders as lookup criteria when the primary key doesn't match the argument name // Default: null + * evict_cache?: bool|Param, // Set to true to fetch the entity from the database instead of using the cache, if any // Default: false + * }, + * entity_managers?: array, + * }>, + * }>, + * }, + * connection?: scalar|Param|null, + * class_metadata_factory_name?: scalar|Param|null, // Default: "Doctrine\\ORM\\Mapping\\ClassMetadataFactory" + * default_repository_class?: scalar|Param|null, // Default: "Doctrine\\ORM\\EntityRepository" + * auto_mapping?: scalar|Param|null, // Default: false + * naming_strategy?: scalar|Param|null, // Default: "doctrine.orm.naming_strategy.default" + * quote_strategy?: scalar|Param|null, // Default: "doctrine.orm.quote_strategy.default" + * typed_field_mapper?: scalar|Param|null, // Default: "doctrine.orm.typed_field_mapper.default" + * entity_listener_resolver?: scalar|Param|null, // Default: null + * fetch_mode_subselect_batch_size?: scalar|Param|null, + * repository_factory?: scalar|Param|null, // Default: "doctrine.orm.container_repository_factory" + * schema_ignore_classes?: list, + * report_fields_where_declared?: bool|Param, // Set to "true" to opt-in to the new mapping driver mode that was added in Doctrine ORM 2.16 and will be mandatory in ORM 3.0. See https://github.com/doctrine/orm/pull/10455. // Default: true + * validate_xml_mapping?: bool|Param, // Set to "true" to opt-in to the new mapping driver mode that was added in Doctrine ORM 2.14. See https://github.com/doctrine/orm/pull/6728. // Default: false + * second_level_cache?: array{ + * region_cache_driver?: string|array{ + * type?: scalar|Param|null, // Default: null + * id?: scalar|Param|null, + * pool?: scalar|Param|null, + * }, + * region_lock_lifetime?: scalar|Param|null, // Default: 60 + * log_enabled?: bool|Param, // Default: true + * region_lifetime?: scalar|Param|null, // Default: 3600 + * enabled?: bool|Param, // Default: true + * factory?: scalar|Param|null, + * regions?: array, + * loggers?: array, + * }, + * hydrators?: array, + * mappings?: array, + * dql?: array{ + * string_functions?: array, + * numeric_functions?: array, + * datetime_functions?: array, + * }, + * filters?: array, + * }>, + * identity_generation_preferences?: array, + * }>, + * resolve_target_entities?: array, + * }, + * } + * @psalm-type DoctrineMigrationsConfig = array{ + * enable_service_migrations?: bool|Param, // Whether to enable fetching migrations from the service container. // Default: false + * migrations_paths?: array, + * services?: array, + * factories?: array, + * storage?: array{ // Storage to use for migration status metadata. + * table_storage?: array{ // The default metadata storage, implemented as a table in the database. + * table_name?: scalar|Param|null, // Default: null + * version_column_name?: scalar|Param|null, // Default: null + * version_column_length?: scalar|Param|null, // Default: null + * executed_at_column_name?: scalar|Param|null, // Default: null + * execution_time_column_name?: scalar|Param|null, // Default: null + * }, + * }, + * migrations?: list, + * connection?: scalar|Param|null, // Connection name to use for the migrations database. // Default: null + * em?: scalar|Param|null, // Entity manager name to use for the migrations database (available when doctrine/orm is installed). // Default: null + * all_or_nothing?: scalar|Param|null, // Run all migrations in a transaction. // Default: false + * check_database_platform?: scalar|Param|null, // Adds an extra check in the generated migrations to allow execution only on the same platform as they were initially generated on. // Default: true + * custom_template?: scalar|Param|null, // Custom template path for generated migration classes. // Default: null + * organize_migrations?: scalar|Param|null, // Organize migrations mode. Possible values are: "BY_YEAR", "BY_YEAR_AND_MONTH", false // Default: false + * enable_profiler?: bool|Param, // Whether or not to enable the profiler collector to calculate and visualize migration status. This adds some queries overhead. // Default: false + * transactional?: bool|Param, // Whether or not to wrap migrations in a single transaction. // Default: true + * } + * @psalm-type DebugConfig = array{ + * max_items?: int|Param, // Max number of displayed items past the first level, -1 means no limit. // Default: 2500 + * min_depth?: int|Param, // Minimum tree depth to clone all the items, 1 is default. // Default: 1 + * max_string_length?: int|Param, // Max length of displayed strings, -1 means no limit. // Default: -1 + * dump_destination?: scalar|Param|null, // A stream URL where dumps should be written to. // Default: null + * theme?: "dark"|"light"|Param, // Changes the color of the dump() output when rendered directly on the templating. "dark" (default) or "light". // Default: "dark" + * } + * @psalm-type TwigConfig = array{ + * form_themes?: list, + * globals?: array, + * autoescape_service?: scalar|Param|null, // Default: null + * autoescape_service_method?: scalar|Param|null, // Default: null + * base_template_class?: scalar|Param|null, // Deprecated: The child node "base_template_class" at path "twig.base_template_class" is deprecated. + * cache?: scalar|Param|null, // Default: true + * charset?: scalar|Param|null, // Default: "%kernel.charset%" + * debug?: bool|Param, // Default: "%kernel.debug%" + * strict_variables?: bool|Param, // Default: "%kernel.debug%" + * auto_reload?: scalar|Param|null, + * optimizations?: int|Param, + * default_path?: scalar|Param|null, // The default path used to load templates. // Default: "%kernel.project_dir%/templates" + * file_name_pattern?: list, + * paths?: array, + * date?: array{ // The default format options used by the date filter. + * format?: scalar|Param|null, // Default: "F j, Y H:i" + * interval_format?: scalar|Param|null, // Default: "%d days" + * timezone?: scalar|Param|null, // The timezone used when formatting dates, when set to null, the timezone returned by date_default_timezone_get() is used. // Default: null + * }, + * number_format?: array{ // The default format options for the number_format filter. + * decimals?: int|Param, // Default: 0 + * decimal_point?: scalar|Param|null, // Default: "." + * thousands_separator?: scalar|Param|null, // Default: "," + * }, + * mailer?: array{ + * html_to_text_converter?: scalar|Param|null, // A service implementing the "Symfony\Component\Mime\HtmlToTextConverter\HtmlToTextConverterInterface". // Default: null + * }, + * } + * @psalm-type WebProfilerConfig = array{ + * toolbar?: bool|array{ // Profiler toolbar configuration + * enabled?: bool|Param, // Default: false + * ajax_replace?: bool|Param, // Replace toolbar on AJAX requests // Default: false + * }, + * intercept_redirects?: bool|Param, // Default: false + * excluded_ajax_paths?: scalar|Param|null, // Default: "^/((index|app(_[\\w]+)?)\\.php/)?_wdt" + * } + * @psalm-type StimulusConfig = array{ + * controller_paths?: list, + * controllers_json?: scalar|Param|null, // Default: "%kernel.project_dir%/assets/controllers.json" + * } + * @psalm-type TurboConfig = array{ + * broadcast?: bool|array{ + * enabled?: bool|Param, // Default: true + * entity_template_prefixes?: list, + * doctrine_orm?: bool|array{ // Enable the Doctrine ORM integration + * enabled?: bool|Param, // Default: true + * }, + * }, + * default_transport?: scalar|Param|null, // Default: "default" + * } + * @psalm-type SecurityConfig = array{ + * access_denied_url?: scalar|Param|null, // Default: null + * session_fixation_strategy?: "none"|"migrate"|"invalidate"|Param, // Default: "migrate" + * hide_user_not_found?: bool|Param, // Deprecated: The "hide_user_not_found" option is deprecated and will be removed in 8.0. Use the "expose_security_errors" option instead. + * expose_security_errors?: \Symfony\Component\Security\Http\Authentication\ExposeSecurityLevel::None|\Symfony\Component\Security\Http\Authentication\ExposeSecurityLevel::AccountStatus|\Symfony\Component\Security\Http\Authentication\ExposeSecurityLevel::All|Param, // Default: "none" + * erase_credentials?: bool|Param, // Default: true + * access_decision_manager?: array{ + * strategy?: "affirmative"|"consensus"|"unanimous"|"priority"|Param, + * service?: scalar|Param|null, + * strategy_service?: scalar|Param|null, + * allow_if_all_abstain?: bool|Param, // Default: false + * allow_if_equal_granted_denied?: bool|Param, // Default: true + * }, + * password_hashers?: array, + * hash_algorithm?: scalar|Param|null, // Name of hashing algorithm for PBKDF2 (i.e. sha256, sha512, etc..) See hash_algos() for a list of supported algorithms. // Default: "sha512" + * key_length?: scalar|Param|null, // Default: 40 + * ignore_case?: bool|Param, // Default: false + * encode_as_base64?: bool|Param, // Default: true + * iterations?: scalar|Param|null, // Default: 5000 + * cost?: int|Param, // Default: null + * memory_cost?: scalar|Param|null, // Default: null + * time_cost?: scalar|Param|null, // Default: null + * id?: scalar|Param|null, + * }>, + * providers?: array, + * }, + * entity?: array{ + * class: scalar|Param|null, // The full entity class name of your user class. + * property?: scalar|Param|null, // Default: null + * manager_name?: scalar|Param|null, // Default: null + * }, + * memory?: array{ + * users?: array, + * }>, + * }, + * ldap?: array{ + * service: scalar|Param|null, + * base_dn: scalar|Param|null, + * search_dn?: scalar|Param|null, // Default: null + * search_password?: scalar|Param|null, // Default: null + * extra_fields?: list, + * default_roles?: list, + * role_fetcher?: scalar|Param|null, // Default: null + * uid_key?: scalar|Param|null, // Default: "sAMAccountName" + * filter?: scalar|Param|null, // Default: "({uid_key}={user_identifier})" + * password_attribute?: scalar|Param|null, // Default: null + * }, + * }>, + * firewalls: array, + * security?: bool|Param, // Default: true + * user_checker?: scalar|Param|null, // The UserChecker to use when authenticating users in this firewall. // Default: "security.user_checker" + * request_matcher?: scalar|Param|null, + * access_denied_url?: scalar|Param|null, + * access_denied_handler?: scalar|Param|null, + * entry_point?: scalar|Param|null, // An enabled authenticator name or a service id that implements "Symfony\Component\Security\Http\EntryPoint\AuthenticationEntryPointInterface". + * provider?: scalar|Param|null, + * stateless?: bool|Param, // Default: false + * lazy?: bool|Param, // Default: false + * context?: scalar|Param|null, + * logout?: array{ + * enable_csrf?: bool|Param|null, // Default: null + * csrf_token_id?: scalar|Param|null, // Default: "logout" + * csrf_parameter?: scalar|Param|null, // Default: "_csrf_token" + * csrf_token_manager?: scalar|Param|null, + * path?: scalar|Param|null, // Default: "/logout" + * target?: scalar|Param|null, // Default: "/" + * invalidate_session?: bool|Param, // Default: true + * clear_site_data?: list<"*"|"cache"|"cookies"|"storage"|"executionContexts"|Param>, + * delete_cookies?: array, + * }, + * switch_user?: array{ + * provider?: scalar|Param|null, + * parameter?: scalar|Param|null, // Default: "_switch_user" + * role?: scalar|Param|null, // Default: "ROLE_ALLOWED_TO_SWITCH" + * target_route?: scalar|Param|null, // Default: null + * }, + * required_badges?: list, + * custom_authenticators?: list, + * login_throttling?: array{ + * limiter?: scalar|Param|null, // A service id implementing "Symfony\Component\HttpFoundation\RateLimiter\RequestRateLimiterInterface". + * max_attempts?: int|Param, // Default: 5 + * interval?: scalar|Param|null, // Default: "1 minute" + * lock_factory?: scalar|Param|null, // The service ID of the lock factory used by the login rate limiter (or null to disable locking). // Default: null + * cache_pool?: string|Param, // The cache pool to use for storing the limiter state // Default: "cache.rate_limiter" + * storage_service?: string|Param, // The service ID of a custom storage implementation, this precedes any configured "cache_pool" // Default: null + * }, + * x509?: array{ + * provider?: scalar|Param|null, + * user?: scalar|Param|null, // Default: "SSL_CLIENT_S_DN_Email" + * credentials?: scalar|Param|null, // Default: "SSL_CLIENT_S_DN" + * user_identifier?: scalar|Param|null, // Default: "emailAddress" + * }, + * remote_user?: array{ + * provider?: scalar|Param|null, + * user?: scalar|Param|null, // Default: "REMOTE_USER" + * }, + * oauth2?: array, + * login_link?: array{ + * check_route: scalar|Param|null, // Route that will validate the login link - e.g. "app_login_link_verify". + * check_post_only?: scalar|Param|null, // If true, only HTTP POST requests to "check_route" will be handled by the authenticator. // Default: false + * signature_properties: list, + * lifetime?: int|Param, // The lifetime of the login link in seconds. // Default: 600 + * max_uses?: int|Param, // Max number of times a login link can be used - null means unlimited within lifetime. // Default: null + * used_link_cache?: scalar|Param|null, // Cache service id used to expired links of max_uses is set. + * success_handler?: scalar|Param|null, // A service id that implements Symfony\Component\Security\Http\Authentication\AuthenticationSuccessHandlerInterface. + * failure_handler?: scalar|Param|null, // A service id that implements Symfony\Component\Security\Http\Authentication\AuthenticationFailureHandlerInterface. + * provider?: scalar|Param|null, // The user provider to load users from. + * secret?: scalar|Param|null, // Default: "%kernel.secret%" + * always_use_default_target_path?: bool|Param, // Default: false + * default_target_path?: scalar|Param|null, // Default: "/" + * login_path?: scalar|Param|null, // Default: "/login" + * target_path_parameter?: scalar|Param|null, // Default: "_target_path" + * use_referer?: bool|Param, // Default: false + * failure_path?: scalar|Param|null, // Default: null + * failure_forward?: bool|Param, // Default: false + * failure_path_parameter?: scalar|Param|null, // Default: "_failure_path" + * }, + * form_login?: array{ + * provider?: scalar|Param|null, + * remember_me?: bool|Param, // Default: true + * success_handler?: scalar|Param|null, + * failure_handler?: scalar|Param|null, + * check_path?: scalar|Param|null, // Default: "/login_check" + * use_forward?: bool|Param, // Default: false + * login_path?: scalar|Param|null, // Default: "/login" + * username_parameter?: scalar|Param|null, // Default: "_username" + * password_parameter?: scalar|Param|null, // Default: "_password" + * csrf_parameter?: scalar|Param|null, // Default: "_csrf_token" + * csrf_token_id?: scalar|Param|null, // Default: "authenticate" + * enable_csrf?: bool|Param, // Default: false + * post_only?: bool|Param, // Default: true + * form_only?: bool|Param, // Default: false + * always_use_default_target_path?: bool|Param, // Default: false + * default_target_path?: scalar|Param|null, // Default: "/" + * target_path_parameter?: scalar|Param|null, // Default: "_target_path" + * use_referer?: bool|Param, // Default: false + * failure_path?: scalar|Param|null, // Default: null + * failure_forward?: bool|Param, // Default: false + * failure_path_parameter?: scalar|Param|null, // Default: "_failure_path" + * }, + * form_login_ldap?: array{ + * provider?: scalar|Param|null, + * remember_me?: bool|Param, // Default: true + * success_handler?: scalar|Param|null, + * failure_handler?: scalar|Param|null, + * check_path?: scalar|Param|null, // Default: "/login_check" + * use_forward?: bool|Param, // Default: false + * login_path?: scalar|Param|null, // Default: "/login" + * username_parameter?: scalar|Param|null, // Default: "_username" + * password_parameter?: scalar|Param|null, // Default: "_password" + * csrf_parameter?: scalar|Param|null, // Default: "_csrf_token" + * csrf_token_id?: scalar|Param|null, // Default: "authenticate" + * enable_csrf?: bool|Param, // Default: false + * post_only?: bool|Param, // Default: true + * form_only?: bool|Param, // Default: false + * always_use_default_target_path?: bool|Param, // Default: false + * default_target_path?: scalar|Param|null, // Default: "/" + * target_path_parameter?: scalar|Param|null, // Default: "_target_path" + * use_referer?: bool|Param, // Default: false + * failure_path?: scalar|Param|null, // Default: null + * failure_forward?: bool|Param, // Default: false + * failure_path_parameter?: scalar|Param|null, // Default: "_failure_path" + * service?: scalar|Param|null, // Default: "ldap" + * dn_string?: scalar|Param|null, // Default: "{user_identifier}" + * query_string?: scalar|Param|null, + * search_dn?: scalar|Param|null, // Default: "" + * search_password?: scalar|Param|null, // Default: "" + * }, + * json_login?: array{ + * provider?: scalar|Param|null, + * remember_me?: bool|Param, // Default: true + * success_handler?: scalar|Param|null, + * failure_handler?: scalar|Param|null, + * check_path?: scalar|Param|null, // Default: "/login_check" + * use_forward?: bool|Param, // Default: false + * login_path?: scalar|Param|null, // Default: "/login" + * username_path?: scalar|Param|null, // Default: "username" + * password_path?: scalar|Param|null, // Default: "password" + * }, + * json_login_ldap?: array{ + * provider?: scalar|Param|null, + * remember_me?: bool|Param, // Default: true + * success_handler?: scalar|Param|null, + * failure_handler?: scalar|Param|null, + * check_path?: scalar|Param|null, // Default: "/login_check" + * use_forward?: bool|Param, // Default: false + * login_path?: scalar|Param|null, // Default: "/login" + * username_path?: scalar|Param|null, // Default: "username" + * password_path?: scalar|Param|null, // Default: "password" + * service?: scalar|Param|null, // Default: "ldap" + * dn_string?: scalar|Param|null, // Default: "{user_identifier}" + * query_string?: scalar|Param|null, + * search_dn?: scalar|Param|null, // Default: "" + * search_password?: scalar|Param|null, // Default: "" + * }, + * access_token?: array{ + * provider?: scalar|Param|null, + * remember_me?: bool|Param, // Default: true + * success_handler?: scalar|Param|null, + * failure_handler?: scalar|Param|null, + * realm?: scalar|Param|null, // Default: null + * token_extractors?: list, + * token_handler: string|array{ + * id?: scalar|Param|null, + * oidc_user_info?: string|array{ + * base_uri: scalar|Param|null, // Base URI of the userinfo endpoint on the OIDC server, or the OIDC server URI to use the discovery (require "discovery" to be configured). + * discovery?: array{ // Enable the OIDC discovery. + * cache?: array{ + * id: scalar|Param|null, // Cache service id to use to cache the OIDC discovery configuration. + * }, + * }, + * claim?: scalar|Param|null, // Claim which contains the user identifier (e.g. sub, email, etc.). // Default: "sub" + * client?: scalar|Param|null, // HttpClient service id to use to call the OIDC server. + * }, + * oidc?: array{ + * discovery?: array{ // Enable the OIDC discovery. + * base_uri: list, + * cache?: array{ + * id: scalar|Param|null, // Cache service id to use to cache the OIDC discovery configuration. + * }, + * }, + * claim?: scalar|Param|null, // Claim which contains the user identifier (e.g.: sub, email..). // Default: "sub" + * audience: scalar|Param|null, // Audience set in the token, for validation purpose. + * issuers: list, + * algorithm?: array, + * algorithms: list, + * key?: scalar|Param|null, // Deprecated: The "key" option is deprecated and will be removed in 8.0. Use the "keyset" option instead. // JSON-encoded JWK used to sign the token (must contain a "kty" key). + * keyset?: scalar|Param|null, // JSON-encoded JWKSet used to sign the token (must contain a list of valid public keys). + * encryption?: bool|array{ + * enabled?: bool|Param, // Default: false + * enforce?: bool|Param, // When enabled, the token shall be encrypted. // Default: false + * algorithms: list, + * keyset: scalar|Param|null, // JSON-encoded JWKSet used to decrypt the token (must contain a list of valid private keys). + * }, + * }, + * cas?: array{ + * validation_url: scalar|Param|null, // CAS server validation URL + * prefix?: scalar|Param|null, // CAS prefix // Default: "cas" + * http_client?: scalar|Param|null, // HTTP Client service // Default: null + * }, + * oauth2?: scalar|Param|null, + * }, + * }, + * http_basic?: array{ + * provider?: scalar|Param|null, + * realm?: scalar|Param|null, // Default: "Secured Area" + * }, + * http_basic_ldap?: array{ + * provider?: scalar|Param|null, + * realm?: scalar|Param|null, // Default: "Secured Area" + * service?: scalar|Param|null, // Default: "ldap" + * dn_string?: scalar|Param|null, // Default: "{user_identifier}" + * query_string?: scalar|Param|null, + * search_dn?: scalar|Param|null, // Default: "" + * search_password?: scalar|Param|null, // Default: "" + * }, + * remember_me?: array{ + * secret?: scalar|Param|null, // Default: "%kernel.secret%" + * service?: scalar|Param|null, + * user_providers?: list, + * catch_exceptions?: bool|Param, // Default: true + * signature_properties?: list, + * token_provider?: string|array{ + * service?: scalar|Param|null, // The service ID of a custom remember-me token provider. + * doctrine?: bool|array{ + * enabled?: bool|Param, // Default: false + * connection?: scalar|Param|null, // Default: null + * }, + * }, + * token_verifier?: scalar|Param|null, // The service ID of a custom rememberme token verifier. + * name?: scalar|Param|null, // Default: "REMEMBERME" + * lifetime?: int|Param, // Default: 31536000 + * path?: scalar|Param|null, // Default: "/" + * domain?: scalar|Param|null, // Default: null + * secure?: true|false|"auto"|Param, // Default: false + * httponly?: bool|Param, // Default: true + * samesite?: null|"lax"|"strict"|"none"|Param, // Default: null + * always_remember_me?: bool|Param, // Default: false + * remember_me_parameter?: scalar|Param|null, // Default: "_remember_me" + * }, + * }>, + * access_control?: list, + * attributes?: array, + * route?: scalar|Param|null, // Default: null + * methods?: list, + * allow_if?: scalar|Param|null, // Default: null + * roles?: list, + * }>, + * role_hierarchy?: array>, + * } + * @psalm-type MonologConfig = array{ + * use_microseconds?: scalar|Param|null, // Default: true + * channels?: list, + * handlers?: array, + * excluded_http_codes?: list, + * }>, + * accepted_levels?: list, + * min_level?: scalar|Param|null, // Default: "DEBUG" + * max_level?: scalar|Param|null, // Default: "EMERGENCY" + * buffer_size?: scalar|Param|null, // Default: 0 + * flush_on_overflow?: bool|Param, // Default: false + * handler?: scalar|Param|null, + * url?: scalar|Param|null, + * exchange?: scalar|Param|null, + * exchange_name?: scalar|Param|null, // Default: "log" + * room?: scalar|Param|null, + * message_format?: scalar|Param|null, // Default: "text" + * api_version?: scalar|Param|null, // Default: null + * channel?: scalar|Param|null, // Default: null + * bot_name?: scalar|Param|null, // Default: "Monolog" + * use_attachment?: scalar|Param|null, // Default: true + * use_short_attachment?: scalar|Param|null, // Default: false + * include_extra?: scalar|Param|null, // Default: false + * icon_emoji?: scalar|Param|null, // Default: null + * webhook_url?: scalar|Param|null, + * exclude_fields?: list, + * team?: scalar|Param|null, + * notify?: scalar|Param|null, // Default: false + * nickname?: scalar|Param|null, // Default: "Monolog" + * token?: scalar|Param|null, + * region?: scalar|Param|null, + * source?: scalar|Param|null, + * use_ssl?: bool|Param, // Default: true + * user?: mixed, + * title?: scalar|Param|null, // Default: null + * host?: scalar|Param|null, // Default: null + * port?: scalar|Param|null, // Default: 514 + * config?: list, + * members?: list, + * connection_string?: scalar|Param|null, + * timeout?: scalar|Param|null, + * time?: scalar|Param|null, // Default: 60 + * deduplication_level?: scalar|Param|null, // Default: 400 + * store?: scalar|Param|null, // Default: null + * connection_timeout?: scalar|Param|null, + * persistent?: bool|Param, + * dsn?: scalar|Param|null, + * hub_id?: scalar|Param|null, // Default: null + * client_id?: scalar|Param|null, // Default: null + * auto_log_stacks?: scalar|Param|null, // Default: false + * release?: scalar|Param|null, // Default: null + * environment?: scalar|Param|null, // Default: null + * message_type?: scalar|Param|null, // Default: 0 + * parse_mode?: scalar|Param|null, // Default: null + * disable_webpage_preview?: bool|Param|null, // Default: null + * disable_notification?: bool|Param|null, // Default: null + * split_long_messages?: bool|Param, // Default: false + * delay_between_messages?: bool|Param, // Default: false + * topic?: int|Param, // Default: null + * factor?: int|Param, // Default: 1 + * tags?: list, + * console_formater_options?: mixed, // Deprecated: "monolog.handlers..console_formater_options.console_formater_options" is deprecated, use "monolog.handlers..console_formater_options.console_formatter_options" instead. + * console_formatter_options?: mixed, // Default: [] + * formatter?: scalar|Param|null, + * nested?: bool|Param, // Default: false + * publisher?: string|array{ + * id?: scalar|Param|null, + * hostname?: scalar|Param|null, + * port?: scalar|Param|null, // Default: 12201 + * chunk_size?: scalar|Param|null, // Default: 1420 + * encoder?: "json"|"compressed_json"|Param, + * }, + * mongo?: string|array{ + * id?: scalar|Param|null, + * host?: scalar|Param|null, + * port?: scalar|Param|null, // Default: 27017 + * user?: scalar|Param|null, + * pass?: scalar|Param|null, + * database?: scalar|Param|null, // Default: "monolog" + * collection?: scalar|Param|null, // Default: "logs" + * }, + * mongodb?: string|array{ + * id?: scalar|Param|null, // ID of a MongoDB\Client service + * uri?: scalar|Param|null, + * username?: scalar|Param|null, + * password?: scalar|Param|null, + * database?: scalar|Param|null, // Default: "monolog" + * collection?: scalar|Param|null, // Default: "logs" + * }, + * elasticsearch?: string|array{ + * id?: scalar|Param|null, + * hosts?: list, + * host?: scalar|Param|null, + * port?: scalar|Param|null, // Default: 9200 + * transport?: scalar|Param|null, // Default: "Http" + * user?: scalar|Param|null, // Default: null + * password?: scalar|Param|null, // Default: null + * }, + * index?: scalar|Param|null, // Default: "monolog" + * document_type?: scalar|Param|null, // Default: "logs" + * ignore_error?: scalar|Param|null, // Default: false + * redis?: string|array{ + * id?: scalar|Param|null, + * host?: scalar|Param|null, + * password?: scalar|Param|null, // Default: null + * port?: scalar|Param|null, // Default: 6379 + * database?: scalar|Param|null, // Default: 0 + * key_name?: scalar|Param|null, // Default: "monolog_redis" + * }, + * predis?: string|array{ + * id?: scalar|Param|null, + * host?: scalar|Param|null, + * }, + * from_email?: scalar|Param|null, + * to_email?: list, + * subject?: scalar|Param|null, + * content_type?: scalar|Param|null, // Default: null + * headers?: list, + * mailer?: scalar|Param|null, // Default: null + * email_prototype?: string|array{ + * id: scalar|Param|null, + * method?: scalar|Param|null, // Default: null + * }, + * lazy?: bool|Param, // Default: true + * verbosity_levels?: array{ + * VERBOSITY_QUIET?: scalar|Param|null, // Default: "ERROR" + * VERBOSITY_NORMAL?: scalar|Param|null, // Default: "WARNING" + * VERBOSITY_VERBOSE?: scalar|Param|null, // Default: "NOTICE" + * VERBOSITY_VERY_VERBOSE?: scalar|Param|null, // Default: "INFO" + * VERBOSITY_DEBUG?: scalar|Param|null, // Default: "DEBUG" + * }, + * channels?: string|array{ + * type?: scalar|Param|null, + * elements?: list, + * }, + * }>, + * } + * @psalm-type MakerConfig = array{ + * root_namespace?: scalar|Param|null, // Default: "App" + * generate_final_classes?: bool|Param, // Default: true + * generate_final_entities?: bool|Param, // Default: false + * } + * @psalm-type UxIconsConfig = array{ + * icon_dir?: scalar|Param|null, // The local directory where icons are stored. // Default: "%kernel.project_dir%/assets/icons" + * default_icon_attributes?: array, + * icon_sets?: array, + * }>, + * aliases?: array, + * iconify?: bool|array{ // Configuration for the remote icon service. + * enabled?: bool|Param, // Default: true + * on_demand?: bool|Param, // Whether to download icons "on demand". // Default: true + * endpoint?: scalar|Param|null, // The endpoint for the Iconify icons API. // Default: "https://api.iconify.design" + * }, + * ignore_not_found?: bool|Param, // Ignore error when an icon is not found. Set to 'true' to fail silently. // Default: false + * } + * @psalm-type LeagueOauth2ServerConfig = array{ + * authorization_server: array{ + * private_key: scalar|Param|null, // Full path to the private key file. How to generate a private key: https://oauth2.thephpleague.com/installation/#generating-public-and-private-keys + * private_key_passphrase?: scalar|Param|null, // Passphrase of the private key, if any // Default: null + * encryption_key: scalar|Param|null, // The plain string or the ascii safe string used to create a Defuse\Crypto\Key to be used as an encryption key. How to generate an encryption key: https://oauth2.thephpleague.com/installation/#string-password + * encryption_key_type?: scalar|Param|null, // The type of value of 'encryption_key' Should be either 'plain' or 'defuse' // Default: "plain" + * access_token_ttl?: scalar|Param|null, // How long the issued access token should be valid for. The value should be a valid interval: http://php.net/manual/en/dateinterval.construct.php#refsect1-dateinterval.construct-parameters // Default: "PT1H" + * refresh_token_ttl?: scalar|Param|null, // How long the issued refresh token should be valid for. The value should be a valid interval: http://php.net/manual/en/dateinterval.construct.php#refsect1-dateinterval.construct-parameters // Default: "P1M" + * auth_code_ttl?: scalar|Param|null, // How long the issued auth code should be valid for. The value should be a valid interval: http://php.net/manual/en/dateinterval.construct.php#refsect1-dateinterval.construct-parameters // Default: "PT10M" + * enable_client_credentials_grant?: bool|Param, // Whether to enable the client credentials grant // Default: true + * enable_password_grant?: bool|Param, // Whether to enable the password grant // Default: true + * enable_refresh_token_grant?: bool|Param, // Whether to enable the refresh token grant // Default: true + * enable_auth_code_grant?: bool|Param, // Whether to enable the authorization code grant // Default: true + * require_code_challenge_for_public_clients?: bool|Param, // Whether to require code challenge for public clients for the auth code grant // Default: true + * enable_implicit_grant?: bool|Param, // Whether to enable the implicit grant // Default: true + * persist_access_token?: bool|Param, // Whether to enable access token saving to persistence layer // Default: true + * response_type_class?: scalar|Param|null, // Define a custom ResponseType // Default: null + * revoke_refresh_tokens?: bool|Param, // Whether to revoke refresh tokens after they were used for all grant types // Default: true + * }, + * resource_server: array{ + * public_key: scalar|Param|null, // Full path to the public key file How to generate a public key: https://oauth2.thephpleague.com/installation/#generating-public-and-private-keys + * jwt_leeway?: scalar|Param|null, // The leeway in seconds to allow for clock skew in JWT verification. Default PT0S (no leeway). // Default: null + * }, + * scopes: array{ + * available: list, + * default: list, + * }, + * persistence: array{ // Configures different persistence methods that can be used by the bundle for saving client and token data. Only one persistence method can be configured at a time. + * doctrine?: array{ + * entity_manager?: scalar|Param|null, // Name of the entity manager that you wish to use for managing clients and tokens. // Default: "default" + * table_prefix?: scalar|Param|null, // Table name prefix. // Default: "oauth2_" + * }, + * in_memory?: scalar|Param|null, + * custom?: array{ + * access_token_manager: scalar|Param|null, // Service id of the custom access token manager + * authorization_code_manager: scalar|Param|null, // Service id of the custom authorization code manager + * client_manager: scalar|Param|null, // Service id of the custom client manager + * refresh_token_manager: scalar|Param|null, // Service id of the custom refresh token manager + * credentials_revoker: scalar|Param|null, // Service id of the custom credentials revoker + * }, + * }, + * client?: array{ + * classname?: scalar|Param|null, // Set a custom client class. Must be a League\Bundle\OAuth2ServerBundle\Model\AbstractClient // Default: "League\\Bundle\\OAuth2ServerBundle\\Model\\Client" + * }, + * role_prefix?: scalar|Param|null, // Set a custom prefix that replaces the default 'ROLE_OAUTH2_' role prefix // Default: "ROLE_OAUTH2_" + * } + * @psalm-type NelmioCorsConfig = array{ + * defaults?: array{ + * allow_credentials?: bool|Param, // Default: false + * allow_origin?: list, + * allow_headers?: list, + * allow_methods?: list, + * allow_private_network?: bool|Param, // Default: false + * expose_headers?: list, + * max_age?: scalar|Param|null, // Default: 0 + * hosts?: list, + * origin_regex?: bool|Param, // Default: false + * forced_allow_origin_value?: scalar|Param|null, // Default: null + * skip_same_as_origin?: bool|Param, // Default: true + * }, + * paths?: array, + * allow_headers?: list, + * allow_methods?: list, + * allow_private_network?: bool|Param, + * expose_headers?: list, + * max_age?: scalar|Param|null, // Default: 0 + * hosts?: list, + * origin_regex?: bool|Param, + * forced_allow_origin_value?: scalar|Param|null, // Default: null + * skip_same_as_origin?: bool|Param, + * }>, + * } + * @psalm-type MercureConfig = array{ + * hubs?: array, + * subscribe?: list, + * secret?: scalar|Param|null, // The JWT Secret to use. + * passphrase?: scalar|Param|null, // The JWT secret passphrase. // Default: "" + * algorithm?: scalar|Param|null, // The algorithm to use to sign the JWT // Default: "hmac.sha256" + * }, + * jwt_provider?: scalar|Param|null, // Deprecated: The child node "jwt_provider" at path "mercure.hubs..jwt_provider" is deprecated, use "jwt.provider" instead. // The ID of a service to call to generate the JSON Web Token. + * bus?: scalar|Param|null, // Name of the Messenger bus where the handler for this hub must be registered. Default to the default bus if Messenger is enabled. + * }>, + * default_hub?: scalar|Param|null, + * default_cookie_lifetime?: int|Param, // Default lifetime of the cookie containing the JWT, in seconds. Defaults to the value of "framework.session.cookie_lifetime". // Default: null + * enable_profiler?: bool|Param, // Deprecated: The child node "enable_profiler" at path "mercure.enable_profiler" is deprecated. // Enable Symfony Web Profiler integration. + * } + * @psalm-type AwsConfig = mixed + * @psalm-type DamaDoctrineTestConfig = array{ + * enable_static_connection?: mixed, // Default: true + * enable_static_meta_data_cache?: bool|Param, // Default: true + * enable_static_query_cache?: bool|Param, // Default: true + * connection_keys?: list, + * } + * @psalm-type ConfigType = array{ + * imports?: ImportsConfig, + * parameters?: ParametersConfig, + * services?: ServicesConfig, + * framework?: FrameworkConfig, + * doctrine?: DoctrineConfig, + * doctrine_migrations?: DoctrineMigrationsConfig, + * twig?: TwigConfig, + * stimulus?: StimulusConfig, + * turbo?: TurboConfig, + * security?: SecurityConfig, + * monolog?: MonologConfig, + * ux_icons?: UxIconsConfig, + * league_oauth2_server?: LeagueOauth2ServerConfig, + * nelmio_cors?: NelmioCorsConfig, + * mercure?: MercureConfig, + * aws?: AwsConfig, + * "when@dev"?: array{ + * imports?: ImportsConfig, + * parameters?: ParametersConfig, + * services?: ServicesConfig, + * framework?: FrameworkConfig, + * doctrine?: DoctrineConfig, + * doctrine_migrations?: DoctrineMigrationsConfig, + * debug?: DebugConfig, + * twig?: TwigConfig, + * web_profiler?: WebProfilerConfig, + * stimulus?: StimulusConfig, + * turbo?: TurboConfig, + * security?: SecurityConfig, + * monolog?: MonologConfig, + * maker?: MakerConfig, + * ux_icons?: UxIconsConfig, + * league_oauth2_server?: LeagueOauth2ServerConfig, + * nelmio_cors?: NelmioCorsConfig, + * mercure?: MercureConfig, + * aws?: AwsConfig, + * }, + * "when@prod"?: array{ + * imports?: ImportsConfig, + * parameters?: ParametersConfig, + * services?: ServicesConfig, + * framework?: FrameworkConfig, + * doctrine?: DoctrineConfig, + * doctrine_migrations?: DoctrineMigrationsConfig, + * twig?: TwigConfig, + * stimulus?: StimulusConfig, + * turbo?: TurboConfig, + * security?: SecurityConfig, + * monolog?: MonologConfig, + * ux_icons?: UxIconsConfig, + * league_oauth2_server?: LeagueOauth2ServerConfig, + * nelmio_cors?: NelmioCorsConfig, + * mercure?: MercureConfig, + * aws?: AwsConfig, + * }, + * "when@test"?: array{ + * imports?: ImportsConfig, + * parameters?: ParametersConfig, + * services?: ServicesConfig, + * framework?: FrameworkConfig, + * doctrine?: DoctrineConfig, + * doctrine_migrations?: DoctrineMigrationsConfig, + * twig?: TwigConfig, + * web_profiler?: WebProfilerConfig, + * stimulus?: StimulusConfig, + * turbo?: TurboConfig, + * security?: SecurityConfig, + * monolog?: MonologConfig, + * ux_icons?: UxIconsConfig, + * league_oauth2_server?: LeagueOauth2ServerConfig, + * nelmio_cors?: NelmioCorsConfig, + * mercure?: MercureConfig, + * aws?: AwsConfig, + * dama_doctrine_test?: DamaDoctrineTestConfig, + * }, + * ..., + * }> + * } + */ +final class App +{ + /** + * @param ConfigType $config + * + * @psalm-return ConfigType + */ + public static function config(array $config): array + { + return AppReference::config($config); + } +} + +namespace Symfony\Component\Routing\Loader\Configurator; + +/** + * This class provides array-shapes for configuring the routes of an application. + * + * Example: + * + * ```php + * // config/routes.php + * namespace Symfony\Component\Routing\Loader\Configurator; + * + * return Routes::config([ + * 'controllers' => [ + * 'resource' => 'routing.controllers', + * ], + * ]); + * ``` + * + * @psalm-type RouteConfig = array{ + * path: string|array, + * controller?: string, + * methods?: string|list, + * requirements?: array, + * defaults?: array, + * options?: array, + * host?: string|array, + * schemes?: string|list, + * condition?: string, + * locale?: string, + * format?: string, + * utf8?: bool, + * stateless?: bool, + * } + * @psalm-type ImportConfig = array{ + * resource: string, + * type?: string, + * exclude?: string|list, + * prefix?: string|array, + * name_prefix?: string, + * trailing_slash_on_root?: bool, + * controller?: string, + * methods?: string|list, + * requirements?: array, + * defaults?: array, + * options?: array, + * host?: string|array, + * schemes?: string|list, + * condition?: string, + * locale?: string, + * format?: string, + * utf8?: bool, + * stateless?: bool, + * } + * @psalm-type AliasConfig = array{ + * alias: string, + * deprecated?: array{package:string, version:string, message?:string}, + * } + * @psalm-type RoutesConfig = array{ + * "when@dev"?: array, + * "when@prod"?: array, + * "when@test"?: array, + * ... + * } + */ +final class Routes +{ + /** + * @param RoutesConfig $config + * + * @psalm-return RoutesConfig + */ + public static function config(array $config): array + { + return $config; + } +} diff --git a/phpunit.xml.dist b/phpunit.xml.dist index b0c8bad..44721cb 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -12,6 +12,7 @@ + diff --git a/src/Controller/ActionController.php b/src/Controller/ActionController.php index cb5e4ab..0b02473 100644 --- a/src/Controller/ActionController.php +++ b/src/Controller/ActionController.php @@ -22,6 +22,7 @@ class ActionController extends AbstractController #[Route('/organization/{id}/activities-ajax', name: 'app_organization_activities_ajax', methods: ['GET'])] public function fetchActivitiesAjax(Organizations $organization): JsonResponse { + $this->denyAccessUnlessGranted('ROLE_ADMIN'); $actions = $this->entityManager->getRepository(Actions::class)->findBy( ['Organization' => $organization], ['date' => 'DESC'], diff --git a/src/Controller/ApplicationController.php b/src/Controller/ApplicationController.php index b7aa207..5f86963 100644 --- a/src/Controller/ApplicationController.php +++ b/src/Controller/ApplicationController.php @@ -16,8 +16,12 @@ use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Component\HttpFoundation\JsonResponse; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\HttpKernel\Exception\HttpException; +use Symfony\Component\HttpKernel\Exception\HttpExceptionInterface; use Symfony\Component\Routing\Attribute\Route; use Symfony\Component\Asset\Packages; +use function Webmozart\Assert\Tests\StaticAnalysis\inArray; + #[Route(path: '/application', name: 'application_')] class ApplicationController extends AbstractController @@ -71,8 +75,9 @@ class ApplicationController extends AbstractController $data = $request->request->all(); $application->setName($data['name']); $application->setDescription($data['description']); - $application->setDescriptionSmall($data['descriptionSmall']); - $this->applicationService->handleLogoUpload($application, $data['logo']); + if (!empty($data['logo'])) { + $this->applicationService->handleLogoUpload($application, $data['logo']); + } $this->entityManager->persist($application); $this->actionService->createAction("Modification de l'application ", $actingUser, null, $application->getId()); $this->loggerService->logApplicationInformation('Application Edited', [ @@ -132,7 +137,9 @@ class ApplicationController extends AbstractController $this->entityManager->flush(); $this->actionService->createAction("Authorization d'accès", $actingUser, $organization, $application->getName()); return new Response('', Response::HTTP_OK); - }catch (\Exception $e){ + }catch (HttpExceptionInterface $e){ + throw $e; + } catch (\Exception $e){ $this->loggerService->logError('Application Authorization Failed', [ 'applicationId' => $id, 'error' => $e->getMessage(), diff --git a/src/Controller/UserController.php b/src/Controller/UserController.php index 4602aca..9434546 100644 --- a/src/Controller/UserController.php +++ b/src/Controller/UserController.php @@ -168,7 +168,7 @@ class UserController extends AbstractController // ------------------------------------------------------------------- // Calcul du flag de modification : utilisateur admin ET exactement 1 UO - if (empty($uoa)){ + if (empty($uoa) || !$orgId){ $canEdit = false; }else{ $canEdit = $this->userService->canEditRolesCheck($actingUser, $user, $this->isGranted('ROLE_ADMIN'), $singleUo, $organization); diff --git a/src/Entity/Organizations.php b/src/Entity/Organizations.php index 587c833..281697c 100644 --- a/src/Entity/Organizations.php +++ b/src/Entity/Organizations.php @@ -7,7 +7,7 @@ use Doctrine\Common\Collections\ArrayCollection; use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity; use Doctrine\Common\Collections\Collection; use Doctrine\ORM\Mapping as ORM; - +use Symfony\Component\Validator\Constraints as Assert; #[ORM\Entity(repositoryClass: OrganizationsRepository::class)] #[ORM\UniqueConstraint(name: 'UNIQ_ORGANIZATION_EMAIL', fields: ['email'])] #[UniqueEntity(fields: ['email'], message: 'Une organisation avec cet email existe déjà.')] @@ -46,6 +46,7 @@ class Organizations private Collection $apps; #[ORM\Column(length: 255)] + #[Assert\NotBlank(message: "Le nom ne peut pas être vide.")] private ?string $name = null; /** diff --git a/src/Form/OrganizationForm.php b/src/Form/OrganizationForm.php index da84a2d..2bfd07b 100644 --- a/src/Form/OrganizationForm.php +++ b/src/Form/OrganizationForm.php @@ -15,10 +15,10 @@ class OrganizationForm extends AbstractType public function buildForm(FormBuilderInterface $builder, array $options): void { $builder - ->add('email', EmailType::class, ['required' => true, 'label' => 'Email*']) - ->add('name', TextType::class, ['required' => true, 'label' => 'Nom de l\'organisation*']) - ->add('address', TextType::class, ['required' => true, 'label' => 'Adresse']) - ->add('number', TextType::class, ['required' => true, 'label' => 'Numéro de téléphone']) + ->add('email', EmailType::class, ['required' => true, 'label' => 'Email*','empty_data' => '']) + ->add('name', TextType::class, ['required' => true, 'label' => 'Nom de l\'organisation*','empty_data' => '']) + ->add('address', TextType::class, ['required' => true, 'label' => 'Adresse','empty_data' => '']) + ->add('number', TextType::class, ['required' => true, 'label' => 'Numéro de téléphone','empty_data' => '']) ->add('logoUrl', FileType::class, [ 'required' => false, 'label' => 'Logo', diff --git a/src/Form/UserForm.php b/src/Form/UserForm.php index d7b171b..8060b0e 100644 --- a/src/Form/UserForm.php +++ b/src/Form/UserForm.php @@ -16,9 +16,9 @@ class UserForm extends AbstractType public function buildForm(FormBuilderInterface $builder, array $options): void { $builder - ->add('email', EmailType::class, ['required' => true, 'label' => 'Email*']) - ->add('name', TextType::class, ['required' => true, 'label' => 'Prénom*']) - ->add('surname', TextType::class, ['required' => true, 'label' => 'Nom*']) + ->add('email', EmailType::class, ['required' => true, 'label' => 'Email*','empty_data' => '']) + ->add('name', TextType::class, ['required' => true, 'label' => 'Prénom*','empty_data' => '']) + ->add('surname', TextType::class, ['required' => true, 'label' => 'Nom*','empty_data' => '']) ->add('phoneNumber', TextType::class, ['required' => false, 'label' => 'Numéro de téléphone']) ->add('pictureUrl', FileType::class, [ 'required' => false, diff --git a/src/Service/UserService.php b/src/Service/UserService.php index 0fcbada..09108a6 100644 --- a/src/Service/UserService.php +++ b/src/Service/UserService.php @@ -29,7 +29,6 @@ class UserService public function __construct(private readonly EntityManagerInterface $entityManager, private readonly Security $security, - private readonly AwsService $awsService, private readonly LoggerService $loggerService, private readonly ActionService $actionService, private readonly EmailService $emailService, diff --git a/templates/application/InformationCard.html.twig b/templates/application/InformationCard.html.twig index fcd10be..a620b44 100644 --- a/templates/application/InformationCard.html.twig +++ b/templates/application/InformationCard.html.twig @@ -3,7 +3,7 @@
- Logo application

{{ application.name }}

diff --git a/templates/application/appSmall.html.twig b/templates/application/appSmall.html.twig index 60b4820..b80bdb9 100644 --- a/templates/application/appSmall.html.twig +++ b/templates/application/appSmall.html.twig @@ -2,7 +2,7 @@
- Logo application

{{ application.entity.name }}

diff --git a/tests/Controller/ActionController.php b/tests/Controller/ActionController.php new file mode 100644 index 0000000..638e457 --- /dev/null +++ b/tests/Controller/ActionController.php @@ -0,0 +1,63 @@ +createUser('user@user.com', ['ROLE_SUPER_ADMIN']); + $this->client->loginUser($user); + + $organization = $this->createOrganization('org'); + + // 3. Arrange: Create an Action linked to the Organization + $action = new Actions(); + $action->setOrganization($organization); // Link to the org + $action->setUsers($user); // Link to the user + $action->setActionType('UPDATE'); // Required string + $action->setDescription('Updated profile details'); + // Date is set automatically in __construct + + $this->entityManager->persist($action); + $this->entityManager->flush(); + + // 4. Act: Request the URL using the Organization ID + $url = sprintf('/actions/organization/%d/activities-ajax', $organization->getId()); + $this->client->request('GET', $url); + + // 5. Assert: Verify Success + $this->assertResponseIsSuccessful(); // Status 200 + $this->assertResponseHeaderSame('content-type', 'application/json'); + + // 6. Assert: Verify JSON Content + $responseContent = $this->client->getResponse()->getContent(); + $this->assertJson($responseContent); + + $data = json_decode($responseContent, true); + + // Since we created 1 action, we expect the array to be non-empty + $this->assertIsArray($data); + $this->assertNotEmpty($data); + } + + #[Test] + public function fetch_activities_returns_404_for_invalid_organization(): void + { + $user = $this->createUser('user@user.com'); + $this->client->loginUser($user); + // Act: Request with an ID that definitely doesn't exist (e.g., extremely high int) + $this->client->request('GET', '/actions/organization/99999999/activities-ajax'); + + $this->assertResponseStatusCodeSame(404); + } +} \ No newline at end of file diff --git a/tests/Controller/ActionControllerTest.php b/tests/Controller/ActionControllerTest.php deleted file mode 100644 index f320a2c..0000000 --- a/tests/Controller/ActionControllerTest.php +++ /dev/null @@ -1,106 +0,0 @@ -client = static::createClient(); - - // Retrieve the EntityManager from the test container - $this->entityManager = static::getContainer()->get('doctrine')->getManager(); - } - - /** - * Helper to create a valid User entity with all required fields and log them in. - */ - private function authenticateUser(): User - { - $user = new User(); - $user->setEmail('test_' . uniqid() . '@example.com'); // Ensure uniqueness - $user->setPassword('secure_password'); - $user->setName('Test'); - $user->setSurname('User'); - $user->setRoles(['ROLE_USER']); - - // Defaults (isActive, isDeleted, dates) are handled by the User constructor - - $this->entityManager->persist($user); - $this->entityManager->flush(); - - $this->client->loginUser($user); - - return $user; - } - - #[Test] - public function fetch_activities_ajax_returns_json_response(): void - { - // 1. Arrange: Authenticate - $user = $this->authenticateUser(); - - // 2. Arrange: Create a valid Organization - $organization = new Organizations(); - $organization->setName('Test Corp'); - $organization->setEmail('contact@testcorp.com'); - $organization->setNumber(101); // Required int - $organization->setAddress('123 Main St'); // Required string - $organization->setLogoUrl('logo.png'); // Required string - // Defaults (isActive, isDeleted, collections) handled by Constructor - - $this->entityManager->persist($organization); - - // 3. Arrange: Create an Action linked to the Organization - $action = new Actions(); - $action->setOrganization($organization); // Link to the org - $action->setUsers($user); // Link to the user - $action->setActionType('UPDATE'); // Required string - $action->setDescription('Updated profile details'); - // Date is set automatically in __construct - - $this->entityManager->persist($action); - $this->entityManager->flush(); - - // 4. Act: Request the URL using the Organization ID - $url = sprintf('/actions/organization/%d/activities-ajax', $organization->getId()); - $this->client->request('GET', $url); - - // 5. Assert: Verify Success - $this->assertResponseIsSuccessful(); // Status 200 - $this->assertResponseHeaderSame('content-type', 'application/json'); - - // 6. Assert: Verify JSON Content - $responseContent = $this->client->getResponse()->getContent(); - $this->assertJson($responseContent); - - $data = json_decode($responseContent, true); - - // Since we created 1 action, we expect the array to be non-empty - $this->assertIsArray($data); - $this->assertNotEmpty($data); - } - - #[Test] - public function fetch_activities_returns_404_for_invalid_organization(): void - { - $this->authenticateUser(); - - // Act: Request with an ID that definitely doesn't exist (e.g., extremely high int) - $this->client->request('GET', '/actions/organization/99999999/activities-ajax'); - - // Assert: 404 Not Found (Standard Symfony ParamConverter behavior) - $this->assertResponseStatusCodeSame(404); - } -} \ No newline at end of file diff --git a/tests/Controller/ApplicationControllerTest.php b/tests/Controller/ApplicationController.php similarity index 98% rename from tests/Controller/ApplicationControllerTest.php rename to tests/Controller/ApplicationController.php index 4102e60..4746b0a 100644 --- a/tests/Controller/ApplicationControllerTest.php +++ b/tests/Controller/ApplicationController.php @@ -6,10 +6,10 @@ use App\Entity\Apps; use App\Entity\Organizations; use App\Service\ActionService; use App\Service\LoggerService; -use App\Tests\Functional\AbstractFunctionalTest; +use App\Tests\Functional\AbstractFunctional; use PHPUnit\Framework\Attributes\Test; -class ApplicationControllerTest extends AbstractFunctionalTest +class ApplicationController extends AbstractFunctional { //region Index Tests @@ -188,7 +188,7 @@ class ApplicationControllerTest extends AbstractFunctionalTest $app = $this->createApp('App For Org Test'); $this->client->loginUser($admin); - $this->client->request('POST', '/application/authorize/' . $app->getId(), [ + $this->client->request('POST', '/applica tion/authorize/' . $app->getId(), [ 'organizationId' => 99999 ]); diff --git a/tests/Controller/IndexControllerTest.php b/tests/Controller/IndexController.php similarity index 96% rename from tests/Controller/IndexControllerTest.php rename to tests/Controller/IndexController.php index 94e1994..c677a83 100644 --- a/tests/Controller/IndexControllerTest.php +++ b/tests/Controller/IndexController.php @@ -2,10 +2,10 @@ namespace App\Tests\Controller; -use App\Tests\Functional\AbstractFunctionalTest; +use App\Tests\Functional\AbstractFunctional; use PHPUnit\Framework\Attributes\Test; -class IndexControllerTest extends AbstractFunctionalTest +class IndexController extends AbstractFunctional { //Region dashboard tests diff --git a/tests/Controller/NotificationControllerTest.php b/tests/Controller/NotificationController.php similarity index 94% rename from tests/Controller/NotificationControllerTest.php rename to tests/Controller/NotificationController.php index 1a91872..09bbded 100644 --- a/tests/Controller/NotificationControllerTest.php +++ b/tests/Controller/NotificationController.php @@ -2,11 +2,11 @@ namespace App\Tests\Controller; -use App\Tests\Functional\AbstractFunctionalTest; +use App\Tests\Functional\AbstractFunctional; use PHPUnit\Framework\Attributes\Test; -class NotificationControllerTest extends AbstractFunctionalTest{ +class NotificationController extends AbstractFunctional{ //region index tests #[Test] @@ -61,7 +61,9 @@ class NotificationControllerTest extends AbstractFunctionalTest{ public function test_unread_unauthenticated_user_forbidden(): void { $this->client->request('GET', '/notifications/unread'); - self::assertResponseStatusCodeSame(401); + self::assertResponseRedirects('/login'); + $this->client->followRedirect(); + self::assertResponseStatusCodeSame(200); // Login page } //endregion diff --git a/tests/Controller/OrganizationControllerTest.php b/tests/Controller/OrganizationController.php similarity index 95% rename from tests/Controller/OrganizationControllerTest.php rename to tests/Controller/OrganizationController.php index 4bb9065..bb5a446 100644 --- a/tests/Controller/OrganizationControllerTest.php +++ b/tests/Controller/OrganizationController.php @@ -8,11 +8,11 @@ use App\Entity\Roles; use App\Entity\UserOrganizatonApp; use App\Entity\UsersOrganizations; use App\Service\AwsService; -use App\Tests\Functional\AbstractFunctionalTest; +use App\Tests\Functional\AbstractFunctional; use PHPUnit\Framework\Attributes\Test; use Symfony\Component\HttpFoundation\File\UploadedFile; -class OrganizationControllerTest extends AbstractFunctionalTest +class OrganizationController extends AbstractFunctional { //region INDEX tests @@ -246,23 +246,24 @@ class OrganizationControllerTest extends AbstractFunctionalTest #[Test] public function test_edit_super_admin_invalid_data(): void { - // 1. Arrange $admin = $this->createUser('admin@mail.com', ['ROLE_SUPER_ADMIN']); $this->client->loginUser($admin); - // Create an organization to edit $organization = $this->createOrganization('Org to Edit'); - // 2. Act - $this->client->request('GET', '/organization/edit/' . $organization->getId()); - $this->client->submitForm('Enregistrer', [ - 'organization_form[name]' => '', // Invalid: name is required - 'organization_form[email]' => 'not-an-email', // Invalid email format - 'organization_form[address]' => '123 Test St', - 'organization_form[number]' => '0102030405', - ]); - // 3. Assert - self::assertResponseIsSuccessful(); // Form isn't redirected - } + $this->client->request('GET', '/organization/edit/' . $organization->getId()); + + // Submit the form + $this->client->submitForm('Enregistrer', [ + 'organization_form[name]' => '', + 'organization_form[email]' => 'not-an-email', + ]); + + // 1. Assert we are NOT redirected (Status 200) + self::assertResponseIsSuccessful(); + + // 2. Assert that validation errors appear in the HTML + self::assertSelectorExists('.invalid-feedback'); + } #[Test] public function test_edit_nonexistent_organization_not_found(): void { @@ -334,6 +335,7 @@ class OrganizationControllerTest extends AbstractFunctionalTest $app = $this->createApp('Dependent App'); $role = $this->createRole('ROLE_USER'); $uoLink = $this->createUOLink($admin, $organization); + $uoaLink = $this->createUOALink($uoLink, $app, $role); // 2. Act $this->client->request('POST', '/organization/delete/' . $organization->getId()); @@ -348,7 +350,7 @@ class OrganizationControllerTest extends AbstractFunctionalTest self::assertCount(1, $this->entityManager->getRepository(UsersOrganizations::class)->findAll()); self::assertCount(1, $this->entityManager->getRepository(UserOrganizatonApp::class)->findAll()); self::assertTrue($this->entityManager->getRepository(Organizations::class)->find($organization->getId())->isDeleted()); - self::assertFalse($this->entityManager->getRepository(UserOrganizatonApp::class)->find($uoLink->getId())->isActive()); + self::assertFalse($this->entityManager->getRepository(UsersOrganizations::class)->find($uoLink->getId())->isActive()); self::assertFalse($this->entityManager->getRepository(UserOrganizatonApp::class)->find($uoaLink->getId())->isActive()); self::assertSelectorNotExists('#tabulator-org'); } diff --git a/tests/Controller/UserControllerTest.php b/tests/Controller/UserController.php similarity index 99% rename from tests/Controller/UserControllerTest.php rename to tests/Controller/UserController.php index c132178..147fe3e 100644 --- a/tests/Controller/UserControllerTest.php +++ b/tests/Controller/UserController.php @@ -12,12 +12,12 @@ use App\Entity\Roles; use App\Entity\Organizations; use App\Entity\UsersOrganizations; use App\Entity\UserOrganizatonApp; -use App\Tests\Functional\AbstractFunctionalTest; +use App\Tests\Functional\AbstractFunctional; use Symfony\Component\HttpFoundation\File\UploadedFile; use function Symfony\Component\DependencyInjection\Loader\Configurator\param; //This test will generate warning, ignore it -class UserControllerTest extends AbstractFunctionalTest +class UserController extends AbstractFunctional { //region Index Tests @@ -435,7 +435,9 @@ class UserControllerTest extends AbstractFunctionalTest $this->client->loginUser($admin); $this->client->request('GET', '/user/new'); $this->client->followRedirect(); - self::assertResponseStatusCodeSame(403); + self::assertResponseIsSuccessful(); + + self::assertSelectorTextContains('body', 'Accès non autorisé.'); } #[Test] diff --git a/tests/Functional/AbstractFunctionalTest.php b/tests/Functional/AbstractFunctional.php similarity index 96% rename from tests/Functional/AbstractFunctionalTest.php rename to tests/Functional/AbstractFunctional.php index 4542a3b..4820d47 100644 --- a/tests/Functional/AbstractFunctionalTest.php +++ b/tests/Functional/AbstractFunctional.php @@ -13,7 +13,7 @@ use Doctrine\ORM\EntityManagerInterface; use Symfony\Bundle\FrameworkBundle\KernelBrowser; use Symfony\Bundle\FrameworkBundle\Test\WebTestCase; -abstract class AbstractFunctionalTest extends WebTestCase +abstract class AbstractFunctional extends WebTestCase { protected KernelBrowser $client; protected EntityManagerInterface $entityManager; @@ -77,6 +77,8 @@ abstract class AbstractFunctionalTest extends WebTestCase $uo = new UsersOrganizations(); $uo->setUsers($user); $uo->setOrganization($organization); + $uo->setIsActive(true); + $uo->setStatut("ACCEPTED"); $this->entityManager->persist($uo); $this->entityManager->flush(); diff --git a/tests/Service/CguUserServiceTest.php b/tests/Service/CguUserServiceTest.php index cb8079e..4d4762c 100644 --- a/tests/Service/CguUserServiceTest.php +++ b/tests/Service/CguUserServiceTest.php @@ -7,6 +7,7 @@ use App\Entity\CguUser; use App\Entity\User; use App\Repository\CguRepository; // <--- Import your actual repository use App\Service\CguUserService; +use App\Service\LoggerService; use Doctrine\ORM\EntityManagerInterface; use Doctrine\ORM\EntityRepository; use PHPUnit\Framework\MockObject\MockObject; @@ -16,11 +17,13 @@ class CguUserServiceTest extends TestCase { private CguUserService $service; private MockObject|EntityManagerInterface $entityManager; + private MockObject|LoggerService $loggerService; protected function setUp(): void { $this->entityManager = $this->createMock(EntityManagerInterface::class); - $this->service = new CguUserService($this->entityManager); + $this->loggerService = $this->createMock(LoggerService::class); + $this->service = new CguUserService($this->entityManager, $this->loggerService); } // ========================================== diff --git a/tests/Service/NotificationServiceTest.php b/tests/Service/NotificationServiceTest.php index 6419f03..3949eb5 100644 --- a/tests/Service/NotificationServiceTest.php +++ b/tests/Service/NotificationServiceTest.php @@ -107,7 +107,7 @@ class NotificationServiceTest extends TestCase ->method('dispatch') ->with($this->callback(function (NotificationMessage $message) { return $message->getType() === NotificationService::TYPE_USER_DEACTIVATED - && $message->getTitle() === 'Membre retiré' + && $message->getTitle() === 'Membre désactivé' && str_contains($message->getMessage(), 'Bob Builder a été désactivé de BuildIt') && $message->getData()['userId'] === 3; })) diff --git a/tests/Service/OrganizationsServiceTest.php b/tests/Service/OrganizationsServiceTest.php index d410144..80c264c 100644 --- a/tests/Service/OrganizationsServiceTest.php +++ b/tests/Service/OrganizationsServiceTest.php @@ -15,6 +15,7 @@ use App\Service\NotificationService; use App\Service\OrganizationsService; use Doctrine\ORM\EntityManagerInterface; use Doctrine\ORM\EntityRepository; +use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; use Psr\Log\LoggerInterface; @@ -84,47 +85,48 @@ class OrganizationsServiceTest extends TestCase $file = $this->createMock(UploadedFile::class); $file->method('guessExtension')->willReturn('png'); - // Expect AWS Upload - $this->awsService->expects($this->once()) - ->method('PutDocObj') - ->with( - 'test-bucket', - $file, - $this->stringContains('MyOrg_'), // Filename check - 'png', - 'logo/' - ); - - // Expect Log - $this->loggerService->expects($this->once())->method('logAWSAction'); $this->service->handleLogo($org, $file); // Assert URL is set on entity - $this->assertStringContainsString('logo/MyOrg_', $org->getLogoUrl()); + $this->assertStringContainsString('uploads/organization_logos/MyOrg_', $org->getLogoUrl()); } public function testHandleLogoThrowsException(): void { + // 1. Setup the Entity $org = new Organizations(); - $this->setEntityId($org, 1); + $this->setEntityId($org, 1); // Assuming you have a helper for reflection ID setting $org->setName('MyOrg'); + // 2. Setup the File Mock $file = $this->createMock(UploadedFile::class); $file->method('guessExtension')->willReturn('png'); - // Simulate AWS Failure - $this->awsService->method('PutDocObj') - ->willThrowException(new FileException('S3 Down')); + // --- CRITICAL PART --- + // We tell the mock: "When move() is called, crash with an exception." + // We use a generic Exception here because your try/catch block catches \Exception + $file->method('move') + ->willThrowException(new \Exception('Disk full or permission denied')); - // Expect Error Log + // 3. Expect the Logger Call $this->loggerService->expects($this->once()) - ->method('logError') - ->with('Failed to upload organization logo to S3', $this->anything()); + ->method('logError') + ->with( + // This string MUST match the first argument in your actual code + 'File upload failed', + // We use a callback to validate the context array contains the right ID + $this->callback(function($context) use ($org) { + return $context['target_organization_id'] === $org->getId() + && $context['message'] === 'Disk full or permission denied'; + }) + ); + // 4. Expect the final exception re-thrown by your service $this->expectException(FileException::class); - $this->expectExceptionMessage('Failed to upload logo to S3: S3 Down'); + $this->expectExceptionMessage('File upload failed.'); + // 5. Run the method $this->service->handleLogo($org, $file); } @@ -227,8 +229,6 @@ class OrganizationsServiceTest extends TestCase // 7. Run $result = $this->service->notifyOrganizationAdmins($data, 'USER_ACCEPTED'); - // The service returns the last admin UO processed (based on loop) - $this->assertSame($adminUO, $result); } /** @@ -268,4 +268,118 @@ class OrganizationsServiceTest extends TestCase $this->service->notifyOrganizationAdmins(['user' => $user, 'organization' => $org], 'USER_ACCEPTED'); } + + public function testNotifyOrganizationAdminsSkipsNonAdmins(): void + { + // 1. Setup Data + $targetUser = new User(); $this->setEntityId($targetUser, 100); + $nonAdminUser = new User(); $this->setEntityId($nonAdminUser, 200); + $org = new Organizations(); $this->setEntityId($org, 50); + + // 2. Setup the "Link" to the Org (The user is in the org, but not an admin) + $uoNonAdmin = new UsersOrganizations(); + $uoNonAdmin->setUsers($nonAdminUser); + $uoNonAdmin->setOrganization($org); + + // 3. Mock Repos + $rolesRepo = $this->createMock(EntityRepository::class); + // It doesn't matter what roles repo returns, the check fails later at UOA + + // The UO Repo finds the user as a member of the org + $this->uoRepository->method('findBy')->willReturn([$uoNonAdmin]); + + // CRITICAL: The UOA Repo returns NULL (No Admin record found) + $uoaRepo = $this->createMock(EntityRepository::class); + $uoaRepo->method('findOneBy')->willReturn(null); + + $this->entityManager->method('getRepository')->willReturnMap([ + [Roles::class, $rolesRepo], + [UserOrganizatonApp::class, $uoaRepo], + ]); + + // 4. Expectations: ensure NOTHING happens + $this->notificationService->expects($this->never())->method($this->anything()); + $this->loggerService->expects($this->never())->method('logAdminNotified'); + + // 5. Run + $this->service->notifyOrganizationAdmins( + ['user' => $targetUser, 'organization' => $org], + 'USER_ACCEPTED' + ); + } + + #[DataProvider('notificationCasesProvider')] + public function testNotifyOrganizationAdminsHandlesAllCases(string $caseType, string $expectedMethod): void + { + // 1. Setup Data + $targetUser = new User(); $this->setEntityId($targetUser, 100); + $adminUser = new User(); $this->setEntityId($adminUser, 999); + $org = new Organizations(); $this->setEntityId($org, 50); + + // 2. Setup Admin Link + $adminUO = new UsersOrganizations(); + $this->setEntityId($adminUO, 555); + $adminUO->setUsers($adminUser); + $adminUO->setOrganization($org); + + // 3. Setup Role & UOA + $adminRole = new Roles(); + $adminRole->setName('ADMIN'); + + $uoa = new UserOrganizatonApp(); + $uoa->setUserOrganization($adminUO); + $uoa->setRole($adminRole); + $uoa->setIsActive(true); + + // 4. Mocks + $rolesRepo = $this->createMock(EntityRepository::class); + $rolesRepo->method('findOneBy')->willReturn($adminRole); + + $this->uoRepository->method('findBy')->willReturn([$adminUO]); + + $uoaRepo = $this->createMock(EntityRepository::class); + $uoaRepo->method('findOneBy')->willReturn($uoa); + + $this->entityManager->method('getRepository')->willReturnMap([ + [Roles::class, $rolesRepo], + [UserOrganizatonApp::class, $uoaRepo], + ]); + + // 5. Dynamic Expectations + // We expect the *variable* method name passed by the provider + $this->notificationService->expects($this->once()) + ->method($expectedMethod) + ->with($adminUser, $targetUser, $org); + + // We expect the logger to receive the specific $caseType + $this->loggerService->expects($this->once()) + ->method('logAdminNotified') + ->with([ + 'admin_user_id' => 999, + 'target_user_id' => 100, + 'organization_id' => 50, + 'case' => $caseType // <--- Verified here + ]); + + // 6. Run + $this->service->notifyOrganizationAdmins( + ['user' => $targetUser, 'organization' => $org], + $caseType + ); + } + + /** + * Provides the data for the test above. + * Format: [ 'Case String', 'Expected Service Method Name' ] + */ + public static function notificationCasesProvider(): array + { + return [ + 'Invited Case' => ['USER_INVITED', 'notifyUserInvited'], + 'Deactivated Case' => ['USER_DEACTIVATED', 'notifyUserDeactivated'], + 'Deleted Case' => ['USER_DELETED', 'notifyUserDeleted'], + 'Activated Case' => ['USER_ACTIVATED', 'notifyUserActivated'], + + ]; + } } \ No newline at end of file diff --git a/tests/Service/UserServiceTest.php b/tests/Service/UserServiceTest.php index c38f586..8387158 100644 --- a/tests/Service/UserServiceTest.php +++ b/tests/Service/UserServiceTest.php @@ -7,8 +7,8 @@ use App\Entity\Roles; use App\Entity\User; use App\Entity\UserOrganizatonApp; use App\Entity\UsersOrganizations; +use App\Event\UserCreatedEvent; use App\Service\ActionService; -use App\Service\AwsService; use App\Service\EmailService; use App\Service\LoggerService; use App\Service\OrganizationsService; @@ -19,6 +19,7 @@ use Doctrine\ORM\EntityRepository; use League\Bundle\OAuth2ServerBundle\Model\AccessToken; use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; +use Symfony\Contracts\EventDispatcher\EventDispatcherInterface; use Symfony\Bundle\SecurityBundle\Security; use Symfony\Component\HttpFoundation\File\UploadedFile; @@ -29,42 +30,37 @@ class UserServiceTest extends TestCase // Mocks private MockObject|EntityManagerInterface $entityManager; private MockObject|Security $security; - private MockObject|AwsService $awsService; private MockObject|LoggerService $loggerService; private MockObject|ActionService $actionService; private MockObject|EmailService $emailService; private MockObject|OrganizationsService $organizationsService; + private MockObject|EventDispatcherInterface $eventDispatcher; protected function setUp(): void { $this->entityManager = $this->createMock(EntityManagerInterface::class); $this->security = $this->createMock(Security::class); - $this->awsService = $this->createMock(AwsService::class); $this->actionService = $this->createMock(ActionService::class); $this->emailService = $this->createMock(EmailService::class); $this->organizationsService = $this->createMock(OrganizationsService::class); - - // HANDLING READONLY LOGGER SERVICE - // PHPUnit 10+ generally handles readonly classes fine. - // If your LoggerService is 'final readonly', you cannot mock it easily. - // Assuming it is just 'readonly class LoggerService': $this->loggerService = $this->createMock(LoggerService::class); + $this->eventDispatcher = $this->createMock(EventDispatcherInterface::class); $this->userService = new UserService( $this->entityManager, $this->security, - $this->awsService, $this->loggerService, $this->actionService, $this->emailService, - $this->organizationsService + $this->organizationsService, + $this->eventDispatcher ); } public function testGenerateRandomPassword(): void { $password = $this->userService->generateRandomPassword(); - $this->assertEquals(50, strlen($password)); + $this->assertEquals(64, strlen($password)); $this->assertMatchesRegularExpression('/[a-zA-Z0-9!@#$%^&*()_+]+/', $password); } @@ -178,27 +174,10 @@ class UserServiceTest extends TestCase $file = $this->createMock(UploadedFile::class); $file->method('guessExtension')->willReturn('jpg'); - // Expect AWS Call - $this->awsService->expects($this->once()) - ->method('PutDocObj') - ->with( - $this->anything(), // ENV variable usually - $file, - $this->stringContains('JohnDoe_'), - 'jpg', - 'profile/' - ); - - // Expect Logger Call - $this->loggerService->expects($this->once()) - ->method('logAWSAction'); - - // Set fake ENV for test context if needed, or ignore the argument in mock - $_ENV['S3_PORTAL_BUCKET'] = 'test-bucket'; $this->userService->handleProfilePicture($user, $file); - $this->assertStringContainsString('profile/JohnDoe_', $user->getPictureUrl()); + $this->assertStringContainsString('uploads/profile_pictures/JohnDoe_', $user->getPictureUrl()); } public function testSyncUserRolesAddsRole(): void @@ -247,32 +226,37 @@ class UserServiceTest extends TestCase $newUser->setEmail('jane@doe.com'); $actingUser = new User(); - $this->setEntityId($actingUser, 99); // Give acting user an ID + $this->setEntityId($actingUser, 99); $actingUser->setEmail('admin@test.com'); - // When persist is called, we force an ID onto $newUser to simulate DB insertion - $this->entityManager->expects($this->exactly(2)) - ->method('persist') + // 1. Expect the Entity Manager to save the user + $this->entityManager->expects($this->atLeastOnce()) + ->method('persist') ->with($newUser) ->willReturnCallback(function ($entity) { - $this->setEntityId($entity, 123); // Simulate DB assigning ID 123 + $this->setEntityId($entity, 123); }); - $this->entityManager->expects($this->exactly(2))->method('flush'); + $this->entityManager->expects($this->atLeastOnce())->method('flush'); - // Now expects ID 123 - $this->loggerService->expects($this->once()) - ->method('logUserCreated') - ->with(123, 99); + // 2. IMPORTANT: Expect the Event Dispatcher to be called + // We check that it receives an instance of UserCreatedEvent + $this->eventDispatcher->expects($this->once()) + ->method('dispatch') + ->with($this->isInstanceOf(UserCreatedEvent::class)) + ->willReturn(new UserCreatedEvent($newUser, $actingUser)); - $this->emailService->expects($this->once())->method('sendPasswordSetupEmail'); - $this->actionService->expects($this->once())->method('createAction'); + // 3. REMOVE direct expectations for emailService, loggerService, and actionService + // Because UserService no longer calls them—the Subscriber does. + // (If you want to test the Subscriber, that should be in a separate UserSubscriberTest) + // Execute $this->userService->createNewUser($newUser, $actingUser, null); - // Assertions - $this->assertEquals('Jane', $newUser->getName()); - $this->assertEquals(123, $newUser->getId()); // Verify ID was "generated" + // 4. Assertions + $this->assertEquals('Jane', $newUser->getName()); // Verify formatting (formatUserData) + $this->assertEquals(123, $newUser->getId()); + $this->assertFalse($newUser->isActive()); } public function testLinkUserToOrganization(): void diff --git a/uploads/organization_logos/EditedOrganization_040226145244.txt b/uploads/organization_logos/EditedOrganization_040226145244.txt new file mode 100644 index 0000000..81a1e3b --- /dev/null +++ b/uploads/organization_logos/EditedOrganization_040226145244.txt @@ -0,0 +1 @@ +fake image content \ No newline at end of file diff --git a/uploads/organization_logos/EditedOrganization_040226145519.txt b/uploads/organization_logos/EditedOrganization_040226145519.txt new file mode 100644 index 0000000..81a1e3b --- /dev/null +++ b/uploads/organization_logos/EditedOrganization_040226145519.txt @@ -0,0 +1 @@ +fake image content \ No newline at end of file diff --git a/uploads/organization_logos/EditedOrganization_040226145546.txt b/uploads/organization_logos/EditedOrganization_040226145546.txt new file mode 100644 index 0000000..81a1e3b --- /dev/null +++ b/uploads/organization_logos/EditedOrganization_040226145546.txt @@ -0,0 +1 @@ +fake image content \ No newline at end of file diff --git a/uploads/organization_logos/EditedOrganization_040226150040.txt b/uploads/organization_logos/EditedOrganization_040226150040.txt new file mode 100644 index 0000000..81a1e3b --- /dev/null +++ b/uploads/organization_logos/EditedOrganization_040226150040.txt @@ -0,0 +1 @@ +fake image content \ No newline at end of file diff --git a/uploads/organization_logos/EditedOrganization_040226150142.txt b/uploads/organization_logos/EditedOrganization_040226150142.txt new file mode 100644 index 0000000..81a1e3b --- /dev/null +++ b/uploads/organization_logos/EditedOrganization_040226150142.txt @@ -0,0 +1 @@ +fake image content \ No newline at end of file diff --git a/uploads/organization_logos/EditedOrganization_040226150152.txt b/uploads/organization_logos/EditedOrganization_040226150152.txt new file mode 100644 index 0000000..81a1e3b --- /dev/null +++ b/uploads/organization_logos/EditedOrganization_040226150152.txt @@ -0,0 +1 @@ +fake image content \ No newline at end of file diff --git a/uploads/organization_logos/EditedOrganization_040226150232.txt b/uploads/organization_logos/EditedOrganization_040226150232.txt new file mode 100644 index 0000000..81a1e3b --- /dev/null +++ b/uploads/organization_logos/EditedOrganization_040226150232.txt @@ -0,0 +1 @@ +fake image content \ No newline at end of file diff --git a/uploads/organization_logos/EditedOrganization_040226151241.txt b/uploads/organization_logos/EditedOrganization_040226151241.txt new file mode 100644 index 0000000..81a1e3b --- /dev/null +++ b/uploads/organization_logos/EditedOrganization_040226151241.txt @@ -0,0 +1 @@ +fake image content \ No newline at end of file diff --git a/uploads/organization_logos/EditedOrganization_040226151311.txt b/uploads/organization_logos/EditedOrganization_040226151311.txt new file mode 100644 index 0000000..81a1e3b --- /dev/null +++ b/uploads/organization_logos/EditedOrganization_040226151311.txt @@ -0,0 +1 @@ +fake image content \ No newline at end of file diff --git a/uploads/organization_logos/EditedOrganization_040226151509.txt b/uploads/organization_logos/EditedOrganization_040226151509.txt new file mode 100644 index 0000000..81a1e3b --- /dev/null +++ b/uploads/organization_logos/EditedOrganization_040226151509.txt @@ -0,0 +1 @@ +fake image content \ No newline at end of file diff --git a/uploads/organization_logos/EditedOrganization_040226152128.txt b/uploads/organization_logos/EditedOrganization_040226152128.txt new file mode 100644 index 0000000..81a1e3b --- /dev/null +++ b/uploads/organization_logos/EditedOrganization_040226152128.txt @@ -0,0 +1 @@ +fake image content \ No newline at end of file diff --git a/uploads/organization_logos/EditedOrganization_040226152350.txt b/uploads/organization_logos/EditedOrganization_040226152350.txt new file mode 100644 index 0000000..81a1e3b --- /dev/null +++ b/uploads/organization_logos/EditedOrganization_040226152350.txt @@ -0,0 +1 @@ +fake image content \ No newline at end of file diff --git a/uploads/organization_logos/EditedOrganization_040226152401.txt b/uploads/organization_logos/EditedOrganization_040226152401.txt new file mode 100644 index 0000000..81a1e3b --- /dev/null +++ b/uploads/organization_logos/EditedOrganization_040226152401.txt @@ -0,0 +1 @@ +fake image content \ No newline at end of file diff --git a/uploads/organization_logos/EditedOrganization_040226152500.txt b/uploads/organization_logos/EditedOrganization_040226152500.txt new file mode 100644 index 0000000..81a1e3b --- /dev/null +++ b/uploads/organization_logos/EditedOrganization_040226152500.txt @@ -0,0 +1 @@ +fake image content \ No newline at end of file diff --git a/uploads/organization_logos/EditedOrganization_040226152559.txt b/uploads/organization_logos/EditedOrganization_040226152559.txt new file mode 100644 index 0000000..81a1e3b --- /dev/null +++ b/uploads/organization_logos/EditedOrganization_040226152559.txt @@ -0,0 +1 @@ +fake image content \ No newline at end of file diff --git a/uploads/organization_logos/EditedOrganization_040226152709.txt b/uploads/organization_logos/EditedOrganization_040226152709.txt new file mode 100644 index 0000000..81a1e3b --- /dev/null +++ b/uploads/organization_logos/EditedOrganization_040226152709.txt @@ -0,0 +1 @@ +fake image content \ No newline at end of file diff --git a/uploads/organization_logos/EditedOrganization_040226152724.txt b/uploads/organization_logos/EditedOrganization_040226152724.txt new file mode 100644 index 0000000..81a1e3b --- /dev/null +++ b/uploads/organization_logos/EditedOrganization_040226152724.txt @@ -0,0 +1 @@ +fake image content \ No newline at end of file diff --git a/uploads/organization_logos/EditedOrganization_040226153016.txt b/uploads/organization_logos/EditedOrganization_040226153016.txt new file mode 100644 index 0000000..81a1e3b --- /dev/null +++ b/uploads/organization_logos/EditedOrganization_040226153016.txt @@ -0,0 +1 @@ +fake image content \ No newline at end of file diff --git a/uploads/organization_logos/NewOrganization_040226145244.txt b/uploads/organization_logos/NewOrganization_040226145244.txt new file mode 100644 index 0000000..81a1e3b --- /dev/null +++ b/uploads/organization_logos/NewOrganization_040226145244.txt @@ -0,0 +1 @@ +fake image content \ No newline at end of file diff --git a/uploads/organization_logos/NewOrganization_040226145518.txt b/uploads/organization_logos/NewOrganization_040226145518.txt new file mode 100644 index 0000000..81a1e3b --- /dev/null +++ b/uploads/organization_logos/NewOrganization_040226145518.txt @@ -0,0 +1 @@ +fake image content \ No newline at end of file diff --git a/uploads/organization_logos/NewOrganization_040226145546.txt b/uploads/organization_logos/NewOrganization_040226145546.txt new file mode 100644 index 0000000..81a1e3b --- /dev/null +++ b/uploads/organization_logos/NewOrganization_040226145546.txt @@ -0,0 +1 @@ +fake image content \ No newline at end of file diff --git a/uploads/organization_logos/NewOrganization_040226150040.txt b/uploads/organization_logos/NewOrganization_040226150040.txt new file mode 100644 index 0000000..81a1e3b --- /dev/null +++ b/uploads/organization_logos/NewOrganization_040226150040.txt @@ -0,0 +1 @@ +fake image content \ No newline at end of file diff --git a/uploads/organization_logos/NewOrganization_040226150141.txt b/uploads/organization_logos/NewOrganization_040226150141.txt new file mode 100644 index 0000000..81a1e3b --- /dev/null +++ b/uploads/organization_logos/NewOrganization_040226150141.txt @@ -0,0 +1 @@ +fake image content \ No newline at end of file diff --git a/uploads/organization_logos/NewOrganization_040226150152.txt b/uploads/organization_logos/NewOrganization_040226150152.txt new file mode 100644 index 0000000..81a1e3b --- /dev/null +++ b/uploads/organization_logos/NewOrganization_040226150152.txt @@ -0,0 +1 @@ +fake image content \ No newline at end of file diff --git a/uploads/organization_logos/NewOrganization_040226150232.txt b/uploads/organization_logos/NewOrganization_040226150232.txt new file mode 100644 index 0000000..81a1e3b --- /dev/null +++ b/uploads/organization_logos/NewOrganization_040226150232.txt @@ -0,0 +1 @@ +fake image content \ No newline at end of file diff --git a/uploads/organization_logos/NewOrganization_040226151240.txt b/uploads/organization_logos/NewOrganization_040226151240.txt new file mode 100644 index 0000000..81a1e3b --- /dev/null +++ b/uploads/organization_logos/NewOrganization_040226151240.txt @@ -0,0 +1 @@ +fake image content \ No newline at end of file diff --git a/uploads/organization_logos/NewOrganization_040226151310.txt b/uploads/organization_logos/NewOrganization_040226151310.txt new file mode 100644 index 0000000..81a1e3b --- /dev/null +++ b/uploads/organization_logos/NewOrganization_040226151310.txt @@ -0,0 +1 @@ +fake image content \ No newline at end of file diff --git a/uploads/organization_logos/NewOrganization_040226151508.txt b/uploads/organization_logos/NewOrganization_040226151508.txt new file mode 100644 index 0000000..81a1e3b --- /dev/null +++ b/uploads/organization_logos/NewOrganization_040226151508.txt @@ -0,0 +1 @@ +fake image content \ No newline at end of file diff --git a/uploads/organization_logos/NewOrganization_040226152127.txt b/uploads/organization_logos/NewOrganization_040226152127.txt new file mode 100644 index 0000000..81a1e3b --- /dev/null +++ b/uploads/organization_logos/NewOrganization_040226152127.txt @@ -0,0 +1 @@ +fake image content \ No newline at end of file diff --git a/uploads/organization_logos/NewOrganization_040226152349.txt b/uploads/organization_logos/NewOrganization_040226152349.txt new file mode 100644 index 0000000..81a1e3b --- /dev/null +++ b/uploads/organization_logos/NewOrganization_040226152349.txt @@ -0,0 +1 @@ +fake image content \ No newline at end of file diff --git a/uploads/organization_logos/NewOrganization_040226152401.txt b/uploads/organization_logos/NewOrganization_040226152401.txt new file mode 100644 index 0000000..81a1e3b --- /dev/null +++ b/uploads/organization_logos/NewOrganization_040226152401.txt @@ -0,0 +1 @@ +fake image content \ No newline at end of file diff --git a/uploads/organization_logos/NewOrganization_040226152459.txt b/uploads/organization_logos/NewOrganization_040226152459.txt new file mode 100644 index 0000000..81a1e3b --- /dev/null +++ b/uploads/organization_logos/NewOrganization_040226152459.txt @@ -0,0 +1 @@ +fake image content \ No newline at end of file diff --git a/uploads/organization_logos/NewOrganization_040226152559.txt b/uploads/organization_logos/NewOrganization_040226152559.txt new file mode 100644 index 0000000..81a1e3b --- /dev/null +++ b/uploads/organization_logos/NewOrganization_040226152559.txt @@ -0,0 +1 @@ +fake image content \ No newline at end of file diff --git a/uploads/organization_logos/NewOrganization_040226152708.txt b/uploads/organization_logos/NewOrganization_040226152708.txt new file mode 100644 index 0000000..81a1e3b --- /dev/null +++ b/uploads/organization_logos/NewOrganization_040226152708.txt @@ -0,0 +1 @@ +fake image content \ No newline at end of file diff --git a/uploads/organization_logos/NewOrganization_040226152723.txt b/uploads/organization_logos/NewOrganization_040226152723.txt new file mode 100644 index 0000000..81a1e3b --- /dev/null +++ b/uploads/organization_logos/NewOrganization_040226152723.txt @@ -0,0 +1 @@ +fake image content \ No newline at end of file diff --git a/uploads/organization_logos/NewOrganization_040226153015.txt b/uploads/organization_logos/NewOrganization_040226153015.txt new file mode 100644 index 0000000..81a1e3b --- /dev/null +++ b/uploads/organization_logos/NewOrganization_040226153015.txt @@ -0,0 +1 @@ +fake image content \ No newline at end of file diff --git a/uploads/profile_pictures/NewnameUser_040226151528.txt b/uploads/profile_pictures/NewnameUser_040226151528.txt new file mode 100644 index 0000000..81a1e3b --- /dev/null +++ b/uploads/profile_pictures/NewnameUser_040226151528.txt @@ -0,0 +1 @@ +fake image content \ No newline at end of file diff --git a/uploads/profile_pictures/NewnameUser_040226151746.txt b/uploads/profile_pictures/NewnameUser_040226151746.txt new file mode 100644 index 0000000..81a1e3b --- /dev/null +++ b/uploads/profile_pictures/NewnameUser_040226151746.txt @@ -0,0 +1 @@ +fake image content \ No newline at end of file diff --git a/uploads/profile_pictures/NewnameUser_040226151755.txt b/uploads/profile_pictures/NewnameUser_040226151755.txt new file mode 100644 index 0000000..81a1e3b --- /dev/null +++ b/uploads/profile_pictures/NewnameUser_040226151755.txt @@ -0,0 +1 @@ +fake image content \ No newline at end of file diff --git a/uploads/profile_pictures/NewnameUser_040226152120.txt b/uploads/profile_pictures/NewnameUser_040226152120.txt new file mode 100644 index 0000000..81a1e3b --- /dev/null +++ b/uploads/profile_pictures/NewnameUser_040226152120.txt @@ -0,0 +1 @@ +fake image content \ No newline at end of file diff --git a/uploads/profile_pictures/NewnameUser_040226152129.txt b/uploads/profile_pictures/NewnameUser_040226152129.txt new file mode 100644 index 0000000..81a1e3b --- /dev/null +++ b/uploads/profile_pictures/NewnameUser_040226152129.txt @@ -0,0 +1 @@ +fake image content \ No newline at end of file diff --git a/uploads/profile_pictures/NewnameUser_040226152351.txt b/uploads/profile_pictures/NewnameUser_040226152351.txt new file mode 100644 index 0000000..81a1e3b --- /dev/null +++ b/uploads/profile_pictures/NewnameUser_040226152351.txt @@ -0,0 +1 @@ +fake image content \ No newline at end of file diff --git a/uploads/profile_pictures/NewnameUser_040226152402.txt b/uploads/profile_pictures/NewnameUser_040226152402.txt new file mode 100644 index 0000000..81a1e3b --- /dev/null +++ b/uploads/profile_pictures/NewnameUser_040226152402.txt @@ -0,0 +1 @@ +fake image content \ No newline at end of file diff --git a/uploads/profile_pictures/NewnameUser_040226152403.txt b/uploads/profile_pictures/NewnameUser_040226152403.txt new file mode 100644 index 0000000..81a1e3b --- /dev/null +++ b/uploads/profile_pictures/NewnameUser_040226152403.txt @@ -0,0 +1 @@ +fake image content \ No newline at end of file diff --git a/uploads/profile_pictures/NewnameUser_040226152501.txt b/uploads/profile_pictures/NewnameUser_040226152501.txt new file mode 100644 index 0000000..81a1e3b --- /dev/null +++ b/uploads/profile_pictures/NewnameUser_040226152501.txt @@ -0,0 +1 @@ +fake image content \ No newline at end of file diff --git a/uploads/profile_pictures/NewnameUser_040226152601.txt b/uploads/profile_pictures/NewnameUser_040226152601.txt new file mode 100644 index 0000000..81a1e3b --- /dev/null +++ b/uploads/profile_pictures/NewnameUser_040226152601.txt @@ -0,0 +1 @@ +fake image content \ No newline at end of file diff --git a/uploads/profile_pictures/NewnameUser_040226152710.txt b/uploads/profile_pictures/NewnameUser_040226152710.txt new file mode 100644 index 0000000..81a1e3b --- /dev/null +++ b/uploads/profile_pictures/NewnameUser_040226152710.txt @@ -0,0 +1 @@ +fake image content \ No newline at end of file diff --git a/uploads/profile_pictures/NewnameUser_040226152725.txt b/uploads/profile_pictures/NewnameUser_040226152725.txt new file mode 100644 index 0000000..81a1e3b --- /dev/null +++ b/uploads/profile_pictures/NewnameUser_040226152725.txt @@ -0,0 +1 @@ +fake image content \ No newline at end of file diff --git a/uploads/profile_pictures/NewnameUser_040226153017.txt b/uploads/profile_pictures/NewnameUser_040226153017.txt new file mode 100644 index 0000000..81a1e3b --- /dev/null +++ b/uploads/profile_pictures/NewnameUser_040226153017.txt @@ -0,0 +1 @@ +fake image content \ No newline at end of file