snapshot of https://github.com/espocrm/espocrm.git (master, 2026-09-18; blobs > 131072B omitted)
Some checks failed
Test / Test on PHP 8.3 (push) Failing after 5s
Test / Test on PHP 8.4 (push) Failing after 4s
Test / Test on PHP 8.5 (push) Failing after 3s
TypeScript static analysis / TypeScript check (push) Failing after 14s
Test Integration on MySQL / Test on PHP 8.3 (push) Failing after 2s
Test Integration on MySQL / Test on PHP 8.5 (push) Failing after 0s
Test Integration on PostgreSQL / Test on PHP 8.4 fix (push) Failing after 1s
Test Integration on PostgreSQL / Test on PHP 8.4 master (push) Failing after 1s

This commit is contained in:
upstream-mirror
2026-09-18 04:19:15 +05:00
commit 7cc7e6b4f3
10077 changed files with 900756 additions and 0 deletions

View File

@@ -0,0 +1,107 @@
<?php
/************************************************************************
* This file is part of EspoCRM.
*
* EspoCRM – Open Source CRM application.
* Copyright (C) 2014-2026 EspoCRM, Inc.
* Website: https://www.espocrm.com
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
* The interactive user interfaces in modified source and object code versions
* of this program must display Appropriate Legal Notices, as required under
* Section 5 of the GNU Affero General Public License version 3.
*
* In accordance with Section 7(b) of the GNU Affero General Public License version 3,
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
************************************************************************/
namespace tests\unit\Espo\Core\Mail\Account;
use Espo\Core\Mail\Account\GroupAccount\BouncedRecognizer;
use Espo\Core\Mail\MessageWrapper;
use Espo\Core\Mail\Parsers\MailMimeParser;
use Espo\ORM\EntityManager;
class BouncedRecognizerTest extends \PHPUnit\Framework\TestCase
{
protected BouncedRecognizer $bouncedRecognizer;
protected function setUp(): void
{
$this->bouncedRecognizer = new BouncedRecognizer();
}
private function createMessage(string $contents): MessageWrapper
{
$entityManager = $this->createMock(EntityManager::class);
$parser = new MailMimeParser($entityManager);
return new MessageWrapper(0, null, $parser, $contents);
}
public function testBounced1a(): void
{
$contents = file_get_contents('tests/unit/testData/Core/Mail/bounced_1.eml');
$message = $this->createMessage($contents);
$this->assertTrue($this->bouncedRecognizer->isBounced($message));
$this->assertTrue($this->bouncedRecognizer->isHard($message));
$this->assertEquals('0011aa', $this->bouncedRecognizer->extractQueueItemId($message));
}
public function testBounced1b(): void
{
$contents = file_get_contents('tests/unit/testData/Core/Mail/bounced_1.eml');
$contents = str_replace('MAILER-DAEMON', 'test', $contents);
$message = $this->createMessage($contents);
$this->assertTrue($this->bouncedRecognizer->isBounced($message));
$this->assertTrue($this->bouncedRecognizer->isHard($message));
}
public function testBounced2(): void
{
$contents = file_get_contents('tests/unit/testData/Core/Mail/bounced_2.eml');
$message = $this->createMessage($contents);
$this->assertTrue($this->bouncedRecognizer->isBounced($message));
$this->assertFalse($this->bouncedRecognizer->isHard($message));
}
public function testNotBounced1(): void
{
$contents = file_get_contents('tests/unit/testData/Core/Mail/test_email_1.eml');
$message = $this->createMessage($contents);
$this->assertFalse($this->bouncedRecognizer->isBounced($message));
}
public function testBounced3(): void
{
$contents = file_get_contents('tests/unit/testData/Core/Mail/bounced_3.eml');
$message = $this->createMessage($contents);
$this->assertTrue($this->bouncedRecognizer->isBounced($message));
$this->assertTrue($this->bouncedRecognizer->isHard($message));
$this->assertEquals('5.4.1', $this->bouncedRecognizer->extractStatus($message));
}
}

View File

@@ -0,0 +1,68 @@
<?php
/************************************************************************
* This file is part of EspoCRM.
*
* EspoCRM – Open Source CRM application.
* Copyright (C) 2014-2026 EspoCRM, Inc.
* Website: https://www.espocrm.com
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
* The interactive user interfaces in modified source and object code versions
* of this program must display Appropriate Legal Notices, as required under
* Section 5 of the GNU Affero General Public License version 3.
*
* In accordance with Section 7(b) of the GNU Affero General Public License version 3,
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
************************************************************************/
namespace tests\unit\Espo\Core\Mail\Account;
use Espo\Core\Mail\Account\FetchData;
use Espo\Core\Field\DateTime;
class FetchDataTest extends \PHPUnit\Framework\TestCase
{
public function testGetSet(): void
{
$raw = (object) [
'lastUID' => (object) [
'test' => '10',
],
'lastDate' => (object) [
'test' => '2022-01-01 00:00:00',
],
];
$data = FetchData::fromRaw($raw, 0);
$this->assertEquals(10, $data->getLastUid('test'));
$this->assertEquals('2022-01-01 00:00:00', $data->getLastDate('test')->toString());
$this->assertEquals(null, $data->getLastUid('not-existing'));
$this->assertEquals(null, $data->getLastDate('not-existing'));
$this->assertEquals(false, $data->getForceByDate('test'));
$this->assertEquals($raw, $data->getRaw());
$now = DateTime::createNow();
$data->setForceByDate('test', true);
$data->setLastUid('test', 11);
$data->setLastDate('test', $now);
$this->assertEquals(11, $data->getLastUid('test'));
$this->assertEquals(true, $data->getForceByDate('test'));
$this->assertEquals($now, $data->getLastDate('test'));
}
}

