LMS-BGN/docs/retrospectives/epic-2-retrospective-2025-1...

10 KiB

Epic 2 Retrospektif — Ujian & Sertifikat (LMS-BGN)

Tanggal: 2025-11-12 Cakupan: Siklus sesi ujian, penilaian, ringkasan, daftar ujian, pengiriman ulang sertifikat

Ringkasan Hasil

  • Endpoint backend untuk Epic 2 telah dibuat dan diverifikasi dengan pengujian.
  • Contoh REST Client dan cURL tersedia untuk QA manual.
  • Kontrak mengikuti docs/api/openapi.yaml dan diselaraskan dengan kebutuhan UI inti.
  • Integrasi frontend sengaja ditunda sesuai instruksi; fokus pada pelurusan dokumentasi.

Cakupan yang Diimplementasikan

  • POST /api/exam-session — membuat sesi ujian (validasi dasar).
  • POST /api/exam-session/{sessionId}/score — penilaian batch dengan idempotencyKey.
  • GET /api/exam-session/{sessionId}/summary — ringkasan hasil teragregasi.
  • GET /api/exams — daftar ujian terpaginasikan dengan filter teks q.
  • POST /api/certificates/{certificateId}/resend — pengiriman ulang email (diterima + throttling).

Cakupan OpenAPI & Kuis Interaktif

  • ExamSessionCreateRequest: saat ini memuat examId dan userId. Mode interaktif ditentukan dari metadata ujian di sisi server (lihat ExamListItem.isInteractive). Tidak ada skema bernama InteractiveExam terpisah pada spesifikasi sekarang.
  • ExamScoreRequest: memuat mode (batch|stream), idempotencyKey, dan answers[] dengan bentuk sederhana { questionId, choiceIndex }. Bentuk ini belum mencakup tipe interaktif (hotspot, puzzle, scenario) dari sampleInteractiveExamData; perlu perluasan skema di fase selanjutnya.
  • ExamSummaryResponse: bentuk respons diselaraskan dengan kebutuhan halaman ringkasan (judul, skor, total, jawaban per soal) dan sesuai contoh di spesifikasi.
  • ExamListItem: memuat isInteractive: boolean sehingga UI dapat membedakan kuis interaktif di listing.
  • Konsekuensi: Integrasi penuh fitur interaktif memerlukan penambahan skema dan payload yang lebih kaya (mis. struktur hotspot, puzzle, scenario) ke OpenAPI sebelum wiring frontend.

Kualitas & Metrik

  • Pengujian: 17 lulus, 0 gagal (Jest + Supertest).
  • Durasi suite: ~1.8s lokal.
  • Artefak QA:
    • REST Client: docs/qa/assessments/backend-endpoints.http
    • cURL: docs/qa/assessments/curl-examples.md

Hal yang Berjalan Baik

  • Bootstrap backend cepat dengan Express; pemisahan concern jelas.
  • Idempotensi & throttling dasar untuk route sensitif.
  • Pengujian menyentuh happy-path dan kasus error inti.
  • Dokumentasi dan contoh memudahkan verifikasi manual.

Tantangan

  • Integrasi frontend belum dihubungkan: src/app/exam-session/page.tsx, exam-summary, dan exams masih butuh wiring API (ditunda sesuai instruksi).
  • Data masih in-memory; belum ada persistence atau auth sehingga alur belum realistis.
  • Kesenjangan pemetaan sampleInteractiveExamData ke payload OpenAPI saat ini (belum ada skema interaktif rinci).

Risiko & Mitigasi

  • Drift kontrak vs ekspektasi frontend — Mitigasi: patuhi docs/api/openapi.yaml, perluas skema untuk interaktif, dan sesuaikan pengujian.
  • Deliverability email & audit — Mitigasi: rencanakan konfigurasi SMTP, audit log, dan retry/backoff.
  • Tidak ada persistence — Mitigasi: definisikan skema minimal, tambah layer DB ringan, pindahkan state idempotensi dari memori ke storage.
  • Keamanan & rate limiting — Mitigasi: tambahkan auth checks dan rate-limit untuk route ujian & sertifikat.

