// 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); } }); } });