Skip to content
Snippets Groups Projects
Commit bb62df65 authored by Jan-Hendrik Willms's avatar Jan-Hendrik Willms Committed by David Siegfried
Browse files

implement tests for consultation jsonapi routes

Closes #1174

Merge request studip/studip!696
parent ad8f5f3b
No related branches found
No related tags found
No related merge requests found
Showing with 696 additions and 1 deletion
...@@ -14,7 +14,12 @@ class BookingsDelete extends JsonApiController ...@@ -14,7 +14,12 @@ class BookingsDelete extends JsonApiController
public function __invoke(Request $request, Response $response, $args) public function __invoke(Request $request, Response $response, $args)
{ {
$json = $this->validate($request); $body = (string) $request->getBody();
if ($body) {
$json = $this->validate($request);
} else {
$json = [];
}
$booking = \ConsultationBooking::find($args['id']); $booking = \ConsultationBooking::find($args['id']);
if (!$booking) { if (!$booking) {
......
<?php
use WoohooLabs\Yang\JsonApi\Response\JsonApiResponse;
use WoohooLabs\Yang\JsonApi\Schema\Document;
use WoohooLabs\Yang\JsonApi\Schema\Resource\ResourceObject;
// Required for consultation mailer
require_once 'vendor/flexi/flexi.php';
trait ConsultationHelper
{
/**
* @var \UnitTester
*/
protected $tester;
protected function _before()
{
\DBManager::getInstance()->setConnection('studip', $this->getModule('\\Helper\\StudipDb')->dbh);
}
protected static $BLOCK_DATA = [
'room' => 'Testraum',
'calendar_events' => false,
'show_participants' => false,
'require_reason' => 'no',
'confirmation_text' => null,
'note' => 'Testnotiz für Block',
'size' => 1,
];
protected static $SLOT_DATA = [
'note' => 'Testnotiz für Slot',
];
protected static $BOOKING_DATA = [
'reason' => 'Test reason',
];
protected function getUserForCredentials(array $credentials): User
{
return User::find($credentials['id']);
}
protected function createBlockWithSlotsForRange(Range $range): ConsultationBlock
{
$blocks = ConsultationBlock::generateBlocks(
$range,
strtotime('today 8:00'),
strtotime('today 10:00'),
date('w'),
1
);
$blocks = iterator_to_array($blocks);
$block = reset($blocks);
$block->setData(self::$BLOCK_DATA);
$block->createSlots(15);
foreach ($block->slots as $slot) {
$slot->setData(self::$SLOT_DATA['note']);
}
$block->store();
return ConsultationBlock::find($block->id);
}
protected function getSlotFromBlock(ConsultationBlock $block): ConsultationSlot
{
return $block->slots->first();
}
protected function withStudipEnv(array $credentials, callable $fn)
{
// Create global template factory if neccessary
$has_template_factory = isset($GLOBALS['template_factory']);
if (!$has_template_factory) {
$GLOBALS['template_factory'] = new Flexi_TemplateFactory($GLOBALS['STUDIP_BASE_PATH'] . '/templates');
}
$result = $this->tester->withPHPLib($credentials, $fn);
if (!$has_template_factory) {
unset($GLOBALS['template_factory']);
}
return $result;
}
protected function createBookingForSlot(array $credentials, ConsultationSlot $slot, User $user): ConsultationBooking
{
return $this->withStudipEnv(
$credentials,
function () use ($slot, $user): ConsultationBooking {
$booking = new ConsultationBooking();
$booking->slot_id = $slot->id;
$booking->user_id = $user->id;
$booking->setData(self::$BOOKING_DATA);
$booking->store();
return $booking;
}
);
}
protected function sendMockRequest(string $route, string $handler, array $credentials, array $variables = [], array $options = []): JsonApiResponse
{
$options = array_merge([
'method' => 'GET',
'considered_successful' => [200],
'json_body' => null,
], $options);
$app = $this->tester->createApp(
$credentials,
strtolower($options['method']),
$route,
$handler
);
$evaluated_route = preg_replace_callback(
'/\{(.+?)(:[^}]+)?}/',
function ($match) use ($variables) {
$key = $match[1];
if (!isset($variables[$key])) {
throw new Exception("No variable '{$key}' defined");
}
return $variables[$key];
},
$route
);
$requestBuilder = $this->tester->createRequestBuilder($credentials);
$requestBuilder->setUri($evaluated_route)->setMethod(strtoupper($options['method']));
if (isset($options['json_body'])) {
$requestBuilder->setJsonApiBody($options['json_body']);
}
/** @var JsonApiResponse $response */
$response = $this->withStudipEnv($credentials, function () use ($app, $requestBuilder) {
return $this->tester->sendMockRequest($app, $requestBuilder->getRequest());
});
if ($options['considered_successful']) {
$this->assertTrue(
$response->isSuccessful($options['considered_successful']),
'Actual status code is ' . $response->getStatusCode()
);
}
return $response;
}
protected function getSingleResourceDocument(JsonApiResponse $response): Document
{
$this->assertTrue($response->hasDocument());
$document = $response->document();
$this->assertTrue($document->isSingleResourceDocument());
return $document;
}
protected function getResourceCollectionDocument(JsonApiResponse $response): Document
{
$this->assertTrue($response->hasDocument());
$document = $response->document();
$this->assertTrue($document->isResourceCollectionDocument());
return $document;
}
protected function assertHasRelations(ResourceObject $resource, ...$relations)
{
foreach ($relations as $relation) {
$this->assertTrue($resource->hasRelationship($relation));
}
}
}
<?php
use JsonApi\Routes\Consultations\BlockShow;
use JsonApi\Schemas\ConsultationBlock as Schema;
require_once __DIR__ . '/ConsultationHelper.php';
class ConsultationsBlockShowTest extends Codeception\Test\Unit
{
use ConsultationHelper;
public function testFetchBlock(): void
{
$credentials = $this->tester->getCredentialsForTestDozent();
$range = User::find($credentials['id']);
$block = $this->createBlockWithSlotsForRange($range);
$response = $this->sendMockRequest(
'/consultation-blocks/{id}',
BlockShow::class,
$credentials,
['id' => $block->id]
);
$document = $this->getSingleResourceDocument($response);
$resourceObject = $document->primaryResource();
$this->assertTrue(is_string($resourceObject->id()));
$this->assertSame($block->id, $resourceObject->id());
$this->assertSame(Schema::TYPE, $resourceObject->type());
$this->assertEquals($block->start, strtotime($resourceObject->attribute('start')));
$this->assertEquals($block->end, strtotime($resourceObject->attribute('end')));
$this->assertSame(self::$BLOCK_DATA['room'], $resourceObject->attribute('room'));
$this->assertSame(self::$BLOCK_DATA['show_participants'], $resourceObject->attribute('show-participants'));
$this->assertSame(self::$BLOCK_DATA['require_reason'], $resourceObject->attribute('require-reason'));
$this->assertSame(self::$BLOCK_DATA['confirmation_text'], $resourceObject->attribute('confirmation-text'));
$this->assertSame(self::$BLOCK_DATA['note'], $resourceObject->attribute('note'));
$this->assertSame(self::$BLOCK_DATA['size'], $resourceObject->attribute('size'));
$this->assertHasRelations($resourceObject, Schema::REL_RANGE, Schema::REL_SLOTS);
}
}
<?php
use JsonApi\Routes\Consultations\BlocksByRangeIndex;
require_once __DIR__ . '/ConsultationHelper.php';
// TODO: Activate consultations on institute for testing
class ConsultationsBlocksByRangeIndexTest extends Codeception\Test\Unit
{
use ConsultationHelper;
public static function rangeProvider(): array
{
return [
'Course' => ['course', 'a07535cf2f8a72df33c12ddfa4b53dde'],
'User' => ['user', '205f3efb7997a0fc9755da2b535038da'],
];
}
/**
* @dataProvider rangeProvider
*/
public function testFetchBlocksByRangeIndex(string $range_type, string $range_id): void
{
$credentials = $this->tester->getCredentialsForTestDozent();
$range = RangeFactory::createRange($range_type, $range_id);
$this->createBlockWithSlotsForRange($range);
$response = $this->sendMockRequest(
"/{type:courses|institutes|users}/{id}/consultations",
BlocksByRangeIndex::class,
$credentials,
['type' => "{$range_type}s", 'id' => $range_id]
);
$document = $this->getResourceCollectionDocument($response);
$resources = $document->primaryResources();
$this->tester->assertCount(1, $resources);
}
}
<?php
use JsonApi\Routes\Consultations\BookingsCreate;
use JsonApi\Schemas\ConsultationBooking as Schema;
use JsonAPi\Schemas\User as UserSchema;
use WoohooLabs\Yang\JsonApi\Response\JsonApiResponse;
require_once __DIR__ . '/ConsultationHelper.php';
class ConsultationsBookingCreateBySlotIndexTest extends Codeception\Test\Unit
{
use ConsultationHelper;
public function testAutorMayCreateBooking(): void
{
$credentials = $this->tester->getCredentialsForTestDozent();
$range = User::find($credentials['id']);
$block = $this->createBlockWithSlotsForRange($range);
$slot = $this->getSlotFromBlock($block);
$this->createBooking(
$credentials,
$slot,
$this->tester->getCredentialsForTestAutor()['id'],
[201]
);
}
public function testSlotIsOccupied(): void
{
$credentials = $this->tester->getCredentialsForTestDozent();
$range = User::find($credentials['id']);
$block = $this->createBlockWithSlotsForRange($range);
$slot = $this->getSlotFromBlock($block);
$this->createBooking(
$credentials,
$slot,
$this->tester->getCredentialsForTestAutor()['id'],
[201]
);
$response = $this->createBooking(
$credentials,
$slot,
$this->tester->getCredentialsForTestAutor()['id'],
null
);
$this->assertEquals(409, $response->getStatusCode());
}
public function testRootMayNotCreateBooking(): void
{
$credentials = $this->tester->getCredentialsForTestDozent();
$range = User::find($credentials['id']);
$block = $this->createBlockWithSlotsForRange($range);
$slot = $this->getSlotFromBlock($block);
$response = $this->createBooking(
$credentials,
$slot,
$this->tester->getCredentialsForRoot()['id'],
null
);
$this->assertEquals(403, $response->getStatusCode());
}
private function createBooking(array $credentials, ConsultationSlot $slot, string $user_id, ?array $considered_succssfull): JsonApiResponse
{
return $this->sendMockRequest(
'/consultation-slots/{id}/bookings',
BookingsCreate::class,
$credentials,
['id' => $slot->id],
[
'considered_successful' => $considered_succssfull,
'method' => 'POST',
'json_body' => [
'data' => [
'type' => Schema::TYPE,
'attributes' => [
'reason' => self::$BOOKING_DATA['reason'],
],
'relationships' => [
Schema::REL_USER => [
'data' => [
'type' => UserSchema::TYPE,
'id' => $user_id,
],
]
]
]
]
]
);
}
}
<?php
use JsonApi\Routes\Consultations\BookingsCreate;
use JsonApi\Schemas\ConsultationBooking as Schema;
use JsonAPi\Schemas\User as UserSchema;
use JsonAPi\Schemas\ConsultationSlot as SlotSchema;
use WoohooLabs\Yang\JsonApi\Response\JsonApiResponse;
require_once __DIR__ . '/ConsultationHelper.php';
class ConsultationsBookingCreateTest extends Codeception\Test\Unit
{
use ConsultationHelper;
public function testAutorMayCreateBooking(): void
{
$credentials = $this->tester->getCredentialsForTestDozent();
$range = User::find($credentials['id']);
$block = $this->createBlockWithSlotsForRange($range);
$slot = $this->getSlotFromBlock($block);
$this->createBooking(
$credentials,
$slot,
$this->tester->getCredentialsForTestAutor()['id'],
[201]
);
}
public function testSlotIsOccupied(): void
{
$credentials = $this->tester->getCredentialsForTestDozent();
$range = User::find($credentials['id']);
$block = $this->createBlockWithSlotsForRange($range);
$slot = $this->getSlotFromBlock($block);
$this->createBooking(
$credentials,
$slot,
$this->tester->getCredentialsForTestAutor()['id'],
[201]
);
$response = $this->createBooking(
$credentials,
$slot,
$this->tester->getCredentialsForTestAutor()['id'],
null
);
$this->assertEquals(409, $response->getStatusCode());
}
public function testRootMayNotCreateBooking(): void
{
$credentials = $this->tester->getCredentialsForTestDozent();
$range = User::find($credentials['id']);
$block = $this->createBlockWithSlotsForRange($range);
$slot = $this->getSlotFromBlock($block);
$response = $this->createBooking(
$credentials,
$slot,
$this->tester->getCredentialsForRoot()['id'],
null
);
$this->assertEquals(403, $response->getStatusCode());
}
private function createBooking(array $credentials, ConsultationSlot $slot, string $user_id, ?array $considered_succssfull): JsonApiResponse
{
return $this->sendMockRequest(
'/consultation-bookings',
BookingsCreate::class,
$credentials,
[],
[
'considered_successful' => $considered_succssfull,
'method' => 'POST',
'json_body' => [
'data' => [
'type' => Schema::TYPE,
'attributes' => [
'reason' => self::$BOOKING_DATA['reason'],
],
'relationships' => [
Schema::REL_SLOT => [
'data' => [
'type' => SlotSchema::TYPE,
'id' => $slot->id,
],
],
Schema::REL_USER => [
'data' => [
'type' => UserSchema::TYPE,
'id' => $user_id,
],
],
]
]
]
]
);
}
}
<?php
use JsonApi\Routes\Consultations\BookingsDelete;
require_once __DIR__ . '/ConsultationHelper.php';
class ConsultationsBookingDeleteTest extends Codeception\Test\Unit
{
use ConsultationHelper;
public function testDeleteBookingWithoutReason(): void
{
$credentials = $this->tester->getCredentialsForTestDozent();
$range = User::find($credentials['id']);
$block = $this->createBlockWithSlotsForRange($range);
$slot = $this->getSlotFromBlock($block);
$booking = $this->createBookingForSlot(
$credentials,
$slot,
$this->getUserForCredentials($this->tester->getCredentialsForTestAutor())
);
$this->sendMockRequest(
'/consultation-bookings/{id}',
BookingsDelete::class,
$credentials,
['id' => $booking->id],
[
'considered_successful' => [204],
'method' => 'DELETE',
]
);
}
public function testDeleteBookingWithReason(): void
{
$credentials = $this->tester->getCredentialsForTestDozent();
$range = User::find($credentials['id']);
$block = $this->createBlockWithSlotsForRange($range);
$slot = $this->getSlotFromBlock($block);
$booking = $this->createBookingForSlot(
$credentials,
$slot,
$this->getUserForCredentials($this->tester->getCredentialsForTestAutor())
);
$this->sendMockRequest(
'/consultation-bookings/{id}',
BookingsDelete::class,
$credentials,
['id' => $booking->id],
[
'considered_successful' => [204],
'method' => 'DELETE',
'json_body' => [
'data' => [
'attributes' => [
'reason' => self::$BOOKING_DATA['reason'],
]
],
],
]
);
}
}
<?php
use JsonApi\Routes\Consultations\BookingsShow;
use JsonApi\Schemas\ConsultationBooking as Schema;
require_once __DIR__ . '/ConsultationHelper.php';
class ConsultationsBookingShowTest extends Codeception\Test\Unit
{
use ConsultationHelper;
public function testFetchBlock(): void
{
$credentials = $this->tester->getCredentialsForTestDozent();
$range = User::find($credentials['id']);
$block = $this->createBlockWithSlotsForRange($range);
$slot = $this->getSlotFromBlock($block);
$booking = $this->createBookingForSlot(
$credentials,
$slot,
$this->getUserForCredentials($this->tester->getCredentialsForTestAutor())
);
$response = $this->sendMockRequest(
'/consultation-bookings/{id}',
BookingsShow::class,
$credentials,
['id' => $booking->id]
);
$document = $this->getSingleResourceDocument($response);
$resourceObject = $document->primaryResource();
$this->assertTrue(is_string($resourceObject->id()));
$this->assertSame($booking->id, $resourceObject->id());
$this->assertSame(Schema::TYPE, $resourceObject->type());
$this->assertEquals(self::$BOOKING_DATA['reason'], $resourceObject->attribute('reason'));
$this->assertHasRelations($resourceObject, Schema::REL_SLOT, Schema::REL_USER);
}
}
<?php
use JsonApi\Routes\Consultations\BookingsBySlotIndex;
require_once __DIR__ . '/ConsultationHelper.php';
class ConsultationsBookingsBySlotIndexTest extends Codeception\Test\Unit
{
use ConsultationHelper;
public function testFetchSlots(): void
{
$credentials = $this->tester->getCredentialsForTestDozent();
$range = $this->getUserForCredentials($credentials);
$block = $this->createBlockWithSlotsForRange($range);
$slot = $this->getSlotFromBlock($block);
$this->createBookingForSlot(
$credentials,
$slot,
$this->getUserForCredentials($this->tester->getCredentialsForTestAutor())
);
$response = $this->sendMockRequest(
'/consultation-slots/{id}/bookings',
BookingsBySlotIndex::class,
$credentials,
['id' => $slot->id]
);
$document = $this->getResourceCollectionDocument($response);
$resources = $document->primaryResources();
$this->tester->assertCount(1, $resources);
}
}
<?php
use JsonApi\Routes\Consultations\SlotShow;
use JsonApi\Schemas\ConsultationSlot as Schema;
require_once __DIR__ . '/ConsultationHelper.php';
class ConsultationsSlotShowTest extends Codeception\Test\Unit
{
use ConsultationHelper;
public function testFetchBlock(): void
{
$credentials = $this->tester->getCredentialsForTestDozent();
$range = User::find($credentials['id']);
$block = $this->createBlockWithSlotsForRange($range);
$slot = $this->getSlotFromBlock($block);
$response = $this->sendMockRequest(
'/consultation-slots/{id}',
SlotShow::class,
$credentials,
['id' => $slot->id]
);
$document = $this->getSingleResourceDocument($response);
$resourceObject = $document->primaryResource();
$this->assertTrue(is_string($resourceObject->id()));
$this->assertSame($slot->id, $resourceObject->id());
$this->assertSame(Schema::TYPE, $resourceObject->type());
$this->assertEquals($slot->start_time, strtotime($resourceObject->attribute('start_time')));
$this->assertEquals($slot->end_time, strtotime($resourceObject->attribute('end_time')));
$this->assertHasRelations($resourceObject, Schema::REL_BLOCK, Schema::REL_BOOKINGS);
//
// $this->assertSame(self::$BLOCK_DATA['room'], $resourceObject->attribute('room'));
// $this->assertSame(self::$BLOCK_DATA['show_participants'], $resourceObject->attribute('show-participants'));
// $this->assertSame(self::$BLOCK_DATA['require_reason'], $resourceObject->attribute('require-reason'));
// $this->assertSame(self::$BLOCK_DATA['confirmation_text'], $resourceObject->attribute('confirmation-text'));
// $this->assertSame(self::$BLOCK_DATA['note'], $resourceObject->attribute('note'));
// $this->assertSame(self::$BLOCK_DATA['size'], $resourceObject->attribute('size'));
}
}
<?php
use JsonApi\Routes\Consultations\SlotsByBlockIndex;
require_once __DIR__ . '/ConsultationHelper.php';
class ConsultationsSlotsByBlockIndexTest extends Codeception\Test\Unit
{
use ConsultationHelper;
public function testFetchSlots(): void
{
$credentials = $this->tester->getCredentialsForTestDozent();
$range = User::find($credentials['id']);
$block = $this->createBlockWithSlotsForRange($range);
$response = $this->sendMockRequest(
'/consultation-blocks/{id}/slots',
SlotsByBlockIndex::class,
$credentials,
['id' => $block->id]
);
$document = $this->getResourceCollectionDocument($response);
$resources = $document->primaryResources();
$this->tester->assertCount(8, $resources);
}
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment