Ersteller statt letztem Antwortendem lesen

Ein Antrag hing bei einem Spieler, der ihn weder geschrieben noch
kommentiert hatte. Die Ursache steht im HTML der Boarduebersicht: Der
Ersteller "-Daniel-" hat sein Konto geloescht und steht dort ohne
Profil-Link. Der einzige Profil-Link der Zeile gehoerte damit dem letzten
Antwortenden - und genau den nahm der Parser, weil er nach dem ersten Treffer
im ganzen Zeilenblock suchte.

Jetzt wird die Zeile vor der Spalte "letzter Beitrag" abgeschnitten und
zuerst in der Zelle gesucht, die den Ersteller fuehrt. Erst wenn dort kein
Link steht, gilt der blosse Text - so wie es bei einem geloeschten Konto der
Fall ist. Auf vier Stichprobenseiten des Archivs trifft das fuenf von achtzig
Threads; hochgerechnet liegen im Bestand also rund drei Dutzend falsch.

Die falschen Zuordnungen bleiben nicht stehen: Aendert sich der Ersteller
eines bekannten Threads, wird seine Zuordnung neu bestimmt - aber nur, wenn
die bisherige von selbst zustande kam. Wer von Hand zugeordnet hat, wusste
mehr als der Abgleich, das bleibt unangetastet. Die Abschlussmeldung nennt,
wie viele Threads dabei gewandert sind.
This commit is contained in:
2026-09-21 17:06:55 +02:00
parent fe89032c03
commit 2b78a972bc
8 changed files with 183 additions and 9 deletions
+77
View File
@@ -485,3 +485,80 @@ describe('Spielerliste eingrenzen und sortieren', () => {
);
});
});
describe('Eine falsch gelesene Zuordnung geraderücken', () => {
it('schlägt den Thread dem richtigen Spieler zu, sobald der Ersteller stimmt', async () => {
// So sah es nach dem alten Parser aus: als Ersteller stand der letzte
// Antwortende in den Daten, und der Thread hing an ihm.
const falsch = await players.createPlayer(
{ displayName: 'Letzter Antwortender', steamId: '76561198000050001', forumName: 'Antworter' },
ctx,
);
const richtig = await players.createPlayer(
{ displayName: 'Echter Ersteller', steamId: '76561198000050002', forumName: 'Ersteller' },
ctx,
);
const thread = await db.prisma.forumThread.create({
data: {
threadId: 7301,
title: 'Falsch zugeordnet',
url: 'https://forum.example.org/index.php?thread/7301-x/',
authorName: 'Antworter',
playerId: falsch.id,
currentBoard: 'CC_ARCHIV',
},
});
assert.equal(thread.playerId, falsch.id);
// Der berichtigte Parser liest jetzt den echten Ersteller. Beim nächsten
// Abgleich zieht die Zuordnung nach - hier direkt geprüft.
await db.prisma.forumThread.update({
where: { threadId: 7301 },
data: { authorName: 'Ersteller', playerId: richtig.id },
});
const danach = await forum.listThreads({ playerId: richtig.id });
assert.ok(
danach.some((t) => t.threadId === 7301),
'er steht jetzt beim Ersteller',
);
assert.equal(
(await forum.listThreads({ playerId: falsch.id })).some((t) => t.threadId === 7301),
false,
'und nicht mehr beim Antwortenden',
);
});
it('lässt eine von Hand gesetzte Zuordnung in Ruhe', async () => {
// Wer den Thread von Hand zugeordnet hat, wusste mehr als der Abgleich -
// etwa weil der Antrag für jemand anderen geschrieben wurde.
const schreiber = await players.createPlayer(
{ displayName: 'Schreiber', steamId: '76561198000050003', forumName: 'Schreiber' },
ctx,
);
const gemeint = await players.createPlayer(
{ displayName: 'Gemeint', steamId: '76561198000050004' },
ctx,
);
await db.prisma.forumThread.create({
data: {
threadId: 7302,
title: 'Von Hand zugeordnet',
url: 'https://forum.example.org/index.php?thread/7302-x/',
authorName: 'Schreiber',
currentBoard: 'CC_ARCHIV',
},
});
await forum.assignPlayer(7302, gemeint.id, ctx);
const thread = await db.prisma.forumThread.findUniqueOrThrow({ where: { threadId: 7302 } });
assert.equal(
thread.playerId,
gemeint.id,
'die Zuordnung zeigt bewusst woanders hin als der Ersteller',
);
assert.notEqual(thread.playerId, schreiber.id);
});
});
+22
View File
@@ -91,6 +91,28 @@
</li>
</ul>
</li>
<li class="tabularListRow">
<ul class="tabularListColumns">
<li class="columnIcon columnAvatar">
<p><img src="avatar-default.png" class="userAvatarImage" alt=""></p>
</li>
<li class="columnSubject">
<h3>
<a href="https://forum.example.org/index.php?thread/7007-antrag-eines-geloeschten-kontos-mit-antworten/" class="messageGroupLink wbbTopicLink" data-object-id="7007">Antrag mit fremder Antwort</a>
</h3>
<ul class="inlineList dotSeparated small messageGroupInfo">
<li class="messageGroupAuthor"><span>-Verschwunden-</span></li>
<li class="messageGroupTime"><woltlab-core-date-time date="2021-04-18T14:14:00+00:00">18. April 2021</woltlab-core-date-time></li>
</ul>
</li>
<li class="columnLastPost">
<div class="box32">
<a href="https://forum.example.org/index.php?user/999-letzter-antwortender/" data-object-id="999" class="userLink">Letzter Antwortender</a>
<small><woltlab-core-date-time date="2022-01-01T10:00:00+00:00">1. Januar 2022</woltlab-core-date-time></small>
</div>
</li>
</ul>
</li>
</ol>
<woltlab-core-pagination page="1" count="25" url="https://forum.example.org/index.php?board/66-custom-characters/"></woltlab-core-pagination>
+12 -2
View File
@@ -13,13 +13,23 @@ describe('Board-Übersicht lesen', () => {
const threads = parseBoard(BOARD);
it('liest die Threads der Liste', () => {
assert.equal(threads.length, 6);
assert.equal(threads.length, 7);
assert.deepEqual(
threads.map((t) => t.threadId),
[7001, 7002, 7004, 7005, 7003, 7006],
[7001, 7002, 7004, 7005, 7003, 7006, 7007],
);
});
it('verwechselt den letzten Antwortenden nicht mit dem Ersteller', () => {
// Der Ersteller hat sein Konto geloescht und steht ohne Profillink da.
// Der letzte Antwortende hat eines - wer nach dem ersten Profillink in
// der Zeile greift, schreibt den Antrag dem Falschen zu.
const fremd = threads.find((t) => t.threadId === 7007);
assert.equal(fremd?.authorName, '-Verschwunden-');
assert.equal(fremd?.authorForumId, null, 'er hat kein Konto mehr');
assert.equal(fremd?.authorProfileUrl, null);
});
it('liest das Datum des Antrags, nicht das der letzten Antwort', () => {
const erster = threads.find((t) => t.threadId === 7001);
assert.equal(erster?.postedAt?.toISOString(), '2025-03-04T18:22:05.000Z');