diff --git a/apps/api/src/utils/__tests__/validation.test.ts b/apps/api/src/utils/__tests__/validation.test.ts index bdf373e..5da0ba0 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 not start or end with a hyphen"); + expect(validateSlug("invalid-")).toBe("slug must not start or end with a hyphen"); + }); + 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..5ae187d 100644 --- a/apps/api/src/utils/validation.ts +++ b/apps/api/src/utils/validation.ts @@ -3,7 +3,8 @@ 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 (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;