fix: zero-pad username suggestion suffix consistently (#29571)
generateUsernameSuggestion built its numeric suffix with
String(rand).padStart(4 - rand.toString().length, "0"). padStart's first
argument is the target total length, not the number of pad characters, so the
expression only padded single-digit numbers: rand=1 became "001" but rand=42
became "42" and rand=999 stayed "999". Suggested usernames therefore had
inconsistent suffix widths, and the magic 4 didn't match the 3-wide output the
existing test pins ("johnny001").
Pull the suffix into a small helper that always pads to 3 digits, so collisions
and the returned value share one definition and every suggestion is uniformly
zero-padded. Add unit tests for the first-attempt path and for two- and
three-digit suffixes; the two-digit case fails on the old code.
Co-authored-by: Bandhan Majumder <133476557+bandhan-majumder@users.noreply.github.com>
This commit is contained in:
co-authored by
Bandhan Majumder
parent
ebf696a991
commit
1599dfd515
@@ -1,8 +1,8 @@
|
||||
import prismaMock from "@calcom/testing/lib/__mocks__/prismaMock";
|
||||
|
||||
import { describe, expect, it, beforeEach } from "vitest";
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
|
||||
import { usernameCheckForSignup } from "./username";
|
||||
import { generateUsernameSuggestion, usernameCheckForSignup } from "./username";
|
||||
|
||||
describe("usernameCheckForSignup ", async () => {
|
||||
beforeEach(() => {
|
||||
@@ -57,6 +57,33 @@ describe("usernameCheckForSignup ", async () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe("generateUsernameSuggestion", () => {
|
||||
afterEach(() => {
|
||||
vi.restoreAllMocks();
|
||||
});
|
||||
|
||||
it("appends a zero-padded suffix when the username is free on the first attempt", async () => {
|
||||
const suggestion = await generateUsernameSuggestion([], "john");
|
||||
expect(suggestion).toBe("john001");
|
||||
});
|
||||
|
||||
it("zero-pads a two-digit suffix to a consistent width", async () => {
|
||||
// Force the first candidate (john001) to collide so a random suffix is generated,
|
||||
// and pin Math.random so the generated suffix is the two-digit number 42.
|
||||
// username length >= 2 -> limit 999 -> rand = ceil(1 + random * 998); 40.5/998 -> ceil(41.5) = 42.
|
||||
vi.spyOn(Math, "random").mockReturnValue(40.5 / 998);
|
||||
const suggestion = await generateUsernameSuggestion(["john001"], "john");
|
||||
expect(suggestion).toBe("john042");
|
||||
});
|
||||
|
||||
it("zero-pads a three-digit suffix to a consistent width", async () => {
|
||||
// 122.5/998 -> ceil(1 + 122.5) = ceil(123.5) = 124.
|
||||
vi.spyOn(Math, "random").mockReturnValue(122.5 / 998);
|
||||
const suggestion = await generateUsernameSuggestion(["john001"], "john");
|
||||
expect(suggestion).toBe("john124");
|
||||
});
|
||||
});
|
||||
|
||||
function mockUserInDB({ id, email, username }: { id: number; email: string; username: string }) {
|
||||
// @ts-expect-error
|
||||
prismaMock.user.findUnique.mockImplementation((arg) => {
|
||||
|
||||
@@ -57,11 +57,12 @@ export const isPremiumUserName = IS_PREMIUM_USERNAME_ENABLED
|
||||
|
||||
export const generateUsernameSuggestion = async (users: string[], username: string) => {
|
||||
const limit = username.length < 2 ? 9999 : 999;
|
||||
const suffix = (value: number) => String(value).padStart(3, "0");
|
||||
let rand = 1;
|
||||
while (users.includes(username + String(rand).padStart(4 - rand.toString().length, "0"))) {
|
||||
while (users.includes(username + suffix(rand))) {
|
||||
rand = Math.ceil(1 + Math.random() * (limit - 1));
|
||||
}
|
||||
return username + String(rand).padStart(4 - rand.toString().length, "0");
|
||||
return username + suffix(rand);
|
||||
};
|
||||
|
||||
const processResult = (
|
||||
|
||||
Reference in New Issue
Block a user