feat(web+android+backend): Konzerte gefolgter Kuenstler als zweiter Reiter
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
1366ec3832
commit
17a677a5a5
13 files changed
+682
-14
No files matched your search
@@ -0,0 +1,117 @@
|
||||
const { test, beforeEach } = require('node:test');
|
||||
const assert = require('node:assert/strict');
|
||||
const https = require('https');
|
||||
const { EventEmitter } = require('events');
|
||||
|
||||
const { kommendeKonzerte, _cache } = require('../lib/konzerte');
|
||||
|
||||
// ─── Ticketmaster-Antwort einschieben ────────────────────────────────────────
|
||||
// Kein Schluessel, kein Netz: https.get wird ersetzt und liefert eine feste Antwort. Geprueft
|
||||
// wird damit genau das, was uns gehoert — Ortszeit, Gegenprobe des Namens, Fenster, Sortierung.
|
||||
let _antworten = {};
|
||||
let _aufrufe = [];
|
||||
const echtesGet = https.get;
|
||||
|
||||
function stub() {
|
||||
https.get = (url, _opts, cb) => {
|
||||
_aufrufe.push(url);
|
||||
const name = decodeURIComponent((url.match(/keyword=([^&]*)/) || [])[1] || '');
|
||||
const res = new EventEmitter();
|
||||
res.statusCode = 200;
|
||||
process.nextTick(() => {
|
||||
res.emit('data', JSON.stringify(_antworten[name] ?? { _embedded: { events: [] } }));
|
||||
res.emit('end');
|
||||
});
|
||||
cb(res);
|
||||
return new EventEmitter(); // das Request-Objekt, auf dem .on('error') gesetzt wird
|
||||
};
|
||||
}
|
||||
stub();
|
||||
process.on('exit', () => { https.get = echtesGet; });
|
||||
|
||||
const inTagen = n => {
|
||||
const d = new Date(Date.now() + n * 864e5);
|
||||
return d.toISOString().slice(0, 10);
|
||||
};
|
||||
|
||||
beforeEach(() => { _cache.clear(); _aufrufe = []; _antworten = {}; });
|
||||
|
||||
const ereignis = (name, tage, extra = {}) => ({
|
||||
id: 'e' + tage,
|
||||
name,
|
||||
dates: { start: { localDate: inTagen(tage), localTime: '20:00:00', dateTime: `${inTagen(tage)}T18:00:00Z` }, ...extra.dates },
|
||||
_embedded: { venues: [{ name: 'Halle', city: { name: 'Köln' }, country: { name: 'Germany' } }] },
|
||||
url: 'https://tm.example/' + tage,
|
||||
...extra,
|
||||
});
|
||||
|
||||
test('Ortszeit schlaegt UTC — 20:00 bleibt 20:00', async () => {
|
||||
_antworten['Rammstein'] = { _embedded: { events: [ereignis('Rammstein Live', 30)] } };
|
||||
const { konzerte } = await kommendeKonzerte([{ id: '1', name: 'Rammstein' }], 'K', 365);
|
||||
assert.equal(konzerte.length, 1);
|
||||
assert.ok(konzerte[0].datum.endsWith('T20:00:00'), `unerwartet: ${konzerte[0].datum}`);
|
||||
});
|
||||
|
||||
test('Gegenprobe: fremde Veranstaltung mit demselben Stichwort faellt raus', async () => {
|
||||
_antworten['MORE'] = { _embedded: { events: [
|
||||
ereignis('MORE Tour 2026', 10),
|
||||
ereignis('Sing Something MORE Festival', 20), // enthaelt das Wort → bleibt
|
||||
ereignis('Ganz was anderes', 30), // enthaelt es nicht → faellt raus
|
||||
] } };
|
||||
const { konzerte } = await kommendeKonzerte([{ id: '1', name: 'MORE' }], 'K', 365);
|
||||
assert.deepEqual(konzerte.map(k => k.titel), ['MORE Tour 2026', 'Sing Something MORE Festival']);
|
||||
});
|
||||
|
||||
test('Name im Attraktionsnamen zaehlt auch', async () => {
|
||||
_antworten['K/DA'] = { _embedded: { events: [
|
||||
{ ...ereignis('League Music Night', 15), _embedded: {
|
||||
venues: [{ name: 'Arena', city: { name: 'Berlin' }, country: { name: 'Germany' } }],
|
||||
attractions: [{ name: 'K/DA' }],
|
||||
} },
|
||||
] } };
|
||||
const { konzerte } = await kommendeKonzerte([{ id: '1', name: 'K/DA' }], 'K', 365);
|
||||
assert.equal(konzerte.length, 1, 'ueber die Attraktion erkannt');
|
||||
});
|
||||
|
||||
test('Termine hinter dem Fenster fallen weg', async () => {
|
||||
_antworten['Coldplay'] = { _embedded: { events: [ereignis('Coldplay Live', 20), ereignis('Coldplay Later', 200)] } };
|
||||
const { konzerte } = await kommendeKonzerte([{ id: '1', name: 'Coldplay' }], 'K', 30);
|
||||
assert.equal(konzerte.length, 1);
|
||||
assert.equal(konzerte[0].titel, 'Coldplay Live');
|
||||
});
|
||||
|
||||
test('Sortierung laeuft NACH VORN — naechstes Konzert zuerst', async () => {
|
||||
_antworten['A'] = { _embedded: { events: [ereignis('A spaet', 90)] } };
|
||||
_antworten['B'] = { _embedded: { events: [ereignis('B frueh', 5)] } };
|
||||
const { konzerte } = await kommendeKonzerte([{ id: '1', name: 'A' }, { id: '2', name: 'B' }], 'K', 365);
|
||||
assert.deepEqual(konzerte.map(k => k.titel), ['B frueh', 'A spaet']);
|
||||
});
|
||||
|
||||
test('Kuenstler ohne Termine werden gemeldet, nicht verschwiegen', async () => {
|
||||
_antworten['Leer'] = { _embedded: { events: [] } };
|
||||
const { konzerte, ohneTermine } = await kommendeKonzerte([{ id: '1', name: 'Leer' }], 'K', 365);
|
||||
assert.equal(konzerte.length, 0);
|
||||
assert.deepEqual(ohneTermine, ['Leer']);
|
||||
});
|
||||
|
||||
test('Zweiter Aufruf kommt aus dem Cache — kein weiterer Abruf', async () => {
|
||||
_antworten['Rammstein'] = { _embedded: { events: [ereignis('Rammstein Live', 30)] } };
|
||||
await kommendeKonzerte([{ id: '1', name: 'Rammstein' }], 'K', 365);
|
||||
const nachErstem = _aufrufe.length;
|
||||
await kommendeKonzerte([{ id: '1', name: 'Rammstein' }], 'K', 365);
|
||||
assert.equal(_aufrufe.length, nachErstem, 'zweiter Aufruf darf nicht erneut abrufen');
|
||||
});
|
||||
|
||||
test('Abgesagt wird durchgereicht statt stillschweigend gezeigt', async () => {
|
||||
_antworten['X'] = { _embedded: { events: [
|
||||
{ ...ereignis('X Tour', 40), dates: { start: { localDate: inTagen(40), localTime: '20:00:00' }, status: { code: 'cancelled' } } },
|
||||
] } };
|
||||
const { konzerte } = await kommendeKonzerte([{ id: '1', name: 'X' }], 'K', 365);
|
||||
assert.equal(konzerte[0].abgesagt, true);
|
||||
});
|
||||
|
||||
test('Ein Kuenstlername ohne Buchstaben loest keinen Abruf aus', async () => {
|
||||
const { konzerte } = await kommendeKonzerte([{ id: '1', name: '???' }], 'K', 365);
|
||||
assert.equal(konzerte.length, 0);
|
||||
assert.equal(_aufrufe.length, 0);
|
||||
});
|
||||
Reference in new issue
Block a user