Merge remote-tracking branch 'origin/master'

This commit is contained in:
Miodec 2022-05-11 14:14:17 +02:00
commit 2e95c618a5
5 changed files with 172 additions and 18 deletions

View file

@ -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
);
});
});

View file

@ -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,
},
},
};

View file

@ -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");
}

View file

@ -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 {

View file

@ -34980,6 +34980,24 @@
"source": "IT Crowd",
"length": 307,
"id": 5934
},
{
"text": "It's a long, hard, dusty road you'll journey on through a world you never known. Only time will tell you if or not if your life has been in vain. Each morning sunrise brings question and query, each sunset brings you more doubts. Always you're searching for what, you're not even sure, melting away like an icicle on fire",
"source": "Xavier: Renegade Angel",
"length": 321,
"id": 5935
},
{
"text": "Powers are for the weak. I have no powers. I mean, unless you count the power to blow minds with my weapons-grade philosophical insights.",
"source": "Xavier: Renegade Angel",
"length": 137,
"id": 5936
},
{
"text": "But would you kindly ponder this question: What would your good do if evil didn't exist, and what would the earth look like if all the shadows disappeared? After all, shadows are cast by things and people. Here is the shadow of my sword. But shadows also come from trees and living beings. Do you want to strip the earth of all trees and living things just because of your fantasy of enjoying naked light? You're stupid.",
"source": "The Master and Margarita",
"length": 420,
"id": 5937
}
]
}