Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 1 addition & 5 deletions src/Values/Email.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,7 @@ public function __construct(string $value)
}

$parts = Strings::split($value, '~@~');
$domain = array_pop($parts);

if ($domain === null) {
throw new InvalidEmailAddressException($value);
}
$domain = (string) array_pop($parts);

// Try normalize the domain part
if (function_exists('idn_to_ascii')) {
Expand Down
70 changes: 70 additions & 0 deletions tests/Cases/Annotations.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,13 @@

use Contributte\Tester\Toolkit;
use Contributte\Utils\Annotations;
use Contributte\Utils\Exception\LogicalException;
use Tester\Assert;
use Tests\Fixtures\AnnotationCoverageFixture;
use Tests\Fixtures\AnnotationFoo;

require_once __DIR__ . '/../bootstrap.php';
require_once __DIR__ . '/../Fixtures/AnnotationCoverageFixture.php';

// Class
Toolkit::test(function (): void {
Expand Down Expand Up @@ -40,3 +43,70 @@ Toolkit::test(function (): void {
Assert::equal(true, Annotations::hasAnnotation($r, 'test'));
Assert::equal(false, Annotations::hasAnnotation($r, 'test2'));
});

// Coverage branch: class description and scalar parsing
Toolkit::test(function (): void {
$r = new ReflectionClass(AnnotationCoverageFixture::class);
$annotations = Annotations::getAnnotations($r);

Assert::same('Coverage fixture description', $annotations['description'][0]);
Assert::same(123, $annotations['number'][0]);
Assert::same([
'name' => 'demo',
'count' => 10,
'enabled' => true,
], iterator_to_array($annotations['options'][0]));
});

// Coverage branch: ReflectionFunction
Toolkit::test(function (): void {
$r = new ReflectionFunction('Tests\\Fixtures\\annotationCoverageFunction');

set_error_handler(static fn (int $severity, string $message): bool => $severity === E_DEPRECATED
&& str_contains($message, 'Using null as an array offset is deprecated'));

try {
Assert::same('Function fixture description', Annotations::getAnnotation($r, 'description'));
Assert::same([
'name' => 'function',
], iterator_to_array(Annotations::getAnnotation($r, 'options')));
} finally {
restore_error_handler();
}
});

// Coverage branch: ReflectionProperty
Toolkit::test(function (): void {
$r = new ReflectionProperty(AnnotationCoverageFixture::class, 'property');

Assert::same([
'name' => 'property',
], iterator_to_array(Annotations::getAnnotation($r, 'options')));
});

// Coverage branch: empty annotations
Toolkit::test(function (): void {
$r = new ReflectionMethod(AnnotationCoverageFixture::class, 'withoutDoc');

Assert::null(Annotations::getAnnotation($r, 'anything'));
});

// Coverage branch: parseComment split failure
Toolkit::test(function (): void {
$parseComment = new ReflectionMethod(Annotations::class, 'parseComment');

$backtrackLimit = ini_get('pcre.backtrack_limit');
ini_set('pcre.backtrack_limit', '1');

try {
Assert::exception(
static fn () => $parseComment->invoke(null, str_repeat('@annotation ', 1000)),
LogicalException::class,
'Cannot split comment'
);
} finally {
if ($backtrackLimit !== false) {
ini_set('pcre.backtrack_limit', $backtrackLimit);
}
}
});
3 changes: 3 additions & 0 deletions tests/Cases/Caster.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -57,9 +57,12 @@ Toolkit::test(function (): void {

Toolkit::test(function (): void {
Assert::same(null, Caster::boolOrNull(null));
Assert::same(true, Caster::boolOrNull(true));
Assert::same(false, Caster::boolOrNull(false));
Assert::same(true, Caster::boolOrNull('true'));
Assert::same(false, Caster::boolOrNull('false'));
Assert::same(null, Caster::boolOrNull('foo'));
Assert::same(null, Caster::boolOrNull(new stdClass()));
Assert::same(false, Caster::ensureBool(null));
Assert::same(true, Caster::ensureBool('yes'));
Assert::same(true, Caster::forceBool(true));
Expand Down
54 changes: 54 additions & 0 deletions tests/Cases/Csv.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,25 @@ use Tester\Assert;

require_once __DIR__ . '/../bootstrap.php';

Toolkit::test(function (): void {
Assert::same('', Csv::toCsv([]));

set_error_handler(static fn (int $severity, string $message): bool => $severity === E_DEPRECATED
&& str_contains($message, 'fputcsv(): the $escape parameter must be provided'));

try {
$csv = Csv::toCsv([
['name', 'city'],
['Milan', 'Hradec Kralove'],
]);
} finally {
restore_error_handler();
}

Assert::contains('name,city', $csv);
Assert::contains('Milan,"Hradec Kralove"', $csv);
});

// Simple array matching
Toolkit::test(function (): void {
Assert::equal([
Expand Down Expand Up @@ -132,3 +151,38 @@ Toolkit::test(function (): void {
], __DIR__ . '/../Fixtures/sample.csv');
}, InvalidStateException::class);
});

Toolkit::test(function (): void {
$emptyFile = tempnam(sys_get_temp_dir(), 'csv-empty');

if ($emptyFile === false) {
Assert::fail('Unable to create temporary file');
}

try {
Assert::same([], Csv::structural([0 => 'name'], $emptyFile));
} finally {
@unlink($emptyFile);
}
});

Toolkit::test(function (): void {
$liner = ['existing' => 'value'];

$proxy = new class extends Csv {

/**
* @param array<int|string, mixed> $liner
* @param string[] $keys
*/
public static function invokeMatchValue(mixed $value, array &$liner, array $keys): void
{
self::matchValue($value, $liner, $keys);
}

};

$proxy::invokeMatchValue('new', $liner, []);

Assert::same(['existing' => 'value'], $liner);
});
65 changes: 65 additions & 0 deletions tests/Cases/FileSystem.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
<?php declare(strict_types = 1);

use Contributte\Tester\Toolkit;
use Contributte\Utils\FileSystem as UtilsFileSystem;
use Nette\Utils\FileSystem as NetteFileSystem;
use Tester\Assert;

require_once __DIR__ . '/../bootstrap.php';

$tempDir = __DIR__ . '/../temp/filesystem-test';
NetteFileSystem::createDir($tempDir);

Toolkit::test(function (): void {
Assert::same(
str_replace(['/', '\\'], DIRECTORY_SEPARATOR, '/foo\\bar/baz'),
UtilsFileSystem::pathalize('/foo\\bar/baz')
);
});

Toolkit::test(function (): void {
Assert::same('.gz', UtilsFileSystem::extension('archive.tar.gz'));
Assert::same('', UtilsFileSystem::extension('README'));
});

Toolkit::test(function () use ($tempDir): void {
$purgedDir = $tempDir . '/purged';

UtilsFileSystem::purge($purgedDir);
Assert::true(is_dir($purgedDir));

NetteFileSystem::write($purgedDir . '/file.txt', 'content');
NetteFileSystem::createDir($purgedDir . '/nested');
NetteFileSystem::write($purgedDir . '/nested/child.txt', 'child');

UtilsFileSystem::purge($purgedDir);

$entries = scandir($purgedDir);
Assert::same([], array_values(array_diff($entries !== false ? $entries : [], ['.', '..'])));
});

Toolkit::test(function () use ($tempDir): void {
$ioDir = $tempDir . '/io';
UtilsFileSystem::createDir($ioDir);

$sourceFile = $ioDir . '/source.txt';
UtilsFileSystem::write($sourceFile, 'Hello');
Assert::same('Hello', UtilsFileSystem::read($sourceFile));

$copiedFile = $ioDir . '/copy.txt';
UtilsFileSystem::copy($sourceFile, $copiedFile);
Assert::same('Hello', UtilsFileSystem::read($copiedFile));

$renamedFile = $ioDir . '/renamed.txt';
UtilsFileSystem::rename($copiedFile, $renamedFile);
Assert::false(file_exists($copiedFile));
Assert::true(file_exists($renamedFile));

UtilsFileSystem::delete($renamedFile);
Assert::false(file_exists($renamedFile));

Assert::true(UtilsFileSystem::isAbsolute($sourceFile));
Assert::false(UtilsFileSystem::isAbsolute('relative/path.txt'));
});

NetteFileSystem::delete($tempDir);
19 changes: 19 additions & 0 deletions tests/Cases/Http.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -28,3 +28,22 @@ Toolkit::test(function (): void {
<meta content=\' foo\' name = bar7.bar >
'));
});

Toolkit::test(function (): void {
Assert::exception(function (): void {
Http::metadata('plain text without html meta tags');
}, LogicException::class, 'Matches count is not equal.');
});

Toolkit::test(function (): void {
$backtrackLimit = ini_get('pcre.backtrack_limit');
ini_set('pcre.backtrack_limit', '1');

try {
Assert::same([], Http::metadata(str_repeat('<meta name="foo" content="bar">', 2000)));
} finally {
if ($backtrackLimit !== false) {
ini_set('pcre.backtrack_limit', $backtrackLimit);
}
}
});
14 changes: 14 additions & 0 deletions tests/Cases/Http/FileResponse.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -68,4 +68,18 @@ Toolkit::test(function () use ($tempDir): void {
Assert::false($responseInline->isForceDownload());
});

// FileResponse::send
Toolkit::test(function () use ($tempDir): void {
$filePath = $tempDir . '/send.txt';
FileSystem::write($filePath, 'send-content');

$response = new FileResponse($filePath, 'send.txt', 'text/plain', false);

ob_start();
$response->send();
$output = ob_get_clean();

Assert::same('send-content', $output);
});

FileSystem::delete($tempDir);
37 changes: 37 additions & 0 deletions tests/Cases/ServerTiming.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
<?php declare(strict_types = 1);

use Contributte\Tester\Toolkit;
use Contributte\Utils\Exception\LogicalException;
use Contributte\Utils\ServerTiming;
use Tester\Assert;

require_once __DIR__ . '/../bootstrap.php';

Toolkit::test(function (): void {
$serverTiming = new ServerTiming();

Assert::exception(function () use ($serverTiming): void {
$serverTiming->end('missing');
}, LogicalException::class, 'Timer "missing" is not running');
});

Toolkit::test(function (): void {
$serverTiming = new ServerTiming();

$serverTiming->start('db', 'Database "query"');
usleep(1000);
$serverTiming->end('db');

$serverTiming->start('cache');
$serverTiming->end('cache');

$formatted = $serverTiming->format();

Assert::contains('db;dur=', $formatted);
Assert::contains('cache;dur=', $formatted);
Assert::contains(';desc="Database \\"query\\""', $formatted);
});

Toolkit::test(function (): void {
Assert::same('', (new ServerTiming())->format());
});
2 changes: 2 additions & 0 deletions tests/Cases/Validators.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ Toolkit::test(function (): void {
Assert::equal(true, Validators::isRc('9353218105'));
Assert::equal(true, Validators::isRc('1210050094'));
Assert::equal(true, Validators::isRc('0712050735'));
Assert::equal(true, Validators::isRc('0471010001'));
Assert::equal(true, Validators::isRc('0421010007'));

Assert::equal(false, Validators::isRc('9353218115'));
Assert::equal(false, Validators::isRc('9357218115'));
Expand Down
12 changes: 12 additions & 0 deletions tests/Cases/Values/Email.phpt
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
<?php declare(strict_types = 1);

use Contributte\Tester\Toolkit;
use Contributte\Utils\Exception\Runtime\InvalidEmailAddressException;
use Contributte\Utils\Values\Email;
use Tester\Assert;

Expand All @@ -15,3 +16,14 @@ Toolkit::test(function (): void {
$email = new Email('foo@bar.baz');
Assert::true($email->equal(new Email('foo@bar.baz')));
});

Toolkit::test(function (): void {
Assert::exception(function (): void {
new Email('not-an-email');
}, InvalidEmailAddressException::class, 'Invalid email address "not-an-email"');
});

Toolkit::test(function (): void {
$email = new Email('foo@bar.baz');
Assert::same('foo@bar.baz', (string) $email);
});
32 changes: 32 additions & 0 deletions tests/Fixtures/AnnotationCoverageFixture.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
<?php declare(strict_types = 1);

namespace Tests\Fixtures;

/**
* Coverage fixture description
*
* @number 123
* @options(name='demo', count=10, enabled=true)
*/
class AnnotationCoverageFixture
{

/** @options(name='property') */
public string $property = 'value';

public function withoutDoc(): void
{
// Intentionally empty.
}

}

/**
* Function fixture description
*
* @options(name='function')
*/
function annotationCoverageFunction(): void
{
// Intentionally empty.
}
Loading