99f1655d04
CI / unit (8.2) (push) Has been cancelled
CI / unit (8.3) (push) Has been cancelled
CI / unit (8.4) (push) Has been cancelled
CI / psalm (push) Has been cancelled
CI / frontend (push) Has been cancelled
CI / integration (mariadb, 32) (push) Has been cancelled
CI / integration (mariadb, 33) (push) Has been cancelled
CI / integration (mariadb, 34) (push) Has been cancelled
CI / integration (mariadb, 35) (push) Has been cancelled
CI / integration (pgsql, 32) (push) Has been cancelled
CI / integration (pgsql, 33) (push) Has been cancelled
CI / integration (pgsql, 34) (push) Has been cancelled
CI / integration (pgsql, 35) (push) Has been cancelled
CI / integration (sqlite, 32) (push) Has been cancelled
CI / integration (sqlite, 33) (push) Has been cancelled
CI / integration (sqlite, 34) (push) Has been cancelled
CI / integration (sqlite, 35) (push) Has been cancelled
CI / integration-targets (push) Has been cancelled
CI / integration-user (32) (push) Has been cancelled
CI / integration-user (33) (push) Has been cancelled
CI / integration-user (34) (push) Has been cancelled
CI / integration-user (35) (push) Has been cancelled
CI / integration-full-restore (push) Has been cancelled
- A location that a passphrase change could not reach gets it at its next backup - A key from before 1.1.0 is never replaced as an unused one - An unreadable settings file on a location is told apart from a missing one - A schedule is shown and saved in the zone it was saved in - Saving an unchanged schedule keeps its moment; --timezone needs a schedule - Schedule coverage uses the time the backup began reading its data - Tests: catch-up scenario, upgrade test restores the new file, autumn clock change
78 lines
3.5 KiB
PHP
78 lines
3.5 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace OCA\NgBackup\Tests\Unit\Service;
|
|
|
|
use OCA\NgBackup\Service\LocationSchedule as S;
|
|
use PHPUnit\Framework\TestCase;
|
|
|
|
class LocationScheduleTest extends TestCase {
|
|
private const BERLIN = 'Europe/Berlin';
|
|
|
|
private static function at(string $time): int {
|
|
return (int)strtotime($time);
|
|
}
|
|
|
|
public function testWhatCanBeSaved(): void {
|
|
$this->assertSame('', S::normalize('off'));
|
|
$this->assertSame('', S::normalize(' '));
|
|
$this->assertSame('02:30', S::normalize('02:30'));
|
|
$this->assertSame('02:30', S::normalize('02:30/24'));
|
|
$this->assertSame('00:00/6', S::normalize('00:00/6'));
|
|
foreach (['2:30', '24:00', '02:30/0', '02:30/25', 'daily', '02:30/x'] as $bad) {
|
|
try {
|
|
S::normalize($bad);
|
|
$this->fail("$bad accepted");
|
|
} catch (\InvalidArgumentException) {
|
|
$this->addToAssertionCount(1);
|
|
}
|
|
}
|
|
}
|
|
|
|
public function testOffHasNoMoment(): void {
|
|
$this->assertNull(S::lastMoment('', self::BERLIN, self::at('2026-11-10 12:00 UTC')));
|
|
}
|
|
|
|
public function testDailyIsInTheZoneOfTheLocation(): void {
|
|
$this->assertSame(self::at('2026-11-10 02:00 UTC'), S::lastMoment('03:00', self::BERLIN, self::at('2026-11-10 12:00 UTC')));
|
|
$this->assertSame(self::at('2026-11-09 02:00 UTC'), S::lastMoment('03:00', self::BERLIN, self::at('2026-11-10 01:00 UTC')), 'not yet today');
|
|
}
|
|
|
|
public function testEveryNHoursStartsOverTheNextDay(): void {
|
|
$zone = 'UTC';
|
|
$this->assertSame(self::at('2026-11-10 12:00 UTC'), S::lastMoment('02:00/5', $zone, self::at('2026-11-10 13:00 UTC')));
|
|
$this->assertSame(self::at('2026-11-09 22:00 UTC'), S::lastMoment('02:00/5', $zone, self::at('2026-11-10 01:59 UTC')), '02, 07, 12, 17, 22, then 02 again');
|
|
$this->assertSame(self::at('2026-11-10 02:00 UTC'), S::lastMoment('02:00/5', $zone, self::at('2026-11-10 02:00 UTC')), 'at the moment itself');
|
|
}
|
|
|
|
public function testAClockChangeInTheNightStillGivesOneMoment(): void {
|
|
$this->assertSame(self::at('2026-03-29 01:30 UTC'), S::lastMoment('02:30', self::BERLIN, self::at('2026-03-29 12:00 UTC')), '02:30 does not exist that night');
|
|
$autumn = S::lastMoment('02:30', self::BERLIN, self::at('2026-10-25 12:00 UTC'));
|
|
$this->assertNotNull($autumn);
|
|
$local = (new \DateTimeImmutable('@' . $autumn))->setTimezone(new \DateTimeZone(self::BERLIN));
|
|
$this->assertSame('2026-10-25 02:30', $local->format('Y-m-d H:i'), '02:30 happens twice that night: one of them, on that day');
|
|
}
|
|
|
|
public function testChoosingATimeDoesNotStartABackup(): void {
|
|
$moment = self::at('2026-11-10 02:00 UTC');
|
|
$this->assertFalse(S::isDue($moment, $moment + 1, null, 0, null, $moment + 3600));
|
|
$this->assertTrue(S::isDue($moment, $moment - 1, null, 0, null, $moment + 3600));
|
|
$this->assertTrue(S::isDue($moment, 0, null, 0, null, $moment), 'a location from before schedules per location');
|
|
}
|
|
|
|
public function testABackupSinceTheMomentIsEnough(): void {
|
|
$moment = 1000000;
|
|
$this->assertFalse(S::isDue($moment, 0, $moment, 1, $moment + 10, $moment + 20));
|
|
$this->assertTrue(S::isDue($moment, 0, $moment - 1, 0, null, $moment + 20), 'it started before this moment: one more');
|
|
}
|
|
|
|
public function testAfterAFailureOnlyAFewAttemptsAnHourApart(): void {
|
|
$moment = 1000000;
|
|
$finished = $moment + 60;
|
|
$this->assertFalse(S::isDue($moment, 0, null, 1, $finished, $finished + S::RETRY_AFTER - 1));
|
|
$this->assertTrue(S::isDue($moment, 0, null, 1, $finished, $finished + S::RETRY_AFTER));
|
|
$this->assertFalse(S::isDue($moment, 0, null, S::MAX_ATTEMPTS, $finished, $finished + 86400));
|
|
}
|
|
}
|