Skip to content
Merged
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: 3 additions & 3 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,11 @@
"brainbits/phpstan-rules": "^4.0",
"dama/doctrine-test-bundle": "^8.2",
"doctrine/dbal": "^4.2",
"ergebnis/phpstan-rules": "^2.8",
"ergebnis/phpstan-rules": "^2.10",
"gemorroj/archive7z": "^5.7",
"mikey179/vfsstream": "^1.6.12",
"monolog/monolog": "^2.0 || ^3.0",
"phpstan/phpstan": "^2.1.4",
"monolog/monolog": "^3.0",
"phpstan/phpstan": "^2.1.8",
"phpstan/phpstan-phpunit": "^2.0.4",
"phpstan/phpstan-symfony": "^2.0.2",
"phpunit/phpunit": "^12.0.2",
Expand Down
2 changes: 2 additions & 0 deletions phpstan.neon.dist
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ parameters:
- '#Constructor in .* has parameter .* with default value#'
- '#SchemaBuilder::foo\(\)#'
ergebnis:
noNamedArgument:
enabled: false
noAssignByReference:
enabled: false
noParameterPassedByReference:
Expand Down
41 changes: 17 additions & 24 deletions src/HttpClientMock/CallbackHandler.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,35 +8,28 @@
use Monolog\Level;
use Monolog\LogRecord;

use function class_exists;
final class CallbackHandler extends AbstractProcessingHandler
{
/** @var callable */
private $fn;

if (class_exists(LogRecord::class)) {
/**
* Callback handler for Monolog 3.x
*/
final class CallbackHandler extends AbstractProcessingHandler
public function __construct(callable $fn, int|string|Level $level = Level::Debug, bool $bubble = true)
{
/** @var callable */
private $fn;
parent::__construct($level, $bubble);

public function __construct(callable $fn, int|string|Level $level = Level::Debug, bool $bubble = true)
{
parent::__construct($level, $bubble);

$this->fn = $fn;
}
$this->fn = $fn;
}

public function clear(): void
{
}
public function clear(): void

Check warning on line 23 in src/HttpClientMock/CallbackHandler.php

View check run for this annotation

Codecov / codecov/patch

src/HttpClientMock/CallbackHandler.php#L23

Added line #L23 was not covered by tests
{
}

Check warning on line 25 in src/HttpClientMock/CallbackHandler.php

View check run for this annotation

Codecov / codecov/patch

src/HttpClientMock/CallbackHandler.php#L25

Added line #L25 was not covered by tests

public function reset(): void
{
}
public function reset(): void

Check warning on line 27 in src/HttpClientMock/CallbackHandler.php

View check run for this annotation

Codecov / codecov/patch

src/HttpClientMock/CallbackHandler.php#L27

Added line #L27 was not covered by tests
{
}

Check warning on line 29 in src/HttpClientMock/CallbackHandler.php

View check run for this annotation

Codecov / codecov/patch

src/HttpClientMock/CallbackHandler.php#L29

Added line #L29 was not covered by tests

protected function write(LogRecord $record): void
{
($this->fn)($record);
}
protected function write(LogRecord $record): void

Check warning on line 31 in src/HttpClientMock/CallbackHandler.php

View check run for this annotation

Codecov / codecov/patch

src/HttpClientMock/CallbackHandler.php#L31

Added line #L31 was not covered by tests
{
($this->fn)($record);

Check warning on line 33 in src/HttpClientMock/CallbackHandler.php

View check run for this annotation

Codecov / codecov/patch

src/HttpClientMock/CallbackHandler.php#L33

Added line #L33 was not covered by tests
}
}
67 changes: 58 additions & 9 deletions src/HttpClientMock/HttpClientMockTrait.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
use ArrayObject;
use Brainbits\FunctionalTestHelpers\HttpClientMock\Exception\HttpClientMockException;
use Monolog\Logger;
use PHPUnit\Framework\Attributes\After;
use PHPUnit\Framework\Attributes\Before;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Console\Event\ConsoleErrorEvent;
use Symfony\Component\Console\Event\ConsoleTerminateEvent;
Expand All @@ -22,15 +24,67 @@
use function Safe\parse_url;
use function sprintf;
use function str_contains;
use function trigger_deprecation;
use function trigger_error;
use function ucfirst;
use function urldecode;
use function vsprintf;

use const E_USER_DEPRECATED;

/** @mixin TestCase */
trait HttpClientMockTrait
{
protected function registerNoMatchingMockRequestAsserts(
/** @var list<MockRequestBuilder> */
protected array $createdMockRequestBuilders = [];
/** @var list<string> */
protected array $mockRequestLoggerNames = ['monolog.logger'];

#[Before]
final protected function setUpMockRequestBuilder(): void
{
$container = static::getContainer();
$eventDispatcher = $container->get('event_dispatcher');
assert($eventDispatcher instanceof EventDispatcherInterface);

$loggerNames = $this->mockRequestLoggerNames;

$this->registerNoMatchingMockRequestListeners(
$eventDispatcher,
...array_map(
static function ($loggerName) {
$logger = static::getContainer()->get($loggerName);
assert($logger instanceof Logger);

return $logger;
},
$loggerNames,
),
);
}

#[After]
protected function assertNoFailedMockRequests(): void
{
$mockRequestBuilders = $this->createdMockRequestBuilders;
$this->createdMockRequestBuilders = [];

foreach ($mockRequestBuilders as $mockRequestBuilder) {
foreach ($mockRequestBuilder->getFailedAssertions() as $failedAssertion) {
throw $failedAssertion;

Check warning on line 73 in src/HttpClientMock/HttpClientMockTrait.php

View check run for this annotation

Codecov / codecov/patch

src/HttpClientMock/HttpClientMockTrait.php#L73

Added line #L73 was not covered by tests
}
}
}

protected function registerNoMatchingMockRequestAsserts(): void

Check warning on line 78 in src/HttpClientMock/HttpClientMockTrait.php

View check run for this annotation

Codecov / codecov/patch

src/HttpClientMock/HttpClientMockTrait.php#L78

Added line #L78 was not covered by tests
{
// legacy start - remove in 8.0.0

trigger_error('no matching mock request listeners are now automatically registered', E_USER_DEPRECATED);

Check warning on line 82 in src/HttpClientMock/HttpClientMockTrait.php

View check run for this annotation

Codecov / codecov/patch

src/HttpClientMock/HttpClientMockTrait.php#L82

Added line #L82 was not covered by tests

// legacy end - remove in 8.0.0
}

protected function registerNoMatchingMockRequestListeners(
EventDispatcherInterface $eventDispatcher,
Logger ...$loggers,
): void {
Expand Down Expand Up @@ -130,18 +184,13 @@
$stack = self::getContainer()->get(MockRequestBuilderCollection::class);
assert($stack instanceof MockRequestBuilderCollection);

$builder = (new MockRequestBuilder())
$this->createdMockRequestBuilders[] = $builder = (new MockRequestBuilder())
->method($method);

// legacy start - remove in 8.0.0

if (is_string($uri) && str_contains($uri, '?')) {
trigger_deprecation(
'functional-test-helpers',
'7.0.0',
'Query parameters in uri is deprecated. Use %s instead',
'queryParam()',
);
trigger_error('Query parameters in uri is deprecated. Use queryParam() instead', E_USER_DEPRECATED);
$uriParts = parse_url($uri);
$uri = ($uriParts['scheme'] ?? false ? $uriParts['scheme'] . '://' : '') .
($uriParts['host'] ?? false ? $uriParts['host'] : '') .
Expand Down
15 changes: 14 additions & 1 deletion src/HttpClientMock/MockRequestBuilder.php
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,9 @@ final class MockRequestBuilder
/** @var callable|null */
public mixed $onMatch = null;

/** @var list<Throwable> */
private array $failedAssertions = [];

public function __construct()
{
$this->responses = new MockResponseCollection();
Expand Down Expand Up @@ -165,10 +168,20 @@ public function assertThat(callable $assert): self
return $this;
}

/** @return list<Throwable> */
public function getFailedAssertions(): array
{
return $this->failedAssertions;
}

public function assert(RealRequest $realRequest): void
{
foreach ($this->assertions as $assertion) {
$assertion($realRequest, $this);
try {
$assertion($realRequest, $this);
} catch (Throwable $e) {
$this->failedAssertions[] = $e;
}
}
}

Expand Down
12 changes: 11 additions & 1 deletion tests/HttpClientMock/HttpClientMockTraitTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,24 +11,34 @@
use Brainbits\FunctionalTestHelpers\HttpClientMock\Matcher\UriParams;
use Brainbits\FunctionalTestHelpers\HttpClientMock\MockRequestBuilderCollection;
use Brainbits\FunctionalTestHelpers\HttpClientMock\MockRequestMatcher;
use Monolog\Logger;
use PHPUnit\Framework\Attributes\Before;
use PHPUnit\Framework\TestCase;
use Symfony\Component\DependencyInjection\Container;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;

final class HttpClientMockTraitTest extends TestCase
{
use HttpClientMockTrait;

private static MockRequestBuilderCollection|null $collection = null;
private static EventDispatcherInterface|null $dispatcher = null;
private static Logger|null $logger = null;

public function setUp(): void
#[Before(100)]
public function createContainerServices(): void
{
self::$collection = new MockRequestBuilderCollection();
self::$dispatcher = $this->createMock(EventDispatcherInterface::class);
self::$logger = $this->createMock(Logger::class);
}

public static function getContainer(): Container
{
$container = new Container();
$container->set(MockRequestBuilderCollection::class, self::$collection);
$container->set('event_dispatcher', self::$dispatcher);
$container->set('monolog.logger', self::$logger);

return $container;
}
Expand Down
40 changes: 23 additions & 17 deletions tests/HttpClientMock/MockRequestBuilderCollectionTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,9 @@
use Brainbits\FunctionalTestHelpers\HttpClientMock\MockRequestMatcher;
use Brainbits\FunctionalTestHelpers\HttpClientMock\MockResponseBuilder;
use Brainbits\FunctionalTestHelpers\HttpClientMock\RealRequest;
use PHPUnit\Framework\AssertionFailedError;
use PHPUnit\Framework\Attributes\CoversClass;
use PHPUnit\Framework\Attributes\DataProvider;
use PHPUnit\Framework\ExpectationFailedException;
use PHPUnit\Framework\TestCase;

#[CoversClass(MockRequestBuilder::class)]
Expand Down Expand Up @@ -173,68 +173,74 @@ public function testAssertContent(): void
{
$collection = new MockRequestBuilderCollection();
$collection->addMockRequestBuilder(
(new MockRequestBuilder())
$builder = (new MockRequestBuilder())
->assertContent(function (string $content): void {
$this->assertSame('this is content', $content);
})
->willRespond(new MockResponseBuilder()),
);

$collection('GET', '/query', ['body' => 'this is content']);

$this->assertSame([], $builder->getFailedAssertions());
}

public function testAssertContentFails(): void
{
$collection = new MockRequestBuilderCollection();
$collection->addMockRequestBuilder(
(new MockRequestBuilder())
$builder = (new MockRequestBuilder())
->assertContent(function (string $content): void {
$this->assertSame('this is content', $content);
})
->willRespond(new MockResponseBuilder()),
);

try {
$collection('GET', '/query', ['body' => 'does-not-match']);
} catch (AssertionFailedError) {
return;
}
$collection('GET', '/query', ['body' => 'does-not-match']);

$this->fail('Expected assertion was not thrown');
$failedAssertions = $builder->getFailedAssertions();
$this->assertCount(1, $failedAssertions);
$this->assertInstanceOf(
ExpectationFailedException::class,
$failedAssertions[0],
);
}

public function testAssertThat(): void
{
$collection = new MockRequestBuilderCollection();
$collection->addMockRequestBuilder(
(new MockRequestBuilder())
$builder = (new MockRequestBuilder())
->assertThat(function (RealRequest $realRequest): void {
$this->assertSame('this is content', $realRequest->getContent());
})
->willRespond(new MockResponseBuilder()),
);

$collection('GET', '/query', ['body' => 'this is content']);

$this->assertSame([], $builder->getFailedAssertions());
}

public function testAssertThatFails(): void
{
$collection = new MockRequestBuilderCollection();
$collection->addMockRequestBuilder(
(new MockRequestBuilder())
$builder = (new MockRequestBuilder())
->assertThat(function (RealRequest $realRequest): void {
$this->assertSame('this is content', $realRequest->getContent());
})
->willRespond(new MockResponseBuilder()),
);

try {
$collection('GET', '/query', ['body' => 'does-not-match']);
} catch (AssertionFailedError) {
return;
}
$collection('GET', '/query', ['body' => 'does-not-match']);

$this->fail('Expected assertion was not thrown');
$failedAssertions = $builder->getFailedAssertions();
$this->assertCount(1, $failedAssertions);
$this->assertInstanceOf(
ExpectationFailedException::class,
$failedAssertions[0],
);
}

/** @return mixed[] */
Expand Down
Loading
Loading