Student controller refactoring and testing.
This commit is contained in:
parent
7379500e9a
commit
2962854541
|
|
@ -4,6 +4,7 @@ namespace App\Actions\Students;
|
||||||
|
|
||||||
use App\Exceptions\AuditionAdminException;
|
use App\Exceptions\AuditionAdminException;
|
||||||
use App\Models\Student;
|
use App\Models\Student;
|
||||||
|
use Arr;
|
||||||
|
|
||||||
class CreateStudent
|
class CreateStudent
|
||||||
{
|
{
|
||||||
|
|
@ -11,29 +12,33 @@ class CreateStudent
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
public function __invoke(
|
/**
|
||||||
string $firstName,
|
* @throws AuditionAdminException
|
||||||
string $lastName,
|
*/
|
||||||
int $grade,
|
public function __invoke(array $newData): Student
|
||||||
array $optionalData = [],
|
{
|
||||||
int|string $school_id = 'user'
|
|
||||||
): Student {
|
// $newData[] must include keys first_name, last_name, grade - throw an exception if it does not
|
||||||
if ($school_id === 'user') {
|
foreach (['first_name', 'last_name', 'grade'] as $key) {
|
||||||
$school_id = auth()->user()->school_id;
|
if (! Arr::has($newData, $key)) {
|
||||||
|
throw new AuditionAdminException('Missing required data');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (Student::where('first_name', $firstName)->where('last_name', $lastName)
|
|
||||||
->where('school_id', $school_id)->exists()) {
|
if (! Arr::has($newData, 'school_id')) {
|
||||||
|
$newData['school_id'] = auth()->user()->school_id;
|
||||||
|
}
|
||||||
|
if (Student::where('first_name', $newData['first_name'])->where('last_name', $newData['last_name'])
|
||||||
|
->where('school_id', $newData['school_id'])->exists()) {
|
||||||
throw new AuditionAdminException('Student already exists');
|
throw new AuditionAdminException('Student already exists');
|
||||||
}
|
}
|
||||||
|
|
||||||
$newStudent = Student::create([
|
return Student::create([
|
||||||
'first_name' => $firstName,
|
'first_name' => $newData['first_name'],
|
||||||
'last_name' => $lastName,
|
'last_name' => $newData['last_name'],
|
||||||
'grade' => $grade,
|
'grade' => $newData['grade'],
|
||||||
'school_id' => $school_id,
|
'school_id' => $newData['school_id'],
|
||||||
'optional_data' => $optionalData,
|
'optional_data' => $newData['optional_data'] ?? null,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
return $newStudent;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,48 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Actions\Students;
|
||||||
|
|
||||||
|
use App\Exceptions\AuditionAdminException;
|
||||||
|
use App\Models\Student;
|
||||||
|
use Arr;
|
||||||
|
|
||||||
|
class UpdateStudent
|
||||||
|
{
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @throws AuditionAdminException
|
||||||
|
*/
|
||||||
|
public function __invoke(Student $student, array $newData): bool
|
||||||
|
{
|
||||||
|
|
||||||
|
// $newData[] must include keys first_name, last_name, grade - throw an exception if it does not
|
||||||
|
foreach (['first_name', 'last_name', 'grade'] as $key) {
|
||||||
|
if (! Arr::has($newData, $key)) {
|
||||||
|
throw new AuditionAdminException('Missing required data');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (! Arr::has($newData, 'school_id')) {
|
||||||
|
$newData['school_id'] = auth()->user()->school_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Student::where('first_name', $newData['first_name'])
|
||||||
|
->where('last_name', $newData['last_name'])
|
||||||
|
->where('school_id', $newData['school_id'])
|
||||||
|
->where('id', '!=', $student->id)
|
||||||
|
->exists()) {
|
||||||
|
throw new AuditionAdminException('Student already exists');
|
||||||
|
}
|
||||||
|
|
||||||
|
return $student->update([
|
||||||
|
'first_name' => $newData['first_name'],
|
||||||
|
'last_name' => $newData['last_name'],
|
||||||
|
'grade' => $newData['grade'],
|
||||||
|
'school_id' => $newData['school_id'],
|
||||||
|
'optional_data' => $newData['optional_data'] ?? null,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -2,9 +2,9 @@
|
||||||
|
|
||||||
namespace App\Http\Controllers;
|
namespace App\Http\Controllers;
|
||||||
|
|
||||||
|
use App\Actions\Students\CreateStudent;
|
||||||
use App\Http\Requests\StudentStoreRequest;
|
use App\Http\Requests\StudentStoreRequest;
|
||||||
use App\Models\Audition;
|
use App\Models\Audition;
|
||||||
use App\Models\AuditLogEntry;
|
|
||||||
use App\Models\Student;
|
use App\Models\Student;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
use Illuminate\Support\Facades\Auth;
|
use Illuminate\Support\Facades\Auth;
|
||||||
|
|
@ -36,31 +36,19 @@ class StudentController extends Controller
|
||||||
*/
|
*/
|
||||||
public function store(StudentStoreRequest $request)
|
public function store(StudentStoreRequest $request)
|
||||||
{
|
{
|
||||||
if ($request->user()->cannot('create', Student::class)) {
|
$creator = app(CreateStudent::class);
|
||||||
abort(403);
|
/** @noinspection PhpUnhandledExceptionInspection */
|
||||||
}
|
$creator([
|
||||||
|
|
||||||
$student = Student::create([
|
|
||||||
'first_name' => $request['first_name'],
|
'first_name' => $request['first_name'],
|
||||||
'last_name' => $request['last_name'],
|
'last_name' => $request['last_name'],
|
||||||
'grade' => $request['grade'],
|
'grade' => $request['grade'],
|
||||||
'school_id' => Auth::user()->school_id,
|
'optional_data' => $request->optional_data,
|
||||||
]);
|
]);
|
||||||
if ($request['shirt_size'] !== 'none') {
|
|
||||||
$student->update(['optional_data->shirt_size' => $request['shirt_size']]);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
/** @codeCoverageIgnoreEnd */
|
||||||
return redirect('/students')->with('success', 'Student Created');
|
return redirect('/students')->with('success', 'Student Created');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Display the specified resource.
|
|
||||||
*/
|
|
||||||
public function show(Request $request, Student $student)
|
|
||||||
{
|
|
||||||
//
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Show the form for editing the specified resource.
|
* Show the form for editing the specified resource.
|
||||||
*/
|
*/
|
||||||
|
|
@ -78,52 +66,17 @@ class StudentController extends Controller
|
||||||
/**
|
/**
|
||||||
* Update the specified resource in storage.
|
* Update the specified resource in storage.
|
||||||
*/
|
*/
|
||||||
public function update(Request $request, Student $student)
|
public function update(StudentStoreRequest $request, Student $student)
|
||||||
{
|
{
|
||||||
|
|
||||||
if ($request->user()->cannot('update', $student)) {
|
if ($request->user()->cannot('update', $student)) {
|
||||||
abort(403);
|
abort(403);
|
||||||
}
|
}
|
||||||
request()->validate([
|
|
||||||
'first_name' => ['required'],
|
|
||||||
'last_name' => ['required'],
|
|
||||||
'grade' => ['required', 'integer'],
|
|
||||||
'shirt_size' => [
|
|
||||||
'nullable',
|
|
||||||
function ($attribute, $value, $fail) {
|
|
||||||
if (! array_key_exists($value, Student::$shirtSizes)) {
|
|
||||||
$fail("The selected $attribute is invalid.");
|
|
||||||
}
|
|
||||||
},
|
|
||||||
],
|
|
||||||
]);
|
|
||||||
|
|
||||||
if (Student::where('first_name', request('first_name'))
|
|
||||||
->where('last_name', request('last_name'))
|
|
||||||
->where('school_id', Auth::user()->school_id)
|
|
||||||
->where('id', '!=', $student->id)
|
|
||||||
->exists()) {
|
|
||||||
return redirect()->route('students.edit', $student)->with('error',
|
|
||||||
'A student with that name already exists at your school.');
|
|
||||||
}
|
|
||||||
|
|
||||||
$student->update([
|
$student->update([
|
||||||
'first_name' => request('first_name'),
|
'first_name' => $request['first_name'],
|
||||||
'last_name' => request('last_name'),
|
'last_name' => $request['last_name'],
|
||||||
'grade' => request('grade'),
|
'grade' => $request['grade'],
|
||||||
]);
|
'optional_data' => $request->optional_data,
|
||||||
|
|
||||||
$student->update(['optional_data->shirt_size' => $request['shirt_size']]);
|
|
||||||
|
|
||||||
$message = 'Updated student #'.$student->id.'<br>Name: '.$student->full_name().'<br>Grade: '.$student->grade.'<br>School: '.$student->school->name;
|
|
||||||
AuditLogEntry::create([
|
|
||||||
'user' => auth()->user()->email,
|
|
||||||
'ip_address' => request()->ip(),
|
|
||||||
'message' => $message,
|
|
||||||
'affected' => [
|
|
||||||
'students' => [$student->id],
|
|
||||||
'schools' => [$student->school_id],
|
|
||||||
],
|
|
||||||
]);
|
]);
|
||||||
|
|
||||||
return redirect('/students')->with('success', 'Student updated successfully.');
|
return redirect('/students')->with('success', 'Student updated successfully.');
|
||||||
|
|
@ -137,16 +90,7 @@ class StudentController extends Controller
|
||||||
if ($request->user()->cannot('delete', $student)) {
|
if ($request->user()->cannot('delete', $student)) {
|
||||||
abort(403);
|
abort(403);
|
||||||
}
|
}
|
||||||
$message = 'Deleted student #'.$student->id.'<br>Name: '.$student->full_name().'<br>Grade: '.$student->grade.'<br>School: '.$student->school->name;
|
|
||||||
AuditLogEntry::create([
|
|
||||||
'user' => auth()->user()->email,
|
|
||||||
'ip_address' => request()->ip(),
|
|
||||||
'message' => $message,
|
|
||||||
'affected' => [
|
|
||||||
'students' => [$student->id],
|
|
||||||
'schools' => [$student->school_id],
|
|
||||||
],
|
|
||||||
]);
|
|
||||||
$student->delete();
|
$student->delete();
|
||||||
|
|
||||||
return redirect(route('students.index'));
|
return redirect(route('students.index'));
|
||||||
|
|
|
||||||
|
|
@ -32,6 +32,14 @@ class StudentStoreRequest extends FormRequest
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Helper method to get optional data (optional)
|
||||||
|
public function getOptionalData(): array
|
||||||
|
{
|
||||||
|
return ($this->shirt_size !== 'none')
|
||||||
|
? ['shirt_size' => $this->shirt_size]
|
||||||
|
: [];
|
||||||
|
}
|
||||||
|
|
||||||
public function authorize(): bool
|
public function authorize(): bool
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,9 @@ namespace App\Observers;
|
||||||
use App\Models\AuditLogEntry;
|
use App\Models\AuditLogEntry;
|
||||||
use App\Models\Student;
|
use App\Models\Student;
|
||||||
|
|
||||||
|
use function auth;
|
||||||
|
use function request;
|
||||||
|
|
||||||
class StudentObserver
|
class StudentObserver
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
|
|
@ -29,7 +32,16 @@ class StudentObserver
|
||||||
*/
|
*/
|
||||||
public function updated(Student $student): void
|
public function updated(Student $student): void
|
||||||
{
|
{
|
||||||
//
|
$message = 'Updated student #'.$student->id.' - '.$student->full_name().'<br>Grade: '.$student->grade.'<br>School: '.$student->school->name;
|
||||||
|
AuditLogEntry::create([
|
||||||
|
'user' => auth()->user()->email ?? 'none',
|
||||||
|
'ip_address' => request()->ip(),
|
||||||
|
'message' => $message,
|
||||||
|
'affected' => [
|
||||||
|
'students' => [$student->id],
|
||||||
|
'schools' => [$student->school_id],
|
||||||
|
],
|
||||||
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -37,22 +49,15 @@ class StudentObserver
|
||||||
*/
|
*/
|
||||||
public function deleted(Student $student): void
|
public function deleted(Student $student): void
|
||||||
{
|
{
|
||||||
//
|
$message = 'Deleted student #'.$student->id.' - '.$student->full_name().'<br>Grade: '.$student->grade.'<br>School: '.$student->school->name;
|
||||||
}
|
AuditLogEntry::create([
|
||||||
|
'user' => auth()->user()->email ?? 'none',
|
||||||
/**
|
'ip_address' => request()->ip(),
|
||||||
* Handle the Student "restored" event.
|
'message' => $message,
|
||||||
*/
|
'affected' => [
|
||||||
public function restored(Student $student): void
|
'students' => [$student->id],
|
||||||
{
|
'schools' => [$student->school_id],
|
||||||
//
|
],
|
||||||
}
|
]);
|
||||||
|
|
||||||
/**
|
|
||||||
* Handle the Student "force deleted" event.
|
|
||||||
*/
|
|
||||||
public function forceDeleted(Student $student): void
|
|
||||||
{
|
|
||||||
//
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,6 @@ use App\Http\Controllers\EntryController;
|
||||||
use App\Http\Controllers\PdfInvoiceController;
|
use App\Http\Controllers\PdfInvoiceController;
|
||||||
use App\Http\Controllers\SchoolController;
|
use App\Http\Controllers\SchoolController;
|
||||||
use App\Http\Controllers\StudentController;
|
use App\Http\Controllers\StudentController;
|
||||||
use App\Http\Controllers\UserController;
|
|
||||||
use Illuminate\Support\Facades\Route;
|
use Illuminate\Support\Facades\Route;
|
||||||
|
|
||||||
Route::middleware(['auth', 'verified'])->group(function () {
|
Route::middleware(['auth', 'verified'])->group(function () {
|
||||||
|
|
@ -40,7 +39,7 @@ Route::middleware([
|
||||||
'auth', 'verified',
|
'auth', 'verified',
|
||||||
])->controller(StudentController::class)->group(function () {
|
])->controller(StudentController::class)->group(function () {
|
||||||
Route::get('/students', 'index')->name('students.index');
|
Route::get('/students', 'index')->name('students.index');
|
||||||
Route::post('students', 'store')->name('students.store');
|
Route::post('students', 'store')->middleware(['can:create,App\Models\Student'])->name('students.store');
|
||||||
Route::get('/students/{student}/edit', 'edit')->name('students.edit');
|
Route::get('/students/{student}/edit', 'edit')->name('students.edit');
|
||||||
Route::patch('/students/{student}', 'update')->name('students.update');
|
Route::patch('/students/{student}', 'update')->name('students.update');
|
||||||
Route::delete('/students/{student}', 'destroy')->name('students.destroy');
|
Route::delete('/students/{student}', 'destroy')->name('students.destroy');
|
||||||
|
|
|
||||||
|
|
@ -16,24 +16,23 @@ beforeEach(function () {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('can create a student with basic data', function () {
|
it('can create a student with basic data', function () {
|
||||||
($this->creator)(
|
($this->creator)([
|
||||||
'John',
|
'first_name' => 'John',
|
||||||
'Doe',
|
'last_name' => 'Doe',
|
||||||
8,
|
'grade' => 8,
|
||||||
[],
|
'school_id' => School::factory()->create()->id,
|
||||||
School::factory()->create()->id
|
]);
|
||||||
);
|
|
||||||
expect(Student::first())->toBeInstanceOf(Student::class);
|
expect(Student::first())->toBeInstanceOf(Student::class);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('can include optional data', function () {
|
it('can include optional data', function () {
|
||||||
($this->creator)(
|
($this->creator)([
|
||||||
'John',
|
'first_name' => 'John',
|
||||||
'Doe',
|
'last_name' => 'Doe',
|
||||||
8,
|
'grade' => 8,
|
||||||
['shirt_size' => 'M'],
|
'optional_data' => ['shirt_size' => 'M'],
|
||||||
School::factory()->create()->id
|
'school_id' => School::factory()->create()->id,
|
||||||
);
|
]);
|
||||||
$student = Student::first();
|
$student = Student::first();
|
||||||
expect($student->optional_data['shirt_size'])->toEqual('M');
|
expect($student->optional_data['shirt_size'])->toEqual('M');
|
||||||
});
|
});
|
||||||
|
|
@ -44,30 +43,40 @@ it('uses the current users school if none is specified', function () {
|
||||||
$user->school_id = $school->id;
|
$user->school_id = $school->id;
|
||||||
$user->save();
|
$user->save();
|
||||||
$this->actingAs($user);
|
$this->actingAs($user);
|
||||||
($this->creator)(
|
($this->creator)([
|
||||||
'John',
|
'first_name' => 'John',
|
||||||
'Doe',
|
'last_name' => 'Doe',
|
||||||
8,
|
'grade' => 8,
|
||||||
['shirt_size' => 'M'],
|
'optional_data' => ['shirt_size' => 'M'],
|
||||||
);
|
]);
|
||||||
$student = Student::first();
|
$student = Student::first();
|
||||||
expect($student->school_id)->toEqual($school->id);
|
expect($student->school_id)->toEqual($school->id);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('throws an error if we try to create a duplicate student (same name and school)', function () {
|
it('throws an error if we try to create a duplicate student (same name and school)', function () {
|
||||||
$school = School::factory()->create();
|
$school = School::factory()->create();
|
||||||
($this->creator)(
|
($this->creator)([
|
||||||
'John',
|
'first_name' => 'John',
|
||||||
'Doe',
|
'last_name' => 'Doe',
|
||||||
8,
|
'grade' => 8,
|
||||||
['shirt_size' => 'M'],
|
'optional_data' => ['shirt_size' => 'M'],
|
||||||
$school->id
|
'school_id' => $school->id,
|
||||||
);
|
]);
|
||||||
($this->creator)(
|
($this->creator)([
|
||||||
'John',
|
'first_name' => 'John',
|
||||||
'Doe',
|
'last_name' => 'Doe',
|
||||||
11,
|
'grade' => 11,
|
||||||
['shirt_size' => 'XL'],
|
'optional_data' => ['shirt_size' => 'XL'],
|
||||||
$school->id
|
'school_id' => $school->id,
|
||||||
);
|
]);
|
||||||
})->throws(AuditionAdminException::class, 'Student already exists');
|
})->throws(AuditionAdminException::class, 'Student already exists');
|
||||||
|
|
||||||
|
it('throws an error if data is missing', function () {
|
||||||
|
$school = School::factory()->create();
|
||||||
|
($this->creator)([
|
||||||
|
'last_name' => 'Doe',
|
||||||
|
'grade' => 8,
|
||||||
|
'optional_data' => ['shirt_size' => 'M'],
|
||||||
|
'school_id' => $school->id,
|
||||||
|
]);
|
||||||
|
})->throws(AuditionAdminException::class, 'Missing required data');
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,98 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/** @noinspection PhpUnhandledExceptionInspection */
|
||||||
|
|
||||||
|
use App\Actions\Students\CreateStudent;
|
||||||
|
use App\Actions\Students\UpdateStudent;
|
||||||
|
use App\Exceptions\AuditionAdminException;
|
||||||
|
use App\Models\School;
|
||||||
|
use App\Models\Student;
|
||||||
|
use App\Models\User;
|
||||||
|
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||||
|
|
||||||
|
uses(RefreshDatabase::class);
|
||||||
|
|
||||||
|
beforeEach(function () {
|
||||||
|
$this->creator = app(CreateStudent::class);
|
||||||
|
$this->editor = app(UpdateStudent::class);
|
||||||
|
$this->editedStudent = ($this->creator)([
|
||||||
|
'first_name' => 'John',
|
||||||
|
'last_name' => 'Doe',
|
||||||
|
'grade' => 8,
|
||||||
|
'school_id' => School::factory()->create()->id,
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('can update a student with basic data', function () {
|
||||||
|
($this->editor)($this->editedStudent, [
|
||||||
|
'first_name' => 'John',
|
||||||
|
'last_name' => 'Doe',
|
||||||
|
'grade' => 14,
|
||||||
|
'school_id' => School::factory()->create()->id,
|
||||||
|
]);
|
||||||
|
expect(Student::first()->grade)->toBe(14);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('can include optional data', function () {
|
||||||
|
($this->editor)($this->editedStudent, [
|
||||||
|
'first_name' => 'John',
|
||||||
|
'last_name' => 'Doe',
|
||||||
|
'grade' => 2,
|
||||||
|
'optional_data' => ['shirt_size' => 'M'],
|
||||||
|
'school_id' => School::factory()->create()->id,
|
||||||
|
]);
|
||||||
|
$student = Student::first();
|
||||||
|
expect($student->optional_data['shirt_size'])->toEqual('M')
|
||||||
|
->and($student->grade)->toBe(2);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('uses the current users school if none is specified', function () {
|
||||||
|
$user = User::factory()->create();
|
||||||
|
$school = School::factory()->create();
|
||||||
|
$user->school_id = $school->id;
|
||||||
|
$user->save();
|
||||||
|
$this->actingAs($user);
|
||||||
|
($this->editor)($this->editedStudent, [
|
||||||
|
'first_name' => 'Jane',
|
||||||
|
'last_name' => 'Doe',
|
||||||
|
'grade' => 8,
|
||||||
|
'optional_data' => ['shirt_size' => 'M'],
|
||||||
|
]);
|
||||||
|
$student = Student::first();
|
||||||
|
expect($student->school_id)->toEqual($school->id)
|
||||||
|
->and($student->first_name)->toEqual('Jane');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('throws an error if we try to create a duplicate student (same name and school)', function () {
|
||||||
|
$school = School::factory()->create();
|
||||||
|
($this->creator)([
|
||||||
|
'first_name' => 'John',
|
||||||
|
'last_name' => 'Doe',
|
||||||
|
'grade' => 8,
|
||||||
|
'optional_data' => ['shirt_size' => 'M'],
|
||||||
|
'school_id' => $school->id,
|
||||||
|
]);
|
||||||
|
$student2 = ($this->creator)([
|
||||||
|
'first_name' => 'Jane',
|
||||||
|
'last_name' => 'Doe',
|
||||||
|
'grade' => 11,
|
||||||
|
'optional_data' => ['shirt_size' => 'XL'],
|
||||||
|
'school_id' => $school->id,
|
||||||
|
]);
|
||||||
|
($this->editor)($student2, [
|
||||||
|
'first_name' => 'John',
|
||||||
|
'last_name' => 'Doe',
|
||||||
|
'grade' => 11,
|
||||||
|
'school_id' => $school->id,
|
||||||
|
]);
|
||||||
|
})->throws(AuditionAdminException::class, 'Student already exists');
|
||||||
|
|
||||||
|
it('throws an error if data is missing', function () {
|
||||||
|
$school = School::factory()->create();
|
||||||
|
($this->editor)($this->editedStudent, [
|
||||||
|
'last_name' => 'Doe',
|
||||||
|
'grade' => 8,
|
||||||
|
'optional_data' => ['shirt_size' => 'M'],
|
||||||
|
'school_id' => $school->id,
|
||||||
|
]);
|
||||||
|
})->throws(AuditionAdminException::class, 'Missing required data');
|
||||||
|
|
@ -8,6 +8,20 @@ use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||||
|
|
||||||
uses(RefreshDatabase::class);
|
uses(RefreshDatabase::class);
|
||||||
|
|
||||||
|
beforeEach(function () {
|
||||||
|
$this->school = School::factory()->create();
|
||||||
|
$this->submitData = [
|
||||||
|
'first_name' => 'John',
|
||||||
|
'last_name' => 'Doe',
|
||||||
|
'grade' => 8,
|
||||||
|
];
|
||||||
|
$this->updateData = [
|
||||||
|
'first_name' => 'George',
|
||||||
|
'last_name' => 'Washington',
|
||||||
|
'grade' => 53,
|
||||||
|
];
|
||||||
|
});
|
||||||
|
|
||||||
describe('Test the index method of the student controller', function () {
|
describe('Test the index method of the student controller', function () {
|
||||||
it('redirects to the dashboard if the user does not have a school', function () {
|
it('redirects to the dashboard if the user does not have a school', function () {
|
||||||
$user = User::factory()->create();
|
$user = User::factory()->create();
|
||||||
|
|
@ -48,5 +62,146 @@ describe('Test the index method of the student controller', function () {
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('Test the store method of the student controller', function () {
|
describe('Test the store method of the student controller', function () {
|
||||||
|
it('redirects to the dashboard if the user does not have a school', function () {
|
||||||
|
$user = User::factory()->create();
|
||||||
|
//$user->update(['school_id' => School::factory()->create()->id]);;
|
||||||
|
$this->actingAs($user);
|
||||||
|
$response = $this->post(route('students.store'), $this->submitData);
|
||||||
|
$response->assertStatus(403);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('creates a student with only basic information', function () {
|
||||||
|
$user = User::factory()->create();
|
||||||
|
$user->school_id = $this->school->id;
|
||||||
|
$user->save();
|
||||||
|
$this->actingAs($user);
|
||||||
|
$response = $this->post(route('students.store'), $this->submitData);
|
||||||
|
$response->assertRedirect(route('students.index'));
|
||||||
|
expect(Student::first())->toBeInstanceOf(Student::class)
|
||||||
|
->and(Student::first()->first_name)->toEqual('John')
|
||||||
|
->and(Student::first()->last_name)->toEqual('Doe')
|
||||||
|
->and(Student::first()->grade)->toEqual(8)
|
||||||
|
->and(Student::first()->school_id)->toEqual($this->school->id);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('creates a student with optional data', function () {
|
||||||
|
$user = User::factory()->create();
|
||||||
|
$user->school_id = $this->school->id;
|
||||||
|
$user->save();
|
||||||
|
$this->actingAs($user);
|
||||||
|
$localData = $this->submitData;
|
||||||
|
$localData['optional_data'] = ['shirt_size' => 'M'];
|
||||||
|
$response = $this->post(route('students.store'), $localData);
|
||||||
|
$response->assertRedirect(route('students.index'));
|
||||||
|
expect(Student::first()->optional_data['shirt_size'])->toEqual('M');
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
it('will not allow a user to create a student for another school', function () {
|
||||||
|
$otherSchool = School::factory()->create();
|
||||||
|
$user = User::factory()->create();
|
||||||
|
$user->school_id = $this->school->id;
|
||||||
|
$user->save();
|
||||||
|
$this->actingAs($user);
|
||||||
|
$localData = $this->submitData;
|
||||||
|
$localData['school_id'] = $otherSchool->id;
|
||||||
|
$response = $this->post(route('students.store'), $localData);
|
||||||
|
expect(Student::first()->school_id)->toBe($this->school->id);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Test the edit method of the student controller', function () {
|
||||||
|
it('will not allow a user to edit a student from another school', function () {
|
||||||
|
$otherStudent = Student::factory()->create();
|
||||||
|
$user = User::factory()->create();
|
||||||
|
$user->school_id = $this->school->id;
|
||||||
|
$user->save();
|
||||||
|
$this->actingAs($user);
|
||||||
|
$response = $this->get(route('students.edit', $otherStudent));
|
||||||
|
$response->assertStatus(403);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('produces an edit for for a student', function () {
|
||||||
|
$user = User::factory()->create();
|
||||||
|
$user->school_id = $this->school->id;
|
||||||
|
$user->save();
|
||||||
|
$student = Student::factory()->forSchool($this->school)->create();
|
||||||
|
$this->actingAs($user);
|
||||||
|
$response = $this->get(route('students.edit', $student));
|
||||||
|
$response->assertViewIs('students.edit')
|
||||||
|
->assertViewHas('student', $student)
|
||||||
|
->assertSee(route('students.update', $student));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Test the update method of the student controller', function () {
|
||||||
|
it('will not allow a user to edit a student from another school', function () {
|
||||||
|
$otherStudent = Student::factory()->create();
|
||||||
|
$user = User::factory()->create();
|
||||||
|
$user->school_id = $this->school->id;
|
||||||
|
$user->save();
|
||||||
|
$this->actingAs($user);
|
||||||
|
expect($otherStudent->school_id)->not->toEqual($this->school->id);
|
||||||
|
$response = $this->patch(route('students.update', $otherStudent), $this->submitData);
|
||||||
|
$response->assertStatus(403);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('modifies a student with only basic information', function () {
|
||||||
|
$user = User::factory()->create();
|
||||||
|
$user->school_id = $this->school->id;
|
||||||
|
$user->save();
|
||||||
|
$this->actingAs($user);
|
||||||
|
$this->post(route('students.store'), $this->submitData);
|
||||||
|
$response = $this->patch(route('students.update', Student::first()), $this->updateData);
|
||||||
|
|
||||||
|
$response->assertRedirect(route('students.index'));
|
||||||
|
expect(Student::first())->toBeInstanceOf(Student::class)
|
||||||
|
->and(Student::first()->first_name)->toEqual('George')
|
||||||
|
->and(Student::first()->last_name)->toEqual('Washington')
|
||||||
|
->and(Student::first()->grade)->toEqual(53)
|
||||||
|
->and(Student::first()->school_id)->toEqual($this->school->id);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('modifies a student with optional data', function () {
|
||||||
|
$user = User::factory()->create();
|
||||||
|
$user->school_id = $this->school->id;
|
||||||
|
$user->save();
|
||||||
|
$this->actingAs($user);
|
||||||
|
$localData = $this->submitData;
|
||||||
|
$localData['optional_data'] = ['shirt_size' => 'M'];
|
||||||
|
$this->post(route('students.store'), $localData);
|
||||||
|
|
||||||
|
$localUpdateData = $this->updateData;
|
||||||
|
$localUpdateData['optional_data'] = ['shirt_size' => 'XL'];
|
||||||
|
|
||||||
|
$response = $this->patch(route('students.update', Student::first()), $localUpdateData);
|
||||||
|
|
||||||
|
$response->assertRedirect(route('students.index'));
|
||||||
|
|
||||||
|
expect(Student::first()->optional_data['shirt_size'])->toEqual('XL');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Test the destroy method of the student controller', function () {
|
||||||
|
it('will not allow a user to delete a student from another school', function () {
|
||||||
|
$otherStudent = Student::factory()->create();
|
||||||
|
$user = User::factory()->create();
|
||||||
|
$user->school_id = $this->school->id;
|
||||||
|
$user->save();
|
||||||
|
$this->actingAs($user);
|
||||||
|
expect($otherStudent->school_id)->not->toEqual($this->school->id);
|
||||||
|
$response = $this->delete(route('students.destroy', $otherStudent));
|
||||||
|
$response->assertStatus(403);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('allows a user to delete their own students', function () {
|
||||||
|
$student = Student::factory()->forSchool($this->school)->create();
|
||||||
|
$user = User::factory()->create();
|
||||||
|
$user->school_id = $this->school->id;
|
||||||
|
$user->save();
|
||||||
|
$this->actingAs($user);
|
||||||
|
$response = $this->delete(route('students.destroy', $student));
|
||||||
|
$response->assertRedirect(route('students.index'));
|
||||||
|
expect(Student::first())->toBeNull();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue