Skip to content

Commit

Permalink
Improve tests
Browse files Browse the repository at this point in the history
  • Loading branch information
charjr committed Sep 22, 2023
1 parent 9bff2f8 commit bb843ea
Show file tree
Hide file tree
Showing 8 changed files with 339 additions and 38 deletions.
17 changes: 8 additions & 9 deletions src/Exception/CannotRouteRequest.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,21 +6,20 @@

/* This exception occurs when the request does not match any routes in your route collection. */

class CannotRouteRequest extends \RuntimeException
use RuntimeException;

class CannotRouteRequest extends RuntimeException
{
public const NOT_FOUND = 404;
public const METHOD_NOT_ALLOWED = 405;

public static function fromErrorCode(int $errorCode): self
{
switch ($errorCode) {
case self::NOT_FOUND:
return self::notFound();
case self::METHOD_NOT_ALLOWED:
return self::methodNotAllowed();
default:
return new self();
}
assert($errorCode === 404 || $errorCode === 405);
return match ($errorCode) {
self::NOT_FOUND => self::notFound(),
self::METHOD_NOT_ALLOWED => self::methodNotAllowed(),
};
}

public static function notFound(): self
Expand Down
4 changes: 3 additions & 1 deletion src/Route/Path.php
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@ public function isEmpty(): bool
/** @return array<string, string> */
public function jsonSerialize(): array
{
return [...$this->operations];
$operations = $this->operations;
ksort($operations);
return $operations;
}
}
8 changes: 4 additions & 4 deletions src/Route/Server.php
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ public function howManyDynamicComponents(): int

public function isEmpty(): bool
{
return count(array_filter($this->paths, fn($p) => !$p->isEmpty())) === 0;
return count($this->paths) === 0;
}

public function isHosted(): bool
Expand All @@ -57,14 +57,14 @@ public function isHosted(): bool
*/
public function jsonSerialize(): array
{
$filteredPaths = array_filter($this->paths, fn($p) => !$p->isEmpty());
$paths = $this->paths;
usort(
$filteredPaths,
$paths,
fn(Path $a, Path $b) => $a->howManyDynamicComponents() <=> $b->howManyDynamicComponents()
);

$staticPaths = $dynamicPaths = $regex = [];
foreach ($filteredPaths as $path) {
foreach ($paths as $path) {
if ($path->isDynamic()) {
$dynamicPaths[$path->url] = $path->jsonSerialize();
$regex[] = sprintf('%s(*MARK:%s)', $path->regex, $path->url);
Expand Down
20 changes: 6 additions & 14 deletions src/RouteCollector.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,18 +13,6 @@
class RouteCollector
{
public function collect(OpenApi $openApi): RouteCollection
{
$collection = $this->collectRoutes($openApi);

if ($collection === []) {
throw CannotCollectRoutes::noRoutes();
}

return RouteCollection::fromServers(...$collection);
}

/** @return array<string, Server> */
private function collectRoutes(OpenApi $openApi): array
{
$collection = [];

Expand All @@ -40,7 +28,11 @@ private function collectRoutes(OpenApi $openApi): array
}
}

return array_filter($collection, fn($s) => !$s->isEmpty());
if ($collection === []) {
throw CannotCollectRoutes::noRoutes();
}

return RouteCollection::fromServers(...$collection);
}

/** @return array<string, string> */
Expand All @@ -54,6 +46,6 @@ private function getServers(OpenApi $openAPI, PathItem $path, Operation $operati
$servers = $openAPI->servers;
}

return array_unique(array_map(fn($p) => rtrim($p->url, '/'), $servers));
return array_map(fn($p) => rtrim($p->url, '/'), $servers);
}
}
62 changes: 53 additions & 9 deletions tests/Route/PathTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

namespace Membrane\OpenAPIRouter\Tests\Route;

use Generator;
use Membrane\OpenAPIRouter\Route\Path;
use PHPUnit\Framework\Attributes\CoversClass;
use PHPUnit\Framework\Attributes\DataProvider;
Expand All @@ -18,7 +19,7 @@ public static function provideUrlsToBaseRegexOn(): array
return [
'static path' => ['/static/path', '/static/path'],
'partially dynamic path' => ['/([^/]+)/path', '/{dynamic}/path'],
'dynamic path' => ['/([^/]+)/([^/]+)', '/{dynamic}/{path}}']
'dynamic path' => ['/([^/]+)/([^/]+)', '/{dynamic}/{path}']
];
}

Expand All @@ -34,35 +35,31 @@ public static function provideUrlsToCheckIfDynamic(): array
return [
'static path' => [false, '/static/path'],
'partially dynamic path' => [true, '/{dynamic}/path'],
'dynamic path' => [true, '/{dynamic}/{path}}']
'dynamic path' => [true, '/{dynamic}/{path}']
];
}

#[Test]
#[DataProvider('provideUrlsToCheckIfDynamic')]
public function itCanTellIfItIsDynamic(bool $expected, string $url): void
{
$sut = new Path($url);

self::assertSame($expected, $sut->isDynamic());
self::assertSame($expected, (new Path($url))->isDynamic());
}

public static function provideUrlsToCountDynamicComponents(): array
{
return [
'static path' => [0, '/static/path'],
'partially dynamic path' => [1, '/{dynamic}/path'],
'dynamic path' => [2, '/{dynamic}/{path}}']
'dynamic path' => [2, '/{dynamic}/{path}']
];
}

