const args = Object.fromEntries( process.argv.slice(2) .map((arg) => arg.replace(/^--/, "").split("=")) .map(([k, v]) => [k, v ?? "true"]) ); const BASE_URL = args.baseUrl || process.env.BASE_URL || "http://localhost:5000/api"; const PASSWORD = "Admin@123"; const STEP_DELAY_MS = Number(args.delayMs || 500); const EMAILS = { DD_ADMIN: args.ddAdminEmail || "lince@gmail.com", DEALER: args.dealerEmail || "dealer@royalenfield.com", ASM: args.asmEmail || "asm.sdelhi@royalenfield.com", RBM: args.rbmEmail || "rbm.ncr@royalenfield.com", DD_ZM: args.ddZmEmail || "zm.ncr@royalenfield.com", ZBH: args.zbhEmail || "yashwin@gmail.com", DD_LEAD: args.ddLeadEmail || "ddlead@royalenfield.com", DD_HEAD: args.ddHeadEmail || "ddhead@royalenfield.com", NBH: args.nbhEmail || "nbh@royalenfield.com", LEGAL: args.legalEmail || "legal@royalenfield.com", }; const ROLE_BY_STAGE = { "ASM Review": ["ASM"], "RBM Review": ["RBM"], "DD ZM Review": ["DD_ZM"], "ZBH Review": ["ZBH"], "DD Lead Review": ["DD_LEAD"], "DD Head Approval": ["DD_HEAD"], "NBH Approval": ["NBH"], "Legal Clearance": ["LEGAL"], "NBH Clearance with EOR": ["NBH"], }; const delay = (ms = STEP_DELAY_MS) => new Promise((r) => setTimeout(r, ms)); async function apiRequest(endpoint, method = "GET", body = null, token = null) { const headers = { "Content-Type": "application/json" }; if (token) headers.Authorization = `Bearer ${token}`; const config = { method, headers }; if (body) config.body = JSON.stringify(body); const response = await fetch(`${BASE_URL}${endpoint}`, config); const data = await response.json(); if (!response.ok) { throw new Error(`API Error ${method} ${endpoint}: ${JSON.stringify(data)}`); } return data; } async function login(email) { if (!login.cache) login.cache = {}; if (login.cache[email]) return login.cache[email]; const data = await apiRequest("/auth/login", "POST", { email, password: PASSWORD }); login.cache[email] = data.token; return data.token; } async function getRelocationByAnyId(id, token) { return apiRequest(`/self-service/relocation/${id}`, "GET", null, token); } async function approveCurrentStage(requestId, stageName) { const candidateRoles = ROLE_BY_STAGE[stageName] || []; if (!candidateRoles.length) { throw new Error(`No actor mapping found for stage: ${stageName}`); } let lastError = null; const attempts = []; for (const roleKey of candidateRoles) { const email = EMAILS[roleKey]; if (!email) { attempts.push(`${roleKey}:missing-email`); continue; } try { const token = await login(email); const res = await apiRequest(`/self-service/relocation/${requestId}/action`, "POST", { action: "APPROVE", comments: `${roleKey} approved relocation request.`, }, token); return { roleKey, email, message: res.message || "Approved" }; } catch (error) { lastError = error; attempts.push(`${roleKey}:${error.message}`); } } throw new Error( `Approval failed for stage: ${stageName}. Attempts -> ${attempts.join(" | ")}` + (lastError ? ` | Last error: ${lastError.message}` : "") ); } async function resolveDealerOutlet(dealerToken) { const dashboard = await apiRequest("/dealer/dashboard", "GET", null, dealerToken); const outlet = dashboard?.data?.outlets?.[0]; if (!outlet) throw new Error("No dealer outlet found to create relocation request."); return outlet; } async function run() { try { console.log("--- STARTING RELOCATION E2E FLOW ---"); const adminToken = await login(EMAILS.DD_ADMIN); const dealerToken = await login(EMAILS.DEALER); let requestId = args.requestId; if (!requestId) { const outlet = await resolveDealerOutlet(dealerToken); console.log(`[STEP 1] Submitting relocation for outlet: ${outlet.name} (${outlet.id})`); const createRes = await apiRequest("/self-service/relocation", "POST", { outletId: args.outletId || outlet.id, relocationType: args.relocationType || "Intercity", newAddress: args.newAddress || "Sector 21, New Premises", newCity: args.newCity || "Gurugram", newState: args.newState || "Haryana", reason: args.reason || "Business expansion and better customer access", proposedDate: args.proposedDate || new Date().toISOString().split("T")[0], }, dealerToken); requestId = createRes.requestId; console.log(`[STEP 1] Request Created: ${requestId}`); await delay(); } else { console.log(`[STEP 1] Resuming request: ${requestId}`); } let step = 2; while (true) { const detailsRes = await getRelocationByAnyId(requestId, adminToken); const request = detailsRes.request; const stage = request.currentStage; const status = request.status; if (stage === "Completed" || status === "Completed") { console.log(`[STEP ${step}] SUCCESS: Relocation request is completed.`); break; } if (stage === "Rejected" || status === "Rejected") { throw new Error(`Relocation request is rejected at stage: ${stage}`); } console.log(`[STEP ${step}] Current Stage: ${stage} | Status: ${status}`); const actor = await approveCurrentStage(requestId, stage); console.log(`[STEP ${step}] ${actor.roleKey} (${actor.email}) -> ${actor.message}`); step++; await delay(); } const finalRes = await getRelocationByAnyId(requestId, adminToken); console.log("--- VERIFICATION RESULTS ---"); console.log(`RequestId: ${finalRes.request.requestId || requestId}`); console.log(`Final Stage: ${finalRes.request.currentStage}`); console.log(`Final Status: ${finalRes.request.status}`); console.log("Outcome: RELOCATION FLOW COMPLETED SUCCESSFULLY"); process.exit(0); } catch (error) { console.error("Workflow failed:", error.message); process.exit(1); } } run();