diff --git a/app/db/seed/core/defineFactory.ts b/app/db/seed/core/defineFactory.ts index 46c210ba0..59540bc89 100644 --- a/app/db/seed/core/defineFactory.ts +++ b/app/db/seed/core/defineFactory.ts @@ -5,10 +5,11 @@ type RequiredArgs = Omit; type CreateArgs = RequiredArgs & Partial; +/** `null` says "all defaults", for when only `options` is of interest. */ type CreateParams = [ keyof RequiredArgs, ] extends [never] - ? [overrides?: Partial, options?: Options] + ? [overrides?: Partial | null, options?: Options] : [overrides: CreateArgs, options?: Options]; type CreateManyParams = [ @@ -16,7 +17,7 @@ type CreateManyParams = [ ] extends [never] ? [ count: number, - overrides?: ManyOverrides, + overrides?: ManyOverrides | null, options?: Options, ] : [ @@ -135,7 +136,7 @@ export function resetFactories() { } function overridesAt( - overrides: ManyOverrides | undefined, + overrides: ManyOverrides | null | undefined, index: number, ): Partial { if (!overrides) return {}; diff --git a/app/db/seed/factories/UserFactory.ts b/app/db/seed/factories/UserFactory.ts index c4c899d14..4957b0f18 100644 --- a/app/db/seed/factories/UserFactory.ts +++ b/app/db/seed/factories/UserFactory.ts @@ -1,4 +1,9 @@ +import { add } from "date-fns"; +import { db } from "~/db/sql"; +import type { Tables } from "~/db/tables"; +import * as AdminRepository from "~/features/admin/AdminRepository.server"; import { ADMIN_ID } from "~/features/admin/admin-constants"; +import * as BuildRepository from "~/features/builds/BuildRepository.server"; import * as UserRepository from "~/features/user-page/UserRepository.server"; import invariant from "~/utils/invariant"; import { REGULAR_USER_TEST_ID } from "../constants"; @@ -8,6 +13,24 @@ import { pinUserId } from "../core/pinUserId"; type UpsertArgs = Parameters[0]; +type Role = "ARTIST" | "VIDEO_ADDER" | "TOURNAMENT_ORGANIZER" | "API_ACCESSER"; + +type Options = { + plusTier?: Tables["PlusTier"]["tier"]; + /** Patron who started supporting now and has a year left of it. */ + patronTier?: NonNullable; + roles?: Array; +}; + +const PATRONAGE_LENGTH = { years: 1 }; + +const GRANT_ROLE: Record Promise> = { + ARTIST: AdminRepository.makeArtistByUserId, + VIDEO_ADDER: AdminRepository.makeVideoAdderByUserId, + TOURNAMENT_ORGANIZER: AdminRepository.makeTournamentOrganizerByUserId, + API_ACCESSER: AdminRepository.makeApiAccesserByUserId, +}; + /** Creates users. Columns outside `UserRepository.upsert` (profile fields, patron * status, plus tier) are set by the repository function that owns them. */ export const { create, createMany } = defineFactory({ @@ -21,6 +44,7 @@ export const { create, createMany } = defineFactory({ bsky: null, }), insert: UserRepository.upsert, + applyOptions: (user, options: Options) => grantPrivileges(user.id, options), }); /** @@ -29,22 +53,41 @@ export const { create, createMany } = defineFactory({ * * Has to be created before any other user, see {@link pinUserId}. */ -export async function createAdmin(overrides?: Partial) { +export async function createAdmin( + overrides?: Partial | null, + options?: Options, +) { const user = await create(overrides); + const id = await pinUserId(user.id, ADMIN_ID); - return { id: await pinUserId(user.id, ADMIN_ID) }; + // after pinning, so that rows keyed by the user id don't point at the old one + if (options) { + await grantPrivileges(id, options); + } + + return { id }; } /** * Creates the user that `wrappedAction({ user: "regular" })` submits as. Has no - * permissions of any kind; use for the "somebody else" side of a permission test. + * permissions of any kind unless `options` gives it some; use for the "somebody + * else" side of a permission test. * * Has to be created before any user without a pinned id, see {@link pinUserId}. */ -export async function createRegular(overrides?: Partial) { +export async function createRegular( + overrides?: Partial | null, + options?: Options, +) { const user = await create(overrides); + const id = await pinUserId(user.id, REGULAR_USER_TEST_ID); - return { id: await pinUserId(user.id, REGULAR_USER_TEST_ID) }; + // after pinning, so that rows keyed by the user id don't point at the old one + if (options) { + await grantPrivileges(id, options); + } + + return { id }; } /** @@ -87,3 +130,43 @@ export function pool() { }, }; } + +async function grantPrivileges( + userId: number, + { plusTier, patronTier, roles }: Options, +) { + if (typeof plusTier === "number") { + await setPlusTier(userId, plusTier); + } + + if (typeof patronTier === "number") { + await AdminRepository.forcePatron({ + id: userId, + patronTier, + patronStartedAt: new Date(), + patronExpiresAt: add(new Date(), PATRONAGE_LENGTH), + }); + } + + for (const role of roles ?? []) { + await GRANT_ROLE[role](userId); + } +} + +async function setPlusTier(userId: number, plusTier: number) { + // `replacePlusTiers` replaces every row, being the monthly recount of who is in + // what tier, so the tiers granted before this one are read back and sent along + const others = await db + .selectFrom("PlusTier") + .select(["userId", "tier"]) + .where("userId", "!=", userId) + .execute(); + + await AdminRepository.replacePlusTiers([ + ...others.map((other) => ({ userId: other.userId, plusTier: other.tier })), + { userId, plusTier }, + ]); + + // xxx: should be in replacePlusTiers etc.? + await BuildRepository.recalculateAllSortValues(userId); +} diff --git a/app/features/api/core/perms.test.ts b/app/features/api/core/perms.test.ts index 6a5420d9d..215b319d2 100644 --- a/app/features/api/core/perms.test.ts +++ b/app/features/api/core/perms.test.ts @@ -1,7 +1,5 @@ -import { add } from "date-fns"; -import { afterEach, beforeEach, describe, expect, test } from "vitest"; +import { afterEach, describe, expect, test } from "vitest"; import * as UserFactory from "~/db/seed/factories/UserFactory"; -import * as AdminRepository from "~/features/admin/AdminRepository.server"; import * as TournamentOrganizationRepository from "~/features/tournament-organization/TournamentOrganizationRepository.server"; import * as UserRepository from "~/features/user-page/UserRepository.server"; import { dbReset } from "~/utils/Test"; @@ -9,23 +7,17 @@ import * as ApiRepository from "../ApiRepository.server"; import { checkUserHasApiAccess } from "./perms"; describe("Permission logic consistency between findAllApiTokens and checkUserHasApiAccess", () => { - const users = UserFactory.pool(); - - beforeEach(async () => { - await users.create(4); - }); - afterEach(async () => { await dbReset(); }); test("both functions grant access for isApiAccesser flag", async () => { - await AdminRepository.makeApiAccesserByUserId(users.id(1)); + const { id } = await UserFactory.create(null, { roles: ["API_ACCESSER"] }); - await ApiRepository.generateToken(users.id(1), "read"); + await ApiRepository.generateToken(id, "read"); const tokens = await ApiRepository.findAllApiTokens(); - const user = await UserRepository.findLeanById(users.id(1)); + const user = await UserRepository.findLeanById(id); const hasAccess = await checkUserHasApiAccess(user!); expect(tokens).toHaveLength(1); @@ -33,12 +25,15 @@ describe("Permission logic consistency between findAllApiTokens and checkUserHas }); test("both functions grant access for isTournamentOrganizer flag", async () => { - await AdminRepository.makeTournamentOrganizerByUserId(users.id(1)); + const { id } = await UserFactory.create( + {}, + { roles: ["TOURNAMENT_ORGANIZER"] }, + ); - await ApiRepository.generateToken(users.id(1), "read"); + await ApiRepository.generateToken(id, "read"); const tokens = await ApiRepository.findAllApiTokens(); - const user = await UserRepository.findLeanById(users.id(1)); + const user = await UserRepository.findLeanById(id); const hasAccess = await checkUserHasApiAccess(user!); expect(tokens).toHaveLength(1); @@ -46,17 +41,12 @@ describe("Permission logic consistency between findAllApiTokens and checkUserHas }); test("both functions grant access for patronTier >= 2", async () => { - await AdminRepository.forcePatron({ - id: users.id(1), - patronTier: 2, - patronStartedAt: new Date(), - patronExpiresAt: add(new Date(), { months: 3 }), - }); + const { id } = await UserFactory.create(null, { patronTier: 2 }); - await ApiRepository.generateToken(users.id(1), "read"); + await ApiRepository.generateToken(id, "read"); const tokens = await ApiRepository.findAllApiTokens(); - const user = await UserRepository.findLeanById(users.id(1)); + const user = await UserRepository.findLeanById(id); const hasAccess = await checkUserHasApiAccess(user!); expect(tokens).toHaveLength(1); @@ -64,17 +54,12 @@ describe("Permission logic consistency between findAllApiTokens and checkUserHas }); test("both functions deny access for patronTier < 2", async () => { - await AdminRepository.forcePatron({ - id: users.id(1), - patronTier: 1, - patronStartedAt: new Date(), - patronExpiresAt: add(new Date(), { months: 3 }), - }); + const { id } = await UserFactory.create(null, { patronTier: 1 }); - await ApiRepository.generateToken(users.id(1), "read"); + await ApiRepository.generateToken(id, "read"); const tokens = await ApiRepository.findAllApiTokens(); - const user = await UserRepository.findLeanById(users.id(1)); + const user = await UserRepository.findLeanById(id); const hasAccess = await checkUserHasApiAccess(user!); expect(tokens).toHaveLength(0); @@ -82,8 +67,10 @@ describe("Permission logic consistency between findAllApiTokens and checkUserHas }); test("both functions grant access for ADMIN/ORGANIZER/STREAMER of established org", async () => { + const [owner, admin, organizer, streamer] = await UserFactory.createMany(4); + const org = await TournamentOrganizationRepository.insert({ - ownerId: users.id(1), + ownerId: owner.id, name: "Test Org", }); @@ -91,14 +78,13 @@ describe("Permission logic consistency between findAllApiTokens and checkUserHas const orgData = await TournamentOrganizationRepository.findBySlug(org.slug); - for (const role of ["ADMIN", "ORGANIZER", "STREAMER"] as const) { - const userId = - role === "ADMIN" - ? users.id(2) - : role === "ORGANIZER" - ? users.id(3) - : users.id(4); + const membersByRole = [ + { role: "ADMIN", userId: admin.id }, + { role: "ORGANIZER", userId: organizer.id }, + { role: "STREAMER", userId: streamer.id }, + ] as const; + for (const { role, userId } of membersByRole) { await TournamentOrganizationRepository.update({ id: org.id, name: orgData!.name, @@ -121,8 +107,10 @@ describe("Permission logic consistency between findAllApiTokens and checkUserHas }); test("both functions deny access for MEMBER of established org", async () => { + const [owner, member] = await UserFactory.createMany(2); + const org = await TournamentOrganizationRepository.insert({ - ownerId: users.id(1), + ownerId: owner.id, name: "Test Org", }); @@ -134,15 +122,15 @@ describe("Permission logic consistency between findAllApiTokens and checkUserHas name: orgData!.name, description: orgData!.description, socials: orgData!.socials, - members: [{ userId: users.id(2), role: "MEMBER", roleDisplayName: null }], + members: [{ userId: member.id, role: "MEMBER", roleDisplayName: null }], series: [], badges: [], }); - await ApiRepository.generateToken(users.id(2), "read"); + await ApiRepository.generateToken(member.id, "read"); const tokens = await ApiRepository.findAllApiTokens(); - const user = await UserRepository.findLeanById(users.id(2)); + const user = await UserRepository.findLeanById(member.id); const hasAccess = await checkUserHasApiAccess(user!); expect(tokens).toHaveLength(0); @@ -150,8 +138,10 @@ describe("Permission logic consistency between findAllApiTokens and checkUserHas }); test("both functions deny access for ADMIN of non-established org", async () => { + const [owner, member] = await UserFactory.createMany(2); + const org = await TournamentOrganizationRepository.insert({ - ownerId: users.id(1), + ownerId: owner.id, name: "Test Org", }); @@ -161,15 +151,15 @@ describe("Permission logic consistency between findAllApiTokens and checkUserHas name: orgData!.name, description: orgData!.description, socials: orgData!.socials, - members: [{ userId: users.id(2), role: "ADMIN", roleDisplayName: null }], + members: [{ userId: member.id, role: "ADMIN", roleDisplayName: null }], series: [], badges: [], }); - await ApiRepository.generateToken(users.id(2), "read"); + await ApiRepository.generateToken(member.id, "read"); const tokens = await ApiRepository.findAllApiTokens(); - const user = await UserRepository.findLeanById(users.id(2)); + const user = await UserRepository.findLeanById(member.id); const hasAccess = await checkUserHasApiAccess(user!); expect(tokens).toHaveLength(0); diff --git a/app/features/builds/BuildRepository.server.test.ts b/app/features/builds/BuildRepository.server.test.ts index fb4bff376..2d6221559 100644 --- a/app/features/builds/BuildRepository.server.test.ts +++ b/app/features/builds/BuildRepository.server.test.ts @@ -6,7 +6,6 @@ import type { MainWeaponId, } from "~/modules/in-game-lists/types"; import { dbReset } from "~/utils/Test"; -import * as AdminRepository from "../admin/AdminRepository.server"; import * as BuildRepository from "./BuildRepository.server"; let owner: { id: number }; @@ -218,22 +217,20 @@ describe("BuildRepository.insert — computeBuildData", () => { }); test("uses owner's PlusTier (tier 2 → sortValue = 5)", async () => { - await AdminRepository.replacePlusTiers([ - { userId: owner.id, plusTier: 2 }, - ]); + const plusOwner = await UserFactory.create(null, { plusTier: 2 }); - await BuildRepository.insert(baseArgs()); + await BuildRepository.insert(baseArgs({ ownerId: plusOwner.id })); const [weapon] = await buildWeaponsByBuildId(await onlyBuildId()); expect(weapon.sortValue).toBe(5); }); test("is null for private builds regardless of tier", async () => { - await AdminRepository.replacePlusTiers([ - { userId: owner.id, plusTier: 1 }, - ]); + const plusOwner = await UserFactory.create(null, { plusTier: 1 }); - await BuildRepository.insert(baseArgs({ isPrivate: 1 })); + await BuildRepository.insert( + baseArgs({ ownerId: plusOwner.id, isPrivate: 1 }), + ); const [weapon] = await buildWeaponsByBuildId(await onlyBuildId()); expect(weapon.sortValue).toBeNull(); @@ -258,13 +255,11 @@ describe("BuildRepository.insert — computeBuildData", () => { }); test("combines top500 with the owner's PlusTier", async () => { - await AdminRepository.replacePlusTiers([ - { userId: owner.id, plusTier: 1 }, - ]); - const playerId = await insertSplatoonPlayer(owner.id, "owner-spl-id"); + const plusOwner = await UserFactory.create(null, { plusTier: 1 }); + const playerId = await insertSplatoonPlayer(plusOwner.id, "owner-spl-id"); await insertXRankPlacement(playerId, SPLATTERSHOT, 1); - await BuildRepository.insert(baseArgs()); + await BuildRepository.insert(baseArgs({ ownerId: plusOwner.id })); const [weapon] = await buildWeaponsByBuildId(await onlyBuildId()); expect(weapon.sortValue).toBe(2); diff --git a/app/features/team/actions/t.$customUrl.edit.server.test.ts b/app/features/team/actions/t.$customUrl.edit.server.test.ts index 0b8b0d755..0aae6dbf0 100644 --- a/app/features/team/actions/t.$customUrl.edit.server.test.ts +++ b/app/features/team/actions/t.$customUrl.edit.server.test.ts @@ -2,7 +2,6 @@ import { afterEach, beforeEach, describe, expect, it } from "vitest"; import { REGULAR_USER_TEST_ID } from "~/db/seed/constants"; import * as ImageFactory from "~/db/seed/factories/ImageFactory"; import * as UserFactory from "~/db/seed/factories/UserFactory"; -import * as AdminRepository from "~/features/admin/AdminRepository.server"; import * as ImageRepository from "~/features/img-upload/ImageRepository.server"; import * as TeamRepository from "~/features/team/TeamRepository.server"; import invariant from "~/utils/invariant"; @@ -51,17 +50,10 @@ const VALID_CUSTOM_THEME = { const expectedStoredTheme = () => JSON.parse(JSON.stringify(clampThemeToGamut(VALID_CUSTOM_THEME))); -const makeUserPatron = () => - AdminRepository.forcePatron({ - id: REGULAR_USER_TEST_ID, - patronTier: 2, - patronStartedAt: new Date(), - patronExpiresAt: new Date(Date.now() + 1000 * 60 * 60 * 24), - }); - describe("team page editing", () => { beforeEach(async () => { - await UserFactory.createRegular(); + // a patron because setting a custom theme is a patron only feature + await UserFactory.createRegular(null, { patronTier: 2 }); await createTeamAction({ name: "Team 1" }, { user: "regular" }); }); afterEach(async () => { @@ -69,8 +61,6 @@ describe("team page editing", () => { }); it("sets a custom theme via UPDATE_CUSTOM_THEME", async () => { - await makeUserPatron(); - const response = await editTeamProfileAction( { _action: "UPDATE_CUSTOM_THEME", @@ -86,8 +76,6 @@ describe("team page editing", () => { }); it("clears a custom theme via UPDATE_CUSTOM_THEME with null", async () => { - await makeUserPatron(); - await editTeamProfileAction( { _action: "UPDATE_CUSTOM_THEME", @@ -111,8 +99,6 @@ describe("team page editing", () => { }); it("prevents setting an invalid custom theme", async () => { - await makeUserPatron(); - const response = await editTeamProfileAction( { _action: "UPDATE_CUSTOM_THEME", @@ -128,8 +114,6 @@ describe("team page editing", () => { }); it("preserves an existing custom theme when editing the team profile", async () => { - await makeUserPatron(); - await editTeamProfileAction( { _action: "UPDATE_CUSTOM_THEME", diff --git a/app/features/team/routes/t.$customUrl.test.ts b/app/features/team/routes/t.$customUrl.test.ts index 08e72d169..fab2fc3ef 100644 --- a/app/features/team/routes/t.$customUrl.test.ts +++ b/app/features/team/routes/t.$customUrl.test.ts @@ -1,7 +1,6 @@ import { afterEach, beforeEach, describe, expect, it } from "vitest"; import { REGULAR_USER_TEST_ID } from "~/db/seed/constants"; import * as UserFactory from "~/db/seed/factories/UserFactory"; -import * as AdminRepository from "~/features/admin/AdminRepository.server"; import invariant from "~/utils/invariant"; import { assertResponseErrored, @@ -175,18 +174,17 @@ describe("Secondary teams", () => { assertResponseErrored(response); }); +}); - const makeUserPatron = () => - AdminRepository.forcePatron({ - id: REGULAR_USER_TEST_ID, - patronTier: 2, - patronStartedAt: new Date(), - patronExpiresAt: new Date(Date.now() + 1000 * 60 * 60 * 24), - }); +describe("Secondary teams as patron", () => { + beforeEach(async () => { + await UserFactory.createRegular(null, { patronTier: 2 }); + }); + afterEach(async () => { + await dbReset(); + }); it("creates more than 2 teams as patron", async () => { - await makeUserPatron(); - await createTeamAction({ name: "Team 1" }, { user: "regular" }); await createTeamAction({ name: "Team 2" }, { user: "regular" }); await createTeamAction({ name: "Team 3" }, { user: "regular" }); diff --git a/app/features/user-card/UserCardRepository.server.test.ts b/app/features/user-card/UserCardRepository.server.test.ts index 8c747919d..66e4cf6e5 100644 --- a/app/features/user-card/UserCardRepository.server.test.ts +++ b/app/features/user-card/UserCardRepository.server.test.ts @@ -61,27 +61,24 @@ describe("UserCardRepository.findAllByUserIds", () => { }); it("keys cards by user id and builds the stats array from db fields", async () => { + const plusMember = await UserFactory.create(null, { plusTier: 2 }); await db .updateTable("User") .set({ div: "1" }) - .where("id", "=", owner.id) + .where("id", "=", plusMember.id) .execute(); - await db - .insertInto("PlusTier") - .values({ userId: owner.id, tier: 2 }) - .execute(); - await insertVerifiedXp(owner.id, 2500); + await insertVerifiedXp(plusMember.id, 2500); const { userCards } = await withNoUser(() => UserCardRepository.findAllByUserIds({ - userIds: [owner.id, other.id], + userIds: [plusMember.id, other.id], }), ); expect(userCards.size).toBe(2); - const card = userCards.get(owner.id); - expect(card?.id).toBe(owner.id); + const card = userCards.get(plusMember.id); + expect(card?.id).toBe(plusMember.id); expect(card?.freeAgentPostId).toBeNull(); const statTypes = card?.stats.map((stat) => stat.type) ?? []; @@ -194,13 +191,10 @@ describe("UserCardRepository.findAllByUserIds", () => { }); it("persists edited card fields and surfaces hidden stats", async () => { - await db - .insertInto("PlusTier") - .values({ userId: owner.id, tier: 2 }) - .execute(); - await insertVerifiedXp(owner.id, 2500); + const plusMember = await UserFactory.create(null, { plusTier: 2 }); + await insertVerifiedXp(plusMember.id, 2500); - await withUserId(owner.id, () => + await withUserId(plusMember.id, () => UserCardRepository.updateOwnCard({ shortBio: "hello", bannerPresetImg: "#ff4655", @@ -210,12 +204,12 @@ describe("UserCardRepository.findAllByUserIds", () => { }), ); - const { userCards } = await withUserId(owner.id, () => + const { userCards } = await withUserId(plusMember.id, () => UserCardRepository.findAllByUserIds({ - userIds: [owner.id], + userIds: [plusMember.id], }), ); - const card = userCards.get(owner.id); + const card = userCards.get(plusMember.id); expect(card?.shortBio).toBe("hello"); expect(card?.banner).toMatchObject({ type: "COLOR", hexCode: "#ff4655" }); @@ -227,7 +221,7 @@ describe("UserCardRepository.findAllByUserIds", () => { }); const extras = await UserCardRepository.findCardEditExtrasByUserId( - owner.id, + plusMember.id, ); expect(extras.hiddenCardStats).toEqual(["XP"]); }); diff --git a/app/features/user-page/UserRepository.test.ts b/app/features/user-page/UserRepository.test.ts index 04a79e853..c338c38cf 100644 --- a/app/features/user-page/UserRepository.test.ts +++ b/app/features/user-page/UserRepository.test.ts @@ -1,6 +1,6 @@ import { afterEach, describe, expect, test } from "vitest"; +import * as UserFactory from "~/db/seed/factories/UserFactory"; import { dbReset } from "~/utils/Test"; -import * as AdminRepository from "../admin/AdminRepository.server"; import * as UserRepository from "./UserRepository.server"; describe("UserRepository", () => { @@ -107,19 +107,13 @@ describe("UserRepository", () => { describe("userRoles", () => { test("returns empty array for basic user", async () => { - await UserRepository.upsert({ - discordId: "79237403620945920", - discordName: "DummyAdmin", - discordAvatar: null, - }); + await UserFactory.createAdmin(); const recentDiscordId = String( (BigInt(Date.now() - 1420070400000) << 22n) + 1n, ); - const { id } = await UserRepository.upsert({ + const { id } = await UserFactory.create({ discordId: recentDiscordId, - discordName: "RegularUser", - discordAvatar: null, }); const user = await UserRepository.findLeanById(id); @@ -128,11 +122,7 @@ describe("UserRepository", () => { }); test("returns ADMIN and STAFF roles for admin user", async () => { - const { id } = await UserRepository.upsert({ - discordId: "79237403620945920", - discordName: "AdminUser", - discordAvatar: null, - }); + const { id } = await UserFactory.createAdmin(); const user = await UserRepository.findLeanById(id); @@ -141,22 +131,7 @@ describe("UserRepository", () => { }); test("returns MINOR_SUPPORT role for patron tier 1", async () => { - const { id } = await UserRepository.upsert({ - discordId: "79237403620945921", - discordName: "PatronUser", - discordAvatar: null, - }); - - const now = new Date(); - const oneYearFromNow = new Date( - now.getTime() + 365 * 24 * 60 * 60 * 1000, - ); - await AdminRepository.forcePatron({ - id, - patronTier: 1, - patronStartedAt: now, - patronExpiresAt: oneYearFromNow, - }); + const { id } = await UserFactory.create(null, { patronTier: 1 }); const user = await UserRepository.findLeanById(id); @@ -165,22 +140,7 @@ describe("UserRepository", () => { }); test("returns SUPPORTER, MINOR_SUPPORT, TOURNAMENT_ADDER, CALENDAR_EVENT_ADDER, and API_ACCESSER roles for patron tier 2", async () => { - const { id } = await UserRepository.upsert({ - discordId: "79237403620945921", - discordName: "SupporterUser", - discordAvatar: null, - }); - - const now = new Date(); - const oneYearFromNow = new Date( - now.getTime() + 365 * 24 * 60 * 60 * 1000, - ); - await AdminRepository.forcePatron({ - id, - patronTier: 2, - patronStartedAt: now, - patronExpiresAt: oneYearFromNow, - }); + const { id } = await UserFactory.create(null, { patronTier: 2 }); const user = await UserRepository.findLeanById(id); @@ -192,13 +152,7 @@ describe("UserRepository", () => { }); test("returns PLUS_SERVER_MEMBER role for plus tier user", async () => { - const { id } = await UserRepository.upsert({ - discordId: "79237403620945921", - discordName: "PlusUser", - discordAvatar: null, - }); - - await AdminRepository.replacePlusTiers([{ userId: id, plusTier: 1 }]); + const { id } = await UserFactory.create(null, { plusTier: 1 }); const user = await UserRepository.findLeanById(id); @@ -206,13 +160,7 @@ describe("UserRepository", () => { }); test("returns ARTIST role for artist user", async () => { - const { id } = await UserRepository.upsert({ - discordId: "79237403620945921", - discordName: "ArtistUser", - discordAvatar: null, - }); - - await AdminRepository.makeArtistByUserId(id); + const { id } = await UserFactory.create(null, { roles: ["ARTIST"] }); const user = await UserRepository.findLeanById(id); @@ -220,13 +168,7 @@ describe("UserRepository", () => { }); test("returns VIDEO_ADDER role for video adder user", async () => { - const { id } = await UserRepository.upsert({ - discordId: "79237403620945921", - discordName: "VideoAdderUser", - discordAvatar: null, - }); - - await AdminRepository.makeVideoAdderByUserId(id); + const { id } = await UserFactory.create(null, { roles: ["VIDEO_ADDER"] }); const user = await UserRepository.findLeanById(id); @@ -234,13 +176,10 @@ describe("UserRepository", () => { }); test("returns TOURNAMENT_ADDER and API_ACCESSER roles for tournament organizer", async () => { - const { id } = await UserRepository.upsert({ - discordId: "79237403620945921", - discordName: "OrganizerUser", - discordAvatar: null, - }); - - await AdminRepository.makeTournamentOrganizerByUserId(id); + const { id } = await UserFactory.create( + {}, + { roles: ["TOURNAMENT_ORGANIZER"] }, + ); const user = await UserRepository.findLeanById(id); @@ -249,14 +188,10 @@ describe("UserRepository", () => { }); test("returns API_ACCESSER role for api accesser user", async () => { - const { id } = await UserRepository.upsert({ - discordId: "79237403620945921", - discordName: "ApiUser", - discordAvatar: null, + const { id } = await UserFactory.create(null, { + roles: ["API_ACCESSER"], }); - await AdminRepository.makeApiAccesserByUserId(id); - const user = await UserRepository.findLeanById(id); expect(user?.roles).toContain("API_ACCESSER"); @@ -264,11 +199,7 @@ describe("UserRepository", () => { test("returns CALENDAR_EVENT_ADDER role for aged discord account", async () => { const agedDiscordId = "79237403620945921"; - const { id } = await UserRepository.upsert({ - discordId: agedDiscordId, - discordName: "AgedUser", - discordAvatar: null, - }); + const { id } = await UserFactory.create({ discordId: agedDiscordId }); const user = await UserRepository.findLeanById(id); @@ -280,10 +211,8 @@ describe("UserRepository", () => { (BigInt(Date.now() - 1420070400000) << 22n) + 1n, ); - const { id } = await UserRepository.upsert({ + const { id } = await UserFactory.create({ discordId: recentDiscordId, - discordName: "NewUser", - discordAvatar: null, }); const user = await UserRepository.findLeanById(id); @@ -292,26 +221,14 @@ describe("UserRepository", () => { }); test("returns multiple roles for user with multiple privileges", async () => { - const { id } = await UserRepository.upsert({ - discordId: "79237403620945920", - discordName: "MultiRoleUser", - discordAvatar: null, - }); - - const now = new Date(); - const oneYearFromNow = new Date( - now.getTime() + 365 * 24 * 60 * 60 * 1000, + const { id } = await UserFactory.create( + {}, + { + patronTier: 2, + plusTier: 2, + roles: ["ARTIST", "VIDEO_ADDER"], + }, ); - await AdminRepository.forcePatron({ - id, - patronTier: 2, - patronStartedAt: now, - patronExpiresAt: oneYearFromNow, - }); - - await AdminRepository.makeArtistByUserId(id); - await AdminRepository.makeVideoAdderByUserId(id); - await AdminRepository.replacePlusTiers([{ userId: id, plusTier: 2 }]); const user = await UserRepository.findLeanById(id);