]> source.dussan.org Git - nextcloud-server.git/commitdiff
fix(user_status): Fix the user status automation on the day availability rules are...
authorJoas Schilling <coding@schilljs.com>
Fri, 24 Feb 2023 14:22:09 +0000 (15:22 +0100)
committerbackportbot-nextcloud[bot] <backportbot-nextcloud[bot]@users.noreply.github.com>
Tue, 14 Mar 2023 16:39:01 +0000 (16:39 +0000)
Signed-off-by: Joas Schilling <coding@schilljs.com>
apps/dav/lib/BackgroundJob/UserStatusAutomation.php
apps/dav/tests/unit/BackgroundJob/UserStatusAutomationTest.php [new file with mode: 0644]

index 8edc28c9a69a7f14deda53c65ef3cdcfb5494f9e..43fccbf233e2241895c1f08b38ece80e3c4f6299 100644 (file)
@@ -92,7 +92,7 @@ class UserStatusAutomation extends TimedJob {
                $isCurrentlyAvailable = false;
                $nextPotentialToggles = [];
 
-               $now = new \DateTime('now');
+               $now = $this->time->getDateTime();
                $lastMidnight = (clone $now)->setTime(0, 0);
 
                $vObject = Reader::read($property);
@@ -105,9 +105,16 @@ class UserStatusAutomation extends TimedJob {
                        foreach ($availables as $available) {
                                /** @var Available $available */
                                if ($available->name === 'AVAILABLE') {
-                                       /** @var \DateTimeInterface $effectiveStart */
-                                       /** @var \DateTimeInterface $effectiveEnd */
-                                       [$effectiveStart, $effectiveEnd] = $available->getEffectiveStartEnd();
+                                       /** @var \DateTimeImmutable $originalStart */
+                                       /** @var \DateTimeImmutable $originalEnd */
+                                       [$originalStart, $originalEnd] = $available->getEffectiveStartEnd();
+
+                                       // Little shenanigans to fix the automation on the day the rules were adjusted
+                                       // Otherwise the $originalStart would match rules for Thursdays on a Friday, etc.
+                                       // So we simply wind back a week and then fastForward to the next occurrence
+                                       // since today's midnight, which then also accounts for the week days.
+                                       $effectiveStart = \DateTime::createFromImmutable($originalStart)->sub(new \DateInterval('P7D'));
+                                       $effectiveEnd = \DateTime::createFromImmutable($originalEnd)->sub(new \DateInterval('P7D'));
 
                                        try {
                                                $it = new RRuleIterator((string) $available->RRULE, $effectiveStart);
@@ -150,8 +157,10 @@ class UserStatusAutomation extends TimedJob {
                $this->setLastRunToNextToggleTime($userId, $nextAutomaticToggle - 1);
 
                if ($isCurrentlyAvailable) {
+                       $this->logger->debug('User is currently available, reverting DND status if applicable');
                        $this->manager->revertUserStatus($userId, IUserStatus::MESSAGE_AVAILABILITY, IUserStatus::DND);
                } else {
+                       $this->logger->debug('User is currently NOT available, reverting call status if applicable and then setting DND');
                        // The DND status automation is more important than the "Away - In call" so we also restore that one if it exists.
                        $this->manager->revertUserStatus($userId, IUserStatus::MESSAGE_CALL, IUserStatus::AWAY);
                        $this->manager->setUserStatus($userId, IUserStatus::MESSAGE_AVAILABILITY, IUserStatus::DND, true);
diff --git a/apps/dav/tests/unit/BackgroundJob/UserStatusAutomationTest.php b/apps/dav/tests/unit/BackgroundJob/UserStatusAutomationTest.php
new file mode 100644 (file)
index 0000000..46b895b
--- /dev/null
@@ -0,0 +1,159 @@
+<?php
+
+declare(strict_types=1);
+
+/**
+ * @copyright Copyright (c) 2023 Joas Schilling <coding@schilljs.com>
+ *
+ * @author Joas Schilling <coding@schilljs.com>
+ *
+ * @license GNU AGPL version 3 or any later version
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ *
+ */
+
+namespace OCA\DAV\Tests\unit\BackgroundJob;
+
+use OCA\DAV\BackgroundJob\UserStatusAutomation;
+use OCP\AppFramework\Utility\ITimeFactory;
+use OCP\BackgroundJob\IJobList;
+use OCP\IConfig;
+use OCP\UserStatus\IManager;
+use OCP\UserStatus\IUserStatus;
+use PHPUnit\Framework\MockObject\MockObject;
+use Psr\Log\LoggerInterface;
+use Test\TestCase;
+
+/**
+ * @group DB
+ */
+class UserStatusAutomationTest extends TestCase {
+
+       protected MockObject|ITimeFactory $time;
+       protected MockObject|IJobList $jobList;
+       protected MockObject|LoggerInterface $logger;
+       protected MockObject|IManager $statusManager;
+       protected MockObject|IConfig $config;
+
+       protected function setUp(): void {
+               parent::setUp();
+
+               $this->time = $this->createMock(ITimeFactory::class);
+               $this->jobList = $this->createMock(IJobList::class);
+               $this->logger = $this->createMock(LoggerInterface::class);
+               $this->statusManager = $this->createMock(IManager::class);
+               $this->config = $this->createMock(IConfig::class);
+
+       }
+
+       protected function getAutomationMock(array $methods): MockObject|UserStatusAutomation {
+               if (empty($methods)) {
+                       return new UserStatusAutomation(
+                               $this->time,
+                               \OC::$server->getDatabaseConnection(),
+                               $this->jobList,
+                               $this->logger,
+                               $this->statusManager,
+                               $this->config,
+                       );
+               }
+
+               return $this->getMockBuilder(UserStatusAutomation::class)
+                       ->setConstructorArgs([
+                               $this->time,
+                               \OC::$server->getDatabaseConnection(),
+                               $this->jobList,
+                               $this->logger,
+                               $this->statusManager,
+                               $this->config,
+                       ])
+                       ->setMethods($methods)
+                       ->getMock();
+       }
+
+       public function dataRun(): array {
+               return [
+                       ['20230217', '2023-02-24 10:49:36.613834', true],
+                       ['20230224', '2023-02-24 10:49:36.613834', true],
+                       ['20230217', '2023-02-24 13:58:24.479357', false],
+                       ['20230224', '2023-02-24 13:58:24.479357', false],
+               ];
+       }
+
+       /**
+        * @dataProvider dataRun
+        */
+       public function testRun(string $ruleDay, string $currentTime, bool $isAvailable): void {
+               $this->config->method('getUserValue')
+                       ->with('user', 'dav', 'user_status_automation', 'no')
+                       ->willReturn('yes');
+
+               $this->time->method('getDateTime')
+                       ->willReturn(new \DateTime($currentTime, new \DateTimeZone('UTC')));
+
+               $automation = $this->getAutomationMock(['getAvailabilityFromPropertiesTable']);
+               $automation->method('getAvailabilityFromPropertiesTable')
+                       ->with('user')
+                       ->willReturn('BEGIN:VCALENDAR
+PRODID:Nextcloud DAV app
+BEGIN:VTIMEZONE
+TZID:Europe/Berlin
+BEGIN:STANDARD
+TZNAME:CET
+TZOFFSETFROM:+0200
+TZOFFSETTO:+0100
+DTSTART:19701025T030000
+RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=-1SU
+END:STANDARD
+BEGIN:DAYLIGHT
+TZNAME:CEST
+TZOFFSETFROM:+0100
+TZOFFSETTO:+0200
+DTSTART:19700329T020000
+RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=-1SU
+END:DAYLIGHT
+END:VTIMEZONE
+BEGIN:VAVAILABILITY
+BEGIN:AVAILABLE
+DTSTART;TZID=Europe/Berlin:' . $ruleDay . 'T090000
+DTEND;TZID=Europe/Berlin:' . $ruleDay . 'T170000
+UID:3e6feeec-8e00-4265-b822-b73174e8b39f
+RRULE:FREQ=WEEKLY;BYDAY=TH
+END:AVAILABLE
+BEGIN:AVAILABLE
+DTSTART;TZID=Europe/Berlin:' . $ruleDay . 'T090000
+DTEND;TZID=Europe/Berlin:' . $ruleDay . 'T120000
+UID:8a634e99-07cf-443b-b480-005a0e1db323
+RRULE:FREQ=WEEKLY;BYDAY=FR
+END:AVAILABLE
+END:VAVAILABILITY
+END:VCALENDAR');
+
+               if ($isAvailable) {
+                       $this->statusManager->expects($this->once())
+                               ->method('revertUserStatus')
+                               ->with('user', IUserStatus::MESSAGE_AVAILABILITY, IUserStatus::DND);
+               } else {
+                       $this->statusManager->expects($this->once())
+                               ->method('revertUserStatus')
+                               ->with('user', IUserStatus::MESSAGE_CALL, IUserStatus::AWAY);
+                       $this->statusManager->expects($this->once())
+                               ->method('setUserStatus')
+                               ->with('user', IUserStatus::MESSAGE_AVAILABILITY, IUserStatus::DND, true);
+               }
+
+               $this->invokePrivate($automation, 'run', [['userId' => 'user']]);
+       }
+}