Skip to content

Commit 059ac3c

Browse files
committed
test: cover abstract identify renewal rules
Signed-off-by: Vitor Mattos <[email protected]>
1 parent 31207cc commit 059ac3c

1 file changed

Lines changed: 184 additions & 0 deletions

File tree

Lines changed: 184 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,184 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
/**
6+
* SPDX-FileCopyrightText: 2026 LibreCode coop and contributors
7+
* SPDX-License-Identifier: AGPL-3.0-or-later
8+
*/
9+
10+
namespace OCA\Libresign\Tests\Unit\Service\IdentifyMethod;
11+
12+
use OCA\Libresign\AppInfo\Application;
13+
use OCA\Libresign\Db\IdentifyMethod;
14+
use OCA\Libresign\Db\SignRequest;
15+
use OCA\Libresign\Db\SignRequestMapper;
16+
use OCA\Libresign\Exception\LibresignException;
17+
use OCA\Libresign\Service\IdentifyMethod\AbstractIdentifyMethod;
18+
use OCA\Libresign\Service\IdentifyMethod\IdentifyService;
19+
use OCA\Libresign\Service\SessionService;
20+
use OCP\AppFramework\Utility\ITimeFactory;
21+
use OCP\IAppConfig;
22+
use OCP\IL10N;
23+
use PHPUnit\Framework\Attributes\DataProvider;
24+
use PHPUnit\Framework\MockObject\MockObject;
25+
26+
final class AbstractIdentifyMethodTest extends \OCA\Libresign\Tests\Unit\TestCase {
27+
private IdentifyService&MockObject $identifyService;
28+
private IAppConfig&MockObject $appConfig;
29+
private SessionService&MockObject $sessionService;
30+
private SignRequestMapper&MockObject $signRequestMapper;
31+
private ITimeFactory&MockObject $timeFactory;
32+
33+
public function setUp(): void {
34+
$this->identifyService = $this->createMock(IdentifyService::class);
35+
$this->appConfig = $this->createMock(IAppConfig::class);
36+
$this->sessionService = $this->createMock(SessionService::class);
37+
$this->signRequestMapper = $this->createMock(SignRequestMapper::class);
38+
$this->timeFactory = $this->createMock(ITimeFactory::class);
39+
$l10n = $this->createMock(IL10N::class);
40+
41+
$l10n->method('t')
42+
->willReturnCallback(static fn (string $text): string => $text);
43+
$this->identifyService->method('getL10n')->willReturn($l10n);
44+
$this->identifyService->method('getAppConfig')->willReturn($this->appConfig);
45+
$this->identifyService->method('getSessionService')->willReturn($this->sessionService);
46+
$this->identifyService->method('getSignRequestMapper')->willReturn($this->signRequestMapper);
47+
$this->identifyService->method('getTimeFactory')->willReturn($this->timeFactory);
48+
}
49+
50+
#[DataProvider('providerRuntimeConfigReadPaths')]
51+
public function testRuntimeConfigIsReadWithCacheRefresh(string $path): void {
52+
$cacheCleared = false;
53+
$this->appConfig->expects($this->once())
54+
->method('clearCache')
55+
->with(true)
56+
->willReturnCallback(function () use (&$cacheCleared): void {
57+
$cacheCleared = true;
58+
});
59+
$this->appConfig->expects($this->once())
60+
->method('getValueInt')
61+
->with(Application::APP_ID, 'renewal_interval', SessionService::NO_RENEWAL_INTERVAL)
62+
->willReturnCallback(function () use (&$cacheCleared): int {
63+
$this->assertTrue($cacheCleared);
64+
return 10;
65+
});
66+
67+
$identifyMethod = $this->newIdentifyMethodEntity(
68+
signRequestId: 10,
69+
identifierValue: '[email protected]',
70+
lastAttemptDate: '2026-02-16T10:00:01+00:00',
71+
identifiedAtDate: null,
72+
);
73+
74+
if ($path === 'renewSession') {
75+
$this->sessionService->expects($this->once())
76+
->method('setIdentifyMethodId')
77+
->with(99);
78+
$this->sessionService->expects($this->once())
79+
->method('resetDurationOfSignPage');
80+
$this->newMethodWithEntity($identifyMethod)->runRenewSession();
81+
return;
82+
}
83+
84+
$this->sessionService->method('getSignStartTime')->willReturn(0);
85+
$this->signRequestMapper->method('getById')->with(10)->willReturn($this->newSignRequest(
86+
createdAt: '2026-02-16T10:00:09+00:00',
87+
uuid: '9f95dc38-c2f8-43e5-a91d-8e191ca9520d',
88+
));
89+
$this->timeFactory->method('getDateTime')->willReturn(new \DateTime('2026-02-16T10:00:10+00:00'));
90+
91+
$this->newMethodWithEntity($identifyMethod)->runThrowIfRenewalIntervalExpired();
92+
}
93+
94+
public static function providerRuntimeConfigReadPaths(): array {
95+
return [
96+
'renewSession path' => ['renewSession'],
97+
'throwIfRenewalIntervalExpired path' => ['throwIfRenewalIntervalExpired'],
98+
];
99+
}
100+
101+
#[DataProvider('providerRenewalWindowByLastAction')]
102+
public function testRenewalWindowUsesIdentifiedAtAsLastAction(?string $identifiedAtDate, bool $mustExpire): void {
103+
$this->appConfig->method('clearCache');
104+
$this->appConfig->method('getValueInt')
105+
->with(Application::APP_ID, 'renewal_interval', SessionService::NO_RENEWAL_INTERVAL)
106+
->willReturn(10);
107+
108+
$this->sessionService->method('getSignStartTime')->willReturn(0);
109+
$this->signRequestMapper->method('getById')->with(10)->willReturn($this->newSignRequest(
110+
createdAt: '2026-02-16T10:00:00+00:00',
111+
uuid: '903c8fa8-f140-4213-a2fd-f435eea3492d',
112+
));
113+
$this->timeFactory->method('getDateTime')->willReturn(new \DateTime('2026-02-16T10:00:12+00:00'));
114+
115+
$identifyMethod = $this->newIdentifyMethodEntity(
116+
signRequestId: 10,
117+
identifierValue: '[email protected]',
118+
lastAttemptDate: '2026-02-16T10:00:01+00:00',
119+
identifiedAtDate: $identifiedAtDate,
120+
);
121+
122+
$method = $this->newMethodWithEntity($identifyMethod);
123+
$method->forceName('email');
124+
125+
if ($mustExpire) {
126+
$this->expectException(LibresignException::class);
127+
$this->expectExceptionMessageMatches('/.*Link expired.*/');
128+
$method->runThrowIfRenewalIntervalExpired();
129+
return;
130+
}
131+
132+
$method->runThrowIfRenewalIntervalExpired();
133+
$this->assertSame(10, $method->getEntity()->getSignRequestId());
134+
}
135+
136+
public static function providerRenewalWindowByLastAction(): array {
137+
return [
138+
'without identifiedAt expires by older lastAttempt' => [null, true],
139+
'with recent identifiedAt keeps renewal valid' => ['2026-02-16T10:00:05+00:00', false],
140+
];
141+
}
142+
143+
private function newMethodWithEntity(IdentifyMethod $entity): AbstractIdentifyMethodForTest {
144+
$method = new AbstractIdentifyMethodForTest($this->identifyService);
145+
$method->setEntity($entity);
146+
return $method;
147+
}
148+
149+
private function newIdentifyMethodEntity(
150+
int $signRequestId,
151+
string $identifierValue,
152+
?string $lastAttemptDate,
153+
?string $identifiedAtDate,
154+
): IdentifyMethod {
155+
$identifyMethod = new IdentifyMethod();
156+
$identifyMethod->setId(99);
157+
$identifyMethod->setSignRequestId($signRequestId);
158+
$identifyMethod->setIdentifierValue($identifierValue);
159+
$identifyMethod->setLastAttemptDate($lastAttemptDate);
160+
$identifyMethod->setIdentifiedAtDate($identifiedAtDate);
161+
return $identifyMethod;
162+
}
163+
164+
private function newSignRequest(string $createdAt, string $uuid): SignRequest {
165+
$signRequest = new SignRequest();
166+
$signRequest->setCreatedAt(new \DateTime($createdAt));
167+
$signRequest->setUuid($uuid);
168+
return $signRequest;
169+
}
170+
}
171+
172+
final class AbstractIdentifyMethodForTest extends AbstractIdentifyMethod {
173+
public function runRenewSession(): void {
174+
$this->renewSession();
175+
}
176+
177+
public function runThrowIfRenewalIntervalExpired(): void {
178+
$this->throwIfRenewalIntervalExpired();
179+
}
180+
181+
public function forceName(string $name): void {
182+
$this->name = $name;
183+
}
184+
}

0 commit comments

Comments
 (0)