Files
galaxy-game/ui/frontend/tests/entity-name.test.ts
Ilia Denisov f80c623a74 ui/phase-14: rename planet end-to-end + order read-back
Wires the first end-to-end command through the full pipeline:
inspector rename action → local order draft → user.games.order
submit → optimistic overlay on map / inspector → server hydration
on cache miss via the new user.games.order.get message type.

Backend: GET /api/v1/user/games/{id}/orders forwards to engine
GET /api/v1/order. Gateway parses the engine PUT response into the
extended UserGamesOrderResponse FBS envelope and adds
executeUserGamesOrderGet for the read-back path. Frontend ports
ValidateTypeName to TS, lands the inline rename editor + Submit
button, and exposes a renderedReport context so consumers see the
overlay-applied snapshot.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-09 11:50:09 +02:00

67 lines
3.3 KiB
TypeScript

// Parity tests for the TS port of `pkg/util/string.go.ValidateTypeName`.
// Cases are aligned with `pkg/util/string_test.go.TestValidateString`
// so the client-side and server-side validators reject the same set
// of inputs — a name that's locally valid is always accepted at the
// wire level.
import { describe, expect, test } from "vitest";
import {
validateEntityName,
type EntityNameInvalidReason,
} from "../src/lib/util/entity-name";
describe("validateEntityName", () => {
const valid: { name: string; input: string; expected: string }[] = [
{ name: "latin + digits", input: "Hello_World-123", expected: "Hello_World-123" },
{ name: "cyrillic", input: "Привет_мир-42", expected: "Привет_мир-42" },
{ name: "greek", input: "Αλφα_Βητα-2024", expected: "Αλφα_Βητα-2024" },
{ name: "arabic", input: "مرحبا_العالم-7", expected: "مرحبا_العالم-7" },
{ name: "japanese katakana", input: "テスト_ケース-1", expected: "テスト_ケース-1" },
{ name: "chinese", input: "你好_世界-123", expected: "你好_世界-123" },
{ name: "hindi (combining marks)", input: "नमस्ते_दुनिया-456", expected: "नमस्ते_दुनिया-456" },
{ name: "thai (combining marks)", input: "สวัสดี_โลก-789", expected: "สวัสดี_โลก-789" },
{ name: "korean", input: "안녕하세요_세계-101", expected: "안녕하세요_세계-101" },
{ name: "trim outer whitespace", input: " Earth ", expected: "Earth" },
{ name: "valid consecutive specials", input: "Valid_(special)_Chars", expected: "Valid_(special)_Chars" },
{ name: "all allowed specials", input: "A@#b$%c^*d-_e=+f~(g)[h]{i}j", expected: "A@#b$%c^*d-_e=+f~(g)[h]{i}j" },
];
for (const tc of valid) {
test(`accepts: ${tc.name}`, () => {
const result = validateEntityName(tc.input);
expect(result.ok).toBe(true);
if (result.ok) {
expect(result.value).toBe(tc.expected);
}
});
}
const invalid: {
name: string;
input: string;
reason: EntityNameInvalidReason;
}[] = [
{ name: "empty after trim", input: " ", reason: "empty" },
{ name: "explicitly empty", input: "", reason: "empty" },
{ name: "too long", input: "ValidatedStringHasTooManyCharacters", reason: "too_long" },
{ name: "internal space", input: "Test 123", reason: "whitespace" },
{ name: "internal tab", input: "Test\tName", reason: "whitespace" },
{ name: "internal newline", input: "Test\nName", reason: "whitespace" },
{ name: "starts with special after trim", input: " -Test123", reason: "starts_with_special" },
{ name: "ends with special after trim", input: "Test123- ", reason: "ends_with_special" },
{ name: "emoji", input: "Test🙂Name", reason: "disallowed_character" },
{ name: "starts with special $", input: "$pecialString", reason: "starts_with_special" },
{ name: "ends with special _", input: "SpecialString_", reason: "ends_with_special" },
{ name: "too many consecutive specials", input: "Too_Many_(special[_]Chars", reason: "consecutive_specials" },
];
for (const tc of invalid) {
test(`rejects: ${tc.name}`, () => {
const result = validateEntityName(tc.input);
expect(result.ok).toBe(false);
if (!result.ok) {
expect(result.reason).toBe(tc.reason);
}
});
}
});