#[Test]
#[DataProvider('provideUrlsToCountDynamicComponents')]
public function itCanCountDynamicComponents(int $expected, string $url): void
{
$sut = new Path($url);

self::assertSame($expected, $sut->howManyDynamicComponents());
self::assertSame($expected, (new Path($url))->howManyDynamicComponents());
}

#[Test]
Expand All @@ -76,4 +73,51 @@ public function itCanAddRoutes(): void

self::assertFalse($sut->isEmpty());
}

public static function providePathsToJsonSerialize(): Generator
{
$expected = [
'delete' => 'delete-operation',
'get' => 'get-operation',
'post' => 'post-operation'
];

$operations = [
['get', 'get-operation'],
['post', 'post-operation'],
['delete', 'delete-operation'],
];

yield [
$expected,
(function() use ($operations) {
$sut = new Path('/path');
foreach ($operations as $operation) {
$sut->addRoute(...$operation);
}
return $sut;
})(),
];
yield [
$expected,
(function() use ($operations) {
$sut = new Path('/path');
foreach (array_reverse($operations) as $operation) {
$sut->addRoute(...$operation);
}
return $sut;
})(),
];
}

#[Test]
public function itIsJsonSerializable(): void
{
$sut = new Path('/path');

$sut->addRoute('get', 'get-operation-id');
$sut->addRoute('post', 'post-operation-id');

self::assertSame(['get' => 'get-operation-id', 'post' => 'post-operation-id'], $sut->jsonSerialize());
}
}
116 changes: 115 additions & 1 deletion tests/Route/ServerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,17 @@

namespace Membrane\OpenAPIRouter\Tests\Route;

use Generator;
use Membrane\OpenAPIRouter\Route\Path;
use Membrane\OpenAPIRouter\Route\Server;
use PHPUnit\Framework\Attributes\CoversClass;
use PHPUnit\Framework\Attributes\DataProvider;
use PHPUnit\Framework\Attributes\Test;
use PHPUnit\Framework\Attributes\UsesClass;
use PHPUnit\Framework\TestCase;

#[CoversClass(Server::class)]
#[UsesClass(Path::class)]
class ServerTest extends TestCase
{
public static function provideUrls(): array
Expand All @@ -23,8 +27,118 @@ public static function provideUrls(): array

#[Test]
#[DataProvider('provideUrls')]
public function itCreatesARegexBasedOnTheUrl(string $expectedRegex, string $url): void
public function itHasARegexBasedOnTheUrl(string $expectedRegex, string $url): void
{
self::assertSame($expectedRegex, (new Server($url))->regex);
}

public static function provideUrlsToCheckIfDynamic(): array
{
return [
'static path' => [false, 'http://static/server'],
'partially dynamic path' => [true, 'http://{dynamic}/server'],
'dynamic path' => [true, 'http://{dynamic}/{server}']
];
}

#[Test]
#[DataProvider('provideUrlsToCheckIfDynamic')]
public function itCanTellIfItIsDynamic(bool $expected, string $url): void
{
self::assertSame($expected, (new Server($url))->isDynamic());
}

public static function provideUrlsToCountDynamicComponents(): array
{
return [
'static server' => [0, 'http://static/server'],
'partially dynamic path' => [1, 'http://{dynamic}/server'],
'dynamic path' => [2, 'http://{dynamic}/{server}']
];
}

#[Test]
#[DataProvider('provideUrlsToCountDynamicComponents')]
public function itCanCountDynamicComponents(int $expected, string $url): void
{
self::assertSame($expected, (new Server($url))->howManyDynamicComponents());
}

#[Test]
public function itCanAddRoutes(): void
{
$sut = new Server('http://server.com');

self::assertTrue($sut->isEmpty());

$sut->addRoute('/path', 'get', 'get-operation-id');

self::assertFalse($sut->isEmpty());
}

public static function provideUrlsToCheckIfHosted(): array
{
return [
'hostless' => [false, '/hostless/v1'],
'hosted' => [true, 'http://www.hosted.io']
];
}

#[Test]
#[DataProvider('provideUrlsToCheckIfHosted')]
public function itCanTellIfItIsHosted(bool $expected, string $url): void
{
self::assertSame($expected, (new Server($url))->isHosted());
}

public static function provideServersToJsonSerialize(): Generator
{
$expected = [
'static' => ['/path' => ['get' => 'get-path', 'post' => 'post-path']],
'dynamic' => [
'regex' => '#^(?|/([^/]+)/path(*MARK:/{another}/path)|/([^/]+)/([^/]+)/path(*MARK:/{yet}/{another}/path))$#',
'paths' => [
'/{another}/path' => ['get' => 'get-another-path'],
'/{yet}/{another}/path' => ['delete' => 'delete-yet-another-path'],
],
],
];

$paths = [
['/path', 'get', 'get-path'],
['/path', 'post', 'post-path'],
['/{another}/path', 'get', 'get-another-path'],
['/{yet}/{another}/path', 'delete', 'delete-yet-another-path'],
];



yield [
$expected,
(function () use ($paths) {
$server = new Server('www.server.net');
foreach ($paths as $path) {
$server->addRoute(...$path);
}
return $server;
})()
];
yield [
$expected,
(function () use ($paths) {
$server = new Server('www.servver.net');
foreach (array_reverse($paths) as $path) {
$server->addRoute(...$path);
}
return $server;
})()
];
}

#[Test]
#[DataProvider('provideServersToJsonSerialize')]
public function itIsJsonSerializable(array $expected, Server $sut): void
{
self::assertSame($expected, $sut->jsonSerialize());
}
}
Loading

0 comments on commit bb843ea

Please sign in to comment.