# Pengujian dan QA

## Lapisan
- Unit: aturan nilai, prasyarat, SKS, status, dan transformasi mapping.
- Feature: autentikasi, RBAC, alur PMB/KRS/nilai/keuangan/yudisium, validasi, audit.
- Integrasi: MySQL, mail/storage, cron, antrean, adapter Feeder terhadap mock resmi yang tervalidasi.
- Manual/UAT: operator tiap unit memeriksa skenario dan laporan.

## Data uji
Gunakan data sintetis; hindari identitas nyata. Fixture mencakup periode terbuka/tutup, kapasitas penuh, konflik jadwal, koreksi nilai, pembayaran tertunda, dan koneksi Feeder gagal.

## Gerbang rilis
Build/dependency terkunci → pemeriksaan konfigurasi → migrasi di staging → smoke test → uji izin → backup dan restore → UAT → rilis dengan rollback siap. Pengujian performa realistis untuk kurang dari 1.000 mahasiswa, termasuk laporan dan ekspor.

## Kriteria QA
Tidak ada akses tidak sah pada skenario kritis; hasil transaksi konsisten; kesalahan integrasi terlihat dan dapat ditindaklanjuti; setiap alur inti punya bukti UAT; restore staging berhasil. Target cakupan numerik ditentukan tim saat implementasi.
- Pemeriksaan izin berulang pada menu dan baris data menggunakan relasi peran/permission yang dimuat sekali per request, bukan menjalankan query per pemeriksaan.

### Kriteria penerimaan periode akademik
- Mengaktifkan periode secara bersamaan tetap menghasilkan tepat satu periode aktif dan perubahan status tercatat pada audit.
- Periode yang menjadi aktif saat permintaan hapus berlangsung tidak dapat terhapus; foreign key tetap menolak penghapusan periode yang sudah dirujuk data lain.

### Kriteria penerimaan kurikulum
- Kurikulum hanya dapat diubah, ditambah mata kuliah, atau dilepas mata kuliahnya saat berstatus draf.
- Penerbitan kurikulum kosong ditolak; penerbitan dan perubahan draf bersamaan diserialkan agar versi terbit tidak berubah.
- Penambahan dan pelepasan mata kuliah pada draf membuat jejak audit untuk relasi kurikulum-mata kuliah.

### Kriteria penerimaan penawaran kelas dan KRS
- Perubahan data kelas dan penghapusan jadwal hanya berhasil saat kelas masih berstatus rencana; perubahan itu diserialkan dengan pembukaan kelas agar kelas terbuka tidak kehilangan jadwal wajib.
- Periode tidak aktif menolak penambahan/penghapusan kelas dan pengajuan KRS melalui UI maupun permintaan langsung.
- Perubahan waktu buka/tutup KRS menghasilkan audit perubahan field dan tidak merekam data di luar allowlist.
- Jika waktu buka/tutup KRS ditetapkan, permintaan membuat, menambah, melepas, atau mengajukan KRS sebelum waktu buka dan sesudah waktu tutup ditolak di server; tindakan pada kedua batas waktu diterima.
- Jika kedua waktu KRS kosong, pengelolaan KRS tetap mengikuti periode aktif. Form periode menolak pengisian hanya salah satu waktu dan menolak waktu tutup yang lebih awal daripada waktu buka.
- KRS yang diajukan hanya dapat disetujui jika riwayat akademik mahasiswa masih berstatus aktif saat keputusan dibuat.
- Kapasitas kelas tidak dapat dilampaui oleh KRS yang diajukan atau disetujui; bentrok jadwal dan batas SKS yang dikonfigurasi ditolak.
- Kelas yang memiliki setidaknya satu KRS diajukan atau disetujui tidak dapat dibatalkan; penutupan pendaftaran tetap tersedia untuk menghentikan pendaftaran baru.
- KRS yang sudah diajukan atau disetujui tidak dapat diubah oleh mahasiswa; keputusan peninjau tetap tercatat.

