51 lines
2.1 KiB
JavaScript
51 lines
2.1 KiB
JavaScript
import assert from "node:assert/strict";
|
|||
|
|
import { test } from "node:test";
|
||
|
|
import { fetchLemfiRate, parseLemfiQuote } from "../utils/lemfi.js";
|
||
|
|
|
||
|
|
// Captured from LemFi's Netherlands EUR → NGN calculator endpoint.
|
||
|
|
const quote = {
|
||
|
|
exchange_from: "EUR",
|
||
|
|
exchange_to: "NGN",
|
||
|
|
rate: "9.8057976951626334827662874e+25",
|
||
|
|
ID: "64eeb42d-7f05-4b50-a172-5e581358517a",
|
||
|
|
};
|
||
|
|
|
||
|
|
test("decodes the live quote to the rate displayed by LemFi", () => {
|
||
|
|
assert.equal(parseLemfiQuote({ data: quote }), 1522);
|
||
|
|
});
|
||
|
|
|
||
|
|
test("keeps promotional rewards out of the base rate comparison", () => {
|
||
|
|
assert.equal(parseLemfiQuote({ data: { ...quote, promotion_rate: { rate: "1e30" } } }), 1522);
|
||
|
|
});
|
||
|
|
|
||
|
|
test("rejects malformed rates and currency pairs", () => {
|
||
|
|
for (const change of [
|
||
|
|
{ exchange_from: "GBP" }, { exchange_to: "USD" },
|
||
|
|
{ rate: undefined }, { rate: "invalid" }, { rate: true },
|
||
|
|
{ rate: "Infinity" }, { rate: "0" }, { rate: "-1" },
|
||
|
|
{ ID: undefined }, { ID: "abc" }, { ID: "000" },
|
||
|
|
]) {
|
||
|
|
assert.throws(() => parseLemfiQuote({ data: { ...quote, ...change } }));
|
||
|
|
}
|
||
|
|
assert.throws(() => parseLemfiQuote({}));
|
||
|
|
});
|
||
|
|
|
||
|
|
test("requests the EUR to NGN Netherlands quote with a timeout", async (t) => {
|
||
|
|
t.mock.method(globalThis, "fetch", async (url, options) => {
|
||
|
|
assert.equal(url.origin + url.pathname, "https://lemfi.com/api/exchange-quote");
|
||
|
|
assert.deepEqual(Object.fromEntries(url.searchParams), {
|
||
|
|
from: "EUR", to: "NGN", country: "Netherlands", lang: "en",
|
||
|
|
});
|
||
|
|
assert.ok(options.signal instanceof AbortSignal);
|
||
|
|
return { ok: true, json: async () => ({ data: quote }) };
|
||
|
|
});
|
||
|
|
assert.equal(await fetchLemfiRate(), 1522);
|
||
|
|
});
|
||
|
|
|
||
|
|
test("rejects upstream HTTP and network errors", async (t) => {
|
||
|
|
const fetchMock = t.mock.method(globalThis, "fetch", async () => ({ ok: false, status: 503 }));
|
||
|
|
await assert.rejects(fetchLemfiRate(), /503/);
|
||
|
|
fetchMock.mock.mockImplementation(async () => { throw new Error("Network unavailable"); });
|
||
|
|
await assert.rejects(fetchLemfiRate(), /Network unavailable/);
|
||
|
|
});
|