@@ -86,6 +86,7 @@ describe("BenchmarkDefinitionService", () => {
8686 createdAt : new Date ( ) ,
8787 dataset : {
8888 name : "Test Dataset" ,
89+ group_id : "test-group" ,
8990 } ,
9091 } ;
9192
@@ -114,6 +115,7 @@ describe("BenchmarkDefinitionService", () => {
114115 lineage : {
115116 id : "workflow-1" ,
116117 name : "Test Workflow" ,
118+ group_id : "test-group" ,
117119 } ,
118120 } ;
119121
@@ -502,6 +504,44 @@ describe("BenchmarkDefinitionService", () => {
502504 ) ;
503505 } ) ;
504506
507+ it ( "rejects a dataset version owned by another group (cross-group reference blocked)" , async ( ) => {
508+ jest
509+ . spyOn ( prisma . benchmarkProject , "findUnique" )
510+ . mockResolvedValue ( mockProject ) ; // group "test-group"
511+ // Dataset version exists but its dataset belongs to a different group.
512+ jest . spyOn ( prisma . datasetVersion , "findUnique" ) . mockResolvedValue ( {
513+ ...mockDatasetVersion ,
514+ dataset : { name : "Other" , group_id : "other-group" } ,
515+ } ) ;
516+
517+ await expect (
518+ service . createDefinition ( "project-1" , createDto ) ,
519+ ) . rejects . toThrow (
520+ 'Dataset version with ID "ds-version-1" does not exist' ,
521+ ) ;
522+ } ) ;
523+
524+ it ( "rejects a workflow version owned by another group (cross-group reference blocked)" , async ( ) => {
525+ jest
526+ . spyOn ( prisma . benchmarkProject , "findUnique" )
527+ . mockResolvedValue ( mockProject ) ; // group "test-group"
528+ jest
529+ . spyOn ( prisma . datasetVersion , "findUnique" )
530+ . mockResolvedValue ( mockDatasetVersion ) ;
531+ jest . spyOn ( prisma . split , "findUnique" ) . mockResolvedValue ( mockSplit ) ;
532+ // Workflow version exists but its lineage belongs to a different group.
533+ jest . spyOn ( prisma . workflowVersion , "findUnique" ) . mockResolvedValue ( {
534+ ...mockWorkflowVersion ,
535+ lineage : { ...mockWorkflowVersion . lineage , group_id : "other-group" } ,
536+ } ) ;
537+
538+ await expect (
539+ service . createDefinition ( "project-1" , createDto ) ,
540+ ) . rejects . toThrow (
541+ 'Workflow version with ID "wv-workflow-1" does not exist' ,
542+ ) ;
543+ } ) ;
544+
505545 it ( "returns 400 when evaluator type is not registered" , async ( ) => {
506546 jest
507547 . spyOn ( prisma . benchmarkProject , "findUnique" )
@@ -701,6 +741,9 @@ describe("BenchmarkDefinitionService", () => {
701741 jest
702742 . spyOn ( prisma . benchmarkDefinition , "findFirst" )
703743 . mockResolvedValue ( existingDefinition as never ) ;
744+ jest
745+ . spyOn ( prisma . benchmarkProject , "findUnique" )
746+ . mockResolvedValue ( mockProject ) ;
704747 jest . spyOn ( prisma . benchmarkDefinition , "update" ) . mockResolvedValue ( {
705748 ...existingDefinition ,
706749 immutable : true ,
@@ -770,6 +813,9 @@ describe("BenchmarkDefinitionService", () => {
770813 jest
771814 . spyOn ( prisma . benchmarkDefinition , "findFirst" )
772815 . mockResolvedValue ( existingDefinition as never ) ;
816+ jest
817+ . spyOn ( prisma . benchmarkProject , "findUnique" )
818+ . mockResolvedValue ( mockProject ) ;
773819
774820 const updatedDefinition = {
775821 ...existingDefinition ,
@@ -1043,16 +1089,27 @@ describe("BenchmarkDefinitionService", () => {
10431089 const baseLineageId = "base-lineage" ;
10441090 const newBaseVersionId = "wv-new-base" ;
10451091
1092+ jest
1093+ . spyOn ( prisma . benchmarkProject , "findUnique" )
1094+ . mockResolvedValue ( mockProject ) ;
1095+
10461096 jest . spyOn ( prisma . workflowVersion , "findUnique" ) . mockResolvedValue ( {
10471097 id : candidateWorkflowVersionId ,
10481098 config : candidateConfig ,
10491099 lineage : {
10501100 id : "cand-lineage" ,
1101+ group_id : "test-group" ,
10511102 workflow_kind : "benchmark_candidate" ,
10521103 source_workflow_id : baseLineageId ,
10531104 } ,
10541105 } as never ) ;
10551106
1107+ jest . spyOn ( prisma . workflowLineage , "findUnique" ) . mockResolvedValue ( {
1108+ id : baseLineageId ,
1109+ group_id : "test-group" ,
1110+ head_version_id : "wv-old-head" ,
1111+ } as never ) ;
1112+
10561113 jest . spyOn ( prisma . workflowVersion , "findFirst" ) . mockResolvedValue ( {
10571114 version_number : 2 ,
10581115 } as never ) ;
@@ -1107,16 +1164,27 @@ describe("BenchmarkDefinitionService", () => {
11071164 const baseLineageId = "base-lineage" ;
11081165 const candidateLineageId = "cand-lineage" ;
11091166
1167+ jest
1168+ . spyOn ( prisma . benchmarkProject , "findUnique" )
1169+ . mockResolvedValue ( mockProject ) ;
1170+
11101171 jest . spyOn ( prisma . workflowVersion , "findUnique" ) . mockResolvedValue ( {
11111172 id : candidateWorkflowVersionId ,
11121173 config : candidateConfig ,
11131174 lineage : {
11141175 id : candidateLineageId ,
1176+ group_id : "test-group" ,
11151177 workflow_kind : "benchmark_candidate" ,
11161178 source_workflow_id : baseLineageId ,
11171179 } ,
11181180 } as never ) ;
11191181
1182+ jest . spyOn ( prisma . workflowLineage , "findUnique" ) . mockResolvedValue ( {
1183+ id : baseLineageId ,
1184+ group_id : "test-group" ,
1185+ head_version_id : "wv-old-head" ,
1186+ } as never ) ;
1187+
11201188 jest . spyOn ( prisma . workflowVersion , "findFirst" ) . mockResolvedValue ( {
11211189 version_number : 2 ,
11221190 } as never ) ;
@@ -1195,11 +1263,15 @@ describe("BenchmarkDefinitionService", () => {
11951263 } ) ;
11961264
11971265 it ( "rejects non-candidate workflows" , async ( ) => {
1266+ jest
1267+ . spyOn ( prisma . benchmarkProject , "findUnique" )
1268+ . mockResolvedValue ( mockProject ) ;
11981269 jest . spyOn ( prisma . workflowVersion , "findUnique" ) . mockResolvedValue ( {
11991270 id : "wv-primary" ,
12001271 config : candidateConfig ,
12011272 lineage : {
12021273 id : "primary-lineage" ,
1274+ group_id : "test-group" ,
12031275 workflow_kind : "primary" ,
12041276 source_workflow_id : null ,
12051277 } ,
@@ -1210,6 +1282,37 @@ describe("BenchmarkDefinitionService", () => {
12101282 ) . rejects . toThrow ( / n o t a b e n c h m a r k c a n d i d a t e / i) ;
12111283 } ) ;
12121284
1285+ it ( "rejects a candidate whose source points at another group's base lineage (cross-group write blocked)" , async ( ) => {
1286+ // Candidate is in the caller's group, but its source_workflow_id points at
1287+ // a lineage owned by a different group. The base-lineage group check must
1288+ // reject this before any version is written.
1289+ jest
1290+ . spyOn ( prisma . benchmarkProject , "findUnique" )
1291+ . mockResolvedValue ( mockProject ) ;
1292+ jest . spyOn ( prisma . workflowVersion , "findUnique" ) . mockResolvedValue ( {
1293+ id : "candidate-v1" ,
1294+ config : candidateConfig ,
1295+ lineage : {
1296+ id : "cand-lineage" ,
1297+ group_id : "test-group" ,
1298+ workflow_kind : "benchmark_candidate" ,
1299+ source_workflow_id : "foreign-base-lineage" ,
1300+ } ,
1301+ } as never ) ;
1302+ // Base lineage belongs to another group.
1303+ jest . spyOn ( prisma . workflowLineage , "findUnique" ) . mockResolvedValue ( {
1304+ id : "foreign-base-lineage" ,
1305+ group_id : "other-group" ,
1306+ head_version_id : "wv-foreign-head" ,
1307+ } as never ) ;
1308+ const createSpy = jest . spyOn ( prisma . workflowVersion , "create" ) ;
1309+
1310+ await expect (
1311+ service . applyToBaseWorkflow ( "project-1" , "candidate-v1" , false ) ,
1312+ ) . rejects . toThrow ( NotFoundException ) ;
1313+ expect ( createSpy ) . not . toHaveBeenCalled ( ) ;
1314+ } ) ;
1315+
12131316 it ( "throws ConflictException when definition pin does not match lineage head inside transaction" , async ( ) => {
12141317 const projectId = "project-1" ;
12151318 const definitionId = "def-1" ;
@@ -1235,6 +1338,10 @@ describe("BenchmarkDefinitionService", () => {
12351338 edges : [ ] ,
12361339 } ;
12371340
1341+ jest
1342+ . spyOn ( prisma . benchmarkProject , "findUnique" )
1343+ . mockResolvedValue ( { ...mockProject , group_id : baseLineageGroupId } ) ;
1344+
12381345 let findFirstCalls = 0 ;
12391346 jest
12401347 . spyOn ( prisma . benchmarkDefinition , "findFirst" )
@@ -1315,6 +1422,10 @@ describe("BenchmarkDefinitionService", () => {
13151422 . spyOn ( service , "getDefinitionById" )
13161423 . mockResolvedValue ( { id : definitionId } as never ) ;
13171424
1425+ jest
1426+ . spyOn ( prisma . benchmarkProject , "findUnique" )
1427+ . mockResolvedValue ( { ...mockProject , group_id : baseLineageGroupId } ) ;
1428+
13181429 jest . spyOn ( prisma . benchmarkDefinition , "findFirst" ) . mockResolvedValue ( {
13191430 id : definitionId ,
13201431 projectId,
0 commit comments