Skip to content

Commit 11b65b2

Browse files
hariombalharajoeauyeungdevin-ai-integration[bot]Udit-takkar
authored
test: add missing negation operator tests for TEXT, NUMBER, and compound rules (calcom#27690)
* Fix exclusion filter - include all team members * Fix display when members aren't saved in the DB * Update tests * test: add missing negation operator tests for TEXT, NUMBER, and compound rules Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com> * fix: revert non-intentional changes to AddMembersWithSwitch.tsx Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com> --------- Co-authored-by: Joe Au-Yeung <j.auyeung419@gmail.com> Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Co-authored-by: Udit Takkar <53316345+Udit-takkar@users.noreply.github.com>
1 parent 4c73695 commit 11b65b2

1 file changed

Lines changed: 268 additions & 0 deletions

File tree

packages/features/routing-forms/lib/findTeamMembersMatchingAttributeLogic.integration-test.ts

Lines changed: 268 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1953,4 +1953,272 @@ describe("findTeamMembersMatchingAttributeLogic", () => {
19531953
});
19541954
});
19551955
});
1956+
1957+
describe("negation operators with TEXT and NUMBER attribute types", () => {
1958+
describe("TEXT not_equal", () => {
1959+
it("should match users without the attribute (undefined != 'sales manager' is true)", async () => {
1960+
const JobTitleAttribute = {
1961+
id: "job-title-attr",
1962+
name: "Job Title",
1963+
type: AttributeType.TEXT,
1964+
slug: "job-title",
1965+
options: [
1966+
{ id: "job-sales-mgr", value: "Sales Manager", slug: "sales-manager" },
1967+
{ id: "job-engineer", value: "Engineer", slug: "engineer" },
1968+
],
1969+
};
1970+
1971+
const { createdUsers } = await createAttributesScenario({
1972+
attributes: [JobTitleAttribute],
1973+
teamMembersWithAttributeOptionValuePerAttribute: [
1974+
{ attributes: { [JobTitleAttribute.id]: "Sales Manager" } },
1975+
{ attributes: { [JobTitleAttribute.id]: "Engineer" } },
1976+
{ attributes: {} },
1977+
],
1978+
});
1979+
1980+
const attributesQueryValue = buildQueryValue({
1981+
rules: [
1982+
{
1983+
raqbFieldId: JobTitleAttribute.id,
1984+
value: ["sales manager"],
1985+
operator: "not_equal",
1986+
valueSrc: ["value"],
1987+
valueType: ["text"],
1988+
},
1989+
],
1990+
}) as AttributesQueryValue;
1991+
1992+
const { teamMembersMatchingAttributeLogic: result } = await findTeamMembersMatchingAttributeLogic({
1993+
dynamicFieldValueOperands: { fields: [], response: {} },
1994+
attributesQueryValue,
1995+
teamId: testFixtures.team.id,
1996+
orgId: testFixtures.org.id,
1997+
});
1998+
1999+
expect(result).toEqual(
2000+
expect.arrayContaining([
2001+
{ userId: createdUsers[1].userId, result: RaqbLogicResult.MATCH },
2002+
{ userId: createdUsers[2].userId, result: RaqbLogicResult.MATCH },
2003+
])
2004+
);
2005+
expect(result).not.toContainEqual({ userId: createdUsers[0].userId, result: RaqbLogicResult.MATCH });
2006+
});
2007+
});
2008+
2009+
describe("TEXT not_like", () => {
2010+
it("should match users without the attribute (undefined not contains 'engineer' is true)", async () => {
2011+
const JobTitleAttribute = {
2012+
id: "job-title-attr-2",
2013+
name: "Job Title",
2014+
type: AttributeType.TEXT,
2015+
slug: "job-title-2",
2016+
options: [
2017+
{ id: "job-sr-eng", value: "Senior Engineer", slug: "senior-engineer" },
2018+
{ id: "job-designer", value: "Designer", slug: "designer" },
2019+
],
2020+
};
2021+
2022+
const { createdUsers } = await createAttributesScenario({
2023+
attributes: [JobTitleAttribute],
2024+
teamMembersWithAttributeOptionValuePerAttribute: [
2025+
{ attributes: { [JobTitleAttribute.id]: "Senior Engineer" } },
2026+
{ attributes: { [JobTitleAttribute.id]: "Designer" } },
2027+
{ attributes: {} },
2028+
],
2029+
});
2030+
2031+
const attributesQueryValue = buildQueryValue({
2032+
rules: [
2033+
{
2034+
raqbFieldId: JobTitleAttribute.id,
2035+
value: ["engineer"],
2036+
operator: "not_like",
2037+
valueSrc: ["value"],
2038+
valueType: ["text"],
2039+
},
2040+
],
2041+
}) as AttributesQueryValue;
2042+
2043+
const { teamMembersMatchingAttributeLogic: result } = await findTeamMembersMatchingAttributeLogic({
2044+
dynamicFieldValueOperands: { fields: [], response: {} },
2045+
attributesQueryValue,
2046+
teamId: testFixtures.team.id,
2047+
orgId: testFixtures.org.id,
2048+
});
2049+
2050+
expect(result).toEqual(
2051+
expect.arrayContaining([
2052+
{ userId: createdUsers[1].userId, result: RaqbLogicResult.MATCH },
2053+
{ userId: createdUsers[2].userId, result: RaqbLogicResult.MATCH },
2054+
])
2055+
);
2056+
expect(result).not.toContainEqual({ userId: createdUsers[0].userId, result: RaqbLogicResult.MATCH });
2057+
});
2058+
});
2059+
2060+
describe("NUMBER not_equal", () => {
2061+
it("should match users without the attribute (undefined != '5' is true)", async () => {
2062+
const ExperienceAttribute = {
2063+
id: "exp-attr",
2064+
name: "Experience Years",
2065+
type: AttributeType.NUMBER,
2066+
slug: "experience-years",
2067+
options: [
2068+
{ id: "exp-5", value: "5", slug: "5" },
2069+
{ id: "exp-10", value: "10", slug: "10" },
2070+
],
2071+
};
2072+
2073+
const { createdUsers } = await createAttributesScenario({
2074+
attributes: [ExperienceAttribute],
2075+
teamMembersWithAttributeOptionValuePerAttribute: [
2076+
{ attributes: { [ExperienceAttribute.id]: "5" } },
2077+
{ attributes: { [ExperienceAttribute.id]: "10" } },
2078+
{ attributes: {} },
2079+
],
2080+
});
2081+
2082+
const attributesQueryValue = buildQueryValue({
2083+
rules: [
2084+
{
2085+
raqbFieldId: ExperienceAttribute.id,
2086+
value: ["5"],
2087+
operator: "not_equal",
2088+
valueSrc: ["value"],
2089+
valueType: ["number"],
2090+
},
2091+
],
2092+
}) as AttributesQueryValue;
2093+
2094+
const { teamMembersMatchingAttributeLogic: result } = await findTeamMembersMatchingAttributeLogic({
2095+
dynamicFieldValueOperands: { fields: [], response: {} },
2096+
attributesQueryValue,
2097+
teamId: testFixtures.team.id,
2098+
orgId: testFixtures.org.id,
2099+
});
2100+
2101+
expect(result).toEqual(
2102+
expect.arrayContaining([
2103+
{ userId: createdUsers[1].userId, result: RaqbLogicResult.MATCH },
2104+
{ userId: createdUsers[2].userId, result: RaqbLogicResult.MATCH },
2105+
])
2106+
);
2107+
expect(result).not.toContainEqual({ userId: createdUsers[0].userId, result: RaqbLogicResult.MATCH });
2108+
});
2109+
});
2110+
});
2111+
2112+
describe("compound rules with users missing some attributes", () => {
2113+
const DepartmentAttribute = {
2114+
id: "dept-attr-2",
2115+
name: "Department",
2116+
type: AttributeType.SINGLE_SELECT,
2117+
slug: "department-2",
2118+
options: [
2119+
{ id: "dept-sales-2", value: "Sales", slug: "sales" },
2120+
{ id: "dept-eng-2", value: "Engineering", slug: "engineering" },
2121+
],
2122+
};
2123+
2124+
const LocationAttribute = {
2125+
id: "loc-attr-2",
2126+
name: "Location",
2127+
type: AttributeType.SINGLE_SELECT,
2128+
slug: "location-2",
2129+
options: [
2130+
{ id: "loc-nyc-2", value: "NYC", slug: "nyc" },
2131+
{ id: "loc-la-2", value: "LA", slug: "la" },
2132+
],
2133+
};
2134+
2135+
it("positive AND negation: should match user with one attribute but missing the negated one", async () => {
2136+
const { createdUsers } = await createAttributesScenario({
2137+
attributes: [DepartmentAttribute, LocationAttribute],
2138+
teamMembersWithAttributeOptionValuePerAttribute: [
2139+
{
2140+
attributes: { [DepartmentAttribute.id]: "Sales", [LocationAttribute.id]: "NYC" },
2141+
},
2142+
{ attributes: { [DepartmentAttribute.id]: "Sales" } },
2143+
{ attributes: {} },
2144+
],
2145+
});
2146+
2147+
const attributesQueryValue = buildSelectTypeFieldQueryValue({
2148+
rules: [
2149+
{
2150+
raqbFieldId: DepartmentAttribute.id,
2151+
value: ["dept-sales-2"],
2152+
operator: "select_equals",
2153+
},
2154+
{
2155+
raqbFieldId: LocationAttribute.id,
2156+
value: ["loc-nyc-2"],
2157+
operator: "select_not_equals",
2158+
},
2159+
],
2160+
}) as AttributesQueryValue;
2161+
2162+
const { teamMembersMatchingAttributeLogic: result } = await findTeamMembersMatchingAttributeLogic({
2163+
dynamicFieldValueOperands: { fields: [], response: {} },
2164+
attributesQueryValue,
2165+
teamId: testFixtures.team.id,
2166+
orgId: testFixtures.org.id,
2167+
});
2168+
2169+
// User 0: Dept=Sales (== matches) AND Location=NYC (!= fails) -> NO MATCH
2170+
// User 1: Dept=Sales (== matches) AND Location=undefined (!= matches) -> MATCH
2171+
// User 2: no Dept (== fails) -> NO MATCH (AND short-circuits)
2172+
expect(result).toEqual([{ userId: createdUsers[1].userId, result: RaqbLogicResult.MATCH }]);
2173+
});
2174+
2175+
it("multiple negation rules: should match users missing different attributes", async () => {
2176+
const { createdUsers } = await createAttributesScenario({
2177+
attributes: [DepartmentAttribute, LocationAttribute],
2178+
teamMembersWithAttributeOptionValuePerAttribute: [
2179+
{
2180+
attributes: { [DepartmentAttribute.id]: "Sales", [LocationAttribute.id]: "NYC" },
2181+
},
2182+
{ attributes: { [DepartmentAttribute.id]: "Engineering" } },
2183+
{ attributes: { [LocationAttribute.id]: "LA" } },
2184+
{ attributes: {} },
2185+
],
2186+
});
2187+
2188+
const attributesQueryValue = buildSelectTypeFieldQueryValue({
2189+
rules: [
2190+
{
2191+
raqbFieldId: DepartmentAttribute.id,
2192+
value: ["dept-sales-2"],
2193+
operator: "select_not_equals",
2194+
},
2195+
{
2196+
raqbFieldId: LocationAttribute.id,
2197+
value: ["loc-nyc-2"],
2198+
operator: "select_not_equals",
2199+
},
2200+
],
2201+
}) as AttributesQueryValue;
2202+
2203+
const { teamMembersMatchingAttributeLogic: result } = await findTeamMembersMatchingAttributeLogic({
2204+
dynamicFieldValueOperands: { fields: [], response: {} },
2205+
attributesQueryValue,
2206+
teamId: testFixtures.team.id,
2207+
orgId: testFixtures.org.id,
2208+
});
2209+
2210+
// User 0: Dept=Sales (!= fails) -> NO MATCH
2211+
// User 1: Dept=Engineering (!= matches) AND Location=undefined (!= matches) -> MATCH
2212+
// User 2: Dept=undefined (!= matches) AND Location=LA (!= matches) -> MATCH
2213+
// User 3: both undefined (both != match) -> MATCH
2214+
expect(result).toEqual(
2215+
expect.arrayContaining([
2216+
{ userId: createdUsers[1].userId, result: RaqbLogicResult.MATCH },
2217+
{ userId: createdUsers[2].userId, result: RaqbLogicResult.MATCH },
2218+
{ userId: createdUsers[3].userId, result: RaqbLogicResult.MATCH },
2219+
])
2220+
);
2221+
expect(result).not.toContainEqual({ userId: createdUsers[0].userId, result: RaqbLogicResult.MATCH });
2222+
});
2223+
});
19562224
});

0 commit comments

Comments
 (0)