fix: validate contract_id

This commit is contained in:
Tiago Farto
2026-05-12 09:18:47 +00:00
parent b08f7e4ad9
commit 2af18ab343
8 changed files with 81 additions and 23 deletions
@@ -21,6 +21,7 @@ vi.mock("react", async () => {
});
const contactId = "test-contact-id";
const environmentId = "test-env-id";
describe("doesContactExist", () => {
afterEach(() => {
@@ -35,23 +36,23 @@ describe("doesContactExist", () => {
environmentId: "test-env",
});
const result = await doesContactExist(contactId);
const result = await doesContactExist(contactId, environmentId);
expect(result).toBe(true);
expect(prisma.contact.findFirst).toHaveBeenCalledWith({
where: { id: contactId },
where: { id: contactId, environmentId },
select: { id: true },
});
});
test("should return false if contact does not exist", async () => {
test("should return false if contact does not exist in the environment", async () => {
vi.mocked(prisma.contact.findFirst).mockResolvedValue(null);
const result = await doesContactExist(contactId);
const result = await doesContactExist(contactId, environmentId);
expect(result).toBe(false);
expect(prisma.contact.findFirst).toHaveBeenCalledWith({
where: { id: contactId },
where: { id: contactId, environmentId },
select: { id: true },
});
});
@@ -1,10 +1,11 @@
import { cache as reactCache } from "react";
import { prisma } from "@formbricks/database";
export const doesContactExist = reactCache(async (id: string): Promise<boolean> => {
export const doesContactExist = reactCache(async (id: string, environmentId: string): Promise<boolean> => {
const contact = await prisma.contact.findFirst({
where: {
id,
environmentId,
},
select: {
id: true,
@@ -81,7 +81,7 @@ describe("createDisplay", () => {
const result = await createDisplay(displayInput);
expect(validateInputs).toHaveBeenCalledWith([displayInput, expect.any(Object)]);
expect(doesContactExist).toHaveBeenCalledWith(contactId);
expect(doesContactExist).toHaveBeenCalledWith(contactId, environmentId);
expect(prisma.display.create).toHaveBeenCalledWith({
data: {
survey: { connect: { id: surveyId } },
@@ -108,14 +108,14 @@ describe("createDisplay", () => {
expect(result).toEqual(mockDisplayWithoutContact); // Changed this line
});
test("should create a display even if contact does not exist", async () => {
test("should create a display without contact if contact does not exist in the environment", async () => {
vi.mocked(doesContactExist).mockResolvedValue(false);
vi.mocked(prisma.display.create).mockResolvedValue(mockDisplayWithoutContact); // Expect no contact connection
const result = await createDisplay(displayInput);
expect(validateInputs).toHaveBeenCalledWith([displayInput, expect.any(Object)]);
expect(doesContactExist).toHaveBeenCalledWith(contactId);
expect(doesContactExist).toHaveBeenCalledWith(contactId, environmentId);
expect(prisma.display.create).toHaveBeenCalledWith({
data: {
survey: { connect: { id: surveyId } },
@@ -142,7 +142,7 @@ describe("createDisplay", () => {
vi.mocked(prisma.survey.findUnique).mockResolvedValue(null);
await expect(createDisplay(displayInput)).rejects.toThrow(new ResourceNotFoundError("Survey", surveyId));
expect(doesContactExist).toHaveBeenCalledWith(contactId);
expect(doesContactExist).toHaveBeenCalledWith(contactId, environmentId);
expect(prisma.survey.findUnique).toHaveBeenCalledWith({
where: { id: surveyId, environmentId },
});
@@ -14,7 +14,7 @@ export const createDisplay = async (displayInput: TDisplayCreateInputV2): Promis
const { contactId, surveyId, environmentId } = displayInput;
try {
const contactExists = contactId ? await doesContactExist(contactId) : false;
const contactExists = contactId ? await doesContactExist(contactId, environmentId) : false;
const survey = await prisma.survey.findUnique({
where: {
@@ -13,6 +13,7 @@ vi.mock("@formbricks/database", () => ({
}));
const contactId = "test-contact-id";
const environmentId = "test-env-id";
const mockContact = {
id: contactId,
attributes: [
@@ -32,10 +33,10 @@ describe("getContact", () => {
mockContact as unknown as Awaited<ReturnType<typeof prisma.contact.findUnique>>
);
const result = await getContact(contactId);
const result = await getContact(contactId, environmentId);
expect(prisma.contact.findUnique).toHaveBeenCalledWith({
where: { id: contactId },
where: { id: contactId, environmentId },
select: {
id: true,
attributes: {
@@ -55,10 +56,10 @@ describe("getContact", () => {
test("should return null when contact is not found", async () => {
vi.mocked(prisma.contact.findUnique).mockResolvedValue(null);
const result = await getContact(contactId);
const result = await getContact(contactId, environmentId);
expect(prisma.contact.findUnique).toHaveBeenCalledWith({
where: { id: contactId },
where: { id: contactId, environmentId },
select: {
id: true,
attributes: {
@@ -2,9 +2,16 @@ import { cache as reactCache } from "react";
import { prisma } from "@formbricks/database";
import { TContactAttributes } from "@formbricks/types/contact-attribute";
export const getContact = reactCache(async (contactId: string) => {
type TContactAttributeResult = {
attributeKey: {
key: string;
};
value: string;
};
export const getContact = reactCache(async (contactId: string, environmentId: string) => {
const contact = await prisma.contact.findUnique({
where: { id: contactId },
where: { id: contactId, environmentId },
select: {
id: true,
attributes: {
@@ -20,10 +27,13 @@ export const getContact = reactCache(async (contactId: string) => {
return null;
}
const contactAttributes = contact.attributes.reduce<TContactAttributes>((acc, attr) => {
acc[attr.attributeKey.key] = attr.value;
return acc;
}, {});
const contactAttributes = contact.attributes.reduce(
(acc: TContactAttributes, attr: TContactAttributeResult) => {
acc[attr.attributeKey.key] = attr.value;
return acc;
},
{}
);
return {
id: contact.id,
@@ -236,6 +236,51 @@ describe("createResponse V2", () => {
const result = await createResponse(mockResponseInput, mockTx as unknown as Prisma.TransactionClient);
expect(result.tags).toEqual([mockTag]);
});
test("should create response with contact when contact belongs to the environment", async () => {
const responseInputWithContact = {
...mockResponseInput,
contactId,
};
const result = await createResponse(
responseInputWithContact,
mockTx as unknown as Prisma.TransactionClient
);
expect(getContact).toHaveBeenCalledWith(contactId, environmentId);
expect(mockTx.response.create).toHaveBeenCalledWith(
expect.objectContaining({
data: expect.objectContaining({
contact: { connect: { id: contactId } },
contactAttributes: mockContact.attributes,
}),
})
);
expect(result.contact).toEqual({
id: contactId,
userId,
});
});
test("should create response without contact when contact is not found in the environment", async () => {
vi.mocked(getContact).mockResolvedValue(null);
const responseInputWithContact = {
...mockResponseInput,
contactId,
};
const result = await createResponse(
responseInputWithContact,
mockTx as unknown as Prisma.TransactionClient
);
const createArgs = mockTx.response.create.mock.calls[0][0];
expect(getContact).toHaveBeenCalledWith(contactId, environmentId);
expect(createArgs.data).not.toHaveProperty("contact");
expect(createArgs.data).not.toHaveProperty("contactAttributes");
expect(result.contact).toBeNull();
});
});
describe("createResponseWithQuotaEvaluation V2", () => {
@@ -17,7 +17,7 @@ import { getContact } from "./contact";
export const createResponseWithQuotaEvaluation = async (
responseInput: TResponseInputV2
): Promise<TResponseWithQuotaFull> => {
const txResponse = await prisma.$transaction(async (tx) => {
const txResponse = await prisma.$transaction(async (tx: Prisma.TransactionClient) => {
const response = await createResponse(responseInput, tx);
const quotaResult = await evaluateResponseQuotas({
@@ -101,7 +101,7 @@ export const createResponse = async (
}
if (contactId) {
contact = await getContact(contactId);
contact = await getContact(contactId, environmentId);
}
const ttc = initialTtc ? (finished ? calculateTtcTotal(initialTtc) : initialTtc) : {};