From b34c89e359cdf64e2b9b103d24170b1101040c5a Mon Sep 17 00:00:00 2001 From: is0692vs Date: Tue, 17 Mar 2026 20:33:56 +0900 Subject: [PATCH 1/2] test(api): align slug validation messaging --- apps/api/src/utils/__tests__/validation.test.ts | 9 +++++++-- apps/api/src/utils/validation.ts | 2 +- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/apps/api/src/utils/__tests__/validation.test.ts b/apps/api/src/utils/__tests__/validation.test.ts index bdf373e..a212534 100644 --- a/apps/api/src/utils/__tests__/validation.test.ts +++ b/apps/api/src/utils/__tests__/validation.test.ts @@ -8,8 +8,8 @@ describe("validation utils", () => { }); it("validates length", () => { - expect(validateSlug("ab")).toBe("slug must be 3-40 characters"); - expect(validateSlug("a".repeat(41))).toBe("slug must be 3-40 characters"); + expect(validateSlug("ab")).toBe("slug must be 3–40 characters"); + expect(validateSlug("a".repeat(41))).toBe("slug must be 3–40 characters"); }); it("validates character set", () => { @@ -21,6 +21,11 @@ describe("validation utils", () => { expect(validateSlug("my--slug")).toBe("slug must not contain consecutive hyphens"); }); + it("rejects leading/trailing hyphens", () => { + expect(validateSlug("-invalid")).toBe("slug must contain only lowercase letters, numbers, and hyphens"); + expect(validateSlug("invalid-")).toBe("slug must contain only lowercase letters, numbers, and hyphens"); + }); + it("accepts valid slugs", () => { expect(validateSlug("my-valid-slug-123")).toBeNull(); expect(validateSlug("abc")).toBeNull(); diff --git a/apps/api/src/utils/validation.ts b/apps/api/src/utils/validation.ts index 725713e..7d305f3 100644 --- a/apps/api/src/utils/validation.ts +++ b/apps/api/src/utils/validation.ts @@ -3,7 +3,7 @@ export const SLUG_RE = /^[a-z0-9][a-z0-9-]*[a-z0-9]$|^[a-z0-9]$/; export function validateSlug(slug: unknown): string | null { if (typeof slug !== "string") return "slug is required"; const s = slug.trim().toLowerCase(); - if (s.length < 3 || s.length > 40) return "slug must be 3-40 characters"; + if (s.length < 3 || s.length > 40) return "slug must be 3–40 characters"; if (!SLUG_RE.test(s)) return "slug must contain only lowercase letters, numbers, and hyphens"; if (s.includes("--")) return "slug must not contain consecutive hyphens"; return null; From ee7b03d6117bff1ab75d1102dd9881f4179c6e72 Mon Sep 17 00:00:00 2001 From: is0692vs Date: Tue, 17 Mar 2026 21:19:23 +0900 Subject: [PATCH 2/2] Refine slug error for edge hyphen positions --- apps/api/src/utils/__tests__/validation.test.ts | 4 ++-- apps/api/src/utils/validation.ts | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/apps/api/src/utils/__tests__/validation.test.ts b/apps/api/src/utils/__tests__/validation.test.ts index a212534..5da0ba0 100644 --- a/apps/api/src/utils/__tests__/validation.test.ts +++ b/apps/api/src/utils/__tests__/validation.test.ts @@ -22,8 +22,8 @@ describe("validation utils", () => { }); it("rejects leading/trailing hyphens", () => { - expect(validateSlug("-invalid")).toBe("slug must contain only lowercase letters, numbers, and hyphens"); - expect(validateSlug("invalid-")).toBe("slug must contain only lowercase letters, numbers, and hyphens"); + expect(validateSlug("-invalid")).toBe("slug must not start or end with a hyphen"); + expect(validateSlug("invalid-")).toBe("slug must not start or end with a hyphen"); }); it("accepts valid slugs", () => { diff --git a/apps/api/src/utils/validation.ts b/apps/api/src/utils/validation.ts index 7d305f3..5ae187d 100644 --- a/apps/api/src/utils/validation.ts +++ b/apps/api/src/utils/validation.ts @@ -4,6 +4,7 @@ export function validateSlug(slug: unknown): string | null { if (typeof slug !== "string") return "slug is required"; const s = slug.trim().toLowerCase(); if (s.length < 3 || s.length > 40) return "slug must be 3–40 characters"; + if (s.startsWith("-") || s.endsWith("-")) return "slug must not start or end with a hyphen"; if (!SLUG_RE.test(s)) return "slug must contain only lowercase letters, numbers, and hyphens"; if (s.includes("--")) return "slug must not contain consecutive hyphens"; return null;