feat(users): validate usernames before creating or renaming (#610)

This commit is contained in:
Jade
2026-08-25 14:04:49 +09:00
committed by GitHub
parent a5b6ecf28e
commit 03ce112457
5 changed files with 78 additions and 3 deletions
+28 -1
View File
@@ -1,7 +1,7 @@
import { describe, expect, test } from "vitest";
import type { User } from "~/types/User";
import { getUserDisplayName } from "~/utils/user";
import { getUserDisplayName, USERNAME_PATTERN, validateUsername } from "~/utils/user";
const makeUser = (overrides: Partial<User>): User => ({
id: "default-id",
@@ -56,3 +56,30 @@ describe("getUserDisplayName", () => {
expect(getUserDisplayName(user)).toBe("John Doe");
});
});
describe("validateUsername", () => {
test.each(["ab", "john_doe", "user-1.name", "alice@example.com", "josé"])(
"accepts %s",
(name) => {
expect(validateUsername(name)).toBeUndefined();
},
);
// "lm@" is the case from #502: Headscale creates it, but policies strip the
// trailing "@" so no rule can ever match the user.
// `ab²`/`abⅫ` are Nl/No, which Go's unicode.IsDigit rejects.
test.each(["", "a", "1abc", "@abc", "lm@", "a@b@c", "bad name", "bad/name", "ab²", "abⅫ"])(
"rejects %s",
(name) => {
expect(validateUsername(name)).toBeTypeOf("string");
},
);
});
describe("USERNAME_PATTERN", () => {
test("is a valid HTML pattern (unicode sets mode)", () => {
const regex = new RegExp(`^${USERNAME_PATTERN}$`, "v");
expect(regex.test("alice@example.com")).toBe(true);
expect(regex.test("lm@")).toBe(false);
});
});