added team permissions (#996)

This commit is contained in:
Kevin Papst
2019-08-16 01:22:33 +02:00
committed by GitHub
parent 9611646bc6
commit 561ec3b1e9
124 changed files with 4122 additions and 362 deletions

View File

@@ -0,0 +1,102 @@
<?php
/*
* This file is part of the Kimai time-tracking app.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace App\Tests\API;
use App\Entity\User;
use App\Tests\DataFixtures\TeamFixtures;
use Symfony\Component\HttpFoundation\Response;
/**
* @group integration
*/
class TeamControllerTest extends APIControllerBaseTest
{
protected function setUp(): void
{
$client = $this->getClientForAuthenticatedUser(User::ROLE_ADMIN);
$em = $client->getContainer()->get('doctrine.orm.entity_manager');
$fixture = new TeamFixtures();
$fixture->setAmount(1);
$this->importFixture($em, $fixture);
}
public function testIsSecure()
{
$this->assertUrlIsSecured('/api/teams');
}
public function testGetCollection()
{
$client = $this->getClientForAuthenticatedUser(User::ROLE_ADMIN);
$this->assertAccessIsGranted($client, '/api/teams');
$result = json_decode($client->getResponse()->getContent(), true);
$this->assertIsArray($result);
$this->assertNotEmpty($result);
$this->assertEquals(1, count($result));
$this->assertStructure($result[0], false);
}
public function testGetEntity()
{
$client = $this->getClientForAuthenticatedUser(User::ROLE_ADMIN);
$this->assertAccessIsGranted($client, '/api/teams/1');
$result = json_decode($client->getResponse()->getContent(), true);
$this->assertIsArray($result);
$this->assertStructure($result, true);
}
public function testNotFound()
{
$this->assertEntityNotFound(User::ROLE_USER, '/api/teams/2');
}
public function testDeleteActionWithUnknownTeam()
{
$this->assertEntityNotFoundForDelete(User::ROLE_ADMIN, '/api/teams/255', []);
}
public function testDeleteAction()
{
$client = $this->getClientForAuthenticatedUser(User::ROLE_ADMIN);
$this->assertAccessIsGranted($client, '/api/teams/1');
$result = json_decode($client->getResponse()->getContent(), true);
$this->assertIsArray($result);
$this->assertStructure($result);
$this->assertNotEmpty($result['id']);
$id = $result['id'];
$this->request($client, '/api/teams/' . $id, 'DELETE');
$this->assertTrue($client->getResponse()->isSuccessful());
$this->assertEquals(Response::HTTP_NO_CONTENT, $client->getResponse()->getStatusCode());
$this->assertEmpty($client->getResponse()->getContent());
$this->assertEntityNotFound(User::ROLE_USER, '/api/teams/' . $id);
}
protected function assertStructure(array $result, $full = true)
{
$expectedKeys = [
'id', 'name'
];
if ($full) {
$expectedKeys = array_merge($expectedKeys, []);
}
$actual = array_keys($result);
sort($actual);
sort($expectedKeys);
$this->assertEquals($expectedKeys, $actual, 'Team structure does not match');
}
}

View File

@@ -26,12 +26,12 @@ class ActivityControllerTest extends ControllerBaseTest
public function testIsSecure()
{
$this->assertUrlIsSecured('/admin/activity/');
$this->assertUrlIsSecuredForRole(User::ROLE_TEAMLEAD, '/admin/activity/');
$this->assertUrlIsSecuredForRole(User::ROLE_USER, '/admin/activity/');
}
public function testIndexAction()
{
$client = $this->getClientForAuthenticatedUser(User::ROLE_ADMIN);
$client = $this->getClientForAuthenticatedUser(User::ROLE_TEAMLEAD);
$this->assertAccessIsGranted($client, '/admin/activity/');
$this->assertHasDataTable($client);
}

View File

@@ -9,13 +9,16 @@
namespace App\Tests\Controller;
use App\Entity\Customer;
use App\Entity\Project;
use App\Entity\Timesheet;
use App\Entity\User;
use App\Tests\DataFixtures\CustomerFixtures;
use App\Tests\DataFixtures\TeamFixtures;
use App\Tests\DataFixtures\TimesheetFixtures;
use App\Tests\Mocks\CustomerTestMetaFieldSubscriberMock;
use Doctrine\ORM\EntityManager;
use Symfony\Component\DomCrawler\Field\ChoiceFormField;
/**
* @group integration
@@ -25,12 +28,12 @@ class CustomerControllerTest extends ControllerBaseTest
public function testIsSecure()
{
$this->assertUrlIsSecured('/admin/customer/');
$this->assertUrlIsSecuredForRole(User::ROLE_TEAMLEAD, '/admin/customer/');
$this->assertUrlIsSecuredForRole(User::ROLE_USER, '/admin/customer/');
}
public function testIndexAction()
{
$client = $this->getClientForAuthenticatedUser(User::ROLE_ADMIN);
$client = $this->getClientForAuthenticatedUser(User::ROLE_TEAMLEAD);
$this->assertAccessIsGranted($client, '/admin/customer/');
$this->assertHasDataTable($client);
}
@@ -111,6 +114,39 @@ class CustomerControllerTest extends ControllerBaseTest
$this->assertEquals('Test Customer 2', $editForm->get('customer_edit_form[name]')->getValue());
}
public function testTeamPermissionAction()
{
$client = $this->getClientForAuthenticatedUser(User::ROLE_ADMIN);
$em = $client->getContainer()->get('doctrine.orm.entity_manager');
/** @var Customer $customer */
$customer = $em->getRepository(Customer::class)->find(1);
self::assertEquals(0, $customer->getTeams()->count());
$fixture = new TeamFixtures();
$fixture->setAmount(2);
$fixture->setAddCustomer(false);
$this->importFixture($em, $fixture);
$this->assertAccessIsGranted($client, '/admin/customer/1/permissions');
$form = $client->getCrawler()->filter('form[name=customer_team_permission_form]')->form();
/** @var ChoiceFormField $team1 */
$team1 = $form->get('customer_team_permission_form[teams][0]');
$team1->tick();
/** @var ChoiceFormField $team2 */
$team2 = $form->get('customer_team_permission_form[teams][1]');
$team2->tick();
$client->submit($form);
$this->assertIsRedirect($client, $this->createUrl('/admin/customer/'));
$client->followRedirect();
$this->assertHasDataTable($client);
/** @var Customer $customer */
$customer = $em->getRepository(Customer::class)->find(1);
self::assertEquals(2, $customer->getTeams()->count());
}
public function testDeleteAction()
{
$client = $this->getClientForAuthenticatedUser(User::ROLE_ADMIN);

View File

@@ -12,8 +12,10 @@ namespace App\Tests\Controller;
use App\DataFixtures\UserFixtures;
use App\Entity\User;
use App\Entity\UserPreference;
use App\Tests\DataFixtures\TeamFixtures;
use App\Tests\DataFixtures\TimesheetFixtures;
use Symfony\Bundle\FrameworkBundle\Client;
use Symfony\Component\DomCrawler\Field\ChoiceFormField;
use Symfony\Component\Security\Core\Encoder\EncoderFactoryInterface;
/**
@@ -95,7 +97,7 @@ class ProfileControllerTest extends ControllerBaseTest
return [
[User::ROLE_USER, UserFixtures::USERNAME_USER, ['#settings', '#password', '#api-token']],
[User::ROLE_SUPER_ADMIN, UserFixtures::USERNAME_SUPER_ADMIN, array_merge($userTabs, ['#roles'])],
[User::ROLE_SUPER_ADMIN, UserFixtures::USERNAME_SUPER_ADMIN, array_merge($userTabs, ['#teams', '#roles'])],
];
}
@@ -315,6 +317,52 @@ class ProfileControllerTest extends ControllerBaseTest
$this->assertEquals(['ROLE_TEAMLEAD', 'ROLE_SUPER_ADMIN', 'ROLE_USER'], $user->getRoles());
}
public function testTeamsActionIsSecured()
{
$this->assertUrlIsSecured('/profile/' . UserFixtures::USERNAME_USER . '/teams');
$this->assertUrlIsSecuredForRole(User::ROLE_TEAMLEAD, '/profile/' . UserFixtures::USERNAME_USER . '/teams');
}
public function testTeamsAction()
{
$client = $this->getClientForAuthenticatedUser(User::ROLE_SUPER_ADMIN);
$em = $client->getContainer()->get('doctrine.orm.entity_manager');
/** @var User $user */
$user = $this->getUserByRole($em, User::ROLE_USER);
$fixture = new TeamFixtures();
$fixture->setAmount(3);
$fixture->setAddCustomer(true);
$fixture->setAddUser(false);
$fixture->addUserToIgnore($user);
$this->importFixture($em, $fixture);
$this->request($client, '/profile/' . UserFixtures::USERNAME_USER . '/teams');
/** @var User $user */
$user = $this->getUserByRole($em, User::ROLE_USER);
$this->assertEquals([], $user->getTeams()->toArray());
$form = $client->getCrawler()->filter('form[name=user_teams]')->form();
/** @var ChoiceFormField $team */
$team = $form->get('user_teams[teams][0]');
$team->tick();
$client->submit($form);
$this->assertIsRedirect($client, $this->createUrl('/profile/' . urlencode(UserFixtures::USERNAME_USER) . '/teams'));
$client->followRedirect();
$this->assertTrue($client->getResponse()->isSuccessful());
$this->assertHasFlashSuccess($client);
$em = $client->getContainer()->get('doctrine.orm.entity_manager');
$user = $this->getUserByRole($em, User::ROLE_USER);
$this->assertEquals(1, $user->getTeams()->count());
}
public function getPreferencesTestData()
{
return [

View File

@@ -14,9 +14,11 @@ use App\Entity\Timesheet;
use App\Entity\User;
use App\Tests\DataFixtures\CustomerFixtures;
use App\Tests\DataFixtures\ProjectFixtures;
use App\Tests\DataFixtures\TeamFixtures;
use App\Tests\DataFixtures\TimesheetFixtures;
use App\Tests\Mocks\ProjectTestMetaFieldSubscriberMock;
use Doctrine\ORM\EntityManager;
use Symfony\Component\DomCrawler\Field\ChoiceFormField;
/**
* @group integration
@@ -26,12 +28,12 @@ class ProjectControllerTest extends ControllerBaseTest
public function testIsSecure()
{
$this->assertUrlIsSecured('/admin/project/');
$this->assertUrlIsSecuredForRole(User::ROLE_TEAMLEAD, '/admin/project/');
$this->assertUrlIsSecuredForRole(User::ROLE_USER, '/admin/project/');
}
public function testIndexAction()
{
$client = $this->getClientForAuthenticatedUser(User::ROLE_ADMIN);
$client = $this->getClientForAuthenticatedUser(User::ROLE_TEAMLEAD);
$this->assertAccessIsGranted($client, '/admin/project/');
$this->assertHasDataTable($client);
}
@@ -135,6 +137,39 @@ class ProjectControllerTest extends ControllerBaseTest
$this->assertEquals('Test 2', $editForm->get('project_edit_form[name]')->getValue());
}
public function testTeamPermissionAction()
{
$client = $this->getClientForAuthenticatedUser(User::ROLE_ADMIN);
$em = $client->getContainer()->get('doctrine.orm.entity_manager');
/** @var Project $project */
$project = $em->getRepository(Project::class)->find(1);
self::assertEquals(0, $project->getTeams()->count());
$fixture = new TeamFixtures();
$fixture->setAmount(2);
$fixture->setAddCustomer(false);
$this->importFixture($em, $fixture);
$this->assertAccessIsGranted($client, '/admin/project/1/permissions');
$form = $client->getCrawler()->filter('form[name=project_team_permission_form]')->form();
/** @var ChoiceFormField $team1 */
$team1 = $form->get('project_team_permission_form[teams][0]');
$team1->tick();
/** @var ChoiceFormField $team2 */
$team2 = $form->get('project_team_permission_form[teams][1]');
$team2->tick();
$client->submit($form);
$this->assertIsRedirect($client, $this->createUrl('/admin/project/'));
$client->followRedirect();
$this->assertHasDataTable($client);
/** @var Project $project */
$project = $em->getRepository(Project::class)->find(1);
self::assertEquals(2, $project->getTeams()->count());
}
public function testDeleteAction()
{
$client = $this->getClientForAuthenticatedUser(User::ROLE_ADMIN);

View File

@@ -0,0 +1,155 @@
<?php
/*
* This file is part of the Kimai time-tracking app.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace App\Tests\Controller;
use App\Entity\Team;
use App\Entity\User;
use App\Tests\DataFixtures\TeamFixtures;
use Doctrine\ORM\EntityManager;
use Symfony\Bundle\FrameworkBundle\Client;
use Symfony\Component\DomCrawler\Field\ChoiceFormField;
/**
* @group integration
*/
class TeamControllerTest extends ControllerBaseTest
{
public function testIsSecure()
{
$this->assertUrlIsSecured('/admin/teams/');
$this->assertUrlIsSecuredForRole(User::ROLE_TEAMLEAD, '/admin/teams/');
}
public function testIndexAction()
{
$client = $this->getClientForAuthenticatedUser(User::ROLE_ADMIN);
$em = $client->getContainer()->get('doctrine.orm.entity_manager');
$fixture = new TeamFixtures();
$fixture->setAmount(5);
$this->importFixture($em, $fixture);
$client = $this->getClientForAuthenticatedUser(User::ROLE_ADMIN);
$this->assertAccessIsGranted($client, '/admin/teams/');
$this->assertPageActions($client, ['create' => $this->createUrl('/admin/teams/create')]);
$this->assertHasDataTable($client);
$this->assertDataTableRowCount($client, 'datatable_admin_teams', 5);
}
public function testCreateAction()
{
$client = $this->getClientForAuthenticatedUser(User::ROLE_ADMIN);
$this->assertAccessIsGranted($client, '/admin/teams/create');
$form = $client->getCrawler()->filter('form[name=team_edit_form]')->form();
$editForm = $client->getCrawler()->filter('form[name=team_edit_form]')->form();
$this->assertEquals('', $editForm->get('team_edit_form[name]')->getValue());
$this->assertEquals('5', $editForm->get('team_edit_form[teamlead]')->getValue());
$client->submit($form, [
'team_edit_form' => [
'name' => 'Test Team',
]
]);
$this->assertIsRedirect($client, $this->createUrl('/admin/teams/1/edit'));
$client->followRedirect();
$this->assertHasFlashSuccess($client);
$this->assertHasCustomerAndProjectPermissionBoxes($client);
}
protected function assertHasCustomerAndProjectPermissionBoxes(Client $client)
{
$content = $client->getResponse()->getContent();
$this->assertStringContainsString('Grant access to customers', $content);
$this->assertStringContainsString('Grant access to projects', $content);
$this->assertEquals(1, $client->getCrawler()->filter('form[name=team_customer_form]')->count());
$this->assertEquals(1, $client->getCrawler()->filter('form[name=team_project_form]')->count());
}
public function testEditAction()
{
$client = $this->getClientForAuthenticatedUser(User::ROLE_ADMIN);
$em = $client->getContainer()->get('doctrine.orm.entity_manager');
$fixture = new TeamFixtures();
$fixture->setAmount(2);
$this->importFixture($em, $fixture);
$this->assertAccessIsGranted($client, '/admin/teams/1/edit');
$form = $client->getCrawler()->filter('form[name=team_edit_form]')->form();
$this->assertNotEmpty($form->get('team_edit_form[name]')->getValue());
$client->submit($form, [
'team_edit_form' => [
'name' => 'Test Team 2'
]
]);
$this->assertIsRedirect($client, $this->createUrl('/admin/teams/1/edit'));
$client->followRedirect();
$editForm = $client->getCrawler()->filter('form[name=team_edit_form]')->form();
$this->assertEquals('Test Team 2', $editForm->get('team_edit_form[name]')->getValue());
}
public function testEditCustomerAccessAction()
{
$client = $this->getClientForAuthenticatedUser(User::ROLE_ADMIN);
/** @var EntityManager $em */
$em = $client->getContainer()->get('doctrine.orm.entity_manager');
$fixture = new TeamFixtures();
$fixture->setAmount(2);
$fixture->setAddCustomer(false);
$this->importFixture($em, $fixture);
$team = $em->getRepository(Team::class)->find(1);
self::assertEquals(0, count($team->getCustomers()));
$this->assertAccessIsGranted($client, '/admin/teams/1/edit');
$form = $client->getCrawler()->filter('form[name=team_customer_form]')->form();
/** @var ChoiceFormField $customer */
$customer = $form->get('team_customer_form[customers][0]');
$customer->tick();
$client->submit($form);
$this->assertIsRedirect($client, $this->createUrl('/admin/teams/1/edit'));
$team = $em->getRepository(Team::class)->find(1);
self::assertEquals(1, count($team->getCustomers()));
}
public function testEditProjectAccessAction()
{
$client = $this->getClientForAuthenticatedUser(User::ROLE_ADMIN);
/** @var EntityManager $em */
$em = $client->getContainer()->get('doctrine.orm.entity_manager');
$fixture = new TeamFixtures();
$fixture->setAmount(2);
$fixture->setAddCustomer(false);
$this->importFixture($em, $fixture);
$team = $em->getRepository(Team::class)->find(1);
self::assertEquals(0, count($team->getProjects()));
$this->assertAccessIsGranted($client, '/admin/teams/1/edit');
$form = $client->getCrawler()->filter('form[name=team_project_form]')->form();
/** @var ChoiceFormField $customer */
$customer = $form->get('team_project_form[projects]');
$customer->select([1]);
$client->submit($form);
$this->assertIsRedirect($client, $this->createUrl('/admin/teams/1/edit'));
$team = $em->getRepository(Team::class)->find(1);
self::assertEquals(1, count($team->getProjects()));
}
}

View File

@@ -47,7 +47,8 @@ class TimesheetTeamControllerTest extends ControllerBaseTest
public function testIndexActionWithQuery()
{
$client = $this->getClientForAuthenticatedUser(User::ROLE_TEAMLEAD);
// Switching the user is not allowed for TEAMLEADs but ONLLY for admin and super-admins
$client = $this->getClientForAuthenticatedUser(User::ROLE_ADMIN);
$start = new \DateTime('first day of this month');
$em = $client->getContainer()->get('doctrine.orm.entity_manager');
@@ -118,7 +119,7 @@ class TimesheetTeamControllerTest extends ControllerBaseTest
public function testCreateAction()
{
$client = $this->getClientForAuthenticatedUser(User::ROLE_TEAMLEAD);
$client = $this->getClientForAuthenticatedUser(User::ROLE_ADMIN);
$this->request($client, '/team/timesheet/create');
$this->assertTrue($client->getResponse()->isSuccessful());
@@ -160,7 +161,7 @@ class TimesheetTeamControllerTest extends ControllerBaseTest
$fixture->setStartDate('2017-05-01');
$this->importFixture($em, $fixture);
$client = $this->getClientForAuthenticatedUser(User::ROLE_TEAMLEAD);
$client = $this->getClientForAuthenticatedUser(User::ROLE_ADMIN);
$this->request($client, '/team/timesheet/1/edit');
$response = $client->getResponse();

View File

@@ -52,7 +52,7 @@ class UserControllerTest extends ControllerBaseTest
$this->assertIsRedirect($client, $this->createUrl('/profile/' . urlencode($username) . '/edit'));
$client->followRedirect();
$expectedTabs = ['#settings', '#password', '#api-token', '#roles'];
$expectedTabs = ['#settings', '#password', '#api-token', '#teams', '#roles'];
$tabs = $client->getCrawler()->filter('div.nav-tabs-custom ul.nav-tabs li');
$this->assertEquals(count($expectedTabs), $tabs->count());
@@ -210,6 +210,6 @@ class UserControllerTest extends ControllerBaseTest
$client = $this->getClientForAuthenticatedUser(User::ROLE_SUPER_ADMIN);
$this->assertAccessIsGranted($client, '/admin/user/permissions');
$this->assertHasDataTable($client);
$this->assertDataTableRowCount($client, 'datatable_user_admin_permissions', 69);
$this->assertDataTableRowCount($client, 'datatable_user_admin_permissions', 84);
}
}

View File

@@ -0,0 +1,144 @@
<?php
/*
* This file is part of the Kimai time-tracking app.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace App\Tests\DataFixtures;
use App\Entity\Customer;
use App\Entity\Team;
use App\Entity\User;
use Doctrine\Bundle\FixturesBundle\Fixture;
use Doctrine\Common\Persistence\ObjectManager;
use Faker\Factory;
/**
* Defines the sample data to load in during controller tests.
*/
class TeamFixtures extends Fixture
{
/**
* @var int
*/
protected $amount = 0;
/**
* @var bool
*/
protected $addCustomer = true;
/**
* @var User[]
*/
protected $skipUser = [];
/**
* @var bool
*/
protected $addUser = true;
public function setAddCustomer(bool $useCustomer)
{
$this->addCustomer = $useCustomer;
}
public function setAddUser(bool $useUser)
{
$this->addUser = $useUser;
}
public function getAmount(): int
{
return $this->amount;
}
public function setAmount(int $amount): TeamFixtures
{
$this->amount = $amount;
return $this;
}
public function addUserToIgnore(User $user)
{
$this->skipUser[] = $user;
}
/**
* {@inheritdoc}
*/
public function load(ObjectManager $manager)
{
$faker = Factory::create();
$user = $this->getAllUsers($manager);
$customer = $this->getAllCustomers($manager);
for ($i = 0; $i < $this->amount; $i++) {
$lead = null;
while (null === $lead) {
$tmp = $user[array_rand($user)];
if (!in_array($tmp, $this->skipUser)) {
$lead = $tmp;
}
}
$entity = new Team();
$entity
->setName($faker->name)
->setTeamLead($lead)
;
if ($this->addUser) {
$userToAdd = null;
while (null === $userToAdd) {
$tmp = $user[array_rand($user)];
if (!in_array($tmp, $this->skipUser)) {
$userToAdd = $tmp;
}
}
$entity->addUser($userToAdd);
}
if ($this->addCustomer) {
$entity->addCustomer($customer[array_rand($customer)]);
}
$manager->persist($entity);
}
$manager->flush();
}
/**
* @param ObjectManager $manager
* @return Customer[]
*/
protected function getAllCustomers(ObjectManager $manager)
{
$all = [];
/* @var Customer[] $entries */
$entries = $manager->getRepository(Customer::class)->findAll();
foreach ($entries as $temp) {
$all[$temp->getId()] = $temp;
}
return $all;
}
/**
* @param ObjectManager $manager
* @return User[]
*/
protected function getAllUsers(ObjectManager $manager)
{
$all = [];
/* @var User[] $entries */
$entries = $manager->getRepository(User::class)->findAll();
foreach ($entries as $temp) {
$all[$temp->getId()] = $temp;
}
return $all;
}
}

View File

@@ -95,6 +95,7 @@ class AppExtensionTest extends TestCase
'timezone' => null,
'language' => 'en',
'theme' => null,
'currency' => 'EUR',
]
],

