11import { Test , TestingModule } from '@nestjs/testing' ;
22import { NotFoundException } from '@nestjs/common' ;
33import { FrameworksService } from './frameworks.service' ;
4+ import { TimelinesService } from '../timelines/timelines.service' ;
45
56jest . mock ( '@db' , ( ) => ( {
67 db : {
@@ -17,6 +18,7 @@ jest.mock('@db', () => ({
1718 findMany : jest . fn ( ) ,
1819 findFirst : jest . fn ( ) ,
1920 create : jest . fn ( ) ,
21+ createManyAndReturn : jest . fn ( ) ,
2022 } ,
2123 requirementMap : {
2224 findMany : jest . fn ( ) ,
@@ -29,6 +31,9 @@ jest.mock('@db', () => ({
2931 findMany : jest . fn ( ) ,
3032 } ,
3133 } ,
34+ // The frameworks-timeline helper imports FindingType (a Prisma enum) at module
35+ // load. Stub it so the spec file can be evaluated without the real client.
36+ FindingType : { soc2 : 'soc2' , iso27001 : 'iso27001' } ,
3237} ) ) ;
3338
3439jest . mock ( './frameworks-scores.helper' , ( ) => ( {
@@ -50,7 +55,10 @@ describe('FrameworksService', () => {
5055
5156 beforeEach ( async ( ) => {
5257 const module : TestingModule = await Test . createTestingModule ( {
53- providers : [ FrameworksService ] ,
58+ providers : [
59+ FrameworksService ,
60+ { provide : TimelinesService , useValue : { } } ,
61+ ] ,
5462 } ) . compile ( ) ;
5563
5664 service = module . get < FrameworksService > ( FrameworksService ) ;
@@ -192,12 +200,13 @@ describe('FrameworksService', () => {
192200 expect ( result . requirementDefinitions ) . toHaveLength ( 1 ) ;
193201 } ) ;
194202
195- it ( 'findOne on a platform FI reads only FrameworkEditorRequirement ' , async ( ) => {
203+ it ( 'findOne on a platform FI merges per-instance custom requirements with platform requirements ' , async ( ) => {
196204 ( mockDb . frameworkInstance . findUnique as jest . Mock ) . mockResolvedValue ( {
197205 id : 'fi_platform' ,
198206 organizationId : 'org_A' ,
199207 frameworkId : 'frk_soc2' ,
200208 customFrameworkId : null ,
209+ currentVersionId : null ,
201210 framework : { id : 'frk_soc2' , name : 'SOC 2' } ,
202211 customFramework : null ,
203212 requirementsMapped : [ ] ,
@@ -207,32 +216,83 @@ describe('FrameworksService', () => {
207216 ) . mockResolvedValue ( [
208217 { id : 'frk_rq_1' , name : 'CC1' , identifier : 'cc1-1' , description : '' } ,
209218 ] ) ;
219+ // Per-instance custom requirement attached directly to fi_platform.
220+ ( mockDb . customRequirement . findMany as jest . Mock ) . mockResolvedValue ( [
221+ {
222+ id : 'creq_local' ,
223+ name : 'Org-local extra' ,
224+ identifier : 'X1' ,
225+ description : '' ,
226+ } ,
227+ ] ) ;
210228 ( mockDb . task . findMany as jest . Mock ) . mockResolvedValue ( [ ] ) ;
211229 ( mockDb . requirementMap . findMany as jest . Mock ) . mockResolvedValue ( [ ] ) ;
212230 ( mockDb . evidenceSubmission . findMany as jest . Mock ) . mockResolvedValue ( [ ] ) ;
213231
214- await service . findOne ( 'fi_platform' , 'org_A' ) ;
232+ const result = await service . findOne ( 'fi_platform' , 'org_A' ) ;
215233
216234 expect ( mockDb . frameworkEditorRequirement . findMany ) . toHaveBeenCalledWith ( {
217235 where : { frameworkId : 'frk_soc2' } ,
218236 orderBy : { name : 'asc' } ,
219237 } ) ;
220- expect ( mockDb . customRequirement . findMany ) . not . toHaveBeenCalled ( ) ;
238+ expect ( mockDb . customRequirement . findMany ) . toHaveBeenCalledWith ( {
239+ where : { frameworkInstanceId : 'fi_platform' } ,
240+ orderBy : { name : 'asc' } ,
241+ } ) ;
242+ const ids = result . requirementDefinitions . map ( ( r : any ) => r . id ) ;
243+ expect ( ids ) . toEqual ( expect . arrayContaining ( [ 'frk_rq_1' , 'creq_local' ] ) ) ;
244+ } ) ;
245+
246+ it ( 'createRequirement on a custom-framework FI hangs the row off the framework' , async ( ) => {
247+ ( mockDb . frameworkInstance . findUnique as jest . Mock ) . mockResolvedValue ( {
248+ id : 'fi_custom' ,
249+ customFrameworkId : 'cfrm_A' ,
250+ } ) ;
251+ ( mockDb . customRequirement . create as jest . Mock ) . mockResolvedValue ( {
252+ id : 'creq_new' ,
253+ } ) ;
254+
255+ await service . createRequirement ( 'fi_custom' , 'org_A' , {
256+ name : 'x' ,
257+ identifier : 'x' ,
258+ description : 'x' ,
259+ } ) ;
260+
261+ expect ( mockDb . customRequirement . create ) . toHaveBeenCalledWith ( {
262+ data : {
263+ name : 'x' ,
264+ identifier : 'x' ,
265+ description : 'x' ,
266+ organizationId : 'org_A' ,
267+ customFrameworkId : 'cfrm_A' ,
268+ } ,
269+ } ) ;
221270 } ) ;
222271
223- it ( 'createRequirement rejects a platform framework instance' , async ( ) => {
272+ it ( 'createRequirement on a platform FI hangs the row off the instance' , async ( ) => {
224273 ( mockDb . frameworkInstance . findUnique as jest . Mock ) . mockResolvedValue ( {
274+ id : 'fi_platform' ,
225275 customFrameworkId : null ,
226276 } ) ;
277+ ( mockDb . customRequirement . create as jest . Mock ) . mockResolvedValue ( {
278+ id : 'creq_new' ,
279+ } ) ;
227280
228- await expect (
229- service . createRequirement ( 'fi_platform' , 'org_A' , {
281+ await service . createRequirement ( 'fi_platform' , 'org_A' , {
282+ name : 'x' ,
283+ identifier : 'x' ,
284+ description : 'x' ,
285+ } ) ;
286+
287+ expect ( mockDb . customRequirement . create ) . toHaveBeenCalledWith ( {
288+ data : {
230289 name : 'x' ,
231290 identifier : 'x' ,
232291 description : 'x' ,
233- } ) ,
234- ) . rejects . toThrow ( / C a n n o t a d d c u s t o m r e q u i r e m e n t s / ) ;
235- expect ( mockDb . customRequirement . create ) . not . toHaveBeenCalled ( ) ;
292+ organizationId : 'org_A' ,
293+ frameworkInstanceId : 'fi_platform' ,
294+ } ,
295+ } ) ;
236296 } ) ;
237297 } ) ;
238298} ) ;
0 commit comments