Skip to content

Commit 3136585

Browse files
feat(setup-check): add CertificateEngineSetupCheck
Adds CertificateEngineSetupCheck to process certificate engine configuration results. Related issue: #6590 Type: Feature Checklist: - [x] Implement ISetupCheck interface - [x] Use CertificateEngineFactory to get current engine - [x] Process ConfigureCheckHelper results from engine - [x] Convert to SetupResult with aggregated messages and tips - [x] Handle engine not defined (error) - [x] Add unit tests covering success, warning, error, and mixed results
1 parent 202ff52 commit 3136585

2 files changed

Lines changed: 329 additions & 0 deletions

File tree

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
/**
5+
* SPDX-FileCopyrightText: 2026 LibreCode coop and contributors
6+
* SPDX-License-Identifier: AGPL-3.0-or-later
7+
*/
8+
9+
namespace OCA\Libresign\SetupCheck;
10+
11+
use OCA\Libresign\Handler\CertificateEngine\CertificateEngineFactory;
12+
use OCA\Libresign\Helper\ConfigureCheckHelper;
13+
use OCP\IL10N;
14+
use OCP\SetupCheck\ISetupCheck;
15+
use OCP\SetupCheck\SetupResult;
16+
17+
class CertificateEngineSetupCheck implements ISetupCheck
18+
{
19+
private IL10N $l10n;
20+
private CertificateEngineFactory $certificateEngineFactory;
21+
22+
public function __construct(
23+
IL10N $l10n,
24+
CertificateEngineFactory $certificateEngineFactory
25+
) {
26+
$this->l10n = $l10n;
27+
$this->certificateEngineFactory = $certificateEngineFactory;
28+
}
29+
30+
public function getName(): string
31+
{
32+
return $this->l10n->t('Certificate engine');
33+
}
34+
35+
public function getCategory(): string
36+
{
37+
return 'security';
38+
}
39+
40+
public function run(): SetupResult
41+
{
42+
try {
43+
$engine = $this->certificateEngineFactory->getEngine();
44+
$checkResults = $engine->configureCheck();
45+
} catch (\Throwable $e) {
46+
$engineName = $this->certificateEngineFactory->getEngine()->getName() ?? 'unknown';
47+
return SetupResult::error(
48+
$this->l10n->t('Define the certificate engine to use'),
49+
$this->l10n->t('Run occ libresign:configure:%s --help', [$engineName])
50+
);
51+
}
52+
53+
// Process results: if any error, return error; else if any warning, return warning; else success
54+
$hasError = false;
55+
$hasWarning = false;
56+
$messages = [];
57+
$tips = [];
58+
59+
foreach ($checkResults as $result) {
60+
if ($result instanceof ConfigureCheckHelper) {
61+
$status = $result->getStatus();
62+
$msg = $result->getMessage();
63+
$tip = $result->getTip();
64+
65+
if ($status === 'error') {
66+
$hasError = true;
67+
$messages[] = "[ERROR] $msg";
68+
} elseif ($status === 'warning') {
69+
$hasWarning = true;
70+
$messages[] = "[WARNING] $msg";
71+
} else {
72+
$messages[] = $msg;
73+
}
74+
75+
if (!empty($tip)) {
76+
$tips[] = $tip;
77+
}
78+
}
79+
}
80+
81+
$tip = '';
82+
if (!empty($tips)) {
83+
$tip = implode("\n", array_unique($tips));
84+
}
85+
86+
if ($hasError) {
87+
return SetupResult::error(implode("\n", $messages), $tip);
88+
} elseif ($hasWarning) {
89+
return SetupResult::warning(implode("\n", $messages), $tip);
90+
} else {
91+
return SetupResult::success(implode("\n", $messages) ?: $this->l10n->t('Certificate engine is configured correctly'));
92+
}
93+
}
94+
}
Lines changed: 235 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,235 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
/**
5+
* SPDX-FileCopyrightText: 2026 LibreCode coop and contributors
6+
* SPDX-License-Identifier: AGPL-3.0-or-later
7+
*/
8+
9+
namespace OCA\Libresign\Tests\Unit\SetupCheck;
10+
11+
use OCA\Libresign\Handler\CertificateEngine\CertificateEngineFactory;
12+
use OCA\Libresign\Handler\CertificateEngine\IEngineHandler;
13+
use OCA\Libresign\Helper\ConfigureCheckHelper;
14+
use OCA\Libresign\SetupCheck\CertificateEngineSetupCheck;
15+
use OCP\IL10N;
16+
use OCP\SetupCheck\SetupResult;
17+
use PHPUnit\Framework\MockObject\MockObject;
18+
use PHPUnit\Framework\TestCase;
19+
20+
interface MockableIEngineHandler extends IEngineHandler
21+
{
22+
public function getName(): string;
23+
}
24+
25+
final class CertificateEngineSetupCheckTest extends TestCase
26+
{
27+
/** @var IL10N&MockObject */
28+
private $l10n;
29+
/** @var CertificateEngineFactory&MockObject */
30+
private $certificateEngineFactory;
31+
32+
protected function setUp(): void
33+
{
34+
parent::setUp();
35+
$this->l10n = $this->createMock(IL10N::class);
36+
$this->certificateEngineFactory = $this->createMock(CertificateEngineFactory::class);
37+
}
38+
39+
private function getInstance(): CertificateEngineSetupCheck
40+
{
41+
return new CertificateEngineSetupCheck(
42+
$this->l10n,
43+
$this->certificateEngineFactory
44+
);
45+
}
46+
47+
public function testGetName(): void
48+
{
49+
$this->l10n->expects($this->once())
50+
->method('t')
51+
->with('Certificate engine')
52+
->willReturn('Certificate engine');
53+
54+
$check = $this->getInstance();
55+
$this->assertSame('Certificate engine', $check->getName());
56+
}
57+
58+
public function testGetCategory(): void
59+
{
60+
$check = $this->getInstance();
61+
$this->assertSame('security', $check->getCategory());
62+
}
63+
64+
public function testRunWithException(): void
65+
{
66+
$engineMock = $this->createMock(MockableIEngineHandler::class);
67+
$engineMock->method('getName')->willReturn('cfssl');
68+
69+
$this->certificateEngineFactory->expects($this->exactly(2))
70+
->method('getEngine')
71+
->willReturnOnConsecutiveCalls(
72+
$this->throwException(new \RuntimeException('Engine not found')),
73+
$engineMock
74+
);
75+
76+
$calls = 0;
77+
$this->l10n->expects($this->exactly(2))
78+
->method('t')
79+
->willReturnCallback(function ($arg1, $arg2 = []) use (&$calls) {
80+
$calls++;
81+
if ($calls === 1) {
82+
$this->assertEquals('Define the certificate engine to use', $arg1);
83+
$this->assertEquals([], $arg2);
84+
return 'Define the certificate engine to use';
85+
} elseif ($calls === 2) {
86+
$this->assertEquals('Run occ libresign:configure:%s --help', $arg1);
87+
$this->assertEquals(['cfssl'], $arg2);
88+
return 'Run occ libresign:configure:cfssl --help';
89+
}
90+
});
91+
92+
$check = $this->getInstance();
93+
$result = $check->run();
94+
95+
$this->assertInstanceOf(SetupResult::class, $result);
96+
$this->assertEquals('error', $result->getSeverity());
97+
$this->assertEquals('Define the certificate engine to use', $result->getDescription());
98+
$this->assertEquals('Run occ libresign:configure:cfssl --help', $result->getLinkToDoc());
99+
}
100+
101+
public function testRunWithSuccess(): void
102+
{
103+
$engine = $this->createMock(IEngineHandler::class);
104+
$engine->method('configureCheck')
105+
->willReturn([
106+
(new ConfigureCheckHelper())->setSuccessMessage('Engine OK'),
107+
]);
108+
109+
$this->certificateEngineFactory->method('getEngine')->willReturn($engine);
110+
111+
$check = $this->getInstance();
112+
$result = $check->run();
113+
114+
$this->assertInstanceOf(SetupResult::class, $result);
115+
$this->assertEquals('success', $result->getSeverity());
116+
$this->assertEquals('Engine OK', $result->getDescription());
117+
}
118+
119+
public function testRunWithError(): void
120+
{
121+
$engine = $this->createMock(IEngineHandler::class);
122+
$engine->method('configureCheck')
123+
->willReturn([
124+
(new ConfigureCheckHelper())->setErrorMessage('Engine error'),
125+
]);
126+
127+
$this->certificateEngineFactory->method('getEngine')->willReturn($engine);
128+
129+
$check = $this->getInstance();
130+
$result = $check->run();
131+
132+
$this->assertInstanceOf(SetupResult::class, $result);
133+
$this->assertEquals('error', $result->getSeverity());
134+
$this->assertEquals('[ERROR] Engine error', $result->getDescription());
135+
}
136+
137+
public function testRunWithMixedResults(): void
138+
{
139+
$engine = $this->createMock(IEngineHandler::class);
140+
$engine->method('configureCheck')
141+
->willReturn([
142+
(new ConfigureCheckHelper())->setSuccessMessage('Success'),
143+
(new ConfigureCheckHelper())->setInfoMessage('Info message'),
144+
(new ConfigureCheckHelper())->setErrorMessage('Error'),
145+
]);
146+
147+
$this->certificateEngineFactory->method('getEngine')->willReturn($engine);
148+
149+
$check = $this->getInstance();
150+
$result = $check->run();
151+
152+
$this->assertInstanceOf(SetupResult::class, $result);
153+
$this->assertEquals('error', $result->getSeverity());
154+
$expected = "Success\nInfo message\n[ERROR] Error";
155+
$this->assertEquals($expected, $result->getDescription());
156+
}
157+
158+
public function testRunWithEmptyResults(): void
159+
{
160+
$engine = $this->createMock(IEngineHandler::class);
161+
$engine->method('configureCheck')->willReturn([]);
162+
163+
$this->certificateEngineFactory->method('getEngine')->willReturn($engine);
164+
165+
$this->l10n->method('t')
166+
->with('Certificate engine is configured correctly')
167+
->willReturn('Certificate engine is configured correctly');
168+
169+
$check = $this->getInstance();
170+
$result = $check->run();
171+
172+
$this->assertInstanceOf(SetupResult::class, $result);
173+
$this->assertEquals('success', $result->getSeverity());
174+
$this->assertEquals('Certificate engine is configured correctly', $result->getDescription());
175+
}
176+
177+
public function testRunWithOnlyWarnings(): void
178+
{
179+
$engine = $this->createMock(IEngineHandler::class);
180+
181+
$warning1 = new ConfigureCheckHelper();
182+
$warning1->setStatus('warning');
183+
$warning1->setMessage('Disk space low');
184+
185+
$warning2 = new ConfigureCheckHelper();
186+
$warning2->setStatus('warning');
187+
$warning2->setMessage('Certificate expires soon');
188+
189+
$engine->method('configureCheck')
190+
->willReturn([$warning1, $warning2]);
191+
192+
$this->certificateEngineFactory->method('getEngine')->willReturn($engine);
193+
194+
$check = $this->getInstance();
195+
$result = $check->run();
196+
197+
$this->assertInstanceOf(SetupResult::class, $result);
198+
$this->assertEquals('warning', $result->getSeverity());
199+
$expected = "[WARNING] Disk space low\n[WARNING] Certificate expires soon";
200+
$this->assertEquals($expected, $result->getDescription());
201+
}
202+
203+
public function testRunCollectsTipsAndRemovesDuplicates(): void
204+
{
205+
$engine = $this->createMock(IEngineHandler::class);
206+
207+
$helper1 = (new ConfigureCheckHelper())
208+
->setErrorMessage('First error')
209+
->setTip('Tip A');
210+
$helper2 = (new ConfigureCheckHelper())
211+
->setInfoMessage('Irrelevant info')
212+
->setTip('');
213+
$helper3 = (new ConfigureCheckHelper())
214+
->setErrorMessage('Second error')
215+
->setTip('Tip B');
216+
$helper4 = (new ConfigureCheckHelper())
217+
->setErrorMessage('First error again')
218+
->setTip('Tip A');
219+
220+
$engine->method('configureCheck')
221+
->willReturn([$helper1, $helper2, $helper3, $helper4]);
222+
223+
$this->certificateEngineFactory->method('getEngine')->willReturn($engine);
224+
225+
$check = $this->getInstance();
226+
$result = $check->run();
227+
228+
$this->assertInstanceOf(SetupResult::class, $result);
229+
$this->assertEquals('error', $result->getSeverity());
230+
231+
$expectedDesc = "[ERROR] First error\nIrrelevant info\n[ERROR] Second error\n[ERROR] First error again";
232+
$this->assertEquals($expectedDesc, $result->getDescription());
233+
$this->assertEquals("Tip A\nTip B", $result->getLinkToDoc());
234+
}
235+
}

0 commit comments

Comments
 (0)