Files
aplikasi-ketertiban/tests/Feature/AttendanceSyncViolationFixTest.php
T
Novy San87 dc9315be9e fix: violation alpha sync per (siswa, tanggal) — hapus violation lama saat push koreksi tanpa alpha
Bug: $alphaDate global null saat push tanpa alpha → violation lama tak pernah
dihapus (false positive); totalAlpha dihitung utk semua siswa dgn tanggal global
→ rawan salah siswa/tanggal. Kini iterasi per (student, date) dari push.
+ test AttendanceSyncViolationFixTest (4 test).
2026-08-31 18:04:05 +07:00

172 lines
6.5 KiB
PHP

<?php
namespace Tests\Feature;
use App\Models\Attendance;
use App\Models\Setting;
use App\Models\Student;
use App\Models\Violation;
use App\Models\ViolationType;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Tests\TestCase;
/**
* FIX 2026-08-31 — bug sync attendance e-jurnal → violation alpha:
* 1. violation lama tidak pernah dihapus saat push koreksi tanpa alpha ($alphaDate null)
* 2. totalAlpha dihitung untuk semua siswa memakai tanggal global → salah siswa/tanggal
*
* Kasus nyata: XII TKJ 3 26-08 HENDIK (sakit) / RAMADANI & RHEDO (hadir) dicatat bolos.
*/
class AttendanceSyncViolationFixTest extends TestCase
{
use RefreshDatabase;
protected function setUp(): void
{
parent::setUp();
Setting::setValue('ejurnal_sync_token', 'test-token');
$category = \App\Models\ViolationCategory::create([
'name' => 'Kehadiran',
'slug' => 'kehadiran',
'color' => '#EF4444',
'is_active' => true,
]);
ViolationType::create([
'category_id' => $category->id,
'name' => 'Membolos/tidak masuk sekolah tanpa keterangan',
'slug' => 'alpha',
'points' => 20, // poin per 10 JP (2 poin/JP)
'description' => 'Tidak hadir tanpa keterangan',
'is_active' => true,
'is_system' => true,
]);
}
protected function makeStudent(int $sourceId, string $name): Student
{
return Student::create([
'source_id' => (string) $sourceId,
'nisn' => '00' . $sourceId,
'student_number' => 'TEST/' . $sourceId,
'full_name' => $name,
'class_name' => 'XII TKJ 3',
'class_level' => 'XII',
'department_name' => 'TKJ',
'status' => 'active',
'is_active' => true,
]);
}
protected function sync(array $attendances): \Illuminate\Testing\TestResponse
{
return $this->post('/api/v1/attendance/sync', [
'token' => 'test-token',
'attendances' => $attendances,
]);
}
public function test_push_alpha_membuat_violation_hanya_untuk_siswa_alpha(): void
{
$s1 = $this->makeStudent(1001, 'SISWA ALPHA');
$s2 = $this->makeStudent(1002, 'SISWA HADIR');
$this->sync([
['source_student_id' => 1001, 'date' => '2026-08-26', 'lesson_hour' => 1, 'status' => 'alpha'],
['source_student_id' => 1001, 'date' => '2026-08-26', 'lesson_hour' => 2, 'status' => 'alpha'],
['source_student_id' => 1002, 'date' => '2026-08-26', 'lesson_hour' => 1, 'status' => 'hadir'],
])->assertOk();
// Siswa alpha → violation 2 JP = 4 poin (2 poin/JP)
$this->assertDatabaseHas('violations', [
'student_id' => $s1->id,
'violation_date' => '2026-08-26',
'points' => 4,
]);
// Siswa hadir → TIDAK boleh kena violation
$this->assertDatabaseMissing('violations', [
'student_id' => $s2->id,
'violation_date' => '2026-08-26',
]);
}
public function test_push_koreksi_tanpa_alpha_menghapus_violation_lama(): void
{
$s1 = $this->makeStudent(1001, 'SISWA KOREKSI');
$alphaType = ViolationType::where('slug', 'alpha')->first();
// Simulasikan violation system-generated lama (salah: siswa sebenarnya sakit)
Violation::create([
'student_id' => $s1->id,
'student_class' => 'XII TKJ 3',
'violation_type_id' => $alphaType->id,
'recorded_by' => null,
'description' => 'Alpha - Tidak hadir tanpa keterangan (4 jam pelajaran)',
'points' => 8,
'violation_date' => '2026-08-26',
'notes' => 'Dibuat otomatis dari sinkron E-Jurnal.',
]);
// Push koreksi: siswa kini SAKIT (tidak ada alpha sama sekali dalam push)
$this->sync([
['source_student_id' => 1001, 'date' => '2026-08-26', 'lesson_hour' => 1, 'status' => 'sakit'],
['source_student_id' => 1001, 'date' => '2026-08-26', 'lesson_hour' => 2, 'status' => 'sakit'],
])->assertOk();
// Violation lama harus TERHAPUS (bug lama: tidak terhapus karena $alphaDate null)
$this->assertNull(
Violation::where('student_id', $s1->id)->where('violation_date', '2026-08-26')->first(),
'Violation alpha lama harus terhapus setelah push koreksi tanpa alpha.'
);
}
public function test_push_multi_tanggal_tidak_membuat_violation_salah_tanggal(): void
{
$s1 = $this->makeStudent(1001, 'SISWA CAMPURAN');
// Push berisi 2 tanggal: 26-08 hadir, 27-08 alpha.
// Bug lama: $alphaDate global = 27-08 → siswa dihitung alpha di 27-08 untuk semua siswa.
// Harusnya violation HANYA untuk 27-08.
$this->sync([
['source_student_id' => 1001, 'date' => '2026-08-26', 'lesson_hour' => 1, 'status' => 'hadir'],
['source_student_id' => 1001, 'date' => '2026-08-27', 'lesson_hour' => 1, 'status' => 'alpha'],
['source_student_id' => 1001, 'date' => '2026-08-27', 'lesson_hour' => 2, 'status' => 'alpha'],
])->assertOk();
$this->assertDatabaseHas('violations', [
'student_id' => $s1->id,
'violation_date' => '2026-08-27',
'points' => 4,
]);
$this->assertDatabaseMissing('violations', [
'student_id' => $s1->id,
'violation_date' => '2026-08-26',
]);
}
public function test_push_siswa_yang_bukan_alpha_tidak_kena_violation_saat_push_berisi_alpha_siswa_lain(): void
{
$s1 = $this->makeStudent(1001, 'SISWA ALPHA');
$s2 = $this->makeStudent(1002, 'SISWA HADIR');
// Bug lama: totalAlpha dihitung utk SEMUA siswa dgn tanggal global → s2 ikut kena.
$this->sync([
['source_student_id' => 1001, 'date' => '2026-08-26', 'lesson_hour' => 1, 'status' => 'alpha'],
['source_student_id' => 1002, 'date' => '2026-08-26', 'lesson_hour' => 1, 'status' => 'hadir'],
['source_student_id' => 1002, 'date' => '2026-08-26', 'lesson_hour' => 2, 'status' => 'hadir'],
])->assertOk();
$this->assertDatabaseHas('violations', [
'student_id' => $s1->id,
'violation_date' => '2026-08-26',
]);
$this->assertDatabaseMissing('violations', [
'student_id' => $s2->id,
'violation_date' => '2026-08-26',
]);
}
}