Resolve external opponent TTR ratings and show lookup diagnostics
All checks were successful
Deploy tt-tagebuch / deploy (push) Successful in 1m0s

This commit is contained in:
Torsten Schulz (local)
2026-10-28 11:03:46 +01:00
parent c473b64150
commit ecee9e0a00
6 changed files with 249 additions and 70 deletions

View File

@@ -0,0 +1,98 @@
import test from 'node:test';
import assert from 'node:assert/strict';
import { resolveTtrPreview, clubNameFromTeam } from '../services/ttrPreviewService.js';
const anna = { firstname: 'Anna', lastname: 'Wolf', nuLigaPersonId: 123 };
function setup(overrides = {}) {
const calls = [];
const input = { homePlayers: [anna], guestPlayers: [], homeTeamName: 'TSV 1875 Bonames (J15)', ...overrides };
const dependencies = {
getSession: async () => ({ cookie: 'test-cookie' }),
client: {
searchClubs: async (_cookie, query) => {
calls.push(['search', query]);
return { success: true, clubs: [{ clubname: 'TSV 1875 Bonames', clubnr: '43002', organization_short: 'HeTTV' }] };
},
getClubRankings: async (_cookie, club, fed) => {
calls.push(['rankings', club, fed]);
return { success: true, entries: [{ ...anna, fedRank: 680 }] };
},
},
};
return { input, dependencies, calls };
}
test('resolves an external opponent without any local club through search and ranking', async () => {
const { input, dependencies, calls } = setup();
const result = await resolveTtrPreview(input, dependencies);
assert.equal(result.players[0].ttr, 680);
assert.equal(result.players[0].source, 'myTischtennis');
assert.deepEqual(calls, [['search', 'TSV 1875 Bonames'], ['rankings', '43002', 'HeTTV']]);
assert.deepEqual(result.diagnostics, []);
assert.equal(clubNameFromTeam('Eintracht Frankfurt VI (J15)'), 'Eintracht Frankfurt');
});
test('uses stored ratings from both clubs without requiring a myTischtennis session', async () => {
const { input, dependencies, calls } = setup({ guestPlayers: [{ firstname: 'Yumin', lastname: 'Kim' }],
guestTeamName: 'Eintracht Frankfurt VI (J15)',
clubs: [{ id: 1, name: 'TSV 1875 Bonames' }, { id: 2, name: 'Eintracht Frankfurt' }],
members: [{ clubId: 1, firstName: 'Anna', lastName: 'Wolf', ttr: 680 },
{ clubId: 2, firstName: 'Yumin', lastName: 'Kim', ttr: 900 }] });
dependencies.getSession = () => { throw new Error('must not log in'); };
const result = await resolveTtrPreview(input, dependencies);
assert.deepEqual(result.players.map(p => p.ttr), [680, 900]);
assert.equal(calls.length, 0);
});
test('an expired session is visible and does not erase already resolved local players', async () => {
const { input, dependencies } = setup({ guestPlayers: [{ firstname: 'Yumin', lastname: 'Kim' }],
members: [{ firstName: 'Anna', lastName: 'Wolf', ttr: 680 }] });
dependencies.getSession = async () => { throw new Error('Session abgelaufen. Bitte erneut einloggen.'); };
const result = await resolveTtrPreview(input, dependencies);
assert.equal(result.players[0].ttr, 680);
assert.equal(result.players[1].ttr, null);
assert.match(result.diagnostics[0].message, /Session abgelaufen/);
});
test('missing or ambiguous ranking values are not converted to zero or guessed', async () => {
const { input, dependencies } = setup();
dependencies.client.getClubRankings = async () => ({ success: true, entries: [{ ...anna, fedRank: null }] });
let result = await resolveTtrPreview(input, dependencies);
assert.equal(result.players[0].ttr, null);
assert.equal(result.diagnostics[0].code, 'players_not_found');
dependencies.client.getClubRankings = async () => ({ success: true, entries: [{ ...anna, fedRank: 680 }, { ...anna, fedRank: 900 }] });
result = await resolveTtrPreview(input, dependencies);
assert.equal(result.players[0].ttr, null);
});
test('daily cached values skip external calls but missing cached players can be retried', async () => {
const { input, dependencies, calls } = setup({ cachedPlayers: [{ side: 'home', name: 'Anna Wolf', nuLigaPersonId: 123, ttr: 670, source: 'myTischtennis' }] });
let result = await resolveTtrPreview(input, dependencies);
assert.equal(result.players[0].ttr, 670);
assert.equal(calls.length, 0);
input.cachedPlayers[0].ttr = null;
result = await resolveTtrPreview(input, dependencies);
assert.equal(result.players[0].ttr, 680);
assert.equal(calls.length, 2);
});
test('nuLiga and myTischtennis numeric IDs cannot accidentally match different players', async () => {
const { input, dependencies } = setup({ members: [{ myTischtennisPlayerId: 123, firstName: 'Other', lastName: 'Player', ttr: 2000 }] });
const result = await resolveTtrPreview(input, dependencies);
assert.equal(result.players[0].ttr, 680);
});
test('configured clubs bypass search and failures report their actual cause', async () => {
const { input, dependencies, calls } = setup({ clubs: [{ name: 'TSV 1875 Bonames', associationMemberNumber: '43002', myTischtennisFedNickname: 'HeTTV' }] });
dependencies.client.getClubRankings = async () => ({ success: false, error: 'Zugriff auf aktuelle Rangliste verweigert' });
const result = await resolveTtrPreview(input, dependencies);
assert.equal(calls.length, 0);
assert.match(result.diagnostics[0].message, /Zugriff auf aktuelle Rangliste verweigert/);
});
test('displayed pairings can resolve ratings without nuScore lineup metadata', async () => {
const { input, dependencies } = setup({ homePlayers: [{ name: 'Anna Wolf' }] });
const result = await resolveTtrPreview(input, dependencies);
assert.equal(result.players[0].ttr, 680);
assert.equal(result.players[0].name, 'Anna Wolf');
});