@@ -56,7 +56,17 @@ jest.mock('@db', () => ({
5656 FindingType : {
5757 soc2 : 'soc2' ,
5858 iso27001 : 'iso27001' ,
59+ hipaa : 'hipaa' ,
60+ gdpr : 'gdpr' ,
61+ nist : 'nist' ,
5962 } ,
63+ FindingStatus : {
64+ open : 'open' ,
65+ closed : 'closed' ,
66+ } ,
67+ PhaseCompletionType : { } ,
68+ TimelinePhaseStatus : { } ,
69+ TimelineStatus : { } ,
6070} ) ) ;
6171
6272jest . mock ( '@trigger.dev/sdk' , ( ) => ( {
@@ -394,14 +404,39 @@ describe('PoliciesController', () => {
394404 } ) ;
395405
396406 describe ( 'getPolicyControls' , ( ) => {
397- it ( 'should return mapped and all controls' , async ( ) => {
407+ it ( 'returns mapped and all controls with framework names derived from requirementsMapped ' , async ( ) => {
398408 const { db } = require ( '@db' ) ;
399409 const mappedControls = [
400- { id : 'ctrl_1' , name : 'Control 1' , description : 'desc' } ,
410+ {
411+ id : 'ctrl_1' ,
412+ name : 'Control 1' ,
413+ description : 'desc' ,
414+ requirementsMapped : [
415+ {
416+ frameworkInstance : {
417+ id : 'fi_1' ,
418+ framework : { id : 'fw_soc2' , name : 'SOC 2' } ,
419+ customFramework : null ,
420+ } ,
421+ } ,
422+ {
423+ frameworkInstance : {
424+ id : 'fi_2' ,
425+ framework : null ,
426+ customFramework : { id : 'cfw_1' , name : 'Internal Policy' } ,
427+ } ,
428+ } ,
429+ ] ,
430+ } ,
401431 ] ;
402432 const allControls = [
403- { id : 'ctrl_1' , name : 'Control 1' , description : 'desc' } ,
404- { id : 'ctrl_2' , name : 'Control 2' , description : 'desc2' } ,
433+ ...mappedControls ,
434+ {
435+ id : 'ctrl_2' ,
436+ name : 'Control 2' ,
437+ description : 'desc2' ,
438+ requirementsMapped : [ ] ,
439+ } ,
405440 ] ;
406441 db . policy . findFirst . mockResolvedValue ( {
407442 id : 'pol_1' ,
@@ -415,12 +450,80 @@ describe('PoliciesController', () => {
415450 mockAuthContext ,
416451 ) ;
417452
418- expect ( result . mappedControls ) . toEqual ( mappedControls ) ;
419- expect ( result . allControls ) . toEqual ( allControls ) ;
453+ expect ( result . mappedControls ) . toEqual ( [
454+ {
455+ id : 'ctrl_1' ,
456+ name : 'Control 1' ,
457+ description : 'desc' ,
458+ frameworks : [
459+ { id : 'fw_soc2' , name : 'SOC 2' } ,
460+ { id : 'cfw_1' , name : 'Internal Policy' } ,
461+ ] ,
462+ } ,
463+ ] ) ;
464+ expect ( result . allControls ) . toEqual ( [
465+ {
466+ id : 'ctrl_1' ,
467+ name : 'Control 1' ,
468+ description : 'desc' ,
469+ frameworks : [
470+ { id : 'fw_soc2' , name : 'SOC 2' } ,
471+ { id : 'cfw_1' , name : 'Internal Policy' } ,
472+ ] ,
473+ } ,
474+ {
475+ id : 'ctrl_2' ,
476+ name : 'Control 2' ,
477+ description : 'desc2' ,
478+ frameworks : [ ] ,
479+ } ,
480+ ] ) ;
420481 expect ( result . authType ) . toBe ( 'session' ) ;
421482 } ) ;
422483
423- it ( 'should return empty mappedControls when policy not found' , async ( ) => {
484+ it ( 'dedupes frameworks when the same FrameworkInstance is reachable via multiple RequirementMaps' , async ( ) => {
485+ const { db } = require ( '@db' ) ;
486+ const controls = [
487+ {
488+ id : 'ctrl_1' ,
489+ name : 'Control 1' ,
490+ description : 'desc' ,
491+ requirementsMapped : [
492+ {
493+ frameworkInstance : {
494+ id : 'fi_1' ,
495+ framework : { id : 'fw_soc2' , name : 'SOC 2' } ,
496+ customFramework : null ,
497+ } ,
498+ } ,
499+ {
500+ frameworkInstance : {
501+ id : 'fi_1' ,
502+ framework : { id : 'fw_soc2' , name : 'SOC 2' } ,
503+ customFramework : null ,
504+ } ,
505+ } ,
506+ ] ,
507+ } ,
508+ ] ;
509+ db . policy . findFirst . mockResolvedValue ( {
510+ id : 'pol_1' ,
511+ controls,
512+ } ) ;
513+ db . control . findMany . mockResolvedValue ( controls ) ;
514+
515+ const result = await controller . getPolicyControls (
516+ 'pol_1' ,
517+ orgId ,
518+ mockAuthContext ,
519+ ) ;
520+
521+ expect ( result . mappedControls [ 0 ] . frameworks ) . toEqual ( [
522+ { id : 'fw_soc2' , name : 'SOC 2' } ,
523+ ] ) ;
524+ } ) ;
525+
526+ it ( 'returns empty mappedControls when policy is not found' , async ( ) => {
424527 const { db } = require ( '@db' ) ;
425528 db . policy . findFirst . mockResolvedValue ( null ) ;
426529 db . control . findMany . mockResolvedValue ( [ ] ) ;
@@ -433,6 +536,33 @@ describe('PoliciesController', () => {
433536
434537 expect ( result . mappedControls ) . toEqual ( [ ] ) ;
435538 } ) ;
539+
540+ it ( 'scopes the requirementsMapped query to the caller organization' , async ( ) => {
541+ const { db } = require ( '@db' ) ;
542+ db . policy . findFirst . mockResolvedValue ( { id : 'pol_1' , controls : [ ] } ) ;
543+ db . control . findMany . mockResolvedValue ( [ ] ) ;
544+
545+ await controller . getPolicyControls ( 'pol_1' , orgId , mockAuthContext ) ;
546+
547+ expect ( db . policy . findFirst ) . toHaveBeenCalledWith (
548+ expect . objectContaining ( {
549+ where : { id : 'pol_1' , organizationId : orgId , archivedAt : null } ,
550+ select : expect . objectContaining ( {
551+ controls : expect . objectContaining ( {
552+ where : { archivedAt : null } ,
553+ select : expect . objectContaining ( {
554+ requirementsMapped : expect . objectContaining ( {
555+ where : {
556+ archivedAt : null ,
557+ frameworkInstance : { organizationId : orgId } ,
558+ } ,
559+ } ) ,
560+ } ) ,
561+ } ) ,
562+ } ) ,
563+ } ) ,
564+ ) ;
565+ } ) ;
436566 } ) ;
437567
438568 describe ( 'addPolicyControls' , ( ) => {
@@ -724,4 +854,87 @@ describe('PoliciesController', () => {
724854 expect ( result . data ) . toEqual ( mockResult ) ;
725855 } ) ;
726856 } ) ;
857+
858+ describe ( 'getPolicyEvidenceTasks' , ( ) => {
859+ it ( 'returns tasks grouped by control, excluding archived tasks' , async ( ) => {
860+ const { db } = require ( '@db' ) ;
861+ db . policy . findFirst . mockResolvedValue ( {
862+ id : 'pol_1' ,
863+ controls : [
864+ {
865+ id : 'ctl_1' ,
866+ name : 'Access Controls' ,
867+ tasks : [
868+ {
869+ id : 'tsk_1' ,
870+ title : 'Enable 2FA' ,
871+ status : 'in_progress' ,
872+ frequency : 'monthly' ,
873+ department : 'it' ,
874+ automationStatus : 'MANUAL' ,
875+ assigneeId : 'mem_1' ,
876+ } ,
877+ ] ,
878+ } ,
879+ {
880+ id : 'ctl_2' ,
881+ name : 'Monitoring' ,
882+ tasks : [ ] ,
883+ } ,
884+ ] ,
885+ } ) ;
886+
887+ const result = await controller . getPolicyEvidenceTasks (
888+ 'pol_1' ,
889+ orgId ,
890+ mockAuthContext ,
891+ ) ;
892+
893+ expect ( db . policy . findFirst ) . toHaveBeenCalledWith ( {
894+ where : { id : 'pol_1' , organizationId : orgId , archivedAt : null } ,
895+ select : expect . objectContaining ( {
896+ id : true ,
897+ controls : expect . objectContaining ( {
898+ where : { archivedAt : null , organizationId : orgId } ,
899+ select : expect . objectContaining ( {
900+ tasks : expect . objectContaining ( {
901+ where : { archivedAt : null , organizationId : orgId } ,
902+ } ) ,
903+ } ) ,
904+ } ) ,
905+ } ) ,
906+ } ) ;
907+ expect ( result . data ) . toEqual ( [
908+ {
909+ control : { id : 'ctl_1' , name : 'Access Controls' } ,
910+ tasks : [
911+ {
912+ id : 'tsk_1' ,
913+ title : 'Enable 2FA' ,
914+ status : 'in_progress' ,
915+ frequency : 'monthly' ,
916+ department : 'it' ,
917+ automationStatus : 'MANUAL' ,
918+ assigneeId : 'mem_1' ,
919+ } ,
920+ ] ,
921+ } ,
922+ {
923+ control : { id : 'ctl_2' , name : 'Monitoring' } ,
924+ tasks : [ ] ,
925+ } ,
926+ ] ) ;
927+ expect ( result . count ) . toBe ( 1 ) ;
928+ expect ( result . authType ) . toBe ( 'session' ) ;
929+ } ) ;
930+
931+ it ( 'throws NotFoundException when policy is not in caller org' , async ( ) => {
932+ const { db } = require ( '@db' ) ;
933+ db . policy . findFirst . mockResolvedValue ( null ) ;
934+
935+ await expect (
936+ controller . getPolicyEvidenceTasks ( 'pol_404' , orgId , mockAuthContext ) ,
937+ ) . rejects . toThrow ( 'Policy not found' ) ;
938+ } ) ;
939+ } ) ;
727940} ) ;
0 commit comments