View File

@@ -0,0 +1,298 @@
<?php
/************************************************************************
* This file is part of EspoCRM.
*
* EspoCRM – Open Source CRM application.
* Copyright (C) 2014-2026 EspoCRM, Inc.
* Website: https://www.espocrm.com
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
* The interactive user interfaces in modified source and object code versions
* of this program must display Appropriate Legal Notices, as required under
* Section 5 of the GNU Affero General Public License version 3.
*
* In accordance with Section 7(b) of the GNU Affero General Public License version 3,
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
************************************************************************/
namespace tests\unit\Espo\Core\Mail\Account;
use Espo\Core\Field\Date;
use Espo\Core\Field\DateTime;
use Espo\Core\Mail\Account\Account;
use Espo\Core\Mail\Account\FetchData;
use Espo\Core\Mail\Account\Fetcher;
use Espo\Core\Mail\Account\Fetcher\ConfigDataProvider;
use Espo\Core\Mail\Account\Storage;
use Espo\Core\Mail\Account\StorageFactory;
use Espo\Core\Mail\Importer;
use Espo\Core\Mail\ParserFactory;
use Espo\Core\Utils\Log;
use PHPUnit\Framework\MockObject\MockObject;
use PHPUnit\Framework\TestCase;
class FetcherTest extends TestCase
{
/**
* @noinspection PhpUnhandledExceptionInspection
*/
public function testFetchSinceDate(): void
{
$storage = $this->createMock(Storage::class);
$importer = $this->createMock(Importer::class);
$fetcher = $this->prepareFetcher($storage, $importer);
$fetchData = $this->prepareFetchData(
lastUid: null,
lastDate: null,
uidValidity: null,
);
$account = $this->prepareAccount($fetchData);
$storage
->expects($this->once())
->method('selectFolder')
->with('INBOX');
$storage
->expects($this->once())
->method('getFolderStatus')
->willReturn(new Storage\FolderStatus(uidValidity: 1));
$storage
->expects($this->once())
->method('getUidsSinceDate')
->with(DateTime::fromString('2026-01-01 00:00'))
->willReturn([1, 2, 3]);
$storage
->expects($this->once())
->method('close');
$importer
->expects($this->exactly(3))
->method('import');
$fetchData
->expects($this->once())
->method('setUidValidity')
->with('INBOX', 1);
$fetchData
->expects($this->once())
->method('setLastUid')
->with('INBOX', 3);
$fetcher->fetch($account);
}
/**
* @noinspection PhpUnhandledExceptionInspection
*/
public function testFetchSinceLastUid(): void
{
$storage = $this->createMock(Storage::class);
$importer = $this->createMock(Importer::class);
$fetcher = $this->prepareFetcher($storage, $importer);
$fetchData = $this->prepareFetchData(
lastUid: 3,
lastDate: DateTime::fromString('2026-01-02 00:00'),
uidValidity: 1,
);
$account = $this->prepareAccount($fetchData);
$storage
->expects($this->once())
->method('selectFolder')
->with('INBOX');
$storage
->expects($this->once())
->method('getFolderStatus')
->willReturn(new Storage\FolderStatus(uidValidity: 1));
$storage
->expects($this->once())
->method('getUidsFromUid')
->with(3)
->willReturn([4]);
$storage
->expects($this->once())
->method('close');
$importer
->expects($this->exactly(1))
->method('import');
$fetchData
->expects($this->once())
->method('setLastUid')
->with('INBOX', 4);
$fetcher->fetch($account);
}
/**
* @noinspection PhpUnhandledExceptionInspection
*/
public function testFetchUidValidityBroken(): void
{
$storage = $this->createMock(Storage::class);
$importer = $this->createMock(Importer::class);
$fetcher = $this->prepareFetcher($storage, $importer);
$fetchData = $this->prepareFetchData(
lastUid: 3,
lastDate: DateTime::fromString('2026-01-02 00:00'),
uidValidity: 1,
);
$account = $this->prepareAccount($fetchData);
$storage
->expects($this->once())
->method('selectFolder')
->with('INBOX');
$storage
->expects($this->once())
->method('getFolderStatus')
->willReturn(new Storage\FolderStatus(uidValidity: 2));
$storage
->expects($this->once())
->method('getUidsSinceDate')
->with(DateTime::fromString('2026-01-02 00:00'))
->willReturn([4]);
$storage
->expects($this->once())
->method('close');
$importer
->expects($this->exactly(1))
->method('import');
$fetchData
->expects($this->once())
->method('setUidValidity')
->with('INBOX', 2);
$fetchData
->expects($this->once())
->method('setLastUid')
->with('INBOX', 4);
$fetcher->fetch($account);
}
private function prepareFetchData(
?int $lastUid,
?DateTime $lastDate,
?int $uidValidity,
): FetchData & MockObject {
$fetchData = $this->createMock(FetchData::class);
$fetchData
->method('getLastUid')
->willReturn($lastUid);
$fetchData
->method('getLastDate')
->willReturn($lastDate);
$fetchData
->method('getUidValidity')
->willReturn($uidValidity);
$fetchData
->method('getForceByDate')
->willReturn(false);
return $fetchData;
}
private function prepareAccount(FetchData $fetchData): Account & MockObject
{
$account = $this->createMock(Account::class);
$account
->method('isAvailableForFetching')
->willReturn(true);
$account
->method('getMonitoredFolderList')
->willReturn(['INBOX']);
$account
->expects($this->once())
->method('getPortionLimit')
->willReturn(5);
$account
->expects($this->once())
->method('getFetchData')
->willReturn($fetchData);
$account
->method('getFetchSince')
->willReturn(Date::fromString('2026-01-01'));
$account
->expects($this->once())
->method('updateFetchData')
->with($fetchData);
return $account;
}
private function prepareFetcher(
Storage & MockObject $storage,
Importer & MockObject $importer,
): Fetcher {
$storageFactory = $this->createMock(StorageFactory::class);
$configDataProvider = $this->createMock(ConfigDataProvider::class);
$log = $this->createMock(Log::class);
$parserFactory = $this->createMock(ParserFactory::class);
$filtersProvider = $this->createMock(Fetcher\FiltersProvider::class);
$unlocker = $this->createMock(Fetcher\Unlocker::class);
$messageFactory = $this->createMock(Fetcher\MessageFactory::class);
$storageFactory
->expects($this->once())
->method('create')
->willReturn($storage);
return new Fetcher(
importer: $importer,
storageFactory: $storageFactory,
configDataProvider: $configDataProvider,
log: $log,
parserFactory: $parserFactory,
filtersProvider: $filtersProvider,
unlocker: $unlocker,
messageFactory: $messageFactory,
);
}
}

