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>
This commit is contained in:
@@ -0,0 +1,66 @@
|
||||
// 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);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
Reference in New Issue
Block a user