[TEST-DRAGONFLY-VITEST-SUITE] Standardisasi Unit Test Suite Vitest, Integrasi SonarQube LCOV, dan Kompatibilitas Graceful Cache DragonflyDB
Modul: Single Billing | Tipe: Refactoring | Stage Saat Ini:
PRODUCTION| Tanggal Dokumen: 2026-09-07
PRODUCTION RELEASED
Dokumen ini telah berada dalam tahap PRODUCTION. Perubahan logika backend, migrasi skema/konfigurasi, dan rilis v1.9.2 telah diterapkan secara penuh di lingkungan Production via MR !232.
LIFECYCLE GUIDE — UPDATE BERTAHAP:
- Buat file ini saat selesai development. Isi semua section kecuali Staging & Production.
- Tanggal Dokumen (Header) & Riwayat Update Log: Gunakan tanggal hari ini (pembuatan dokumen).
- Baris Development (Lifecycle): Gunakan tanggal & jam commit terakhir terkait di branch development.
- Update baris Staging saat naik ke UAT. Ubah "Stage Saat Ini" di header menjadi STAGING.
- Update baris Production saat naik ke Prod. Ubah "Stage Saat Ini" di header menjadi PRODUCTION.
- Update entry di README.md setiap kali status berubah. -->
1. Informasi Tugas & Referensi Bisnis
Task ID / Nomor Referensi:
TEST-DRAGONFLY-VITEST-SUITESumber Instruksi: Standardisasi Testing Backend LNSW (Vitest Runner), Integrasi SonarQube LCOV, & Kompatibilitas Cache DragonflyDB
Dokumen Referensi / Lampiran: Tidak ada
Task / Dependensi Terkait (Cross-Service):
Modul Terkait:
Single BillingService / Repository:
single-billing-BEPIC Pengembang: Muhamad Firly Ramadan & Hermansyah Efendi
Latar Belakang & Ringkasan Kebutuhan: Backend
single-billing-BEsebelumnya belum memiliki automated unit test suite yang komprehensif, sehingga proses verifikasi regresi logika bisnis pasca-refactoring masih bergantung pada pengujian manual dan laporan SonarQube CI/CD belum memiliki metrik code coverage yang valid. Di sisi lain, inisialisasi koneksi Redis yang bersifat wajib (mandatory/tightly coupled) menyebabkan server gagal bootstrap jika dijalankan pada lingkungan lokal tanpa instans Redis atau saat terjadi migrasi ke DragonflyDB. Task ini mengadopsi Vitest sebagai test-runner modern berbasis V8 coverage, membangun 36 file test suite (524 test cases lulus 100%), memperbaruiapp/helpers/redis.jsagar memiliki mekanisme safe fallback opsional (aplikasi tetap berjalan normal meskipun Redis offline) dengan parameter kompatibilitas DragonflyDB (enableReadyCheck: false), serta menyelaraskan pelaporan metrik SonarQube melalui berkascoverage/lcov.info.Ruang Lingkup & Rincian Sub-Pekerjaan (Technical Scope & Milestones):
- [x] Pemasangan dependensi
vitestdan@vitest/coverage-v8padadevDependencies - [x] Pembuatan konfigurasi
vitest.config.mjsdan global mocks lingkungan pengujian ditests/setup.js - [x] Pembuatan 36 file unit test suite mencakup seluruh komponen kritis (helpers, models, middlewares, dynamic routes, validations, connection wrapper)
- [x] Refactoring
app/helpers/redis.jsdengan fungsiresolveHostaman, kompatibilitas DragonflyDB, dan safe fallback saatREDIS_NODEStidak dikonfigurasi - [x] Penyesuaian properti
sonar-project.propertiesuntuk integrasi analisis coverage LCOV pada pipeline CI/CD SonarQube - [x] Verifikasi eksekusi pengujian lokal empiris: 36 file lolos, 524 assertions sukses tanpa kegagalan
- [x] Pemasangan dependensi
2. Deployment Lifecycle
| # | Environment | Tanggal & Waktu | PIC | Status | Catatan |
|---|---|---|---|---|---|
| 1 | Development | 2026-08-27 13:58 | Muhamad Firly Ramadan & Hermansyah Efendi | DEPLOYED | Branch: development (Merged from feat/dragonfly-db-with-unit-test) |
| 2 | Staging (UAT) | 2026-10-14 13:00 | Hermansyah Efendi / QA | DEPLOYED | Release Candidate via MR Staging !230. Co-deployed: [FEAT-SIMPONI-V3-ISOLATION, TASK-SET-EXPIRED-BILLING-V3, FEAT-BILLING-REKAP-EXPIRED-DATE-PAYLOAD, REFACTOR-TAGIHAN-CONTROLLER-DEDUPLICATION, FIX-PAYMENT-WEBHOOK-RACE-CONDITION, FIX-TAGIHAN-PREVENT-PAID-INVOICE-REORDER, FEAT-REQUEST-LOGGER-POSTMAN, TEST-DRAGONFLY-VITEST-SUITE, FEAT-RUNTIME-ENV-VALIDATION, TASK-NON-BLOCKING-BILLING-DISPATCH-AND-RETRY-CONFIG] |
| 3 | Production | 2026-10-14 20:06 | achmad fauzi | DEPLOYED | Branch: prod via MR !232 (Release v1.9.2). Co-deployed: [FEAT-SIMPONI-V3-ISOLATION, TASK-SET-EXPIRED-BILLING-V3, FEAT-BILLING-REKAP-EXPIRED-DATE-PAYLOAD, REFACTOR-TAGIHAN-CONTROLLER-DEDUPLICATION, FIX-PAYMENT-WEBHOOK-RACE-CONDITION, FIX-TAGIHAN-PREVENT-PAID-INVOICE-REORDER, FEAT-REQUEST-LOGGER-POSTMAN, TEST-DRAGONFLY-VITEST-SUITE, FEAT-RUNTIME-ENV-VALIDATION, TASK-NON-BLOCKING-BILLING-DISPATCH-AND-RETRY-CONFIG] |
Riwayat Update Log
| Tanggal | Environment | Perubahan | PIC |
|---|---|---|---|
| 2026-10-14 | Production | Status diupdate ke PRODUCTION (DEPLOYED, CLOSED), rilis Release v1.9.2 via MR !232 | Hermansyah Efendi |
| 2026-09-07 | Development | Inisialisasi draft audit log dari context block backend (commit ac4ce9d) | Muhamad Firly Ramadan & Hermansyah Efendi |
3. Analisis Teknis & Rincian Perubahan
Alur Logika & Bisnis:
- Safe Fallback & Kompatibilitas Cache DragonflyDB (
app/helpers/redis.js):- Konfigurasi cache runtime menggunakan 4 variabel lingkungan:
REDIS_NODES(formathost:portatau multi-node clusterhost1:port1,host2:port2),REDIS_KEY_PREFIX(namespace cache, e.g.single-billing:),REDIS_PASSWORD, danREDIS_USERNAME. - Variabel lingkungan
REDIS_NODESdiparsing menggunakan helperresolveHost(). Helper ini memvalidasi input string, memecah node cluster berdasarkan delimiter koma, dan menetapkan fallback port default6379. - Apabila
REDIS_NODEStidak dikonfigurasi atau string kosong,redisClientdiinisialisasinulltanpa memicu fatal exception saat proses bootstrap aplikasi. Sistem mencatat log:"[redis]: REDIS_NODES is not configured. Running without Redis (distributed locking disabled)". - Fungsi
acquireLock()danreleaseLock()mengimplementasikan mekanisme non-blocking graceful fallback saat Redis tidak aktif, menjamin proses CI/CD runner dan development lokal dapat berjalan mulus tanpa ketergantungan wajib pada Redis container eksternal. - Penambahan opsi konfigurasi
enableReadyCheck: falsepada driver ioredis Cluster untuk kompatibilitas protokol DragonflyDB yang tidak merespon perintah pemeriksaan kesiapan info Redis standar.
- Konfigurasi cache runtime menggunakan 4 variabel lingkungan:
- Standardisasi Framework Pengujian Vitest & LCOV SonarQube:
- Mengadopsi Vitest sebagai pengganti atau inisiasi test-runner modern dengan coverage engine
@vitest/coverage-v8. - File
tests/setup.jsmenyediakan mocking terpusat untuk koneksi database (connection.js), logger winston, dan external HTTP gateway guna memastikan seluruh unit test bersifat terisolasi (hermetic), deterministik, dan bebas side-effect jaringan. - Penyelarasan konfigurasi
sonar-project.propertiesdengan menyertakan direktoritests/dan mengarahkan path report kecoverage/lcov.infoguna pemenuhan metrik Quality Gate SonarQube.
- Mengadopsi Vitest sebagai pengganti atau inisiasi test-runner modern dengan coverage engine
- Klasifikasi File yang Berubah:
vitest.config.mjs(Config NEW — runner & coverage v8 settings)sonar-project.properties(Config — routing lcov report ke SonarQube)app/helpers/redis.js(Helper — safe connection parsing, fallback non-blocking, DragonflyDB mode)package.json&package-lock.json(Chore — vitest & coverage-v8 dependencies)tests/**(36 unit test files covering helpers, models, middlewares, validations, utils)
- Safe Fallback & Kompatibilitas Cache DragonflyDB (
Perubahan Database (DDL / DML):
sql-- Tidak ada migration file yang terdeteksi — pembaruan infrastruktur testing dan koneksi cache in-memory.Kontrak API & Response Payload: Tidak ada penambahan atau perubahan kontrak API publik. Penambahan script runner NPM pada
package.json:"test": "vitest run --coverage""test:only": "vitest run""test:watch": "vitest"
4. Bukti Teknis (Technical Proof & Audit Trail)
- Repository:
single-billing-BE - Branch Utama:
development - Merge Request / PR: !232 (Release v1.9.2 — Merged to prod) | Staging: !230
Daftar Commit Terkait (Multi-Commit Trail)
| Commit ID | Branch | Author | Tanggal & Jam | Ringkasan Perubahan |
|---|---|---|---|---|
2499b45 | feat/dragonfly-db-with-unit-test | Muhamad Firly Ramadan | 2026-08-19 12:53 | chore(deps): add vitest and coverage-v8 |
8104e10 | feat/dragonfly-db-with-unit-test | Muhamad Firly Ramadan | 2026-08-19 12:53 | chore(vitest): add config and global module mocks |
96c6585 | feat/dragonfly-db-with-unit-test | Muhamad Firly Ramadan | 2026-08-19 12:53 | chore(git): ignore coverage output |
a79b160 | feat/dragonfly-db-with-unit-test | Muhamad Firly Ramadan | 2026-08-19 12:54 | test(helpers): cover status, zod and sql guard helpers |
533b336 | feat/dragonfly-db-with-unit-test | Muhamad Firly Ramadan | 2026-08-19 12:54 | test(helpers): cover banking helper utilities |
c29cce8 | feat/dragonfly-db-with-unit-test | Muhamad Firly Ramadan | 2026-08-19 12:54 | test(helpers): cover simponi billing and inquiry senders |
75cca03 | feat/dragonfly-db-with-unit-test | Muhamad Firly Ramadan | 2026-08-19 12:54 | test(helpers): cover bmri response templates |
35ec298 | feat/dragonfly-db-with-unit-test | Muhamad Firly Ramadan | 2026-08-19 12:54 | test(helpers): cover billing receipt builder |
88199ca | feat/dragonfly-db-with-unit-test | Muhamad Firly Ramadan | 2026-08-19 12:54 | test(middlewares): cover method and mandatory field validation |
f093034 | feat/dragonfly-db-with-unit-test | Muhamad Firly Ramadan | 2026-08-19 12:54 | test(middlewares): cover token introspection |
e777f87 | feat/dragonfly-db-with-unit-test | Muhamad Firly Ramadan | 2026-08-19 12:54 | test(middlewares): cover schema validation |
79e824b | feat/dragonfly-db-with-unit-test | Muhamad Firly Ramadan | 2026-08-19 12:54 | test(utils): cover dynamic route registration |
b1d9851 | feat/dragonfly-db-with-unit-test | Muhamad Firly Ramadan | 2026-08-19 12:54 | test(validations): cover dashboard query schema |
1fc337b | feat/dragonfly-db-with-unit-test | Muhamad Firly Ramadan | 2026-08-19 12:54 | test(connection): cover pool query wrapper |
49c9969 | feat/dragonfly-db-with-unit-test | Muhamad Firly Ramadan | 2026-08-19 12:54 | test(models): cover banking raw query templates |
72bb0d7 | feat/dragonfly-db-with-unit-test | Muhamad Firly Ramadan | 2026-08-19 12:54 | test(models): cover inquiry and payment schemas |
f538521 | feat/dragonfly-db-with-unit-test | Muhamad Firly Ramadan | 2026-08-19 12:54 | test(models): cover satker and va schemas |
eec3df77 | feat/dragonfly-db-with-unit-test | Muhamad Firly Ramadan | 2026-08-19 12:54 | test(models): cover banking model definitions |
5fcd6ea | feat/dragonfly-db-with-unit-test | Muhamad Firly Ramadan | 2026-08-19 12:54 | test(models): cover tagihan schemas and va padding |
8f05a6d | feat/dragonfly-db-with-unit-test | Muhamad Firly Ramadan | 2026-08-24 10:00 | feat(dragonfly): add dragonflydb compability config |
bfc73af | feat/dragonfly-db-with-unit-test | Hermansyah Efendi | 2026-08-27 12:31 | fix(redis): make connection optional and add safe env parsing |
de0e6c3 | feat/dragonfly-db-with-unit-test | Hermansyah Efendi | 2026-08-27 12:33 | chore(sonar): align configuration with vitest and lcov coverage |
ac4ce9d | feat/dragonfly-db-with-unit-test | Hermansyah Efendi | 2026-08-27 13:58 | test(helpers): cover helpers and middlewares suites |
Cuplikan Kode / Implementasi Inti
// app/helpers/redis.js (Graceful Redis / DragonflyDB Fallback & Safe Host Parsing)
const resolveHost = (raw) => {
if (!raw || typeof raw !== "string" || !raw.trim()) return [];
return raw
.split(",")
.map((entry) => entry.trim())
.filter(Boolean)
.map((entry) => {
const [host, port] = entry.split(":");
return { host: host?.trim(), port: Number(port?.trim() || 6379) };
})
.filter((node) => Boolean(node.host));
};
const redisClusterNodes = resolveHost(process.env.REDIS_NODES);
const isRedisConfigured = redisClusterNodes.length > 0;
let redisClient = null;
if (isRedisConfigured) {
redisClient = new Cluster(redisClusterNodes, {
redisOptions: {
username: process.env.REDIS_USERNAME || undefined,
password: process.env.REDIS_PASSWORD || undefined,
enableReadyCheck: false, // DragonflyDB compatibility
keyPrefix: redisKeyPrefix,
},
// ... retry strategy
});
} else {
console.log("[redis]: REDIS_NODES is not configured. Running without Redis (distributed locking disabled)");
}5. Bukti Verifikasi Pengujian (QA / Unit Test / API Verification)
A. Verifikasi Unit Test (Development Stage)
- Test Suite File:
tests/**(36 test suites: helpers, models, middlewares, dynamic routes, validations, connection wrapper) - Status Eksekusi CLI:
PASSED
RUN v4.1.11 D:/WORK/LNSW/single-billing-BE
Test Files 36 passed (36)
Tests 524 passed (524)
Start at 13:15:11
Duration 2.66s (transform 2.97s, setup 3.15s, import 4.71s, tests 1.08s, environment 11ms)B. Panduan Pengujian QA & Verifikasi Staging (QA Playbook)
Diisi saat development sebagai panduan eksekusi pengujian QA, kemudian dilengkapi bukti response JSON saat verifikasi di Staging.
- Cakupan / Surface Pengujian:
Worker / DB / Cron Only&CLI Runner - Kebutuhan Akses / Role Akun: Terminal akses repo backend
single-billing-BE. - Hasil Pengujian UAT:
PASSED - Catatan QA: Verifikasi unit test suite Vitest, eksekusi 36 suites, dan graceful cache fallback DragonflyDB berhasil lolos pengujian UAT.
Skenario Pengujian QA (Test Scenarios)
Skenario 1 — Eksekusi Seluruh Test Suite Vitest:
- Pre-kondisi: Dependensi
devDependenciestelah terpasang (npm install). - Trigger / Command: Jalankan perintah
npm testataunpx vitest run. - Ekspektasi Output: Seluruh 36 file test dinyatakan passed, 524 test cases sukses, summary coverage ter-generate di folder
coverage/. - Verifikasi Database / Log: Log runner terminal menampilkan status
36 passed (36)tanpa error assertion.
- Pre-kondisi: Dependensi
Skenario 2 — Verifikasi Bootstrap Server Tanpa Redis:
- Pre-kondisi: Kosongkan nilai variabel
REDIS_NODESpada.env. - Trigger / Command: Jalankan
npm run dev. - Ekspektasi Output: Server berhasil menyala normal pada port target tanpa crash/unhandled exception.
- Verifikasi Database / Log: Tampil log info graceful:
[redis]: REDIS_NODES is not configured. Running without Redis (distributed locking disabled).
- Pre-kondisi: Kosongkan nilai variabel
Skenario 3 — Verifikasi Output LCOV untuk SonarQube:
- Pre-kondisi: Test suite telah dijalankan dengan flag
--coverage. - Trigger / Command: Jalankan
npm run testlalu periksa filecoverage/lcov.info. - Ekspektasi Output: Berkas
coverage/lcov.infoterbentuk dan berisi data line coverage yang siap di-ingest oleh scanner SonarQube. - Verifikasi Database / Log: Ukuran berkas
coverage/lcov.infovalid dan memetakan baris kode komponenapp/.
- Pre-kondisi: Test suite telah dijalankan dengan flag
Bukti Response Verifikasi Staging (Diisi saat UAT)
{
"status": "PASSED",
"note": "Pengujian verifikasi UAT dan validasi integrasi berhasil lolos, rilis production v1.9.2 telah live."
}