View File

@@ -0,0 +1,227 @@
<?php
/************************************************************************
* This file is part of EspoCRM.
*
* EspoCRM – Open Source CRM application.
* Copyright (C) 2014-2026 EspoCRM, Inc.
* Website: https://www.espocrm.com
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
* The interactive user interfaces in modified source and object code versions
* of this program must display Appropriate Legal Notices, as required under
* Section 5 of the GNU Affero General Public License version 3.
*
* In accordance with Section 7(b) of the GNU Affero General Public License version 3,
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
************************************************************************/
namespace tests\unit\Espo\Core\Mail\Event;
use ICal\ICal;
use ICal\Event;
use Espo\Core\Mail\Event\EventFactory;
use Espo\Core\Mail\Event\Event as MailEvent;
class EventTest extends \PHPUnit\Framework\TestCase
{
private $icsContents1 =
"BEGIN:VCALENDAR
METHOD:REQUEST
PRODID:Microsoft Exchange Server 2010
VERSION:2.0
BEGIN:VTIMEZONE
TZID:Jordan Standard Time
BEGIN:STANDARD
DTSTART:16010101T010000
TZOFFSETFROM:+0300
TZOFFSETTO:+0200
RRULE:FREQ=YEARLY;INTERVAL=1;BYDAY=-1FR;BYMONTH=10
END:STANDARD
BEGIN:DAYLIGHT
DTSTART:16010101T235959
TZOFFSETFROM:+0200
TZOFFSETTO:+0300
RRULE:FREQ=YEARLY;INTERVAL=1;BYDAY=-1TH;BYMONTH=3
END:DAYLIGHT
END:VTIMEZONE
BEGIN:VEVENT
ORGANIZER;CN=Test Org:MAILTO:test-org@test.com
ATTENDEE;ROLE=REQ-PARTICIPANT;PARTSTAT=NEEDS-ACTION;RSVP=TRUE;CN=Test-1:MAIL
TO:test-1@test.com
ATTENDEE;ROLE=REQ-PARTICIPANT;CN=Test-2:MAILTO:test-2@test.com
UID:040000008200E00074C5B7101A82E008000000004679AD350342D501000000000000000
010000000BB4B99DDF6B4934B8DC8B8BA92CF3645
SUMMARY;LANGUAGE=en-US:test2
DTSTART;TZID=Jordan Standard Time:20190729T180000
DTEND;TZID=Jordan Standard Time:20190729T183000
CLASS:PUBLIC
PRIORITY:5
DTSTAMP:20190724T093603Z
TRANSP:OPAQUE
STATUS:CONFIRMED
SEQUENCE:0
LOCATION;LANGUAGE=en-US:
X-MICROSOFT-CDO-APPT-SEQUENCE:0
X-MICROSOFT-CDO-OWNERAPPTID:2117584454
X-MICROSOFT-CDO-BUSYSTATUS:TENTATIVE
X-MICROSOFT-CDO-INTENDEDSTATUS:BUSY
X-MICROSOFT-CDO-ALLDAYEVENT:FALSE
X-MICROSOFT-CDO-IMPORTANCE:1
X-MICROSOFT-CDO-INSTTYPE:0
X-MICROSOFT-DONOTFORWARDMEETING:FALSE
X-MICROSOFT-DISALLOW-COUNTER:FALSE
X-MICROSOFT-LOCATIONS:[]
BEGIN:VALARM
DESCRIPTION:REMINDER
TRIGGER;RELATED=START:-PT15M
ACTION:DISPLAY
END:VALARM
END:VEVENT
END:VCALENDAR
";
private $icsContents2 =
"BEGIN:VCALENDAR
VERSION:2.0
CALSCALE:GREGORIAN
BEGIN:VEVENT
SUMMARY:Test-2a
DTSTART;TZID=America/New_York:20130802T103400
DTEND;TZID=America/New_York:20130802T110400
LOCATION:1 Broadway Ave.\, Brooklyn
DESCRIPTION: Description Test 1
STATUS:CONFIRMED
SEQUENCE:3
BEGIN:VALARM
TRIGGER:-PT10M
DESCRIPTION:Pickup Reminder
ACTION:DISPLAY
END:VALARM
END:VEVENT
END:VCALENDAR
";
private $icsContents3 =
"BEGIN:VCALENDAR
PRODID:-//Google Inc//Google Calendar 70.9054//EN
VERSION:2.0
CALSCALE:GREGORIAN
METHOD:REQUEST
BEGIN:VEVENT
DTSTART;VALUE=DATE:20210810
DTEND;VALUE=DATE:20210811
DTSTAMP:20210810T091857Z
ORGANIZER;CN=test:mailto:test@group.calendar.google.c
om
UID:4r15namb5v2h4dou58gkfajjbe@google.com
ATTENDEE;CUTYPE=INDIVIDUAL;ROLE=REQ-PARTICIPANT;PARTSTAT=NEEDS-ACTION;RSVP=
TRUE;CN=test.com;X-NUM-GUESTS=0:mailto:test@test.com
X-MICROSOFT-CDO-OWNERAPPTID:1443094082
CREATED:20210810T091748Z
LAST-MODIFIED:20210810T091856Z
LOCATION:
SEQUENCE:0
STATUS:CONFIRMED
SUMMARY:test ics 4
TRANSP:TRANSPARENT
END:VEVENT
END:VCALENDAR";
public function testEvent1(): void
{
$ical = new ICal();
$ical->initString($this->icsContents1);
/* @var $event Event */
$event = $ical->events()[0];
$espoEvent = MailEvent::create()
->withUid($event->uid ?? null)
->withDateStart($event->dtstart_tz ?? null)
->withDateEnd($event->dtend_tz ?? null)
->withName($event->summary ?? null)
->withLocation($event->location ?? null)
->withDescription($event->description ?? null)
->withTimezone($ical->calendarTimeZone() ?? null)
->withOrganizer($event->organizer)
->withAttendees($event->attendee);
$this->assertEquals(
'040000008200E00074C5B7101A82E008000000004679AD350342D501000000000000000' .
'010000000BB4B99DDF6B4934B8DC8B8BA92CF3645',
$espoEvent->getUid()
);
$this->assertEquals('2019-07-29 15:00:00', $espoEvent->getDateStart());
$this->assertEquals('test-org@test.com', $espoEvent->getOrganizerEmailAddress());
$this->assertEquals(['test-1@test.com', 'test-2@test.com'], $espoEvent->getAttendeeEmailAddressList());
}
public function testEvent2(): void
{
$ical = new ICal();
$ical->initString($this->icsContents2);
/* @var $event Event */
$event = $ical->events()[0];
$espoEvent = MailEvent::create()
->withUid($event->uid ?? null)
->withDateStart($event->dtstart_tz ?? null)
->withDateEnd($event->dtend_tz ?? null)
->withName($event->summary ?? null)
->withLocation($event->location ?? null)
->withDescription($event->description ?? null)
->withTimezone($ical->calendarTimeZone() ?? null)
->withOrganizer($event->organizer)
->withAttendees($event->attendee);
$this->assertEquals(
null,
$espoEvent->getUid()
);
$this->assertEquals('2013-08-02 14:34:00', $espoEvent->getDateStart());
$this->assertEquals(null, $espoEvent->getOrganizerEmailAddress());
$this->assertEquals([], $espoEvent->getAttendeeEmailAddressList());
$this->assertEquals(
"1 Broadway Ave., Brooklyn",
$espoEvent->getLocation()
);
$this->assertFalse($espoEvent->isAllDay());
}
public function testEvent3(): void
{
$ical = new ICal();
$ical->initString($this->icsContents3);
$event = EventFactory::createFromU01jmg3Ical($ical);
$this->assertTrue($event->isAllDay());
$this->assertEquals('2021-08-10', $event->getDateStart());
$this->assertEquals('2021-08-10', $event->getDateEnd());
}
}