### Kriteria penerimaan presensi MVP
- Kelas tanpa KRS disetujui tidak muncul sebagai kelas presensi dan tidak menerima pertemuan.
- Tanggal pertemuan di luar periode, bukan hari jadwal, atau sudah tercatat untuk kelas yang sama ditolak.
- Daftar hadir hanya memuat mahasiswa yang KRS-nya disetujui pada kelas tersebut; setiap mahasiswa memiliki satu status per pertemuan.
- Pengguna tanpa `attendance.view` tidak dapat membuka daftar; pengguna tanpa `attendance.manage` tidak dapat membuat pertemuan atau menyimpan perubahan.
- Simpanan mencatat status dan pelaku, dan perubahan setelah simpan tetap konsisten; pengesahan/penguncian belum menjadi kriteria karena kebijakannya belum disepakati.

### Kriteria penerimaan nilai MVP
- Kelas tanpa mahasiswa ber-KRS disetujui tidak dapat memulai lembar nilai.
- Nama komponen dalam kelas tidak boleh duplikat; skor di luar 0–100 ditolak.
- Pengajuan ditolak jika total bobot tidak tepat 100% atau ada skor kosong.
- Setelah diajukan, komponen dan skor tidak dapat diubah sampai lembar ditolak; penolakan wajib disertai catatan.
- Persetujuan menyimpan skor numerik berbobot per pendaftaran; nilai huruf dan bobot mutu tetap kosong sampai skala kampus dikonfigurasi.
- Izin `grade.view`, `grade.manage`, dan `grade.review` diterapkan terpisah pada endpoint dan tampilan.

### Kriteria penerimaan konfigurasi skala
- Admin dapat memasukkan label/rentang/bobot mutu/status lulus tanpa nilai skala bawaan.
- Rentang di luar 0–100, batas minimum di atas maksimum, label duplikat, atau rentang tumpang tindih ditolak.
- Pembuatan/perubahan skala bersamaan tetap menolak label/rentang duplikat atau tumpang tindih; persetujuan nilai memakai satu snapshot skala yang terkunci selama transaksi.
- Nilai akhir yang tidak tercakup skala tidak dapat disetujui sampai rentang diperbaiki.
- Perubahan atau penghapusan skala tidak mengubah snapshot konversi pada nilai yang telah disetujui.

### Kriteria penerimaan ringkasan hasil studi
- Pengguna hanya dapat membuka ringkasan jika memiliki `academic-result.view`.
- Pilihan periode dibatasi pada KRS mahasiswa yang telah disetujui.
- Nilai yang belum disetujui tidak ditampilkan sebagai nilai akhir; SKS KRS dan SKS yang sudah bernilai dihitung terpisah.
- Rata-rata sementara hanya menghitung nilai disetujui dan ditimbang dengan SKS; label UI menjelaskan bahwa ini bukan IP/IPK resmi.
- Pratinjau cetak lintas semester hanya memuat KRS disetujui, menandai nilai yang belum tersedia, memakai IPK hanya ketika aturan kampus lengkap, dan menyatakan dengan jelas bahwa dokumen bukan transkrip resmi.
- IPK tidak dihitung dari subset nilai berbobot mutu: untuk kebijakan semua percobaan dan bobot mutu tertinggi, setiap percobaan bernilai harus memiliki bobot mutu; untuk kebijakan percobaan terakhir, percobaan terakhir setiap mata kuliah harus memiliki bobot mutu.
- Tampilan cetak memakai data periode yang dipilih, tidak menampilkan nilai yang belum disetujui sebagai final, dan secara jelas menyebut bahwa ringkasan belum menjadi dokumen resmi.

### Kriteria penerimaan PMB MVP
- Form publik hanya menerima pendaftaran saat siklus berstatus buka dan tanggal masih dalam rentang.
- Email dinormalisasi ke huruf kecil sebelum pemeriksaan keunikan; pendaftaran dengan perbedaan kapital atau request bersamaan pada siklus yang sama tidak membuat duplikat dan memberi pesan validasi yang aman. Spam dibatasi dengan rate limit.
- Perubahan status dan tanggal siklus yang berjalan bersamaan dengan pengiriman formulir harus mengikuti siklus terkunci; siklus yang telah ditutup tidak menerima pendaftaran baru.
- Pendaftaran dimulai pada status dikirim; penerimaan hanya dapat dilakukan setelah verifikasi; penolakan memerlukan catatan.
- Pendaftar yang belum diterima tidak dapat dikonversi; konversi menghasilkan tepat satu mahasiswa beserta riwayat program dan periode masuk.
- Benturan unik NIM/kode mahasiswa saat dua konversi berlangsung bersamaan menghasilkan pesan validasi aman; tidak ada mahasiswa atau riwayat akademik parsial yang tertinggal.
- Akses daftar/detail calon dibatasi permission PMB; formulir publik tidak menampilkan data pendaftar lain.

