import { describe, expect, test } from "vitest"; import * as CalendarEventFactory from "~/db/seed/factories/CalendarEventFactory"; import * as CalendarEventResultFactory from "~/db/seed/factories/CalendarEventResultFactory"; import * as TournamentFactory from "~/db/seed/factories/TournamentFactory"; import * as UserFactory from "~/db/seed/factories/UserFactory"; import { dateToDatabaseTimestamp } from "~/utils/dates"; import * as UserRepository from "./UserRepository.server"; describe("UserRepository", () => { test("created user has createdAt field", async () => { await UserRepository.upsert({ discordId: "1", discordName: "TestUser", discordAvatar: null, }); const user = await UserRepository.findModInfoById(1); expect(user).toBeDefined(); expect(user?.createdAt).toBeDefined(); }); test("updates user name when upserting", async () => { await UserRepository.upsert({ discordId: "1", discordName: "TestUser", discordAvatar: null, }); const user = await UserRepository.findLayoutDataByIdentifier("1"); expect(user?.username).toBe("TestUser"); await UserRepository.upsert({ discordId: "1", discordName: "UpdatedUser", discordAvatar: null, }); const updatedUser = await UserRepository.findLayoutDataByIdentifier("1"); expect(updatedUser?.username).toBe("UpdatedUser"); }); test("updating a user doesn't change the createdAt field", async () => { await UserRepository.upsert({ discordId: "1", discordName: "TestUser", discordAvatar: null, }); const user = await UserRepository.findModInfoById(1); const createdAt = user?.createdAt; await UserRepository.upsert({ discordId: "1", discordName: "UpdatedUser", discordAvatar: null, }); const updatedUser = await UserRepository.findModInfoById(1); expect(updatedUser?.createdAt).toEqual(createdAt); }); test("new user gets joinOrder of 1", async () => { const { id } = await UserRepository.upsert({ discordId: "1", discordName: "TestUser", discordAvatar: null, }); const result = await UserRepository.findJoinOrderByUserId(id); expect(result?.joinOrder).toBe(1); }); test("joinOrder increments for each new user and does not change on update", async () => { const { id: firstId } = await UserRepository.upsert({ discordId: "1", discordName: "FirstUser", discordAvatar: null, }); const { id: secondId } = await UserRepository.upsert({ discordId: "2", discordName: "SecondUser", discordAvatar: null, }); expect( (await UserRepository.findJoinOrderByUserId(firstId))?.joinOrder, ).toBe(1); expect( (await UserRepository.findJoinOrderByUserId(secondId))?.joinOrder, ).toBe(2); await UserRepository.upsert({ discordId: "1", discordName: "UpdatedFirstUser", discordAvatar: null, }); expect( (await UserRepository.findJoinOrderByUserId(firstId))?.joinOrder, ).toBe(1); }); describe("findResultsByUserId filters", () => { const startTimeOf = (year: number) => dateToDatabaseTimestamp(new Date(Date.UTC(year, 5, 1))); const seedResults = async () => { const user = await UserFactory.create(); const mate = await UserFactory.create(); const [firstOpponent, secondOpponent] = await UserFactory.createMany(2); const wonEvent = await CalendarEventFactory.create({ name: "Gamma Open", authorId: user.id, startTimes: [startTimeOf(2024)], }); await CalendarEventResultFactory.create({ eventId: wonEvent.id, participantCount: 8, results: [ { teamName: "Team Gamma", placement: 1, players: [ { userId: user.id, name: null }, { userId: mate.id, name: null }, ], }, ], }); const lostEvent = await CalendarEventFactory.create({ name: "Delta Open", authorId: user.id, startTimes: [startTimeOf(2022)], }); await CalendarEventResultFactory.create({ eventId: lostEvent.id, participantCount: 50, results: [ { teamName: "Team Delta", placement: 5, players: [ { userId: user.id, name: null }, { userId: firstOpponent.id, name: null }, ], }, ], }); await TournamentFactory.createPlayed( { name: "Alpha Invitational", authorId: user.id, startTimes: [startTimeOf(2023)], minMembersPerTeam: 1, }, { teamRosters: [[user.id], [secondOpponent.id]], playedOut: "all", tier: 1, }, ); return { userId: user.id, mateUserId: mate.id }; }; const filteredResults = async ( userId: number, filters: Parameters[1], ) => { const [results, count] = await Promise.all([ UserRepository.findResultsByUserId(userId, filters), UserRepository.countResultsByUserId(userId, filters), ]); expect(count).toBe(results.length); return results; }; test("returns every result without filters", async () => { const { userId } = await seedResults(); const results = await filteredResults(userId, {}); expect(results).toHaveLength(3); }); test("filters by result source", async () => { const { userId } = await seedResults(); const tournaments = await filteredResults(userId, { source: "SENDOU" }); const reported = await filteredResults(userId, { source: "EXTERNAL" }); expect(tournaments).toHaveLength(1); expect(tournaments[0].eventName).toBe("Alpha Invitational"); expect(reported.map((result) => result.eventName).sort()).toEqual([ "Delta Open", "Gamma Open", ]); }); test("filters by tier, excluding results without one", async () => { const { userId } = await seedResults(); const bestTiers = await filteredResults(userId, { minTier: 1, maxTier: 3, }); const worstTiers = await filteredResults(userId, { minTier: 8, maxTier: 9, }); expect(bestTiers).toHaveLength(1); expect(bestTiers[0].eventName).toBe("Alpha Invitational"); expect(worstTiers).toHaveLength(0); }); test("filters by placement", async () => { const { userId } = await seedResults(); const wins = await filteredResults(userId, { maxPlacement: 1 }); expect(wins.every((result) => result.placement === 1)).toBe(true); expect(wins.map((result) => result.eventName)).toContain("Gamma Open"); expect(wins.map((result) => result.eventName)).not.toContain( "Delta Open", ); }); test("filters by year range", async () => { const { userId } = await seedResults(); const results = await filteredResults(userId, { fromYear: 2023, toYear: 2024, }); expect(results.map((result) => result.eventName).sort()).toEqual([ "Alpha Invitational", "Gamma Open", ]); }); test("filters by teammate", async () => { const { userId, mateUserId } = await seedResults(); const results = await filteredResults(userId, { mateUserId }); expect(results).toHaveLength(1); expect(results[0].eventName).toBe("Gamma Open"); }); test("filters by team name", async () => { const { userId } = await seedResults(); const results = await filteredResults(userId, { teamName: "delta" }); expect(results).toHaveLength(1); expect(results[0].eventName).toBe("Delta Open"); }); test("filters by minimum participant count", async () => { const { userId } = await seedResults(); const results = await filteredResults(userId, { minParticipantCount: 16, }); expect(results).toHaveLength(1); expect(results[0].eventName).toBe("Delta Open"); }); test("filters by tournament name", async () => { const { userId } = await seedResults(); const results = await filteredResults(userId, { tournamentName: "alpha", }); expect(results).toHaveLength(1); expect(results[0].eventName).toBe("Alpha Invitational"); }); }); describe("userRoles", () => { test("returns empty array for basic user", async () => { await UserFactory.createAdmin(); const recentDiscordId = String( (BigInt(Date.now() - 1420070400000) << 22n) + 1n, ); const { id } = await UserFactory.create({ discordId: recentDiscordId, }); const user = await UserRepository.findLeanById(id); expect(user?.roles).toEqual([]); }); test("returns ADMIN and STAFF roles for admin user", async () => { const { id } = await UserFactory.createAdmin(); const user = await UserRepository.findLeanById(id); expect(user?.roles).toContain("ADMIN"); expect(user?.roles).toContain("STAFF"); }); test("returns MINOR_SUPPORT role for patron tier 1", async () => { const { id } = await UserFactory.create(null, { patronTier: 1 }); const user = await UserRepository.findLeanById(id); expect(user?.roles).toContain("MINOR_SUPPORT"); expect(user?.roles).not.toContain("SUPPORTER"); }); test("returns SUPPORTER, MINOR_SUPPORT, TOURNAMENT_ADDER, CALENDAR_EVENT_ADDER, and API_ACCESSER roles for patron tier 2", async () => { const { id } = await UserFactory.create(null, { patronTier: 2 }); const user = await UserRepository.findLeanById(id); expect(user?.roles).toContain("SUPPORTER"); expect(user?.roles).toContain("MINOR_SUPPORT"); expect(user?.roles).toContain("TOURNAMENT_ADDER"); expect(user?.roles).toContain("CALENDAR_EVENT_ADDER"); expect(user?.roles).toContain("API_ACCESSER"); }); test("returns PLUS_SERVER_MEMBER role for plus tier user", async () => { const { id } = await UserFactory.create(null, { plusTier: 1 }); const user = await UserRepository.findLeanById(id); expect(user?.roles).toContain("PLUS_SERVER_MEMBER"); }); test("returns ARTIST role for artist user", async () => { const { id } = await UserFactory.create(null, { roles: ["ARTIST"] }); const user = await UserRepository.findLeanById(id); expect(user?.roles).toContain("ARTIST"); }); test("returns VIDEO_ADDER role for video adder user", async () => { const { id } = await UserFactory.create(null, { roles: ["VIDEO_ADDER"] }); const user = await UserRepository.findLeanById(id); expect(user?.roles).toContain("VIDEO_ADDER"); }); test("returns TOURNAMENT_ADDER and API_ACCESSER roles for tournament organizer", async () => { const { id } = await UserFactory.create( {}, { roles: ["TOURNAMENT_ORGANIZER"] }, ); const user = await UserRepository.findLeanById(id); expect(user?.roles).toContain("TOURNAMENT_ADDER"); expect(user?.roles).toContain("API_ACCESSER"); }); test("returns API_ACCESSER role for api accesser user", async () => { const { id } = await UserFactory.create(null, { roles: ["API_ACCESSER"], }); const user = await UserRepository.findLeanById(id); expect(user?.roles).toContain("API_ACCESSER"); }); test("returns CALENDAR_EVENT_ADDER role for aged discord account", async () => { const agedDiscordId = "79237403620945921"; const { id } = await UserFactory.create({ discordId: agedDiscordId }); const user = await UserRepository.findLeanById(id); expect(user?.roles).toContain("CALENDAR_EVENT_ADDER"); }); test("does not return CALENDAR_EVENT_ADDER role for new discord account", async () => { const recentDiscordId = String( (BigInt(Date.now() - 1420070400000) << 22n) + 1n, ); const { id } = await UserFactory.create({ discordId: recentDiscordId, }); const user = await UserRepository.findLeanById(id); expect(user?.roles).not.toContain("CALENDAR_EVENT_ADDER"); }); test("returns multiple roles for user with multiple privileges", async () => { const { id } = await UserFactory.create( {}, { patronTier: 2, plusTier: 2, roles: ["ARTIST", "VIDEO_ADDER"], }, ); const user = await UserRepository.findLeanById(id); expect(user?.roles).toContain("SUPPORTER"); expect(user?.roles).toContain("MINOR_SUPPORT"); expect(user?.roles).toContain("PLUS_SERVER_MEMBER"); expect(user?.roles).toContain("ARTIST"); expect(user?.roles).toContain("VIDEO_ADDER"); expect(user?.roles).toContain("TOURNAMENT_ADDER"); expect(user?.roles).toContain("CALENDAR_EVENT_ADDER"); expect(user?.roles).toContain("API_ACCESSER"); }); }); });