• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

eliashaeussler / typo3-badges / 23913678716

02 Apr 2026 05:38PM UTC coverage: 95.333% (-0.8%) from 96.154%
23913678716

push

github

web-flow
Merge pull request #1225 from eliashaeussler/feature/health-check

99 of 108 new or added lines in 5 files covered. (91.67%)

572 of 600 relevant lines covered (95.33%)

6.63 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

95.08
/src/Health/ExtensionMetadataCheck.php
1
<?php
2

3
declare(strict_types=1);
4

5
/*
6
 * This file is part of the Symfony project "eliashaeussler/typo3-badges".
7
 *
8
 * Copyright (C) 2021-2026 Elias Häußler <elias@haeussler.dev>
9
 *
10
 * This program is free software: you can redistribute it and/or modify
11
 * it under the terms of the GNU General Public License as published by
12
 * the Free Software Foundation, either version 3 of the License, or
13
 * (at your option) any later version.
14
 *
15
 * This program is distributed in the hope that it will be useful,
16
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18
 * GNU General Public License for more details.
19
 *
20
 * You should have received a copy of the GNU General Public License
21
 * along with this program. If not, see <https://www.gnu.org/licenses/>.
22
 */
23

24
namespace App\Health;
25

26
use App\Entity\Dto\ExtensionMetadata;
27
use App\Service\ApiService;
28
use Exception;
29

30
use function array_key_exists;
31
use function explode;
32
use function gettype;
33
use function implode;
34
use function is_array;
35
use function is_scalar;
36
use function sprintf;
37
use function trim;
38

39
/**
40
 * ExtensionMetadataCheck.
41
 *
42
 * @author Elias Häußler <elias@haeussler.dev>
43
 * @license GPL-3.0-or-later
44
 */
45
final readonly class ExtensionMetadataCheck implements HealthCheck
46
{
47
    public function __construct(
6✔
48
        private ApiService $apiService,
49
    ) {}
6✔
50

51
    public function check(): HealthState
6✔
52
    {
53
        try {
54
            $metadata = $this->apiService->getExtensionMetadata('warming', true);
6✔
55
        } catch (Exception $exception) {
1✔
56
            return HealthState::fromException($exception);
1✔
57
        }
58

59
        return $this->assertIfValidOrReturnUnhealthyResponse(
5✔
60
            $metadata,
5✔
61
            [
5✔
62
                'composer' => ['0.meta.composer_name'],
5✔
63
                'download' => ['0.downloads'],
5✔
64
                'extension' => ['0.key', self::notEmpty(...)],
5✔
65
                'stability' => ['0.current_version.state', self::notEmpty(...)],
5✔
66
                'typo3 version' => ['0.current_version.typo3_versions', self::notEmpty(...)],
5✔
67
                'verified' => ['0.verified'],
5✔
68
                'extension version' => ['0.current_version.number', self::notEmpty(...)],
5✔
69
            ],
5✔
70
        ) ?? new HealthState(true);
5✔
71
    }
72

73
    /**
74
     * @param array<string, array{0: string, 1?: callable(mixed): bool}> $checks
75
     */
76
    private function assertIfValidOrReturnUnhealthyResponse(ExtensionMetadata $metadata, array $checks): ?HealthState
5✔
77
    {
78
        foreach ($checks as $name => $check) {
5✔
79
            $path = $check[0];
5✔
80
            $validator = $check[1] ?? static fn () => true;
5✔
81
            $pathSegments = explode('.', $path);
5✔
82
            $currentPath = [];
5✔
83
            $reference = $metadata->getMetadata();
5✔
84

85
            foreach ($pathSegments as $pathSegment) {
5✔
86
                $currentPath[] = $pathSegment;
5✔
87

88
                if (!array_key_exists($pathSegment, $reference)) {
5✔
89
                    return new HealthState(
2✔
90
                        false,
2✔
91
                        sprintf(
2✔
92
                            '%s: Expected path segment %s, but is missing in response.',
2✔
93
                            $name,
2✔
94
                            implode('.', $currentPath),
2✔
95
                        ),
2✔
96
                    );
2✔
97
                }
98

99
                $reference = $reference[$pathSegment];
4✔
100

101
                if (!is_array($reference) && $currentPath !== $pathSegments) {
4✔
102
                    return new HealthState(
1✔
103
                        false,
1✔
104
                        sprintf(
1✔
105
                            '%s: Expected array at path segment %s, but received %s.',
1✔
106
                            $name,
1✔
107
                            implode('.', $currentPath),
1✔
108
                            gettype($reference),
1✔
109
                        ),
1✔
110
                    );
1✔
111
                }
112
            }
113

114
            if (!$validator($reference)) {
2✔
115
                return new HealthState(
1✔
116
                    false,
1✔
117
                    sprintf('%s: Received value is invalid.', $name),
1✔
118
                );
1✔
119
            }
120
        }
121

122
        return null;
1✔
123
    }
124

125
    private static function notEmpty(mixed $value): bool
2✔
126
    {
127
        if (is_array($value)) {
2✔
128
            return [] !== $value;
1✔
129
        }
130

131
        if (is_scalar($value)) {
2✔
132
            return '' !== trim((string) $value);
2✔
133
        }
134

135
        /* @phpstan-ignore empty.notAllowed */
NEW
136
        return !empty($value);
×
137
    }
138

NEW
139
    public function getName(): string
×
140
    {
NEW
141
        return 'metadata';
×
142
    }
143
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc