Added tests for schedule import

This commit is contained in:
Igor Scheller 2023-07-30 21:43:41 +02:00 committed by xuwhite
parent 790a04dc14
commit e3e0fb33a2
5 changed files with 638 additions and 43 deletions

View File

@ -85,9 +85,6 @@ msgstr "Das Programm konnte nicht abgerufen werden."
msgid "schedule.import.read-error" msgid "schedule.import.read-error"
msgstr "Das Programm konnte nicht gelesen werden." msgstr "Das Programm konnte nicht gelesen werden."
msgid "schedule.import.invalid-shift-type"
msgstr "Der Schichttyp konnte nicht gefunden werden."
msgid "schedule.import.success" msgid "schedule.import.success"
msgstr "Das Programm wurde erfolgreich importiert." msgstr "Das Programm wurde erfolgreich importiert."

View File

@ -83,9 +83,6 @@ msgstr "The schedule could not be requested."
msgid "schedule.import.read-error" msgid "schedule.import.read-error"
msgstr "Unable to parse schedule." msgstr "Unable to parse schedule."
msgid "schedule.import.invalid-shift-type"
msgstr "The shift type can't not be found."
msgid "schedule.import.success" msgid "schedule.import.success"
msgstr "Schedule import successful." msgstr "Schedule import successful."

View File

