Laporan Remediasi Audit CBT — Batch 2: Alur Peserta & Integritas Runner FE
Tanggal: 29 September 2026
Status Batch: SELESAI & TERVERIFIKASI
Baseline Commit: 8abc4dac0bbc610ea8a38ea32550b45299d4c63d
Lingkungan Pengujian: PostgreSQL 16 disposable (scola_assessment_test) & Vitest / vue-tsc
1. Ringkasan Temuan & Solusi
B01 — Admission & Runner Frontend Gagal Menyelesaikan Kontrak API (P1)
- Akar Masalah:
- FE memanggil
GET /api/v1/runner/sessions/{id}/manifestyang tidak ada (404), danPUT /answersyang tidak ada (404). - Nama field tidak cocok:
expected_revisionvsrevision_number,selected_optionvsselected_option_id,essay_textvsresponse_text. - Modal login memanggil
/runner/authalih-alih/api/v1/runner/auth/lookup. - Perbaikan:
- Di
useExamRunner.ts: Alur diubah menjadiPOST /sessions/{id}/start$\rightarrow$GET /attempts/{id}/items$\rightarrow$POST /attempts/{id}/autosave. - Di
ExamRunner.vue: Bindings disesuaikan dengan skema backend (selected_option_id,numeric_value,response_text). - Di
ParticipantAuthModal.vue&ReadinessCheck.vue: Endpoint disesuaikan ke/api/v1/runner/auth/lookup, menyimpanrunner_token, dan mendukung parameter query SEB handoff (sessiondansession_id).
B07 — Debounce Save Lintas Soal & Submit Tanpa Flush Membuang Jawaban (P1)
- Akar Masalah:
- Timer debounce tunggal global (
autosaveTimeout) membatalkan penyimpanan soal sebelumnya saat peserta berpindah soal cepat (<500ms). - Submit langsung memanggil
/submittanpa menunggu autosave in-flight atau pending debounce selesai. - Perbaikan:
- Mengganti timer global dengan antrean per-soal (
pendingTimers = new Map<string, any>(),pendingDrafts = new Map<string, any>()). Timer Soal 1 tidak membatalkan timer Soal 2. - Menambahkan flush barrier pada
submitExam: seluruh timer pending dieksekusi seketika dan di-await viaPromise.allSettledsebelum memanggilPOST /submit.
B08 — ACK Usang Menimpa State Lokal & Ketiadaan Rekonsiliasi Draft Gagal (P1)
- Akar Masalah:
- ACK lama yang tiba belakangan karena latensi menimpa jawaban yang lebih baru di state frontend.
- Saat fetch error, draft tidak disimpan di memori dan tidak bisa direkonsiliasi.
- Perbaikan:
- Memasang monotonic ACK guard: respon ACK hanya memperbarui state lokal jika
ack.accepted_revision >= currentRevision. - Jawaban selalu dicatat di
answers.value[qId]danpendingDraftssaat input, sehingga tidak hilang meskipun jaringan terputus.
B09 — Timer Mengikuti Jam Lokal Peserta & Rentan Submit Prematur (P1)
- Akar Masalah:
updateCountdownmembandingkanserverDeadlinelangsung denganDate.now()lokal peserta. Jika jam laptop 5 menit lebih cepat, ujian tersubmit otomatis secara prematur.- Perbaikan:
- Menghitung delta waktu server:
serverTimeDelta = serverNowMs - Date.now(). - Countdown menghitung sisa waktu berdasarkan waktu server aktual:
nowServer = Date.now() + serverTimeDelta. Jam lokal yang tidak akurat tidak menyebabkan submit prematur.
B11 — Concurrent Start Attempt Memunculkan IntegrityError / HTTP 500 (P2)
- Akar Masalah:
- Dua request
POST /sessions/{id}/startyang tiba bersamaan sama-sama membaca absence sebelum insert; yang kedua memicu unhandledIntegrityError(500). - Perbaikan:
- Di
runner_session_service.py:start_or_resume_attempt: menangkapIntegrityError, melakukan rollback subtransaksi secara aman, lalu me-resolve dan mengembalikan attempt yang telah dibuat secara idempoten (HTTP 200 OK).
2. File yang Diubah
| File | Keterangan Perubahan |
|---|---|
frontend/src/composables/useExamRunner.ts |
Kontrak start/items/autosave, debounce per-soal, flush barrier submit, monotonic ACK guard, clock skew resistance. |
frontend/src/views/runner/ExamRunner.vue |
Bindings field selected_option_id & response_text. |
frontend/src/components/readiness/ParticipantAuthModal.vue |
Perbaikan endpoint auth lookup & penyimpanan runner_token. |
frontend/src/views/runner/ReadinessCheck.vue |
Dukungan handoff query session_id/session & available_sessions. |
backend/app/services/runner_session_service.py |
Penanganan IntegrityError pada concurrent start race condition. |
frontend/tests/unit/useExamRunner.test.ts |
Unit test suite Vitest untuk B01, B07, B08, B09 (7 passed). |
tests/integration/test_b11_concurrent_start_regression.py |
Regression test suite PostgreSQL disposable untuk B11 (1 passed). |
3. Bukti Verifikasi Pengujian
A. Pengujian Backend PostgreSQL (B11 & Full Regression Suite)
- Sebelum patch B11:
tests/integration/test_b11_concurrent_start_regression.pyFAILED (IntegrityError). - Sesudah patch B11:
tests/integration/test_b11_concurrent_start_regression.py::test_b11_concurrent_start_requests_both_succeed_idempotently PASSED [100%] - Full Backend Integration Suite:
Hasil: 169 passed, 5 warnings in 82.75s (0:01:22) — 100% lulus tanpa regresi!
TEST_DATABASE_URL="postgresql://cbt@/scola_assessment_test?host=/var/run/postgresql" \ SCOLA_ASSESSMENT_ALLOW_TEST_DB_RESET=yes \ PYTHONPATH=backend \ .venv/bin/pytest tests/integration/ -q --tb=short
B. Pengujian Frontend Vitest & Build
- Vitest Unit Tests:
Hasil:
npx vitest run tests/unit/useExamRunner.test.ts✓ tests/unit/useExamRunner.test.ts (7 tests) 40ms ✓ B01: loadManifest follows valid backend protocol (POST /start then GET /items) ✓ B01: loadManifest sets clear runnerError on failure ✓ B07: per-question debounce does not discard prior question save ✓ B07: submitExam flushes pending saves before calling submit endpoint ✓ B08: monotonic ACK guard ignores older out-of-order ACK ✓ B08: offline save failure retains draft locally without data loss ✓ B09: clock skew compensation prevents premature automatic submit Test Files 1 passed (1) Tests 7 passed (7) - Frontend Typecheck & Production Build:
Hasil: 389 modules transformed. Built in 2.23s — Zero errors.
npm run build (vue-tsc && vite build)