Tindak Lanjut (Tanpa Implementasi Langsung)

  • Perluas OpenAPI untuk mendukung tipe interaktif (hotspot, puzzle, scenario) dan bentuk jawaban yang relevan.
  • Definisikan pemetaan dari sampleInteractiveExamData ke payload backend (create/score/summary) dan tambahkan contoh di dokumentasi.
  • Tambah persistence minimal untuk sesi & jawaban; pertimbangkan auth dasar.
  • Siapkan rencana wiring frontend ke backend untuk sprint berikutnya, sesuai skema yang diperluas.
  • CI: jalankan npm test pada push/PR.
  • Observabilitas: logging terstruktur & audit trail untuk alur ujian/sertifikat.

Referensi

  • Konteks: docs/epic-2-context.md
  • OpenAPI: docs/api/openapi.yaml
  • QA: docs/qa/assessments/backend-endpoints.http, docs/qa/assessments/curl-examples.md
  • Frontend (belum terhubung): src/app/exam-session/page.tsx, src/app/exam-summary/page.tsx, src/app/exams/page.tsx

Contoh Payload — Interaktif (Draft)

Catatan: Contoh berikut bertujuan memudahkan diskusi skema. Beberapa field bertanda "proposal" tidak ada dalam spesifikasi saat ini dan akan diajukan di perluasan OpenAPI berikutnya.

Create (proposal ekstensi)

Request:

{
  "examId": "interactive-dapur-mbg-001",
  "userId": "user-123",
  "isInteractive": true,
  "idempotencyKey": "create-5a1d9e2c-9f12-4d3a-b3a1-7c8e0b4f6a11",
  "clientContext": {
    "device": "web",
    "appVersion": "0.1.0",
    "locale": "id-ID"
  }
}

Response (sesuai spesifikasi saat ini):

{
  "sessionId": "sess-001",
  "status": "created",
  "startTime": "2025-11-12T08:00:00Z"
}

Score (proposal dukungan tipe interaktif)

Request:

{
  "mode": "batch",
  "idempotencyKey": "score-8d3bc2f0-26a3-4fd0-9b4c-5e2b7b1f9c77",
  "answers": [
    { "questionId": "1", "type": "enhanced_mcq", "choiceIndex": 0, "confidence": 4 },
    { "questionId": "video-scenario-1", "type": "video_scenario", "scenarioAnswers": [
      { "stepId": "step1", "selectedOptionId": "opt4" },
      { "stepId": "step2", "selectedOptionId": "opt4" }
    ]},
    { "questionId": "hotspot-scenario-1", "type": "image_hotspot", "selectedHotspots": ["hotspot1", "hotspot2", "hotspot4"] },
    { "questionId": "media-gallery-1", "type": "media_gallery", "viewedItems": ["video1", "image1", "video2", "image2", "document1", "image3"] },
    { "questionId": "3", "type": "puzzle", "matches": [
      { "pieceId": "ccp", "targetId": "def1" },
      { "pieceId": "haccp", "targetId": "def2" },
      { "pieceId": "sanitasi", "targetId": "def3" },
      { "pieceId": "kontaminasi", "targetId": "def4" }
    ]},
    { "questionId": "6", "type": "scenario", "selectedOptionId": "option2" }
  ]
}

Response (contoh ringkas):

{
  "totalScore": 85,
  "totalQuestions": 10,
  "correctAnswers": 8,
  "scorePercent": 85,
  "perQuestion": [
    { "questionId": "1", "isCorrect": true, "earnedPoints": 10, "maxPoints": 10 },
    { "questionId": "video-scenario-1", "isCorrect": true, "earnedPoints": 20, "maxPoints": 20 }
  ]
}

Summary (kompatibel + proposal detail)

Response (kompatibel):