View File

@@ -11,6 +11,7 @@ namespace App\Tests\Entity;
use App\Entity\Customer;
use App\Entity\CustomerMeta;
use App\Entity\Team;
use Doctrine\Common\Collections\Collection;
use PHPUnit\Framework\TestCase;
@@ -49,6 +50,8 @@ class CustomerTest extends TestCase
$this->assertInstanceOf(Collection::class, $sut->getMetaFields());
$this->assertEquals(0, $sut->getMetaFields()->count());
$this->assertNull($sut->getMetaField('foo'));
$this->assertInstanceOf(Collection::class, $sut->getTeams());
$this->assertEquals(0, $sut->getTeams()->count());
}
public function testSetterAndGetter()
@@ -127,4 +130,23 @@ class CustomerTest extends TestCase
self::assertEquals(3, $sut->getMetaFields()->count());
self::assertCount(2, $sut->getVisibleMetaFields());
}
public function testTeams()
{
$sut = new Customer();
$team = new Team();
self::assertEmpty($sut->getTeams());
self::assertEmpty($team->getCustomers());
$sut->addTeam($team);
self::assertCount(1, $sut->getTeams());
self::assertCount(1, $team->getCustomers());
self::assertSame($team, $sut->getTeams()[0]);
self::assertSame($sut, $team->getCustomers()[0]);
$sut->removeTeam(new Team());
$sut->removeTeam($team);
self::assertCount(0, $sut->getTeams());
self::assertCount(0, $team->getCustomers());
}
}

View File

@@ -12,6 +12,7 @@ namespace App\Tests\Entity;
use App\Entity\Customer;
use App\Entity\Project;
use App\Entity\ProjectMeta;
use App\Entity\Team;
use Doctrine\Common\Collections\Collection;
use PHPUnit\Framework\TestCase;
@@ -37,6 +38,8 @@ class ProjectTest extends TestCase
$this->assertInstanceOf(Collection::class, $sut->getMetaFields());
$this->assertEquals(0, $sut->getMetaFields()->count());
$this->assertNull($sut->getMetaField('foo'));
$this->assertInstanceOf(Collection::class, $sut->getTeams());
$this->assertEquals(0, $sut->getTeams()->count());
}
public function testSetterAndGetter()
@@ -101,4 +104,22 @@ class ProjectTest extends TestCase
self::assertEquals(3, $sut->getMetaFields()->count());
self::assertCount(2, $sut->getVisibleMetaFields());
}
public function testTeams()
{
$sut = new Project();
$team = new Team();
self::assertEmpty($sut->getTeams());
self::assertEmpty($team->getProjects());
$sut->addTeam($team);
self::assertCount(1, $sut->getTeams());
self::assertCount(1, $team->getProjects());
self::assertSame($team, $sut->getTeams()[0]);
self::assertSame($sut, $team->getProjects()[0]);
$sut->removeTeam($team);
self::assertCount(0, $sut->getTeams());
self::assertCount(0, $team->getProjects());
}
}

108
tests/Entity/TeamTest.php Normal file
View File

@@ -0,0 +1,108 @@
<?php
/*
* This file is part of the Kimai time-tracking app.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace App\Tests\Entity;
use App\Entity\Customer;
use App\Entity\Project;
use App\Entity\Team;
use App\Entity\User;
use Doctrine\Common\Collections\Collection;
use PHPUnit\Framework\TestCase;
/**
* @covers \App\Entity\Team
*/
class TeamTest extends TestCase
{
public function testDefaultValues()
{
$sut = new Team();
self::assertNull($sut->getId());
self::assertNull($sut->getName());
self::assertNull($sut->getTeamLead());
self::assertInstanceOf(Collection::class, $sut->getUsers());
self::assertEquals(0, $sut->getUsers()->count());
self::assertInstanceOf(Collection::class, $sut->getCustomers());
self::assertEquals(0, $sut->getCustomers()->count());
self::assertInstanceOf(Collection::class, $sut->getProjects());
self::assertEquals(0, $sut->getProjects()->count());
}
public function testSetterAndGetter()
{
$sut = new Team();
self::assertInstanceOf(Team::class, $sut->setName('foo-bar'));
self::assertEquals('foo-bar', $sut->getName());
self::assertEquals('foo-bar', (string) $sut);
$user = (new User())->setAlias('Foo!');
self::assertInstanceOf(Team::class, $sut->setTeamLead($user));
self::assertSame($user, $sut->getTeamLead());
self::assertFalse($sut->isTeamlead(new User()));
self::assertTrue($sut->isTeamlead($user));
}
public function testCustomer()
{
$customer = new Customer();
$customer->setName('foo');
self::assertEmpty($customer->getTeams());
$sut = new Team();
$sut->addCustomer($customer);
self::assertEquals(1, $sut->getCustomers()->count());
$actual = $sut->getCustomers()[0];
self::assertSame($actual, $customer);
self::assertSame($sut, $customer->getTeams()[0]);
$sut->removeCustomer(new Customer());
self::assertEquals(1, $sut->getCustomers()->count());
$sut->removeCustomer($customer);
self::assertEquals(0, $sut->getCustomers()->count());
}
public function testProject()
{
$project = new Project();
$project->setName('foo');
self::assertEmpty($project->getTeams());
$sut = new Team();
$sut->addProject($project);
self::assertEquals(1, $sut->getProjects()->count());
$actual = $sut->getProjects()[0];
self::assertSame($actual, $project);
self::assertSame($sut, $project->getTeams()[0]);
$sut->removeProject(new Project());
self::assertEquals(1, $sut->getProjects()->count());
$sut->removeProject($project);
self::assertEquals(0, $sut->getProjects()->count());
}
public function testUsers()
{
$user = new User();
$user->setAlias('foo');
self::assertEmpty($user->getTeams());
$sut = new Team();
$sut->addUser($user);
self::assertEquals(1, $sut->getUsers()->count());
$actual = $sut->getUsers()[0];
self::assertSame($actual, $user);
self::assertSame($sut, $user->getTeams()[0]);
self::assertFalse($sut->hasUser(new User()));
self::assertTrue($sut->hasUser($user));
$sut->removeUser(new User());
self::assertEquals(1, $sut->getUsers()->count());
$sut->removeUser($user);
self::assertEquals(0, $sut->getUsers()->count());
}
}

View File

@@ -9,6 +9,7 @@
namespace App\Tests\Entity;
use App\Entity\Team;
use App\Entity\User;
use App\Entity\UserPreference;
use Doctrine\Common\Collections\ArrayCollection;
@@ -97,4 +98,44 @@ class UserTest extends TestCase
$this->assertEquals('fr', $sut->getLocale());
}
public function testTeams()
{
$sut = new User();
$team = new Team();
self::assertEmpty($sut->getTeams());
self::assertEmpty($team->getUsers());
$sut->addTeam($team);
self::assertCount(1, $sut->getTeams());
self::assertSame($team, $sut->getTeams()[0]);
self::assertSame($sut, $team->getUsers()[0]);
self::assertFalse($sut->isTeamleadOf($team));
self::assertTrue($sut->isInTeam($team));
$team2 = new Team();
self::assertFalse($sut->isInTeam($team2));
self::assertFalse($sut->isTeamleadOf($team2));
$team2->setTeamLead($sut);
self::assertTrue($sut->isTeamleadOf($team2));
self::assertTrue($sut->isInTeam($team2));
$sut->removeTeam(new Team());
self::assertCount(2, $sut->getTeams());
$sut->removeTeam($team);
self::assertCount(1, $sut->getTeams());
$sut->removeTeam($team2);
self::assertCount(0, $sut->getTeams());
}
public function testRoles()
{
$sut = new User();
self::assertFalse($sut->isTeamlead());
$sut->addRole(User::ROLE_ADMIN);
self::assertFalse($sut->isTeamlead());
$sut->addRole(User::ROLE_TEAMLEAD);
self::assertTrue($sut->isTeamlead());
}
}

View File

@@ -17,13 +17,18 @@ abstract class AbstractMockFactory
/**
* @var TestCase
*/
protected $testCase;
private $testCase;
public function __construct(TestCase $testCase)
{
$this->testCase = $testCase;
}
protected function getTestCase(): TestCase
{
return $this->testCase;
}
protected function getMockBuilder(string $className): MockBuilder
{
return new MockBuilder($this->testCase, $className);

View File

@@ -0,0 +1,46 @@
<?php
/*
* This file is part of the Kimai time-tracking app.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace App\Tests\Mocks\Security;
use App\Entity\User;
use App\Entity\UserPreference;
use App\Repository\UserRepository;
use App\Security\CurrentUser;
use App\Tests\Mocks\AbstractMockFactory;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorage;
use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken;
class CurrentUserFactory extends AbstractMockFactory
{
public function create(User $user, ?string $timezone = null): CurrentUser
{
return $this->getCurrentUserMock($user, $timezone);
}
protected function getCurrentUserMock(User $user, ?string $timezone = null)
{
if (null !== $timezone) {
$pref = new UserPreference();
$pref->setName('timezone');
$pref->setValue($timezone);
$user->addPreference($pref);
}
$repository = $this->getMockBuilder(UserRepository::class)->setMethods(['getUserById'])->disableOriginalConstructor()->getMock();
$repository->expects(TestCase::atMost(1))->method('getUserById')->willReturn($user);
$token = $this->getMockBuilder(UsernamePasswordToken::class)->setMethods(['getUser'])->disableOriginalConstructor()->getMock();
$token->expects(TestCase::atLeast(1))->method('getUser')->willReturn($user);
$tokenStorage = new TokenStorage();
$tokenStorage->setToken($token);
return new CurrentUser($tokenStorage, $repository);
}
}

View File

@@ -10,38 +10,16 @@
namespace App\Tests\Mocks\Security;
use App\Entity\User;
use App\Entity\UserPreference;
use App\Repository\UserRepository;
use App\Security\CurrentUser;
use App\Tests\Mocks\AbstractMockFactory;
use App\Timesheet\UserDateTimeFactory;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorage;
use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken;
class UserDateTimeFactoryFactory extends AbstractMockFactory
{
public function create(?string $timezone = null): UserDateTimeFactory
{
return new UserDateTimeFactory($this->getCurrentUserMock($timezone));
}
$userFactory = new CurrentUserFactory($this->getTestCase());
$currentUser = $userFactory->create(new User(), $timezone);
protected function getCurrentUserMock(?string $timezone = null)
{
$user = new User();
if (null !== $timezone) {
$pref = new UserPreference();
$pref->setName('timezone');
$pref->setValue($timezone);
$user->addPreference($pref);
}
$repository = $this->getMockBuilder(UserRepository::class)->setMethods(['getUserById'])->disableOriginalConstructor()->getMock();
$repository->expects(TestCase::exactly(1))->method('getUserById')->willReturn($user);
$token = $this->getMockBuilder(UsernamePasswordToken::class)->setMethods(['getUser'])->disableOriginalConstructor()->getMock();
$token->expects(TestCase::exactly(1))->method('getUser')->willReturn($user);
$tokenStorage = new TokenStorage();
$tokenStorage->setToken($token);
return new CurrentUser($tokenStorage, $repository);
return new UserDateTimeFactory($currentUser);
}
}

View File

@@ -0,0 +1,44 @@
<?php
/*
* This file is part of the Kimai time-tracking app.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace App\Tests\Repository\Query;
use App\Entity\Activity;
use App\Entity\Project;
use App\Repository\Query\ActivityFormTypeQuery;
/**
* @covers \App\Repository\Query\ActivityFormTypeQuery
*/
class ActivityFormTypeQueryTest extends BaseQueryTest
{
public function testQuery()
{
$sut = new ActivityFormTypeQuery();
self::assertTrue($sut->isGlobalsOnly());
$project = new Project();
self::assertNull($sut->getProject());
self::assertInstanceOf(ActivityFormTypeQuery::class, $sut->setProject($project));
self::assertSame($project, $sut->getProject());
$activity = new Activity();
self::assertNull($sut->getActivity());
self::assertInstanceOf(ActivityFormTypeQuery::class, $sut->setActivity($activity));
self::assertSame($activity, $sut->getActivity());
self::assertFalse($sut->isGlobalsOnly());
$activity = new Activity();
self::assertNull($sut->getActivityToIgnore());
self::assertInstanceOf(ActivityFormTypeQuery::class, $sut->setActivityToIgnore($activity));
self::assertSame($activity, $sut->getActivityToIgnore());
}
}

View File

@@ -9,6 +9,7 @@
namespace App\Tests\Repository\Query;
use App\Entity\Team;
use App\Repository\Query\BaseQuery;
use PHPUnit\Framework\TestCase;
@@ -29,6 +30,7 @@ class BaseQueryTest extends TestCase
$this->assertPageSize($sut);
$this->assertOrderBy($sut, $orderBy);
$this->assertOrder($sut);
$this->assertTeams($sut);
}
protected function assertResultType(BaseQuery $sut)
@@ -49,6 +51,14 @@ class BaseQueryTest extends TestCase
}
}
protected function assertTeams(BaseQuery $sut)
{
self::assertEmpty($sut->getTeams());
self::assertInstanceOf(BaseQuery::class, $sut->addTeam(new Team()));
self::assertEquals(1, count($sut->getTeams()));
}
protected function assertPage(BaseQuery $sut)
{
$this->assertEquals(BaseQuery::DEFAULT_PAGE, $sut->getPage());

View File

@@ -0,0 +1,45 @@
<?php
/*
* This file is part of the Kimai time-tracking app.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace App\Tests\Repository\Query;
use App\Entity\Customer;
use App\Entity\Team;
use App\Entity\User;
use App\Repository\Query\CustomerFormTypeQuery;
/**
* @covers \App\Repository\Query\CustomerFormTypeQuery
*/
class CustomerFormTypeQueryTest extends BaseQueryTest
{
public function testQuery()
{
$sut = new CustomerFormTypeQuery();
self::assertEmpty($sut->getTeams());
self::assertInstanceOf(CustomerFormTypeQuery::class, $sut->addTeam(new Team()));
self::assertCount(1, $sut->getTeams());
$customer = new Customer();
self::assertNull($sut->getCustomer());
self::assertInstanceOf(CustomerFormTypeQuery::class, $sut->setCustomer($customer));
self::assertSame($customer, $sut->getCustomer());
$customer = new Customer();
self::assertNull($sut->getCustomerToIgnore());
self::assertInstanceOf(CustomerFormTypeQuery::class, $sut->setCustomerToIgnore($customer));
self::assertSame($customer, $sut->getCustomerToIgnore());
$user = new User();
self::assertNull($sut->getUser());
self::assertInstanceOf(CustomerFormTypeQuery::class, $sut->setUser($user));
self::assertSame($user, $sut->getUser());
}
}

View File

@@ -0,0 +1,51 @@
<?php
/*
* This file is part of the Kimai time-tracking app.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace App\Tests\Repository\Query;
use App\Entity\Customer;
use App\Entity\Project;
use App\Entity\Team;
use App\Entity\User;
use App\Repository\Query\ProjectFormTypeQuery;
/**
* @covers \App\Repository\Query\ProjectFormTypeQuery
*/
class ProjectFormTypeQueryTest extends BaseQueryTest
{
public function testQuery()
{
$sut = new ProjectFormTypeQuery();
self::assertEmpty($sut->getTeams());
self::assertInstanceOf(ProjectFormTypeQuery::class, $sut->addTeam(new Team()));
self::assertCount(1, $sut->getTeams());
$project = new Project();
self::assertNull($sut->getProject());
self::assertInstanceOf(ProjectFormTypeQuery::class, $sut->setProject($project));
self::assertSame($project, $sut->getProject());
$project = new Project();
self::assertNull($sut->getProjectToIgnore());
self::assertInstanceOf(ProjectFormTypeQuery::class, $sut->setProjectToIgnore($project));
self::assertSame($project, $sut->getProjectToIgnore());
$customer = new Customer();
self::assertNull($sut->getCustomer());
self::assertInstanceOf(ProjectFormTypeQuery::class, $sut->setCustomer($customer));
self::assertSame($customer, $sut->getCustomer());
$user = new User();
self::assertNull($sut->getUser());
self::assertInstanceOf(ProjectFormTypeQuery::class, $sut->setUser($user));
self::assertSame($user, $sut->getUser());
}
}

View File

@@ -0,0 +1,34 @@
<?php
/*
* This file is part of the Kimai time-tracking app.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace App\Tests\Repository\Query;
use App\Entity\Team;
use App\Entity\User;
use App\Repository\Query\UserFormTypeQuery;
/**
* @covers \App\Repository\Query\UserFormTypeQuery
*/
class UserFormTypeQueryTest extends BaseQueryTest
{
public function testQuery()
{
$sut = new UserFormTypeQuery();
self::assertEmpty($sut->getTeams());
self::assertInstanceOf(UserFormTypeQuery::class, $sut->addTeam(new Team()));
self::assertCount(1, $sut->getTeams());
$user = new User();
self::assertNull($sut->getUser());
self::assertInstanceOf(UserFormTypeQuery::class, $sut->setUser($user));
self::assertSame($user, $sut->getUser());
}
}

View File

@@ -9,9 +9,10 @@
namespace App\Tests\Repository;
use App\Entity\User;
use App\Repository\TimesheetRepository;
use App\Repository\WidgetRepository;
use App\Security\CurrentUser;
use App\Tests\Mocks\Security\CurrentUserFactory;
use PHPUnit\Framework\TestCase;
/**
@@ -22,7 +23,7 @@ class WidgetRepositoryTest extends TestCase
public function testHasWidget()
{
$repoMock = $this->getMockBuilder(TimesheetRepository::class)->disableOriginalConstructor()->getMock();
$userMock = $this->getMockBuilder(CurrentUser::class)->disableOriginalConstructor()->getMock();
$userMock = (new CurrentUserFactory($this))->create(new User());
$sut = new WidgetRepository($repoMock, $userMock, ['test' => []]);
@@ -37,7 +38,7 @@ class WidgetRepositoryTest extends TestCase
public function testGetWidgetThrowsExceptionOnNonExistingWidget()
{
$repoMock = $this->getMockBuilder(TimesheetRepository::class)->disableOriginalConstructor()->getMock();
$userMock = $this->getMockBuilder(CurrentUser::class)->disableOriginalConstructor()->getMock();
$userMock = (new CurrentUserFactory($this))->create(new User());
$sut = new WidgetRepository($repoMock, $userMock, ['test' => []]);
$sut->get('foo');
@@ -50,7 +51,7 @@ class WidgetRepositoryTest extends TestCase
public function testGetWidgetThrowsExceptionOnInvalidType()
{
$repoMock = $this->getMockBuilder(TimesheetRepository::class)->disableOriginalConstructor()->getMock();
$userMock = $this->getMockBuilder(CurrentUser::class)->disableOriginalConstructor()->getMock();
$userMock = (new CurrentUserFactory($this))->create(new User());
$sut = new WidgetRepository($repoMock, $userMock, ['test' => ['type' => 'FooBar', 'user' => false]]);
$sut->get('test');
@@ -63,7 +64,7 @@ class WidgetRepositoryTest extends TestCase
public function testGetWidgetTriggersExceptionOnWrongClass()
{
$repoMock = $this->getMockBuilder(TimesheetRepository::class)->disableOriginalConstructor()->getMock();
$userMock = $this->getMockBuilder(CurrentUser::class)->disableOriginalConstructor()->getMock();
$userMock = (new CurrentUserFactory($this))->create(new User());
$sut = new WidgetRepository($repoMock, $userMock, ['test' => ['type' => 'CompoundChart', 'user' => false]]);
$sut->get('test');
@@ -77,7 +78,7 @@ class WidgetRepositoryTest extends TestCase
$repoMock = $this->getMockBuilder(TimesheetRepository::class)->disableOriginalConstructor()->getMock();
$repoMock->method('getStatistic')->willReturn($data);
$userMock = $this->getMockBuilder(CurrentUser::class)->disableOriginalConstructor()->getMock();
$userMock = (new CurrentUserFactory($this))->create(new User());
$widget = [
'color' => 'sunny',

View File

@@ -52,6 +52,7 @@ abstract class AbstractVoterTest extends TestCase
$user = $this->getMockBuilder(User::class)->getMock();
$user->method('getId')->willReturn($id);
$user->method('getRoles')->willReturn($roles);
$user->method('getTeams')->willReturn([]);
return $user;
}
@@ -65,8 +66,11 @@ abstract class AbstractVoterTest extends TestCase
{
if (!$overwrite) {
$activities = ['view_activity', 'edit_activity', 'budget_activity', 'delete_activity', 'create_activity'];
$activitiesTeam = ['view_activity', 'create_activity', 'edit_teamlead_activity', 'budget_teamlead_activity'];
$projects = ['view_project', 'edit_project', 'budget_project', 'delete_project', 'create_project'];
$projectsTeam = ['view_project', 'edit_teamlead_project', 'budget_teamlead_project', 'permissions_teamlead_project'];
$customers = ['view_customer', 'edit_customer', 'budget_customer', 'delete_customer', 'create_customer'];
$customersTeam = ['view_customer', 'edit_teamlead_customer', 'budget_teamlead_customer'];
$invoice = ['view_invoice', 'create_invoice'];
$invoiceTemplate = ['view_invoice_template', 'create_invoice_template', 'edit_invoice_template', 'delete_invoice_template'];
$timesheet = ['view_own_timesheet', 'start_own_timesheet', 'stop_own_timesheet', 'create_own_timesheet', 'edit_own_timesheet', 'export_own_timesheet', 'delete_own_timesheet'];
@@ -76,17 +80,18 @@ abstract class AbstractVoterTest extends TestCase
$user = ['view_user', 'create_user', 'delete_user'];
$rate = ['view_rate_own_timesheet', 'edit_rate_own_timesheet'];
$rateOther = ['view_rate_other_timesheet', 'edit_rate_other_timesheet'];
$teams = ['view_team', 'create_team', 'edit_team', 'delete_team'];
$roleUser = [];
$roleUser = ['edit_team_activity', 'edit_team_project', 'edit_team_customer'];
$roleTeamlead = ['view_rate_own_timesheet', 'view_rate_other_timesheet', 'hourly-rate_own_profile'];
$roleAdmin = ['hourly-rate_own_profile', 'edit_exported_timesheet'];
$roleSuperAdmin = ['hourly-rate_own_profile', 'hourly-rate_other_profile', 'delete_own_profile', 'roles_own_profile', 'system_information', 'system_configuration', 'plugins', 'edit_exported_timesheet'];
$permissions = [
'ROLE_USER' => array_merge($timesheet, $profile, $roleUser),
'ROLE_TEAMLEAD' => array_merge($invoice, $timesheet, $timesheetOthers, $profile, $roleTeamlead),
'ROLE_ADMIN' => array_merge($activities, $projects, $customers, $invoice, $invoiceTemplate, $timesheet, $timesheetOthers, $profile, $rate, $rateOther, $roleAdmin),
'ROLE_SUPER_ADMIN' => array_merge($activities, $projects, $customers, $invoice, $invoiceTemplate, $timesheet, $timesheetOthers, $profile, $profileOther, $user, $rate, $rateOther, $roleSuperAdmin),
'ROLE_TEAMLEAD' => array_merge($invoice, $timesheet, $timesheetOthers, $profile, $roleTeamlead, $activitiesTeam, $projectsTeam, $customersTeam),
'ROLE_ADMIN' => array_merge($activities, $projects, $customers, $invoice, $invoiceTemplate, $timesheet, $timesheetOthers, $profile, $rate, $rateOther, $roleAdmin, $teams),
'ROLE_SUPER_ADMIN' => array_merge($activities, $projects, $customers, $invoice, $invoiceTemplate, $timesheet, $timesheetOthers, $profile, $profileOther, $user, $rate, $rateOther, $roleSuperAdmin, $teams),
];
}

View File

@@ -10,6 +10,9 @@
namespace App\Tests\Voter;
use App\Entity\Activity;
use App\Entity\Customer;
use App\Entity\Project;
use App\Entity\Team;
use App\Entity\User;
use App\Voter\ActivityVoter;
use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken;
@@ -24,6 +27,11 @@ class ActivityVoterTest extends AbstractVoterTest
* @dataProvider getTestData
*/
public function testVote(User $user, $subject, $attribute, $result)
{
$this->assertVote($user, $subject, $attribute, $result);
}
protected function assertVote(User $user, $subject, $attribute, $result)
{
$token = new UsernamePasswordToken($user, 'foo', 'bar', $user->getRoles());
$sut = $this->getVoter(ActivityVoter::class, $user);
@@ -47,9 +55,19 @@ class ActivityVoterTest extends AbstractVoterTest
yield [$user, new Activity(), 'delete', $result];
}
$result = VoterInterface::ACCESS_DENIED;
foreach ([$user0, $user1, $user2] as $user) {
foreach ([$user2] as $user) {
yield [$user, new Activity(), 'view', $result];
}
$result = VoterInterface::ACCESS_DENIED;
foreach ([$user0, $user1] as $user) {
yield [$user, new Activity(), 'view', $result];
yield [$user, new Activity(), 'edit', $result];
yield [$user, new Activity(), 'budget', $result];
yield [$user, new Activity(), 'delete', $result];
}
foreach ([$user2] as $user) {
yield [$user, new Activity(), 'edit', $result];
yield [$user, new Activity(), 'budget', $result];
yield [$user, new Activity(), 'delete', $result];
@@ -66,4 +84,65 @@ class ActivityVoterTest extends AbstractVoterTest
yield [$user, $user, 'delete', $result];
}
}
public function testTeamlead()
{
$team = new Team();
$user = new User();
$user->addRole(User::ROLE_TEAMLEAD);
$team->setTeamLead($user);
$activity = new Activity();
$project = new Project();
$customer = new Customer();
$project->setCustomer($customer);
$activity->setProject($project);
$customer->addTeam($team);
$this->assertVote($user, $activity, 'edit', VoterInterface::ACCESS_GRANTED);
$activity = new Activity();
$project = new Project();
$customer = new Customer();
$project->setCustomer($customer);
$activity->setProject($project);
$project->addTeam($team);
$this->assertVote($user, $activity, 'edit', VoterInterface::ACCESS_GRANTED);
$activity = new Activity();
$this->assertVote($user, $activity, 'edit', VoterInterface::ACCESS_DENIED);
}
public function testTeamMember()
{
$team = new Team();
$user = new User();
$user->addRole(User::ROLE_USER);
$team->setTeamLead($user);
$activity = new Activity();
$project = new Project();
$customer = new Customer();
$customer->addTeam($team);
$project->setCustomer($customer);
$activity->setProject($project);
$this->assertVote($user, $activity, 'edit', VoterInterface::ACCESS_GRANTED);
$activity = new Activity();
$team = new Team();
$user = new User();
$user->addRole(User::ROLE_USER);
$team->addUser($user);
$project = new Project();
$customer = new Customer();
$project->addTeam($team);
$project->setCustomer($customer);
$activity->setProject($project);
$this->assertVote($user, $activity, 'edit', VoterInterface::ACCESS_GRANTED);
}
}

View File

@@ -10,6 +10,7 @@
namespace App\Tests\Voter;
use App\Entity\Customer;
use App\Entity\Team;
use App\Entity\User;
use App\Voter\CustomerVoter;
use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken;
@@ -24,6 +25,11 @@ class CustomerVoterTest extends AbstractVoterTest
* @dataProvider getTestData
*/
public function testVote(User $user, $subject, $attribute, $result)
{
$this->assertVote($user, $subject, $attribute, $result);
}
protected function assertVote(User $user, $subject, $attribute, $result)
{
$token = new UsernamePasswordToken($user, 'foo', 'bar', $user->getRoles());
$sut = $this->getVoter(CustomerVoter::class, $user);
@@ -47,9 +53,19 @@ class CustomerVoterTest extends AbstractVoterTest
yield [$user, new Customer(), 'delete', $result];
}
$result = VoterInterface::ACCESS_DENIED;
foreach ([$user0, $user1, $user2] as $user) {
foreach ([$user2] as $user) {
yield [$user, new Customer(), 'view', $result];
}
$result = VoterInterface::ACCESS_DENIED;
foreach ([$user0, $user1] as $user) {
yield [$user, new Customer(), 'view', $result];
yield [$user, new Customer(), 'edit', $result];
yield [$user, new Customer(), 'budget', $result];
yield [$user, new Customer(), 'delete', $result];
}
foreach ([$user2] as $user) {
yield [$user, new Customer(), 'edit', $result];
yield [$user, new Customer(), 'budget', $result];
yield [$user, new Customer(), 'delete', $result];
@@ -66,4 +82,40 @@ class CustomerVoterTest extends AbstractVoterTest
yield [$user, $user, 'delete', $result];
}
}
public function testTeamlead()
{
$team = new Team();
$user = new User();
$user->addRole(User::ROLE_TEAMLEAD);
$team->setTeamLead($user);
$customer = new Customer();
$customer->addTeam($team);
$this->assertVote($user, $customer, 'edit', VoterInterface::ACCESS_GRANTED);
}
public function testTeamMember()
{
$team = new Team();
$user = new User();
$user->addRole(User::ROLE_USER);
$team->setTeamLead($user);
$customer = new Customer();
$customer->addTeam($team);
$this->assertVote($user, $customer, 'edit', VoterInterface::ACCESS_GRANTED);
$team = new Team();
$user = new User();
$user->addRole(User::ROLE_USER);
$team->addUser($user);
$customer = new Customer();
$customer->addTeam($team);
$this->assertVote($user, $customer, 'edit', VoterInterface::ACCESS_GRANTED);
}
}

View File

@@ -9,7 +9,9 @@
namespace App\Tests\Voter;
use App\Entity\Customer;
use App\Entity\Project;
use App\Entity\Team;
use App\Entity\User;
use App\Voter\ProjectVoter;
use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken;
@@ -24,10 +26,19 @@ class ProjectVoterTest extends AbstractVoterTest
* @dataProvider getTestData
*/
public function testVote(User $user, $subject, $attribute, $result)
{
$this->assertVote($user, $subject, $attribute, $result);
}
protected function assertVote(User $user, $subject, $attribute, $result)
{
$token = new UsernamePasswordToken($user, 'foo', 'bar', $user->getRoles());
$sut = $this->getVoter(ProjectVoter::class, $user);
if ($subject instanceof Project && null === $subject->getCustomer()) {
$subject->setCustomer(new Customer());
}
$this->assertEquals($result, $sut->vote($token, $subject, [$attribute]));
}
@@ -47,9 +58,19 @@ class ProjectVoterTest extends AbstractVoterTest
yield [$user, new Project(), 'delete', $result];
}
$result = VoterInterface::ACCESS_DENIED;
foreach ([$user0, $user1, $user2] as $user) {
foreach ([$user2] as $user) {
yield [$user, new Project(), 'view', $result];
}
$result = VoterInterface::ACCESS_DENIED;
foreach ([$user0, $user1] as $user) {
yield [$user, new Project(), 'view', $result];
yield [$user, new Project(), 'edit', $result];
yield [$user, new Project(), 'budget', $result];
yield [$user, new Project(), 'delete', $result];
}
foreach ([$user2] as $user) {
yield [$user, new Project(), 'edit', $result];
yield [$user, new Project(), 'budget', $result];
yield [$user, new Project(), 'delete', $result];
@@ -67,4 +88,53 @@ class ProjectVoterTest extends AbstractVoterTest
yield [$user, $user, 'delete', $result];
}
}
public function testTeamlead()
{
$team = new Team();
$user = new User();
$user->addRole(User::ROLE_TEAMLEAD);
$team->setTeamLead($user);
$project = new Project();
$customer = new Customer();
$project->setCustomer($customer);
$customer->addTeam($team);
$this->assertVote($user, $project, 'edit', VoterInterface::ACCESS_GRANTED);
$project = new Project();
$customer = new Customer();
$project->setCustomer($customer);
$project->addTeam($team);
$this->assertVote($user, $project, 'edit', VoterInterface::ACCESS_GRANTED);
}
public function testTeamMember()
{
$team = new Team();
$user = new User();
$user->addRole(User::ROLE_USER);
$team->setTeamLead($user);
$project = new Project();
$customer = new Customer();
$customer->addTeam($team);
$project->setCustomer($customer);
$this->assertVote($user, $project, 'edit', VoterInterface::ACCESS_GRANTED);
$team = new Team();
$user = new User();
$user->addRole(User::ROLE_USER);
$team->addUser($user);
$project = new Project();
$customer = new Customer();
$project->addTeam($team);
$project->setCustomer($customer);
$this->assertVote($user, $project, 'edit', VoterInterface::ACCESS_GRANTED);
}
}

