mirror of
https://github.com/Sendouc/sendou.ink.git
synced 2026-07-30 07:44:43 -05:00
Step 1
This commit is contained in:
parent
5e6d292d7a
commit
369d81eefe
|
|
@ -5,10 +5,11 @@ type RequiredArgs<Args, Defaults> = Omit<Args, keyof Defaults>;
|
|||
|
||||
type CreateArgs<Args, Defaults> = RequiredArgs<Args, Defaults> & Partial<Args>;
|
||||
|
||||
/** `null` says "all defaults", for when only `options` is of interest. */
|
||||
type CreateParams<Args, Defaults, Options> = [
|
||||
keyof RequiredArgs<Args, Defaults>,
|
||||
] extends [never]
|
||||
? [overrides?: Partial<Args>, options?: Options]
|
||||
? [overrides?: Partial<Args> | null, options?: Options]
|
||||
: [overrides: CreateArgs<Args, Defaults>, options?: Options];
|
||||
|
||||
type CreateManyParams<Args, Defaults, Options> = [
|
||||
|
|
@ -16,7 +17,7 @@ type CreateManyParams<Args, Defaults, Options> = [
|
|||
] extends [never]
|
||||
? [
|
||||
count: number,
|
||||
overrides?: ManyOverrides<Args, Defaults>,
|
||||
overrides?: ManyOverrides<Args, Defaults> | null,
|
||||
options?: Options,
|
||||
]
|
||||
: [
|
||||
|
|
@ -135,7 +136,7 @@ export function resetFactories() {
|
|||
}
|
||||
|
||||
function overridesAt<Args, Defaults>(
|
||||
overrides: ManyOverrides<Args, Defaults> | undefined,
|
||||
overrides: ManyOverrides<Args, Defaults> | null | undefined,
|
||||
index: number,
|
||||
): Partial<Args> {
|
||||
if (!overrides) return {};
|
||||
|
|
|
|||
|
|
@ -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<typeof UserRepository.upsert>[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<Tables["User"]["patronTier"]>;
|
||||
roles?: Array<Role>;
|
||||
};
|
||||
|
||||
const PATRONAGE_LENGTH = { years: 1 };
|
||||
|
||||
const GRANT_ROLE: Record<Role, (userId: number) => Promise<unknown>> = {
|
||||
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<UpsertArgs>) {
|
||||
export async function createAdmin(
|
||||
overrides?: Partial<UpsertArgs> | 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<UpsertArgs>) {
|
||||
export async function createRegular(
|
||||
overrides?: Partial<UpsertArgs> | 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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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" });
|
||||
|
|
|
|||
|
|
@ -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"]);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user