This commit is contained in:
Traines 2024-12-07 23:48:08 +00:00
parent 80e633dcb7
commit 0e328aa681
7 changed files with 113 additions and 343 deletions

View file

@ -33,7 +33,7 @@ jobs:
- run: npm run lint
- run: npm run test-unit
"""
integration-tests:
runs-on: ubuntu-latest
strategy:
@ -84,3 +84,4 @@ jobs:
- run: npm install
- run: npm run test-e2e
"""

View file

@ -155,7 +155,7 @@ const request = async (ctx, userAgent, reqData) => {
}
const body = await res.text();
//console.log(body);
console.log(body);
profile.logResponse(ctx, res, body, reqId);
const b = JSON.parse(body);

View file

@ -187,17 +187,9 @@ Pass in just opt.age, and the age group will calculated automatically.`);
typ: ageGroupLabel[tvlrAgeGroup || ageGroup.ADULT],
anzahl: 1,
alter: 'age' in opt
? [opt.age]
? [opt.age+'']
: [],
/*ermaessigungen: opt.loyaltyCard TODO
? formatLoyaltyCard(opt.loyaltyCard)
: null,*/
ermaessigungen: [
{
"art": "KEINE_ERMAESSIGUNG",
"klasse": "KLASSENLOS"
}
]
ermaessigungen: [formatLoyaltyCard(opt.loyaltyCard)]
}]
};
return basicCtrfReq;

View file

@ -13,71 +13,44 @@ const c = {
// see https://gist.github.com/juliuste/202bb04f450a79f8fa12a2ec3abcd72d
const formatLoyaltyCard = (data) => {
if (data.type === c.BAHNCARD) {
if (data.discount === 25) {
return data.class === 1
? 1
: 2;
if (!data) {
return {
"art": "KEINE_ERMAESSIGUNG",
"klasse": "KLASSENLOS"
}
if (data.discount === 50) {
return data.class === 1
? 3
: 4;
}
const cls = data.class === 1 ? 'KLASSE_1' : 'KLASSE_2';
if (data.type === c.BAHNCARD) {
return {
art: 'BAHNCARD'+data.discount,
klasse: cls
}
}
if (data.type === c.VORTEILSCARD) {
return 9;
return {
art: 'A-VORTEILSCARD',
klasse: 'KLASSENLOS'
}
}
if (data.type === c.HALBTAXABO) {
return data.railplus
? 10
: 11;
}
if (data.type === c.VOORDEELURENABO) {
return data.railplus
? 12
: 13;
}
if (data.type === c.SHCARD) {
return 14;
return {
art: 'CH-HALBTAXABO_OHNE_RAILPLUS',
klasse: 'KLASSENLOS'
}
}
// TODO Rest
if (data.type === c.GENERALABONNEMENT) {
return 15;
return {
art: 'CH-GENERAL-ABONNEMENT',
klasse: cls
}
}
return 0;
};
const parseLoyaltyCard = (cardId) => {
switch (cardId) {
case 1:
case 2: return {type: c.BAHNCARD, discount: 25, class: cardId === 1 ? 1 : 2};
case 3:
case 4: return {type: c.BAHNCARD, discount: 50, class: cardId === 3 ? 1 : 2};
case 9: return {type: c.VORTEILSCARD};
case 10:
case 11: return {type: c.HALBTAXABO, railplus: cardId === 10};
case 12:
case 13: return {type: c.VOORDEELURENABO, railplus: cardId === 12};
case 14: return {type: c.SHCARD};
case 15: return {type: c.GENERALABONNEMENT};
default: return {type: c.NONE};
return {
"art": "KEINE_ERMAESSIGUNG",
"klasse": "KLASSENLOS"
}
};
const bcFirst50 = {
type: c.BAHNCARD,
class: 1,
discount: 50,
};
eql(parseLoyaltyCard(formatLoyaltyCard(bcFirst50)), bcFirst50);
const halbtaxRailplus = {
type: c.HALBTAXABO,
railplus: true,
};
eql(parseLoyaltyCard(formatLoyaltyCard(halbtaxRailplus)), halbtaxRailplus);
export {
c as data,
formatLoyaltyCard,
parseLoyaltyCard,
formatLoyaltyCard
};

View file

@ -34,52 +34,85 @@ const opt = {
},
};
const berlinWienQuery0 = Object.freeze({
getPasslist: false,
maxChg: -1,
minChgTime: 0,
depLocL: [{
type: 'S',
lid: 'A=1@L=8098160@',
}],
viaLocL: [],
arrLocL: [{
type: 'S',
lid: 'A=1@L=8000284@',
}],
jnyFltrL: [
{type: 'PROD', mode: 'INC', value: '1023'},
{type: 'META', mode: 'INC', meta: 'notBarrierfree'},
],
gisFltrL: [],
getTariff: false,
ushrp: true,
getPT: true,
getIV: false,
getPolyline: false,
outDate: '20230912',
outTime: '080910',
outFrwd: true,
const berlinWienQuery0 = Object.freeze(
{
"abfahrtsHalt": "A=1@L=8098160@",
"anfrageZeitpunkt": "2024-12-07T23:50:12",
"ankunftsHalt": "A=1@L=8000284@",
"ankunftSuche": "ABFAHRT",
"klasse": "KLASSE_2",
"produktgattungen": [
"ICE",
"EC_IC",
"IR",
"REGIONAL",
"SBAHN",
"BUS",
"SCHIFF",
"UBAHN",
"TRAM",
"ANRUFPFLICHTIG"
],
"schnelleVerbindungen": true,
"sitzplatzOnly": false,
"bikeCarriage": false,
"reservierungsKontingenteVorhanden": false,
"nurDeutschlandTicketVerbindungen": false,
"deutschlandTicketVorhanden": false
});
tap.test('formats a journeys() request correctly (DB)', (t) => {
const _opt = {...opt};
delete _opt.loyaltyCard;
delete _opt.age;
const ctx = {profile, opt: _opt};
// transformJourneysQuery() mutates its 2nd argument!
const query = {...berlinWienQuery0};
const req = profile.transformJourneysQuery(ctx, query);
t.same(req.body, {
...berlinWienQuery0,
reisende: [
{
"typ": "ERWACHSENER",
"ermaessigungen": [
{
"art": "KEINE_ERMAESSIGUNG",
"klasse": "KLASSENLOS"
}
],
"alter": [],
"anzahl": 1
}
]
});
t.end();
});
tap.test('formats a journeys() request with BC correctly (DB)', (t) => {
const ctx = {profile, opt};
// transformJourneysQuery() mutates its 2nd argument!
const query = {...berlinWienQuery0};
const req = profile.transformJourneysQuery(ctx, query);
t.same(req, {
t.same(req.body, {
...berlinWienQuery0,
trfReq: {
jnyCl: 2,
tvlrProf: [{
type: 'Y', // "young"
age: 24,
redtnCard: 2, // BahnCard 25
}],
cType: 'PK',
},
reisende: [
{
"typ": "JUGENDLICHER",
"ermaessigungen": [
{
"art": "BAHNCARD25",
"klasse": "KLASSE_2"
}
],
"alter": ["24"],
"anzahl": 1
}
]
});
t.end();
});
});

View file

@ -5,16 +5,19 @@ const products = [
{
id: 'train',
bitmasks: [1, 2],
vendo: 'REGIONAL',
default: true,
},
{
id: 'bus',
bitmasks: [4],
vendo: 'BUS',
default: true,
},
{
id: 'tram',
bitmasks: [8, 32],
vendo: 'TRAM',
default: false,
},
];
@ -26,25 +29,8 @@ const ctx = {
};
tap.test('formatProductsFilter works without customisations', (t) => {
const expected = 1 | 2 | 4;
const expected = ['REGIONAL', 'BUS'];
const filter = {};
t.same(format(ctx, filter), {
type: 'PROD',
mode: 'INC',
value: String(expected),
});
t.same(format(ctx, filter), expected);
t.end();
});
tap.test('formatProductsFilter works with customisations', (t) => {
t.equal(Number(format(ctx, {
bus: true,
}).value), 1 | 2 | 4);
t.equal(Number(format(ctx, {
bus: false,
}).value), 1 | 2);
t.equal(Number(format(ctx, {
tram: true,
}).value), 1 | 2 | 4 | 8 | 32);
t.end();
});
});

View file

@ -18,72 +18,13 @@ import {
} 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 resNoMatch = {"verbindungen":[],"verbindungReference":{},"fehlerNachricht":{"code":"MDA-AK-MSG-1001","ueberschrift":"Datum liegt außerhalb der Fahrplanperiode.","text":"Das Datum liegt außerhalb der Fahrplanperiode."}};
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) => {
tap.test('checkIfResponseIsOk properly throws HAFAS errors', (t) => {
try {
checkIfResIsOk({
body: resNoMatch,
@ -94,169 +35,13 @@ tap.test('checkIfResponseIsOk properly throws HAFAS "NO_MATCH" errors', (t) => {
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.code, 'MDA-AK-MSG-1001');
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.equal(err.hafasMessage, 'Datum liegt außerhalb der Fahrplanperiode.');
t.equal(err.hafasDescription, 'Das Datum liegt außerhalb der Fahrplanperiode.');
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();
});