mirror of
https://github.com/monkeytypegame/monkeytype.git
synced 2025-11-02 22:01:09 +08:00
Add more tests (#2965) Bruception and sondrekje
* Cover updateName * Test clearPb, export getUsersCollection for testing purposes * Add base coverage * Month -> thirty days Co-authored-by: Sondre Kjempekjenn <sondre.kjempekjenn@gmail.com>
This commit is contained in:
parent
526a343e12
commit
0a371b531a
4 changed files with 154 additions and 18 deletions
|
|
@ -1,30 +1,55 @@
|
|||
import { addUser, getUser, updateName } from "../../src/dal/user";
|
||||
import _ from "lodash";
|
||||
import {
|
||||
addUser,
|
||||
clearPb,
|
||||
getUser,
|
||||
getUsersCollection,
|
||||
updateName,
|
||||
} from "../../src/dal/user";
|
||||
|
||||
const mockPersonalBest = {
|
||||
acc: 1,
|
||||
consistency: 1,
|
||||
difficulty: "normal" as const,
|
||||
lazyMode: true,
|
||||
language: "no",
|
||||
punctuation: false,
|
||||
raw: 230,
|
||||
wpm: 215,
|
||||
timestamp: 13123123,
|
||||
};
|
||||
|
||||
describe("UserDal", () => {
|
||||
it("should be able to insert users", async () => {
|
||||
// given
|
||||
const newUser = {
|
||||
name: "Test",
|
||||
email: "mockemail@email.com",
|
||||
uid: "userId",
|
||||
};
|
||||
|
||||
// when
|
||||
await addUser(newUser.name, newUser.email, newUser.uid);
|
||||
const insertedUser = await getUser("userId", "test");
|
||||
|
||||
// then
|
||||
expect(insertedUser.email).toBe(newUser.email);
|
||||
expect(insertedUser.uid).toBe(newUser.uid);
|
||||
expect(insertedUser.name).toBe(newUser.name);
|
||||
});
|
||||
|
||||
it("should error if the user already exists", async () => {
|
||||
// given
|
||||
const newUser = {
|
||||
name: "Test",
|
||||
email: "mockemail@email.com",
|
||||
uid: "userId",
|
||||
};
|
||||
|
||||
// when
|
||||
await addUser(newUser.name, newUser.email, newUser.uid);
|
||||
|
||||
// then
|
||||
// should error because user already exists
|
||||
await expect(
|
||||
addUser(newUser.name, newUser.email, newUser.uid)
|
||||
|
|
@ -32,6 +57,7 @@ describe("UserDal", () => {
|
|||
});
|
||||
|
||||
it("updatename should not allow unavailable usernames", async () => {
|
||||
// given
|
||||
const mockUsers = [...Array(3).keys()]
|
||||
.map((id) => ({
|
||||
name: `Test${id}`,
|
||||
|
|
@ -39,14 +65,125 @@ describe("UserDal", () => {
|
|||
uid: `userId${id}`,
|
||||
}))
|
||||
.map(({ name, email, uid }) => addUser(name, email, uid));
|
||||
|
||||
await Promise.all(mockUsers);
|
||||
|
||||
const userToUpdateNameFor = await getUser("userId0", "test");
|
||||
const userWithNameTaken = await getUser("userId1", "test");
|
||||
|
||||
// when, then
|
||||
await expect(
|
||||
updateName(userToUpdateNameFor.uid, userWithNameTaken.name)
|
||||
).rejects.toThrow("Username already taken");
|
||||
});
|
||||
|
||||
it("updatename should not allow invalid usernames", async () => {
|
||||
// given
|
||||
const testUser = {
|
||||
name: "Test",
|
||||
email: "mockemail@email.com",
|
||||
uid: "userId",
|
||||
};
|
||||
|
||||
await addUser(testUser.name, testUser.email, testUser.uid);
|
||||
|
||||
const invalidNames = [
|
||||
null, // falsy
|
||||
undefined, // falsy
|
||||
"", // empty
|
||||
" ".repeat(16), // too long
|
||||
".testName", // cant begin with period
|
||||
"miodec", // profanity
|
||||
"asdasdAS$", // invalid characters
|
||||
];
|
||||
|
||||
// when, then
|
||||
invalidNames.forEach(
|
||||
async (invalidName) =>
|
||||
await expect(
|
||||
updateName(testUser.uid, invalidName as unknown as string)
|
||||
).rejects.toThrow("Invalid username")
|
||||
);
|
||||
});
|
||||
|
||||
it("updateName should fail if user has changed name recently", async () => {
|
||||
// given
|
||||
const testUser = {
|
||||
name: "Test",
|
||||
email: "mockemail@email.com",
|
||||
uid: "userId",
|
||||
};
|
||||
|
||||
await addUser(testUser.name, testUser.email, testUser.uid);
|
||||
|
||||
// when
|
||||
await updateName(testUser.uid, "renamedTestUser");
|
||||
|
||||
const updatedUser = await getUser(testUser.uid, "test");
|
||||
|
||||
// then
|
||||
expect(updatedUser.name).toBe("renamedTestUser");
|
||||
|
||||
await expect(updateName(updatedUser.uid, "NewValidName")).rejects.toThrow(
|
||||
"You can change your name once every 30 days"
|
||||
);
|
||||
});
|
||||
|
||||
it("updateName should change the name of a user", async () => {
|
||||
// given
|
||||
const testUser = {
|
||||
name: "Test",
|
||||
email: "mockemail@email.com",
|
||||
uid: "userId",
|
||||
};
|
||||
|
||||
await addUser(testUser.name, testUser.email, testUser.uid);
|
||||
|
||||
// when
|
||||
await updateName(testUser.uid, "renamedTestUser");
|
||||
|
||||
// then
|
||||
const updatedUser = await getUser(testUser.uid, "test");
|
||||
expect(updatedUser.name).toBe("renamedTestUser");
|
||||
});
|
||||
|
||||
it("clearPb should clear the personalBests of a user", async () => {
|
||||
// given
|
||||
const testUser = {
|
||||
name: "Test",
|
||||
email: "mockemail@email.com",
|
||||
uid: "userId",
|
||||
};
|
||||
await addUser(testUser.name, testUser.email, testUser.uid);
|
||||
await getUsersCollection().updateOne(
|
||||
{ uid: testUser.uid },
|
||||
{
|
||||
$set: {
|
||||
personalBests: {
|
||||
time: { 20: [mockPersonalBest] },
|
||||
words: {},
|
||||
quote: {},
|
||||
custom: {},
|
||||
zen: {},
|
||||
},
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
const { personalBests } = (await getUser(testUser.uid, "test")) ?? {};
|
||||
expect(personalBests).toStrictEqual({
|
||||
time: { 20: [mockPersonalBest] },
|
||||
words: {},
|
||||
quote: {},
|
||||
custom: {},
|
||||
zen: {},
|
||||
});
|
||||
// when
|
||||
await clearPb(testUser.uid);
|
||||
|
||||
// then
|
||||
const updatedUser = (await getUser(testUser.uid, "test")) ?? {};
|
||||
expect(_.values(updatedUser.personalBests).filter(_.isEmpty)).toHaveLength(
|
||||
5
|
||||
);
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -6,10 +6,10 @@ export default {
|
|||
setupFilesAfterEnv: ["<rootDir>/setup-tests.ts"],
|
||||
coverageThreshold: {
|
||||
global: {
|
||||
branches: 0,
|
||||
functions: 0,
|
||||
lines: 0,
|
||||
statements: 0,
|
||||
branches: 36,
|
||||
functions: 18,
|
||||
lines: 39,
|
||||
statements: 35,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
|
|
|||
|
|
@ -13,15 +13,9 @@ import {
|
|||
WithId,
|
||||
} from "mongodb";
|
||||
|
||||
let usersCollection: Collection<WithId<MonkeyTypes.User>>;
|
||||
|
||||
function getUsersCollection(): Collection<WithId<MonkeyTypes.User>> {
|
||||
if (!usersCollection) {
|
||||
usersCollection = db.collection<MonkeyTypes.User>("users");
|
||||
}
|
||||
|
||||
return usersCollection;
|
||||
}
|
||||
// Export for use in tests
|
||||
export const getUsersCollection = (): Collection<WithId<MonkeyTypes.User>> =>
|
||||
db.collection<MonkeyTypes.User>("users");
|
||||
|
||||
export async function addUser(
|
||||
name: string,
|
||||
|
|
@ -47,6 +41,9 @@ export async function deleteUser(uid: string): Promise<DeleteResult> {
|
|||
return await getUsersCollection().deleteOne({ uid });
|
||||
}
|
||||
|
||||
const DAY_IN_SECONDS = 24 * 60 * 60;
|
||||
const THIRTY_DAYS_IN_SECONDS = DAY_IN_SECONDS * 30;
|
||||
|
||||
export async function updateName(
|
||||
uid: string,
|
||||
name: string
|
||||
|
|
@ -62,7 +59,7 @@ export async function updateName(
|
|||
|
||||
if (
|
||||
!user?.needsToChangeName &&
|
||||
Date.now() - (user.lastNameChange ?? 0) < 2592000000
|
||||
Date.now() - (user.lastNameChange ?? 0) < THIRTY_DAYS_IN_SECONDS
|
||||
) {
|
||||
throw new MonkeyError(409, "You can change your name once every 30 days");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,6 +5,8 @@ export function inRange(value: number, min: number, max: number): boolean {
|
|||
return value >= min && value <= max;
|
||||
}
|
||||
|
||||
const VALID_NAME_PATTERN = /^[\da-zA-Z_.-]+$/;
|
||||
|
||||
export function isUsernameValid(name: string): boolean {
|
||||
if (_.isNil(name) || !inRange(name.length, 1, 16)) {
|
||||
return false;
|
||||
|
|
@ -24,7 +26,7 @@ export function isUsernameValid(name: string): boolean {
|
|||
return false;
|
||||
}
|
||||
|
||||
return /^[0-9a-zA-Z_.-]+$/.test(name);
|
||||
return VALID_NAME_PATTERN.test(name);
|
||||
}
|
||||
|
||||
export function isTagPresetNameValid(name: string): boolean {
|
||||
|
|
@ -32,7 +34,7 @@ export function isTagPresetNameValid(name: string): boolean {
|
|||
return false;
|
||||
}
|
||||
|
||||
return /^[0-9a-zA-Z_.-]+$/.test(name);
|
||||
return VALID_NAME_PATTERN.test(name);
|
||||
}
|
||||
|
||||
export function isTestTooShort(result: MonkeyTypes.CompletedEvent): boolean {
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue