Example Database
 [x] Model find all
 [x] Soft delete leaves row

Attendance Flow (Tests\Integration\AttendanceFlow)
 [x] Student attendance entry flow success
 [x] Student attendance entry prevents duplicate
 [x] Student attendance complete entry and exit
 [x] Student attendance exit without entry
 [x] Teacher attendance entry flow success
 [x] Attendance with invalid unique code
 [x] Attendance with invalid waktu
 [x] Student can attend consecutive days

Example Session
 [x] Session simple

Health
 [x] Is defined app path
 [x] Base url has been set

Common Helpers (Tests\Unit\Helpers\CommonHelpers)
 [x] Clean str removes special characters
 [x] Clean str removes forbidden characters
 [x] Clean str trims whitespace
 [x] Clean number converts string to int
 [x] Clean number returns zero for empty string
 [x] Clean number returns zero for non numeric
 [x] Clean number trims whitespace
 [x] Clean number handles negative numbers
 [x] Clr quotes removes double quotes
 [x] Clr quotes removes single quotes
 [x] Clr quotes removes both quote types
 [x] Str trim removes leading whitespace
 [x] Str trim removes trailing whitespace
 [x] Str trim handles empty string
 [x] Str trim handles null
 [x] Str replace replaces string
 [x] Str replace handles empty string
 [x] Str replace handles null
 [x] Remove forbidden characters removes semicolon
 [x] Remove forbidden characters removes angular brackets
 [x] Remove forbidden characters removes multiple characters
 [x] Remove forbidden characters removes slash
 [x] Remove special characters removes hash
 [x] Remove special characters removes exclamation
 [x] Remove special characters removes parentheses
 [x] Remove special characters with quotes removal
 [x] Remove special characters without quotes removal
 [x] Generate token returns string
 [x] Generate token generates unique tokens
 [x] Generate token short version shorter
 [x] Generate token contains no dots only dashes
 [x] Count items returns correct count
 [x] Count items returns zero for empty array
 [x] Count items returns zero for null
 [x] Count items returns zero for non array
 [x] Get c s v input value returns value
 [x] Get c s v input value returns empty string for missing key
 [x] Get c s v input value returns zero for missing key int
 [x] Get c s v input value returns empty for empty array
 [x] Get c s v input value handles null array
 [x] Clean str handles x s s attempt
 [x] Clean str handles s q l injection attempt
 [x] Remove forbidden characters handles path traversal
 [x] Generate token is reasonably long

Kelas Model
 [x] Format kelas label uses desa kelompok and dapuan

Guru Model (Tests\Unit\Models\GuruModel)
 [x] Create guru successfully
 [x] Create guru with rfid
 [x] Create guru generates unique code
 [x] Cek guru by unique code
 [x] Cek guru by rfid code
 [x] Get all guru
 [x] Get guru by id
 [x] Update guru
 [x] Update guru with rfid
 [x] Cek guru with invalid code
 [x] Get guru by id with invalid id
 [x] Get all guru when empty
 [x] Create guru generates different unique codes
 [x] Update guru does not change unique code
 [x] Get all guru ordered by name
 [x] Cek guru returns first match for or condition
 [x] Generate c s v object successfully
 [x] Generate c s v object with b o m
 [x] Import c s v item successfully
 [x] Import c s v item with multiple records
 [x] Generate c s v object with empty file

Jurusan Model (Tests\Unit\Models\JurusanModel)
 [x] Generate c s v object successfully
 [x] Generate c s v object with b o m
 [x] Import c s v item successfully
 [x] Import c s v item duplicate handling
 [x] Import c s v item with multiple records
 [x] Generate c s v object with empty file

Kelas Model (Tests\Unit\Models\KelasModel)
 [x] Generate c s v object successfully
 [x] Generate c s v object with b o m
 [x] Import c s v item successfully
 [x] Import c s v item duplicate handling
 [x] Import c s v item with multiple records
 [x] Generate c s v object with empty file

