@recaptime-dev's working patches + fork for Phorge, a community fork of Phabricator. (Upstream dev and stable branches are at upstream/main and upstream/stable respectively.) hq.recaptime.dev/wiki/Phorge
phorge phabricator
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

Calendar Import: add unit tests to cover participants

Summary:
Add unit tests to easily double-check matched participants in imported calendar events.

This will simplify the addition of future features without the risk to break older workflows.

Ref T15564

Closes T15905
Closes T15906

Test Plan:
See green lights over your new unit tests:

arc unit src/applications/calendar/import/__tests__/CalendarImportTestCase.php

Reviewers: O1 Blessed Committers, aklapper

Reviewed By: O1 Blessed Committers, aklapper

Subscribers: aklapper, tobiaswiese, Matthew, Cigaryno

Maniphest Tasks: T15906, T15564, T15905

Differential Revision: https://we.phorge.it/D25767

+257 -1
+1 -1
.arclint
··· 65 65 "text": { 66 66 "type": "text", 67 67 "exclude": [ 68 - "(^src/(.*/)?__tests__/[^/]+/.*\\.(txt|json|expect))" 68 + "(^src/(.*/)?__tests__/[^/]+/.*\\.(txt|json|expect|ics))" 69 69 ] 70 70 }, 71 71 "text-without-length": {
+2
src/__phutil_library_map__.php
··· 311 311 'BulkSelectParameterType' => 'applications/transactions/bulk/type/BulkSelectParameterType.php', 312 312 'BulkStringParameterType' => 'applications/transactions/bulk/type/BulkStringParameterType.php', 313 313 'BulkTokenizerParameterType' => 'applications/transactions/bulk/type/BulkTokenizerParameterType.php', 314 + 'CalendarImportTestCase' => 'applications/calendar/import/__tests__/CalendarImportTestCase.php', 314 315 'CalendarTimeUtil' => 'applications/calendar/util/CalendarTimeUtil.php', 315 316 'CalendarTimeUtilTestCase' => 'applications/calendar/__tests__/CalendarTimeUtilTestCase.php', 316 317 'CelerityAPI' => 'applications/celerity/CelerityAPI.php', ··· 6318 6319 'BulkSelectParameterType' => 'BulkParameterType', 6319 6320 'BulkStringParameterType' => 'BulkParameterType', 6320 6321 'BulkTokenizerParameterType' => 'BulkParameterType', 6322 + 'CalendarImportTestCase' => 'PhabricatorTestCase', 6321 6323 'CalendarTimeUtil' => 'Phobject', 6322 6324 'CalendarTimeUtilTestCase' => 'PhabricatorTestCase', 6323 6325 'CelerityAPI' => 'Phobject',
+231
src/applications/calendar/import/__tests__/CalendarImportTestCase.php
··· 1 + <?php 2 + 3 + final class CalendarImportTestCase extends PhabricatorTestCase { 4 + 5 + protected function getPhabricatorTestCaseConfiguration() { 6 + return array( 7 + self::PHABRICATOR_TESTCONFIG_BUILD_STORAGE_FIXTURES => true, 8 + ); 9 + } 10 + 11 + // Indexes of the "expectedInviteesTests" test. 12 + const INVITED_USER = 0; 13 + const INVITED_EXPECTED = 1; 14 + const INVITED_RESULT = 2; 15 + 16 + public function testIcsFileImportWithGuestThatIsHost() { 17 + $alice_unverified = 18 + $this->generateTestUserWithVerifiedMail( 19 + 'alice@example.com', 20 + 0); 21 + $lincoln_verified = 22 + $this->generateTestUserWithVerifiedMail( 23 + 'a.lincoln@example.com', 24 + 1); 25 + $alien_unverified = 26 + $this->generateTestUserWithVerifiedMail( 27 + 'alien.unferified@example.com', 28 + 0); 29 + $alien_verified = 30 + $this->generateTestUserWithVerifiedMail( 31 + 'alien.verified@example.com', 32 + 1); 33 + 34 + // Tests are event-based. Each event has their expected invitees. 35 + $tests = array( 36 + // Test zero. Alice imports an event with A.Lincoln. 37 + array( 38 + 'test' => pht('alice invites a.lincoln via verified email'), 39 + 'file' => 'simple-event-alincoln-guest.ics', 40 + 'fileAuthor' => $alice_unverified, 41 + 'expectedInvitees' => 3, 42 + 'expectedInviteesTests' => array( 43 + // Documentation: 44 + // Array 0 (INVITED_USER): User object 45 + // Array 1 (INVITED_EXPECTED): Presence (bool) 46 + array($lincoln_verified, false), 47 + array($alice_unverified, false), 48 + array($alien_unverified, false), 49 + array($alien_verified, false), 50 + ), 51 + ), 52 + // Test one. A.Lincoln imports an event with A.Lincoln. 53 + array( 54 + 'test' => pht('a.lincoln self-invite via verified email'), 55 + 'file' => 'simple-event-alincoln-guest.ics', 56 + 'fileAuthor' => $lincoln_verified, 57 + 'expectedInvitees' => 3, 58 + 'expectedInviteesTests' => array( 59 + // array($lincoln_verified, true), // Self-invitation. T15564 60 + array($alice_unverified, false), 61 + array($alien_unverified, false), 62 + array($alien_verified, false), 63 + ), 64 + ), 65 + ); 66 + 67 + foreach ($tests as $test) { 68 + $this->runIcsFileImportTestWithExpectedResults( 69 + $test['test'], 70 + $test['file'], 71 + $test['fileAuthor'], 72 + $test['expectedInvitees'], 73 + $test['expectedInviteesTests']); 74 + } 75 + } 76 + 77 + private function runIcsFileImportTestWithExpectedResults( 78 + $test, $file, $importer_author, $expecteds, $invitees_tests) { 79 + 80 + $ics_path = __DIR__.'/events/'.$file; 81 + 82 + // Prepare a calendar import. 83 + $import_type = new PhabricatorCalendarICSFileImportEngine(); 84 + $calendar_import = PhabricatorCalendarImport::initializeNewCalendarImport( 85 + $importer_author, 86 + clone $import_type); 87 + 88 + // Create the File containing the ICS example. 89 + $file_data = Filesystem::readFile($ics_path); 90 + $file_test_engine = new PhabricatorTestStorageEngine(); 91 + $file_params = array( 92 + 'name' => $file, 93 + 'viewPolicy' => PhabricatorPolicies::POLICY_USER, 94 + 'authorPHID' => $importer_author->getPHID(), 95 + 'storageEngines' => array($file_test_engine), 96 + ); 97 + $file_up = PhabricatorFile::newFromFileData($file_data, $file_params); 98 + 99 + // Create a calendar import with our ICS file. 100 + $import_xactions = array(); 101 + $import_xactions[] = id(new PhabricatorCalendarImportTransaction()) 102 + ->setTransactionType( 103 + PhabricatorCalendarImportICSFileTransaction::TRANSACTIONTYPE) 104 + ->setNewValue($file_up->getPHID()); 105 + 106 + // Persist the calendar import and get it. 107 + id(new PhabricatorCalendarImportEditor()) 108 + ->setActor($importer_author) 109 + ->setContentSource($this->newContentSource()) 110 + ->applyTransactions($calendar_import, $import_xactions); 111 + 112 + $import_type->importEventsFromSource( 113 + $importer_author, 114 + $calendar_import, 115 + false); 116 + 117 + // Find imported events from the perspective of the importer author itself. 118 + // So we check if we gained some extra people email visibility by mistake. 119 + // The backend does not support attachInvitees() and it's done by default. 120 + $events = (new PhabricatorCalendarEventQuery()) 121 + ->setViewer($importer_author) 122 + ->withImportSourcePHIDs(array($calendar_import->getPHID())) 123 + ->execute(); 124 + 125 + // At the moment test cases are hardcoded with one event. 126 + $this->assertEqual( 127 + 1, 128 + count($events), 129 + pht('Unexpected events in file "%s" on test "%s"', 130 + $file, 131 + $test)); 132 + 133 + // Take the first event. 134 + $event = head($events); 135 + 136 + // How many people we invited in this event. 137 + $this->assertEqual( 138 + $expecteds, 139 + count($event->getInvitees()), 140 + pht('Unexpected invitees in file "%s" on test "%s"', $file, $test)); 141 + 142 + foreach ($invitees_tests as $invitees_test) { 143 + $this->assertMatchingInvitees($test, $file, $event, $invitees_tests); 144 + } 145 + 146 + $event->delete(); 147 + } 148 + 149 + /** 150 + * Check if the invitees matches. 151 + */ 152 + private function assertMatchingInvitees($test, $file, $event, $expecteds) { 153 + 154 + // Index what we expect, by user PHID. 155 + $expected_users_by_phid = []; 156 + foreach ($expecteds as $expected_invited_data) { 157 + $user_phid = $expected_invited_data[self::INVITED_USER] 158 + ->getPHID(); 159 + $expected_users_by_phid[$user_phid] 160 + = $expected_invited_data; 161 + } 162 + 163 + // Get current invitees (mixed between "PHID-CXNV" and "PHID-USER"). 164 + $actuals_phids_mixed = mpull($event->getInvitees(), 'getInviteePHID'); 165 + 166 + // Get just actual users. 167 + $actual_users = (new PhabricatorUser())->loadAllWhere( 168 + 'phid IN (%Ls)', 169 + $actuals_phids_mixed); 170 + $actual_users = mpull($actual_users, null, 'getPHID'); 171 + 172 + // Map actual users with the expected ones. 173 + foreach ($actual_users as $actual_user) { 174 + $user_phid = $actual_user->getPHID(); 175 + $found = isset($expected_users_by_phid[$user_phid]); 176 + if (!$found) { 177 + $expected_users_by_phid[$user_phid] = array(); 178 + } 179 + $expected_users_by_phid[$user_phid][self::INVITED_RESULT] 180 + = $found; 181 + } 182 + 183 + // In the future it may be useful to also check external users 184 + // by their email. In case, start from here! 185 + // but note that the 'getURI()' returns 'mailto:' stuff. 186 + // $actual_externals = (new PhabricatorCalendarExternalInvitee()) 187 + // ->loadAllWhere( 188 + // 'phid IN (%Ls)', 189 + // $actuals_phids_mixed); 190 + // $actual_externals = mpull($actual_externals, null, 'getURI'); 191 + 192 + // Check results (matched or not). 193 + foreach ($expected_users_by_phid as $phid => $expecteds_data) { 194 + $expected = idx($expecteds_data, self::INVITED_EXPECTED, null); 195 + $result = idx($expecteds_data, self::INVITED_RESULT, false); 196 + $user = idx($expecteds_data, self::INVITED_USER, null); 197 + if ($expected !== null) { 198 + $this->assertEqual( 199 + $expected, 200 + $result, 201 + pht('Unexpected presence of user "%s" in file "%s" on test "%s"', 202 + $user == null ? '(unknown)' : $user->loadPrimaryEmailAddress(), 203 + $file, 204 + $test)); 205 + } 206 + } 207 + } 208 + 209 + /** 210 + * Generate a test user with a specific verified (or not) email. 211 + * @param string $mail Email address 212 + * @param int $is_verified 0: unverified, 1: verified 213 + * @return PhabricatorUser 214 + */ 215 + private function generateTestUserWithVerifiedMail($mail, $is_verified) { 216 + $user = $this->generateNewTestUser(); 217 + 218 + // Set our primary address as verified or not. 219 + $email = id(new PhabricatorUserEmail())->loadOneWhere( 220 + 'userPHID = %s', 221 + $user->getPHID()); 222 + 223 + $email->setAddress($mail); 224 + $email->setIsVerified($is_verified); 225 + $email->setIsPrimary(true); 226 + $email->save(); 227 + 228 + return $user; 229 + } 230 + 231 + }
+23
src/applications/calendar/import/__tests__/events/simple-event-alincoln-guest.ics
··· 1 + BEGIN:VCALENDAR 2 + VERSION:2.0 3 + PRODID:-//Phacility//Phabricator//EN 4 + BEGIN:VEVENT 5 + DTSTART;VALUE=DATE:20240205 6 + DTEND;VALUE=DATE:20240206 7 + DTSTAMP:20240411T230207Z 8 + ORGANIZER;CN=asd@group.calendar.google.com:mailto:lol@group.calendar.google.com 9 + UID:blabla@google.com 10 + ATTENDEE;CUTYPE=INDIVIDUAL;ROLE=REQ-PARTICIPANT;PARTSTAT=NEEDS-ACTION;CN= 11 + foo@example.com;X-NUM-GUESTS=0:mailto:foo@example.com 12 + ATTENDEE;CUTYPE=INDIVIDUAL;ROLE=REQ-PARTICIPANT;PARTSTAT=NEEDS-ACTION;CN=a. 13 + lincoln@example.com;X-NUM-GUESTS=0:mailto:a.lincoln@example.com 14 + ATTENDEE;CUTYPE=INDIVIDUAL;ROLE=REQ-PARTICIPANT;PARTSTAT=NEEDS-ACTION;CN= 15 + alien@example.com;X-NUM-GUESTS=0:mailto:alien@example.com 16 + CREATED:20240123T154250Z 17 + LAST-MODIFIED:20240123T162620Z 18 + SEQUENCE:0 19 + STATUS:CONFIRMED 20 + SUMMARY:A Lincoln, Foo and Alien to FOSDEM 2024 21 + TRANSP:TRANSPARENT 22 + END:VEVENT 23 + END:VCALENDAR