feat: implement assignment retrieval logic for unauthenticated users and add AssignmentFactory and feature tests for assignment management
This commit is contained in:
parent
bae7e8201a
commit
9e16c9d187
@ -68,6 +68,15 @@ public function deleteAssignmentAction(): Action
|
|||||||
public function assignments(): Collection
|
public function assignments(): Collection
|
||||||
{
|
{
|
||||||
$studentProfile = auth()->user()->student;
|
$studentProfile = auth()->user()->student;
|
||||||
|
|
||||||
|
if (! $studentProfile) {
|
||||||
|
return Assignment::with(['course', 'assignmentSubmissions', 'studyGroups.students'])
|
||||||
|
->whereHas('course', function ($q) {
|
||||||
|
$q->where('semester', app(GeneralSettings::class)->current_semester);
|
||||||
|
})
|
||||||
|
->get();
|
||||||
|
}
|
||||||
|
|
||||||
$pinnedIds = $this->pinnedIds;
|
$pinnedIds = $this->pinnedIds;
|
||||||
|
|
||||||
$assignments = Assignment::with(['course', 'assignmentSubmissions' => function ($q) use ($studentProfile) {
|
$assignments = Assignment::with(['course', 'assignmentSubmissions' => function ($q) use ($studentProfile) {
|
||||||
@ -130,6 +139,11 @@ private function getAssignmentPriority($assignment): int
|
|||||||
public function assignmentCards()
|
public function assignmentCards()
|
||||||
{
|
{
|
||||||
$studentProfile = auth()->user()->student;
|
$studentProfile = auth()->user()->student;
|
||||||
|
|
||||||
|
if (! $studentProfile) {
|
||||||
|
return collect();
|
||||||
|
}
|
||||||
|
|
||||||
$pinnedIds = $this->pinnedIds;
|
$pinnedIds = $this->pinnedIds;
|
||||||
|
|
||||||
return $this->assignments()->map(function ($assignment) use ($studentProfile, $pinnedIds) {
|
return $this->assignments()->map(function ($assignment) use ($studentProfile, $pinnedIds) {
|
||||||
@ -224,6 +238,10 @@ public function pinnedIds(): array
|
|||||||
{
|
{
|
||||||
$studentProfile = auth()->user()->student;
|
$studentProfile = auth()->user()->student;
|
||||||
|
|
||||||
|
if (! $studentProfile) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
return AssignmentPin::where('student_id', $studentProfile->id)
|
return AssignmentPin::where('student_id', $studentProfile->id)
|
||||||
->pluck('assignment_id')
|
->pluck('assignment_id')
|
||||||
->toArray();
|
->toArray();
|
||||||
|
|||||||
25
database/factories/AssignmentFactory.php
Normal file
25
database/factories/AssignmentFactory.php
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Database\Factories;
|
||||||
|
|
||||||
|
use App\Enums\AssignmentType;
|
||||||
|
use App\Models\Assignment;
|
||||||
|
use App\Models\Course;
|
||||||
|
use Illuminate\Database\Eloquent\Factories\Factory;
|
||||||
|
|
||||||
|
class AssignmentFactory extends Factory
|
||||||
|
{
|
||||||
|
protected $model = Assignment::class;
|
||||||
|
|
||||||
|
public function definition(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'course_id' => Course::factory(),
|
||||||
|
'class_session_id' => null,
|
||||||
|
'title' => $this->faker->sentence(),
|
||||||
|
'description' => $this->faker->paragraph(),
|
||||||
|
'due_date' => $this->faker->dateTimeBetween('now', '+1 month'),
|
||||||
|
'type' => $this->faker->randomElement(AssignmentType::cases()),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
243
tests/Feature/AssignmentTest.php
Normal file
243
tests/Feature/AssignmentTest.php
Normal file
@ -0,0 +1,243 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use App\Enums\AssignmentType;
|
||||||
|
use App\Enums\RoleEnum;
|
||||||
|
use App\Filament\Resources\Learning\Assignments\Pages\CreateAssignment;
|
||||||
|
use App\Filament\Resources\Learning\Assignments\Pages\ListAssignments;
|
||||||
|
use App\Filament\Resources\Learning\Assignments\Pages\SubmissionDetailPage;
|
||||||
|
use App\Filament\Resources\Learning\Assignments\Pages\SubmitAssignmentPage;
|
||||||
|
use App\Models\Assignment;
|
||||||
|
use App\Models\ClassSession;
|
||||||
|
use App\Models\Course;
|
||||||
|
use App\Models\Student;
|
||||||
|
use App\Models\StudyGroup;
|
||||||
|
use App\Models\User;
|
||||||
|
use App\Settings\GeneralSettings;
|
||||||
|
use Illuminate\Http\UploadedFile;
|
||||||
|
use Illuminate\Support\Facades\Storage;
|
||||||
|
use Livewire\Livewire;
|
||||||
|
use Spatie\Permission\Models\Permission;
|
||||||
|
use Spatie\Permission\Models\Role;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Setup necessary roles and permissions
|
||||||
|
*/
|
||||||
|
beforeEach(function () {
|
||||||
|
Role::findOrCreate(RoleEnum::Developer->value);
|
||||||
|
Role::findOrCreate(RoleEnum::Kosma->value);
|
||||||
|
Role::findOrCreate(RoleEnum::Student->value);
|
||||||
|
|
||||||
|
$permissions = [
|
||||||
|
'ViewAny:Assignment',
|
||||||
|
'View:Assignment',
|
||||||
|
'Create:Assignment',
|
||||||
|
'Update:Assignment',
|
||||||
|
'Delete:Assignment',
|
||||||
|
];
|
||||||
|
|
||||||
|
foreach ($permissions as $permission) {
|
||||||
|
Permission::findOrCreate($permission);
|
||||||
|
}
|
||||||
|
|
||||||
|
Storage::fake('public');
|
||||||
|
$this->currentSemester = app(GeneralSettings::class)->current_semester;
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Assignment Authorization', function () {
|
||||||
|
it('restricts actions to Developer and Kosma, while allowing students to view and submit', function () {
|
||||||
|
$developer = User::factory()->create();
|
||||||
|
$developer->assignRole(RoleEnum::Developer);
|
||||||
|
$developer->givePermissionTo(['ViewAny:Assignment', 'Create:Assignment']);
|
||||||
|
|
||||||
|
$studentUser = User::factory()->create();
|
||||||
|
$studentUser->assignRole(RoleEnum::Student);
|
||||||
|
$studentUser->givePermissionTo(['ViewAny:Assignment']);
|
||||||
|
$student = Student::factory()->create(['user_id' => $studentUser->id]);
|
||||||
|
|
||||||
|
$this->actingAs($developer);
|
||||||
|
Livewire::test(ListAssignments::class)
|
||||||
|
->assertActionVisible('createAssignment');
|
||||||
|
|
||||||
|
$this->actingAs($studentUser);
|
||||||
|
Livewire::test(ListAssignments::class)
|
||||||
|
->assertActionHidden('createAssignment');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Assignment Model', function () {
|
||||||
|
it('has relationships and supports soft deletes', function () {
|
||||||
|
$assignment = Assignment::factory()->create();
|
||||||
|
|
||||||
|
expect($assignment->course)->toBeInstanceOf(Course::class);
|
||||||
|
expect($assignment->assignmentSubmissions())->toBeInstanceOf(\Illuminate\Database\Eloquent\Relations\HasMany::class);
|
||||||
|
|
||||||
|
$assignment->delete();
|
||||||
|
expect($assignment->refresh()->trashed())->toBeTrue();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Assignment CRUD and Rules', function () {
|
||||||
|
beforeEach(function () {
|
||||||
|
$user = User::factory()->create();
|
||||||
|
$user->assignRole(RoleEnum::Developer);
|
||||||
|
$user->givePermissionTo(['ViewAny:Assignment', 'Create:Assignment', 'Update:Assignment', 'Delete:Assignment']);
|
||||||
|
$this->actingAs($user);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('can create an assignment', function () {
|
||||||
|
$course = Course::factory()->create(['semester' => $this->currentSemester]);
|
||||||
|
$students = Student::factory()->count(2)->create();
|
||||||
|
|
||||||
|
Livewire::test(CreateAssignment::class)
|
||||||
|
->fillForm([
|
||||||
|
'title' => 'New Test Assignment',
|
||||||
|
'course_id' => $course->id,
|
||||||
|
'due_date' => now()->addDays(7)->toDateTimeString(),
|
||||||
|
'type' => AssignmentType::Individual->value,
|
||||||
|
'student_ids' => $students->pluck('id')->toArray(),
|
||||||
|
])
|
||||||
|
->call('create')
|
||||||
|
->assertHasNoFormErrors();
|
||||||
|
|
||||||
|
$this->assertDatabaseHas('assignments', [
|
||||||
|
'title' => 'New Test Assignment',
|
||||||
|
'course_id' => $course->id,
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('enforces one assignment per session rule', function () {
|
||||||
|
$course = Course::factory()->create(['semester' => $this->currentSemester]);
|
||||||
|
$session = ClassSession::factory()->create(['course_id' => $course->id]);
|
||||||
|
|
||||||
|
Assignment::factory()->create([
|
||||||
|
'course_id' => $course->id,
|
||||||
|
'class_session_id' => $session->id,
|
||||||
|
'type' => AssignmentType::Individual,
|
||||||
|
]);
|
||||||
|
|
||||||
|
Livewire::test(CreateAssignment::class)
|
||||||
|
->set('data.course_id', $course->id)
|
||||||
|
->assertFormSet(['class_session_id' => null]);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Assignment Submission Logic', function () {
|
||||||
|
it('allows individual submission', function () {
|
||||||
|
$user = User::factory()->create();
|
||||||
|
$user->assignRole(RoleEnum::Student);
|
||||||
|
$user->givePermissionTo(['ViewAny:Assignment']);
|
||||||
|
$student = Student::factory()->create(['user_id' => $user->id]);
|
||||||
|
|
||||||
|
$assignment = Assignment::factory()->create([
|
||||||
|
'type' => AssignmentType::Individual,
|
||||||
|
'due_date' => now()->addDays(1),
|
||||||
|
]);
|
||||||
|
$assignment->students()->attach($student->id);
|
||||||
|
$assignment->course->update(['semester' => $this->currentSemester]);
|
||||||
|
|
||||||
|
$file = UploadedFile::fake()->create('submission.pdf', 100, 'application/pdf');
|
||||||
|
|
||||||
|
$this->actingAs($user);
|
||||||
|
Livewire::test(SubmitAssignmentPage::class, ['record' => $assignment])
|
||||||
|
->assertSuccessful()
|
||||||
|
->set('file', $file)
|
||||||
|
->call('submit')
|
||||||
|
->assertHasNoErrors();
|
||||||
|
|
||||||
|
$this->assertDatabaseHas('assignment_submissions', [
|
||||||
|
'assignment_id' => $assignment->id,
|
||||||
|
'student_id' => $student->id,
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('only allows group leader to submit group assignments', function () {
|
||||||
|
$course = Course::factory()->create(['semester' => $this->currentSemester]);
|
||||||
|
|
||||||
|
$leaderUser = User::factory()->create();
|
||||||
|
$leaderUser->assignRole(RoleEnum::Student);
|
||||||
|
$leaderUser->givePermissionTo(['ViewAny:Assignment']);
|
||||||
|
$leader = Student::factory()->create(['user_id' => $leaderUser->id]);
|
||||||
|
|
||||||
|
$memberUser = User::factory()->create();
|
||||||
|
$memberUser->assignRole(RoleEnum::Student);
|
||||||
|
$memberUser->givePermissionTo(['ViewAny:Assignment']);
|
||||||
|
$member = Student::factory()->create(['user_id' => $memberUser->id]);
|
||||||
|
|
||||||
|
$group = StudyGroup::factory()->create([
|
||||||
|
'leader_id' => $leader->id,
|
||||||
|
]);
|
||||||
|
$group->courses()->attach($course->id);
|
||||||
|
$group->students()->attach($member->id);
|
||||||
|
|
||||||
|
$assignment = Assignment::factory()->create([
|
||||||
|
'course_id' => $course->id,
|
||||||
|
'type' => AssignmentType::Group,
|
||||||
|
'due_date' => now()->addDays(1),
|
||||||
|
]);
|
||||||
|
$assignment->studyGroups()->attach($group->id);
|
||||||
|
|
||||||
|
// Test Leader can submit
|
||||||
|
$this->actingAs($leaderUser);
|
||||||
|
Livewire::test(SubmitAssignmentPage::class, ['record' => $assignment])
|
||||||
|
->assertSuccessful()
|
||||||
|
->assertSet('canSubmit', true);
|
||||||
|
|
||||||
|
// Test Member cannot submit
|
||||||
|
$this->actingAs($memberUser);
|
||||||
|
Livewire::test(SubmitAssignmentPage::class, ['record' => $assignment])
|
||||||
|
->assertSuccessful()
|
||||||
|
->assertSet('canSubmit', false);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Assignment Pins and Interactions', function () {
|
||||||
|
beforeEach(function () {
|
||||||
|
$user = User::factory()->create();
|
||||||
|
$user->assignRole(RoleEnum::Student);
|
||||||
|
$user->givePermissionTo(['ViewAny:Assignment']);
|
||||||
|
$this->studentProfile = Student::factory()->create(['user_id' => $user->id]);
|
||||||
|
$this->actingAs($user);
|
||||||
|
$this->currentSemester = app(GeneralSettings::class)->current_semester;
|
||||||
|
});
|
||||||
|
|
||||||
|
it('can pin and unpin an assignment', function () {
|
||||||
|
$assignment = Assignment::factory()->create();
|
||||||
|
$assignment->course->update(['semester' => $this->currentSemester]);
|
||||||
|
$assignment->students()->attach($this->studentProfile->id);
|
||||||
|
|
||||||
|
Livewire::test(ListAssignments::class)
|
||||||
|
->callAction('pin', [], ['record' => $assignment->id]);
|
||||||
|
|
||||||
|
$this->assertDatabaseHas('assignment_pins', [
|
||||||
|
'student_id' => $this->studentProfile->id,
|
||||||
|
'assignment_id' => $assignment->id,
|
||||||
|
]);
|
||||||
|
|
||||||
|
Livewire::test(ListAssignments::class)
|
||||||
|
->callAction('pin', [], ['record' => $assignment->id]);
|
||||||
|
|
||||||
|
$this->assertDatabaseMissing('assignment_pins', [
|
||||||
|
'student_id' => $this->studentProfile->id,
|
||||||
|
'assignment_id' => $assignment->id,
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Assignment Details and Preview', function () {
|
||||||
|
beforeEach(function () {
|
||||||
|
$user = User::factory()->create();
|
||||||
|
$user->assignRole(RoleEnum::Developer);
|
||||||
|
$user->givePermissionTo(['ViewAny:Assignment', 'View:Assignment']);
|
||||||
|
$this->actingAs($user);
|
||||||
|
$this->currentSemester = app(GeneralSettings::class)->current_semester;
|
||||||
|
});
|
||||||
|
|
||||||
|
it('can view submission detail page', function () {
|
||||||
|
$assignment = Assignment::factory()->create();
|
||||||
|
$assignment->course->update(['semester' => $this->currentSemester]);
|
||||||
|
|
||||||
|
Livewire::test(SubmissionDetailPage::class, ['record' => $assignment])
|
||||||
|
->assertSuccessful()
|
||||||
|
->assertSee($assignment->title);
|
||||||
|
});
|
||||||
|
});
|
||||||
Loading…
Reference in New Issue
Block a user