Skip to content

Commit

Permalink
Fix: Reject rule sets referencing unknown layers
Browse files Browse the repository at this point in the history
  • Loading branch information
localheinz committed Oct 20, 2020
1 parent 5ade86b commit b3eae34
Show file tree
Hide file tree
Showing 5 changed files with 221 additions and 9 deletions.
26 changes: 26 additions & 0 deletions src/Configuration/Configuration.php
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ class Configuration

/**
* @param array<string, mixed> $args
*
* @throws Exception\InvalidConfigurationException
*/
public static function fromArray(array $args): self
{
Expand All @@ -45,11 +47,35 @@ public static function fromArray(array $args): self
return new self($options);
}

/**
* @throws Exception\InvalidConfigurationException
*/
private function __construct(array $options)
{
$this->layers = array_map(static function (array $v): ConfigurationLayer {
return ConfigurationLayer::fromArray($v);
}, $options['layers']);

$layerNames = array_values(array_map(static function (ConfigurationLayer $configurationLayer): string {
return $configurationLayer->getName();
}, $this->layers));

$layerNamesUsedInRuleset = array_unique(array_merge(
array_keys($options['ruleset']),
...array_values(array_map(static function (?array $rules): array {
return (array) $rules;
}, $options['ruleset']))
));

$unknownLayerNames = array_diff(
$layerNamesUsedInRuleset,
$layerNames
);

if ([] !== $unknownLayerNames) {
throw Exception\InvalidConfigurationException::fromUnknownLayerNames(...$unknownLayerNames);
}

$this->ruleset = ConfigurationRuleset::fromArray($options['ruleset']);
$this->paths = $options['paths'];
$this->skipViolations = ConfigurationSkippedViolation::fromArray($options['skip_violations']);
Expand Down
18 changes: 18 additions & 0 deletions src/Configuration/Exception/InvalidConfigurationException.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<?php

declare(strict_types=1);

namespace SensioLabs\Deptrac\Configuration\Exception;

final class InvalidConfigurationException extends \InvalidArgumentException
{
public static function fromUnknownLayerNames(string ...$layerNames): self
{
natsort($layerNames);

return new self(sprintf(
'Configuration can not reference rule sets with unknown layer names, got "%s" as unknown.',
implode('", "', $layerNames)
));
}
}
57 changes: 51 additions & 6 deletions tests/Configuration/ConfigurationTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,50 @@
use PHPUnit\Framework\TestCase;
use SensioLabs\Deptrac\AstRunner\AstMap\ClassLikeName;
use SensioLabs\Deptrac\Configuration\Configuration;
use SensioLabs\Deptrac\Configuration\Exception;

/**
* @covers \SensioLabs\Deptrac\Configuration\Configuration
*/
class ConfigurationTest extends TestCase
{
public function testFromArrayRejectsRulesetUsingUnknownLayerNames(): void
{
$this->expectException(Exception\InvalidConfigurationException::class);
$this->expectExceptionMessage('Configuration can not reference rule sets with unknown layer names, got "quux", "qux" as unknown.');

Configuration::fromArray([
'layers' => [
[
'name' => 'foo',
'collectors' => [],
],
[
'name' => 'bar',
'collectors' => [],
],
[
'name' => 'baz',
'collectors' => [],
],
],
'paths' => [
'src',
],
'ruleset' => [
'foo' => [
'bar',
],
'bar' => null,
'baz' => [
'bar',
'qux',
],
'quux' => null,
],
]);
}

public function testFromArray(): void
{
$configuration = Configuration::fromArray([
Expand All @@ -19,7 +60,11 @@ public function testFromArray(): void
'collectors' => [],
],
[
'name' => 'some_name',
'name' => 'xx',
'collectors' => [],
],
[
'name' => 'yy',
'collectors' => [],
],
],
Expand All @@ -32,15 +77,15 @@ public function testFromArray(): void
'bar2',
],
'ruleset' => [
'lala' => ['xx', 'yy'],
'some_name' => ['xx', 'yy'],
],
]);

static::assertCount(2, $configuration->getLayers());
static::assertCount(3, $configuration->getLayers());
static::assertEquals('some_name', $configuration->getLayers()[0]->getName());
static::assertEquals(['foo', 'bar'], $configuration->getPaths());
static::assertEquals(['foo2', 'bar2'], $configuration->getExcludeFiles());
static::assertEquals(['xx', 'yy'], $configuration->getRuleset()->getAllowedDependencies('lala'));
static::assertEquals(['xx', 'yy'], $configuration->getRuleset()->getAllowedDependencies('some_name'));
static::assertTrue($configuration->ignoreUncoveredInternalClasses());
}

Expand All @@ -53,7 +98,7 @@ public function testExcludedFilesAreOptional(): void
'collectors' => [],
],
[
'name' => 'some_name',
'name' => 'some_other_name',
'collectors' => [],
],
],
Expand All @@ -62,7 +107,7 @@ public function testExcludedFilesAreOptional(): void
'bar',
],
'ruleset' => [
'lala' => ['xx', 'yy'],
'some_name' => ['some_other_name'],
],
]);

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
<?php

