<?php
declare(strict_types=1);
namespace PhpList\Core\Tests\Unit\Composer;
use Composer\Package\PackageInterface;
use PhpList\Core\Composer\ModuleFinder;
use PhpList\Core\Composer\PackageRepository;
use PHPUnit\Framework\TestCase;
use Prophecy\Prophecy\ObjectProphecy;
use Prophecy\Prophecy\ProphecySubjectInterface;
use Symfony\Component\Yaml\Yaml;
/**
* Testcase.
*
* @author Oliver Klee <oliver@phplist.com>
*/
class ModuleFinderTest extends TestCase
{
/**
* @var string
*/
const YAML_COMMENT = '# This file is autogenerated. Please do not edit.';
/**
* @var ModuleFinder
*/
private $subject = null;
/**
* @var PackageRepository|ObjectProphecy
*/
private $packageRepositoryProphecy = null;
protected function setUp()
{
$this->subject = new ModuleFinder();
$this->packageRepositoryProphecy = $this->prophesize(PackageRepository::class);
/** @var PackageRepository|ProphecySubjectInterface $packageRepository */
$packageRepository = $this->packageRepositoryProphecy->reveal();
$this->subject->injectPackageRepository($packageRepository);
}
/**
* @test
*/
public function findBundleClassesForNoModulesReturnsEmptyArray()
{
$this->packageRepositoryProphecy->findModules()->willReturn([]);
$result = $this->subject->findBundleClasses();
static::assertSame([], $result);
}
/**
* @return PackageInterface[][]
*/
public function modulesWithoutBundlesDataProvider(): array
{
/** @var array[][] $extrasSets */
$extrasSets = [
'one module without/with empty extras' => [[]],
'one module with extras for other stuff' => [['branch-alias' => ['dev-master' => '4.0.x-dev']]],
'one module with empty "phplist/core" extras section' => [['phplist/core' => []]],
'one module with empty bundles extras section' => [['phplist/core' => ['bundles' => []]]],
];
return $this->buildMockPackagesWithModuleConfiguration($extrasSets);
}
/**
* @param array[][] $extrasSets
*
* @return PackageInterface[][]
*/
private function buildMockPackagesWithModuleConfiguration(array $extrasSets): array
{
$moduleSets = [];
foreach ($extrasSets as $packageName => $extrasSet) {
$moduleSet = $this->buildSingleMockPackageWithModuleConfiguration($extrasSet);
$moduleSets[$packageName] = [$moduleSet];
}
return $moduleSets;
}
/**
* @param array[] $extrasSet
*
* @return PackageInterface[]
*/
private function buildSingleMockPackageWithModuleConfiguration(array $extrasSet): array
{
/** @var PackageInterface[] $moduleSet */
$moduleSet = [];
foreach ($extrasSet as $extras) {
$moduleSet[] = $this->buildPackageProphecyWithExtras($extras, 'phplist/test');
}
return $moduleSet;
}
/**
* @param array $extras
* @param string $packageName
*
* @return PackageInterface|ProphecySubjectInterface
*/
private function buildPackageProphecyWithExtras(array $extras, string $packageName): PackageInterface
{
/** @var PackageInterface|ObjectProphecy $packageProphecy */
$packageProphecy = $this->prophesize(PackageInterface::class);
$packageProphecy->getExtra()->willReturn($extras);
$packageProphecy->getName()->willReturn($packageName);
return $packageProphecy->reveal();
}
/**
* @test
* @param PackageInterface[] $modules
* @dataProvider modulesWithoutBundlesDataProvider
*/
public function findBundleClassesForModulesWithoutBundlesReturnsEmptyArray(array $modules)
{
$this->packageRepositoryProphecy->findModules()->willReturn($modules);
$result = $this->subject->findBundleClasses();
static::assertSame([], $result);
}
/**
* @return PackageInterface[][]
*/
public function modulesWithInvalidBundlesDataProvider(): array
{
/** @var array[][] $extrasSets */
$extrasSets = [
'one module with core section as string' => [['phplist/core' => 'foo']],
'one module with core section as int' => [['phplist/core' => 42]],
'one module with core section as float' => [['phplist/core' => 3.14159]],
'one module with core section as bool' => [['phplist/core' => true]],
'one module with bundles section as string' => [['phplist/core' => ['bundles' => 'foo']]],
'one module with bundles section as int' => [['phplist/core' => ['bundles' => 42]]],
'one module with bundles section as float' => [['phplist/core' => ['bundles' => 3.14159]]],
'one module with bundles section as bool' => [['phplist/core' => ['bundles' => true]]],
'one module with one bundle class name as array' => [['phplist/core' => ['bundles' => [[]]]]],
'one module with one bundle class name as int' => [['phplist/core' => ['bundles' => [42]]]],
'one module with one bundle class name as float' => [['phplist/core' => ['bundles' => [3.14159]]]],
'one module with one bundle class name as bool' => [['phplist/core' => ['bundles' => [true]]]],
'one module with one bundle class name as null' => [['phplist/core' => ['bundles' => [null]]]],
];
return $this->buildMockPackagesWithModuleConfiguration($extrasSets);
}
/**
* @test
* @param PackageInterface[] $modules
* @dataProvider modulesWithInvalidBundlesDataProvider
*/
public function findBundleClassesForModulesWithInvalidBundlesConfigurationThrowsException(array $modules)
{
$this->packageRepositoryProphecy->findModules()->willReturn($modules);
$this->expectException(\InvalidArgumentException::class);
$this->subject->findBundleClasses();
}
/**
* @return array[]
*/
public function modulesWithBundlesDataProvider(): array
{
/** @var array[][] $dataSets */
$dataSets = [
'one module with one bundle' => [
[
'phplist/foo' => [
'phplist/core' => [
'bundles' => ['Symfony\\Bundle\\FrameworkBundle\\FrameworkBundle'],
],
],
],
['phplist/foo' => ['Symfony\\Bundle\\FrameworkBundle\\FrameworkBundle']],
],
'one module with two bundles' => [
[
'phplist/foo' => [
'phplist/core' => [
'bundles' => [
'Symfony\\Bundle\\FrameworkBundle\\FrameworkBundle',
'PhpList\\Core\\EmptyStartPageBundle\\PhpListEmptyStartPageBundle',
],
],
],
],
[
'phplist/foo' => [
'Symfony\\Bundle\\FrameworkBundle\\FrameworkBundle',
'PhpList\\Core\\EmptyStartPageBundle\\PhpListEmptyStartPageBundle',
],
],
],
'two module with one bundle each' => [
[
'phplist/foo' => [
'phplist/core' => [
'bundles' => ['Symfony\\Bundle\\FrameworkBundle\\FrameworkBundle'],
],
],
'phplist/bar' => [
'phplist/core' => [
'bundles' => ['PhpList\\Core\\EmptyStartPageBundle\\PhpListEmptyStartPageBundle'],
],
],
],
[
'phplist/foo' => ['Symfony\\Bundle\\FrameworkBundle\\FrameworkBundle'],
'phplist/bar' => ['PhpList\\Core\\EmptyStartPageBundle\\PhpListEmptyStartPageBundle'],
],
],
];
$moduleSets = [];
/** @var array[] $dataSet */
foreach ($dataSets as $dataSetName => $dataSet) {
/** @var string[][][] $extraSets */
/** @var string[][] $expectedBundles */
list($extraSets, $expectedBundles) = $dataSet;
$testCases = [];
foreach ($extraSets as $packageName => $extras) {
$testCases[] = $this->buildPackageProphecyWithExtras($extras, $packageName);
}
$moduleSets[$dataSetName] = [$testCases, $expectedBundles];
}
return $moduleSets;
}
/**
* @test
* @param PackageInterface[] $modules
* @param string[][] $expectedBundles
* @dataProvider modulesWithBundlesDataProvider
*/
public function findBundleClassesForModulesWithBundlesReturnsBundleClassNames(
array $modules,
array $expectedBundles
) {
$this->packageRepositoryProphecy->findModules()->willReturn($modules);
$result = $this->subject->findBundleClasses();
static::assertSame($expectedBundles, $result);
}
/**
* @test
*/
public function createBundleConfigurationYamlForNoModulesReturnsCommentOnly()
{
$this->packageRepositoryProphecy->findModules()->willReturn([]);
$result = $this->subject->createBundleConfigurationYaml();
static::assertSame(static::YAML_COMMENT . "\n{ }", $result);
}
/**
* @test
* @param PackageInterface[][] $modules
* @param array[] $bundles
* @dataProvider modulesWithBundlesDataProvider
*/
public function createBundleConfigurationYamlReturnsYamlForBundles(array $modules, array $bundles)
{
$this->packageRepositoryProphecy->findModules()->willReturn($modules);
$result = $this->subject->createBundleConfigurationYaml();
static::assertSame(static::YAML_COMMENT . "\n" . Yaml::dump($bundles), $result);
}
/**
* @return PackageInterface[][]
*/
public function modulesWithoutRoutesDataProvider(): array
{
/** @var array[][] $extrasSets */
$extrasSets = [
'one module without/with empty extras' => [[]],
'one module with extras for other stuff' => [['branch-alias' => ['dev-master' => '4.0.x-dev']]],
'one module with empty "phplist/core" extras section' => [['phplist/core' => []]],
'one module with empty routes extras section' => [['phplist/core' => ['routes' => []]]],
];
return $this->buildMockPackagesWithModuleConfiguration($extrasSets);
}
/**
* @test
* @param PackageInterface[] $modules
* @dataProvider modulesWithoutRoutesDataProvider
*/
public function findRoutesForModulesWithoutRoutesReturnsEmptyArray(array $modules)
{
$this->packageRepositoryProphecy->findModules()->willReturn($modules);
$result = $this->subject->findRoutes();
static::assertSame([], $result);
}
/**
* @return PackageInterface[][]
*/
public function modulesWithInvalidRoutesDataProvider(): array
{
/** @var array[][] $extrasSets */
$extrasSets = [
'one module with core section as string' => [['phplist/core' => 'foo']],
'one module with core section as int' => [['phplist/core' => 42]],
'one module with core section as float' => [['phplist/core' => 3.14159]],
'one module with core section as bool' => [['phplist/core' => true]],
'one module with routes section as string' => [['phplist/core' => ['routes' => 'foo']]],
'one module with routes section as int' => [['phplist/core' => ['routes' => 42]]],
'one module with routes section as float' => [['phplist/core' => ['routes' => 3.14159]]],
'one module with routes section as bool' => [['phplist/core' => ['routes' => true]]],
'one module with one route class name as string' => [['phplist/core' => ['routes' => ['foo']]]],
'one module with one route class name as int' => [['phplist/core' => ['routes' => [42]]]],
'one module with one route class name as float' => [['phplist/core' => ['routes' => [3.14159]]]],
'one module with one route class name as bool' => [['phplist/core' => ['routes' => [true]]]],
'one module with one route class name as null' => [['phplist/core' => ['routes' => [null]]]],
];
return $this->buildMockPackagesWithModuleConfiguration($extrasSets);
}
/**
* @test
* @param PackageInterface[] $modules
* @dataProvider modulesWithInvalidRoutesDataProvider
*/
public function findRoutesClassesForModulesWithInvalidRoutesConfigurationThrowsException(array $modules)
{
$this->packageRepositoryProphecy->findModules()->willReturn($modules);
$this->expectException(\InvalidArgumentException::class);
$this->subject->findRoutes();
}
/**
* @return array[][]
*/
public function modulesWithRoutesDataProvider(): array
{
/** @var array[][] $dataSets */
$dataSets = [
'one module with one route' => [
[
'phplist/foo' => [
'phplist/core' => [
'routes' => [
'homepage' => [
'path' => '/',
'defaults' => ['_controller' => 'PhpListEmptyStartPageBundle:Default:index'],
],
],
],
],
],
[
'phplist/foo.homepage' => [
'path' => '/',
'defaults' => ['_controller' => 'PhpListEmptyStartPageBundle:Default:index'],
],
],
],
'one module with two routes' => [
[
'phplist/foo' => [
'phplist/core' => [
'routes' => [
'homepage' => [
'path' => '/',
'defaults' => ['_controller' => 'PhpListEmptyStartPageBundle:Default:index'],
],
'blog' => [
'path' => '/blog',
'defaults' => ['_controller' => 'PhpListEmptyStartPageBundle:Blog:index'],
],
],
],
],
],
[
'phplist/foo.homepage' => [
'path' => '/',
'defaults' => ['_controller' => 'PhpListEmptyStartPageBundle:Default:index'],
],
'phplist/foo.blog' => [
'path' => '/blog',
'defaults' => ['_controller' => 'PhpListEmptyStartPageBundle:Blog:index'],
],
],
],
'two module with one route each' => [
[
'phplist/foo' => [
'phplist/core' => [
'routes' => [
'homepage' => [
'path' => '/',
'defaults' => ['_controller' => 'PhpListEmptyStartPageBundle:Default:index'],
],
],
],
],
'phplist/bar' => [
'phplist/core' => [
'routes' => [
'blog' => [
'path' => '/blog',
'defaults' => ['_controller' => 'PhpListEmptyStartPageBundle:Blog:index'],
],
],
],
],
],
[
'phplist/foo.homepage' => [
'path' => '/',
'defaults' => ['_controller' => 'PhpListEmptyStartPageBundle:Default:index'],
],
'phplist/bar.blog' => [
'path' => '/blog',
'defaults' => ['_controller' => 'PhpListEmptyStartPageBundle:Blog:index'],
],
],
],
];
return $this->buildModuleSets($dataSets);
}
/**
* @param array[][] $dataSets
*
* @return array[]
*/
private function buildModuleSets(array $dataSets): array
{
$moduleSets = [];
/** @var array[] $dataSet */
foreach ($dataSets as $dataSetName => $dataSet) {
/** @var string[][][] $extraSets */
/** @var array[] $expectedRoutes */
list($extraSets, $expectedRoutes) = $dataSet;
$testCases = [];
foreach ($extraSets as $packageName => $extras) {
$testCases[] = $this->buildPackageProphecyWithExtras($extras, $packageName);
}
$moduleSets[$dataSetName] = [$testCases, $expectedRoutes];
}
return $moduleSets;
}
/**
* @test
* @param PackageInterface[] $modules
* @param array[] $expectedRoutes
* @dataProvider modulesWithRoutesDataProvider
*/
public function findRoutesForModulesWithRoutesReturnsRoutes(array $modules, array $expectedRoutes)
{
$this->packageRepositoryProphecy->findModules()->willReturn($modules);
$result = $this->subject->findRoutes();
static::assertSame($expectedRoutes, $result);
}
/**
* @test
*/
public function createRouteConfigurationYamlForNoModulesReturnsCommentOnly()
{
$this->packageRepositoryProphecy->findModules()->willReturn([]);
$result = $this->subject->createRouteConfigurationYaml();
static::assertSame(static::YAML_COMMENT . "\n{ }", $result);
}
/**
* @test
* @param PackageInterface[][] $modules
* @param array[] $routes
* @dataProvider modulesWithRoutesDataProvider
*/
public function createRouteConfigurationYamlReturnsYamlForRoutes(array $modules, array $routes)
{
$this->packageRepositoryProphecy->findModules()->willReturn($modules);
$result = $this->subject->createRouteConfigurationYaml();
static::assertSame(static::YAML_COMMENT . "\n" . Yaml::dump($routes), $result);
}
/**
* @return PackageInterface[][]
*/
public function modulesWithoutConfigurationDataProvider(): array
{
/** @var array[][] $extrasSets */
$extrasSets = [
'without/with empty extras' => [[]],
'with extras for other stuff' => [['branch-alias' => ['dev-master' => '4.0.x-dev']]],
'with empty "phplist/core" extras section' => [['phplist/core' => []]],
'with empty configuration extras section' => [['phplist/core' => ['configuration' => []]]],
];
return $this->buildMockPackagesWithModuleConfiguration($extrasSets);
}
/**
* @test
* @param PackageInterface[] $modules
* @dataProvider modulesWithoutConfigurationDataProvider
*/
public function findGeneralConfigurationForModulesWithoutConfigurationReturnsEmptyArray(array $modules)
{
$this->packageRepositoryProphecy->findModules()->willReturn($modules);
$result = $this->subject->findGeneralConfiguration();
static::assertSame([], $result);
}
/**
* @return PackageInterface[][]
*/
public function modulesWithInvalidConfigurationDataProvider(): array
{
/** @var array[][] $extrasSets */
$extrasSets = [
'core section as string' => [['phplist/core' => 'foo']],
'core section as int' => [['phplist/core' => 42]],
'core section as float' => [['phplist/core' => 3.14159]],
'core section as bool' => [['phplist/core' => true]],
'configuration section as string' => [['phplist/core' => ['configuration' => 'foo']]],
'configuration section as int' => [['phplist/core' => ['configuration' => 42]]],
'configuration section as float' => [['phplist/core' => ['configuration' => 3.14159]]],
'configuration section as bool' => [['phplist/core' => ['configuration' => true]]],
];
return $this->buildMockPackagesWithModuleConfiguration($extrasSets);
}
/**
* @test
* @param PackageInterface[] $modules
* @dataProvider modulesWithInvalidConfigurationDataProvider
*/
public function findGeneralConfigurationForModulesWithInvalidConfigurationThrowsException(array $modules)
{
$this->packageRepositoryProphecy->findModules()->willReturn($modules);
$this->expectException(\InvalidArgumentException::class);
$this->subject->findGeneralConfiguration();
}
/**
* @return array[][]
*/
public function modulesWithConfigurationDataProvider(): array
{
/** @var array[][] $dataSets */
$dataSets = [
'one module with configuration' => [
[
'phplist/foo' => [
'phplist/core' => [
'configuration' => ['foo' => 'bar'],
],
],
],
['foo' => 'bar'],
],
'two modules non-overlapping configuration sets' => [
[
'phplist/foo' => [
'phplist/core' => [
'configuration' => ['foo' => 'bar'],
],
],
'phplist/bar' => [
'phplist/core' => [
'configuration' => [
'foobar' => ['life' => 'everything'],
],
],
],
],
[
'foo' => 'bar',
'foobar' => ['life' => 'everything'],
],
],
'two modules overlapping configuration sets' => [
[
'phplist/foo' => [
'phplist/core' => [
'configuration' => [
'foo' => 'bar',
'foobar' => [1 => 'hello'],
'good' => 'code',
],
],
],
'phplist/bar' => [
'phplist/core' => [
'configuration' => [
'foo' => 'bonjour',
'foobar' => [2 => 'world'],
],
],
],
],
[
'foo' => 'bonjour',
'foobar' => [1 => 'hello', 2 => 'world'],
'good' => 'code',
],
],
];
return $this->buildModuleSets($dataSets);
}
/**
* @test
* @param PackageInterface[] $modules
* @param array $expectedConfiguration
* @dataProvider modulesWithConfigurationDataProvider
*/
public function findGeneralConfigurationForModulesWithConfigurationReturnsConfiguration(
array $modules,
array $expectedConfiguration
) {
$this->packageRepositoryProphecy->findModules()->willReturn($modules);
$result = $this->subject->findGeneralConfiguration();
static::assertSame($expectedConfiguration, $result);
}
/**
* @test
*/
public function createGeneralConfigurationYamlForNoModulesReturnsCommentOnly()
{
$this->packageRepositoryProphecy->findModules()->willReturn([]);
$result = $this->subject->createGeneralConfigurationYaml();
static::assertSame(static::YAML_COMMENT . "\n{ }", $result);
}
/**
* @test
* @param PackageInterface[][] $modules
* @param array[] $routes
* @dataProvider modulesWithConfigurationDataProvider
*/
public function createGeneralConfigurationYamlReturnsYamlForConfiguration(array $modules, array $routes)
{
$this->packageRepositoryProphecy->findModules()->willReturn($modules);
$result = $this->subject->createGeneralConfigurationYaml();
static::assertSame(static::YAML_COMMENT . "\n" . Yaml::dump($routes), $result);
}
}