Files
calendar/packages/lib/raqb/findTeamMembersMatchingAttributeLogic.test.ts
T

1030 lines
29 KiB
TypeScript

import type { BaseWidget } from "react-awesome-query-builder";
import { describe, it, expect, vi, beforeEach } from "vitest";
import { RouteActionType } from "@calcom/app-store/routing-forms/zod";
import { RaqbLogicResult } from "@calcom/lib/raqb/evaluateRaqbLogic";
import {
findTeamMembersMatchingAttributeLogic,
TroubleshooterCase,
} from "@calcom/lib/raqb/findTeamMembersMatchingAttributeLogic";
// import { EmailField } from "@calcom/ui";
import * as getAttributesModule from "@calcom/lib/service/attribute/server/getAttributes";
import type { AttributeType } from "@calcom/prisma/enums";
import { RoutingFormFieldType } from "@calcom/routing-forms/lib/FieldTypes";
import type { AttributesQueryValue, FormFieldsQueryValue } from "@calcom/routing-forms/types/types";
vi.mock("@calcom/lib/service/attribute/server/getAttributes");
vi.mock("../../components/react-awesome-query-builder/widgets", () => ({
default: {},
}));
vi.mock("@calcom/ui", () => ({}));
const orgId = 1001;
function mockAttributesScenario({
attributes,
teamMembersWithAttributeOptionValuePerAttribute,
}: {
attributes: {
id: string;
name: string;
type: AttributeType;
slug: string;
options: {
id: string;
value: string;
slug: string;
}[];
}[];
teamMembersWithAttributeOptionValuePerAttribute: {
userId: number;
attributes: Record<string, string | string[]>;
}[];
}) {
const commonOptionsProps = {
isGroup: false,
contains: [],
};
vi.mocked(getAttributesModule.getAttributesAssignmentData).mockResolvedValue({
attributesOfTheOrg: attributes.map((attribute) => ({
...attribute,
options: attribute.options.map((option) => ({
...option,
...commonOptionsProps,
})),
})),
attributesAssignedToTeamMembersWithOptions: teamMembersWithAttributeOptionValuePerAttribute.map(
(member) => {
return {
...member,
attributes: Object.fromEntries(
Object.entries(member.attributes).map(([attributeId, value]) => {
return [
attributeId,
// eslint-disable-next-line @typescript-eslint/no-non-null-asserted-optional-chain
{
attributeOption:
value instanceof Array
? value.map((value) => ({ value, isGroup: false, contains: [] }))
: { value, isGroup: false, contains: [] },
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
type: attributes.find((attribute) => attribute.id === attributeId)!.type,
},
];
})
),
};
}
),
});
}
function mockHugeAttributesOfTypeSingleSelect({
numAttributes,
numOptionsPerAttribute,
numTeamMembers,
numAttributesUsedPerTeamMember,
}: {
numAttributes: number;
numOptionsPerAttribute: number;
numTeamMembers: number;
numAttributesUsedPerTeamMember: number;
}) {
if (numAttributesUsedPerTeamMember > numAttributes) {
throw new Error("numAttributesUsedPerTeamMember cannot be greater than numAttributes");
}
const attributes = Array.from({ length: numAttributes }, (_, i) => ({
id: `attr${i + 1}`,
name: `Attribute ${i + 1}`,
type: "SINGLE_SELECT" as const,
slug: `attribute-${i + 1}`,
options: Array.from({ length: numOptionsPerAttribute }, (_, i) => ({
id: `opt${i + 1}`,
value: `Option ${i + 1}`,
slug: `option-${i + 1}`,
})),
}));
const assignedAttributeOptionIdForEachMember = 1;
const teamMembersWithAttributeOptionValuePerAttribute = Array.from({ length: numTeamMembers }, (_, i) => ({
userId: i + 1,
attributes: Object.fromEntries(
Array.from({ length: numAttributesUsedPerTeamMember }, (_, j) => [
attributes[j].id,
attributes[j].options[assignedAttributeOptionIdForEachMember].value,
])
),
}));
mockAttributesScenario({
attributes,
teamMembersWithAttributeOptionValuePerAttribute,
});
return {
attributes,
teamMembersWithAttributeOptionValuePerAttribute,
};
}
function buildQueryValue({
rules,
}: {
rules: {
raqbFieldId: string;
value: string | number | string[] | [string[]];
operator: string;
valueSrc: NonNullable<BaseWidget["valueSrc"]>[];
valueType: string[];
}[];
}) {
const queryValue = {
id: "query-id-1",
type: "group",
children1: rules.reduce((acc, rule, index) => {
acc[`rule-${index + 1}`] = {
type: "rule",
properties: {
field: rule.raqbFieldId,
value: rule.value,
operator: rule.operator,
valueSrc: rule.valueSrc,
valueType: rule.valueType,
},
};
return acc;
}, {} as any),
};
return queryValue;
}
function buildSelectTypeFieldQueryValue({
rules,
}: {
rules: {
raqbFieldId: string;
value: string | number | string[] | [string[]];
operator: string;
valueType?: string[];
}[];
}) {
return buildQueryValue({
rules: rules.map((rule) => ({
raqbFieldId: rule.raqbFieldId,
value: rule.value,
operator: rule.operator,
valueSrc: ["value"],
valueType: rule.valueType ?? ["select"],
})),
}) as AttributesQueryValue;
}
function buildRoute({
id,
action,
queryValue,
attributesQueryValue,
}: {
id: string;
action: {
type: RouteActionType;
value: string;
};
queryValue: FormFieldsQueryValue;
attributesQueryValue?: AttributesQueryValue;
}) {
return {
id,
action,
queryValue,
attributesQueryValue,
};
}
function buildDefaultCustomPageRoute({
id,
attributesQueryValue,
}: {
id: string;
attributesQueryValue?: AttributesQueryValue;
}) {
return buildRoute({
id,
action: { type: RouteActionType.CustomPageMessage, value: "test" },
queryValue: { type: "group" } as unknown as FormFieldsQueryValue,
attributesQueryValue,
});
}
function buildScenarioWhereMainAttributeLogicFails() {
const Option1OfAttribute1 = { id: "opt1", value: "Option 1", slug: "option-1" };
const Option2OfAttribute1 = { id: "opt2", value: "Option 2", slug: "option-2" };
const Attribute1 = {
id: "attr1",
name: "Attribute 1",
type: "SINGLE_SELECT" as const,
slug: "attribute-1",
options: [Option1OfAttribute1, Option2OfAttribute1],
};
mockAttributesScenario({
attributes: [Attribute1],
teamMembersWithAttributeOptionValuePerAttribute: [
{ userId: 1, attributes: { [Attribute1.id]: Option1OfAttribute1.value } },
],
});
const failingAttributesQueryValue = buildSelectTypeFieldQueryValue({
rules: [
{
raqbFieldId: Attribute1.id,
value: [Option2OfAttribute1.id],
operator: "select_equals",
},
],
}) as AttributesQueryValue;
const matchingAttributesQueryValue = buildSelectTypeFieldQueryValue({
rules: [
{
raqbFieldId: Attribute1.id,
value: [Option1OfAttribute1.id],
operator: "select_equals",
},
],
}) as AttributesQueryValue;
return { failingAttributesQueryValue, matchingAttributesQueryValue };
}
describe("findTeamMembersMatchingAttributeLogic", () => {
beforeEach(() => {
vi.resetAllMocks();
});
it("should return null if the route does not have an attributesQueryValue set", async () => {
mockAttributesScenario({
attributes: [],
teamMembersWithAttributeOptionValuePerAttribute: [],
});
const { teamMembersMatchingAttributeLogic, troubleshooter } = await findTeamMembersMatchingAttributeLogic(
{
dynamicFieldValueOperands: {
fields: [],
response: {},
},
attributesQueryValue: { type: "group" } as unknown as AttributesQueryValue,
teamId: 1,
orgId,
},
{
enableTroubleshooter: true,
}
);
expect(teamMembersMatchingAttributeLogic).toBeNull();
expect(troubleshooter).toEqual(
expect.objectContaining({
type: TroubleshooterCase.MATCHES_ALL_MEMBERS_BECAUSE_OF_EMPTY_QUERY_VALUE,
})
);
});
it("should return matching team members with a SINGLE_SELECT attribute when a static option is selected and troubleshooter should be null by default", async () => {
const Option1OfAttribute1 = { id: "opt1", value: "Option 1", slug: "option-1" };
const Attribute1 = {
id: "attr1",
name: "Attribute 1",
type: "SINGLE_SELECT" as const,
slug: "attribute-1",
options: [Option1OfAttribute1],
};
mockAttributesScenario({
attributes: [Attribute1],
teamMembersWithAttributeOptionValuePerAttribute: [
{ userId: 1, attributes: { [Attribute1.id]: Option1OfAttribute1.value } },
],
});
const attributesQueryValue = buildSelectTypeFieldQueryValue({
rules: [
{
raqbFieldId: Attribute1.id,
value: [Option1OfAttribute1.id],
operator: "select_equals",
},
],
}) as AttributesQueryValue;
const { teamMembersMatchingAttributeLogic: result, troubleshooter } =
await findTeamMembersMatchingAttributeLogic({
dynamicFieldValueOperands: {
fields: [],
response: {},
},
attributesQueryValue,
teamId: 1,
orgId,
});
expect(result).toEqual([
{
userId: 1,
result: RaqbLogicResult.MATCH,
},
]);
expect(troubleshooter).toBeUndefined();
});
it("should return matching team members with a SINGLE_SELECT attribute when 'Value of Field' option is selected", async () => {
const Option1OfAttribute1HumanReadableValue = "Option 1";
const Option1OfField1HumanReadableValue = Option1OfAttribute1HumanReadableValue;
const Field1Id = "field-1";
const Option1OfAttribute1 = {
id: "attr-1-opt-1",
value: Option1OfAttribute1HumanReadableValue,
slug: "option-1",
};
const Option1OfField1 = {
id: "field-1-opt-1",
label: Option1OfField1HumanReadableValue,
};
const Attribute1 = {
id: "attr1",
name: "Attribute 1",
type: "SINGLE_SELECT" as const,
slug: "attribute-1",
options: [Option1OfAttribute1],
};
mockAttributesScenario({
attributes: [Attribute1],
teamMembersWithAttributeOptionValuePerAttribute: [
{ userId: 1, attributes: { [Attribute1.id]: Option1OfAttribute1.value } },
],
});
const attributesQueryValue = buildSelectTypeFieldQueryValue({
rules: [
{
raqbFieldId: Attribute1.id,
value: [`{field:${Field1Id}}`],
operator: "select_equals",
},
],
}) as AttributesQueryValue;
const { teamMembersMatchingAttributeLogic: result } = await findTeamMembersMatchingAttributeLogic({
dynamicFieldValueOperands: {
fields: [
{
id: Field1Id,
type: RoutingFormFieldType.SINGLE_SELECT,
label: "Field 1",
options: [Option1OfField1],
},
],
response: {
[Field1Id]: {
value: Option1OfAttribute1HumanReadableValue,
label: Option1OfAttribute1HumanReadableValue,
},
},
},
attributesQueryValue: attributesQueryValue,
teamId: 1,
orgId,
});
expect(result).toEqual([
{
userId: 1,
result: RaqbLogicResult.MATCH,
},
]);
});
it("should return matching team members with a SINGLE_SELECT attribute when 'Any in'(select_any_in) option is selected", async () => {
const Option1OfAttribute1HumanReadableValue = "Option 1";
const Option1OfAttribute1 = {
id: "attr-1-opt-1",
value: Option1OfAttribute1HumanReadableValue,
slug: "option-1",
};
const Option2OfAttribute1 = {
id: "attr-1-opt-2",
value: "Option 2",
slug: "option-2",
};
const Option3OfAttribute1 = {
id: "attr-1-opt-3",
value: "Option 3",
slug: "option-3",
};
const Attribute1 = {
id: "attr1",
name: "Attribute 1",
type: "SINGLE_SELECT" as const,
slug: "attribute-1",
options: [Option1OfAttribute1, Option2OfAttribute1, Option3OfAttribute1],
};
mockAttributesScenario({
attributes: [Attribute1],
teamMembersWithAttributeOptionValuePerAttribute: [
{ userId: 1, attributes: { [Attribute1.id]: Option1OfAttribute1.value } },
],
});
const attributesQueryValue = buildSelectTypeFieldQueryValue({
rules: [
{
raqbFieldId: Attribute1.id,
value: [[Option1OfAttribute1.id, Option2OfAttribute1.id]],
operator: "select_any_in",
valueType: ["multiselect"],
},
],
}) as AttributesQueryValue;
const { teamMembersMatchingAttributeLogic: result } = await findTeamMembersMatchingAttributeLogic({
dynamicFieldValueOperands: {
fields: [],
response: {},
},
attributesQueryValue,
teamId: 1,
orgId,
});
expect(result).toEqual([
{
userId: 1,
result: RaqbLogicResult.MATCH,
},
]);
});
it("should return matching team members with a MULTI_SELECT attribute when 'Any in'(multiselect_some_in) option is selected and just one option is used in attribute for the user", async () => {
const Option1OfAttribute1HumanReadableValue = "Option 1";
const Option1OfAttribute1 = {
id: "attr-1-opt-1",
value: Option1OfAttribute1HumanReadableValue,
slug: "option-1",
};
const Option2OfAttribute1 = {
id: "attr-1-opt-2",
value: "Option 2",
slug: "option-2",
};
const Option3OfAttribute1 = {
id: "attr-1-opt-3",
value: "Option 3",
slug: "option-3",
};
const Attribute1 = {
id: "attr1",
name: "Attribute 1",
type: "MULTI_SELECT" as const,
slug: "attribute-1",
options: [Option1OfAttribute1, Option2OfAttribute1, Option3OfAttribute1],
};
mockAttributesScenario({
attributes: [Attribute1],
teamMembersWithAttributeOptionValuePerAttribute: [
// user 1 has only one option selected for the attribute
{ userId: 1, attributes: { [Attribute1.id]: Option1OfAttribute1.value } },
],
});
const attributesQueryValue = buildSelectTypeFieldQueryValue({
rules: [
{
raqbFieldId: Attribute1.id,
value: [[Option1OfAttribute1.id, Option2OfAttribute1.id]],
operator: "multiselect_some_in",
valueType: ["multiselect"],
},
],
}) as AttributesQueryValue;
const { teamMembersMatchingAttributeLogic: result } = await findTeamMembersMatchingAttributeLogic({
dynamicFieldValueOperands: {
fields: [],
response: {},
},
attributesQueryValue,
teamId: 1,
orgId,
});
expect(result).toEqual([
{
userId: 1,
result: RaqbLogicResult.MATCH,
},
]);
});
it("should return matching team members with a MULTI_SELECT attribute when 'Any in'(multiselect_some_in) option is selected and more than one option is used in attribute for the user", async () => {
const Option1OfAttribute1HumanReadableValue = "Option 1";
const Option1OfAttribute1 = {
id: "attr-1-opt-1",
value: Option1OfAttribute1HumanReadableValue,
slug: "option-1",
};
const Option2OfAttribute1 = {
id: "attr-1-opt-2",
value: "Option 2",
slug: "option-2",
};
const Option3OfAttribute1 = {
id: "attr-1-opt-3",
value: "Option 3",
slug: "option-3",
};
const Attribute1 = {
id: "attr1",
name: "Attribute 1",
type: "MULTI_SELECT" as const,
slug: "attribute-1",
options: [Option1OfAttribute1, Option2OfAttribute1, Option3OfAttribute1],
};
mockAttributesScenario({
attributes: [Attribute1],
teamMembersWithAttributeOptionValuePerAttribute: [
{
userId: 1,
// user 1 has two options selected for the attribute
attributes: { [Attribute1.id]: [Option2OfAttribute1.value, Option1OfAttribute1.value] },
},
],
});
const attributesQueryValue = buildSelectTypeFieldQueryValue({
rules: [
{
raqbFieldId: Attribute1.id,
value: [[Option1OfAttribute1.id, Option2OfAttribute1.id]],
operator: "multiselect_some_in",
valueType: ["multiselect"],
},
],
}) as AttributesQueryValue;
const { teamMembersMatchingAttributeLogic: result } = await findTeamMembersMatchingAttributeLogic({
dynamicFieldValueOperands: {
fields: [],
response: {},
},
attributesQueryValue,
teamId: 1,
orgId,
});
expect(result).toEqual([
{
userId: 1,
result: RaqbLogicResult.MATCH,
},
]);
});
describe("Fallback", () => {
it("should return null when main attribute logic fails and no fallback is defined", async () => {
const { failingAttributesQueryValue } = buildScenarioWhereMainAttributeLogicFails();
const {
teamMembersMatchingAttributeLogic: result,
checkedFallback,
troubleshooter,
} = await findTeamMembersMatchingAttributeLogic({
dynamicFieldValueOperands: {
fields: [],
response: {},
},
attributesQueryValue: failingAttributesQueryValue,
fallbackAttributesQueryValue: null,
teamId: 1,
orgId,
});
expect(result).toEqual(null);
// We checked the fallback, that is why we know it is not there
expect(checkedFallback).toEqual(true);
expect(troubleshooter).toBeUndefined();
});
it("should return matching members when main attribute logic fails and but fallback matches", async () => {
const { failingAttributesQueryValue, matchingAttributesQueryValue } =
buildScenarioWhereMainAttributeLogicFails();
const {
teamMembersMatchingAttributeLogic: result,
checkedFallback,
troubleshooter,
} = await findTeamMembersMatchingAttributeLogic({
dynamicFieldValueOperands: {
fields: [],
response: {},
},
attributesQueryValue: failingAttributesQueryValue,
fallbackAttributesQueryValue: matchingAttributesQueryValue,
teamId: 1,
orgId,
});
expect(checkedFallback).toEqual(true);
expect(result).toEqual([
{
userId: 1,
result: RaqbLogicResult.MATCH,
},
]);
expect(troubleshooter).toBeUndefined();
});
it("should return 0 matching members when main attribute logic and fallback attribute logic fail", async () => {
const { failingAttributesQueryValue, matchingAttributesQueryValue } =
buildScenarioWhereMainAttributeLogicFails();
const {
teamMembersMatchingAttributeLogic: result,
checkedFallback,
troubleshooter,
} = await findTeamMembersMatchingAttributeLogic({
dynamicFieldValueOperands: {
fields: [],
response: {},
},
attributesQueryValue: failingAttributesQueryValue,
fallbackAttributesQueryValue: failingAttributesQueryValue,
teamId: 1,
orgId,
});
expect(checkedFallback).toEqual(true);
expect(troubleshooter).toBeUndefined();
expect(result).toEqual([]);
});
});
describe("Error handling", () => {
it("should throw an error if the attribute type is not supported", async () => {
const Option1OfAttribute1 = { id: "opt1", value: "Option 1", slug: "option-1" };
const Attribute1 = {
id: "attr1",
name: "Attribute 1",
type: "UNSUPPORTED_ATTRIBUTE_TYPE" as unknown as AttributeType,
slug: "attribute-1",
options: [Option1OfAttribute1],
};
mockAttributesScenario({
attributes: [Attribute1],
teamMembersWithAttributeOptionValuePerAttribute: [
{
userId: 1,
attributes: { [Attribute1.id]: Option1OfAttribute1.value },
},
],
});
await expect(
findTeamMembersMatchingAttributeLogic({
dynamicFieldValueOperands: {
fields: [],
response: {},
},
attributesQueryValue: buildSelectTypeFieldQueryValue({
rules: [
{
raqbFieldId: Attribute1.id,
value: [Option1OfAttribute1.id],
operator: "select_equals",
},
],
}),
teamId: 1,
orgId,
})
).rejects.toThrow("Unsupported attribute type");
});
it("should return warnings in preview and live mode", async () => {
const Option1OfAttribute1HumanReadableValue = "Option 1";
const Option1OfAttribute1 = {
id: "attr-1-opt-1",
value: Option1OfAttribute1HumanReadableValue,
slug: "option-1",
};
const Option2OfAttribute1 = {
id: "attr-1-opt-2",
value: "Option 2",
slug: "option-2",
};
const Option3OfAttribute1 = {
id: "attr-1-opt-3",
value: "Option 3",
slug: "option-3",
};
const Attribute1 = {
id: "attr1",
name: "Attribute 1",
type: "SINGLE_SELECT" as const,
slug: "attribute-1",
options: [Option1OfAttribute1, Option2OfAttribute1, Option3OfAttribute1],
};
mockAttributesScenario({
attributes: [Attribute1],
teamMembersWithAttributeOptionValuePerAttribute: [
{ userId: 1, attributes: { [Attribute1.id]: Option1OfAttribute1.value } },
],
});
const attributesQueryValue = buildSelectTypeFieldQueryValue({
rules: [
{
raqbFieldId: Attribute1.id,
value: [["NON_EXISTING_OPTION_1", "NON_EXISTING_OPTION_2"]],
operator: "select_any_in",
valueType: ["multiselect"],
},
],
}) as AttributesQueryValue;
async function runInMode({ mode }: { mode: "preview" | "live" }) {
const result = await findTeamMembersMatchingAttributeLogic({
dynamicFieldValueOperands: {
fields: [],
response: {},
},
attributesQueryValue,
teamId: 1,
orgId,
isPreview: mode === "preview" ? true : false,
fallbackAttributesQueryValue: null,
});
return result;
}
await (async function liveMode() {
const result = await runInMode({ mode: "live" });
// it will fallback to the fallback attribute logic which isn't defined and thus will return null
expect(result.teamMembersMatchingAttributeLogic).toEqual(null);
expect(result.mainAttributeLogicBuildingWarnings).toEqual([
"Value NON_EXISTING_OPTION_1 is not in list of values",
]);
})();
await (async function previewMode() {
const result = await runInMode({ mode: "preview" });
// it will fallback to the fallback attribute logic which isn't defined and thus will return null
expect(result.teamMembersMatchingAttributeLogic).toEqual(null);
expect(result.mainAttributeLogicBuildingWarnings).toEqual([
"Value NON_EXISTING_OPTION_1 is not in list of values",
]);
})();
});
it("should not throw error if children1 is empty", async () => {
const Option1OfAttribute1HumanReadableValue = "Option 1";
const Option1OfAttribute1 = {
id: "attr-1-opt-1",
value: Option1OfAttribute1HumanReadableValue,
slug: "option-1",
};
const Option2OfAttribute1 = {
id: "attr-1-opt-2",
value: "Option 2",
slug: "option-2",
};
const Option3OfAttribute1 = {
id: "attr-1-opt-3",
value: "Option 3",
slug: "option-3",
};
const Attribute1 = {
id: "attr1",
name: "Attribute 1",
type: "SINGLE_SELECT" as const,
slug: "attribute-1",
options: [Option1OfAttribute1, Option2OfAttribute1, Option3OfAttribute1],
};
mockAttributesScenario({
attributes: [Attribute1],
teamMembersWithAttributeOptionValuePerAttribute: [
{ userId: 1, attributes: { [Attribute1.id]: Option1OfAttribute1.value } },
],
});
const attributesQueryValue = buildQueryValue({
rules: [],
}) as AttributesQueryValue;
expect(attributesQueryValue.children1).toEqual({});
async function runInMode({ mode }: { mode: "preview" | "live" }) {
const { teamMembersMatchingAttributeLogic: result } = await findTeamMembersMatchingAttributeLogic({
dynamicFieldValueOperands: {
fields: [],
response: {},
},
attributesQueryValue,
teamId: 1,
orgId,
isPreview: mode === "preview" ? true : false,
});
return result;
}
await (async function liveMode() {
const result = await runInMode({ mode: "live" });
expect(result).toEqual(null);
})();
await (async function previewMode() {
const result = await runInMode({ mode: "preview" });
expect(result).toEqual(null);
})();
});
});
describe("Performance testing", () => {
describe("20 attributes, 4000 team members", async () => {
// In tests, the performance is actually really bad than real world. So, skipping this test for now
it("should return matching team members with a SINGLE_SELECT attribute when 'all in' option is selected", async () => {
const { attributes } = mockHugeAttributesOfTypeSingleSelect({
numAttributes: 20,
numOptionsPerAttribute: 30,
numTeamMembers: 4000,
numAttributesUsedPerTeamMember: 10,
});
const attributesQueryValue = buildSelectTypeFieldQueryValue({
rules: [
{
raqbFieldId: attributes[0].id,
value: [attributes[0].options[1].id],
operator: "select_equals",
},
],
}) as AttributesQueryValue;
const { teamMembersMatchingAttributeLogic: result, timeTaken } =
await findTeamMembersMatchingAttributeLogic(
{
dynamicFieldValueOperands: {
fields: [],
response: {},
},
attributesQueryValue,
teamId: 1,
orgId,
},
{
concurrency: 1,
enablePerf: true,
}
);
expect(result).toEqual(
expect.arrayContaining([
{
userId: 1,
result: RaqbLogicResult.MATCH,
},
{
userId: 2,
result: RaqbLogicResult.MATCH,
},
{
userId: 3,
result: RaqbLogicResult.MATCH,
},
{
userId: 2000,
result: RaqbLogicResult.MATCH,
},
// Last Item
{
userId: 4000,
result: RaqbLogicResult.MATCH,
},
])
);
if (!timeTaken) {
throw new Error("Looks like performance testing is not enabled");
}
const totalTimeTaken = Object.values(timeTaken).reduce((sum, time) => (sum ?? 0) + (time ?? 0), 0);
console.log("Total time taken", totalTimeTaken, {
timeTaken,
});
expect(totalTimeTaken).toBeLessThan(1000);
// All of them should match
expect(result?.length).toBe(4000);
}, 10000);
});
});
describe("Troubleshooter", () => {
it("troubleshooter data should be there when enableTroubleshooter is true", async () => {
const Option1OfAttribute1HumanReadableValue = "Option 1";
const Option1OfAttribute1 = {
id: "attr-1-opt-1",
value: Option1OfAttribute1HumanReadableValue,
slug: "option-1",
};
const Option2OfAttribute1 = {
id: "attr-1-opt-2",
value: "Option 2",
slug: "option-2",
};
const Option3OfAttribute1 = {
id: "attr-1-opt-3",
value: "Option 3",
slug: "option-3",
};
const Attribute1 = {
id: "attr1",
name: "Attribute 1",
type: "SINGLE_SELECT" as const,
slug: "attribute-1",
options: [Option1OfAttribute1, Option2OfAttribute1, Option3OfAttribute1],
};
mockAttributesScenario({
attributes: [Attribute1],
teamMembersWithAttributeOptionValuePerAttribute: [
{ userId: 1, attributes: { [Attribute1.id]: Option1OfAttribute1.value } },
],
});
const attributesQueryValue = buildSelectTypeFieldQueryValue({
rules: [
{
raqbFieldId: Attribute1.id,
value: [[Option1OfAttribute1.id, Option2OfAttribute1.id]],
operator: "select_any_in",
valueType: ["multiselect"],
},
],
}) as AttributesQueryValue;
const { teamMembersMatchingAttributeLogic: result, troubleshooter } =
await findTeamMembersMatchingAttributeLogic(
{
dynamicFieldValueOperands: {
fields: [],
response: {},
},
attributesQueryValue,
teamId: 1,
orgId,
},
{
enableTroubleshooter: true,
}
);
expect(result).toEqual([
{
userId: 1,
result: RaqbLogicResult.MATCH,
},
]);
expect(troubleshooter).not.toBeUndefined();
});
});
});