View File

@@ -0,0 +1,295 @@
<?php
/************************************************************************
* This file is part of EspoCRM.
*
* EspoCRM – Open Source CRM application.
* Copyright (C) 2014-2026 EspoCRM, Inc.
* Website: https://www.espocrm.com
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
* The interactive user interfaces in modified source and object code versions
* of this program must display Appropriate Legal Notices, as required under
* Section 5 of the GNU Affero General Public License version 3.
*
* In accordance with Section 7(b) of the GNU Affero General Public License version 3,
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
************************************************************************/
namespace tests\unit\Espo\Core\Mail;
use Espo\Entities\Email;
use Espo\Entities\EmailFilter;
use Espo\Core\Mail\FiltersMatcher;
use Espo\ORM\EntityManager;
use PHPUnit\Framework\TestCase;
class FiltersMatcherTest extends TestCase
{
private $object;
private $entityManager;
private $emailDefs;
private $filterDefs;
protected function setUp() : void
{
$this->object = new FiltersMatcher();
$this->entityManager = $this->createMock(EntityManager::class);
$this->emailDefs = [
'attributes' => [
'from' => [
'type' => 'varchar'
],
'to' => [
'type' => 'varchar'
],
'name' => [
'type' => 'varchar'
],
'subject' => [
'type' => 'varchar'
],
'body' => [
'type' => 'text'
],
'bodyPlain' => [
'type' => 'text'
]
]
];
$this->filterDefs = [
'attributes' => [
'from' => [
'type' => 'varchar'
],
'to' => [
'type' => 'varchar'
],
'subject' => [
'type' => 'varchar'
],
'bodyContains' => [
'type' => 'jsonArray'
],
'bodyContainsAll' => [
'type' => 'jsonArray'
],
]
];
}
protected function tearDown() : void
{
$this->object = NULL;
}
protected function createEntity(string $entityType, string $className, array $defs)
{
return new $className($entityType, $defs, $this->entityManager);
}
public function testMatch(): void
{
$email = $this->createEntity('Email', Email::class, $this->emailDefs);
$email->set('from', 'test@tester');
$filter = $this->createEntity('EmailFilter', EmailFilter::class, $this->filterDefs);
$filter->set([
'from' => 'test@tester'
]);
$filterList = [$filter];
$this->assertNotNull($this->object->findMatch($email, $filterList));
$email = $this->createEntity('Email', Email::class, $this->emailDefs);
$email->set('from', 'test@tester');
$filter = $this->createEntity('EmailFilter', EmailFilter::class, $this->filterDefs);
$filter->set([
'from' => '*@tester'
]);
$filterList = [$filter];
$this->assertNotNull($this->object->findMatch($email, $filterList));
$email->set('from', 'test@tester');
$email->set('to', 'test@tester;baraka@tester');
$filter = $this->createEntity('EmailFilter', EmailFilter::class, $this->filterDefs);
$filter->set([
'to' => 'baraka@tester'
]);
$filterList = [$filter];
$this->assertNotNull($this->object->findMatch($email, $filterList));
$email->set('from', 'test@tester');
$email->set('to', 'test@tester;baraka@man');
$filter = $this->createEntity('EmailFilter', EmailFilter::class, $this->filterDefs);
$filter->set([
'to' => '*@tester'
]);
$filterList = [$filter];
$this->assertNotNull($this->object->findMatch($email, $filterList));
$email->set('subject', 'test hello man');
$filter = $this->createEntity('EmailFilter', EmailFilter::class, $this->filterDefs);
$filter->set([
'subject' => '*hello*'
]);
$filterList = [$filter];
$this->assertNotNull($this->object->findMatch($email, $filterList));
$email->set('name', 'test hello man');
$filter = $this->createEntity('EmailFilter', EmailFilter::class, $this->filterDefs);
$filter->set([
'subject' => 'hello'
]);
$filterList = [$filter];
$this->assertNull($this->object->findMatch($email, $filterList));
$email->set('name', 'test hello man');
$email->set('from', 'test@tester');
$filter = $this->createEntity('EmailFilter', EmailFilter::class, $this->filterDefs);
$filter->set([
'subject' => '*hello*',
'from' => 'test@tester'
]);
$filterList = [$filter];
$this->assertNotNull($this->object->findMatch($email, $filterList));
$email->set('name', 'test hello man');
$email->set('from', 'hello@tester');
$filter = $this->createEntity('EmailFilter', EmailFilter::class, $this->filterDefs);
$filter->set([
'subject' => '*hello*',
'from' => 'test@tester'
]);
$filterList = [$filter];
$this->assertNull($this->object->findMatch($email, $filterList));
$email->set('name', 'test hello man');
$email->set('body', 'one hello three');
$filter = $this->createEntity('EmailFilter', EmailFilter::class, $this->filterDefs);
$filter->set([
'subject' => 'test hello man',
'bodyContains' => ['hello']
]);
$filterList = [$filter];
$this->assertNull($this->object->findMatch($email, $filterList, true));
$email->set('name', 'test hello man');
$email->set('body', 'one hello three');
$filter = $this->createEntity('EmailFilter', EmailFilter::class, $this->filterDefs);
$filter->set([
'subject' => 'test hello man',
'bodyContains' => ['hello']
]);
$filterList = [$filter];
$this->assertNotNull($this->object->findMatch($email, $filterList));
$email->set('name', 'Access information to the EspoCRM cloud');
$email->set('from', 'no-reply@test.com');
$email->set('to', 'info@test.com');
$filter = $this->createEntity('EmailFilter', EmailFilter::class, $this->filterDefs);
$filter->set([
'subject' => 'Access information to the EspoCRM cloud',
'from' => 'no-reply@test.com',
'to' => 'info@test.com'
]);
$this->assertTrue($this->object->match($email, $filter));
$email->set('body', 'test hello');
$filter = $this->createEntity('EmailFilter', EmailFilter::class, $this->filterDefs);
$filter->set([
'bodyContainsAll' => ['test', 'hello'],
]);
$filterList = [$filter];
$this->assertNotNull($this->object->findMatch($email, $filterList));
$email->set('body', 'test');
$filter = $this->createEntity('EmailFilter', EmailFilter::class, $this->filterDefs);
$filter->set([
'bodyContainsAll' => ['test', 'hello'],
]);
$filterList = [$filter];
$this->assertNull($this->object->findMatch($email, $filterList));
$email->set('body', 'test hello one');
$filter = $this->createEntity('EmailFilter', EmailFilter::class, $this->filterDefs);
$filter->set([
'bodyContains' => ['test', 'one'],
'bodyContainsAll' => ['test', 'hello'],
]);
$filterList = [$filter];
$this->assertNotNull($this->object->findMatch($email, $filterList));
}
public function testMatchBody()
{
$email = $this->createEntity('Email', Email::class, $this->emailDefs);
$email->set('body', 'hello Man tester');
$filter = $this->createEntity('EmailFilter', EmailFilter::class, $this->filterDefs);
$filter->set([
'bodyContains' => ['man', 'red']
]);
$filterList = [$filter];
$this->assertNotNull($this->object->findMatch($email, $filterList));
$email = $this->createEntity('Email', Email::class, $this->emailDefs);
$email->set('body', 'hello Man tester');
$email->set('from', 'hello@test');
$filter = $this->createEntity('EmailFilter', EmailFilter::class, $this->filterDefs);
$filter->set([
'bodyContains' => ['man', 'red'],
'from' => 'test@tester'
]);
$filterList = [$filter];
$this->assertNull($this->object->findMatch($email, $filterList));
}
public function testMatchEmpty(): void
{
$email = $this->createEntity('Email', Email::class, $this->emailDefs);
$filter = $this->createEntity('EmailFilter', EmailFilter::class, $this->filterDefs);
$email->set([
'name' => 'Test',
'from' => 'test@test.com',
'to' => 'test@test.com',
'body' => 'Test',
]);
$filter->set([
'subject' => '',
'bodyContains' => [''],
'from' => '',
'to' => '',
]);
$this->assertNull($this->object->findMatch($email, [$filter]));
}
}

