fix: fix date comparison for OOO (#17756)
* fix: fix date comparison for OOO * fix date comparison with today, and add tests * remove wrong code * remove logic that is no longer in use * fix type error * mock more methods --------- Co-authored-by: Alex van Andel <me@alexvanandel.com>
This commit is contained in:
co-authored by
Alex van Andel
parent
caeaf9053b
commit
58b77b2a16
@@ -0,0 +1,129 @@
|
||||
import { describe, expect, it, beforeEach, afterEach } from "vitest";
|
||||
import { vi } from "vitest";
|
||||
|
||||
import { prisma } from "@calcom/prisma";
|
||||
|
||||
import { outOfOfficeCreateOrUpdate } from "../outOfOffice.handler";
|
||||
|
||||
const prismaMock = {
|
||||
outOfOfficeEntry: {
|
||||
findFirst: vi.fn().mockResolvedValue(undefined),
|
||||
findUnique: vi.fn().mockResolvedValue(undefined),
|
||||
upsert: vi.fn().mockResolvedValue(undefined),
|
||||
},
|
||||
};
|
||||
vi.spyOn(prisma.outOfOfficeEntry, "findFirst").mockImplementation(prismaMock.outOfOfficeEntry.findFirst);
|
||||
vi.spyOn(prisma.outOfOfficeEntry, "findUnique").mockImplementation(prismaMock.outOfOfficeEntry.findUnique);
|
||||
vi.spyOn(prisma.outOfOfficeEntry, "upsert").mockImplementation(prismaMock.outOfOfficeEntry.upsert);
|
||||
|
||||
afterEach(() => {
|
||||
prismaMock.outOfOfficeEntry.findFirst.mockClear();
|
||||
prismaMock.outOfOfficeEntry.findUnique.mockClear();
|
||||
prismaMock.outOfOfficeEntry.upsert.mockClear();
|
||||
});
|
||||
|
||||
const mockUser = {
|
||||
id: 4,
|
||||
username: "pro",
|
||||
email: "pro@example.com",
|
||||
password: {
|
||||
hash: "",
|
||||
userId: 0,
|
||||
},
|
||||
completedOnboarding: true,
|
||||
identityProvider: "CAL",
|
||||
profiles: [],
|
||||
avatar: "",
|
||||
organization: null,
|
||||
organizationId: null,
|
||||
locale: "en",
|
||||
timeZone: "UTC",
|
||||
defaultScheduleId: null,
|
||||
email_verified: true,
|
||||
name: null,
|
||||
theme: null,
|
||||
} as const;
|
||||
|
||||
describe("outOfOfficeCreateOrUpdate", () => {
|
||||
beforeEach(() => {
|
||||
vi.resetAllMocks();
|
||||
vi.setSystemTime(new Date("2024-11-22T03:23:45Z"));
|
||||
});
|
||||
|
||||
it("should throw error if start date is after end date", async () => {
|
||||
const input = {
|
||||
dateRange: {
|
||||
startDate: new Date("2024-11-23T23:00:00.000Z"),
|
||||
endDate: new Date("2024-11-22T23:00:00.000Z"),
|
||||
},
|
||||
offset: 60,
|
||||
reasonId: 1,
|
||||
notes: "",
|
||||
toTeamUserId: null,
|
||||
};
|
||||
|
||||
await expect(outOfOfficeCreateOrUpdate({ ctx: { user: mockUser }, input })).rejects.toThrow(
|
||||
"start_date_must_be_before_end_date"
|
||||
);
|
||||
});
|
||||
|
||||
it("should handle timezone offset correctly", async () => {
|
||||
const input = {
|
||||
dateRange: {
|
||||
startDate: new Date("2024-11-23T23:00:00.000Z"),
|
||||
endDate: new Date("2024-11-23T23:00:00.000Z"),
|
||||
},
|
||||
offset: 60, // Paris timezone offset in minutes
|
||||
reasonId: 1,
|
||||
notes: "",
|
||||
toTeamUserId: null,
|
||||
};
|
||||
const startTimeUtc = "2024-11-24T00:00:00.000Z";
|
||||
const endTimeUtc = "2024-11-24T23:59:59.999Z";
|
||||
|
||||
await outOfOfficeCreateOrUpdate({
|
||||
ctx: { user: mockUser },
|
||||
input,
|
||||
});
|
||||
|
||||
expect(prisma.outOfOfficeEntry.findFirst).toHaveBeenNthCalledWith(1, {
|
||||
select: {
|
||||
toUserId: true,
|
||||
userId: true,
|
||||
},
|
||||
where: {
|
||||
OR: [
|
||||
{
|
||||
AND: [
|
||||
{
|
||||
start: {
|
||||
lte: endTimeUtc,
|
||||
},
|
||||
},
|
||||
{
|
||||
end: {
|
||||
gte: startTimeUtc,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
AND: [
|
||||
{
|
||||
start: {
|
||||
gte: startTimeUtc,
|
||||
},
|
||||
},
|
||||
{
|
||||
end: {
|
||||
lte: endTimeUtc,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
toUserId: 4,
|
||||
},
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -10,12 +10,12 @@ import type { OOOEntryPayloadType } from "@calcom/features/webhooks/lib/sendPayl
|
||||
import sendPayload from "@calcom/features/webhooks/lib/sendPayload";
|
||||
import { getTranslation } from "@calcom/lib/server";
|
||||
import prisma from "@calcom/prisma";
|
||||
import { WebhookTriggerEvents } from "@calcom/prisma/enums";
|
||||
import type { TrpcSessionUser } from "@calcom/trpc/server/trpc";
|
||||
|
||||
import { TRPCError } from "@trpc/server";
|
||||
|
||||
import type { TOutOfOfficeDelete, TOutOfOfficeInputSchema } from "./outOfOffice.schema";
|
||||
import { WebhookTriggerEvents } from ".prisma/client";
|
||||
|
||||
type TBookingRedirect = {
|
||||
ctx: {
|
||||
@@ -30,11 +30,11 @@ export const outOfOfficeCreateOrUpdate = async ({ ctx, input }: TBookingRedirect
|
||||
throw new TRPCError({ code: "BAD_REQUEST", message: "start_date_and_end_date_required" });
|
||||
}
|
||||
|
||||
const inputStartTime = dayjs(startDate).startOf("day");
|
||||
const inputEndTime = dayjs(endDate).endOf("day");
|
||||
const startTimeUtc = dayjs.utc(startDate).add(input.offset, "minute").startOf("day");
|
||||
const endTimeUtc = dayjs.utc(endDate).add(input.offset, "minute").endOf("day");
|
||||
|
||||
// If start date is after end date throw error
|
||||
if (inputStartTime.isAfter(inputEndTime)) {
|
||||
if (startTimeUtc.isAfter(endTimeUtc)) {
|
||||
throw new TRPCError({ code: "BAD_REQUEST", message: "start_date_must_be_before_end_date" });
|
||||
}
|
||||
|
||||
@@ -85,17 +85,11 @@ export const outOfOfficeCreateOrUpdate = async ({ ctx, input }: TBookingRedirect
|
||||
OR: [
|
||||
// Outside of range
|
||||
{
|
||||
AND: [
|
||||
{ start: { lte: inputEndTime.toISOString() } },
|
||||
{ end: { gte: inputStartTime.toISOString() } },
|
||||
],
|
||||
AND: [{ start: { lte: endTimeUtc.toISOString() } }, { end: { gte: startTimeUtc.toISOString() } }],
|
||||
},
|
||||
// Inside of range
|
||||
{
|
||||
AND: [
|
||||
{ start: { gte: inputStartTime.toISOString() } },
|
||||
{ end: { lte: inputEndTime.toISOString() } },
|
||||
],
|
||||
AND: [{ start: { gte: startTimeUtc.toISOString() } }, { end: { lte: endTimeUtc.toISOString() } }],
|
||||
},
|
||||
],
|
||||
},
|
||||
@@ -106,14 +100,11 @@ export const outOfOfficeCreateOrUpdate = async ({ ctx, input }: TBookingRedirect
|
||||
throw new TRPCError({ code: "BAD_REQUEST", message: "booking_redirect_infinite_not_allowed" });
|
||||
}
|
||||
|
||||
const startTimeUtc = dayjs.utc(startDate).add(input.offset, "minute").startOf("day").toISOString();
|
||||
const endTimeUtc = dayjs.utc(endDate).add(input.offset, "minute").endOf("day").toISOString();
|
||||
|
||||
const isDuplicateOutOfOfficeEntry = await prisma.outOfOfficeEntry.findFirst({
|
||||
where: {
|
||||
userId: ctx.user.id,
|
||||
start: startTimeUtc,
|
||||
end: endTimeUtc,
|
||||
start: startTimeUtc.toISOString(),
|
||||
end: endTimeUtc.toISOString(),
|
||||
},
|
||||
});
|
||||
|
||||
@@ -144,8 +135,8 @@ export const outOfOfficeCreateOrUpdate = async ({ ctx, input }: TBookingRedirect
|
||||
},
|
||||
create: {
|
||||
uuid: uuidv4(),
|
||||
start: startTimeUtc,
|
||||
end: endTimeUtc,
|
||||
start: startTimeUtc.toISOString(),
|
||||
end: endTimeUtc.toISOString(),
|
||||
notes: input.notes,
|
||||
userId: ctx.user.id,
|
||||
reasonId: input.reasonId,
|
||||
@@ -154,8 +145,8 @@ export const outOfOfficeCreateOrUpdate = async ({ ctx, input }: TBookingRedirect
|
||||
updatedAt: new Date(),
|
||||
},
|
||||
update: {
|
||||
start: startTimeUtc,
|
||||
end: endTimeUtc,
|
||||
start: startTimeUtc.toISOString(),
|
||||
end: endTimeUtc.toISOString(),
|
||||
notes: input.notes,
|
||||
userId: ctx.user.id,
|
||||
reasonId: input.reasonId,
|
||||
|
||||
Reference in New Issue
Block a user