test: fix flaky user tests (@fehmer) (#6837)

This commit is contained in:
Christian Fehmer 2025-08-07 14:47:35 +02:00 committed by GitHub
parent 821c640888
commit c8a91ede52
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 46 additions and 90 deletions

View file

@ -2,6 +2,9 @@ import { ObjectId } from "mongodb";
import * as BlacklistDal from "../../../src/dal/blocklist";
describe("BlocklistDal", () => {
beforeAll(async () => {
await BlacklistDal.createIndicies();
});
describe("add", () => {
beforeEach(() => {
vitest.useFakeTimers();

View file

@ -217,9 +217,9 @@ describe("LeaderboardsDal", () => {
]);
});
//TODO figure out why premium with expireTimestamp is not working
it.skip("should create leaderboard with premium", async () => {
it("should create leaderboard with premium", async () => {
//GIVEN
vi.useRealTimers(); //timestamp for premium is calculated in mongo
const noPremium = await createUser(lbBests(pb(4)));
const lifetime = await createUser(lbBests(pb(3)), premium(-1));
const validPremium = await createUser(lbBests(pb(2)), premium(1000));

View file

@ -126,22 +126,24 @@ describe("UserDal", () => {
it("isNameAvailable should correctly check if a username is available", async () => {
// given
const { uid: user1 } = await UserTestData.createUser({ name: "user1" });
await UserTestData.createUser({ name: "user2" });
const name1 = "user" + new ObjectId().toHexString();
const name2 = "user" + new ObjectId().toHexString();
const { uid: user1 } = await UserTestData.createUser({ name: name1 });
await UserTestData.createUser({ name: name2 });
const testCases = [
{
name: "user1",
name: name1,
whosChecking: user1,
expected: true,
},
{
name: "USER1",
name: name1.toUpperCase(),
whosChecking: user1,
expected: true,
},
{
name: "user2",
name: name2,
whosChecking: user1,
expected: false,
},
@ -156,8 +158,10 @@ describe("UserDal", () => {
it("updatename should not allow unavailable usernames", async () => {
// given
const user1 = await UserTestData.createUser({ name: "bob" });
const user2 = await UserTestData.createUser({ name: "kevin" });
const name1 = "user" + new ObjectId().toHexString();
const name2 = "user" + new ObjectId().toHexString();
const user1 = await UserTestData.createUser({ name: name1 });
const user2 = await UserTestData.createUser({ name: name2 });
const _decoy = await UserTestData.createUser();
// when, then
@ -167,36 +171,40 @@ describe("UserDal", () => {
});
it("same usernames (different casing) should be available only for the same user", async () => {
const user1 = await UserTestData.createUser({ name: "bob" });
const user2 = await UserTestData.createUser({ name: "kevin" });
const name1 = "user" + new ObjectId().toHexString();
const name2 = "user" + new ObjectId().toHexString();
const user1 = await UserTestData.createUser({ name: name1 });
const user2 = await UserTestData.createUser({ name: name2 });
await UserDAL.updateName(user1.uid, "BOB", user1.name);
await UserDAL.updateName(user1.uid, name1.toUpperCase(), user1.name);
const updatedUser1 = await UserDAL.getUser(user1.uid, "test");
// when, then
expect(updatedUser1.name).toBe("BOB");
expect(updatedUser1.name).toBe(name1.toUpperCase());
await expect(
UserDAL.updateName(user2.uid, "bob", user2.name)
UserDAL.updateName(user2.uid, name1, user2.name)
).rejects.toThrow("Username already taken");
});
it("UserDAL.updateName should change the name of a user", async () => {
// given
const testUser = await UserTestData.createUser({ name: "bob" });
const name = "user" + new ObjectId().toHexString();
const renamed = "renamed" + new ObjectId().toHexString();
const testUser = await UserTestData.createUser({ name: name });
// when
await UserDAL.updateName(testUser.uid, "renamedTestUser", testUser.name);
await UserDAL.updateName(testUser.uid, renamed, testUser.name);
// then
const updatedUser = await UserDAL.getUser(testUser.uid, "test");
expect(updatedUser.name).toBe("renamedTestUser");
expect(updatedUser.name).toBe(renamed);
});
it("clearPb should clear the personalBests of a user", async () => {
// given
const testUser = await UserTestData.createUser({ name: "bob" });
const testUser = await UserTestData.createUser();
await UserDAL.getUsersCollection().updateOne(
{ uid: testUser.uid },
{
@ -233,7 +241,7 @@ describe("UserDal", () => {
it("autoBan should automatically ban after configured anticheat triggers", async () => {
// given
const testUser = await UserTestData.createUser({ name: "bob" });
const testUser = await UserTestData.createUser();
// when
Date.now = vi.fn(() => 0);
@ -249,7 +257,7 @@ describe("UserDal", () => {
it("autoBan should not ban ban if triggered once", async () => {
// given
const testUser = await UserTestData.createUser({ name: "bob" });
const testUser = await UserTestData.createUser();
// when
Date.now = vi.fn(() => 0);
@ -263,7 +271,7 @@ describe("UserDal", () => {
it("autoBan should correctly remove old anticheat triggers", async () => {
// given
const testUser = await UserTestData.createUser({ name: "bob" });
const testUser = await UserTestData.createUser();
// when
Date.now = vi.fn(() => 0);
@ -1240,7 +1248,6 @@ describe("UserDal", () => {
};
let user = await UserTestData.createUser({
name: "bob",
xp: 100,
inbox: [rewardOne, rewardTwo, rewardThree, rewardFour],
});
@ -1473,17 +1480,21 @@ describe("UserDal", () => {
});
describe("isDiscordIdAvailable", () => {
it("should return true for available discordId", async () => {
await expect(UserDAL.isDiscordIdAvailable("myId")).resolves.toBe(true);
const discordId = new ObjectId().toHexString();
await expect(UserDAL.isDiscordIdAvailable(discordId)).resolves.toBe(true);
});
it("should return false if discordId is taken", async () => {
// given
const discordId = new ObjectId().toHexString();
await UserTestData.createUser({
discordId: "myId",
discordId: discordId,
});
// when, then
await expect(UserDAL.isDiscordIdAvailable("myId")).resolves.toBe(false);
await expect(UserDAL.isDiscordIdAvailable(discordId)).resolves.toBe(
false
);
});
});
describe("updateLbMemory", () => {
@ -1566,8 +1577,9 @@ describe("UserDal", () => {
it("increments bananas", async () => {
//GIVEN
const name = "user" + new ObjectId().toHexString();
const { uid } = await UserTestData.createUser({
name: "bob",
name,
bananas: 1,
personalBests: {
time: {
@ -1584,7 +1596,7 @@ describe("UserDal", () => {
await UserDAL.incrementBananas(uid, 75);
const read = await UserDAL.getUser(uid, "read");
expect(read.bananas).toEqual(2);
expect(read.name).toEqual("bob");
expect(read.name).toEqual(name);
//NOT within 25% of PB
await UserDAL.incrementBananas(uid, 74);
@ -1594,7 +1606,6 @@ describe("UserDal", () => {
it("ignores missing personalBests", async () => {
//GIVEN
const { uid } = await UserTestData.createUser({
name: "bob",
bananas: 1,
});
@ -1608,7 +1619,6 @@ describe("UserDal", () => {
it("ignores missing personalBests time", async () => {
//GIVEN
const { uid } = await UserTestData.createUser({
name: "bob",
bananas: 1,
personalBests: {} as any,
});
@ -1622,7 +1632,6 @@ describe("UserDal", () => {
it("ignores missing personalBests time 60", async () => {
//GIVEN
const { uid } = await UserTestData.createUser({
name: "bob",
bananas: 1,
personalBests: { time: {} } as any,
});
@ -1636,7 +1645,6 @@ describe("UserDal", () => {
it("ignores empty personalBests time 60", async () => {
//GIVEN
const { uid } = await UserTestData.createUser({
name: "bob",
bananas: 1,
personalBests: { time: { "60": [] } } as any,
});
@ -1650,7 +1658,6 @@ describe("UserDal", () => {
it("should increment missing bananas", async () => {
//GIVEN
const { uid } = await UserTestData.createUser({
name: "bob",
personalBests: { time: { "60": [{ wpm: 100 }] } } as any,
});
@ -1869,7 +1876,6 @@ describe("UserDal", () => {
it("addFavoriteQuote success", async () => {
// given
const { uid } = await UserTestData.createUser({
name: "bob",
favoriteQuotes: {
english: ["1"],
german: ["2"],
@ -1882,9 +1888,6 @@ describe("UserDal", () => {
// then
const read = await UserDAL.getUser(uid, "read");
expect(read.name).toEqual("bob");
expect(read).not.toHaveProperty("tmp");
expect(read.favoriteQuotes).toStrictEqual({
english: ["1", "4"],
german: ["2"],
@ -1895,7 +1898,6 @@ describe("UserDal", () => {
it("should not add a quote twice", async () => {
// given
const { uid } = await UserTestData.createUser({
name: "bob",
favoriteQuotes: {
english: ["1", "3", "4"],
german: ["2"],
@ -1906,8 +1908,6 @@ describe("UserDal", () => {
// then
const read = await UserDAL.getUser(uid, "read");
expect(read.name).toEqual("bob");
expect(read).not.toHaveProperty("tmp");
expect(read.favoriteQuotes).toStrictEqual({
english: ["1", "3", "4"],

View file

@ -1,8 +1,4 @@
import { GenericContainer, StartedTestContainer, Wait } from "testcontainers";
import { getConnection } from "../../src/init/redis";
//enable the test, will be skipped otherwise
process.env["INTEGRATION_TESTS"] = "true";
let startedMongoContainer: StartedTestContainer | undefined;
let startedRedisContainer: StartedTestContainer | undefined;
@ -11,12 +7,8 @@ export async function setup(): Promise<void> {
process.env.TZ = "UTC";
//use testcontainer to start mongodb
//const network = await new Network(new RandomUuid()).start();
const mongoContainer = new GenericContainer("mongo:5.0.13")
//.withName("monkeytype-mongo-test")
.withExposedPorts(27017)
// .withNetwork(network)
//.withNetworkMode(network.getName())
.withWaitStrategy(Wait.forListeningPorts());
startedMongoContainer = await mongoContainer.start();
@ -41,7 +33,5 @@ export async function setup(): Promise<void> {
export async function teardown(): Promise<void> {
await startedMongoContainer?.stop();
await getConnection()?.quit();
await startedRedisContainer?.stop();
}

View file

@ -1,14 +1,10 @@
import { Collection, Db, MongoClient, WithId } from "mongodb";
import { afterAll, beforeAll, afterEach } from "vitest";
import { setupCommonMocks } from "../setup-common-mocks";
import { getConnection } from "../../src/init/redis";
process.env["MODE"] = "dev";
if (!process.env["REDIS_URI"]) {
// use mock if not set
process.env["REDIS_URI"] = "redis://mock";
}
let db: Db;
let client: MongoClient;
@ -40,5 +36,8 @@ afterAll(async () => {
db = undefined;
//@ts-ignore
client = undefined;
await getConnection()?.quit();
vi.resetAllMocks();
});

View file

@ -93,7 +93,6 @@
"concurrently": "8.2.2",
"eslint": "8.57.1",
"eslint-watch": "8.0.0",
"ioredis-mock": "7.4.0",
"openapi3-ts": "2.0.2",
"oxlint": "1.8.0",
"readline-sync": "1.4.10",

35
pnpm-lock.yaml generated
View file

@ -246,9 +246,6 @@ importers:
eslint-watch:
specifier: 8.0.0
version: 8.0.0(eslint@8.57.1)
ioredis-mock:
specifier: 7.4.0
version: 7.4.0(ioredis@4.28.5)
openapi3-ts:
specifier: 2.0.2
version: 2.0.2
@ -5171,14 +5168,6 @@ packages:
fecha@4.2.3:
resolution: {integrity: sha512-OP2IUU6HeYKJi3i0z4A19kHMQoLVs4Hc+DPqqxI2h/DPZHTm/vjsfC6P0b4jCMy14XizLBqvndQ+UilD7707Jw==}
fengari-interop@0.1.3:
resolution: {integrity: sha512-EtZ+oTu3kEwVJnoymFPBVLIbQcCoy9uWCVnMA6h3M/RqHkUBsLYp29+RRHf9rKr6GwjubWREU1O7RretFIXjHw==}
peerDependencies:
fengari: ^0.1.0
fengari@0.1.4:
resolution: {integrity: sha512-6ujqUuiIYmcgkGz8MGAdERU57EIluGGPSUgGPTsco657EHa+srq0S3/YUl/r9kx1+D+d4rGfYObd+m8K22gB1g==}
figures@3.2.0:
resolution: {integrity: sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==}
engines: {node: '>=8'}
@ -5975,12 +5964,6 @@ packages:
resolution: {integrity: sha512-xgs2NH9AE66ucSq4cNG1nhSFghr5l6tdL15Pk+jl46bmmBapgoaY/AacXyaDznAqmGL99TiLSQgO/XazFSKYeQ==}
engines: {node: '>=0.10.0'}
ioredis-mock@7.4.0:
resolution: {integrity: sha512-jcNG+9YjjBA1p6Hb1nYaC1yhW+n9S5VOgbGZXt59ZmtI2WrPWH+lSD4gE017uaGitPqW7tquFdAfcBPvFEQbew==}
engines: {node: '>=12'}
peerDependencies:
ioredis: 4.x || 5.x
ioredis@4.28.5:
resolution: {integrity: sha512-3GYo0GJtLqgNXj4YhrisLaNNvWSNwSS2wS4OELGfGxH8I69+XfNdnmV1AyN+ZqMh0i7eX+SWjrwFKDBDgfBC1A==}
engines: {node: '>=6'}
@ -15761,16 +15744,6 @@ snapshots:
fecha@4.2.3: {}
fengari-interop@0.1.3(fengari@0.1.4):
dependencies:
fengari: 0.1.4
fengari@0.1.4:
dependencies:
readline-sync: 1.4.10
sprintf-js: 1.1.3
tmp: 0.0.33
figures@3.2.0:
dependencies:
escape-string-regexp: 1.0.5
@ -16896,14 +16869,6 @@ snapshots:
invert-kv@1.0.0: {}
ioredis-mock@7.4.0(ioredis@4.28.5):
dependencies:
fengari: 0.1.4
fengari-interop: 0.1.3(fengari@0.1.4)
ioredis: 4.28.5
redis-commands: 1.7.0
standard-as-callback: 2.1.0
ioredis@4.28.5:
dependencies:
cluster-key-slot: 1.1.2