View File

@@ -0,0 +1,73 @@
<?php
/************************************************************************
* This file is part of EspoCRM.
*
* EspoCRM – Open Source CRM application.
* Copyright (C) 2014-2026 EspoCRM, Inc.
* Website: https://www.espocrm.com
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
* The interactive user interfaces in modified source and object code versions
* of this program must display Appropriate Legal Notices, as required under
* Section 5 of the GNU Affero General Public License version 3.
*
* In accordance with Section 7(b) of the GNU Affero General Public License version 3,
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
************************************************************************/
namespace tests\unit\Espo\Core\Mail;
use Espo\Core\Mail\Importer\Data as ImporterData;
use Espo\Entities\EmailFilter;
class ImporterDataTest extends \PHPUnit\Framework\TestCase
{
function testData1()
{
$filter = $this->createMock(EmailFilter::class);
$data = ImporterData
::create()
->withTeamIdList(['t1'])
->withUserIdList(['u1'])
->withAssignedUserId('a1')
->withFetchOnlyHeader(true)
->withFolderData(['t' => '1'])
->withFilterList([$filter]);
$this->assertEquals(['t1'], $data->getTeamIdList());
$this->assertEquals(['u1'], $data->getUserIdList());
$this->assertEquals('a1', $data->getAssignedUserId());
$this->assertEquals(true, $data->fetchOnlyHeader());
$this->assertEquals(['t' => '1'], $data->getFolderData());
$this->assertEquals([$filter], $data->getFilterList());
}
function testData2()
{
$data = ImporterData
::create()
->withFetchOnlyHeader(false);
$this->assertEquals([], $data->getTeamIdList());
$this->assertEquals([], $data->getUserIdList());
$this->assertEquals(null, $data->getAssignedUserId());
$this->assertEquals(false, $data->fetchOnlyHeader());
$this->assertEquals([], $data->getFilterList());
}
}

