@@ -377,6 +377,67 @@ public function testDisassociateTrack2TrackGroup(){
377377 }
378378 }
379379
380+ public function testDisassociateTrackFromTrackGroupPreservesSelectionPlanWhenTrackReachableViaAnotherGroup ()
381+ {
382+ // Set up a second category group (TrackGroupB) that also contains $defaultTrack
383+ // and belongs to the same $default_selection_plan as $defaultTrackGroup (TrackGroupA).
384+ // Removing $defaultTrack from TrackGroupA should NOT clear the selection plan on
385+ // presentations because TrackGroupB still covers the track within the same plan.
386+ $ track_group_b = new \models \summit \PresentationCategoryGroup ();
387+ $ track_group_b ->setName ("TRACK GROUP B " );
388+ $ track_group_b ->addCategory (self ::$ defaultTrack );
389+ self ::$ summit ->addCategoryGroup ($ track_group_b );
390+ self ::$ default_selection_plan ->addTrackGroup ($ track_group_b );
391+ self ::$ em ->persist ($ track_group_b );
392+ self ::$ em ->flush ();
393+
394+ $ headers = [
395+ "HTTP_Authorization " => " Bearer " . $ this ->access_token ,
396+ "CONTENT_TYPE " => "application/json "
397+ ];
398+
399+ $ params = [
400+ 'id ' => self ::$ summit ->getId (),
401+ 'track_group_id ' => self ::$ defaultTrackGroup ->getId (),
402+ 'track_id ' => self ::$ defaultTrack ->getId ()
403+ ];
404+
405+ // Only collect presentations that belong to $default_selection_plan — the plan
406+ // that has TrackGroupB as a second group covering $defaultTrack.
407+ // Presentations of $default_selection_plan2 are intentionally excluded: that plan
408+ // only has $defaultTrackGroup, so their selection plan will be correctly cleared.
409+ $ presentations = self ::$ defaultTrack ->getPresentationsBySelectionPlanIds (
410+ [self ::$ default_selection_plan ->getId ()]
411+ );
412+ $ this ->assertNotEmpty ($ presentations );
413+ foreach ($ presentations as $ presentation ) {
414+ $ this ->assertTrue ($ presentation ->getSelectionPlanId () > 0 );
415+ }
416+ $ presentation_ids = array_map (fn ($ p ) => $ p ->getId (), $ presentations );
417+
418+ // disassociate $defaultTrack from TrackGroupA
419+ $ response = $ this ->action (
420+ "DELETE " ,
421+ "OAuth2PresentationCategoryGroupController@disassociateTrack2TrackGroup " ,
422+ $ params ,
423+ [],
424+ [],
425+ [],
426+ $ headers
427+ );
428+
429+ $ this ->assertResponseStatus (204 );
430+
431+ // reset EM and re-fetch presentations — selection plan must be preserved
432+ // because TrackGroupB still contains $defaultTrack within the same plan
433+ self ::$ em = \LaravelDoctrine \ORM \Facades \Registry::resetManager (\models \utils \SilverstripeBaseModel::EntityManager);
434+ foreach ($ presentation_ids as $ id ) {
435+ $ presentation = self ::$ em ->find (\models \summit \Presentation::class, $ id );
436+ $ this ->assertNotNull ($ presentation );
437+ $ this ->assertGreaterThan (0 , $ presentation ->getSelectionPlanId ());
438+ }
439+ }
440+
380441 public function testAddPrivateTrackGroup (){
381442 $ params = [
382443 'id ' => self ::$ summit ->getId (),
0 commit comments