feat: implement attendance factory, model factory trait, and feature tests for attendance logic

This commit is contained in:
Yoga Pangestu 2026-03-31 11:24:27 +07:00
parent 513219105f
commit c59fb6e5bf
3 changed files with 198 additions and 0 deletions

View File

@ -2,11 +2,14 @@
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
class Attendance extends Model
{
use HasFactory;
protected $guarded = ['id'];
protected function casts(): array

View File

@ -0,0 +1,25 @@
<?php
namespace Database\Factories;
use App\Models\Attendance;
use App\Models\ClassSession;
use App\Models\CourseSchedule;
use App\Models\Student;
use Illuminate\Database\Eloquent\Factories\Factory;
class AttendanceFactory extends Factory
{
protected $model = Attendance::class;
public function definition(): array
{
return [
'student_id' => Student::factory(),
'class_session_id' => ClassSession::factory(),
'course_schedule_id' => CourseSchedule::factory(),
'date' => now()->toDateString(),
'attended_at' => now(),
];
}
}

View File

@ -0,0 +1,170 @@
<?php
use App\Enums\RoleEnum;
use App\Filament\Resources\Learning\Attendances\Pages\ManageAttendances;
use App\Models\Attendance;
use App\Models\ClassSession;
use App\Models\Course;
use App\Models\CourseSchedule;
use App\Models\Student;
use App\Models\User;
use App\Settings\GeneralSettings;
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:Attendance',
'Create:Attendance',
'Delete:Attendance',
];
foreach ($permissions as $permission) {
Permission::findOrCreate($permission);
}
$this->currentSemester = app(GeneralSettings::class)->current_semester;
});
describe('Attendance Authorization', function () {
it('allows all roles to view, but only specific actions are allowed for each role', function () {
$studentUser = User::factory()->create();
$studentUser->assignRole(RoleEnum::Student);
$studentUser->givePermissionTo(['ViewAny:Attendance']);
$student = Student::factory()->create(['user_id' => $studentUser->id]);
$this->actingAs($studentUser);
Livewire::test(ManageAttendances::class)
->assertSuccessful();
});
it('allows non-student roles like Developer to access page safely', function () {
$developer = User::factory()->create();
$developer->assignRole(RoleEnum::Developer);
$developer->givePermissionTo(['ViewAny:Attendance']);
$this->actingAs($developer);
Livewire::test(ManageAttendances::class)
->assertSuccessful();
});
});
describe('Attendance Model', function () {
it('has necessary relationships', function () {
$attendance = Attendance::factory()->create();
expect($attendance->student)->toBeInstanceOf(Student::class);
expect($attendance->classSession)->toBeInstanceOf(ClassSession::class);
expect($attendance->courseSchedule)->toBeInstanceOf(CourseSchedule::class);
});
});
describe('Attendance Logic (Presensi)', function () {
beforeEach(function () {
$this->user = User::factory()->create();
$this->user->assignRole(RoleEnum::Student);
$this->user->givePermissionTo(['ViewAny:Attendance']);
$this->student = Student::factory()->create(['user_id' => $this->user->id]);
$this->actingAs($this->user);
});
it('can perform attendance during class session', function () {
$course = Course::factory()->create(['semester' => $this->currentSemester]);
// Define day and time
$now = now();
$session = ClassSession::factory()->create([
'course_id' => $course->id,
'date' => $now->toDateString(),
'start_time' => $now->copy()->subMinutes(10), // started 10 minutes ago
'end_time' => $now->copy()->addMinutes(110),
]);
// Need a compatible schedule
$schedule = CourseSchedule::factory()->create([
'course_id' => $course->id,
'day_of_week' => $now->dayOfWeekIso,
]);
Livewire::test(ManageAttendances::class)
->call('attend', $session->id)
->assertHasNoErrors();
$this->assertDatabaseHas('attendances', [
'student_id' => $this->student->id,
'class_session_id' => $session->id,
'course_schedule_id' => $schedule->id,
]);
});
it('prevents attendance before class starts', function () {
$course = Course::factory()->create(['semester' => $this->currentSemester]);
$now = now();
$session = ClassSession::factory()->create([
'course_id' => $course->id,
'date' => $now->toDateString(),
'start_time' => $now->copy()->addHour(), // starts in 1 hour
]);
Livewire::test(ManageAttendances::class)
->call('attend', $session->id);
$this->assertDatabaseMissing('attendances', [
'student_id' => $this->student->id,
'class_session_id' => $session->id,
]);
});
it('prevents double attendance for same session', function () {
$attendance = Attendance::factory()->create([
'student_id' => $this->student->id,
]);
Livewire::test(ManageAttendances::class)
->call('attend', $attendance->class_session_id);
expect(Attendance::where('student_id', $this->student->id)
->where('class_session_id', $attendance->class_session_id)
->count())->toBe(1);
});
});
describe('Attendance History Table', function () {
it('can filter results by course in history table', function () {
$this->user = User::factory()->create();
$this->user->assignRole(RoleEnum::Student);
$this->user->givePermissionTo(['ViewAny:Attendance']);
$student = Student::factory()->create(['user_id' => $this->user->id]);
$this->actingAs($this->user);
$course1 = Course::factory()->create(['semester' => $this->currentSemester]);
$course2 = Course::factory()->create(['semester' => $this->currentSemester]);
$schedule1 = CourseSchedule::factory()->create(['course_id' => $course1->id]);
$schedule2 = CourseSchedule::factory()->create(['course_id' => $course2->id]);
$att1 = Attendance::factory()->create([
'student_id' => $student->id,
'course_schedule_id' => $schedule1->id,
'date' => now()->toDateString(),
]);
$att2 = Attendance::factory()->create([
'student_id' => $student->id,
'course_schedule_id' => $schedule2->id,
'date' => now()->subDay()->toDateString(),
]);
Livewire::test(ManageAttendances::class)
->filterTable('course_id', $course1->id)
->assertCanSeeTableRecords([$att1])
->assertCanNotSeeTableRecords([$att2]);
});
});