diff --git a/src/Model/DashboardSection.php b/src/Model/DashboardSection.php index a9ea31ca..f998cd9a 100644 --- a/src/Model/DashboardSection.php +++ b/src/Model/DashboardSection.php @@ -11,8 +11,8 @@ namespace App\Model; class DashboardSection { - const TYPE_SIMPLE = 'simple'; - const TYPE_CHART = 'chart'; + public const TYPE_SIMPLE = 'simple'; + public const TYPE_CHART = 'chart'; /** * @var null|string @@ -54,6 +54,7 @@ class DashboardSection public function setType(string $type) { $this->type = $type; + return $this; } diff --git a/src/Repository/ActivityRepository.php b/src/Repository/ActivityRepository.php index 51802330..3f797fec 100644 --- a/src/Repository/ActivityRepository.php +++ b/src/Repository/ActivityRepository.php @@ -15,6 +15,8 @@ use App\Entity\User; use App\Model\ActivityStatistic; use App\Repository\Query\ActivityQuery; use Doctrine\ORM\Query; +use Doctrine\ORM\QueryBuilder; +use Pagerfanta\Pagerfanta; /** * Class ActivityRepository @@ -130,7 +132,7 @@ class ActivityRepository extends AbstractRepository /** * @param ActivityQuery $query - * @return \Doctrine\ORM\QueryBuilder|\Pagerfanta\Pagerfanta + * @return QueryBuilder|Pagerfanta|array */ public function findByQuery(ActivityQuery $query) { diff --git a/src/Repository/CustomerRepository.php b/src/Repository/CustomerRepository.php index cfb2e17e..c9112fc3 100644 --- a/src/Repository/CustomerRepository.php +++ b/src/Repository/CustomerRepository.php @@ -16,6 +16,8 @@ use App\Entity\Timesheet; use App\Model\CustomerStatistic; use App\Repository\Query\CustomerQuery; use Doctrine\ORM\Query; +use Doctrine\ORM\QueryBuilder; +use Pagerfanta\Pagerfanta; /** * Class CustomerRepository @@ -97,7 +99,7 @@ class CustomerRepository extends AbstractRepository /** * @param CustomerQuery $query - * @return \Doctrine\ORM\QueryBuilder|\Pagerfanta\Pagerfanta + * @return QueryBuilder|Pagerfanta|array */ public function findByQuery(CustomerQuery $query) { diff --git a/src/Repository/InvoiceTemplateRepository.php b/src/Repository/InvoiceTemplateRepository.php index 7fadf65b..0c597c08 100644 --- a/src/Repository/InvoiceTemplateRepository.php +++ b/src/Repository/InvoiceTemplateRepository.php @@ -12,6 +12,8 @@ namespace App\Repository; use App\Entity\InvoiceTemplate; use App\Repository\Query\BaseQuery; use Doctrine\ORM\Query; +use Doctrine\ORM\QueryBuilder; +use Pagerfanta\Pagerfanta; /** * Class InvoiceTemplateRepository @@ -40,7 +42,7 @@ class InvoiceTemplateRepository extends AbstractRepository /** * @param BaseQuery $query - * @return \Doctrine\ORM\QueryBuilder|\Pagerfanta\Pagerfanta + * @return QueryBuilder|Pagerfanta|array */ public function findByQuery(BaseQuery $query) { diff --git a/src/Repository/ProjectRepository.php b/src/Repository/ProjectRepository.php index 8ce7356e..c28c4fa7 100644 --- a/src/Repository/ProjectRepository.php +++ b/src/Repository/ProjectRepository.php @@ -15,6 +15,8 @@ use App\Entity\Timesheet; use App\Model\ProjectStatistic; use App\Repository\Query\ProjectQuery; use Doctrine\ORM\Query; +use Doctrine\ORM\QueryBuilder; +use Pagerfanta\Pagerfanta; /** * Class ProjectRepository @@ -90,7 +92,7 @@ class ProjectRepository extends AbstractRepository /** * @param ProjectQuery $query - * @return \Doctrine\ORM\QueryBuilder|\Pagerfanta\Pagerfanta + * @return QueryBuilder|Pagerfanta|array */ public function findByQuery(ProjectQuery $query) { diff --git a/src/Repository/Query/BaseQuery.php b/src/Repository/Query/BaseQuery.php index 1c8ff56b..d6a81c18 100644 --- a/src/Repository/Query/BaseQuery.php +++ b/src/Repository/Query/BaseQuery.php @@ -20,6 +20,7 @@ class BaseQuery public const DEFAULT_PAGESIZE = 25; public const DEFAULT_PAGE = 1; + public const RESULT_TYPE_OBJECTS = 'Objects'; public const RESULT_TYPE_PAGER = 'PagerFanta'; public const RESULT_TYPE_QUERYBUILDER = 'QueryBuilder'; @@ -141,13 +142,18 @@ class BaseQuery /** * @param string $resultType * @return $this + * @throws \InvalidArgumentException */ - public function setResultType($resultType) + public function setResultType(string $resultType) { - if (in_array($resultType, [self::RESULT_TYPE_PAGER, self::RESULT_TYPE_QUERYBUILDER])) { - $this->resultType = $resultType; + $allowed = [self::RESULT_TYPE_PAGER, self::RESULT_TYPE_QUERYBUILDER, self::RESULT_TYPE_OBJECTS]; + + if (!in_array($resultType, $allowed)) { + throw new \InvalidArgumentException('Unsupported query result type'); } + $this->resultType = $resultType; + return $this; } diff --git a/src/Repository/RepositoryException.php b/src/Repository/RepositoryException.php new file mode 100644 index 00000000..469ea0d8 --- /dev/null +++ b/src/Repository/RepositoryException.php @@ -0,0 +1,14 @@ +getResultType()) { + if (BaseQuery::RESULT_TYPE_PAGER === $query->getResultType()) { return $this->getPager($qb->getQuery(), $query->getPage(), $query->getPageSize()); + } elseif (BaseQuery::RESULT_TYPE_OBJECTS === $query->getResultType()) { + return $qb->getQuery()->execute(); } return $qb; diff --git a/src/Repository/TimesheetRepository.php b/src/Repository/TimesheetRepository.php index f24ce85f..413492f7 100644 --- a/src/Repository/TimesheetRepository.php +++ b/src/Repository/TimesheetRepository.php @@ -33,14 +33,20 @@ class TimesheetRepository extends AbstractRepository /** * @param Timesheet $entry * @return bool + * @throws RepositoryException * @throws \Doctrine\ORM\ORMException * @throws \Doctrine\ORM\OptimisticLockException */ public function stopRecording(Timesheet $entry) { + if (null !== $entry->getEnd()) { + throw new RepositoryException('Timesheet entry already stopped'); + } + // seems to be necessary so Doctrine will recognize a changed timestamp $entry->setBegin(clone $entry->getBegin()); $entry->setEnd(new DateTime()); + $entityManager = $this->getEntityManager(); $entityManager->persist($entry); $entityManager->flush(); @@ -51,7 +57,7 @@ class TimesheetRepository extends AbstractRepository /** * @param User $user * @param Activity $activity - * @return bool + * @return Timesheet * @throws \Doctrine\ORM\ORMException * @throws \Doctrine\ORM\OptimisticLockException */ @@ -67,7 +73,7 @@ class TimesheetRepository extends AbstractRepository $entityManager->persist($entry); $entityManager->flush(); - return true; + return $entry; } /** @@ -279,7 +285,7 @@ class TimesheetRepository extends AbstractRepository /** * @param TimesheetQuery $query - * @return QueryBuilder|Pagerfanta + * @return QueryBuilder|Pagerfanta|array */ public function findByQuery(TimesheetQuery $query) { diff --git a/src/Repository/WidgetRepository.php b/src/Repository/WidgetRepository.php index 85358eac..d1441b16 100644 --- a/src/Repository/WidgetRepository.php +++ b/src/Repository/WidgetRepository.php @@ -60,7 +60,7 @@ class WidgetRepository $begin = !empty($widget['begin']) ? new \DateTime($widget['begin']) : null; $end = !empty($widget['end']) ? new \DateTime($widget['end']) : null; $theUser = $widget['user'] ? $user : null; - $type = isset($widget['type']) ? $widget['type'] : Widget::TYPE_COUNTER; + $type = $widget['type'] ?? Widget::TYPE_COUNTER; $data = $this->repository->getStatistic($widget['query'], $begin, $end, $theUser); diff --git a/tests/Controller/ControllerBaseTest.php b/tests/Controller/ControllerBaseTest.php index 45bd5a64..3bb323b8 100644 --- a/tests/Controller/ControllerBaseTest.php +++ b/tests/Controller/ControllerBaseTest.php @@ -11,10 +11,7 @@ namespace App\Tests\Controller; use App\DataFixtures\UserFixtures; use App\Entity\User; -use Doctrine\Bundle\FixturesBundle\Fixture; -use Doctrine\Common\DataFixtures\Executor\ORMExecutor; -use Doctrine\Common\DataFixtures\Loader; -use Doctrine\ORM\EntityManager; +use App\Tests\KernelTestTrait; use Symfony\Bundle\FrameworkBundle\Client; use Symfony\Bundle\FrameworkBundle\Test\WebTestCase; use Symfony\Component\HttpFoundation\RedirectResponse; @@ -24,6 +21,8 @@ use Symfony\Component\HttpFoundation\RedirectResponse; */ abstract class ControllerBaseTest extends WebTestCase { + use KernelTestTrait; + public const DEFAULT_LANGUAGE = 'en'; /** @@ -213,52 +212,6 @@ abstract class ControllerBaseTest extends WebTestCase } } - /** - * @param EntityManager $em - * @param Fixture $fixture - */ - protected function importFixture(EntityManager $em, Fixture $fixture) - { - $loader = new Loader(); - $loader->addFixture($fixture); - - $executor = new ORMExecutor($em, null); - $executor->execute($loader->getFixtures(), true); - } - - /** - * @param EntityManager $em - * @param string $role - * @return User|null - */ - protected function getUserByRole(EntityManager $em, string $role = User::ROLE_USER) - { - $name = null; - - switch ($role) { - case User::ROLE_SUPER_ADMIN: - $name = UserFixtures::USERNAME_SUPER_ADMIN; - break; - - case User::ROLE_ADMIN: - $name = UserFixtures::USERNAME_ADMIN; - break; - - case User::ROLE_TEAMLEAD: - $name = UserFixtures::USERNAME_TEAMLEAD; - break; - - case User::ROLE_USER: - $name = UserFixtures::USERNAME_USER; - break; - - default: - return null; - } - - return $em->getRepository(User::class)->findOneBy(['username' => $name]); - } - /** * @param Client $client */ diff --git a/tests/KernelTestTrait.php b/tests/KernelTestTrait.php new file mode 100644 index 00000000..a2d9d679 --- /dev/null +++ b/tests/KernelTestTrait.php @@ -0,0 +1,69 @@ +addFixture($fixture); + + $executor = new ORMExecutor($em, null); + $executor->execute($loader->getFixtures(), true); + } + + /** + * @param EntityManager $em + * @param string $role + * @return User|null + */ + protected function getUserByRole(EntityManager $em, string $role = User::ROLE_USER) + { + $name = null; + + switch ($role) { + case User::ROLE_SUPER_ADMIN: + $name = UserFixtures::USERNAME_SUPER_ADMIN; + break; + + case User::ROLE_ADMIN: + $name = UserFixtures::USERNAME_ADMIN; + break; + + case User::ROLE_TEAMLEAD: + $name = UserFixtures::USERNAME_TEAMLEAD; + break; + + case User::ROLE_USER: + $name = UserFixtures::USERNAME_USER; + break; + + default: + return null; + } + + return $em->getRepository(User::class)->findOneBy(['username' => $name]); + } +} diff --git a/tests/Repository/AbstractRepositoryTest.php b/tests/Repository/AbstractRepositoryTest.php new file mode 100644 index 00000000..9582d057 --- /dev/null +++ b/tests/Repository/AbstractRepositoryTest.php @@ -0,0 +1,58 @@ +entityManager = $kernel->getContainer() + ->get('doctrine') + ->getManager(); + } + + /** + * @return EntityManager + */ + protected function getEntityManager() + { + return $this->entityManager; + } + + /** + * {@inheritdoc} + */ + protected function tearDown() + { + parent::tearDown(); + + $this->entityManager->close(); + $this->entityManager = null; // avoid memory leaks + } +} diff --git a/tests/Repository/Query/BaseQueryTest.php b/tests/Repository/Query/BaseQueryTest.php index efb8c7d4..9d001f7c 100644 --- a/tests/Repository/Query/BaseQueryTest.php +++ b/tests/Repository/Query/BaseQueryTest.php @@ -37,11 +37,18 @@ class BaseQueryTest extends TestCase { $this->assertEquals(BaseQuery::RESULT_TYPE_PAGER, $sut->getResultType()); - $sut->setResultType('foo-bar'); - $this->assertEquals(BaseQuery::RESULT_TYPE_PAGER, $sut->getResultType()); - $sut->setResultType(BaseQuery::RESULT_TYPE_QUERYBUILDER); $this->assertEquals(BaseQuery::RESULT_TYPE_QUERYBUILDER, $sut->getResultType()); + + $sut->setResultType(BaseQuery::RESULT_TYPE_OBJECTS); + $this->assertEquals(BaseQuery::RESULT_TYPE_OBJECTS, $sut->getResultType()); + + try { + $sut->setResultType('foo-bar'); + } catch (\Exception $exception) { + $this->assertInstanceOf(\InvalidArgumentException::class, $exception); + $this->assertEquals('Unsupported query result type', $exception->getMessage()); + } } protected function assertHiddenEntity(BaseQuery $sut) diff --git a/tests/Repository/TimesheetRepositoryTest.php b/tests/Repository/TimesheetRepositoryTest.php new file mode 100644 index 00000000..1b92fce8 --- /dev/null +++ b/tests/Repository/TimesheetRepositoryTest.php @@ -0,0 +1,102 @@ +getEntityManager(); + $repository = $em->getRepository(Timesheet::class); + + $query = new TimesheetQuery(); + + $result = $repository->findByQuery($query); + $this->assertInstanceOf(Pagerfanta::class, $result); + + $query->setResultType(BaseQuery::RESULT_TYPE_QUERYBUILDER); + $result = $repository->findByQuery($query); + $this->assertInstanceOf(QueryBuilder::class, $result); + + $query->setResultType(BaseQuery::RESULT_TYPE_PAGER); + $result = $repository->findByQuery($query); + $this->assertInstanceOf(Pagerfanta::class, $result); + + $query->setResultType(BaseQuery::RESULT_TYPE_OBJECTS); + $result = $repository->findByQuery($query); + $this->assertInternalType('array', $result); + } + + public function testStoppedEntriesCannotBeStoppedAgain() + { + $em = $this->getEntityManager(); + $user = $this->getUserByRole($em, User::ROLE_USER); + $repository = $em->getRepository(Timesheet::class); + + $fixtures = new TimesheetFixtures(); + $fixtures->setUser($user); + $fixtures->setAmount(1); + + $this->importFixture($em, $fixtures); + + $query = new TimesheetQuery(); + $query->setResultType(BaseQuery::RESULT_TYPE_OBJECTS); + $query->setUser($user); + $query->setState(TimesheetQuery::STATE_STOPPED); + + $entities = $repository->findByQuery($query); + + $this->assertCount(1, $entities); + $this->assertInstanceOf(Timesheet::class, $entities[0]); + + $this->expectException(RepositoryException::class); + $this->expectExceptionMessage('Timesheet entry already stopped'); + + $repository->stopRecording($entities[0]); + } + + public function testStartAndStop() + { + $em = $this->getEntityManager(); + $user = $this->getUserByRole($em, User::ROLE_USER); + $repository = $em->getRepository(Timesheet::class); + + $fixtures = new TimesheetFixtures(); + $fixtures->setUser($user); + $fixtures->setAmount(1); + $this->importFixture($em, $fixtures); + + $query = new TimesheetQuery(); + $query->setResultType(BaseQuery::RESULT_TYPE_OBJECTS); + $entities = $repository->findByQuery($query); + $activity = $entities[0]->getActivity(); + + $user = $this->getUserByRole($em, User::ROLE_USER); + $timesheet = $repository->startRecording($user, $activity); + $this->assertInstanceOf(Timesheet::class, $timesheet); + $this->assertNull($timesheet->getEnd()); + + $result = $repository->stopRecording($timesheet); + $this->assertTrue($result); + $this->assertInstanceOf(\DateTime::class, $timesheet->getEnd()); + } +}