103 lines
3.6 KiB
TypeScript
103 lines
3.6 KiB
TypeScript
|
|
import assert from "node:assert/strict";
|
||
|
|
import test from "node:test";
|
||
|
|
import {
|
||
|
|
BIO_MAX_LENGTH,
|
||
|
|
DISCRIMINATOR_ALPHABET,
|
||
|
|
DISCRIMINATOR_LENGTH,
|
||
|
|
formatHandle,
|
||
|
|
generateDiscriminator,
|
||
|
|
generateUniqueDiscriminator,
|
||
|
|
validateUsername,
|
||
|
|
} from "../lib/handle";
|
||
|
|
|
||
|
|
/** Liefert eine deterministische Random-Funktion, die der Reihe nach `values` ausgibt. */
|
||
|
|
function seededRandom(values: number[]): () => number {
|
||
|
|
let i = 0;
|
||
|
|
return () => values[i++ % values.length];
|
||
|
|
}
|
||
|
|
|
||
|
|
test("generateDiscriminator hat die konfigurierte Länge", () => {
|
||
|
|
assert.equal(generateDiscriminator().length, DISCRIMINATOR_LENGTH);
|
||
|
|
});
|
||
|
|
|
||
|
|
test("generateDiscriminator nutzt nur Zeichen aus dem Alphabet", () => {
|
||
|
|
for (let i = 0; i < 200; i++) {
|
||
|
|
for (const ch of generateDiscriminator()) {
|
||
|
|
assert.ok(DISCRIMINATOR_ALPHABET.includes(ch), `unerwartetes Zeichen: ${ch}`);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
});
|
||
|
|
|
||
|
|
test("Alphabet enthält keine verwechselbaren Zeichen (0 O 1 I L)", () => {
|
||
|
|
for (const forbidden of ["0", "O", "1", "I", "L"]) {
|
||
|
|
assert.ok(!DISCRIMINATOR_ALPHABET.includes(forbidden), `verboten: ${forbidden}`);
|
||
|
|
}
|
||
|
|
});
|
||
|
|
|
||
|
|
test("generateDiscriminator ist deterministisch bei fester Random-Quelle", () => {
|
||
|
|
// random()=0 -> immer erstes Zeichen des Alphabets
|
||
|
|
assert.equal(
|
||
|
|
generateDiscriminator(() => 0),
|
||
|
|
DISCRIMINATOR_ALPHABET[0].repeat(DISCRIMINATOR_LENGTH),
|
||
|
|
);
|
||
|
|
});
|
||
|
|
|
||
|
|
test("generateUniqueDiscriminator umgeht bereits vergebene Discriminatoren", () => {
|
||
|
|
const first = DISCRIMINATOR_ALPHABET[0].repeat(DISCRIMINATOR_LENGTH);
|
||
|
|
const second =
|
||
|
|
DISCRIMINATOR_ALPHABET[1] + DISCRIMINATOR_ALPHABET[0].repeat(DISCRIMINATOR_LENGTH - 1);
|
||
|
|
// Erster Versuch kollidiert (alles idx 0), zweiter Versuch erstes Zeichen = idx 1
|
||
|
|
const random = seededRandom([0, 0, 0, 0, 1 / DISCRIMINATOR_ALPHABET.length, 0, 0, 0]);
|
||
|
|
assert.equal(generateUniqueDiscriminator([first], random), second);
|
||
|
|
});
|
||
|
|
|
||
|
|
test("generateUniqueDiscriminator ignoriert Groß-/Kleinschreibung", () => {
|
||
|
|
const taken = DISCRIMINATOR_ALPHABET[0].repeat(DISCRIMINATOR_LENGTH).toLowerCase();
|
||
|
|
const random = seededRandom([0, 0, 0, 0, 1 / DISCRIMINATOR_ALPHABET.length, 0, 0, 0]);
|
||
|
|
const result = generateUniqueDiscriminator([taken], random);
|
||
|
|
assert.notEqual(result, DISCRIMINATOR_ALPHABET[0].repeat(DISCRIMINATOR_LENGTH));
|
||
|
|
});
|
||
|
|
|
||
|
|
test("generateUniqueDiscriminator wirft, wenn nach maxAttempts kein freier gefunden wird", () => {
|
||
|
|
const onlyZero = () => 0; // erzeugt immer denselben Discriminator
|
||
|
|
const taken = [DISCRIMINATOR_ALPHABET[0].repeat(DISCRIMINATOR_LENGTH)];
|
||
|
|
assert.throws(() => generateUniqueDiscriminator(taken, onlyZero, 10));
|
||
|
|
});
|
||
|
|
|
||
|
|
test("formatHandle baut Username#Discriminator", () => {
|
||
|
|
assert.equal(formatHandle("Held", "A12B"), "Held#A12B");
|
||
|
|
});
|
||
|
|
|
||
|
|
test("formatHandle liefert null ohne Username", () => {
|
||
|
|
assert.equal(formatHandle(null, "A12B"), null);
|
||
|
|
});
|
||
|
|
|
||
|
|
test("formatHandle liefert null ohne Discriminator", () => {
|
||
|
|
assert.equal(formatHandle("Held", null), null);
|
||
|
|
});
|
||
|
|
|
||
|
|
test("validateUsername akzeptiert gültige Namen und trimmt", () => {
|
||
|
|
assert.deepEqual(validateUsername(" Held_2 "), { ok: true, value: "Held_2" });
|
||
|
|
});
|
||
|
|
|
||
|
|
test("validateUsername lehnt zu kurze Namen ab", () => {
|
||
|
|
assert.equal(validateUsername("ab").ok, false);
|
||
|
|
});
|
||
|
|
|
||
|
|
test("validateUsername lehnt zu lange Namen ab", () => {
|
||
|
|
assert.equal(validateUsername("a".repeat(25)).ok, false);
|
||
|
|
});
|
||
|
|
|
||
|
|
test("validateUsername lehnt '#' und Leerzeichen ab", () => {
|
||
|
|
assert.equal(validateUsername("Held#1").ok, false);
|
||
|
|
assert.equal(validateUsername("Held Held").ok, false);
|
||
|
|
});
|
||
|
|
|
||
|
|
test("validateUsername lehnt Umlaute ab (Handle bleibt URL-tauglich)", () => {
|
||
|
|
assert.equal(validateUsername("Hähnchen").ok, false);
|
||
|
|
});
|
||
|
|
|
||
|
|
test("Bio-Limit ist 280", () => {
|
||
|
|
assert.equal(BIO_MAX_LENGTH, 280);
|
||
|
|
});
|