Files
edr-platform/e2e/freight/etrade-mock/server.js
Nathnael 2f86557f61 test(e2e): teach the eTrade mock to answer "no record"
Every TIN resolved to the same canned company, so the premise of both manual
routes — eTrade holds nothing — could not be reached at all, and neither could
the dead end an ordinary company hits on a TIN with no trade licence.

The answer now follows the TIN's leading digit, so a spec picks its outcome by
picking its number and no per-spec stubbing is needed: 9… returns a
registration with no businesses (the co-operative / investor case, where the
API's resolveCompanyData finds no licence), 8… 404s the registration lookup
entirely, anything else behaves as before.
2026-08-18 11:37:58 +00:00

163 lines
6.1 KiB
JavaScript

// Stand-in for https://etrade.gov.et in the e2e stack. ETradeService's base
// URL is hardcoded (apps/edr-freight-api/src/modules/companies/services/etrade.service.ts),
// not env-configurable, so this is reached by aliasing this container AS
// "etrade.gov.et" on the compose network (see docker-compose.e2e.yaml) rather
// than by pointing an env var here. HTTPS because the real service is HTTPS
// — the cert itself only needs to exist, never be trusted, since
// ETradeService's httpsAgent sets rejectUnauthorized:false. The compose
// service's command generates a throwaway self-signed cert into
// TLS_CERT_PATH/TLS_KEY_PATH before starting this — nothing shaped like a
// key/cert is committed here.
//
// A TIN resolves to the same canned company whatever its digits, EXCEPT for
// the two failure shapes the onboarding specs need — chosen by the TIN's
// leading digit so a spec picks its outcome by picking its number, with no
// per-spec stubbing and no state in this process:
//
// 1xxxxxxxxx (default) registration + one business licence → "Verified with eTrade"
// 9xxxxxxxxx registration, but `Businesses: []` → the API's resolveCompanyData
// returns businessInfo: null,
// so /fetch-etrade-info 400s
// with "couldn't find a
// business license for this
// TIN". This is the real
// co-operative / foreign-
// investor case: the TIN is
// registered, the trade
// licence is not.
// 8xxxxxxxxx 404 on the registration lookup → getCompanyInfoByTin throws,
// same 400 to the portal by a
// different route (eTrade knows
// nothing about this TIN at all).
//
// Both failures reach the portal as a 400, which ETradeInfo renders as its
// `notFound` branch: a red dead end for an ordinary company, and the blue
// "that's expected" alert for one that types its registration.
const https = require("node:https");
const fs = require("node:fs");
const PORT = process.env.PORT || 443;
const options = {
key: fs.readFileSync(process.env.TLS_KEY_PATH || "/tmp/etrade-mock/key.pem"),
cert: fs.readFileSync(
process.env.TLS_CERT_PATH || "/tmp/etrade-mock/cert.pem",
),
};
/** No trade licence on file for this TIN (a co-operative, a foreign investor). */
const TIN_WITHOUT_LICENCE = "9";
/** eTrade holds no registration whatsoever for this TIN. */
const TIN_UNKNOWN = "8";
function companyInfo(tin) {
return {
Tin: tin,
LegalCondtion: "Private Limited Company",
RegNo: "REG-E2E-0001",
RegDate: "2020-01-01",
// Embeds the (per-run-unique) TIN rather than a static name — specs
// that look a company up by name (e.g. onboarding.cy.ts) need this to
// stay unique across repeated e2e runs against the same warm DB, same
// as it would be if the customer had typed a real company name.
BusinessName: `E2E Mock Trading PLC ${tin}`,
BusinessNameAmh: "ኢቱኢ ሞክ ትሬዲንግ",
PaidUpCapital: 100000,
AssociateShortInfos: [],
Businesses: [
{
MainGuid: "e2e-guid-0001",
OwnerTIN: tin,
DateRegistered: "2020-01-01",
TradeNameAmh: "ኢቱኢ",
TradesName: "E2E Mock Trading",
LicenceNumber: "LIC-E2E-0001",
RenewalDate: "2026-01-01",
RenewedFrom: "2025-01-01",
RenewedTo: "2027-01-01",
BusinessLicensingGroupMain: null,
SubGroups: null,
},
],
};
}
function businessInfo(tin) {
return {
MainGuid: "e2e-guid-0001",
OwnerTIN: tin,
DateRegistered: "2020-01-01",
TradeName: "E2E Mock Trading",
LicenceNumber: "LIC-E2E-0001",
Status: 1,
StatusDescription: "Active",
Capital: 100000,
AssociateShortInfos: [
{
Position: "Manager",
ManagerName: "አበበ በቀለ",
ManagerNameEng: "Abebe Bekele",
Photo: null,
MobilePhone: "+251911223344",
RegularPhone: null,
},
],
AddressInfo: {
Region: "ADDIS ABABA",
Zone: "Zone 1",
Woreda: "Woreda 1",
Kebele: "Kebele 1",
HouseNo: "123",
MobilePhone: "+251911223344",
RegularPhone: "",
},
RenewedTo: "2027-01-01",
RenewedToDateString: "2027-01-01",
RenewalDate: "2026-01-01",
RenewedFrom: "2025-01-01",
CancellationDate: null,
};
}
const server = https.createServer(options, (req, res) => {
const url = new URL(req.url, `https://${req.headers.host}`);
const regMatch = url.pathname.match(
/^\/api\/Registration\/GetRegistrationInfoByTin\/([^/]+)\/en$/,
);
if (req.method === "GET" && regMatch) {
const tin = regMatch[1];
if (tin.startsWith(TIN_UNKNOWN)) {
res.writeHead(404).end();
return;
}
const info = companyInfo(tin);
// Registered, but holding no trade licence. The API reads `Businesses`
// rather than the HTTP status to decide this, so an empty array is the
// honest shape — not an error.
if (tin.startsWith(TIN_WITHOUT_LICENCE)) info.Businesses = [];
res.writeHead(200, { "content-type": "application/json" });
res.end(JSON.stringify(info));
return;
}
if (
req.method === "GET" &&
url.pathname === "/api/BusinessMain/GetBusinessByLicenseNo"
) {
const tin = url.searchParams.get("Tin") || "";
res.writeHead(200, { "content-type": "application/json" });
res.end(JSON.stringify(businessInfo(tin)));
return;
}
res.writeHead(404).end();
});
server.listen(PORT, () => {
console.log(`etrade-mock listening on ${PORT}`);
});