2022-01-26 12:10:51 +01:00

688 lines
25 KiB
PHP

<?php
/**
* Chill is a software for social workers
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/
declare(strict_types=1);
namespace Chill\MainBundle\Tests\Export;
use Chill\MainBundle\Export\AggregatorInterface;
use Chill\MainBundle\Export\ExportInterface;
use Chill\MainBundle\Export\ExportManager;
use Chill\MainBundle\Export\FilterInterface;
use Chill\MainBundle\Form\Type\Export\ExportType;
use Chill\MainBundle\Security\Authorization\AuthorizationHelper;
use Chill\MainBundle\Test\PrepareCenterTrait;
use Chill\MainBundle\Test\PrepareScopeTrait;
use Chill\MainBundle\Test\PrepareUserTrait;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\QueryBuilder;
use Prophecy\Argument;
use Prophecy\Prophet;
use Psr\Log\LoggerInterface;
use RuntimeException;
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorage;
use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken;
use Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface;
use Symfony\Component\Security\Core\Role\Role;
use Symfony\Component\Security\Core\User\UserInterface;
use function count;
/**
* Test the export manager.
*
* @internal
* @coversNothing
*/
final class ExportManagerTest extends KernelTestCase
{
use PrepareCenterTrait;
use PrepareScopeTrait;
use PrepareUserTrait;
private Prophet $prophet;
protected function setUp(): void
{
self::bootKernel();
$this->prophet = new \Prophecy\Prophet();
}
protected function tearDown(): void
{
$this->prophet->checkPredictions();
}
public function testAggregatorsApplyingOn()
{
$center = $this->prepareCenter(100, 'center');
$centers = [$center];
$user = $this->prepareUser([]);
$authorizationChecker = $this->prophet->prophesize();
$authorizationChecker->willImplement(AuthorizationCheckerInterface::class);
$authorizationChecker->isGranted('CHILL_STAT_DUMMY', $center)
->willReturn(true);
$exportManager = $this->createExportManager(
null,
null,
$authorizationChecker->reveal(),
null,
$user
);
$exportFooBar = $this->prophet->prophesize();
$exportFooBar->willImplement(ExportInterface::class);
$exportFooBar->requiredRole()->willReturn(new Role('CHILL_STAT_DUMMY'));
$exportFooBar->supportsModifiers()->willReturn(['foo', 'bar']);
$aggregatorBar = $this->prophet->prophesize();
$aggregatorBar->willImplement(AggregatorInterface::class);
$aggregatorBar->applyOn()->willReturn('bar');
$aggregatorBar->addRole()->willReturn(null);
$exportManager->addAggregator($aggregatorBar->reveal(), 'bar');
$exportBar = $this->prophet->prophesize();
$exportBar->willImplement(ExportInterface::class);
$exportBar->requiredRole()->willReturn(new Role('CHILL_STAT_DUMMY'));
$exportBar->supportsModifiers()->willReturn(['bar']);
$aggregatorFoo = $this->prophet->prophesize();
$aggregatorFoo->willImplement(AggregatorInterface::class);
$aggregatorFoo->applyOn()->willReturn('foo');
$aggregatorFoo->addRole()->willReturn(null);
$exportManager->addAggregator($aggregatorFoo->reveal(), 'foo');
$exportFoo = $this->prophet->prophesize();
$exportFoo->willImplement(ExportInterface::class);
$exportFoo->requiredRole()->willReturn(new Role('CHILL_STAT_DUMMY'));
$exportFoo->supportsModifiers()->willReturn(['foo']);
$obtained = iterator_to_array($exportManager->getAggregatorsApplyingOn($exportFoo->reveal(), $centers));
$this->assertEquals(1, count($obtained));
$this->assertContains('foo', array_keys($obtained));
$obtained = iterator_to_array($exportManager->getAggregatorsApplyingOn($exportBar->reveal(), $centers));
$this->assertEquals(1, count($obtained));
$this->assertContains('bar', array_keys($obtained));
$obtained = iterator_to_array($exportManager->getAggregatorsApplyingOn($exportFooBar->reveal(), $centers));
$this->assertEquals(2, count($obtained));
$this->assertContains('bar', array_keys($obtained));
$this->assertContains('foo', array_keys($obtained));
// test with empty centers
$obtained = iterator_to_array($exportManager->getAggregatorsApplyingOn($exportFooBar->reveal(), []));
$this->assertEquals(0, count($obtained));
}
public function testFiltersApplyingOn()
{
$center = $this->prepareCenter(100, 'center');
$centers = [$center];
$user = $this->prepareUser([]);
$authorizationChecker = $this->prophet->prophesize();
$authorizationChecker->willImplement(AuthorizationCheckerInterface::class);
$authorizationChecker->isGranted('CHILL_STAT_DUMMY', $center)
->willReturn(true);
$exportManager = $this->createExportManager(
null,
null,
$authorizationChecker->reveal(),
null,
$user
);
$exportFooBar = $this->prophet->prophesize();
$exportFooBar->willImplement(ExportInterface::class);
$exportFooBar->requiredRole()->willReturn(new Role('CHILL_STAT_DUMMY'));
$exportFooBar->supportsModifiers()->willReturn(['foo', 'bar']);
$filterBar = $this->prophet->prophesize();
$filterBar->willImplement(FilterInterface::class);
$filterBar->applyOn()->willReturn('bar');
$filterBar->addRole()->willReturn(null);
$filterBar->describeAction(Argument::cetera())->willReturn('string');
$exportManager->addFilter($filterBar->reveal(), 'bar');
$exportBar = $this->prophet->prophesize();
$exportBar->willImplement(ExportInterface::class);
$exportBar->requiredRole()->willReturn(new Role('CHILL_STAT_DUMMY'));
$exportBar->supportsModifiers()->willReturn(['bar']);
$filterFoo = $this->prophet->prophesize();
$filterFoo->willImplement(FilterInterface::class);
$filterFoo->applyOn()->willReturn('foo');
$filterFoo->addRole()->willReturn(null);
$filterFoo->describeAction(Argument::cetera())->willReturn('string');
$exportManager->addFilter($filterFoo->reveal(), 'foo');
$exportFoo = $this->prophet->prophesize();
$exportFoo->willImplement(ExportInterface::class);
$exportFoo->requiredRole()->willReturn(new Role('CHILL_STAT_DUMMY'));
$exportFoo->supportsModifiers()->willReturn(['foo']);
$obtained = iterator_to_array($exportManager->getFiltersApplyingOn($exportFoo->reveal(), $centers));
$this->assertEquals(1, count($obtained));
$this->assertContains('foo', array_keys($obtained));
$obtained = iterator_to_array($exportManager->getFiltersApplyingOn($exportBar->reveal(), $centers));
$this->assertEquals(1, count($obtained));
$this->assertContains('bar', array_keys($obtained));
$obtained = iterator_to_array($exportManager->getFiltersApplyingOn($exportFooBar->reveal(), $centers));
$this->assertEquals(2, count($obtained));
$this->assertContains('bar', array_keys($obtained));
$this->assertContains('foo', array_keys($obtained));
$obtained = iterator_to_array($exportManager->getFiltersApplyingOn($exportFooBar->reveal(), []));
$this->assertEquals(0, count($obtained));
}
public function testFormattersByTypes()
{
$exportManager = $this->createExportManager();
//create a formatter
$formatterFoo = $this->prophet->prophesize();
$formatterFoo->willImplement('Chill\MainBundle\Export\FormatterInterface');
$formatterFoo->getType()->willReturn('foo');
$formatterBar = $this->prophet->prophesize();
$formatterBar->willImplement('Chill\MainBundle\Export\FormatterInterface');
$formatterBar->getType()->willReturn('bar');
$exportManager->addFormatter($formatterFoo->reveal(), 'foo');
$exportManager->addFormatter($formatterBar->reveal(), 'bar');
$obtained = $exportManager->getFormattersByTypes(['foo']);
$this->assertContains($formatterFoo->reveal(), $obtained);
$this->assertNotContains($formatterBar->reveal(), $obtained);
}
/**
* Test the generation of an export.
*/
public function testGenerate()
{
$center = $this->prepareCenter(100, 'center');
$user = $this->prepareUser([]);
$authorizationChecker = $this->prophet->prophesize();
$authorizationChecker->willImplement(AuthorizationCheckerInterface::class);
$authorizationChecker->isGranted('CHILL_STAT_DUMMY', $center)
->willReturn(true);
$exportManager = $this->createExportManager(
null,
null,
$authorizationChecker->reveal(),
null,
$user
);
$export = $this->prophet->prophesize();
$export->willImplement(ExportInterface::class);
$em = self::$container->get('doctrine.orm.entity_manager');
$export->initiateQuery(
Argument::is(['foo']),
Argument::Type('array'),
Argument::is(['a' => 'b'])
)
->will(static function () use ($em) {
$qb = $em->createQueryBuilder();
return $qb->addSelect('COUNT(user.id) as export')
->from('ChillMainBundle:User', 'user');
});
//$export->initiateQuery()->shouldBeCalled();
$export->supportsModifiers()->willReturn(['foo']);
$export->requiredRole()->willReturn(new Role('CHILL_STAT_DUMMY'));
$export->getResult(Argument::Type(QueryBuilder::class), Argument::Type('array'))->willReturn([
[
'aggregator' => 'cat a',
'export' => 0,
],
[
'aggregator' => 'cat b',
'export' => 1,
],
]);
$export->getLabels(
Argument::is('export'),
Argument::is([0, 1]),
Argument::Type('array')
)
->willReturn(static function ($value) {
switch ($value) {
case 0:
case 1:
return $value;
case '_header':
return 'export';
default: throw new RuntimeException(sprintf('The value %s is not valid', $value));
}
});
$export->getQueryKeys(Argument::Type('array'))->willReturn(['export']);
$export->getTitle()->willReturn('dummy title');
$exportManager->addExport($export->reveal(), 'dummy');
$filter = $this->prophet->prophesize();
$filter->willImplement(FilterInterface::class);
//$filter->alterQuery()->shouldBeCalled();
$filter->alterQuery(Argument::Type(QueryBuilder::class), Argument::Type('array'))
->willReturn(null);
$filter->addRole()->shouldBeCalled();
//$filter->addRole()->shouldBeCalled();
$filter->applyOn()->willReturn('foo');
$filter->describeAction(Argument::cetera())->willReturn('filtered string');
$exportManager->addFilter($filter->reveal(), 'filter_foo');
$aggregator = $this->prophet->prophesize();
$aggregator->willImplement(AggregatorInterface::class);
$aggregator->applyOn()->willReturn('foo');
$aggregator->alterQuery(Argument::Type(QueryBuilder::class), Argument::Type('array'))
->willReturn(null);
//$aggregator->alterQuery()->shouldBeCalled();
$aggregator->getQueryKeys(Argument::Type('array'))->willReturn(['aggregator']);
$aggregator->getLabels(
Argument::is('aggregator'),
Argument::is(['cat a', 'cat b']),
Argument::is([])
)
->willReturn(static function ($value) {
switch ($value) {
case '_header': return 'foo_header';
case 'cat a': return 'label cat a';
case 'cat b': return 'label cat b';
default:
throw new RuntimeException(sprintf('This value (%s) is not valid', $value));
}
});
$aggregator->addRole()->willReturn(null);
//$aggregator->addRole()->shouldBeCalled();
$exportManager->addAggregator($aggregator->reveal(), 'aggregator_foo');
//add formatter interface
$formatter = new \Chill\MainBundle\Export\Formatter\SpreadSheetFormatter(
self::$container->get('translator'),
$exportManager
);
$exportManager->addFormatter($formatter, 'spreadsheet');
//ob_start();
$response = $exportManager->generate(
'dummy',
[$center],
[
ExportType::FILTER_KEY => [
'filter_foo' => [
'enabled' => true,
'form' => [],
],
],
ExportType::AGGREGATOR_KEY => [
'aggregator_foo' => [
'enabled' => true,
'form' => [],
],
],
ExportType::PICK_FORMATTER_KEY => [
'alias' => 'spreadsheet',
],
ExportType::EXPORT_KEY => [
'a' => 'b',
],
],
[
'format' => 'csv',
'aggregator_foo' => [
'order' => 1,
],
]
);
//$content = ob_get_clean();
$this->assertInstanceOf(Response::class, $response);
$expected = <<<'EOT'
"dummy title",""
"",""
"filtered string",""
"foo_header","_header"
"label cat a",""
"label cat b","1"
EOT;
$this->assertEquals($expected, $response->getContent());
}
public function testGetAggregator()
{
$exportManager = $this->createExportManager();
//create a filter and add it to ExportManager
$agg = $this->prophet->prophesize();
$agg->willImplement('Chill\MainBundle\Export\AggregatorInterface');
$exportManager->addAggregator($agg->reveal(), 'dummy');
$obtained = $exportManager->getAggregator('dummy');
$this->assertInstanceof('Chill\MainBundle\Export\AggregatorInterface', $obtained);
}
public function testGetAggregatorNonExistant()
{
$this->expectException(RuntimeException::class);
$exportManager = $this->createExportManager();
$exportManager->getAggregator('non existing');
}
public function testGetAggregators()
{
$exportManager = $this->createExportManager();
//create three filters and add them to ExportManager
$aggFoo = $this->prophet->prophesize();
$aggFoo->willImplement('Chill\MainBundle\Export\AggregatorInterface');
$aggBar = $this->prophet->prophesize();
$aggBar->willImplement('Chill\MainBundle\Export\AggregatorInterface');
$aggFooBar = $this->prophet->prophesize();
$aggFooBar->willImplement('Chill\MainBundle\Export\AggregatorInterface');
$exportManager->addAggregator($aggFoo->reveal(), 'foo');
$exportManager->addAggregator($aggBar->reveal(), 'bar');
$exportManager->addAggregator($aggFooBar->reveal(), 'foobar');
$obtained = iterator_to_array($exportManager->getAggregators(['foo', 'bar']));
$this->assertContains($aggBar->reveal(), $obtained);
$this->assertContains($aggFoo->reveal(), $obtained);
$this->assertNotContains($aggFooBar->reveal(), $obtained);
}
public function testGetExistingExportsTypes()
{
$exportManager = $this->createExportManager();
//create an export and add it to ExportManager
$export = $this->prophet->prophesize();
$export->willImplement(ExportInterface::class);
$export->getType()->willReturn('my_type');
$exportManager->addExport($export->reveal(), 'dummy');
$this->assertContains('my_type', $exportManager->getExistingExportsTypes());
}
public function testGetExport()
{
$exportManager = $this->createExportManager();
//create an export and add it to ExportManager
$export = $this->prophet->prophesize();
$export->willImplement(ExportInterface::class);
$exportManager->addExport($export->reveal(), 'dummy');
$obtained = $exportManager->getExport('dummy');
$this->assertInstanceof(ExportInterface::class, $obtained);
}
public function testGetExportNonExistant()
{
$this->expectException(RuntimeException::class);
$exportManager = $this->createExportManager();
$exportManager->getExport('non existing');
}
public function testGetExportsWithoutGranting()
{
$exportManager = $this->createExportManager();
//create an export and add it to ExportManager
$export = $this->prophet->prophesize();
$export->willImplement(ExportInterface::class);
$exportManager->addExport($export->reveal(), 'dummy');
$exports = iterator_to_array($exportManager->getExports(false));
$this->assertGreaterThan(0, count($exports));
$this->assertContains($export->reveal(), $exports);
$this->assertContains('dummy', array_keys($exports));
}
public function testGetFilter()
{
$exportManager = $this->createExportManager();
//create a filter and add it to ExportManager
$filter = $this->prophet->prophesize();
$filter->willImplement('Chill\MainBundle\Export\FilterInterface');
$exportManager->addFilter($filter->reveal(), 'dummy');
$obtained = $exportManager->getFilter('dummy');
$this->assertInstanceof('Chill\MainBundle\Export\FilterInterface', $obtained);
}
public function testGetFilterNonExistant()
{
$this->expectException(RuntimeException::class);
$exportManager = $this->createExportManager();
$exportManager->getFilter('non existing');
}
public function testGetFilters()
{
$exportManager = $this->createExportManager();
//create three filters and add them to ExportManager
$filterFoo = $this->prophet->prophesize();
$filterFoo->willImplement('Chill\MainBundle\Export\FilterInterface');
$filterBar = $this->prophet->prophesize();
$filterBar->willImplement('Chill\MainBundle\Export\FilterInterface');
$filterFooBar = $this->prophet->prophesize();
$filterFooBar->willImplement('Chill\MainBundle\Export\FilterInterface');
$exportManager->addFilter($filterFoo->reveal(), 'foo');
$exportManager->addFilter($filterBar->reveal(), 'bar');
$exportManager->addFilter($filterFooBar->reveal(), 'foobar');
$obtained = iterator_to_array($exportManager->getFilters(['foo', 'bar']));
$this->assertContains($filterBar->reveal(), $obtained);
$this->assertContains($filterFoo->reveal(), $obtained);
$this->assertNotContains($filterFooBar->reveal(), $obtained);
}
public function testGetFormatter()
{
$exportManager = $this->createExportManager();
//create a formatter
$formatter = $this->prophet->prophesize();
$formatter->willImplement('Chill\MainBundle\Export\FormatterInterface');
$exportManager->addFormatter($formatter->reveal(), 'dummy');
$obtained = $exportManager->getFormatter('dummy');
$this->assertInstanceOf('Chill\MainBundle\Export\FormatterInterface', $obtained);
}
public function testIsGrantedForElementWithExportAndUserIsGranted()
{
$center = $this->prepareCenter(100, 'center A');
$user = $this->prepareUser([]);
$authorizationChecker = $this->prophet->prophesize();
$authorizationChecker->willImplement('Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface');
$authorizationChecker->isGranted('CHILL_STAT_DUMMY', $center)
->willReturn(true);
$exportManager = $this->createExportManager(
null,
null,
$authorizationChecker->reveal(),
null,
$user
);
$export = $this->prophet->prophesize();
$export->willImplement(ExportInterface::class);
$export->requiredRole()->willReturn(new Role('CHILL_STAT_DUMMY'));
$result = $exportManager->isGrantedForElement($export->reveal(), null, [$center]);
$this->assertTrue($result);
}
public function testIsGrantedForElementWithExportAndUserIsGrantedNotForAllCenters()
{
$center = $this->prepareCenter(100, 'center A');
$centerB = $this->prepareCenter(102, 'center B');
$user = $this->prepareUser([]);
$authorizationChecker = $this->prophet->prophesize();
$authorizationChecker->willImplement('Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface');
$authorizationChecker->isGranted('CHILL_STAT_DUMMY', $center)
->willReturn(true);
$authorizationChecker->isGranted('CHILL_STAT_DUMMY', $centerB)
->willReturn(false);
$exportManager = $this->createExportManager(
null,
null,
$authorizationChecker->reveal(),
null,
$user
);
$export = $this->prophet->prophesize();
$export->willImplement(ExportInterface::class);
$export->requiredRole()->willReturn(new Role('CHILL_STAT_DUMMY'));
$result = $exportManager->isGrantedForElement($export->reveal(), null, [$center, $centerB]);
$this->assertFalse($result);
}
public function testIsGrantedForElementWithExportEmptyCenters()
{
$user = $this->prepareUser([]);
$exportManager = $this->createExportManager(
null,
null,
null,
null,
$user
);
$export = $this->prophet->prophesize();
$export->willImplement(\Chill\MainBundle\Export\ExportInterface::class);
$export->requiredRole()->willReturn(new Role('CHILL_STAT_DUMMY'));
$result = $exportManager->isGrantedForElement($export->reveal(), null, []);
$this->assertFalse($result);
}
public function testIsGrantedForElementWithModifierFallbackToExport()
{
$center = $this->prepareCenter(100, 'center A');
$centerB = $this->prepareCenter(102, 'center B');
$user = $this->prepareUser([]);
$authorizationChecker = $this->prophet->prophesize();
$authorizationChecker->willImplement(AuthorizationCheckerInterface::class);
$authorizationChecker->isGranted('CHILL_STAT_DUMMY', $center)
->willReturn(true);
$authorizationChecker->isGranted('CHILL_STAT_DUMMY', $centerB)
->willReturn(false);
$exportManager = $this->createExportManager(
null,
null,
$authorizationChecker->reveal(),
null,
$user
);
$modifier = $this->prophet->prophesize();
$modifier->willImplement(\Chill\MainBundle\Export\ModifierInterface::class);
$modifier->addRole()->willReturn(null);
$export = $this->prophet->prophesize();
$export->willImplement(ExportInterface::class);
$export->requiredRole()->willReturn(new Role('CHILL_STAT_DUMMY'));
$result = $exportManager->isGrantedForElement(
$modifier->reveal(),
$export->reveal(),
[$center, $centerB]
);
$this->assertFalse($result);
}
public function testNonExistingFormatter()
{
$this->expectException(RuntimeException::class);
$exportManager = $this->createExportManager();
$exportManager->getFormatter('non existing');
}
/**
* Create an ExportManager where every element may be replaced by a double.
*
* If null is provided for an element, this is replaced by the equivalent
* from the container; if the user provided is null, this is replaced by the
* user 'center a_social' from database.
*/
protected function createExportManager(
?LoggerInterface $logger = null,
?EntityManagerInterface $em = null,
?AuthorizationCheckerInterface $authorizationChecker = null,
?AuthorizationHelper $authorizationHelper = null,
?UserInterface $user = null
): ExportManager {
$localUser = $user ?? self::$container->get(
'doctrine.orm.entity_manager'
)
->getRepository('ChillMainBundle:User')
->findOneBy(['username' => 'center a_social']);
$token = new UsernamePasswordToken($localUser, 'password', 'provider');
$tokenStorage = new TokenStorage();
$tokenStorage->setToken($token);
return new ExportManager(
$logger ?? self::$container->get('logger'),
$em ?? self::$container->get('doctrine.orm.entity_manager'),
$authorizationChecker ?? self::$container->get('security.authorization_checker'),
$authorizationHelper ?? self::$container->get('chill.main.security.authorization.helper'),
$tokenStorage
);
}
}