From 848f47c9255199a2d1db39c35eeca94ae2a8b495 Mon Sep 17 00:00:00 2001 From: Fraser Speirs Date: Tue, 3 Feb 2026 11:11:58 +0000 Subject: [PATCH] Make SchoolProject destroy dependent Transitions Previously, when a SchoolProject was destroyed, there was a rule that dependent `school_project_transitions` would be nullified rather than destroyed. At the same time, the definition of a `SchoolProjectTransition` states that the `school_project_id`, which refers to the project being transitioned, CANNOT be null. Therefore, when PR [#637](https://github.com/RaspberryPiFoundation/editor-api/pull/637) allowed deletion of Students and, consequently, Projects, the error in these relations and nullification rules was exposed. This leads to an error when deleting a Student who owns projects which have been transitioned. This commit changes the `dependent:` rule between SchoolProject and SchoolProjectTransition to destroy the transition when the Proejct is destroyed. It also adds a test to ensure this happens. --- app/models/school_project.rb | 2 +- spec/models/school_project_spec.rb | 2 +- spec/services/student_removal_service_spec.rb | 16 ++++++++++++++++ 3 files changed, 18 insertions(+), 2 deletions(-) diff --git a/app/models/school_project.rb b/app/models/school_project.rb index c5d41d082..52aceb96b 100644 --- a/app/models/school_project.rb +++ b/app/models/school_project.rb @@ -4,7 +4,7 @@ class SchoolProject < ApplicationRecord belongs_to :school belongs_to :project has_many :feedback, dependent: :destroy - has_many :school_project_transitions, autosave: false, dependent: :nullify + has_many :school_project_transitions, autosave: false, dependent: :destroy include Statesman::Adapters::ActiveRecordQueries[ transition_class: ::SchoolProjectTransition, diff --git a/spec/models/school_project_spec.rb b/spec/models/school_project_spec.rb index 6d1eba003..8cc785498 100644 --- a/spec/models/school_project_spec.rb +++ b/spec/models/school_project_spec.rb @@ -20,7 +20,7 @@ it { is_expected.to belong_to(:school) } it { is_expected.to belong_to(:project) } it { is_expected.to have_many(:feedback).dependent(:destroy) } - it { is_expected.to have_many(:school_project_transitions).dependent(:nullify) } + it { is_expected.to have_many(:school_project_transitions).dependent(:destroy) } describe '#status' do it 'defaults to unsubmitted' do diff --git a/spec/services/student_removal_service_spec.rb b/spec/services/student_removal_service_spec.rb index 00002488a..ec493d5c9 100644 --- a/spec/services/student_removal_service_spec.rb +++ b/spec/services/student_removal_service_spec.rb @@ -79,6 +79,22 @@ # Other student's project should remain expect(Project.exists?(other_school_project.id)).to be true end + + it 'deletes school project transitions when deleting projects' do + project = create(:project, user_id: student.id, school: school) + school_project = project.school_project + + # Transition the project to create SchoolProjectTransition records + school_project.transition_status_to!(:submitted, student.id) + school_project.transition_status_to!(:returned, teacher.id) + + results = service.remove_students + + expect(results.first[:error]).to be_nil + expect(Project.exists?(project.id)).to be false + expect(SchoolProject.exists?(school_project.id)).to be false + expect(SchoolProjectTransition.where(school_project_id: school_project.id).count).to eq(0) + end end context 'when student does not have a role in the school' do