← back to Costa Rica
test/plaid-lib-timeout.test.js
77 lines
'use strict';
// lib/plaid.js now routes every live call through fetchT (PRE-FLIGHT #4/#5 class) —
// a hung Plaid connection during host bank-linking can no longer leave the request
// pending forever. Proves the wiring: a header-fast/body-stalled response rejects
// with PROVIDER_TIMEOUT, and a Plaid HTTP error fails closed (throws), never
// fabricating a token. Forces LIVE via env + require-cache reset; faked fetch.
const { test } = require('node:test');
const assert = require('node:assert');
const PLAID = require.resolve('../lib/plaid');
const realFetch = global.fetch;
function loadLivePlaid() {
process.env.PLAID_CLIENT_ID = 'cid';
process.env.PLAID_SECRET = 'sec';
delete require.cache[PLAID];
return require(PLAID);
}
function unloadLivePlaid() {
delete process.env.PLAID_CLIENT_ID;
delete process.env.PLAID_SECRET;
delete require.cache[PLAID];
}
test('plaid createLinkToken(): a body-stalled Plaid response rejects PROVIDER_TIMEOUT (not an infinite hang)', async () => {
process.env.PROVIDER_HTTP_TIMEOUT_MS = '40';
const plaid = loadLivePlaid();
global.fetch = (url, opts) => Promise.resolve({
ok: true, status: 200,
json: () => new Promise((_resolve, reject) => {
opts.signal.addEventListener('abort', () => { const e = new Error('aborted'); e.name = 'AbortError'; reject(e); });
}),
});
try {
assert.equal(plaid.liveMode, true, 'live mode for this test');
await assert.rejects(
() => plaid.createLinkToken(42),
(err) => { assert.equal(err.code, 'PROVIDER_TIMEOUT'); return true; },
);
} finally {
global.fetch = realFetch;
unloadLivePlaid();
delete process.env.PROVIDER_HTTP_TIMEOUT_MS;
}
});
test('plaid createLinkToken(): a Plaid HTTP error fails closed (throws, no token fabricated)', async () => {
const plaid = loadLivePlaid();
global.fetch = () => Promise.resolve({ ok: false, status: 400, json: async () => ({ error_code: 'INVALID_API_KEYS' }) });
try {
await assert.rejects(
() => plaid.createLinkToken(42),
(err) => { assert.match(err.message, /plaid .* HTTP 400/); assert.match(err.message, /INVALID_API_KEYS/); return true; },
);
} finally {
global.fetch = realFetch;
unloadLivePlaid();
}
});
test('plaid createLinkToken(): sandbox (no creds) returns a deterministic fake token, no fetch', async () => {
delete require.cache[PLAID];
const plaid = require(PLAID); // no creds -> liveMode false
let fetched = false;
global.fetch = () => { fetched = true; return Promise.reject(new Error('should not fetch in sandbox')); };
try {
const t = await plaid.createLinkToken(42);
assert.equal(plaid.liveMode, false);
assert.match(t.link_token, /^link-sandbox-/);
assert.equal(fetched, false, 'sandbox never hits the network');
} finally {
global.fetch = realFetch;
delete require.cache[PLAID];
}
});