const fs = require('fs'); const path = require('path'); const BASE_URL = 'http://localhost:3000/api/v1'; async function runTests() { console.log('=== STARTING BACKEND API TDD TESTS (PORT 3000) ==='); let failures = 0; // Helper for reporting test cases const assert = (condition, message) => { if (condition) { console.log(`[PASS] ${message}`); } else { console.error(`[FAIL] ${message}`); failures++; } }; let token = ''; let uploadedDocId = ''; const testFilename = 'test-mobile-upload.jpg'; // Create a mock image file for uploading const mockImagePath = path.join(__dirname, 'mock_upload.jpg'); fs.writeFileSync(mockImagePath, 'fake-jpeg-content-data'); try { // ------------------------------------------------------------- // Test 1: POST /auth/login // ------------------------------------------------------------- console.log('\n--- Test 1: Authentication Login ---'); const loginRes = await fetch(`${BASE_URL}/auth/login`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ username: 'admin', password: 'password' }) }); assert(loginRes.status === 200, `POST /auth/login status should be 200 (got ${loginRes.status})`); if (loginRes.ok) { const loginData = await loginRes.json(); assert(loginData.status === 'success', 'Login response status field should be "success"'); assert(loginData.data && loginData.data.token, 'Login response should contain auth token'); token = loginData.data?.token || ''; } else { console.log('Skipping login payload asserts due to failed request'); } // ------------------------------------------------------------- // Test 2: POST /documents/upload // ------------------------------------------------------------- console.log('\n--- Test 2: Document Upload ---'); const form = new FormData(); const mockFile = new File(['fake-jpeg-content-data'], testFilename, { type: 'image/jpeg' }); form.append('image', mockFile); form.append('latitude', '-6.2134'); form.append('longitude', '106.8451'); const uploadRes = await fetch(`${BASE_URL}/documents/upload`, { method: 'POST', headers: token ? { 'Authorization': `Bearer ${token}` } : {}, body: form }); assert(uploadRes.status === 201, `POST /documents/upload status should be 201 (got ${uploadRes.status})`); if (uploadRes.ok) { const uploadData = await uploadRes.json(); assert(uploadData.status === 'success', 'Upload status should be "success"'); assert(uploadData.data && uploadData.data.id, 'Upload response should contain document id'); assert(uploadData.data?.latitude === -6.2134, 'Latitude should be returned correctly'); assert(uploadData.data?.longitude === 106.8451, 'Longitude should be returned correctly'); assert(uploadData.data?.header?.no_do === '', 'Header DO should be empty string initially'); assert(Array.isArray(uploadData.data?.items) && uploadData.data.items.length === 0, 'Items should be empty initially'); uploadedDocId = uploadData.data?.id || ''; } else { console.log('Skipping upload payload asserts due to failed request'); const errText = await uploadRes.text(); console.log('Upload error response body:', errText); } // ------------------------------------------------------------- // ------------------------------------------------------------- // Test 2b: Duplicate Document Upload Creates New Document (Deduplication Disabled) // ------------------------------------------------------------- console.log('\n--- Test 2b: Duplicate Document Upload Creates New Document ---'); const dupForm = new FormData(); const mockFileDup = new File(['fake-jpeg-content-data'], testFilename, { type: 'image/jpeg' }); dupForm.append('image', mockFileDup); dupForm.append('latitude', '-6.9999'); dupForm.append('longitude', '107.9999'); const dupUploadRes = await fetch(`${BASE_URL}/documents/upload`, { method: 'POST', headers: token ? { 'Authorization': `Bearer ${token}` } : {}, body: dupForm }); assert(dupUploadRes.status === 201, `POST /documents/upload (duplicate) status should be 201 (got ${dupUploadRes.status})`); if (dupUploadRes.ok) { const dupUploadData = await dupUploadRes.json(); assert(dupUploadData.status === 'success', 'Duplicate upload status should be "success"'); assert(dupUploadData.data?.id !== uploadedDocId, 'Duplicate upload should return a new document ID (deduplication disabled)'); assert(dupUploadData.data?.latitude === -6.9999, 'New document should return its own latitude (-6.9999)'); assert(dupUploadData.data?.longitude === 107.9999, 'New document should return its own longitude (107.9999)'); } else { console.log('Skipping duplicate upload asserts due to failed request'); } // ------------------------------------------------------------- // Test 3: GET /documents (List with polling wait) // ------------------------------------------------------------- console.log('\n--- Test 3: Documents List (Waiting for background parse) ---'); let found = false; let retries = 0; const maxRetries = 15; let listData; while (!found && retries < maxRetries) { if (retries > 0) { console.log(`Waiting 2s for background parse... (Attempt ${retries}/${maxRetries})`); await new Promise(resolve => setTimeout(resolve, 2000)); } const listRes = await fetch(`${BASE_URL}/documents`, { method: 'GET', headers: token ? { 'Authorization': `Bearer ${token}` } : {} }); assert(listRes.status === 200, `GET /documents status should be 200 (got ${listRes.status})`); if (listRes.ok) { listData = await listRes.json(); assert(listData.status === 'success', 'List status should be "success"'); assert(Array.isArray(listData.data), 'List data should be an array'); found = listData.data?.some(doc => doc.id.toString() === uploadedDocId.toString()); } retries++; } assert(found, `List should contain the newly uploaded document (id: ${uploadedDocId}) after background parsing`); if (found && listData) { const matchedDoc = listData.data?.find(doc => doc.id.toString() === uploadedDocId.toString()); if (matchedDoc) { assert(matchedDoc.latitude === -6.2134, `Database latitude should remain -6.2134 (got ${matchedDoc.latitude})`); assert(matchedDoc.longitude === 106.8451, `Database longitude should remain 106.8451 (got ${matchedDoc.longitude})`); } } // ------------------------------------------------------------- // Test 4: PUT /documents/[id] (Confirm / Update) // ------------------------------------------------------------- console.log('\n--- Test 4: Document Confirm/Update ---'); if (uploadedDocId) { const updatePayload = { tanggal: '2026-06-30', noPo: 'PO-TEST-123', noSo: 'SO-TEST-456', noDo: 'DO-TEST-789', kepadaYth: 'PT. PRIMAFOOD INTERNATIONAL', orderUntuk: 'PRIMA FRESH MART', alamat: 'Jl. Ancol Barat VIII/1', platTruk: 'B 9999 XYZ', namaDriver: 'Budi', namaPenerima: 'Andi', latitude: -6.2134, longitude: 106.8451, items: [ { nomor_sku: '11048006', nama_barang: 'BEBEK PARTING-NEW(*)', banyak: '10 KRG', jumlah: '10' } ] }; const updateRes = await fetch(`${BASE_URL}/documents/${uploadedDocId}`, { method: 'PUT', headers: { 'Content-Type': 'application/json', ...(token ? { 'Authorization': `Bearer ${token}` } : {}) }, body: JSON.stringify(updatePayload) }); assert(updateRes.status === 200, `PUT /documents/${uploadedDocId} status should be 200 (got ${updateRes.status})`); if (updateRes.ok) { const updateData = await updateRes.json(); assert(updateData.status === 'success', 'Update status should be "success"'); assert(updateData.data?.header?.no_do === 'DO-TEST-789', 'Header DO should be updated'); assert(updateData.data?.shipment?.nama_penerima === 'Andi', 'Shipment recipient name should be updated'); assert(updateData.data?.items?.length === 1, 'Items array length should be 1'); assert(updateData.data?.items?.[0]?.nomor_sku === '11048006', 'Item SKU should be correctly updated'); } else { console.log('Skipping update payload asserts due to failed request'); const errText = await updateRes.text(); console.log('Update error response body:', errText); } } else { console.log('Skipping Test 4: No uploadedDocId available'); } } catch (err) { console.error('Unhandled exception during tests:', err); failures++; } finally { // Clean up mock image if (fs.existsSync(mockImagePath)) { fs.unlinkSync(mockImagePath); } } console.log('\n=== TDD TEST RUN COMPLETED ==='); if (failures === 0) { console.log('ALL TESTS PASSED SUCCESSFULLY! ✅'); process.exit(0); } else { console.error(`${failures} TEST(S) FAILED! ❌`); process.exit(1); } } runTests();