{
  "examId": "interactive-dapur-mbg-001",
  "examTitle": "Kuis Interaktif Dapur MBG",
  "totalQuestions": 10,
  "correctAnswers": 8,
  "score": 85,
  "timeSpent": "25 menit 30 detik",
  "completedAt": "2025-11-12T08:30:00Z",
  "answers": [
    {
      "questionId": "1",
      "question": "Apa langkah pertama dalam memastikan keamanan pangan di Dapur MBG?",
      "userAnswer": "Melakukan analisis bahaya",
      "correctAnswer": "Melakukan analisis bahaya",
      "isCorrect": true,
      "type": "enhanced_mcq"
    },
    {
      "questionId": "video-scenario-1",
      "question": "Analisis Situasi Dapur: Identifikasi Masalah Keamanan Pangan",
      "userAnswer": "step1: opt4; step2: opt4",
      "correctAnswer": "step1: opt4; step2: opt4",
      "isCorrect": true,
      "type": "video_scenario"
    },
    {
      "questionId": "hotspot-scenario-1",
      "question": "Identifikasi Area Bermasalah dalam Tata Letak Dapur",
      "userAnswer": "hotspot1,hotspot2,hotspot4",
      "correctAnswer": "hotspot1,hotspot2,hotspot3,hotspot4,hotspot5",
      "isCorrect": false,
      "type": "image_hotspot"
    },
    {
      "questionId": "3",
      "question": "Cocokkan istilah keamanan pangan dengan definisinya",
      "userAnswer": "ccp->def1; haccp->def2; sanitasi->def3; kontaminasi->def4",
      "correctAnswer": "ccp->def1; haccp->def2; sanitasi->def3; kontaminasi->def4",
      "isCorrect": true,
      "type": "puzzle"
    }
  ]
}

Catatan kompatibilitas:

  • Agar tetap kompatibel, userAnswer untuk tipe interaktif diringkas sebagai string; perluasan dapat menambahkan details opsional berisi struktur lengkap per tipe (proposal).

Catatan Implementasi (Update)

  • Artefak QA telah disinkronkan: payload union untuk ExamScoreRequest tersedia di docs/qa/assessments/backend-endpoints.http, termasuk blok uji MCQ-only untuk hasil skor positif.
  • Backend kini memparse dan menilai tipe interaktif (video_scenario, image_hotspot, media_gallery, puzzle, scenario) dengan skema sederhana, termasuk partial scoring per tipe; bentuk { questionId, choiceIndex } tetap didukung untuk MCQ.
  • Respons GET /api/exam-session/{sessionId}/summary sekarang menyertakan answers[].details per tipe (langkah video, hotspot terpilih vs kunci, progres gallery, langkah puzzle, dan opsi scenario) sehingga kompatibel + kaya detail sesuai contoh draft.
  • Pemeriksaan Authorization (Bearer) diterapkan server-side untuk endpoint ujian: create session, score, dan summary. QA contoh sudah menyertakan header Authorization: Bearer test-token.

NFR & Observabilitas (Ringkas)

  • Logging terstruktur dengan korelasi requestId/sessionId untuk create/score/summary/resend
  • Metrics: score_latency_ms, summary_latency_ms, error_rate, resend_throttle_hits
  • Alerting sederhana untuk lonjakan error rata-rata p95
  • Audit trail untuk resend (recipient, status, attempt)

Dependencies (Versi Target)

  • Node.js 18.x, Express 4.x, Jest 29.x, Supertest 6.x, Nodemailer 6.x

Acceptance Criteria Atomik + Pemetaan Test

  • POST /api/exam-session membuat sesi valid → tes integrasi create (Bearer wajib)
  • POST /api/exam-session/{sessionId}/score menghitung skor (MCQ+interaktif) & idempotent → tes integrasi score + idempotency
  • GET /api/exam-session/{sessionId}/summary menampilkan agregat & answers[].details → tes integrasi summary
  • POST /api/certificates/{certificateId}/resend throttling + audit → tes integrasi resend

Traceability Matrix (Ringkas)

AC ID Bagian Komponen Story Tes
R-AC1 Create Session server.js route create docs/stories/2-1-exam-session-create-api.md backend/tests/server.test.js (create session)
R-AC2 Scoring & Idempotency scorer util + route score docs/stories/2-2-exam-scoring-api.md backend/tests/server.test.js (score + idempotency)
R-AC3 Summary kompatibel summary builder + route summary docs/stories/2-3-exam-summary-read-api.md backend/tests/server.test.js (summary)
R-AC4 Resend throttled + audit resend handler TBD: docs/stories/2-4-certificate-resend-api.md backend/tests/server.test.js (resend)