From 57aa6fb690dba324586e086cc7b977b3f18f40bb Mon Sep 17 00:00:00 2001 From: =?utf8?q?Carlos=20Capote=20P=C3=A9rez-Andreu?= Date: Thu, 23 Apr 2020 17:22:25 +0100 Subject: [PATCH] Test the new Job.process.membership parameters --- CRM/Member/BAO/Membership.php | 9 +- .../api/v3/JobProcessMembershipTest.php | 515 ++++++++++++++++++ 2 files changed, 516 insertions(+), 8 deletions(-) create mode 100644 tests/phpunit/api/v3/JobProcessMembershipTest.php diff --git a/CRM/Member/BAO/Membership.php b/CRM/Member/BAO/Membership.php index 6d351d4b67..4a4ed1ca5b 100644 --- a/CRM/Member/BAO/Membership.php +++ b/CRM/Member/BAO/Membership.php @@ -2173,12 +2173,6 @@ INNER JOIN civicrm_contact contact ON ( contact.id = membership.contact_id AND * @throws \CRM_Core_Exception */ public static function updateAllMembershipStatus($params = []) { - if (empty($params['only_active_membership_types'])) { - $params['only_active_membership_types'] = TRUE; - } - if (empty($params['exclude_test_memberships'])) { - $params['exclude_test_memberships'] = TRUE; - } // We want all of the statuses as id => name, even the disabled ones (cf. // CRM-15475), to identify which are Pending, Deceased, Cancelled, and // Expired. @@ -2285,7 +2279,6 @@ WHERE {$whereClause}"; $memParams = $memberParams; $memParams['status_id'] = $statusId; $memParams['createActivity'] = TRUE; - $memParams['version'] = 3; // Unset columns which should remain unchanged from their current saved // values. This avoids race condition in which these values may have @@ -2302,7 +2295,7 @@ WHERE {$whereClause}"; //since there is change in status. //process member record. - civicrm_api('membership', 'create', $memParams); + civicrm_api3('membership', 'create', $memParams); $updateCount++; } } diff --git a/tests/phpunit/api/v3/JobProcessMembershipTest.php b/tests/phpunit/api/v3/JobProcessMembershipTest.php new file mode 100644 index 0000000000..e0eaccbd54 --- /dev/null +++ b/tests/phpunit/api/v3/JobProcessMembershipTest.php @@ -0,0 +1,515 @@ +loadReferenceDates(); + $this->loadMembershipStatuses(); + $this->loadMembershipTypes(); + } + + public function loadMembershipStatuses() { + $statuses = civicrm_api3('MembershipStatus', 'get', ['options' => ['limit' => 0]])['values']; + $this->_statuses = array_map( + function($status) { + return $status['name']; + }, + $statuses + ); + } + + public function loadMembershipTypes() { + $this->membershipTypeCreate(['name' => 'General']); + $this->membershipTypeCreate(['name' => 'Old']); + $types = civicrm_api3('MembershipType', 'get', ['options' => ['limit' => 0]])['values']; + $this->_types = array_map( + function($type) { + return $type['name']; + }, + $types + ); + } + + public function loadReferenceDates() { + $this->_yesterday = date('Y-m-d', time() - 60 * 60 * 24); + $this->_today = date('Y-m-d'); + $this->_tomorrow = date('Y-m-d', time() + 60 * 60 * 24); + } + + public function tearDown() { + parent::tearDown(); + + // For each case, the `old` membershipt type must start as + // active, so we can assign it (we'll disabled it after + // assigning it) + $this->callAPISuccess('MembershipType', 'create', [ + 'id' => array_search('Old', $this->_types), + 'is_active' => TRUE, + ]); + } + + /** + * Creates a membership that is expired but that should be ignored + * by the process as it is in `deceased` status. + */ + public function createDeceasedMembershipThatShouldBeExpired() { + $contactId = $this->individualCreate(['is_deceased' => FALSE]); + $membershipId = $this->contactMembershipCreate([ + 'contact_id' => $contactId, + 'start_date' => $this->_yesterday, + 'end_date' => $this->_yesterday, + ]); + + $this->callAPISuccess('Membership', 'create', [ + 'id' => $membershipId, + 'status_id' => array_search('Deceased', $this->_statuses), + ]); + + return $membershipId; + } + + /** + * Creates a test membership in `grace` status that should be + * in `current` status but that won't be updated unless the process + * is explicitly told not to exclude tests. + */ + public function createTestMembershipThatShouldBeCurrent() { + $contactId = $this->individualCreate(); + $membershipId = $this->contactMembershipCreate([ + 'contact_id' => $contactId, + 'start_date' => $this->_yesterday, + 'end_date' => $this->_tomorrow, + 'is_test' => TRUE, + ]); + + $this->callAPISuccess('Membership', 'create', [ + 'id' => $membershipId, + 'status_id' => array_search('Grace', $this->_statuses), + ]); + + return $membershipId; + } + + /** + * Creates a grace membership that should be in `current` status + * that should be fixed even when the process is executed with + * the default parameters. + */ + public function createGraceMembershipThatShouldBeCurrent() { + $contactId = $this->individualCreate(); + $membershipId = $this->contactMembershipCreate([ + 'contact_id' => $contactId, + 'start_date' => $this->_yesterday, + 'end_date' => $this->_tomorrow, + ]); + + $this->callAPISuccess('Membership', 'create', [ + 'id' => $membershipId, + 'status_id' => array_search('Grace', $this->_statuses), + ]); + + return $membershipId; + } + + /** + * Creates a pending membership that should be in `current` status + * that won't be fixed unless the process is executed + * with an explicit `exclude_membership_status_ids` list that + * doesn't include it. + */ + public function createPendingMembershipThatShouldBeCurrent() { + $contactId = $this->individualCreate(); + $membershipId = $this->contactMembershipCreate([ + 'contact_id' => $contactId, + 'start_date' => $this->_yesterday, + 'end_date' => $this->_tomorrow, + ]); + + $this->callAPISuccess('Membership', 'create', [ + 'id' => $membershipId, + 'status_id' => array_search('Pending', $this->_statuses), + ]); + + return $membershipId; + } + + /** + * Creates a membership that uses an inactive membership type + * and should be in `current` status. + */ + public function createOldMembershipThatShouldBeCurrent() { + $contactId = $this->individualCreate(); + $membershipId = $this->contactMembershipCreate([ + 'contact_id' => $contactId, + 'start_date' => $this->_yesterday, + 'end_date' => $this->_tomorrow, + 'membership_type_id' => array_search('Old', $this->_types), + ]); + + $this->callAPISuccess('Membership', 'create', [ + 'id' => $membershipId, + 'status_id' => array_search('Grace', $this->_statuses), + ]); + + $this->callAPISuccess('MembershipType', 'create', [ + 'id' => array_search('Old', $this->_types), + 'is_active' => FALSE, + ]); + + return $membershipId; + } + + /** + * Returns the name of the status of a membership given its id. + */ + public function getMembershipStatus($membershipId) { + $membership = $this->callAPISuccess('Membership', 'getsingle', ['id' => $membershipId]); + $statusId = $membership['status_id']; + return $this->_statuses[$statusId]; + } + + /** + * Test that by default test memberships are excluded. + */ + public function testByDefaultTestsAreExcluded() { + $testId = $this->createTestMembershipThatShouldBeCurrent(); + + $this->callAPISuccess('job', 'process_membership', []); + + $this->assertEquals('Grace', $this->getMembershipStatus($testId)); + } + + /** + * Test that by default memberships of inactive types are excluded. + */ + public function testByDefaultInactiveAreExcluded() { + $oldId = $this->createOldMembershipThatShouldBeCurrent(); + + $this->callAPISuccess('job', 'process_membership', []); + + $this->assertEquals('Grace', $this->getMembershipStatus($oldId)); + } + + /** + * Test that by default grace memberships are considered. + */ + public function testByDefaultGraceIsConsidered() { + $graceId = $this->createGraceMembershipThatShouldBeCurrent(); + + $this->callAPISuccess('job', 'process_membership', []); + + $this->assertEquals('Current', $this->getMembershipStatus($graceId)); + } + + /** + * Test that by default pending memberships are excluded. + * + * The pending status is still excluded as it's in the + * exclude_membership_status_ids list by default. + */ + public function testByDefaultPendingIsExcluded() { + $pendingId = $this->createPendingMembershipThatShouldBeCurrent(); + + $this->callAPISuccess('job', 'process_membership', []); + + $this->assertEquals('Pending', $this->getMembershipStatus($pendingId)); + } + + /** + * Test that by default memberships of type deceased are excluded. + */ + public function testByDefaultDeceasedIsExcluded() { + $deceasedId = $this->createDeceasedMembershipThatShouldBeExpired(); + + $this->callAPISuccess('job', 'process_membership', []); + + $this->assertEquals('Deceased', $this->getMembershipStatus($deceasedId)); + } + + /** + * Test that when including test memberships, + * pending memberships are excluded. + * + * The pending status is still excluded as it's in the + * exclude_membership_status_ids list by default. + */ + public function testIncludingTestMembershipsExcludesPending() { + $pendingId = $this->createPendingMembershipThatShouldBeCurrent(); + + $this->callAPISuccess('job', 'process_membership', [ + 'exclude_test_memberships' => FALSE, + ]); + + $this->assertEquals('Pending', $this->getMembershipStatus($pendingId)); + } + + /** + * Test that when including test memberships, + * grace memberships are considered. + */ + public function testIncludingTestMembershipsConsidersGrace() { + $graceId = $this->createGraceMembershipThatShouldBeCurrent(); + + $this->callAPISuccess('job', 'process_membership', [ + 'exclude_test_memberships' => FALSE, + ]); + + $this->assertEquals('Current', $this->getMembershipStatus($graceId)); + } + + /** + * Test that when including test memberships, + * memberships of inactive types are still ignored. + */ + public function testIncludingTestMembershipsIgnoresInactive() { + $oldId = $this->createOldMembershipThatShouldBeCurrent(); + + $this->callAPISuccess('job', 'process_membership', [ + 'exclude_test_memberships' => FALSE, + ]); + + $this->assertEquals('Grace', $this->getMembershipStatus($oldId)); + } + + /** + * Test that when including test memberships, + * acually includes test memberships. + */ + public function testIncludingTestMembershipsActuallyIncludesThem() { + $testId = $this->createTestMembershipThatShouldBeCurrent(); + + $this->callAPISuccess('job', 'process_membership', [ + 'exclude_test_memberships' => FALSE, + ]); + + $this->assertEquals('Current', $this->getMembershipStatus($testId)); + } + + /** + * Test that when including test memberships, + * memberships of type deceased are still ignored. + */ + public function testIncludingTestMembershipsStillIgnoresDeceased() { + $deceasedId = $this->createDeceasedMembershipThatShouldBeExpired(); + + $this->callAPISuccess('job', 'process_membership', [ + 'exclude_test_memberships' => FALSE, + ]); + + $this->assertEquals('Deceased', $this->getMembershipStatus($deceasedId)); + } + + /** + * Test that when including inactive membership types, + * pending memberships are considered. + * + * The pending status is still excluded as it's in the + * exclude_membership_status_ids list by default. + */ + public function testIncludingInactiveMembershipTypesStillExcludesPending() { + $pendingId = $this->createPendingMembershipThatShouldBeCurrent(); + + $this->callAPISuccess('job', 'process_membership', [ + 'only_active_membership_types' => FALSE, + ]); + + $this->assertEquals('Pending', $this->getMembershipStatus($pendingId)); + } + + /** + * Test that when including inactive membership types, + * grace memberships are considered. + */ + public function testIncludingInactiveMembershipTypesConsidersGrace() { + $graceId = $this->createGraceMembershipThatShouldBeCurrent(); + + $this->callAPISuccess('job', 'process_membership', [ + 'only_active_membership_types' => FALSE, + ]); + + $this->assertEquals('Current', $this->getMembershipStatus($graceId)); + } + + /** + * Test that when including inactive membership types, + * memberships of disabled membership types are considered. + */ + public function testIncludingInactiveMembershipTypesConsidersInactive() { + $oldId = $this->createOldMembershipThatShouldBeCurrent(); + + $this->callAPISuccess('job', 'process_membership', [ + 'only_active_membership_types' => FALSE, + ]); + + $this->assertEquals('Current', $this->getMembershipStatus($oldId)); + } + + /** + * Test that when including inactive membership types, + * test memberships are still ignored. + */ + public function testIncludingInactiveMembershipTypesStillIgnoresTests() { + $testId = $this->createTestMembershipThatShouldBeCurrent(); + + $this->callAPISuccess('job', 'process_membership', [ + 'only_active_membership_types' => FALSE, + ]); + + $this->assertEquals('Grace', $this->getMembershipStatus($testId)); + } + + /** + * Test that when including inactive membership types, + * memberships of type deceased are still ignored. + */ + public function testMembershipTypeDeceasedIsExcluded() { + $deceasedId = $this->createDeceasedMembershipThatShouldBeExpired(); + + $this->callAPISuccess('job', 'process_membership', [ + 'only_active_membership_types' => FALSE, + ]); + + $this->assertEquals('Deceased', $this->getMembershipStatus($deceasedId)); + } + + /** + * Test that when explicitly setting the status ids to exclude, + * memberships in deceased status are still ignored. + */ + public function testSpecifyingTheStatusIdsToExcludeStillExcludesDeceased() { + $deceasedId = $this->createDeceasedMembershipThatShouldBeExpired(); + + $this->callAPISuccess('job', 'process_membership', [ + 'exclude_membership_status_ids' => [ + array_search('Cancelled', $this->_statuses), + ], + ]); + + $this->assertEquals('Deceased', $this->getMembershipStatus($deceasedId)); + } + + /** + * Test that when explicitly setting the status ids to exclude, + * test memberships are still ignored. + */ + public function testSpecifyingTheStatusIdsToExcludeStillExcludesTests() { + $testId = $this->createTestMembershipThatShouldBeCurrent(); + + $this->callAPISuccess('job', 'process_membership', [ + 'exclude_membership_status_ids' => [ + array_search('Cancelled', $this->_statuses), + ], + ]); + + $this->assertEquals('Grace', $this->getMembershipStatus($testId)); + } + + /** + * Test that when explicitly setting the status ids to exclude, + * memberships of disabled membership types are still ignored. + */ + public function testSpecifyingTheStatusIdsToExcludeStillExcludesInactive() { + $oldId = $this->createOldMembershipThatShouldBeCurrent(); + + $this->callAPISuccess('job', 'process_membership', [ + 'exclude_membership_status_ids' => [ + array_search('Cancelled', $this->_statuses), + ], + ]); + + $this->assertEquals('Grace', $this->getMembershipStatus($oldId)); + } + + /** + * Test that when explicitly setting the status ids to exclude, + * grace memberships are considered by default. + */ + public function testSpecifyingTheStatusIdsToExcludeGraceIsIncludedByDefault() { + $graceId = $this->createGraceMembershipThatShouldBeCurrent(); + + $this->callAPISuccess('job', 'process_membership', [ + 'exclude_membership_status_ids' => [ + array_search('Cancelled', $this->_statuses), + ], + ]); + + $this->assertEquals('Current', $this->getMembershipStatus($graceId)); + } + + /** + * Test that when explicitly setting the status ids to exclude, + * if the specified list doesn't include pending, then pending + * memberships are considered. + */ + public function testSpecifyingTheStatusIdsToExcludePendingIsExcludedByDefault() { + $pendingId = $this->createPendingMembershipThatShouldBeCurrent(); + + $this->callAPISuccess('job', 'process_membership', [ + 'exclude_membership_status_ids' => [ + array_search('Cancelled', $this->_statuses), + ], + ]); + + $this->assertEquals('Current', $this->getMembershipStatus($pendingId)); + } + +} -- 2.25.1