@@ -42,8 +42,11 @@ const normalizeSql = (query: CapturedSqlQuery): string =>
4242const getSqlValues = ( query : CapturedSqlQuery ) : unknown [ ] => [
4343 ...( query . values ?? [ ] ) ,
4444] ;
45+ const FLEXI_TALENT_IGNORED_PROJECT_IDS_ENV = "FLEXI_TALENT_IGNORED_PROJECT_IDS" ;
4546
4647describe ( "EngagementsService" , ( ) => {
48+ const originalFlexiTalentIgnoredProjectIds =
49+ process . env [ FLEXI_TALENT_IGNORED_PROJECT_IDS_ENV ] ;
4750 let service : EngagementsService ;
4851 let db : {
4952 $queryRaw : jest . Mock ;
@@ -87,6 +90,25 @@ describe("EngagementsService", () => {
8790 sendAssignmentOfferResponseEmails : jest . Mock ;
8891 } ;
8992
93+ /**
94+ * Creates the service with the current mocks and environment.
95+ *
96+ * Tests that change environment configuration call this after setting the env
97+ * value so the service constructor reads the intended Flexi ignore list.
98+ *
99+ * @returns Service instance under test.
100+ */
101+ const createService = ( ) : EngagementsService =>
102+ new EngagementsService (
103+ db as any ,
104+ projectService as any ,
105+ skillsService as any ,
106+ memberService as any ,
107+ eventBusService as any ,
108+ assignmentOfferEmailService as any ,
109+ assignmentOfferResponseEmailService as any ,
110+ ) ;
111+
90112 const createDto = {
91113 projectId : "project-1" ,
92114 title : "Test Engagement" ,
@@ -167,6 +189,7 @@ describe("EngagementsService", () => {
167189 } ) ;
168190
169191 beforeEach ( ( ) => {
192+ delete process . env [ FLEXI_TALENT_IGNORED_PROJECT_IDS_ENV ] ;
170193 db = {
171194 $queryRaw : jest . fn ( ) ,
172195 $transaction : jest . fn ( ) ,
@@ -210,18 +233,16 @@ describe("EngagementsService", () => {
210233 assignmentOfferResponseEmailService = {
211234 sendAssignmentOfferResponseEmails : jest . fn ( ) . mockResolvedValue ( undefined ) ,
212235 } ;
213- service = new EngagementsService (
214- db as any ,
215- projectService as any ,
216- skillsService as any ,
217- memberService as any ,
218- eventBusService as any ,
219- assignmentOfferEmailService as any ,
220- assignmentOfferResponseEmailService as any ,
221- ) ;
236+ service = createService ( ) ;
222237 } ) ;
223238
224239 afterEach ( ( ) => {
240+ if ( originalFlexiTalentIgnoredProjectIds === undefined ) {
241+ delete process . env [ FLEXI_TALENT_IGNORED_PROJECT_IDS_ENV ] ;
242+ } else {
243+ process . env [ FLEXI_TALENT_IGNORED_PROJECT_IDS_ENV ] =
244+ originalFlexiTalentIgnoredProjectIds ;
245+ }
225246 jest . restoreAllMocks ( ) ;
226247 jest . useRealTimers ( ) ;
227248 } ) ;
@@ -1558,6 +1579,126 @@ describe("EngagementsService", () => {
15581579 } ) ;
15591580 } ) ;
15601581
1582+ it ( "excludes configured ignored projects from Flexi engagement summaries" , async ( ) => {
1583+ process . env [ FLEXI_TALENT_IGNORED_PROJECT_IDS_ENV ] =
1584+ "38965, 1001006, 38965, " ;
1585+ service = createService ( ) ;
1586+ db . engagement . count
1587+ . mockResolvedValueOnce ( 12 )
1588+ . mockResolvedValueOnce ( 7 )
1589+ . mockResolvedValueOnce ( 5 ) ;
1590+
1591+ const result = await service . getFlexiEngagementSummary ( ) ;
1592+
1593+ expect ( db . engagement . count ) . toHaveBeenNthCalledWith ( 1 , {
1594+ where : {
1595+ projectId : { notIn : [ "38965" , "1001006" ] } ,
1596+ } ,
1597+ } ) ;
1598+ expect ( db . engagement . count ) . toHaveBeenNthCalledWith ( 2 , {
1599+ where : {
1600+ projectId : { notIn : [ "38965" , "1001006" ] } ,
1601+ AND : [
1602+ { status : { in : [ EngagementStatus . OPEN , EngagementStatus . ACTIVE ] } } ,
1603+ ] ,
1604+ } ,
1605+ } ) ;
1606+ expect ( db . engagement . count ) . toHaveBeenNthCalledWith ( 3 , {
1607+ where : {
1608+ projectId : { notIn : [ "38965" , "1001006" ] } ,
1609+ AND : [
1610+ {
1611+ status : {
1612+ in : [ EngagementStatus . CLOSED , EngagementStatus . CANCELLED ] ,
1613+ } ,
1614+ } ,
1615+ ] ,
1616+ } ,
1617+ } ) ;
1618+ expect ( result ) . toEqual ( { total : 12 , active : 7 , closed : 5 } ) ;
1619+ } ) ;
1620+
1621+ it ( "filters ignored projects from Flexi engagement name lists and project search matches" , async ( ) => {
1622+ process . env [ FLEXI_TALENT_IGNORED_PROJECT_IDS_ENV ] = "38965,1001006" ;
1623+ service = createService ( ) ;
1624+ projectService . searchFlexiProjectIdsByName . mockResolvedValue ( [
1625+ "38965" ,
1626+ "project-platform" ,
1627+ "1001006" ,
1628+ ] ) ;
1629+ db . engagement . findMany . mockResolvedValue ( [ ] ) ;
1630+ db . engagement . count . mockResolvedValue ( 0 ) ;
1631+
1632+ await service . getFlexiEngagementList ( {
1633+ bucket : FlexiEngagementBucket . Total ,
1634+ searchText : "platform" ,
1635+ sortBy : FlexiEngagementSortBy . Name ,
1636+ sortOrder : "asc" ,
1637+ page : 1 ,
1638+ perPage : 20 ,
1639+ } as FlexiEngagementListQueryDto ) ;
1640+
1641+ expect ( db . engagement . findMany . mock . calls [ 0 ] [ 0 ] . where ) . toEqual ( {
1642+ projectId : { notIn : [ "38965" , "1001006" ] } ,
1643+ AND : [
1644+ {
1645+ OR : [
1646+ {
1647+ title : {
1648+ contains : "platform" ,
1649+ mode : "insensitive" ,
1650+ } ,
1651+ } ,
1652+ { projectId : { in : [ "project-platform" ] } } ,
1653+ ] ,
1654+ } ,
1655+ ] ,
1656+ } ) ;
1657+ } ) ;
1658+
1659+ it ( "filters ignored projects from Flexi engagement member-count SQL" , async ( ) => {
1660+ process . env [ FLEXI_TALENT_IGNORED_PROJECT_IDS_ENV ] = "38965,1001006" ;
1661+ service = createService ( ) ;
1662+ db . $queryRaw
1663+ . mockResolvedValueOnce ( [ { total : "0" } ] )
1664+ . mockResolvedValueOnce ( [ ] ) ;
1665+
1666+ await service . getFlexiEngagementList ( {
1667+ bucket : FlexiEngagementBucket . Total ,
1668+ sortBy : FlexiEngagementSortBy . MemberCount ,
1669+ sortOrder : "desc" ,
1670+ page : 1 ,
1671+ perPage : 20 ,
1672+ } as FlexiEngagementListQueryDto ) ;
1673+
1674+ const countQuery = db . $queryRaw . mock . calls [ 0 ] [ 0 ] ;
1675+ const pageQuery = db . $queryRaw . mock . calls [ 1 ] [ 0 ] ;
1676+
1677+ expect ( normalizeSql ( countQuery ) ) . toContain ( 'e."projectId" NOT IN' ) ;
1678+ expect ( normalizeSql ( pageQuery ) ) . toContain ( 'e."projectId" NOT IN' ) ;
1679+ expect ( getSqlValues ( countQuery ) ) . toEqual (
1680+ expect . arrayContaining ( [ "38965" , "1001006" ] ) ,
1681+ ) ;
1682+ expect ( getSqlValues ( pageQuery ) ) . toEqual (
1683+ expect . arrayContaining ( [ "38965" , "1001006" ] ) ,
1684+ ) ;
1685+ } ) ;
1686+
1687+ it ( "returns not found for ignored Flexi engagement details" , async ( ) => {
1688+ process . env [ FLEXI_TALENT_IGNORED_PROJECT_IDS_ENV ] = "38965,1001006" ;
1689+ service = createService ( ) ;
1690+ db . engagement . findUnique . mockResolvedValue ( {
1691+ ...buildFlexiEngagement ( { projectId : "38965" } ) ,
1692+ assignments : [ ] ,
1693+ } ) ;
1694+
1695+ await expect (
1696+ service . getFlexiEngagementDetail ( "eng-ignored" ) ,
1697+ ) . rejects . toThrow ( "Engagement not found." ) ;
1698+ expect ( projectService . getProjectNamesByIds ) . not . toHaveBeenCalled ( ) ;
1699+ expect ( skillsService . getSkillNamesByIds ) . not . toHaveBeenCalled ( ) ;
1700+ } ) ;
1701+
15611702 it ( "lists Flexi engagements by name with bucket, project search, and top-level pagination" , async ( ) => {
15621703 const query = {
15631704 bucket : FlexiEngagementBucket . Active ,
@@ -1787,6 +1928,96 @@ describe("EngagementsService", () => {
17871928 } ) ;
17881929 } ) ;
17891930
1931+ it ( "excludes ignored project assignments from Flexi member summaries" , async ( ) => {
1932+ process . env [ FLEXI_TALENT_IGNORED_PROJECT_IDS_ENV ] = "38965,1001006" ;
1933+ service = createService ( ) ;
1934+ db . engagementAssignment . findMany . mockResolvedValue ( [ ] ) ;
1935+
1936+ const result = await service . getFlexiMemberSummary ( ) ;
1937+
1938+ expect ( db . engagementAssignment . findMany ) . toHaveBeenCalledWith ( {
1939+ where : {
1940+ status : {
1941+ in : [
1942+ AssignmentStatus . SELECTED ,
1943+ AssignmentStatus . ASSIGNED ,
1944+ AssignmentStatus . OFFER_REJECTED ,
1945+ AssignmentStatus . COMPLETED ,
1946+ AssignmentStatus . TERMINATED ,
1947+ ] ,
1948+ } ,
1949+ engagement : {
1950+ projectId : {
1951+ notIn : [ "38965" , "1001006" ] ,
1952+ } ,
1953+ } ,
1954+ } ,
1955+ select : {
1956+ memberId : true ,
1957+ status : true ,
1958+ } ,
1959+ } ) ;
1960+ expect ( result ) . toEqual ( {
1961+ totalUniqueMembers : 0 ,
1962+ assignedMembers : 0 ,
1963+ completedMembers : 0 ,
1964+ } ) ;
1965+ } ) ;
1966+
1967+ it ( "excludes ignored project assignments from Flexi member list SQL" , async ( ) => {
1968+ process . env [ FLEXI_TALENT_IGNORED_PROJECT_IDS_ENV ] = "38965,1001006" ;
1969+ service = createService ( ) ;
1970+ db . $queryRaw
1971+ . mockResolvedValueOnce ( [ { total : 0n } ] )
1972+ . mockResolvedValueOnce ( [ ] ) ;
1973+
1974+ await service . getFlexiMemberList ( new FlexiMemberListQueryDto ( ) ) ;
1975+
1976+ const countQuery = db . $queryRaw . mock . calls [ 0 ] [ 0 ] ;
1977+ const pageQuery = db . $queryRaw . mock . calls [ 1 ] [ 0 ] ;
1978+
1979+ expect ( normalizeSql ( countQuery ) ) . toContain ( 'e."projectId" NOT IN' ) ;
1980+ expect ( normalizeSql ( pageQuery ) ) . toContain ( 'e."projectId" NOT IN' ) ;
1981+ expect ( getSqlValues ( countQuery ) ) . toEqual (
1982+ expect . arrayContaining ( [ "38965" , "1001006" ] ) ,
1983+ ) ;
1984+ expect ( getSqlValues ( pageQuery ) ) . toEqual (
1985+ expect . arrayContaining ( [ "38965" , "1001006" ] ) ,
1986+ ) ;
1987+ } ) ;
1988+
1989+ it ( "excludes ignored project assignments from Flexi member detail lookups" , async ( ) => {
1990+ process . env [ FLEXI_TALENT_IGNORED_PROJECT_IDS_ENV ] = "38965,1001006" ;
1991+ service = createService ( ) ;
1992+ db . engagementAssignment . findMany . mockResolvedValue ( [ ] ) ;
1993+
1994+ await expect (
1995+ service . getFlexiMemberDetail ( "member-ignored" ) ,
1996+ ) . rejects . toThrow ( "Member assignment history not found." ) ;
1997+ expect ( db . engagementAssignment . findMany ) . toHaveBeenCalledWith ( {
1998+ where : {
1999+ status : {
2000+ in : [
2001+ AssignmentStatus . SELECTED ,
2002+ AssignmentStatus . ASSIGNED ,
2003+ AssignmentStatus . OFFER_REJECTED ,
2004+ AssignmentStatus . COMPLETED ,
2005+ AssignmentStatus . TERMINATED ,
2006+ ] ,
2007+ } ,
2008+ engagement : {
2009+ projectId : {
2010+ notIn : [ "38965" , "1001006" ] ,
2011+ } ,
2012+ } ,
2013+ memberId : "member-ignored" ,
2014+ } ,
2015+ include : {
2016+ engagement : true ,
2017+ } ,
2018+ } ) ;
2019+ } ) ;
2020+
17902021 it ( "defaults Flexi member list requests to handle ascending" , async ( ) => {
17912022 const query = new FlexiMemberListQueryDto ( ) ;
17922023
0 commit comments