Skip to content

Commit c4fa279

Browse files
committed
Move BlackHole to the new package
0 parents  commit c4fa279

8 files changed

Lines changed: 330 additions & 0 deletions

File tree

.gitattributes

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
# template
2+
/docs/*.py export-ignore
3+
/docs/*.txt export-ignore
4+
/docs/_* export-ignore
5+
/tests export-ignore
6+
/.git* export-ignore
7+
/*.yml export-ignore
8+
/*.yaml export-ignore
9+
/*.xml export-ignore
10+
/*.dist export-ignore
11+
12+
# project

.gitignore

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
# ide
2+
/.idea
3+
4+
# composer
5+
/vendor
6+
/composer.lock
7+
8+
# QA
9+
/reports
10+
/.phpunit.result.cache
11+
12+
# misc dev
13+
/debug

LICENSE.md

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
The MIT License (MIT)
2+
=====================
3+
4+
Copyright © 2025 Anton Smirnov
5+
6+
Permission is hereby granted, free of charge, to any person
7+
obtaining a copy of this software and associated documentation
8+
files (the “Software”), to deal in the Software without
9+
restriction, including without limitation the rights to use,
10+
copy, modify, merge, publish, distribute, sublicense, and/or sell
11+
copies of the Software, and to permit persons to whom the
12+
Software is furnished to do so, subject to the following
13+
conditions:
14+
15+
The above copyright notice and this permission notice shall be
16+
included in all copies or substantial portions of the Software.
17+
18+
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND,
19+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
20+
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
21+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
22+
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
23+
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
24+
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
25+
OTHER DEALINGS IN THE SOFTWARE.

README.md

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
# Debug Package for the Peso Framework
2+
3+
[![GitHub Actions]][GitHub Actions Link]
4+
[![Codecov]][Codecov Link]
5+
6+
[GitHub Actions]: https://img.shields.io/github/actions/workflow/status/phpeso/debug/ci.yml?style=flat-square
7+
[Codecov]: https://img.shields.io/codecov/c/gh/phpeso/debug?style=flat-square
8+
9+
[GitHub Actions Link]: https://github.com/phpeso/debug/actions
10+
[Codecov Link]: https://codecov.io/gh/phpeso/debug
11+
12+
Some shared debug stuff for the Peso Framework.
13+
14+
## Services
15+
16+
* BlackHoleService. Accepts all valid requests and returns the corresponding "not found" error

composer.json

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
{
2+
"name": "peso/debug",
3+
"type": "library",
4+
"description": "Dev tools for Peso components",
5+
"keywords": ["dev", "peso"],
6+
"license": "MIT",
7+
"config": {
8+
"allow-plugins": {
9+
"dealerdirect/phpcodesniffer-composer-installer": true
10+
},
11+
"sort-packages": true
12+
},
13+
"autoload": {
14+
"psr-4": {
15+
"Peso\\Debug\\": "src"
16+
}
17+
},
18+
"autoload-dev": {
19+
"psr-4": {
20+
"Peso\\Debug\\Tests\\": "tests"
21+
}
22+
},
23+
"require": {
24+
"php": "^8.2",
25+
"peso/core": "^1.1"
26+
},
27+
"require-dev": {
28+
"phpunit/phpunit": "^11.5"
29+
}
30+
}

phpunit.dist.xml

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
<?xml version="1.0" encoding="utf-8" ?>
2+
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
3+
displayDetailsOnAllIssues="true"
4+
failOnAllIssues="true"
5+
executionOrder="random"
6+
xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/11.5/phpunit.xsd">
7+
<testsuites>
8+
<testsuite name="all">
9+
<directory>tests</directory>
10+
</testsuite>
11+
</testsuites>
12+
<source>
13+
<include>
14+
<directory>src</directory>
15+
</include>
16+
</source>
17+
<php>
18+
<ini name="error_reporting" value="E_ALL"/>
19+
</php>
20+
</phpunit>

src/Services/BlackHoleService.php

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
<?php
2+
3+
/**
4+
* @copyright 2025 Anton Smirnov
5+
* @license MIT https://spdx.org/licenses/MIT.html
6+
*/
7+
8+
declare(strict_types=1);
9+
10+
namespace Peso\Debug\Services;
11+
12+
use Override;
13+
use Peso\Core\Exceptions\ConversionNotPerformedException;
14+
use Peso\Core\Exceptions\ExchangeRateNotFoundException;
15+
use Peso\Core\Exceptions\RequestNotSupportedException;
16+
use Peso\Core\Requests\CurrentConversionRequest;
17+
use Peso\Core\Requests\CurrentExchangeRateRequest;
18+
use Peso\Core\Requests\HistoricalConversionRequest;
19+
use Peso\Core\Requests\HistoricalExchangeRateRequest;
20+
use Peso\Core\Responses\ErrorResponse;
21+
use Peso\Core\Services\PesoServiceInterface;
22+
23+
/**
24+
* Accepts all valid requests and returns the corresponding "not found" error
25+
*/
26+
final readonly class BlackHoleService implements PesoServiceInterface
27+
{
28+
/**
29+
* @var array<class-string>
30+
*/
31+
private array $requests;
32+
33+
/**
34+
* @param class-string ...$requests
35+
*/
36+
public function __construct(string ...$requests)
37+
{
38+
$this->requests = $requests;
39+
}
40+
41+
#[Override]
42+
public function send(object $request): ErrorResponse
43+
{
44+
if ($this->requests !== [] && !\in_array($request::class, $this->requests)) {
45+
goto notSupported;
46+
}
47+
48+
if ($request instanceof CurrentExchangeRateRequest || $request instanceof HistoricalExchangeRateRequest) {
49+
return new ErrorResponse(ExchangeRateNotFoundException::fromRequest($request));
50+
}
51+
52+
if ($request instanceof CurrentConversionRequest || $request instanceof HistoricalConversionRequest) {
53+
return new ErrorResponse(ConversionNotPerformedException::fromRequest($request));
54+
}
55+
56+
notSupported:
57+
return new ErrorResponse(RequestNotSupportedException::fromRequest($request));
58+
}
59+
60+
#[Override]
61+
public function supports(object $request): bool
62+
{
63+
if ($this->requests !== [] && !\in_array($request::class, $this->requests)) {
64+
return false;
65+
}
66+
67+
// only standard requests
68+
return
69+
$request instanceof CurrentExchangeRateRequest ||
70+
$request instanceof HistoricalExchangeRateRequest ||
71+
$request instanceof CurrentConversionRequest ||
72+
$request instanceof HistoricalConversionRequest;
73+
}
74+
}
Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
<?php
2+
3+
/**
4+
* @copyright 2025 Anton Smirnov
5+
* @license MIT https://spdx.org/licenses/MIT.html
6+
*/
7+
8+
declare(strict_types=1);
9+
10+
namespace Peso\Debug\Tests\Services;
11+
12+
use Arokettu\Date\Date;
13+
use Peso\Core\Exceptions\ConversionNotPerformedException;
14+
use Peso\Core\Exceptions\ExchangeRateNotFoundException;
15+
use Peso\Core\Exceptions\RequestNotSupportedException;
16+
use Peso\Core\Requests\CurrentConversionRequest;
17+
use Peso\Core\Requests\CurrentExchangeRateRequest;
18+
use Peso\Core\Requests\HistoricalConversionRequest;
19+
use Peso\Core\Requests\HistoricalExchangeRateRequest;
20+
use Peso\Core\Responses\ErrorResponse;
21+
use Peso\Core\Types\Decimal;
22+
use Peso\Debug\Services\BlackHoleService;
23+
use PHPUnit\Framework\TestCase;
24+
use stdClass;
25+
26+
final class BlackHoleServiceTest extends TestCase
27+
{
28+
public function testSupportsAllSupported(): void
29+
{
30+
$service = new BlackHoleService();
31+
32+
self::assertTrue($service->supports(new CurrentExchangeRateRequest('PHP', 'USD')));
33+
self::assertTrue($service->supports(new HistoricalExchangeRateRequest('PHP', 'USD', Date::today())));
34+
self::assertTrue($service->supports(new CurrentConversionRequest(Decimal::init(1), 'PHP', 'USD')));
35+
self::assertTrue($service->supports(
36+
new HistoricalConversionRequest(Decimal::init(1), 'PHP', 'USD', Date::today()),
37+
));
38+
39+
self::assertFalse($service->supports(new stdClass()));
40+
}
41+
42+
public function testSupportsLimited(): void
43+
{
44+
$service = new BlackHoleService(
45+
CurrentExchangeRateRequest::class,
46+
HistoricalConversionRequest::class,
47+
stdClass::class, // still not supported
48+
);
49+
50+
self::assertTrue($service->supports(new CurrentExchangeRateRequest('PHP', 'USD')));
51+
self::assertFalse($service->supports(new HistoricalExchangeRateRequest('PHP', 'USD', Date::today())));
52+
self::assertFalse($service->supports(new CurrentConversionRequest(Decimal::init(1), 'PHP', 'USD')));
53+
self::assertTrue($service->supports(
54+
new HistoricalConversionRequest(Decimal::init(1), 'PHP', 'USD', Date::today()),
55+
));
56+
57+
self::assertFalse($service->supports(new stdClass()));
58+
}
59+
60+
public function testReturnsAppropriateErrors(): void
61+
{
62+
$service = new BlackHoleService();
63+
$today = Date::today();
64+
65+
$response = $service->send(new CurrentExchangeRateRequest('PHP', 'USD'));
66+
self::assertInstanceOf(ErrorResponse::class, $response);
67+
self::assertInstanceOf(ExchangeRateNotFoundException::class, $response->exception);
68+
self::assertEquals('Unable to find exchange rate for PHP/USD', $response->exception->getMessage());
69+
70+
$response = $service->send(new HistoricalExchangeRateRequest('PHP', 'USD', $today));
71+
self::assertInstanceOf(ErrorResponse::class, $response);
72+
self::assertInstanceOf(ExchangeRateNotFoundException::class, $response->exception);
73+
self::assertEquals(
74+
'Unable to find exchange rate for PHP/USD on ' . $today->toString(),
75+
$response->exception->getMessage(),
76+
);
77+
78+
$response = $service->send(new CurrentConversionRequest(Decimal::init(1), 'PHP', 'USD'));
79+
self::assertInstanceOf(ErrorResponse::class, $response);
80+
self::assertInstanceOf(ConversionNotPerformedException::class, $response->exception);
81+
self::assertEquals('Unable to convert 1 PHP to USD', $response->exception->getMessage());
82+
83+
$response = $service->send(new HistoricalConversionRequest(Decimal::init(1), 'PHP', 'USD', $today));
84+
self::assertInstanceOf(ErrorResponse::class, $response);
85+
self::assertInstanceOf(ConversionNotPerformedException::class, $response->exception);
86+
self::assertEquals(
87+
'Unable to convert 1 PHP to USD on ' . $today->toString(),
88+
$response->exception->getMessage(),
89+
);
90+
91+
$response = $service->send(new stdClass());
92+
self::assertInstanceOf(ErrorResponse::class, $response);
93+
self::assertInstanceOf(RequestNotSupportedException::class, $response->exception);
94+
self::assertEquals('Unsupported request type: "stdClass"', $response->exception->getMessage());
95+
}
96+
97+
public function testReturnsAppropriateErrorsWhenLimited(): void
98+
{
99+
$service = new BlackHoleService(
100+
CurrentExchangeRateRequest::class,
101+
HistoricalConversionRequest::class,
102+
stdClass::class, // still not supported
103+
);
104+
$today = Date::today();
105+
106+
$response = $service->send(new CurrentExchangeRateRequest('PHP', 'USD'));
107+
self::assertInstanceOf(ErrorResponse::class, $response);
108+
self::assertInstanceOf(ExchangeRateNotFoundException::class, $response->exception);
109+
self::assertEquals('Unable to find exchange rate for PHP/USD', $response->exception->getMessage());
110+
111+
$response = $service->send(new HistoricalExchangeRateRequest('PHP', 'USD', $today));
112+
self::assertInstanceOf(ErrorResponse::class, $response);
113+
self::assertInstanceOf(RequestNotSupportedException::class, $response->exception);
114+
self::assertEquals(\sprintf(
115+
'Unsupported request type: "%s"',
116+
HistoricalExchangeRateRequest::class,
117+
), $response->exception->getMessage());
118+
119+
$response = $service->send(new CurrentConversionRequest(Decimal::init(1), 'PHP', 'USD'));
120+
self::assertInstanceOf(ErrorResponse::class, $response);
121+
self::assertInstanceOf(RequestNotSupportedException::class, $response->exception);
122+
self::assertEquals(\sprintf(
123+
'Unsupported request type: "%s"',
124+
CurrentConversionRequest::class,
125+
), $response->exception->getMessage());
126+
127+
$response = $service->send(new HistoricalConversionRequest(Decimal::init(1), 'PHP', 'USD', $today));
128+
self::assertInstanceOf(ErrorResponse::class, $response);
129+
self::assertInstanceOf(ConversionNotPerformedException::class, $response->exception);
130+
self::assertEquals(
131+
'Unable to convert 1 PHP to USD on ' . $today->toString(),
132+
$response->exception->getMessage(),
133+
);
134+
135+
$response = $service->send(new stdClass());
136+
self::assertInstanceOf(ErrorResponse::class, $response);
137+
self::assertInstanceOf(RequestNotSupportedException::class, $response->exception);
138+
self::assertEquals('Unsupported request type: "stdClass"', $response->exception->getMessage());
139+
}
140+
}

0 commit comments

Comments
 (0)