Tote CCs zurueckholen, Redesign-Beitritte und Problemfaelle
Vier Punkte aus dem Betrieb. Ein toter CC liess sich nicht zurueckholen. Der Knopf dafuer wurde nie angezeigt: bei Grund "Im RP gestorben" landete die Seite im Zweig "Bezahlt reaktivieren", den der Server zu Recht abweist - ein Toter kehrt nur durch einen Administrator zurueck. Der Dialog dafuer war fertig gebaut und wurde schlicht nie geoeffnet. Jetzt steht an einem toten CC "Zurueckholen", fuer Editoren stattdessen der Hinweis, dass das ein Administrator entscheidet. Der Abgleich eines einzelnen Spielers fragte nur dessen hinterlegte SteamID ab. Guthaben, das unter einem frueheren Konto gekauft wurde, blieb damit unsichtbar - obwohl das Konto hinterlegt war und der Sammellauf es beruecksichtigt hat. Jetzt werden alle Kennungen des Spielers abgefragt. Ein Beitritt zu einem Gruppen-CC ueber ein Redesign kostete nur das Guthaben. Damit entstand aus einem Redesign ein zusaetzlicher CC, was die Regel gerade nicht hergibt: ein Redesign ersetzt einen CC. Jetzt muss angegeben werden, welcher eigene CC dafuer aufgegeben wird; ein aktiver wird dabei inaktiv gesetzt, ein bereits inaktiver bleibt unveraendert. Ueber ein CC-Paket kommt weiterhin ein Platz hinzu, ohne Vorgaenger. Welcher CC aufgegeben wurde, haengt am Beitritt und bleibt nachvollziehbar. Faelle, die sich beim Eintragen nicht klaeren lassen - ein Antrag ohne auffindbares Paket, ein fehlender GExtension-Eintrag, widerspruechliche Angaben - koennen als Spieler angelegt und dabei als Problem vorgemerkt werden, mit Notiz. Auch nachtraeglich setzbar und wieder abraeumbar. Die Markierung ist folgenlos: sie sperrt nichts und veraendert weder Guthaben noch CCs. Die Uebersicht kennzeichnet betroffene Spieler und laesst sich darauf eingrenzen. Nebenbei: die Spielerliste gab die Markierung nicht mit aus, aufgefallen beim Test.
This commit is contained in:
@@ -5,12 +5,14 @@ import { startTestDatabase, type TestDatabase } from './harness.js';
|
||||
type Forum = typeof import('../src/services/forum.js');
|
||||
type Players = typeof import('../src/services/players.js');
|
||||
type Characters = typeof import('../src/services/characters.js');
|
||||
type Scraper = typeof import('../src/services/scraper.js');
|
||||
type Db = typeof import('../src/db.js');
|
||||
|
||||
let database: TestDatabase;
|
||||
let forum: Forum;
|
||||
let players: Players;
|
||||
let characters: Characters;
|
||||
let scraper: Scraper;
|
||||
let db: Db;
|
||||
let ctx: { actorId: number; actorName: string };
|
||||
|
||||
@@ -24,6 +26,7 @@ before(async () => {
|
||||
forum = await import('../src/services/forum.js');
|
||||
players = await import('../src/services/players.js');
|
||||
characters = await import('../src/services/characters.js');
|
||||
scraper = await import('../src/services/scraper.js');
|
||||
|
||||
const user = await db.prisma.user.create({
|
||||
data: { username: 'pfleger', passwordHash: 'x', role: 'ADMIN' },
|
||||
@@ -196,3 +199,115 @@ describe('Antrag am CC', () => {
|
||||
assert.match(cc?.forumThreads[0]?.url ?? '', /thread\/9501/);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Problemfälle', () => {
|
||||
it('legt einen Spieler an, obwohl etwas offen ist', async () => {
|
||||
const spieler = await players.createPlayer(
|
||||
{
|
||||
displayName: 'Fall ohne Paket',
|
||||
isProblem: true,
|
||||
problemNote: 'CC-Antrag da, aber kein Paket auffindbar',
|
||||
},
|
||||
ctx,
|
||||
);
|
||||
|
||||
assert.equal(spieler.isProblem, true);
|
||||
assert.equal(spieler.problemNote, 'CC-Antrag da, aber kein Paket auffindbar');
|
||||
assert.ok(spieler.problemSince, 'wann es aufgefallen ist, gehört dazu');
|
||||
});
|
||||
|
||||
it('markiert auch im Nachhinein', async () => {
|
||||
const spieler = await players.createPlayer(
|
||||
{ displayName: 'Erst später auffällig', steamId: '76561198000011111' },
|
||||
ctx,
|
||||
);
|
||||
assert.equal(spieler.isProblem, false);
|
||||
|
||||
const markiert = await players.markProblem(
|
||||
spieler.id,
|
||||
{ isProblem: true, note: 'Kein GExtension-Eintrag' },
|
||||
ctx,
|
||||
);
|
||||
assert.equal(markiert.isProblem, true);
|
||||
assert.equal(markiert.problemNote, 'Kein GExtension-Eintrag');
|
||||
});
|
||||
|
||||
it('behält den Anfang, wenn die Notiz nachgeschärft wird', async () => {
|
||||
const spieler = await players.createPlayer(
|
||||
{ displayName: 'Langer Fall', isProblem: true, problemNote: 'unklar' },
|
||||
ctx,
|
||||
);
|
||||
const anfang = spieler.problemSince;
|
||||
|
||||
const nachgeschaerft = await players.markProblem(
|
||||
spieler.id,
|
||||
{ isProblem: true, note: 'Paket liegt unter altem Konto' },
|
||||
ctx,
|
||||
);
|
||||
assert.deepEqual(nachgeschaerft.problemSince, anfang, 'der Fall ist derselbe, nur klarer');
|
||||
});
|
||||
|
||||
it('räumt die Markierung restlos ab', async () => {
|
||||
const spieler = await players.createPlayer(
|
||||
{ displayName: 'Geklärt', isProblem: true, problemNote: 'war ein Tippfehler' },
|
||||
ctx,
|
||||
);
|
||||
|
||||
const geklaert = await players.markProblem(spieler.id, { isProblem: false }, ctx);
|
||||
assert.equal(geklaert.isProblem, false);
|
||||
assert.equal(geklaert.problemNote, null);
|
||||
assert.equal(geklaert.problemSince, null);
|
||||
});
|
||||
|
||||
it('ändert an Guthaben und CCs nichts', async () => {
|
||||
const spieler = await players.createPlayer(
|
||||
{ displayName: 'Trotzdem berechtigt', steamId: '76561198000012222' },
|
||||
ctx,
|
||||
);
|
||||
await db.prisma.creditLedger.create({
|
||||
data: { playerId: spieler.id, kind: 'CC', amount: 1, source: 'CC_PACKAGE' },
|
||||
});
|
||||
const { character } = await characters.createCharacter(spieler.id, { name: 'Bleibt' }, ctx);
|
||||
|
||||
await players.markProblem(spieler.id, { isProblem: true, note: 'Herkunft unklar' }, ctx);
|
||||
|
||||
const detail = await players.getPlayerDetail(spieler.id);
|
||||
assert.equal(detail.balances.CC, 0, 'das Paket ist verbraucht, nicht verschwunden');
|
||||
assert.equal(detail.characters.find((c) => c.id === character.id)?.status, 'ACTIVE');
|
||||
});
|
||||
|
||||
it('listet sie auf Wunsch für sich', async () => {
|
||||
const offen = await players.searchPlayers('', 100, 0, true);
|
||||
assert.ok(offen.total >= 3);
|
||||
assert.equal(
|
||||
offen.items.every((p) => p.isProblem),
|
||||
true,
|
||||
);
|
||||
|
||||
const alle = await players.searchPlayers('', 100, 0, false);
|
||||
assert.ok(alle.total > offen.total, 'ohne Filter sind auch die unauffälligen dabei');
|
||||
});
|
||||
});
|
||||
|
||||
describe('Konten beim Abgleich', () => {
|
||||
it('sammelt alle Steam-Kennungen eines Spielers, nicht nur die hinterlegte', async () => {
|
||||
const spieler = await players.createPlayer(
|
||||
{ displayName: 'Kontowechsler', steamId: '76561198000013333' },
|
||||
ctx,
|
||||
);
|
||||
await players.addAlias(spieler.id, 'STEAM_ID', '76561198000014444', 'Erstes Konto', ctx);
|
||||
await players.addAlias(spieler.id, 'STEAM_ID', 'STEAM_0:1:19867635', 'Ganz altes Konto', ctx);
|
||||
|
||||
const kennungen = await scraper.steamIdsOf(spieler.id);
|
||||
assert.deepEqual(
|
||||
kennungen.sort(),
|
||||
['76561198000013333', '76561198000014444', '76561198000000999'].sort(),
|
||||
'auch in der Legacy-Schreibweise nachgetragene Konten gehören dazu',
|
||||
);
|
||||
});
|
||||
|
||||
it('kommt mit einem Spieler ohne jede Kennung klar', async () => {
|
||||
const spieler = await players.createPlayer({ displayName: 'Nur ein Name' }, ctx);
|
||||
assert.deepEqual(await scraper.steamIdsOf(spieler.id), []);
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user