// todo: use import assertions once they're supported by Node.js & ESLint // https://github.com/tc39/proposal-import-assertions import {createRequire} from 'module'; const require = createRequire(import.meta.url); import tap from 'tap'; import forEach from 'lodash/forEach.js'; import { checkIfResponseIsOk as checkIfResIsOk, request, } from '../../lib/request.js'; import { INVALID_REQUEST, NOT_FOUND, HafasError, HafasInvalidRequestError, HafasNotFoundError, } from '../../lib/errors.js'; import {formatTripReq} from '../../format/trip-req.js'; const resParameter = require('../fixtures/error-parameter.json'); const resNoMatch = require('../fixtures/error-no-match.json'); const resH9360 = require('../fixtures/error-h9360.json'); const resLocation = require('../fixtures/error-location.json'); const USER_AGENT = 'public-transport/hafas-client:test'; const secret = Symbol('secret'); tap.test('checkIfResponseIsOk properly throws HAFAS "H9360" errors', (t) => { try { checkIfResIsOk({ body: resH9360, errProps: {secret}, }); } catch (err) { t.ok(err); t.ok(err instanceof HafasError); t.equal(err.isHafasError, true); t.equal(err.message.slice(0, 7), 'H9360: '); t.ok(err.message.length > 7); t.ok(err instanceof HafasInvalidRequestError); t.equal(err.isCausedByServer, false); t.equal(err.code, INVALID_REQUEST); t.equal(err.hafasCode, 'H9360'); t.equal(err.hafasResponseId, resH9360.id); t.equal(err.hafasMessage, 'HAFAS Kernel: Date outside of the timetable period.'); t.equal(err.hafasDescription, 'Fehler bei der Datumseingabe oder Datum außerhalb der Fahrplanperiode (01.05.2022 - 10.12.2022)'); t.equal(err.secret, secret); t.end(); } }); tap.test('checkIfResponseIsOk properly throws HAFAS "LOCATION" errors', (t) => { try { checkIfResIsOk({ body: resLocation, errProps: {secret}, }); } catch (err) { t.ok(err); t.ok(err instanceof HafasError); t.equal(err.isHafasError, true); t.equal(err.message.slice(0, 10), 'LOCATION: '); t.ok(err.message.length > 10); t.ok(err instanceof HafasNotFoundError); t.equal(err.isCausedByServer, false); t.equal(err.code, NOT_FOUND); t.equal(err.hafasCode, 'LOCATION'); t.equal(err.hafasResponseId, resLocation.id); t.equal(err.hafasMessage, 'HCI Service: location missing or invalid'); t.equal(err.hafasDescription, 'Während der Suche ist ein interner Fehler aufgetreten'); t.equal(err.secret, secret); t.end(); } }); tap.test('checkIfResponseIsOk properly throws HAFAS "NO_MATCH" errors', (t) => { try { checkIfResIsOk({ body: resNoMatch, errProps: {secret}, }); } catch (err) { t.ok(err); t.ok(err instanceof HafasError); t.equal(err.isHafasError, true); t.equal(err.message.slice(0, 10), 'NO_MATCH: '); t.ok(err.message.length > 10); t.ok(err instanceof HafasNotFoundError); t.equal(err.isCausedByServer, false); t.equal(err.code, NOT_FOUND); t.equal(err.hafasCode, 'NO_MATCH'); t.equal(err.hafasResponseId, resNoMatch.id); t.equal(err.hafasMessage, 'Nothing found.'); t.equal(err.hafasDescription, 'Während der Suche ist leider ein interner Fehler aufgetreten. Bitte wenden Sie sich an unsere Serviceauskunft unter Tel. 0421 596059.'); t.equal(err.secret, secret); t.end(); } }); tap.test('checkIfResponseIsOk properly throws HAFAS "PARAMETER" errors', (t) => { try { checkIfResIsOk({ body: resParameter, errProps: {secret}, }); } catch (err) { t.ok(err); t.ok(err instanceof HafasError); t.equal(err.isHafasError, true); t.equal(err.message.slice(0, 11), 'PARAMETER: '); t.ok(err.message.length > 11); t.ok(err instanceof HafasInvalidRequestError); t.equal(err.isCausedByServer, false); t.equal(err.code, INVALID_REQUEST); t.equal(err.hafasCode, 'PARAMETER'); t.equal(err.hafasResponseId, resParameter.id); t.equal(err.hafasMessage, 'HCI Service: parameter invalid'); t.equal(err.hafasDescription, 'Während der Suche ist ein interner Fehler aufgetreten'); t.equal(err.secret, secret); t.end(); } }); tap.test('checkIfResponseIsOk properly parses an unknown HAFAS errors', (t) => { const body = { ver: '1.42', id: '1234567890', err: 'FOO', errTxt: 'random errTxt', errTxtOut: 'even more random errTxtOut', svcResL: [], }; try { checkIfResIsOk({ body, errProps: {secret}, }); } catch (err) { t.ok(err); t.ok(err instanceof HafasError); t.equal(err.isHafasError, true); t.equal(err.message, `${body.err}: ${body.errTxt}`); t.equal(err.isCausedByServer, false); t.equal(err.code, null); t.equal(err.hafasCode, body.err); t.equal(err.hafasResponseId, body.id); t.equal(err.hafasMessage, body.errTxt); t.equal(err.hafasDescription, body.errTxtOut); t.equal(err.secret, secret); t.end(); } }); const freeze = (val) => { if ( 'object' === typeof val && val !== null && !Array.isArray(val) ) { Object.freeze(val); } }; const ctx = { // random but unique opt: { language: 'ga', }, profile: { endpoint: 'https://does.not.exist', client: { type: 'FOO', id: 'BAR', name: 'baZ', }, auth: { type: 'AID', aid: 'some-auth-token', }, ver: '1.23.4', timezone: 'Europe/Amsterdam', locale: 'de-LU', defaultLanguage: 'fr', transformReq: (_, req) => req, }, }; forEach(ctx, freeze); tap.test('lib/request calls profile.transformReqBody & profile.transformReq properly', async (t) => { const customTransformReqBody = (ctx, reqBody) => { const p = 'transformReqBody call: '; t.same(ctx, customCtx, 'ctx should be the passed-in ctx'); t.ok(reqBody, 'reqBody'); t.equal(reqBody.client, ctx.profile.client, p + 'reqBody.client'); t.equal(reqBody.ext, ctx.profile.ext, p + 'reqBody.ext'); t.equal(reqBody.var, ctx.profile.var, p + 'reqBody.var'); t.equal(reqBody.auth, ctx.profile.auth, p + 'reqBody.auth'); t.equal(reqBody.lang, ctx.opt.language, p + 'reqBody.lang'); // We test if lib/request.js handles returning a new object. return { ...reqBody, }; }; const customTransformReq = (ctx, req) => { const p = 'transformReq call: '; t.same(ctx, customCtx, p + 'ctx should be the passed-in ctx'); t.equal(typeof req.body, 'string', p + 'req.body'); t.ok(req.body, p + 'req.body'); // We test if lib/request.js handles returning a new object. return { ...req, // From node-fetch, used by isomorphic-fetch: // > req/res timeout in ms, it resets on redirect. 0 to disable (OS limit applies). Signal is recommended instead. timeout: 100, }; }; const customCtx = { ...ctx, profile: { ...ctx.profile, transformReqBody: customTransformReqBody, transformReq: customTransformReq, }, }; const tripReq = formatTripReq(customCtx, 'unknown-trip-id'); // todo: set 1s timeout await t.rejects(async () => { await request(customCtx, USER_AGENT, tripReq); }); t.end(); });