add test for admin EnsembleController
This commit is contained in:
parent
fa25e76c5b
commit
e1d72ee040
|
|
@ -1,10 +1,10 @@
|
||||||
<component name="ProjectRunConfigurationManager">
|
<component name="ProjectRunConfigurationManager">
|
||||||
<configuration default="false" name="tests - paralell" type="PestRunConfigurationType">
|
<configuration default="false" name="tests - paralell with HTML to reports" type="PestRunConfigurationType">
|
||||||
<option name="pestRunnerSettings">
|
<option name="pestRunnerSettings">
|
||||||
<PestRunner directory="$PROJECT_DIR$/tests" method="" options="--parallel --recreate-databases" />
|
<PestRunner directory="$PROJECT_DIR$/tests" method="" options="--parallel --recreate-databases --coverage-html reports" />
|
||||||
</option>
|
</option>
|
||||||
<option name="runnerSettings">
|
<option name="runnerSettings">
|
||||||
<PhpTestRunnerSettings directory="$PROJECT_DIR$/tests" method="" options="--parallel --recreate-databases" />
|
<PhpTestRunnerSettings directory="$PROJECT_DIR$/tests" method="" options="--parallel --recreate-databases --coverage-html reports" />
|
||||||
</option>
|
</option>
|
||||||
<method v="2" />
|
<method v="2" />
|
||||||
</configuration>
|
</configuration>
|
||||||
|
|
@ -3,12 +3,13 @@
|
||||||
namespace App\Http\Controllers\Admin;
|
namespace App\Http\Controllers\Admin;
|
||||||
|
|
||||||
use App\Http\Controllers\Controller;
|
use App\Http\Controllers\Controller;
|
||||||
|
use App\Http\Requests\EnsembleStoreOrUpdateRequest;
|
||||||
use App\Models\Ensemble;
|
use App\Models\Ensemble;
|
||||||
use App\Models\Event;
|
use App\Models\Event;
|
||||||
use App\Models\SeatingLimit;
|
use App\Models\SeatingLimit;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
use Illuminate\Support\Facades\Auth;
|
|
||||||
use Illuminate\Support\Facades\Cache;
|
use Illuminate\Support\Facades\Cache;
|
||||||
|
use Illuminate\Support\Facades\Log;
|
||||||
|
|
||||||
use function redirect;
|
use function redirect;
|
||||||
|
|
||||||
|
|
@ -21,30 +22,24 @@ class EnsembleController extends Controller
|
||||||
return view('admin.ensembles.index', compact('events'));
|
return view('admin.ensembles.index', compact('events'));
|
||||||
}
|
}
|
||||||
|
|
||||||
public function store(Request $request)
|
public function store(EnsembleStoreOrUpdateRequest $request)
|
||||||
{
|
{
|
||||||
if (! Auth::user()->is_admin) {
|
Log::channel('file')->warning('hello');
|
||||||
abort(403);
|
$validated = $request->validated();
|
||||||
}
|
// get the maximum value of rank from the ensemble table where event_id is equal to the request event_id
|
||||||
request()->validate([
|
|
||||||
'name' => 'required',
|
|
||||||
'code' => ['required', 'max:6'],
|
|
||||||
'event_id' => ['required', 'exists:events,id'],
|
|
||||||
]);
|
|
||||||
// get the maximum value of rank from the ensembles table where event_id is equal to the request event_id
|
|
||||||
$maxCode = Ensemble::where('event_id', request('event_id'))->max('rank');
|
$maxCode = Ensemble::where('event_id', request('event_id'))->max('rank');
|
||||||
|
|
||||||
Ensemble::create([
|
Ensemble::create([
|
||||||
'name' => request('name'),
|
'name' => $validated['name'],
|
||||||
'code' => request('code'),
|
'code' => $validated['code'],
|
||||||
'event_id' => request('event_id'),
|
'event_id' => $validated['event_id'],
|
||||||
'rank' => $maxCode + 1,
|
'rank' => $maxCode + 1,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
return redirect()->route('admin.ensembles.index')->with('success', 'Ensemble created successfully');
|
return redirect()->route('admin.ensembles.index')->with('success', 'Ensemble created successfully');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function destroy(Request $request, Ensemble $ensemble)
|
public function destroy(Ensemble $ensemble)
|
||||||
{
|
{
|
||||||
if ($ensemble->seats->count() > 0) {
|
if ($ensemble->seats->count() > 0) {
|
||||||
return redirect()->route('admin.ensembles.index')->with('error',
|
return redirect()->route('admin.ensembles.index')->with('error',
|
||||||
|
|
@ -55,25 +50,32 @@ class EnsembleController extends Controller
|
||||||
return redirect()->route('admin.ensembles.index')->with('success', 'Ensemble deleted successfully');
|
return redirect()->route('admin.ensembles.index')->with('success', 'Ensemble deleted successfully');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function updateEnsemble(Request $request, Ensemble $ensemble)
|
public function update(EnsembleStoreOrUpdateRequest $request, Ensemble $ensemble)
|
||||||
{
|
{
|
||||||
request()->validate([
|
$valid = $request->validated();
|
||||||
'name' => 'required',
|
|
||||||
'code' => 'required|max:6',
|
|
||||||
]);
|
|
||||||
|
|
||||||
$ensemble->update([
|
$ensemble->update([
|
||||||
'name' => request('name'),
|
'name' => $valid['name'],
|
||||||
'code' => request('code'),
|
'code' => $valid['code'],
|
||||||
]);
|
]);
|
||||||
|
|
||||||
return redirect()->route('admin.ensembles.index')->with('success', 'Ensemble updated successfully');
|
return redirect()->route('admin.ensembles.index')->with('success', 'Ensemble updated successfully');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//TODO Consider moving seating limit related functions to their own controller with index, edit, and update methods
|
||||||
public function seatingLimits(Ensemble $ensemble)
|
public function seatingLimits(Ensemble $ensemble)
|
||||||
{
|
{
|
||||||
$limits = [];
|
$limits = [];
|
||||||
$ensembles = Ensemble::with(['event'])->orderBy('event_id')->get();
|
/**
|
||||||
|
* If we weren't called with an ensemble, we're going to use an array of ensembles to fill a drop-down and
|
||||||
|
* choose one. The user will be sent back here, this time with the chosen audition.
|
||||||
|
*/
|
||||||
|
$ensembles = Ensemble::with(['event'])->orderBy('event_id')->orderBy('rank')->get();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If we were called with an ensemble, we need to load existing seating limits. We will put them in an array
|
||||||
|
* indexed by audition_id for easy use in the form to set seating limits.
|
||||||
|
*/
|
||||||
if ($ensemble->exists()) {
|
if ($ensemble->exists()) {
|
||||||
$ensemble->load('seatingLimits');
|
$ensemble->load('seatingLimits');
|
||||||
foreach ($ensemble->seatingLimits as $lim) {
|
foreach ($ensemble->seatingLimits as $lim) {
|
||||||
|
|
@ -112,10 +114,6 @@ class EnsembleController extends Controller
|
||||||
|
|
||||||
public function updateEnsembleRank(Request $request)
|
public function updateEnsembleRank(Request $request)
|
||||||
{
|
{
|
||||||
if (! Auth::user()->is_admin) {
|
|
||||||
abort(403);
|
|
||||||
}
|
|
||||||
|
|
||||||
$order = $request->input('order');
|
$order = $request->input('order');
|
||||||
$eventId = $request->input('event_id');
|
$eventId = $request->input('event_id');
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,33 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Requests;
|
||||||
|
|
||||||
|
use Illuminate\Foundation\Http\FormRequest;
|
||||||
|
use Illuminate\Validation\Rule;
|
||||||
|
|
||||||
|
class EnsembleStoreOrUpdateRequest extends FormRequest
|
||||||
|
{
|
||||||
|
public function authorize(): true
|
||||||
|
{
|
||||||
|
// Adjust authorization logic as needed
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function rules(): array
|
||||||
|
{
|
||||||
|
// Get the ID of the ensemble being updated, if any
|
||||||
|
$ensembleId = $this->route('ensemble')?->id;
|
||||||
|
|
||||||
|
return [
|
||||||
|
'name' => [
|
||||||
|
'required',
|
||||||
|
// Composite unique rule on (event_id, name)
|
||||||
|
Rule::unique('ensembles')->where(function ($query) {
|
||||||
|
return $query->where('event_id', $this->input('event_id'));
|
||||||
|
})->ignore($ensembleId),
|
||||||
|
],
|
||||||
|
'code' => ['required', 'max:6'],
|
||||||
|
'event_id' => ['required', 'exists:events,id'],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -74,7 +74,7 @@ Route::middleware(['auth', 'verified', CheckIfAdmin::class])->prefix('admin/')->
|
||||||
Route::post('/', 'store')->name('admin.ensembles.store');
|
Route::post('/', 'store')->name('admin.ensembles.store');
|
||||||
Route::delete('/{ensemble}', 'destroy')->name('admin.ensembles.destroy');
|
Route::delete('/{ensemble}', 'destroy')->name('admin.ensembles.destroy');
|
||||||
Route::post('/updateEnsembleRank', 'updateEnsembleRank')->name('admin.ensembles.updateEnsembleRank');
|
Route::post('/updateEnsembleRank', 'updateEnsembleRank')->name('admin.ensembles.updateEnsembleRank');
|
||||||
Route::patch('/{ensemble}', 'updateEnsemble')->name('admin.ensembles.update');
|
Route::patch('/{ensemble}', 'update')->name('admin.ensembles.update');
|
||||||
Route::get('/seating-limits', 'seatingLimits')->name('admin.ensembles.seatingLimits');
|
Route::get('/seating-limits', 'seatingLimits')->name('admin.ensembles.seatingLimits');
|
||||||
Route::get('/seating-limits/{ensemble}', 'seatingLimits')->name('admin.ensembles.seatingLimits.ensemble');
|
Route::get('/seating-limits/{ensemble}', 'seatingLimits')->name('admin.ensembles.seatingLimits.ensemble');
|
||||||
Route::post('/seating-limits/{ensemble}',
|
Route::post('/seating-limits/{ensemble}',
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,209 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
use App\Models\Audition;
|
||||||
|
use App\Models\Ensemble;
|
||||||
|
use App\Models\Entry;
|
||||||
|
use App\Models\Event;
|
||||||
|
use App\Models\Seat;
|
||||||
|
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||||
|
|
||||||
|
uses(RefreshDatabase::class);
|
||||||
|
beforeEach(function () {
|
||||||
|
$this->ensemble = Ensemble::create([
|
||||||
|
'name' => 'Wind Ensemble',
|
||||||
|
'rank' => 1,
|
||||||
|
'code' => 'we',
|
||||||
|
'event_id' => Event::factory()->create()->id,
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('EnsembleController::index', function () {
|
||||||
|
it('denies access to non-admin users', function () {
|
||||||
|
$this->get(route('admin.ensembles.index'))->assertRedirect(route('home'));
|
||||||
|
actAsNormal();
|
||||||
|
$this->get(route('admin.ensembles.index'))->assertRedirect(route('dashboard'));
|
||||||
|
actAsTab();
|
||||||
|
$this->get(route('admin.ensembles.index'))->assertRedirect(route('dashboard'));
|
||||||
|
});
|
||||||
|
it('shows an index of events', function () {
|
||||||
|
actAsAdmin();
|
||||||
|
$this->get(route('admin.ensembles.index'))->assertOk()
|
||||||
|
->assertSee($this->ensemble->name);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('EnsembleController::store', function () {
|
||||||
|
it('denies access to non-admin users', function () {
|
||||||
|
$this->post(route('admin.ensembles.store'))->assertRedirect(route('home'));
|
||||||
|
actAsNormal();
|
||||||
|
$this->post(route('admin.ensembles.store'))->assertRedirect(route('dashboard'));
|
||||||
|
actAsTab();
|
||||||
|
$this->post(route('admin.ensembles.store'))->assertRedirect(route('dashboard'));
|
||||||
|
});
|
||||||
|
it('creates an ensemble', function () {
|
||||||
|
actAsAdmin();
|
||||||
|
$this->post(route('admin.ensembles.store'), [
|
||||||
|
'name' => 'New Ensemble',
|
||||||
|
'code' => 'ne',
|
||||||
|
'event_id' => Event::factory()->create()->id,
|
||||||
|
])->assertRedirect(route('admin.ensembles.index'))->assertSessionHas('success');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('EnsembleController::destroy', function () {
|
||||||
|
it('denies access to non-admin users', function () {
|
||||||
|
$this->delete(route('admin.ensembles.destroy', $this->ensemble))->assertRedirect(route('home'));
|
||||||
|
actAsNormal();
|
||||||
|
$this->delete(route('admin.ensembles.destroy', $this->ensemble))->assertRedirect(route('dashboard'));
|
||||||
|
actAsTab();
|
||||||
|
$this->delete(route('admin.ensembles.destroy', $this->ensemble))->assertRedirect(route('dashboard'));
|
||||||
|
});
|
||||||
|
it('will not destroy an ensemble with seated students', function () {
|
||||||
|
actAsAdmin();
|
||||||
|
$audition = Audition::factory()->create();
|
||||||
|
$entry = Entry::factory()->create();
|
||||||
|
Seat::create([
|
||||||
|
'ensemble_id' => $this->ensemble->id,
|
||||||
|
'audition_id' => $audition->id,
|
||||||
|
'seat' => 3,
|
||||||
|
'entry_id' => $entry->id,
|
||||||
|
]);
|
||||||
|
$this->delete(route('admin.ensembles.destroy', $this->ensemble))->assertRedirect(route('admin.ensembles.index'))
|
||||||
|
->assertSessionHas('error', 'Ensemble has students seated and cannot be deleted');
|
||||||
|
});
|
||||||
|
it('can delete an ensemble', function () {
|
||||||
|
$startCount = Ensemble::count();
|
||||||
|
actAsAdmin();
|
||||||
|
$this->delete(route('admin.ensembles.destroy', $this->ensemble))->assertRedirect(route('admin.ensembles.index'))
|
||||||
|
->assertSessionHas('success', 'Ensemble deleted successfully');
|
||||||
|
expect(Ensemble::count())->toEqual($startCount - 1);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('EnsembleController::update', function () {
|
||||||
|
it('denies access to non-admin users', function () {
|
||||||
|
$this->patch(route('admin.ensembles.update', $this->ensemble))->assertRedirect(route('home'));
|
||||||
|
actAsNormal();
|
||||||
|
$this->patch(route('admin.ensembles.update', $this->ensemble))->assertRedirect(route('dashboard'));
|
||||||
|
actAsTab();
|
||||||
|
$this->patch(route('admin.ensembles.update', $this->ensemble))->assertRedirect(route('dashboard'));
|
||||||
|
});
|
||||||
|
it('can update an event', function () {
|
||||||
|
$event = Event::factory()->create();
|
||||||
|
actAsAdmin();
|
||||||
|
$response = $this->patch(route('admin.ensembles.update', $this->ensemble), [
|
||||||
|
'name' => 'Wind Ensemble Restart',
|
||||||
|
'code' => 'we2',
|
||||||
|
'event_id' => $event->id,
|
||||||
|
]);
|
||||||
|
$response->assertRedirect(route('admin.ensembles.index'))
|
||||||
|
->assertSessionHas('success', 'Ensemble updated successfully');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('EnsembleController::seatingLimits with no ensemble', function () {
|
||||||
|
|
||||||
|
it('denies access to non-admin users', function () {
|
||||||
|
$this->get(route('admin.ensembles.seatingLimits'))->assertRedirect(route('home'));
|
||||||
|
actAsNormal();
|
||||||
|
$this->get(route('admin.ensembles.seatingLimits'))->assertRedirect(route('dashboard'));
|
||||||
|
actAsTab();
|
||||||
|
$this->get(route('admin.ensembles.seatingLimits'))->assertRedirect(route('dashboard'));
|
||||||
|
});
|
||||||
|
it('returns a page to choose and ensemble for which to set limits', function () {
|
||||||
|
actAsAdmin();
|
||||||
|
$response = $this->get(route('admin.ensembles.seatingLimits'))->assertOk()
|
||||||
|
->assertViewIs('admin.ensembles.seatingLimits')
|
||||||
|
->assertViewHas('ensembles');
|
||||||
|
expect($response->viewData('ensemble')->exists)->toBeFalse()
|
||||||
|
->and($response->viewData('ensembles')->first()->id)->toEqual($this->ensemble->id)
|
||||||
|
->and($response->viewData('limits'))->toBe([]);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('EnsembleController::seatingLimits get with ensemble', function () {
|
||||||
|
it('denies access to non-admin users', function () {
|
||||||
|
$this->get(route('admin.ensembles.seatingLimits', $this->ensemble))->assertRedirect(route('home'));
|
||||||
|
actAsNormal();
|
||||||
|
$this->get(route('admin.ensembles.seatingLimits', $this->ensemble))->assertRedirect(route('dashboard'));
|
||||||
|
actAsTab();
|
||||||
|
$this->get(route('admin.ensembles.seatingLimits', $this->ensemble))->assertRedirect(route('dashboard'));
|
||||||
|
});
|
||||||
|
it('displays a form with fields for every audition to set the max for this ensemble', function () {
|
||||||
|
actAsAdmin();
|
||||||
|
$auditions = Audition::factory()->count(5)->create(['event_id' => $this->ensemble->event_id]);
|
||||||
|
DB::table('seating_limits')->insert([
|
||||||
|
'ensemble_id' => $this->ensemble->id,
|
||||||
|
'audition_id' => $auditions[0]->id,
|
||||||
|
'maximum_accepted' => 6,
|
||||||
|
]);
|
||||||
|
$response = $this->get(route('admin.ensembles.seatingLimits.ensemble.set', $this->ensemble))->assertOk();
|
||||||
|
foreach (Audition::all() as $audition) {
|
||||||
|
$response->assertSee('audition['.$audition->id.']');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('EnsembleController::seatingLimitsSet', function () {
|
||||||
|
it('denies access to non-admin users', function () {
|
||||||
|
$this->post(route('admin.ensembles.seatingLimits.ensemble.set',
|
||||||
|
$this->ensemble))->assertRedirect(route('home'));
|
||||||
|
actAsNormal();
|
||||||
|
$this->post(route('admin.ensembles.seatingLimits.ensemble.set',
|
||||||
|
$this->ensemble))->assertRedirect(route('dashboard'));
|
||||||
|
actAsTab();
|
||||||
|
$this->post(route('admin.ensembles.seatingLimits.ensemble.set',
|
||||||
|
$this->ensemble))->assertRedirect(route('dashboard'));
|
||||||
|
});
|
||||||
|
it('sets seating limits', function () {
|
||||||
|
actAsAdmin();
|
||||||
|
$auditions = Audition::factory()->count(3)->create(['event_id' => $this->ensemble->event_id]);
|
||||||
|
$response = $this->post(route('admin.ensembles.seatingLimits.ensemble.set', $this->ensemble), [
|
||||||
|
'audition' => [
|
||||||
|
$auditions[0]->id => 5,
|
||||||
|
$auditions[1]->id => 10,
|
||||||
|
$auditions[2]->id => 20,
|
||||||
|
],
|
||||||
|
]);
|
||||||
|
$response->assertRedirect(route('admin.ensembles.seatingLimits.ensemble', $this->ensemble))
|
||||||
|
->assertSessionHas('success', 'Seating limits set for '.$this->ensemble->name);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('EnsembleController::updateEnsembleRank', function () {
|
||||||
|
it('denies access to non-admin users', function () {
|
||||||
|
$this->post(route('admin.ensembles.updateEnsembleRank'))->assertRedirect(route('home'));
|
||||||
|
actAsNormal();
|
||||||
|
$this->post(route('admin.ensembles.updateEnsembleRank'))->assertRedirect(route('dashboard'));
|
||||||
|
actAsTab();
|
||||||
|
$this->post(route('admin.ensembles.updateEnsembleRank'))->assertRedirect(route('dashboard'));
|
||||||
|
});
|
||||||
|
it('reorders ensembles', function () {
|
||||||
|
actAsAdmin();
|
||||||
|
$newEnsemble = Ensemble::create([
|
||||||
|
'name' => 'Alternates',
|
||||||
|
'code' => 'Alt',
|
||||||
|
'rank' => 2,
|
||||||
|
'event_id' => $this->ensemble->event_id,
|
||||||
|
]);
|
||||||
|
expect($this->ensemble->rank)->toBe(1);
|
||||||
|
expect($newEnsemble->rank)->toBe(2);
|
||||||
|
$response = $this->post(route('admin.ensembles.updateEnsembleRank'), [
|
||||||
|
'event_id' => $this->ensemble->event_id,
|
||||||
|
'order' => [
|
||||||
|
[
|
||||||
|
'id' => $this->ensemble->id,
|
||||||
|
'rank' => 2,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'id' => $newEnsemble->id,
|
||||||
|
'rank' => 1,
|
||||||
|
],
|
||||||
|
],
|
||||||
|
])->assertJson(['status' => 'success']);
|
||||||
|
$this->ensemble->refresh();
|
||||||
|
$newEnsemble->refresh();
|
||||||
|
expect($this->ensemble->rank)->toBe(2);
|
||||||
|
expect($newEnsemble->rank)->toBe(1);
|
||||||
|
});
|
||||||
|
});
|
||||||
Loading…
Reference in New Issue