mirror of
https://github.com/Sendouc/sendou.ink.git
synced 2026-10-01 15:48:23 -05:00
Move app to apps/web-react in pnpm workspace layout
This commit is contained in:
@@ -0,0 +1,326 @@
|
||||
import { add, sub } from "date-fns";
|
||||
import { beforeEach, describe, expect, test } from "vitest";
|
||||
import * as ScrimPostFactory from "~/db/seed/factories/ScrimPostFactory";
|
||||
import * as TeamFactory from "~/db/seed/factories/TeamFactory";
|
||||
import * as UserFactory from "~/db/seed/factories/UserFactory";
|
||||
import { dateToDatabaseTimestamp } from "~/utils/dates";
|
||||
import { DuplicateEntryError } from "~/utils/errors";
|
||||
import * as ScrimPostRepository from "./ScrimPostRepository.server";
|
||||
|
||||
const users = UserFactory.pool();
|
||||
|
||||
const BOOKED_AT = add(new Date(), { hours: 10 });
|
||||
|
||||
const dbTs = (date: Date) => dateToDatabaseTimestamp(date);
|
||||
|
||||
const WINDOW = {
|
||||
startTime: dbTs(sub(BOOKED_AT, { hours: 1 })),
|
||||
endTime: dbTs(add(BOOKED_AT, { hours: 1 })),
|
||||
};
|
||||
|
||||
describe("findPendingOverlapsForUsers", () => {
|
||||
beforeEach(async () => {
|
||||
await users.create(5);
|
||||
});
|
||||
|
||||
test("returns a specific-time pending post in window with its member ids", async () => {
|
||||
const { id: postId } = await ScrimPostFactory.create({
|
||||
startsAt: dbTs(BOOKED_AT),
|
||||
users: [
|
||||
{ userId: users.id(1), isOwner: 1 },
|
||||
{ userId: users.id(2), isOwner: 0 },
|
||||
],
|
||||
});
|
||||
|
||||
const { posts } = await ScrimPostRepository.findPendingOverlapsForUsers({
|
||||
userIds: [users.id(1)],
|
||||
...WINDOW,
|
||||
excludePostId: -1,
|
||||
});
|
||||
|
||||
expect(posts).toHaveLength(1);
|
||||
expect(posts[0]!.id).toBe(postId);
|
||||
expect(posts[0]!.memberIds.sort()).toEqual(
|
||||
[users.id(1), users.id(2)].sort(),
|
||||
);
|
||||
});
|
||||
|
||||
test("returns a ranged post whose interval overlaps the window even if its start is outside", async () => {
|
||||
const { id: postId } = await ScrimPostFactory.create({
|
||||
startsAt: dbTs(sub(BOOKED_AT, { hours: 2 })),
|
||||
rangeEndsAt: dbTs(BOOKED_AT),
|
||||
users: [{ userId: users.id(1), isOwner: 1 }],
|
||||
});
|
||||
|
||||
const { posts } = await ScrimPostRepository.findPendingOverlapsForUsers({
|
||||
userIds: [users.id(1)],
|
||||
...WINDOW,
|
||||
excludePostId: -1,
|
||||
});
|
||||
|
||||
expect(posts.map((p) => p.id)).toEqual([postId]);
|
||||
});
|
||||
|
||||
test("does not return a ranged post whose interval does not overlap the window", async () => {
|
||||
await ScrimPostFactory.create({
|
||||
startsAt: dbTs(sub(BOOKED_AT, { hours: 5 })),
|
||||
rangeEndsAt: dbTs(sub(BOOKED_AT, { hours: 3 })),
|
||||
users: [{ userId: users.id(1), isOwner: 1 }],
|
||||
});
|
||||
|
||||
const { posts } = await ScrimPostRepository.findPendingOverlapsForUsers({
|
||||
userIds: [users.id(1)],
|
||||
...WINDOW,
|
||||
excludePostId: -1,
|
||||
});
|
||||
|
||||
expect(posts).toHaveLength(0);
|
||||
});
|
||||
|
||||
test("excludes the just-booked post even when it overlaps", async () => {
|
||||
const { id: postId } = await ScrimPostFactory.create({
|
||||
startsAt: dbTs(BOOKED_AT),
|
||||
users: [{ userId: users.id(1), isOwner: 1 }],
|
||||
});
|
||||
|
||||
const { posts } = await ScrimPostRepository.findPendingOverlapsForUsers({
|
||||
userIds: [users.id(1)],
|
||||
...WINDOW,
|
||||
excludePostId: postId,
|
||||
});
|
||||
|
||||
expect(posts).toHaveLength(0);
|
||||
});
|
||||
|
||||
test("does not return posts that involve none of the given users", async () => {
|
||||
await ScrimPostFactory.create({
|
||||
startsAt: dbTs(BOOKED_AT),
|
||||
users: [{ userId: users.id(3), isOwner: 1 }],
|
||||
});
|
||||
|
||||
const { posts } = await ScrimPostRepository.findPendingOverlapsForUsers({
|
||||
userIds: [users.id(1), users.id(2)],
|
||||
...WINDOW,
|
||||
excludePostId: -1,
|
||||
});
|
||||
|
||||
expect(posts).toHaveLength(0);
|
||||
});
|
||||
|
||||
test("excludes already-accepted (booked) posts", async () => {
|
||||
await ScrimPostFactory.create(
|
||||
{
|
||||
startsAt: dbTs(BOOKED_AT),
|
||||
users: [{ userId: users.id(1), isOwner: 1 }],
|
||||
},
|
||||
{
|
||||
requests: [
|
||||
{
|
||||
users: [{ userId: users.id(3), isOwner: 1 }],
|
||||
startsAt: dbTs(BOOKED_AT),
|
||||
isAccepted: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
);
|
||||
|
||||
const { posts, requestIds } =
|
||||
await ScrimPostRepository.findPendingOverlapsForUsers({
|
||||
userIds: [users.id(1), users.id(3)],
|
||||
...WINDOW,
|
||||
excludePostId: -1,
|
||||
});
|
||||
|
||||
expect(posts).toHaveLength(0);
|
||||
expect(requestIds).toHaveLength(0);
|
||||
});
|
||||
|
||||
test("returns pending request ids whose effective time falls in the window", async () => {
|
||||
const { id: postId } = await ScrimPostFactory.create(
|
||||
{
|
||||
startsAt: dbTs(add(BOOKED_AT, { hours: 3 })),
|
||||
users: [{ userId: users.id(3), isOwner: 1 }],
|
||||
},
|
||||
{
|
||||
requests: [
|
||||
{
|
||||
users: [{ userId: users.id(1), isOwner: 1 }],
|
||||
startsAt: dbTs(BOOKED_AT),
|
||||
},
|
||||
],
|
||||
},
|
||||
);
|
||||
const post = await ScrimPostRepository.findById(postId);
|
||||
const requestId = post!.requests[0]!.id;
|
||||
|
||||
const { posts, requestIds } =
|
||||
await ScrimPostRepository.findPendingOverlapsForUsers({
|
||||
userIds: [users.id(1)],
|
||||
...WINDOW,
|
||||
excludePostId: -1,
|
||||
});
|
||||
|
||||
expect(posts).toHaveLength(0);
|
||||
expect(requestIds).toEqual([requestId]);
|
||||
});
|
||||
|
||||
test("does not return pending requests whose effective time is outside the window", async () => {
|
||||
await ScrimPostFactory.create(
|
||||
{
|
||||
startsAt: dbTs(add(BOOKED_AT, { hours: 3 })),
|
||||
users: [{ userId: users.id(3), isOwner: 1 }],
|
||||
},
|
||||
{
|
||||
requests: [
|
||||
{
|
||||
users: [{ userId: users.id(1), isOwner: 1 }],
|
||||
startsAt: dbTs(add(BOOKED_AT, { hours: 3 })),
|
||||
},
|
||||
],
|
||||
},
|
||||
);
|
||||
|
||||
const { requestIds } =
|
||||
await ScrimPostRepository.findPendingOverlapsForUsers({
|
||||
userIds: [users.id(1)],
|
||||
...WINDOW,
|
||||
excludePostId: -1,
|
||||
});
|
||||
|
||||
expect(requestIds).toHaveLength(0);
|
||||
});
|
||||
});
|
||||
|
||||
describe("findUserScrims", () => {
|
||||
beforeEach(async () => {
|
||||
await users.create(5);
|
||||
});
|
||||
|
||||
test("passed-over requester does not see the scrim booked between the post and another team", async () => {
|
||||
await ScrimPostFactory.create(
|
||||
{
|
||||
startsAt: dbTs(BOOKED_AT),
|
||||
users: [{ userId: users.id(3), isOwner: 1 }],
|
||||
},
|
||||
{
|
||||
requests: [
|
||||
{
|
||||
users: [
|
||||
{ userId: users.id(1), isOwner: 1 },
|
||||
{ userId: users.id(2), isOwner: 0 },
|
||||
],
|
||||
},
|
||||
{
|
||||
users: [
|
||||
{ userId: users.id(4), isOwner: 1 },
|
||||
{ userId: users.id(5), isOwner: 0 },
|
||||
],
|
||||
isAccepted: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
);
|
||||
|
||||
const passedOverRequesterScrims = await ScrimPostRepository.findUserScrims(
|
||||
users.id(1),
|
||||
);
|
||||
|
||||
expect(passedOverRequesterScrims).toHaveLength(0);
|
||||
});
|
||||
|
||||
test("post owner sees the accepted request's side as the opponent", async () => {
|
||||
await ScrimPostFactory.create(
|
||||
{
|
||||
startsAt: dbTs(BOOKED_AT),
|
||||
users: [{ userId: users.id(3), isOwner: 1 }],
|
||||
},
|
||||
{
|
||||
requests: [
|
||||
{ users: [{ userId: users.id(1), isOwner: 1 }] },
|
||||
{ users: [{ userId: users.id(4), isOwner: 1 }], isAccepted: true },
|
||||
],
|
||||
},
|
||||
);
|
||||
|
||||
const postOwnerScrims = await ScrimPostRepository.findUserScrims(
|
||||
users.id(3),
|
||||
);
|
||||
|
||||
expect(postOwnerScrims).toHaveLength(1);
|
||||
expect(postOwnerScrims[0]!.status).toBe("booked");
|
||||
});
|
||||
});
|
||||
|
||||
describe("insertRequest", () => {
|
||||
beforeEach(async () => {
|
||||
await users.create(5);
|
||||
});
|
||||
|
||||
const insertTeamRequest = ({
|
||||
scrimPostId,
|
||||
teamId,
|
||||
userId,
|
||||
}: {
|
||||
scrimPostId: number;
|
||||
teamId: number;
|
||||
userId: number;
|
||||
}) =>
|
||||
ScrimPostRepository.insertRequest({
|
||||
scrimPostId,
|
||||
teamId,
|
||||
message: null,
|
||||
startsAt: null,
|
||||
users: [{ userId, isOwner: 1 }],
|
||||
});
|
||||
|
||||
test("throws if the team already has a request for the post", async () => {
|
||||
const { id: postId } = await ScrimPostFactory.create({
|
||||
startsAt: dbTs(BOOKED_AT),
|
||||
users: [{ userId: users.id(1), isOwner: 1 }],
|
||||
});
|
||||
const team = await TeamFactory.create({ memberUserIds: [users.id(2)] });
|
||||
|
||||
await insertTeamRequest({
|
||||
scrimPostId: postId,
|
||||
teamId: team.id,
|
||||
userId: users.id(2),
|
||||
});
|
||||
|
||||
await expect(
|
||||
insertTeamRequest({
|
||||
scrimPostId: postId,
|
||||
teamId: team.id,
|
||||
userId: users.id(3),
|
||||
}),
|
||||
).rejects.toThrowError(DuplicateEntryError);
|
||||
|
||||
const post = await ScrimPostRepository.findById(postId);
|
||||
expect(post!.requests).toHaveLength(1);
|
||||
});
|
||||
|
||||
test("allows the team to request another post", async () => {
|
||||
const { id: postId } = await ScrimPostFactory.create({
|
||||
startsAt: dbTs(BOOKED_AT),
|
||||
users: [{ userId: users.id(1), isOwner: 1 }],
|
||||
});
|
||||
const { id: otherPostId } = await ScrimPostFactory.create({
|
||||
startsAt: dbTs(BOOKED_AT),
|
||||
users: [{ userId: users.id(4), isOwner: 1 }],
|
||||
});
|
||||
const team = await TeamFactory.create({ memberUserIds: [users.id(2)] });
|
||||
|
||||
await insertTeamRequest({
|
||||
scrimPostId: postId,
|
||||
teamId: team.id,
|
||||
userId: users.id(2),
|
||||
});
|
||||
await insertTeamRequest({
|
||||
scrimPostId: otherPostId,
|
||||
teamId: team.id,
|
||||
userId: users.id(2),
|
||||
});
|
||||
|
||||
const otherPost = await ScrimPostRepository.findById(otherPostId);
|
||||
expect(otherPost!.requests).toHaveLength(1);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user