declare(strict_types=1);

namespace Tests\SensioLabs\Deptrac\Configuration\Exception;

use PHPUnit\Framework\TestCase;
use SensioLabs\Deptrac\Configuration\Exception\InvalidConfigurationException;

/**
* @covers \SensioLabs\Deptrac\Configuration\Exception\InvalidConfigurationException
*/
final class InvalidConfigurationExceptionTest extends TestCase
{
public function testIsInvalidArgumentException(): void
{
$exception = new InvalidConfigurationException();

self::assertInstanceOf(\InvalidArgumentException::class, $exception);
}

public function testFromUnknownLayerNamesReturnsException(): void
{
$layerNames = [
'foo',
'bar',
];

$exception = InvalidConfigurationException::fromUnknownLayerNames(...$layerNames);

natsort($layerNames);

$message = sprintf(
'Configuration can not reference rule sets with unknown layer names, got "%s" as unknown.',
implode('", "', $layerNames)
);

self::assertSame($message, $exception->getMessage());
}
}
89 changes: 86 additions & 3 deletions tests/RulesetEngineTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@
use SensioLabs\Deptrac\Dependency\Result;
use SensioLabs\Deptrac\RulesetEngine;

/**
* @covers \SensioLabs\Deptrac\RulesetEngine
*/
class RulesetEngineTest extends TestCase
{
private function createDependencies(array $fromTo): iterable
Expand All @@ -38,6 +41,16 @@ public function dependencyProvider(): iterable
'ClassA' => ['LayerA'],
'ClassB' => ['LayerB'],
],
[
[
'name' => 'LayerA',
'collectors' => [],
],
[
'name' => 'LayerB',
'collectors' => [],
],
],
[
'LayerA' => [
'LayerB',
Expand All @@ -54,6 +67,16 @@ public function dependencyProvider(): iterable
'ClassA' => ['LayerA'],
'ClassB' => ['LayerB'],
],
[
[
'name' => 'LayerA',
'collectors' => [],
],
[
'name' => 'LayerB',
'collectors' => [],
],
],
[
'LayerA' => [],
'LayerB' => [],
Expand All @@ -69,6 +92,16 @@ public function dependencyProvider(): iterable
'ClassA' => ['LayerA'],
'ClassB' => ['LayerB'],
],
[
[
'name' => 'LayerA',
'collectors' => [],
],
[
'name' => 'LayerB',
'collectors' => [],
],
],
[],
1,
];
Expand All @@ -82,6 +115,7 @@ public function dependencyProvider(): iterable
'ClassB' => [],
],
[],
[],
0,
];

Expand All @@ -93,6 +127,16 @@ public function dependencyProvider(): iterable
'ClassA' => ['LayerA'],
'ClassB' => ['LayerB'],
],
[
[
'name' => 'LayerA',
'collectors' => [],
],
[
'name' => 'LayerB',
'collectors' => [],
],
],
[
'LayerA' => ['LayerB'],
],
Expand All @@ -107,6 +151,16 @@ public function dependencyProvider(): iterable
'ClassA' => ['LayerA'],
'ClassB' => ['LayerB'],
],
[
[
'name' => 'LayerA',
'collectors' => [],
],
[
'name' => 'LayerB',
'collectors' => [],
],
],
[
'LayerB' => ['LayerA'],
],
Expand All @@ -125,6 +179,24 @@ public function dependencyProvider(): iterable
'ClassC' => ['LayerC'],
'ClassD' => ['LayerD'],
],
[
[
'name' => 'LayerA',
'collectors' => [],
],
[
'name' => 'LayerB',
'collectors' => [],
],
[
'name' => 'LayerC',
'collectors' => [],
],
[
'name' => 'LayerD',
'collectors' => [],
],
],
[],
3,
];
Expand All @@ -136,6 +208,12 @@ public function dependencyProvider(): iterable
[
'ClassA' => ['LayerA'],
],
[
[
'name' => 'LayerA',
'collectors' => [],
],
],
[],
0,
];
Expand All @@ -144,8 +222,13 @@ public function dependencyProvider(): iterable
/**
* @dataProvider dependencyProvider
*/
public function testProcess(array $dependenciesAsArray, array $classesInLayers, array $rulesetConfiguration, int $expectedCount): void
{
public function testProcess(
array $dependenciesAsArray,
array $classesInLayers,
array $layersConfiguration,
array $rulesetConfiguration,
int $expectedCount
): void {
$dependencyResult = new Result();
foreach ($this->createDependencies($dependenciesAsArray) as $dep) {
$dependencyResult->addDependency($dep);
Expand All @@ -157,7 +240,7 @@ public function testProcess(array $dependenciesAsArray, array $classesInLayers,
}

$configuration = Configuration::fromArray([
'layers' => [],
'layers' => $layersConfiguration,
'paths' => [],
'ruleset' => $rulesetConfiguration,
]);
Expand Down

0 comments on commit b3eae34

Please sign in to comment.