View File

@@ -0,0 +1,236 @@
<?php
/************************************************************************
* This file is part of EspoCRM.
*
* EspoCRM – Open Source CRM application.
* Copyright (C) 2014-2026 EspoCRM, Inc.
* Website: https://www.espocrm.com
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
* The interactive user interfaces in modified source and object code versions
* of this program must display Appropriate Legal Notices, as required under
* Section 5 of the GNU Affero General Public License version 3.
*
* In accordance with Section 7(b) of the GNU Affero General Public License version 3,
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
************************************************************************/
namespace tests\unit\Espo\Core\Mail;
use Espo\Entities\Email;
use Espo\Core\Notification\AssignmentNotificator;
use Espo\ORM\Metadata;
use Espo\ORM\Value\ValueAccessor;
use Espo\ORM\Value\ValueAccessorFactory;
use Espo\Core\FieldProcessing\Relation\LinkMultipleSaver;
use Espo\Core\Job\JobSchedulerFactory;
use Espo\Core\Mail\Importer;
use Espo\Core\Mail\Importer\Data as ImporterData;
use Espo\Core\Mail\MessageWrapper;
use Espo\Core\Mail\ParserFactory;
use Espo\Core\Mail\Parsers\MailMimeParser;
use Espo\Core\Notification\AssignmentNotificatorFactory;
use Espo\Core\ORM\EntityManager;
use Espo\Core\Repositories\Database;
use Espo\Core\Utils\Config;
use Espo\ORM\Repository\RDBSelectBuilder;
use PHPUnit\Framework\TestCase;
class ImporterTest extends TestCase
{
private $emailRepository;
private $config;
private $assignmentNotificatorFactory;
private $parserFactory;
private $linkMultipleSaver;
private $parentFinder;
private $jobSchedulerFactory;
private $duplicateFinder;
private $email;
private $repositoryMap;
private $entityManager;
protected function setUp(): void
{
$entityManager = $this->entityManager = $this->createMock(EntityManager::class);
$this->config = $this->createMock(Config::class);
$emailRepository = $this->createMock(Database::class);
$emptyRepository = $this->createMock(Database::class);
$metadata = $this->createMock(Metadata::class);
$selectBuilder = $this->createMock(RDBSelectBuilder::class);
$this->assignmentNotificatorFactory = $this->createMock(AssignmentNotificatorFactory::class);
$this->parserFactory = $this->createMock(ParserFactory::class);
$this->linkMultipleSaver = $this->createMock(LinkMultipleSaver::class);
$this->parentFinder = $this->createMock(Importer\ParentFinder::class);
$this->parserFactory
->expects($this->any())
->method('create')
->willReturnCallback(
function () {
return new MailMimeParser($this->entityManager);
}
);
$emailRepository
->expects($this->any())
->method('where')
->willReturn($selectBuilder);
$emailRepository
->expects($this->any())
->method('select')
->willReturn($selectBuilder);
$entityManager
->expects($this->any())
->method('getMetadata')
->willReturn($metadata);
$metadata
->expects($this->any())
->method('get')
->willReturn(null);
$emptyRepository
->expects($this->any())
->method('where')
->willReturn($selectBuilder);
$this->emailRepository = $emailRepository;
$this->repositoryMap = [
[Email::class, $this->emailRepository],
//['Account', $emptyRepository],
//['Contact', $emptyRepository],
//['Lead', $emptyRepository],
];
$valueAccessor = $this->createMock(ValueAccessor::class);
$valueAccessorFactory = $this->createMock(ValueAccessorFactory::class);
$valueAccessorFactory
->expects($this->any())
->method('create')
->willReturn(
$valueAccessor
);
$emailDefs = require('tests/unit/testData/Core/Mail/email_defs.php');
//$attachmentDefs = require('tests/unit/testData/Core/Mail/attachment_defs.php');
$this->email = new Email('Email', $emailDefs, $entityManager, $valueAccessorFactory);
//$attachment = new Attachment('Attachment', $attachmentDefs, $entityManager, $valueAccessorFactory);
$this->assignmentNotificatorFactory
->expects($this->any())
->method('create')
->willReturn(
$this->createMock(AssignmentNotificator::class)
);
$this->duplicateFinder = $this->createMock(Importer\DuplicateFinder::class);
$this->jobSchedulerFactory = $this->createMock(JobSchedulerFactory::class);
}
public function testImport1(): void
{
$entityManager = $this->entityManager;
$config = $this->config;
$email = $this->email;
$entityManager
->expects($this->any())
->method('getRDBRepositoryByClass')
->willReturnMap($this->repositoryMap);
$entityManager
->expects($this->exactly(1))
->method('saveEntity')
->with($this->isInstanceOf(Email::class))
->willReturnCallback(function (Email $entity) {
$entity->set('id', 'test-id');
});
$this->emailRepository
->expects($this->once())
->method('getNew')
->willReturn($email);
$config
->expects($this->any())
->method('get')
->willReturnMap(
[
['b2cMode', false]
]
);
$saver = $this->createMock(Importer\EmailSaver::class);
$saver
->expects($this->exactly(1))
->method('save')
->with($this->isInstanceOf(Email::class));
$contents = file_get_contents('tests/unit/testData/Core/Mail/test_email_1.eml');
$importer = new Importer\DefaultImporter(
entityManager: $entityManager,
config: $config,
notificatorFactory: $this->assignmentNotificatorFactory,
parserFactory: $this->parserFactory,
linkMultipleSaver: $this->linkMultipleSaver,
duplicateFinder: $this->duplicateFinder,
jobSchedulerFactory: $this->jobSchedulerFactory,
parentFinder: $this->parentFinder,
autoReplyDetector: $this->createMock(Importer\AutoReplyDetector::class),
emailSaver: $saver,
);
$message = new MessageWrapper(0, null, null, $contents);
$data = ImporterData
::create()
->withTeamIdList(['teamTestId'])
->withUserIdList(['userTestId']);
$email = $importer->import($message, $data);
$this->assertEquals('test 3', $email->get('name'));
$userIdList = $email->getLinkMultipleIdList('users');
$this->assertTrue(in_array('userTestId', $userIdList));
$this->assertStringContainsString('<br>Admin Test', $email->get('body'));
$this->assertStringContainsString('Admin Test', $email->get('bodyPlain'));
$messageId = '<e558c4dfc2a0f0d60f5ebff474c97ffc/1466410740/1950@espo>';
$this->assertEquals($messageId, $email->get('messageId'));
$this->assertEquals($messageId . '-r@test.com', $email->get('messageIdInternal'));
}
}