View File

@@ -57,7 +57,6 @@ class RolePermissionVoterTest extends AbstractVoterTest
];
$others = [
'create_activity' => null,
'create_customer' => null,
'create_project' => null,
];
@@ -101,6 +100,11 @@ class RolePermissionVoterTest extends AbstractVoterTest
yield [$user, $entity, $permission, $result];
}
}
foreach ([$user0, $user1] as $user) {
foreach (['view_activity' => null] as $permission => $entity) {
yield [$user, $entity, $permission, $result];
}
}
foreach ([$user0, $user1] as $user) {
foreach ($invoice as $permission => $entity) {
yield [$user, $entity, $permission, $result];

View File

@@ -0,0 +1,82 @@
<?php
/*
* This file is part of the Kimai time-tracking app.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace App\Tests\Voter;
use App\Entity\Team;
use App\Entity\User;
use App\Voter\TeamVoter;
use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken;
use Symfony\Component\Security\Core\Authorization\Voter\VoterInterface;
/**
* @covers \App\Voter\TeamVoter
*/
class TeamVoterTest extends AbstractVoterTest
{
/**
* @dataProvider getTestData
*/
public function testVote(User $user, $subject, $attribute, $result)
{
$token = new UsernamePasswordToken($user, 'foo', 'bar', $user->getRoles());
$sut = $this->getVoter(TeamVoter::class, $user);
$this->assertEquals($result, $sut->vote($token, $subject, [$attribute]));
}
public function getTestData()
{
$user0 = $this->getUser(0, null);
$user1 = $this->getUser(1, User::ROLE_USER);
$user2 = $this->getUser(2, User::ROLE_TEAMLEAD);
$user3 = $this->getUser(3, User::ROLE_ADMIN);
$user4 = $this->getUser(4, User::ROLE_SUPER_ADMIN);
$team = new Team();
$result = VoterInterface::ACCESS_ABSTAIN;
$allTeamPerms = ['view_team', 'create_team', 'edit_team', 'delete_team'];
foreach ($allTeamPerms as $fullPerm) {
yield [$user0, [], $fullPerm, $result];
yield [$user0, new \stdClass(), $fullPerm, $result];
yield [$user0, $team, $fullPerm, $result];
yield [$user1, $team, $fullPerm, $result];
yield [$user2, $team, $fullPerm, $result];
yield [$user3, $team, $fullPerm, $result];
yield [$user4, $team, $fullPerm, $result];
}
$result = VoterInterface::ACCESS_DENIED;
yield [$user0, $team, 'view', $result];
yield [$user0, $team, 'edit', $result];
yield [$user0, $team, 'delete', $result];
yield [$user1, $team, 'view', $result];
yield [$user1, $team, 'edit', $result];
yield [$user1, $team, 'delete', $result];
yield [$user2, $team, 'view', $result];
yield [$user2, $team, 'edit', $result];
yield [$user2, $team, 'delete', $result];
$result = VoterInterface::ACCESS_GRANTED;
yield [$user3, $team, 'view', $result];
yield [$user3, $team, 'edit', $result];
yield [$user3, $team, 'delete', $result];
yield [$user4, $team, 'view', $result];
yield [$user4, $team, 'edit', $result];
yield [$user4, $team, 'delete', $result];
}
}

View File

@@ -12,7 +12,7 @@ namespace App\Tests\Widget\Type;
use App\Entity\User;
use App\Model\Statistic\Day;
use App\Repository\TimesheetRepository;
use App\Security\CurrentUser;
use App\Tests\Mocks\Security\CurrentUserFactory;
use App\Tests\Mocks\Security\UserDateTimeFactoryFactory;
use App\Widget\Type\AbstractWidgetType;
use App\Widget\Type\DailyWorkingTimeChart;
@@ -30,9 +30,9 @@ class DailyWorkingTimeChartTest extends TestCase
public function createSut(): AbstractWidgetType
{
$repository = $this->getMockBuilder(TimesheetRepository::class)->disableOriginalConstructor()->getMock();
$user = $this->getMockBuilder(CurrentUser::class)->disableOriginalConstructor()->setMethods(['getUser'])->getMock();
$user->expects($this->once())->method('getUser')->willReturn(new User());
$mockFactory = new UserDateTimeFactoryFactory($this);
$userFactory = new CurrentUserFactory($this);
$user = $userFactory->create(new User(), 'Europe/Berlin');
return new DailyWorkingTimeChart($repository, $user, $mockFactory->create('Europe/Berlin'));
}
@@ -106,8 +106,10 @@ class DailyWorkingTimeChartTest extends TestCase
['year' => '2019', 'month' => '1', 'day' => 1, 'rate' => 13.75, 'duration' => 1234]
];
});
$user = $this->getMockBuilder(CurrentUser::class)->disableOriginalConstructor()->setMethods(['getUser'])->getMock();
$user->expects($this->once())->method('getUser')->willReturn((new User())->setUsername('tralalala'));
$userFactory = new CurrentUserFactory($this);
$user = $userFactory->create(new User(), 'Europe/Berlin');
$mockFactory = new UserDateTimeFactoryFactory($this);
$sut = new DailyWorkingTimeChart($repository, $user, $mockFactory->create('Europe/Berlin'));

View File

@@ -8,6 +8,7 @@ parameters:
- '#Access to an undefined property Faker\\Generator::\$stateAbbr.#'
- '#Access to an undefined property Faker\\Generator::\$catchPhrase.#'
- '#Access to an undefined property Faker\\Generator::\$bs.#'
- '#Call to static method PHPUnit\\Framework\\Assert::assertSame\(\) with App\\Entity\\[a-zA-Z0-9]+ and null will always evaluate to false.#'
excludes_analyse:
- %rootDir%/../../../tests/Ldap/LdapDriverTest.php
inferPrivatePropertyTypeFromConstructor: true