@ -52,7 +52,7 @@ class ScheduleController extends BaseController
public function index(): Response public function index(): Response
{ {
return $this->response->withView( return $this->response->withView(
'admin/schedule/index.twig', 'admin/schedule/index',
[ [
'is_index' => true, 'is_index' => true,
'schedules' => ScheduleModel::all(), 'schedules' => ScheduleModel::all(),
@ -63,10 +63,10 @@ class ScheduleController extends BaseController
public function edit(Request $request): Response public function edit(Request $request): Response
{ {
$scheduleId = $request->getAttribute('schedule_id'); // optional $scheduleId = $request->getAttribute('schedule_id'); // optional
$schedule = ScheduleModel::find($scheduleId); $schedule = ScheduleModel::findOrNew($scheduleId);
return $this->response->withView( return $this->response->withView(
'admin/schedule/edit.twig', 'admin/schedule/edit',
[ [
'schedule' => $schedule, 'schedule' => $schedule,
'shift_types' => ShiftType::all()->sortBy('name')->pluck('name', 'id'), 'shift_types' => ShiftType::all()->sortBy('name')->pluck('name', 'id'),
@ -75,9 +75,6 @@ class ScheduleController extends BaseController
); );
} }
/**
* @throws ErrorException
*/
public function save(Request $request): Response public function save(Request $request): Response
{ {
$scheduleId = $request->getAttribute('schedule_id'); // optional $scheduleId = $request->getAttribute('schedule_id'); // optional
@ -103,10 +100,7 @@ class ScheduleController extends BaseController
'minutes_before' => 'int', 'minutes_before' => 'int',
'minutes_after' => 'int', 'minutes_after' => 'int',
] + $locationsValidation); ] + $locationsValidation);
ShiftType::findOrFail($data['shift_type']);
if (!ShiftType::find($data['shift_type'])) {
throw new ErrorException('schedule.import.invalid-shift-type');
}
$schedule->name = $data['name']; $schedule->name = $data['name'];
$schedule->url = $data['url']; $schedule->url = $data['url'];
@ -205,7 +199,7 @@ class ScheduleController extends BaseController
} }
return $this->response->withView( return $this->response->withView(
'admin/schedule/load.twig', 'admin/schedule/load',
[ [
'schedule_id' => $scheduleModel->id, 'schedule_id' => $scheduleModel->id,
'schedule' => $schedule, 'schedule' => $schedule,
@ -294,7 +288,7 @@ class ScheduleController extends BaseController
$this->log->info('Created schedule location "{location}"', ['location' => $room->getName()]); $this->log->info('Created schedule location "{location}"', ['location' => $room->getName()]);
} }
protected function fireDeleteShiftEntryEvents(Event $event, ScheduleUrl $schedule): void protected function fireDeleteShiftEntryEvents(Event $event, ScheduleModel $schedule): void
{ {
$shiftEntries = $this->db $shiftEntries = $this->db
->table('shift_entries') ->table('shift_entries')
@ -325,7 +319,7 @@ class ScheduleController extends BaseController
} }
} }
protected function createEvent(Event $event, int $shiftTypeId, Location $location, ScheduleModel $scheduleUrl): void protected function createEvent(Event $event, int $shiftTypeId, Location $location, ScheduleModel $schedule): void
{ {
$user = auth()->user(); $user = auth()->user();
$eventTimeZone = Carbon::now()->timezone; $eventTimeZone = Carbon::now()->timezone;
@ -337,17 +331,17 @@ class ScheduleController extends BaseController
$shift->end = $event->getEndDate()->copy()->timezone($eventTimeZone); $shift->end = $event->getEndDate()->copy()->timezone($eventTimeZone);
$shift->location()->associate($location); $shift->location()->associate($location);
$shift->url = $event->getUrl() ?? ''; $shift->url = $event->getUrl() ?? '';
$shift->transaction_id = Uuid::uuidBy($scheduleUrl->id, '5c4ed01e'); $shift->transaction_id = Uuid::uuidBy($schedule->id, '5c4ed01e');
$shift->createdBy()->associate($user); $shift->createdBy()->associate($user);
$shift->save(); $shift->save();
$scheduleShift = new ScheduleShift(['guid' => $event->getGuid()]); $scheduleShift = new ScheduleShift(['guid' => $event->getGuid()]);
$scheduleShift->schedule()->associate($scheduleUrl); $scheduleShift->schedule()->associate($schedule);
$scheduleShift->shift()->associate($shift); $scheduleShift->shift()->associate($shift);
$scheduleShift->save(); $scheduleShift->save();
$this->log->info( $this->log->info(
'Created schedule shift "{shift}" in "{location}" ({from} {to}, {guid})', 'Created schedule shift "{shift}" in "{location}" ({from} - {to}, {guid})',
[ [
'shift' => $shift->title, 'shift' => $shift->title,
'location' => $shift->location->name, 'location' => $shift->location->name,
@ -358,7 +352,7 @@ class ScheduleController extends BaseController
); );
} }
protected function updateEvent(Event $event, int $shiftTypeId, Location $location, ScheduleUrl $schedule): void protected function updateEvent(Event $event, int $shiftTypeId, Location $location, ScheduleModel $schedule): void
{ {
$user = auth()->user(); $user = auth()->user();
$eventTimeZone = Carbon::now()->timezone; $eventTimeZone = Carbon::now()->timezone;
@ -390,15 +384,15 @@ class ScheduleController extends BaseController
); );
} }
protected function deleteEvent(Event $event, ScheduleUrl $schedule): void protected function deleteEvent(Event $event, ScheduleModel $schedule): void
{ {
/** @var ScheduleShift $scheduleShift */ /** @var ScheduleShift $scheduleShift */
$scheduleShift = ScheduleShift::whereGuid($event->getGuid())->where('schedule_id', $schedule->id)->first(); $scheduleShift = ScheduleShift::whereGuid($event->getGuid())->where('schedule_id', $schedule->id)->first();
$shift = $scheduleShift->shift; $shift = $scheduleShift->shift;
$shift->delete();
$scheduleShift->delete();
$this->fireDeleteShiftEntryEvents($event, $schedule); $this->fireDeleteShiftEntryEvents($event, $schedule);
$shift->delete();
$scheduleShift->delete();
$this->log->info( $this->log->info(
'Deleted schedule shift "{shift}" in {location} ({from} {to}, {guid})', 'Deleted schedule shift "{shift}" in {location} ({from} {to}, {guid})',
@ -428,32 +422,38 @@ class ScheduleController extends BaseController
{ {
$scheduleId = (int) $request->getAttribute('schedule_id'); $scheduleId = (int) $request->getAttribute('schedule_id');
/** @var ScheduleModel $scheduleUrl */ /** @var ScheduleModel $scheduleModel */
$scheduleUrl = ScheduleModel::findOrFail($scheduleId); $scheduleModel = ScheduleModel::findOrFail($scheduleId);
try { try {
$scheduleResponse = $this->guzzle->get($scheduleUrl->url); $scheduleResponse = $this->guzzle->get($scheduleModel->url);
} catch (ConnectException | GuzzleException) { } catch (ConnectException | GuzzleException $e) {
$this->log->error('Exception during schedule request', ['exception' => $e]);
throw new ErrorException('schedule.import.request-error'); throw new ErrorException('schedule.import.request-error');
} }
if ($scheduleResponse->getStatusCode() != 200) { if ($scheduleResponse->getStatusCode() != 200) {
$this->log->warning(
'Problem during schedule request, got code {code}',
['code' => $scheduleResponse->getStatusCode()]
);
throw new ErrorException('schedule.import.request-error'); throw new ErrorException('schedule.import.request-error');
} }
$scheduleData = (string) $scheduleResponse->getBody(); $scheduleData = (string) $scheduleResponse->getBody();
if (!$this->parser->load($scheduleData)) { if (!$this->parser->load($scheduleData)) {
$this->log->warning('Problem during schedule parsing');
throw new ErrorException('schedule.import.read-error'); throw new ErrorException('schedule.import.read-error');
} }
$shiftType = $scheduleUrl->shift_type; $shiftType = $scheduleModel->shift_type;
$schedule = $this->parser->getSchedule(); $schedule = $this->parser->getSchedule();
$minutesBefore = $scheduleUrl->minutes_before; $minutesBefore = $scheduleModel->minutes_before;
$minutesAfter = $scheduleUrl->minutes_after; $minutesAfter = $scheduleModel->minutes_after;
$newRooms = $this->newRooms($schedule->getRooms()); $newRooms = $this->newRooms($schedule->getRooms());
return array_merge( return array_merge(
$this->shiftsDiff($schedule, $scheduleUrl, $shiftType, $minutesBefore, $minutesAfter), $this->shiftsDiff($schedule, $scheduleModel, $shiftType, $minutesBefore, $minutesAfter),
[$newRooms, $shiftType, $scheduleUrl, $schedule, $minutesBefore, $minutesAfter] [$newRooms, $shiftType, $scheduleModel, $schedule, $minutesBefore, $minutesAfter]
); );
} }
@ -482,7 +482,7 @@ class ScheduleController extends BaseController
*/ */
protected function shiftsDiff( protected function shiftsDiff(
Schedule $schedule, Schedule $schedule,
ScheduleModel $scheduleUrl, ScheduleModel $scheduleModel,
int $shiftType, int $shiftType,
int $minutesBefore, int $minutesBefore,
int $minutesAfter int $minutesAfter
@ -500,7 +500,7 @@ class ScheduleController extends BaseController
foreach ($schedule->getDay() as $day) { foreach ($schedule->getDay() as $day) {
foreach ($day->getRoom() as $room) { foreach ($day->getRoom() as $room) {
if (!$scheduleUrl->activeLocations->where('name', $room->getName())->count()) { if (!$scheduleModel->activeLocations->where('name', $room->getName())->count()) {
continue; continue;
} }
@ -519,7 +519,7 @@ class ScheduleController extends BaseController
} }
$scheduleEventsGuidList = array_keys($scheduleEvents); $scheduleEventsGuidList = array_keys($scheduleEvents);
$existingShifts = $this->getScheduleShiftsByGuid($scheduleUrl, $scheduleEventsGuidList); $existingShifts = $this->getScheduleShiftsByGuid($scheduleModel, $scheduleEventsGuidList);
foreach ($existingShifts as $scheduleShift) { foreach ($existingShifts as $scheduleShift) {
$guid = $scheduleShift->guid; $guid = $scheduleShift->guid;
$shift = $scheduleShift->shift; $shift = $scheduleShift->shift;
@ -545,7 +545,7 @@ class ScheduleController extends BaseController
$newEvents[$scheduleEvent->getGuid()] = $scheduleEvent; $newEvents[$scheduleEvent->getGuid()] = $scheduleEvent;
} }
$scheduleShifts = $this->getScheduleShiftsWhereNotGuid($scheduleUrl, $scheduleEventsGuidList); $scheduleShifts = $this->getScheduleShiftsWhereNotGuid($scheduleModel, $scheduleEventsGuidList);
foreach ($scheduleShifts as $scheduleShift) { foreach ($scheduleShifts as $scheduleShift) {
$event = $this->eventFromScheduleShift($scheduleShift); $event = $this->eventFromScheduleShift($scheduleShift);
$deleteEvents[$event->getGuid()] = $event; $deleteEvents[$event->getGuid()] = $event;
@ -588,11 +588,11 @@ class ScheduleController extends BaseController
* *
* @return Collection|ScheduleShift[] * @return Collection|ScheduleShift[]
*/ */
protected function getScheduleShiftsByGuid(ScheduleModel $scheduleUrl, array $events): Collection | array protected function getScheduleShiftsByGuid(ScheduleModel $schedule, array $events): Collection | array
{ {
return ScheduleShift::with('shift.location') return ScheduleShift::with('shift.location')
->whereIn('guid', $events) ->whereIn('guid', $events)
->where('schedule_id', $scheduleUrl->id) ->where('schedule_id', $schedule->id)
->get(); ->get();
} }
@ -600,11 +600,11 @@ class ScheduleController extends BaseController
* @param string[] $events * @param string[] $events
* @return Collection|ScheduleShift[] * @return Collection|ScheduleShift[]
*/ */
protected function getScheduleShiftsWhereNotGuid(ScheduleModel $scheduleUrl, array $events): Collection | array protected function getScheduleShiftsWhereNotGuid(ScheduleModel $schedule, array $events): Collection | array
{ {
return ScheduleShift::with('shift.location') return ScheduleShift::with('shift.location')
->whereNotIn('guid', $events) ->whereNotIn('guid', $events)
->where('schedule_id', $scheduleUrl->id) ->where('schedule_id', $schedule->id)
->get(); ->get();
} }
} }

View File

@ -0,0 +1,511 @@
<?php
declare(strict_types=1);
namespace Engelsystem\Test\Unit\Controllers\Admin;
use Engelsystem\Controllers\Admin\ScheduleController;
use Engelsystem\Controllers\HasUserNotifications;
use Engelsystem\Controllers\NotificationType;
use Engelsystem\Events\EventDispatcher;
use Engelsystem\Helpers\Authenticator;
use Engelsystem\Helpers\Schedule\Event as EventData;
use Engelsystem\Helpers\Schedule\Room as RoomData;
use Engelsystem\Helpers\Schedule\Schedule as ScheduleData;
use Engelsystem\Helpers\Uuid;
use Engelsystem\Http\Redirector;
use Engelsystem\Http\Request;
use Engelsystem\Http\Response;
use Engelsystem\Http\Validation\Validator;
use Engelsystem\Models\AngelType;
use Engelsystem\Models\Location;
use Engelsystem\Models\Shifts\Schedule;
use Engelsystem\Models\Shifts\ScheduleShift;
use Engelsystem\Models\Shifts\Shift;
use Engelsystem\Models\Shifts\ShiftEntry;
use Engelsystem\Models\Shifts\ShiftType;
use Engelsystem\Models\User\User;
use Engelsystem\Test\Unit\Controllers\ControllerTest;
use Engelsystem\Test\Unit\HasDatabase;
use GuzzleHttp\Client;
use GuzzleHttp\Exception\RequestException;
use GuzzleHttp\Handler\MockHandler;
use GuzzleHttp\HandlerStack;
use Illuminate\Database\Eloquent\ModelNotFoundException;
use PHPUnit\Framework\MockObject\MockObject;
class ScheduleControllerTest extends ControllerTest
{
use HasDatabase;
use HasUserNotifications;
protected AngelType $angelType;
protected EventDispatcher | MockObject $event;
protected Location $location;
protected Shift $oldShift;
protected Redirector | MockObject $redirect;
protected Schedule $schedule;
protected string $scheduleFile = __DIR__ . '/../../Helpers/Schedule/Assets/schedule-multiple.xml';
protected ShiftType $shiftType;
protected User $user;
protected Validator $validator;
/**
* @covers \Engelsystem\Controllers\Admin\ScheduleController::index
* @covers \Engelsystem\Controllers\Admin\ScheduleController::__construct
*/
public function testIndex(): void
{
$this->response->expects($this->once())
->method('withView')
->willReturnCallback(function (string $view, array $data) {
$this->assertEquals('admin/schedule/index', $view);
$this->assertArrayHasKey('schedules', $data);
/** @var Schedule[] $schedules */
$schedules = $data['schedules'];
$this->assertNotEmpty($schedules);
$this->assertEquals('Foo Schedule', $schedules[0]->name);
return $this->response;
});
/** @var ScheduleController $controller */
$controller = $this->app->make(ScheduleController::class);
$response = $controller->index();
$this->assertEquals($this->response, $response);
}
/**
* @covers \Engelsystem\Controllers\Admin\ScheduleController::edit
*/
public function testEdit(): void
{
$this->response->expects($this->once())
->method('withView')
->willReturnCallback(function (string $view, array $data) {
$this->assertEquals('admin/schedule/edit', $view);
$this->assertArrayHasKey('schedule', $data);
/** @var Schedule $schedule */
$schedule = $data['schedule'];
$this->assertNotEmpty($schedule);
$this->assertEquals('Foo Schedule', $schedule->name);
return $this->response;
});
$request = $this->request->withAttribute('schedule_id', $this->schedule->id);
/** @var ScheduleController $controller */
$controller = $this->app->make(ScheduleController::class);
$response = $controller->edit($request);
$this->assertEquals($this->response, $response);
}
/**
* @covers \Engelsystem\Controllers\Admin\ScheduleController::save
*/
public function testSaveNew(): void
{
$newId = $this->schedule->id + 1;
$this->setExpects($this->redirect, 'to', ['/admin/schedule/load/' . $newId], $this->response);
$request = Request::create('', 'POST', [
'name' => 'Name',
'url' => 'https://example.test/schedule.xml',
'shift_type' => $this->shiftType->id,
'location_' . $this->location->id => 1,
'minutes_before' => 20,
'minutes_after' => 25,
]);
/** @var ScheduleController $controller */
$controller = $this->app->make(ScheduleController::class);
$controller->setValidator($this->validator);
$response = $controller->save($request);
$this->assertEquals($this->response, $response);
$schedule = Schedule::find($newId);
$this->assertNotNull($schedule);
$this->assertEquals('Name', $schedule->name);
$this->assertEquals('https://example.test/schedule.xml', $schedule->url);
$this->assertEquals($this->shiftType->id, $schedule->shift_type);
$this->assertEquals(20, $schedule->minutes_before);
$this->assertEquals(25, $schedule->minutes_after);
$this->assertCount(1, $schedule->activeLocations);
/** @var Location $location */
$location = $schedule->activeLocations->first();
$this->assertEquals($this->location->id, $location->id);
$this->assertHasNotification('schedule.edit.success');
$this->assertTrue($this->log->hasInfoThatContains('Schedule {name}'));
}
/**
* @covers \Engelsystem\Controllers\Admin\ScheduleController::save
*/
public function testSaveEdit(): void
{
$this->setExpects($this->redirect, 'to', ['/admin/schedule/load/' . $this->schedule->id], $this->response);
$shiftType = ShiftType::factory()->create();
$request = Request::create('', 'POST', [
'name' => 'New name',
'url' => 'https://test.example/schedule.xml',
'shift_type' => $shiftType->id,
'minutes_before' => 10,
'minutes_after' => 5,
])
->withAttribute('schedule_id', $this->schedule->id);
/** @var ScheduleController $controller */
$controller = $this->app->make(ScheduleController::class);
$controller->setValidator($this->validator);
$response = $controller->save($request);
$this->assertEquals($this->response, $response);
$schedule = Schedule::find($this->schedule->id);
$this->assertNotNull($schedule);
$this->assertEquals('New name', $schedule->name);
$this->assertEquals('https://test.example/schedule.xml', $schedule->url);
$this->assertEquals($shiftType->id, $schedule->shift_type);
$this->assertEquals(10, $schedule->minutes_before);
$this->assertEquals(5, $schedule->minutes_after);
$this->assertHasNotification('schedule.edit.success');
$this->assertTrue($this->log->hasInfoThatContains('Schedule {name}'));
}
/**
* @covers \Engelsystem\Controllers\Admin\ScheduleController::save
*/
public function testSaveInvalidShiftType(): void
{
$request = Request::create('', 'POST', [
'name' => 'Test',
'url' => 'https://test.example',
'shift_type' => 1337,
'minutes_before' => 0,
'minutes_after' => 0,
]);
/** @var ScheduleController $controller */
$controller = $this->app->make(ScheduleController::class);
$controller->setValidator($this->validator);
$this->expectException(ModelNotFoundException::class);
$controller->save($request);
}
/**
* @covers \Engelsystem\Controllers\Admin\ScheduleController::save
* @covers \Engelsystem\Controllers\Admin\ScheduleController::delete
* @covers \Engelsystem\Controllers\Admin\ScheduleController::fireDeleteShiftEntryEvents
* @covers \Engelsystem\Controllers\Admin\ScheduleController::deleteEvent
*/
public function testSaveDelete(): void
{
$this->setExpects($this->redirect, 'to', ['/admin/schedule'], $this->response);
$this->event->expects($this->exactly(2))
->method('dispatch')
->with('shift.entry.deleting')
->willReturn([]);
$request = Request::create('', 'POST', ['delete' => 'yes'])
->withAttribute('schedule_id', $this->schedule->id);
/** @var ScheduleController $controller */
$controller = $this->app->make(ScheduleController::class);
$response = $controller->save($request);
$this->assertEquals($this->response, $response);
$this->assertNull(Schedule::find($this->schedule->id));
$this->assertHasNotification('schedule.delete.success');
$this->assertTrue($this->log->hasInfoThatContains('Deleted schedule shift'));
$this->assertTrue($this->log->hasInfoThatContains('Schedule {name}'));
}
/**
* @covers \Engelsystem\Controllers\Admin\ScheduleController::loadSchedule
* @covers \Engelsystem\Controllers\Admin\ScheduleController::getScheduleData
*/
public function testLoadSchedule(): void
{
$this->setScheduleResponses([new Response(file_get_contents($this->scheduleFile))]);
$this->response->expects($this->once())
->method('withView')
->willReturnCallback(function (string $view, array $data) {
$this->assertEquals('admin/schedule/load', $view);
$this->assertArrayHasKey('schedule', $data);
/** @var ScheduleData $scheduleData */
$scheduleData = $data['schedule'];
$this->assertInstanceOf(ScheduleData::class, $scheduleData);
$this->assertArrayHasKey('locations', $data);
$this->assertArrayHasKey('add', $data['locations']);
/** @var RoomData[] $roomData */
$roomData = $data['locations']['add'];
$this->assertNotEmpty($roomData);
$this->assertInstanceOf(RoomData::class, $roomData[0]);
$this->assertArrayHasKey('shifts', $data);
foreach (['add', 'update', 'delete'] as $type) {
$this->assertArrayHasKey($type, $data['shifts']);
/** @var EventData[] $eventData */
$eventData = $data['shifts'][$type];
$this->assertNotEmpty($eventData);
$this->assertInstanceOf(EventData::class, $eventData[array_key_first($eventData)]);
}
return $this->response;
});
$request = Request::create('', 'POST')
->withAttribute('schedule_id', $this->schedule->id);
/** @var ScheduleController $controller */
$controller = $this->app->make(ScheduleController::class);
$response = $controller->loadSchedule($request);
$this->assertEquals($this->response, $response);
}
public function loadScheduleErrorsData(): array
{
return [
// Server error
[new RequestException('Error Communicating with Server', new Request()), 'schedule.import.request-error'],
// Not found
[new Response('', 202), 'schedule.import.request-error', true],
// Decoding error
[new Response(''), 'schedule.import.read-error', true],
];
}
/**
* @covers \Engelsystem\Controllers\Admin\ScheduleController::loadSchedule
* @covers \Engelsystem\Controllers\Admin\ScheduleController::getScheduleData
* @dataProvider loadScheduleErrorsData
*/
public function testScheduleResponseErrors(object $request, string $notification, bool $logWarning = false): void
{
$this->setScheduleResponses([$request]);
$this->setExpects($this->redirect, 'back', null, $this->response);
$request = Request::create('', 'POST')
->withAttribute('schedule_id', $this->schedule->id);
/** @var ScheduleController $controller */
$controller = $this->app->make(ScheduleController::class);
$response = $controller->loadSchedule($request);
$this->assertEquals($this->response, $response);
$this->assertHasNotification($notification, NotificationType::ERROR);
if ($logWarning) {
$this->assertTrue($this->log->hasWarningThatContains(' during schedule '));
} else {
$this->assertTrue($this->log->hasErrorThatContains(' during schedule '));
}
}
/**
* @covers \Engelsystem\Controllers\Admin\ScheduleController::getScheduleData
*/
public function testGetScheduleDataScheduleNotFound(): void
{
$request = Request::create('', 'POST', ['delete' => 'yes'])
->withAttribute('schedule_id', 42);
/** @var ScheduleController $controller */
$controller = $this->app->make(ScheduleController::class);
$this->expectException(ModelNotFoundException::class);
$controller->loadSchedule($request);
}
/**
* @covers \Engelsystem\Controllers\Admin\ScheduleController::importSchedule
* @covers \Engelsystem\Controllers\Admin\ScheduleController::getScheduleData
* @covers \Engelsystem\Controllers\Admin\ScheduleController::newRooms
* @covers \Engelsystem\Controllers\Admin\ScheduleController::shiftsDiff
* @covers \Engelsystem\Controllers\Admin\ScheduleController::getScheduleShiftsByGuid
* @covers \Engelsystem\Controllers\Admin\ScheduleController::getScheduleShiftsWhereNotGuid
* @covers \Engelsystem\Controllers\Admin\ScheduleController::eventFromScheduleShift
* @covers \Engelsystem\Controllers\Admin\ScheduleController::fireUpdateShiftUpdateEvent
* @covers \Engelsystem\Controllers\Admin\ScheduleController::getAllLocations
* @covers \Engelsystem\Controllers\Admin\ScheduleController::createLocation
* @covers \Engelsystem\Controllers\Admin\ScheduleController::createEvent
* @covers \Engelsystem\Controllers\Admin\ScheduleController::updateEvent
*/
public function testImportSchedule(): void
{
$this->setScheduleResponses([new Response(file_get_contents($this->scheduleFile))]);
$this->setExpects($this->redirect, 'to', ['/admin/schedule'], $this->response);
$request = Request::create('', 'POST')
->withAttribute('schedule_id', $this->schedule->id);
$this->event->expects($this->exactly(2))
->method('dispatch')
->withConsecutive(['shift.updating'], ['shift.entry.deleting'])
->willReturn([]);
/** @var ScheduleController $controller */
$controller = $this->app->make(ScheduleController::class);
$response = $controller->importSchedule($request);
$this->assertEquals($this->response, $response);
$this->assertTrue($this->log->hasInfoThatContains('Started schedule'));
$this->assertTrue($this->log->hasInfoThatContains('Created schedule location'));
$this->assertTrue($this->log->hasInfoThatContains('Created schedule shift'));
$this->assertTrue($this->log->hasInfoThatContains('Updated schedule shift'));
$this->assertTrue($this->log->hasInfoThatContains('Deleted schedule shift'));
$this->assertTrue($this->log->hasInfoThatContains('Ended schedule'));
$this->assertHasNotification('schedule.import.success');
$this->assertCount(4, $this->schedule->shifts);
$location = Location::whereName('Example Room')->get();
$this->assertCount(1, $location);
$location2 = Location::whereName('Another Room')->get();
$this->assertCount(1, $location2);
$location3 = Location::whereName('Third Room')->get();
$this->assertCount(1, $location3);
/** @var Location $location */
$location = $location->first();
/** @var Location $location2 */
$location2 = $location2->first();
/** @var Location $location3 */
$location3 = $location3->first();
$this->assertCount(1, $location->shifts);
$this->assertCount(3, $location2->shifts);
$this->assertCount(0, $location3->shifts);
// Deleted shift
$this->assertNull(Shift::find($this->oldShift->id));
// Updated shift
/** @var ScheduleShift $scheduleShift */
$scheduleShift = ScheduleShift::whereGuid('3e896c59-0d90-4817-8f74-af7fbb758f32')->first();
$this->assertNotEmpty($scheduleShift);
$shift = $scheduleShift->shift;
$this->assertEquals('First event [DE]', $shift->title);
$this->assertEquals('https://example.com/first-1-event', $shift->url);
$this->assertEquals('2042-10-02 09:45:00', $shift->start->toDateTimeString());
$this->assertEquals('2042-10-02 11:45:00', $shift->end->toDateTimeString());
$this->assertEquals($this->shiftType->id, $shift->shift_type_id);
$this->assertEquals($location->id, $shift->location_id);
$this->assertEquals($this->user->id, $shift->updated_by);
// Created shift
/** @var ScheduleShift $scheduleShift */
$scheduleShift = ScheduleShift::whereGuid('6e662ec5-d18d-417d-8719-360a416bb153')->first();
$this->assertNotEmpty($scheduleShift);
$shift = $scheduleShift->shift;
$this->assertEquals('Third event', $shift->title);
$this->assertEquals('https://example.com/third-3-event', $shift->url);
$this->assertEquals('2042-10-02 10:45:00', $shift->start->toDateTimeString());
$this->assertEquals('2042-10-02 11:45:00', $shift->end->toDateTimeString());
$this->assertEquals($this->shiftType->id, $shift->shift_type_id);
$this->assertEquals($location2->id, $shift->location_id);
$this->assertEquals($this->user->id, $shift->created_by);
$this->assertNull($shift->updated_by);
}
/**
* @covers \Engelsystem\Controllers\Admin\ScheduleController::importSchedule
* @covers \Engelsystem\Controllers\Admin\ScheduleController::getScheduleData
*/
public function testImportScheduleError(): void
{
$this->setScheduleResponses([new Response('', 404)]);
$this->setExpects($this->redirect, 'back', null, $this->response);
$request = Request::create('', 'POST')
->withAttribute('schedule_id', $this->schedule->id);
/** @var ScheduleController $controller */
$controller = $this->app->make(ScheduleController::class);
$response = $controller->importSchedule($request);
$this->assertEquals($this->response, $response);
$this->assertHasNotification('schedule.import.request-error', NotificationType::ERROR);
}
protected function setScheduleResponses(array $queue): void
{
$handler = new MockHandler($queue);
$guzzle = new Client(['handler' => HandlerStack::create($handler)]);
$this->app->instance(Client::class, $guzzle);
}
public function setUp(): void
{
parent::setUp();
$this->validator = new Validator();
$this->redirect = $this->createMock(Redirector::class);
$this->app->instance('redirect', $this->redirect);
$this->event = $this->createMock(EventDispatcher::class);
$this->app->instance('events.dispatcher', $this->event);
$this->shiftType = ShiftType::factory()->create();
$this->location = Location::factory()->create(['name' => 'Example Room']);
$location3 = Location::factory()->create(['name' => 'Another Room']);
$this->angelType = AngelType::factory()->create();
$this->schedule = Schedule::factory()->create([
'shift_type' => $this->shiftType->id,
'name' => 'Foo Schedule',
'needed_from_shift_type' => false,
]);
$this->schedule->activeLocations()->attach($this->location);
$this->schedule->activeLocations()->attach($location3);
/** @var Shift[] $shifts */
$shifts = Shift::factory(3)->create([
'location_id' => $this->location->id,
'shift_type_id' => $this->shiftType->id,
]);
foreach ($shifts as $shift) {
(new ScheduleShift([
'shift_id' => $shift->id,
'schedule_id' => $this->schedule->id,
'guid' => Uuid::uuid(),
]))->save();
}
$this->oldShift = $shifts[1];
/** @var ScheduleShift $firstScheduleShift */
$firstScheduleShift = ScheduleShift::query()->first();
$firstScheduleShift->guid = '3e896c59-0d90-4817-8f74-af7fbb758f32';
$firstScheduleShift->save();
$firstShift = $firstScheduleShift->shift;
$firstShift->neededAngelTypes()->create(['angel_type_id' => $this->angelType->id, 'count' => 3]);
$this->user = User::factory()->create();
// Shift from import
ShiftEntry::factory()->create([
'shift_id' => $firstShift->id,
'angel_type_id' => $this->angelType->id,
'user_id' => $this->user->id,
]);
// Shift from some previous import
ShiftEntry::factory()->create([
'shift_id' => $this->oldShift->id,
'angel_type_id' => $this->angelType->id,
'user_id' => $this->user->id,
]);
$authenticator = $this->createMock(Authenticator::class);
$this->setExpects($authenticator, 'user', null, $this->user, $this->any());
$this->app->instance('authenticator', $authenticator);
}
}

View File

@ -0,0 +1,90 @@
<?xml version='1.0' encoding='utf-8' ?>
<schedule
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="https://c3voc.de/schedule/schema.xsd"
>
<version>4.2-frickel</version>
<conference>
<title>Some Bigger Test Event</title>
<acronym>test-4</acronym>
<start>2042-10-01</start>
<end>2042-10-02</end>
<days>2</days>
<timeslot_duration>00:30</timeslot_duration>
</conference>
<day index='1' date='2042-10-01' start='2042-10-01T10:00:00+02:00' end='2042-10-01T22:59:00+02:00'>
<room name='Example Room'>
<event guid='3e896c59-0d90-4817-8f74-af7fbb758f32' id='1'>
<date>2042-10-02T10:00:00+00:00</date>
<title>First event</title>
<subtitle>With a subtitle</subtitle>
<start>10:00</start>
<duration>01:30</duration>
<room>Example Room</room>
<slug>first-1-event</slug>
<track>Testing</track>
<type>Talk</type>
<abstract>A minimal description</abstract>
<url>https://example.com/first-1-event</url>
<language>DE</language>
</event>
</room>
<room name='Another Room'>
<event guid='bf675f51-2a3d-4fa9-a5b2-390d5e5bdff2' id='2'>
<date>2042-10-02T13:30:00+00:00</date>
<title>Second event</title>
<subtitle>With a subtitle</subtitle>
<start>13:30</start>
<duration>00:30</duration>
<room>Another Room</room>
<slug>second-2-event</slug>
<track>Tinkering</track>
<type>Talk</type>
<abstract>A minimal description</abstract>
</event>
</room>
</day>
<day index='2' date='2042-10-02' start='2042-10-02T10:00:00+02:00' end='2042-10-02T22:59:00+02:00'>
<room name='Another Room'>
<event guid='6e662ec5-d18d-417d-8719-360a416bb153' id='3'>
<date>2042-10-02T11:00:00+00:00</date>
<title>Third event</title>
<subtitle>With a subtitle</subtitle>
<start>11:00</start>
<duration>00:30</duration>
<room>Another Room</room>
<slug>third-3-event</slug>
<track>Testing</track>
<type>Talk</type>
<abstract>A minimal description</abstract>
<url>https://example.com/third-3-event</url>
</event>
<event guid='c6999865-5329-43f2-8aca-85ae39932d09' id='42'>
<date>2042-10-02T11:45:00+00:00</date>
<title>Fourth event</title>
<subtitle>With a subtitle</subtitle>
<start>11:45</start>
<duration>00:30</duration>
<room>Another Room</room>
<slug>fourth-42-event</slug>
<track>Testing</track>
<type>Talk</type>
<abstract>A minimal description</abstract>
</event>
</room>
<room name='Third Room'>
<event guid='1248925b-f608-4ebc-8d37-7a22fadc51a4' id='55'>
<date>2042-10-02T10:00:00+00:00</date>
<title>Fifth event</title>
<subtitle>Will be ignored</subtitle>
<start>10:00</start>
<duration>01:00</duration>
<room>Third Room</room>
<slug>fifth-55-event</slug>
<track>Testing</track>
<type>Talk</type>
<abstract/>
</event>
</room>
</day>
</schedule>