### Kriteria penerimaan mapping Neo Feeder
- Mapping hanya dapat menunjuk ke rekam lokal yang masih ada; penghapusan rekam lokal dan pembuatan mapping yang bersamaan tidak boleh meninggalkan mapping yatim.
- Satu ID eksternal tidak dapat dipetakan ke dua rekam lokal dalam sistem/jenis entitas yang sama.
- Uji autentikasi dan operasi baca sandbox boleh menggunakan panduan publik sebagai titik awal; impor/ekspor live menunggu respons sandbox, mapping, dan aturan sumber data ditinjau operator.
- HTTP publik ditolak pada production. Sandbox lokal dapat mengizinkannya hanya dengan opt-in `NEO_FEEDER_ALLOW_INSECURE_HTTP=true`; risiko kredensial terbuka harus dipahami dan dibatasi ke akun/jaringan uji.
- Tombol uji sandbox hanya menjalankan `GetToken`, menerima autentikasi berhasil hanya jika respons JSON memiliki `error_code = 0` dan token pada path konfigurasi, tidak menyimpan/menampilkan token, tidak mengikuti redirect, dan menulis audit tanpa URL, username, password, atau token.
- Pratinjau tarik hanya menerima pilihan checklist yang dipetakan ke operasi baca allowlist dari koleksi Postman (tidak termasuk `GetToken` atau operasi tulis), dapat menerima beberapa pilihan dan memprosesnya tiga jenis per tahap, membatasi hasil sampai 20 baris tiap jenis, menyajikan kolom sebagai tabel dan nilai bertingkat sebagai teks, menyediakan navigasi halaman/jenis berikutnya, tidak menyimpan respons ke database, menyamarkan kunci rahasia yang muncul, serta mencatat audit tanpa isi respons. Kesalahan jumlah pilihan ditampilkan dengan pesan Indonesia yang jelas.

### Kriteria penerimaan audit
- Perubahan model yang terdaftar menghasilkan event aktor/waktu/entitas/aksi dan field terkait.
- Field sensitif yang tidak masuk allowlist, kredensial, dan token tidak tersimpan sebagai nilai audit.
- Pengguna tanpa `audit.view` tidak dapat membuka log; endpoint baca tidak menyediakan aksi ubah/hapus.
- Respons sukses GET pada route terlindungi mencatat aktor, waktu, dan nama route; ekspor/unduhan memakai event khusus. Log tidak memuat path/parameter record, nilai pencarian, isi respons, atau secret.
- Unduhan dokumen PMB memeriksa bahwa dokumen terkait dengan pendaftar pada URL, mencatat aktor unduh, memakai `Cache-Control: no-store, private`, dan tidak mengungkapkan file yang tidak ada.
- Kegagalan transaksi domain tidak meninggalkan event audit yang menyatakan perubahan berhasil.
- Ekspor daftar mahasiswa memakai izin khusus, hanya memuat kolom yang disetujui, mencatat aktor/waktu/kolom pada audit, dan melindungi sel CSV dari formula spreadsheet.
- Seluruh ekspor CSV menetralkan nilai teks yang diawali karakter formula spreadsheet, termasuk teks konfigurasi pada header.
- Semua ekspor CSV memakai `Cache-Control: no-store, private` dan `X-Content-Type-Options: nosniff`.
- Login/logout berhasil mencatat aktor tanpa alamat IP atau kredensial; perubahan akun mencatat perubahan peran/status tanpa menyimpan email atau kata sandi sebagai nilai audit.
- Email akun dinormalisasi ke huruf kecil saat dibuat atau diubah; pemeriksaan keunikan menerima bentuk kapital berbeda sebagai alamat yang sama.
- Dua perubahan akun yang bersamaan tidak dapat menonaktifkan atau menurunkan peran superadmin terakhir; akun superadmin yang sedang digunakan juga tidak dapat dinonaktifkan atau diturunkan.
- Pemulihan hanya mengirim token untuk akun aktif, memakai respons yang sama untuk email yang tidak dikenal, menolak token kedaluwarsa/terpakai, menegakkan aturan password, mencabut sesi database yang telah ada, dan tidak mencatat kata sandi atau token di audit.
- Penggantian kata sandi wajib memeriksa kata sandi lama di server, menolak penggunaan ulang kata sandi yang sama, mencabut sesi database lain tetapi mempertahankan sesi saat ini, dan mencatat audit tanpa nilai kata sandi.
- Menonaktifkan akun yang sudah login membuat permintaan terlindungi berikutnya mencabut sesi, mengarahkan kembali ke login, serta mencatat logout tanpa menyimpan token sesi.

### Kriteria penerimaan register tugas akhir awal
- Pengguna tanpa `final-project.view/create/update` tidak dapat membaca, membuat, atau mengubah register melalui URL langsung.
- Tahap dan status hanya disimpan dari isian petugas; aplikasi tidak mengarang urutan maupun menganggap status tertentu sebagai kelulusan.
- Mahasiswa harus ada; pembimbing yang dipilih harus dosen aktif; tanggal selesai tidak boleh mendahului tanggal mulai.
- Perubahan register mencatat aktor, waktu, dan field audit tanpa menyimpan topik/catatan sebagai nilai log.
- Catatan bimbingan terikat pada tugas akhir yang tepat; pengguna tidak dapat memindahkan atau mengubah catatan proyek lain dengan mengganti ID di URL.

### Kriteria penerimaan yudisium awal
- Kasus hanya dapat diajukan untuk riwayat studi yang diizinkan dan tidak boleh diduplikasi.
- Hanya pengaju berizin yang dapat mengajukan; hanya peninjau berizin yang dapat memutuskan.
- Pengembalian wajib memiliki catatan; persetujuan wajib memiliki tanggal, periode, dan referensi keputusan.
- Persetujuan memperbarui kasus yudisium dan status riwayat studi menjadi lulus secara atomik; bila salah satu gagal, keduanya tetap konsisten.
- Pengajuan ulang menyimpan histori keputusan terdahulu; sistem tidak mengklaim telah memeriksa syarat kelulusan kampus.
- Pengajuan ulang ditolak jika riwayat akademik mahasiswa sudah tidak berstatus aktif.

### Kriteria penerimaan pencatatan keuangan
- Pembayaran hanya diterima untuk tagihan terbuka; jumlah pembayaran posted tidak boleh melampaui nominal tagihan.
- Pencatatan, pembatalan pembayaran, dan pembatalan tagihan mengunci tagihan terlebih dahulu agar transaksi pada tagihan yang sama berjalan konsisten.
- Pembatalan pembayaran memerlukan alasan, mempertahankan riwayat transaksi, dan hanya mengubah pembayaran yang masih berstatus posted.
- Referensi tagihan dan kuitansi yang sama tidak dapat tercatat dua kali; benturan unik akibat request bersamaan menghasilkan validasi yang aman tanpa pesan database.
- Laporan total pembayaran hanya menghitung transaksi posted; transaksi batal tetap terlihat di riwayat, tetapi tidak menambah saldo bayar.
- Permission baca, buat tagihan, catat pembayaran, dan batalkan pembayaran diuji terpisah.

### Kriteria penerimaan pendaftaran wisuda awal
- Hanya kasus yudisium approved yang riwayat akademiknya berstatus graduated dapat dipilih dan didaftarkan.
- Pendaftaran hanya dapat dibuat saat acara terbuka dan hanya oleh izin `graduation.registration.manage`.
- Kasus yudisium yang sama tidak dapat didaftarkan dua kali pada acara yang sama.
- Permintaan pendaftaran yang berjalan bersamaan dengan penutupan acara harus mengikuti status acara setelah penguncian; pendaftaran tidak boleh berhasil jika acara sudah ditutup.
- Perubahan data peserta yang berjalan bersamaan tidak boleh melewati pemeriksaan bahwa peserta milik acara yang diminta.
- Membuka, menutup, membuat acara, dan mendaftarkan peserta memiliki permission terpisah dan tercatat pada audit.
- Pendaftaran wisuda tidak mengubah status akademik atau keputusan yudisium.
