Uh oh!
There was an error while loading. Please reload this page.
Validate that an event's course belongs to the event group's organization - #2241
Merged
moveson merged 2 commits intoAug 24, 2026
Merged
Conversation
Covers the cross-organization course validation, the factory default, the persisted-change rejection, and the nil-course guard from #2234. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…tion Courses were originally organization-less, and relic events pointing at another organization's course still exist on staging (#2235). Such events break the event edit form, whose course selector offers only the event group organization's courses and silently blanks course_id. Validate the consistency so no path (UI, API, imports, duplication) can create new relics. Also bail out of conform_changed_course when the course is blank, so a nil course_id yields the belongs_to presence validation's 422 instead of an ArgumentError 500 (the guard from #2234). The event factory now builds its course in the event group's organization, and specs that explicitly paired independently-built courses and event groups thread a single organization through. Resolves#2235 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for freeto join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
The hardening PR from #2235, written TDD-style (failing specs committed first). Two pieces:
1. The validation.
Eventnow validates that its course belongs to its event group's organization. This closes every path that could mint new cross-organization relics — UI, API, imports, andDuplicateEventGroup(which copies events into a group in the same organization, so it passes naturally). The validation skips when course or event group is absent, leaving those to the presence validations.2. The nil-course guard (carried over from closed#2234).
conform_changed_coursebails when the course is blank, so a nilcourse_id(e.g., an APIPATCHwithcourse_id: null) produces thebelongs_topresence validation's 422 instead of anArgumentError500 fromChangeEventCourse.Deployment note
Merge only after the staging data repair from #2235 is complete. Production audited clean, so no production data blocks this — but the eight staging relics would fail validation on their next save. (Existing invalid rows don't break reads; they'd surface only when someone edits one.)
Factory and spec fallout
The event factory previously built
courseandevent_groupindependently, each minting its own organization — so every factory event was cross-organization by default. The factory now builds the course in the event group's organization (course { association :course, organization: event_group&.organization }), which fixed most of the suite automatically. Twelve spec files that explicitly paired independently-built courses and event groups now thread a single organization through their setup (a one-line change per site). Rubocop's whole-file CI check also swept the touched API controller specs (response.parsed_bodyconversions and the like).Testing
🤖 Generated with Claude Code