From dedda1b753029fd343760f2d60982f0cbdc127b7 Mon Sep 17 00:00:00 2001 From: Benjamin Gaussorgues Date: Mon, 26 Jan 2026 10:46:22 +0100 Subject: [PATCH 1/3] fix(openmetrics): remove superfluous empty lines Signed-off-by: Benjamin Gaussorgues --- core/Controller/OpenMetricsController.php | 2 -- tests/Core/Controller/OpenMetricsControllerTest.php | 1 - 2 files changed, 3 deletions(-) diff --git a/core/Controller/OpenMetricsController.php b/core/Controller/OpenMetricsController.php index 58f5288531fbe..7ed388e7eac13 100644 --- a/core/Controller/OpenMetricsController.php +++ b/core/Controller/OpenMetricsController.php @@ -87,7 +87,6 @@ private function generate(): \Generator { # UNIT nextcloud_exporter_duration seconds # HELP nextcloud_exporter_duration Exporter run time nextcloud_exporter_duration $elapsed - # EOF SUMMARY; @@ -112,7 +111,6 @@ private function formatFamily(IMetricFamily $family): string { } $output .= "\n"; } - $output .= "\n"; return $output; } diff --git a/tests/Core/Controller/OpenMetricsControllerTest.php b/tests/Core/Controller/OpenMetricsControllerTest.php index dbbb2a80c1281..b331bef5711b3 100644 --- a/tests/Core/Controller/OpenMetricsControllerTest.php +++ b/tests/Core/Controller/OpenMetricsControllerTest.php @@ -58,7 +58,6 @@ public function testGetMetrics(): void { # UNIT nextcloud_exporter_duration seconds # HELP nextcloud_exporter_duration Exporter run time nextcloud_exporter_duration %f - # EOF EXPECTED; From 66cf18950fa6cfa2c241ec21d90b2922a150b163 Mon Sep 17 00:00:00 2001 From: Benjamin Gaussorgues Date: Mon, 26 Jan 2026 10:47:24 +0100 Subject: [PATCH 2/3] fix(openmetrics): ensure unit is a suffix of metric name Signed-off-by: Benjamin Gaussorgues --- core/Controller/OpenMetricsController.php | 8 +++---- .../OpenMetrics/Exporters/AppsCount.php | 4 ++-- .../OpenMetrics/Exporters/RunningJobs.php | 2 +- .../Controller/OpenMetricsControllerTest.php | 8 +++---- .../Exporters/ExporterTestCase.php | 21 +++++++++++++++++-- 5 files changed, 30 insertions(+), 13 deletions(-) diff --git a/core/Controller/OpenMetricsController.php b/core/Controller/OpenMetricsController.php index 7ed388e7eac13..8c57eb1a66335 100644 --- a/core/Controller/OpenMetricsController.php +++ b/core/Controller/OpenMetricsController.php @@ -83,10 +83,10 @@ private function generate(): \Generator { $elapsed = (string)(microtime(true) - $_SERVER['REQUEST_TIME_FLOAT']); yield <<assertEquals('200', $response->getStatus()); $this->assertEquals('application/openmetrics-text; version=1.0.0; charset=utf-8', $response->getHeaders()['Content-Type']); $expected = <<assertNotEmpty($this->exporter->name()); $this->assertNotEmpty($this->metrics); } - public function testValidNames(): void { + public function testValidExporterName(): void { + $exporterName = $this->exporter->name(); + $this->assertMatchesRegularExpression('/^[a-z_:][a-z0-9_:]*$/i', $exporterName, ); + + $unit = $this->exporter->unit(); + if ($unit === '') { + return; + } + // Unit name must follow metric name format + $this->assertMatchesRegularExpression('/^[a-z_:][a-z0-9_:]*$/i', $unit); + // Unit name must be a suffix in exporter name + $this->assertMatchesRegularExpression( + '/(^|_)' . $unit . '$/', + $exporterName, + 'Metric name "' . $exporterName . '" must contains unit "' . $unit . '" as a suffix', + ); + } + + public function testValidLabelKey(): void { $labelNames = []; foreach ($this->metrics as $metric) { foreach ($metric->labels as $label => $value) { From 47c6a59e81c3253d704c943f1b6ec1def04238d7 Mon Sep 17 00:00:00 2001 From: Benjamin Gaussorgues Date: Mon, 26 Jan 2026 11:44:15 +0100 Subject: [PATCH 3/3] chore(openmetrics): add more complete test for controller Signed-off-by: Benjamin Gaussorgues --- .../Controller/OpenMetricsControllerTest.php | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/tests/Core/Controller/OpenMetricsControllerTest.php b/tests/Core/Controller/OpenMetricsControllerTest.php index a5b740d9a093a..56e40cdf7d773 100644 --- a/tests/Core/Controller/OpenMetricsControllerTest.php +++ b/tests/Core/Controller/OpenMetricsControllerTest.php @@ -9,6 +9,7 @@ namespace Tests\Core\Controller; +use Generator; use OC\Core\Controller\OpenMetricsController; use OC\OpenMetrics\ExporterManager; use OCP\AppFramework\Http\IOutput; @@ -16,6 +17,9 @@ use OCP\AppFramework\Http\StreamTraversableResponse; use OCP\IConfig; use OCP\IRequest; +use OCP\OpenMetrics\IMetricFamily; +use OCP\OpenMetrics\Metric; +use OCP\OpenMetrics\MetricType; use PHPUnit\Framework\MockObject\MockObject; use Psr\Log\LoggerInterface; use Test\TestCase; @@ -34,10 +38,23 @@ protected function setUp(): void { ->willReturn('192.168.1.1'); $this->config = $this->createMock(IConfig::class); $this->exporterManager = $this->createMock(ExporterManager::class); + $this->exporterManager->method('export')->willReturnCallback([$this, 'getFakeMetrics']); $this->logger = $this->createMock(LoggerInterface::class); $this->controller = new OpenMetricsController('core', $this->request, $this->config, $this->exporterManager, $this->logger); } + public function getFakeMetrics(): Generator { + $metric = $this->createMock(IMetricFamily::class); + $metric->method('type')->willReturn(MetricType::gauge); + $metric->method('unit')->willReturn('fake'); + $metric->method('name')->willReturn('fake_count'); + $metric->method('help')->willReturn('A fake count used for tests'); + $metric->method('metrics')->willReturnCallback(function () { + yield new Metric(42, ['type' => 'used']); + yield new Metric(24, ['type' => 'unused']); + }); + yield $metric; + } public function testGetMetrics(): void { $output = $this->createMock(IOutput::class); $fullOutput = ''; @@ -54,6 +71,11 @@ public function testGetMetrics(): void { $this->assertEquals('200', $response->getStatus()); $this->assertEquals('application/openmetrics-text; version=1.0.0; charset=utf-8', $response->getHeaders()['Content-Type']); $expected = <<