View File

@@ -0,0 +1,72 @@
<?php
/************************************************************************
* This file is part of EspoCRM.
*
* EspoCRM – Open Source CRM application.
* Copyright (C) 2014-2026 EspoCRM, Inc.
* Website: https://www.espocrm.com
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
* The interactive user interfaces in modified source and object code versions
* of this program must display Appropriate Legal Notices, as required under
* Section 5 of the GNU Affero General Public License version 3.
*
* In accordance with Section 7(b) of the GNU Affero General Public License version 3,
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
************************************************************************/
namespace tests\unit\Espo\Core\Mail;
use Espo\Core\Mail\SenderParams;
class SenderParamsTest extends \PHPUnit\Framework\TestCase
{
public function testFromArray1(): void
{
$array = [
'fromAddress' => 'test@test',
'fromName' => 'name',
'replyToAddress' => 'reply@test',
'replyToName' => 'reply',
];
$this->assertEquals($array, SenderParams::fromArray($array)->toArray());
}
public function testFromArray2(): void
{
$array = [
'fromAddress' => 'test@test',
'fromName' => 'name',
];
$this->assertEquals($array, SenderParams::fromArray($array)->toArray());
}
public function testBuilding(): void
{
$params = SenderParams::create()
->withFromAddress('test@test')
->withFromName('name')
->withReplyToAddress('r@test')
->withReplyToName('r');
$this->assertEquals('test@test', $params->getFromAddress());
$this->assertEquals('name', $params->getFromName());
$this->assertEquals('r@test', $params->getReplyToAddress());
$this->assertEquals('r', $params->getReplyToName());
}
}

