Page MenuHomePhorge

D25767.1726847683.diff
No OneTemporary

D25767.1726847683.diff

diff --git a/.arclint b/.arclint
--- a/.arclint
+++ b/.arclint
@@ -65,7 +65,7 @@
"text": {
"type": "text",
"exclude": [
- "(^src/(.*/)?__tests__/[^/]+/.*\\.(txt|json|expect))"
+ "(^src/(.*/)?__tests__/[^/]+/.*\\.(txt|json|expect|ics))"
]
},
"text-without-length": {
diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php
--- a/src/__phutil_library_map__.php
+++ b/src/__phutil_library_map__.php
@@ -311,6 +311,7 @@
'BulkSelectParameterType' => 'applications/transactions/bulk/type/BulkSelectParameterType.php',
'BulkStringParameterType' => 'applications/transactions/bulk/type/BulkStringParameterType.php',
'BulkTokenizerParameterType' => 'applications/transactions/bulk/type/BulkTokenizerParameterType.php',
+ 'CalendarImportTestCase' => 'applications/calendar/import/__tests__/CalendarImportTestCase.php',
'CalendarTimeUtil' => 'applications/calendar/util/CalendarTimeUtil.php',
'CalendarTimeUtilTestCase' => 'applications/calendar/__tests__/CalendarTimeUtilTestCase.php',
'CelerityAPI' => 'applications/celerity/CelerityAPI.php',
@@ -6317,6 +6318,7 @@
'BulkSelectParameterType' => 'BulkParameterType',
'BulkStringParameterType' => 'BulkParameterType',
'BulkTokenizerParameterType' => 'BulkParameterType',
+ 'CalendarImportTestCase' => 'PhabricatorTestCase',
'CalendarTimeUtil' => 'Phobject',
'CalendarTimeUtilTestCase' => 'PhabricatorTestCase',
'CelerityAPI' => 'Phobject',
diff --git a/src/applications/calendar/import/__tests__/CalendarImportTestCase.php b/src/applications/calendar/import/__tests__/CalendarImportTestCase.php
new file mode 100644
--- /dev/null
+++ b/src/applications/calendar/import/__tests__/CalendarImportTestCase.php
@@ -0,0 +1,231 @@
+<?php
+
+final class CalendarImportTestCase extends PhabricatorTestCase {
+
+ protected function getPhabricatorTestCaseConfiguration() {
+ return array(
+ self::PHABRICATOR_TESTCONFIG_BUILD_STORAGE_FIXTURES => true,
+ );
+ }
+
+ // Indexes of the "expectedInviteesTests" test.
+ const INVITED_USER = 0;
+ const INVITED_EXPECTED = 1;
+ const INVITED_RESULT = 2;
+
+ public function testIcsFileImportWithGuestThatIsHost() {
+ $alice_unverified =
+ $this->generateTestUserWithVerifiedMail(
+ 'alice@example.com',
+ 0);
+ $lincoln_verified =
+ $this->generateTestUserWithVerifiedMail(
+ 'a.lincoln@example.com',
+ 1);
+ $alien_unverified =
+ $this->generateTestUserWithVerifiedMail(
+ 'alien.unferified@example.com',
+ 0);
+ $alien_verified =
+ $this->generateTestUserWithVerifiedMail(
+ 'alien.verified@example.com',
+ 1);
+
+ // Tests are event-based. Each event has their expected invitees.
+ $tests = array(
+ // Test zero. Alice imports an event with A.Lincoln.
+ array(
+ 'test' => pht('alice invites a.lincoln via verified email'),
+ 'file' => 'simple-event-alincoln-guest.ics',
+ 'fileAuthor' => $alice_unverified,
+ 'expectedInvitees' => 3,
+ 'expectedInviteesTests' => array(
+ // Documentation:
+ // Array 0 (INVITED_USER): User object
+ // Array 1 (INVITED_EXPECTED): Presence (bool)
+ array($lincoln_verified, false),
+ array($alice_unverified, false),
+ array($alien_unverified, false),
+ array($alien_verified, false),
+ ),
+ ),
+ // Test one. A.Lincoln imports an event with A.Lincoln.
+ array(
+ 'test' => pht('a.lincoln self-invite via verified email'),
+ 'file' => 'simple-event-alincoln-guest.ics',
+ 'fileAuthor' => $lincoln_verified,
+ 'expectedInvitees' => 3,
+ 'expectedInviteesTests' => array(
+// array($lincoln_verified, true), // Self-invitation. T15564
+ array($alice_unverified, false),
+ array($alien_unverified, false),
+ array($alien_verified, false),
+ ),
+ ),
+ );
+
+ foreach ($tests as $test) {
+ $this->runIcsFileImportTestWithExpectedResults(
+ $test['test'],
+ $test['file'],
+ $test['fileAuthor'],
+ $test['expectedInvitees'],
+ $test['expectedInviteesTests']);
+ }
+ }
+
+ private function runIcsFileImportTestWithExpectedResults(
+ $test, $file, $importer_author, $expecteds, $invitees_tests) {
+
+ $ics_path = __DIR__.'/events/'.$file;
+
+ // Prepare a calendar import.
+ $import_type = new PhabricatorCalendarICSFileImportEngine();
+ $calendar_import = PhabricatorCalendarImport::initializeNewCalendarImport(
+ $importer_author,
+ clone $import_type);
+
+ // Create the File containing the ICS example.
+ $file_data = Filesystem::readFile($ics_path);
+ $file_test_engine = new PhabricatorTestStorageEngine();
+ $file_params = array(
+ 'name' => $file,
+ 'viewPolicy' => PhabricatorPolicies::POLICY_USER,
+ 'authorPHID' => $importer_author->getPHID(),
+ 'storageEngines' => array($file_test_engine),
+ );
+ $file_up = PhabricatorFile::newFromFileData($file_data, $file_params);
+
+ // Create a calendar import with our ICS file.
+ $import_xactions = array();
+ $import_xactions[] = id(new PhabricatorCalendarImportTransaction())
+ ->setTransactionType(
+ PhabricatorCalendarImportICSFileTransaction::TRANSACTIONTYPE)
+ ->setNewValue($file_up->getPHID());
+
+ // Persist the calendar import and get it.
+ id(new PhabricatorCalendarImportEditor())
+ ->setActor($importer_author)
+ ->setContentSource($this->newContentSource())
+ ->applyTransactions($calendar_import, $import_xactions);
+
+ $import_type->importEventsFromSource(
+ $importer_author,
+ $calendar_import,
+ false);
+
+ // Find imported events from the perspective of the importer author itself.
+ // So we check if we gained some extra people email visibility by mistake.
+ // The backend does not support attachInvitees() and it's done by default.
+ $events = (new PhabricatorCalendarEventQuery())
+ ->setViewer($importer_author)
+ ->withImportSourcePHIDs(array($calendar_import->getPHID()))
+ ->execute();
+
+ // At the moment test cases are hardcoded with one event.
+ $this->assertEqual(
+ 1,
+ count($events),
+ pht('Unexpected events in file "%s" on test "%s"',
+ $file,
+ $test));
+
+ // Take the first event.
+ $event = head($events);
+
+ // How many people we invited in this event.
+ $this->assertEqual(
+ $expecteds,
+ count($event->getInvitees()),
+ pht('Unexpected invitees in file "%s" on test "%s"', $file, $test));
+
+ foreach ($invitees_tests as $invitees_test) {
+ $this->assertMatchingInvitees($test, $file, $event, $invitees_tests);
+ }
+
+ $event->delete();
+ }
+
+ /**
+ * Check if the invitees matches.
+ */
+ private function assertMatchingInvitees($test, $file, $event, $expecteds) {
+
+ // Index what we expect, by user PHID.
+ $expected_users_by_phid = [];
+ foreach ($expecteds as $expected_invited_data) {
+ $user_phid = $expected_invited_data[self::INVITED_USER]
+ ->getPHID();
+ $expected_users_by_phid[$user_phid]
+ = $expected_invited_data;
+ }
+
+ // Get current invitees (mixed between "PHID-CXNV" and "PHID-USER").
+ $actuals_phids_mixed = mpull($event->getInvitees(), 'getInviteePHID');
+
+ // Get just actual users.
+ $actual_users = (new PhabricatorUser())->loadAllWhere(
+ 'phid IN (%Ls)',
+ $actuals_phids_mixed);
+ $actual_users = mpull($actual_users, null, 'getPHID');
+
+ // Map actual users with the expected ones.
+ foreach ($actual_users as $actual_user) {
+ $user_phid = $actual_user->getPHID();
+ $found = isset($expected_users_by_phid[$user_phid]);
+ if (!$found) {
+ $expected_users_by_phid[$user_phid] = array();
+ }
+ $expected_users_by_phid[$user_phid][self::INVITED_RESULT]
+ = $found;
+ }
+
+// In the future it may be useful to also check external users
+// by their email. In case, start from here!
+// but note that the 'getURI()' returns 'mailto:' stuff.
+// $actual_externals = (new PhabricatorCalendarExternalInvitee())
+// ->loadAllWhere(
+// 'phid IN (%Ls)',
+// $actuals_phids_mixed);
+// $actual_externals = mpull($actual_externals, null, 'getURI');
+
+ // Check results (matched or not).
+ foreach ($expected_users_by_phid as $phid => $expecteds_data) {
+ $expected = idx($expecteds_data, self::INVITED_EXPECTED, null);
+ $result = idx($expecteds_data, self::INVITED_RESULT, false);
+ $user = idx($expecteds_data, self::INVITED_USER, null);
+ if ($expected !== null) {
+ $this->assertEqual(
+ $expected,
+ $result,
+ pht('Unexpected presence of user "%s" in file "%s" on test "%s"',
+ $user == null ? '(unknown)' : $user->loadPrimaryEmailAddress(),
+ $file,
+ $test));
+ }
+ }
+ }
+
+ /**
+ * Generate a test user with a specific verified (or not) email.
+ * @param string $mail Email address
+ * @param int $is_verified 0: unverified, 1: verified
+ * @return PhabricatorUser
+ */
+ private function generateTestUserWithVerifiedMail($mail, $is_verified) {
+ $user = $this->generateNewTestUser();
+
+ // Set our primary address as verified or not.
+ $email = id(new PhabricatorUserEmail())->loadOneWhere(
+ 'userPHID = %s',
+ $user->getPHID());
+
+ $email->setAddress($mail);
+ $email->setIsVerified($is_verified);
+ $email->setIsPrimary(true);
+ $email->save();
+
+ return $user;
+ }
+
+}
diff --git a/src/applications/calendar/import/__tests__/events/simple-event-alincoln-guest.ics b/src/applications/calendar/import/__tests__/events/simple-event-alincoln-guest.ics
new file mode 100644
--- /dev/null
+++ b/src/applications/calendar/import/__tests__/events/simple-event-alincoln-guest.ics
@@ -0,0 +1,23 @@
+BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//Phacility//Phabricator//EN
+BEGIN:VEVENT
+DTSTART;VALUE=DATE:20240205
+DTEND;VALUE=DATE:20240206
+DTSTAMP:20240411T230207Z
+ORGANIZER;CN=asd@group.calendar.google.com:mailto:lol@group.calendar.google.com
+UID:blabla@google.com
+ATTENDEE;CUTYPE=INDIVIDUAL;ROLE=REQ-PARTICIPANT;PARTSTAT=NEEDS-ACTION;CN=
+ foo@example.com;X-NUM-GUESTS=0:mailto:foo@example.com
+ATTENDEE;CUTYPE=INDIVIDUAL;ROLE=REQ-PARTICIPANT;PARTSTAT=NEEDS-ACTION;CN=a.
+ lincoln@example.com;X-NUM-GUESTS=0:mailto:a.lincoln@example.com
+ATTENDEE;CUTYPE=INDIVIDUAL;ROLE=REQ-PARTICIPANT;PARTSTAT=NEEDS-ACTION;CN=
+ alien@example.com;X-NUM-GUESTS=0:mailto:alien@example.com
+CREATED:20240123T154250Z
+LAST-MODIFIED:20240123T162620Z
+SEQUENCE:0
+STATUS:CONFIRMED
+SUMMARY:A Lincoln, Foo and Alien to FOSDEM 2024
+TRANSP:TRANSPARENT
+END:VEVENT
+END:VCALENDAR

File Metadata

Mime Type
text/plain
Expires
Fri, Sep 20, 15:54 (18 h, 10 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
893213
Default Alt Text
D25767.1726847683.diff (10 KB)

Event Timeline