Presensi Guru Model (Tests\Unit\Models\PresensiGuruModel)
 [x] Cek absen returns false when no attendance
 [x] Cek absen returns id presensi when attendance exists
 [x] Absen masuk creates new attendance record
 [x] Absen keluar updates existing attendance
 [x] Get presensi by id guru tanggal returns correct record
 [x] Get presensi by id returns correct record
 [x] Update presensi with new record
 [x] Update presensi with existing record
 [x] Get presensi by tanggal returns all teachers
 [x] Cek absen with non existent teacher
 [x] Cek absen with different date
 [x] Get presensi by id guru tanggal returns null when no record
 [x] Get presensi by id returns null for invalid id
 [x] Absen keluar with invalid id
 [x] Update presensi keeps existing keterangan when null
 [x] Multiple absen masuk on same day
 [x] Cek absen with time object
 [x] Update presensi only updates jam masuk when provided
 [x] Update presensi only updates jam keluar when provided
 [x] Get presensi by kehadiran with hadir
 [x] Get presensi by kehadiran with tanpa keterangan

Presensi Siswa Model (Tests\Unit\Models\PresensiSiswaModel)
 [x] Cek absen returns false when no attendance
 [x] Cek absen returns id presensi when attendance exists
 [x] Absen masuk creates new attendance record
 [x] Absen keluar updates existing attendance
 [x] Get presensi by id siswa tanggal returns correct record
 [x] Get presensi by id returns correct record
 [x] Update presensi with new record
 [x] Update presensi with existing record
 [x] Get presensi by kelas tanggal returns all students
 [x] Cek absen with non existent student
 [x] Cek absen with different date
 [x] Get presensi by id siswa tanggal returns null when no record
 [x] Get presensi by id returns null for invalid id
 [x] Absen masuk without kelas id
 [x] Absen keluar with invalid id
 [x] Update presensi keeps existing keterangan when null
 [x] Multiple absen masuk on same day
 [x] Cek absen with time object
 [x] Update presensi only updates jam masuk when provided
 [x] Update presensi only updates jam keluar when provided
 [x] Get presensi by kehadiran with hadir
 [x] Get presensi by kehadiran with tanpa keterangan

Siswa Model (Tests\Unit\Models\SiswaModel)
 [x] Create siswa successfully
 [x] Create siswa with rfid
 [x] Cek siswa by unique code
 [x] Cek siswa by rfid code
 [x] Get siswa by id
 [x] Get all siswa with kelas
 [x] Get all siswa with kelas filter by tingkat
 [x] Get all siswa with kelas filter by jurusan
 [x] Get siswa by kelas
 [x] Get siswa count by kelas
 [x] Update siswa
 [x] Update siswa with rfid
 [x] Delete siswa
 [x] Delete multi selected
 [x] Cek siswa with invalid code
 [x] Get siswa by id with invalid id
 [x] Get siswa by kelas with no students
 [x] Get siswa count by kelas with no students
 [x] Delete siswa with invalid id
 [x] Get siswa with invalid id
 [x] Create siswa generates unique code
 [x] Update siswa does not change unique code
 [x] Get siswa count by kelas with invalid id
 [x] Get all siswa with kelas ordered by name
 [x] Delete multi selected with empty array
 [x] Cek siswa returns first match for or condition

QRGenerator
 [x] Generate qr code

RFIDRules (Tests\Unit\Validation\RFIDRules)
 [x] Is rfid unique with empty string returns true
 [x] Is rfid unique for new siswa with unique code
 [x] Is rfid unique for new guru with unique code
 [x] Is rfid unique returns false when code exists in siswa
 [x] Is rfid unique returns false when code exists in guru
 [x] Is rfid unique checks across both tables
 [x] Is rfid unique excludes same student when updating
 [x] Is rfid unique excludes same teacher when updating
 [x] Is rfid unique does not exclude other students
 [x] Is rfid unique without type parameter
 [x] Is rfid unique with only exclude id no type
 [x] Is rfid unique excludes correctly across types
 [x] Is rfid unique with null rfid code
 [x] Is rfid unique with whitespace rfid code
 [x] Is rfid unique case sensitivity
 [x] Is rfid unique with special characters
 [x] Is rfid unique multiple students with null rfid