View File

@@ -0,0 +1,91 @@
<?php
/************************************************************************
* This file is part of EspoCRM.
*
* EspoCRM – Open Source CRM application.
* Copyright (C) 2014-2026 EspoCRM, Inc.
* Website: https://www.espocrm.com
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
* The interactive user interfaces in modified source and object code versions
* of this program must display Appropriate Legal Notices, as required under
* Section 5 of the GNU Affero General Public License version 3.
*
* In accordance with Section 7(b) of the GNU Affero General Public License version 3,
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
************************************************************************/
namespace tests\unit\Espo\Core\Mail;
use Espo\Core\Mail\SmtpParams;
use PHPUnit\Framework\TestCase;
class SmtpParamsTest extends TestCase
{
public function testFromArray1(): void
{
$array = [
'server' => 'localhost',
'port' => 587,
'fromAddress' => 'test@test',
'fromName' => 'name',
'auth' => false,
];
$this->assertEquals($array, SmtpParams::fromArray($array)->toArray());
}
public function testFromArray2(): void
{
$array = [
'server' => 'localhost',
'port' => 587,
'fromAddress' => 'test@test',
'fromName' => 'name',
'auth' => true,
'connectionOptions' => ['test' => 'test'],
'authMechanism' => 'login',
'username' => 'tester',
'password' => 'password',
'security' => 'ssl',
];
$this->assertEquals($array, SmtpParams::fromArray($array)->toArray());
}
public function testBuilding(): void
{
$params = SmtpParams::create('localhost', 587)
->withFromAddress('test@test')
->withFromName('name')
->withAuth()
->withUsername('tester')
->withPassword('test')
->withAuthMechanism('login')
->withConnectionOptions(['test' => 'test']);
$this->assertEquals('localhost', $params->getServer());
$this->assertEquals(587, $params->getPort());
$this->assertEquals('test@test', $params->getFromAddress());
$this->assertEquals('name', $params->getFromName());
$this->assertEquals(true, $params->useAuth());
$this->assertEquals('tester', $params->getUsername());
$this->assertEquals('test', $params->getPassword());
$this->assertEquals(['test' => 'test'], $params->getConnectionOptions());
}
}