Modernization Impact Report
Dihasilkan oleh Ripple Tracer (IBM Bob 2.0 — LegacyLens PoC) Tanggal analisis: sesuai timestamp run
Ringkasan Eksekutif
- Jumlah file dianalisis: 5 (
utils/pricing.js,checkout.js,invoice.js,report.js,app.js) - Jumlah file diubah (sesi ini): 1 (
utils/pricing.js— signaturecalculateDiscount()diubah dari callback ke async) - Jumlah file terdampak (ripple): 3 (
checkout.js,invoice.js,report.js) - Status akhir: ✅ Aman — bug
invoice.jsdiperbaiki, 17/17 test lolos
1. Perubahan yang Dilakukan
| File | Perubahan | Alasan |
|---|---|---|
utils/pricing.js | Signature calculateDiscount(price, rate, callback) → async calculateDiscount(price, rate) | Modernisasi dari callback ke async/await; error kini di-throw, bukan di-forward ke callback |
checkout.js | processCheckout(order, callback) → async processCheckout(order); konsumsi via await calculateDiscount(...) | Mengikuti signature baru pricing.js |
report.js | Rekursi callback processNext() → Promise.all(transactions.map(tx => calculateDiscount(...))) | Lebih ringkas + paralel |
app.js | Semua route handler function(req,res) → async (req,res) + try/catch | Menyesuaikan modul yang kini semua berbasis Promise |
package.json | moment 2.24.0 → 2.31.0 | Patch keamanan; tidak ada breaking change di API yang dipakai |
2. Impact Map
| File Terdampak | Terhubung via | Status Migrasi | Level Risiko |
|---|---|---|---|
checkout.js | require('./utils/pricing') → memanggil calculateDiscount() langsung | ✅ Benar — menggunakan await | Tinggi |
invoice.js | require('./utils/pricing') → memanggil calculateDiscount() langsung | ❌ BUG — await hilang di baris 41 | Tinggi |
report.js | require('./utils/pricing') → memanggil calculateDiscount() via Promise.all | ✅ Benar — await Promise.all(...) | Tinggi |
app.js | require('./invoice') & require('./checkout') & require('./report') | ✅ Benar — route handler sudah async/await | Sedang |
3. Hasil Validasi Test
| Test | File yang Ditest | Status | Catatan |
|---|---|---|---|
menghitung grand total dengan diskon 10% | checkout.js | ✅ Lolos | grandTotal = 180000 |
menghitung grand total tanpa diskon (rate 0) | checkout.js | ✅ Lolos | grandTotal = 50000 |
menghitung grand total dengan diskon 100% | checkout.js | ✅ Lolos | grandTotal = 0 |
melempar error jika harga negatif | checkout.js | ✅ Lolos | throws 'Harga tidak valid' |
melempar error jika rate melebihi 100 | checkout.js | ✅ Lolos | throws 'Rate diskon tidak valid' |
createInvoice — totalDue dengan diskon 15% | invoice.js | ✅ Lolos | totalDue = 425000 (setelah fix await) |
createInvoice — tanpa diskon (0%) | invoice.js | ✅ Lolos | totalDue = amount asli |
createInvoice — diskon 100% | invoice.js | ✅ Lolos | totalDue = 0 |
createInvoice — properti wajib tidak undefined | invoice.js | ✅ Lolos | invoiceId, issuedAt, totalDue terisi |
createInvoice — error amount negatif | invoice.js | ✅ Lolos | throws 'Harga tidak valid' |
createInvoice — error rate > 100 | invoice.js | ✅ Lolos | throws 'Rate diskon tidak valid' |
generateDiscountReport — akumulasi multiple transaksi | report.js | ✅ Lolos | totalOriginal/Discounted/Saved benar |
generateDiscountReport — transaksi tunggal tanpa diskon | report.js | ✅ Lolos | totalDiscounted = amount asli |
generateDiscountReport — diskon 100% | report.js | ✅ Lolos | totalDiscounted = 0 |
generateDiscountReport — array kosong | report.js | ✅ Lolos | semua nilai = 0 |
generateDiscountReport — null | report.js | ✅ Lolos | semua nilai = 0 |
generateDiscountReport — error amount negatif | report.js | ✅ Lolos | throws 'Harga tidak valid' |
Blind spot ditutup: Sebelum sesi ini,
invoice.jsdanreport.jssama sekali tidak memiliki unit test. Bug diinvoice.jstidak akan pernah tertangkap CI. Setelah fixawaitdan penambahan test suite, seluruh 17 test (3 suite) lolos 100% — blind spot tertutup.
4. Root Cause Trace
Rantai sebab-akibat:
utils/pricing.js (calculateDiscount)
↓ signature diubah: callback → async/await (return Promise)
↓ invoice.js baris 41: const result = calculateDiscount(...)
← HILANG 'await' — result = Promise object, bukan { original, discounted, ... }
↓ invoice.lineItems[0].amount = result.original → undefined
↓ invoice.lineItems[1].amount = -result.savedAmount → NaN
↓ invoice.totalDue = result.discounted → undefined
↓ invoice.issuedAt = result.calculatedAt → undefined
↓ app.js /invoice endpoint mengembalikan invoice dengan semua nilai rupiah undefined
← Tidak ada exception dilempar — bug SILENT
Penjelasan naratif:
calculateDiscount() di utils/pricing.js diubah dari callback pattern ke async function. Perubahan ini membuatnya mengembalikan Promise<result> — artinya setiap pemanggil wajib menggunakan await untuk mendapatkan nilai aktualnya.
checkout.js dan report.js berhasil dimigrasi dengan benar. Namun invoice.js baris 41 melewatkan await:
// SEBELUM FIX (buggy)
const result = calculateDiscount(transaction.amount, transaction.discountRate);
// ^^^^^^^^^^^^^^^^^ Promise dikembalikan tapi tidak di-await
// result = Promise { <pending> }, bukan { original, discounted, ... }
// SETELAH FIX (benar)
const result = await calculateDiscount(transaction.amount, transaction.discountRate);
Karena result berisi objek Promise (bukan data), akses ke result.original, result.discounted, result.savedAmount, dan result.calculatedAt semuanya menghasilkan undefined. JavaScript tidak melempar exception untuk akses properti yang tidak ada — ini yang membuat bug ini silent dan berbahaya.
5. Rekomendasi Lanjutan
- [WAJIB — sudah dikerjakan] Tambahkan
awaitdiinvoice.jsbaris 41. - [WAJIB — sudah dikerjakan] Tambahkan unit test untuk
createInvoice()ditests/invoice.test.js. - [WAJIB — sudah dikerjakan] Tambahkan unit test untuk
generateDiscountReport()ditests/report.test.js. - [DISARANKAN] Aktifkan TypeScript strict mode atau gunakan JSDoc
@returns {Promise<DiscountResult>}+ ESLint@typescript-eslint/no-floating-promisesuntuk menangkap missing-await secara statis di CI. - [DISARANKAN] Verifikasi endpoint
/invoiceviacurlatau Postman setelah fix — pastikantotalDueberisi angka valid, bukanundefined. - [DISARANKAN] Pertimbangkan integration test untuk
app.jsendpoints menggunakansupertestagar perubahan di lapisan routing juga tercakup.
Dokumen ini di-generate oleh Ripple Tracer — IBM Bob 2.0 | LegacyLens PoC