From 76c8613d50982e38cb842ad64f884455aa154cef Mon Sep 17 00:00:00 2001 From: Ben Anderson Date: Sat, 19 Apr 2025 14:54:36 +1200 Subject: [PATCH 1/3] Write out the squeel gem This gem isn't supported on Rails >= 5, and also fully deprecated. I've replaced all usages of it with the equivalent Rails 4.2 code which isn't necessarily the most ergonomic nor the most performance, in some cases, but should be fine for our cases. I also had to repair one migration that was added back in 2013 and will never be run again, and could theoretically just be commented out, but the fix was trivial. --- Gemfile | 1 - Gemfile.lock | 7 --- app/controllers/contests_controller.rb | 19 ++++-- app/controllers/groups/members_controller.rb | 4 +- app/controllers/home_controller.rb | 2 +- app/controllers/problems_controller.rb | 8 ++- app/controllers/user_controller.rb | 7 ++- app/models/contest.rb | 3 + app/models/contest_relation.rb | 2 +- app/models/contest_supervisor.rb | 5 +- app/models/problem_set.rb | 14 ++++- app/models/request.rb | 20 ++++++- app/policies/contest_policy.rb | 59 ++++++++++++------- app/policies/group_policy.rb | 6 +- app/policies/problem_policy.rb | 13 +++- app/policies/problem_set_policy.rb | 10 +++- app/policies/request_policy.rb | 2 +- app/policies/submission_policy.rb | 23 ++++++-- app/workers/judge_submission_worker.rb | 12 +++- ...1221014009_create_user_problem_relation.rb | 2 +- 20 files changed, 155 insertions(+), 64 deletions(-) diff --git a/Gemfile b/Gemfile index 7652b503..71acfe10 100644 --- a/Gemfile +++ b/Gemfile @@ -22,7 +22,6 @@ gem "pundit", "~> 1.1.0" gem "recaptcha", require: "recaptcha/rails" gem "loofah", "<= 2.20.0" gem "whenever", require: false # for cron jobs -gem "squeel" # , '~> 1.1.1' # until version 1.1.2 released gem "tilt" gem "simple-navigation", "3.11.0" gem "simple_form", "3.2.1" diff --git a/Gemfile.lock b/Gemfile.lock index a013d206..0d739b05 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -223,8 +223,6 @@ GEM pdfkit (0.8.2) pg (0.21.0) pkg-config (1.5.1) - polyamorous (1.1.0) - activerecord (>= 3.0) prawn (2.2.2) pdf-core (~> 0.7.0) ttfunk (~> 1.5) @@ -363,10 +361,6 @@ GEM actionpack (>= 3.0) activesupport (>= 3.0) sprockets (>= 2.8, < 4.0) - squeel (1.2.3) - activerecord (>= 3.0) - activesupport (>= 3.0) - polyamorous (~> 1.1.0) ssrf_filter (1.0.8) standard (1.0.5) rubocop (= 1.12.1) @@ -468,7 +462,6 @@ DEPENDENCIES simplecov sinatra spring - squeel standard strong_presenter (~> 0.2.2) superfish-rails (~> 1.6.0) diff --git a/app/controllers/contests_controller.rb b/app/controllers/contests_controller.rb index 864b0bd0..172e35cf 100644 --- a/app/controllers/contests_controller.rb +++ b/app/controllers/contests_controller.rb @@ -24,13 +24,24 @@ def browse case params[:filter].to_s when "active" raise Pundit::NotAuthorizedError unless user_signed_in? - @contests = Contest.joins(:contest_relations).where { (contest_relations.user_id == my { current_user.id }) & (contest_relations.finish_at > Time.now) }.order("end_time ASC") + @contests = Contest + .joins(:contest_relations) + .where(contest_relations: { user_id: current_user.id }) + .where("contest_relations.finish_at > ?", Time.now) + .order("end_time ASC") when "current" - @contests = visible_contests.where { (start_time < Time.now + 30.minutes) & (end_time > Time.now) }.order("end_time ASC") + @contests = visible_contests + .where("start_time < ?", Time.now + 30.minutes) + .where("end_time > ?", Time.now) + .order("end_time ASC") when "upcoming" - @contests = visible_contests.where { (start_time > Time.now + 30.minutes) }.order("start_time ASC") + @contests = visible_contests + .where("start_time > ?", Time.now + 30.minutes) + .order("start_time ASC") when "past" - @contests = visible_contests.where { (end_time < Time.now) }.order("end_time DESC") + @contests = visible_contests + .where("end_time < ?", Time.now) + .order("end_time DESC") else raise Pundit::NotAuthorizedError end diff --git a/app/controllers/groups/members_controller.rb b/app/controllers/groups/members_controller.rb index 7e42bba6..a421ddeb 100644 --- a/app/controllers/groups/members_controller.rb +++ b/app/controllers/groups/members_controller.rb @@ -97,8 +97,8 @@ def invites redirect_to(invites_members_group_path(@group), notice: "#{@user.username} has been invited to join this group") end else # get request - @pending_requests = @group.invitations.pending.order(:created_at).reverse_order - @requests = @group.invitations.closed.order(:created_at).reverse_order + @pending_requests = @group.invitations.merge(Request.pending).order(created_at: :desc) + @requests = @group.invitations.merge(Request.closed).order(created_at: :desc) end end diff --git a/app/controllers/home_controller.rb b/app/controllers/home_controller.rb index b7942003..763c32b7 100644 --- a/app/controllers/home_controller.rb +++ b/app/controllers/home_controller.rb @@ -2,6 +2,6 @@ class HomeController < ApplicationController def home @mygroups = current_user.try(:groups) @problem_set_associations = Group.find(0).problem_set_associations - @contests = policy_scope(Contest).where { (end_time > Time.now) }.order("end_time ASC") + @contests = policy_scope(Contest).not_ended.order("end_time ASC") end end diff --git a/app/controllers/problems_controller.rb b/app/controllers/problems_controller.rb index 3db6593f..a830a994 100644 --- a/app/controllers/problems_controller.rb +++ b/app/controllers/problems_controller.rb @@ -91,7 +91,13 @@ def submissions if current_user.openbook? @submissions = @problem.submission_history(current_user) else - start_time = current_user.contest_relations.joins(contest: {problem_set: :problems}).where { (started_at <= DateTime.now) & (finish_at > DateTime.now) & (contest.problem_set.problems.id == my { params[:id] }) }.minimum(:started_at) + start_time = current_user + .contest_relations + .joins(contest: {problem_set: :problems}) + .where("started_at <= :now AND finish_at > :now", now: DateTime.now) + .where(problems: {id: params[:id]}) + .minimum(:started_at) + @submissions = @problem.submission_history(current_user, start_time) end diff --git a/app/controllers/user_controller.rb b/app/controllers/user_controller.rb index a5d58d44..e6dd4588 100644 --- a/app/controllers/user_controller.rb +++ b/app/controllers/user_controller.rb @@ -9,7 +9,12 @@ def permitted_params def show @user = User.find(params[:id]) authorize @user, :show? - @solved_problems = @user.user_problem_relations.where(ranked_score: 100).joins(:problem).select([:problem_id, :ranked_submission_id, {problem: :name}]).order("problems.name") + @solved_problems = @user + .user_problem_relations + .where(ranked_score: 100) + .joins(:problem) + .select("problems.name", "problem_id", "ranked_submission_id") + .order("problems.name") end def edit diff --git a/app/models/contest.rb b/app/models/contest.rb index fce12562..61fba1be 100644 --- a/app/models/contest.rb +++ b/app/models/contest.rb @@ -24,6 +24,9 @@ class Contest < ActiveRecord::Base # public = everyone, protected = in group, private = competitors OBSERVATION = Enumeration.new 0 => :public, 1 => :protected, 2 => :private + scope :not_ended, -> { where("end_time > ?", Time.current) } + scope :publicly_observable, -> { where(observation: OBSERVATION[:public]) } + before_save do # update the end time that was cached if duration_changed? || end_time_changed? contest_relations.find_each do |relation| diff --git a/app/models/contest_relation.rb b/app/models/contest_relation.rb index 842ce85a..5ad74803 100644 --- a/app/models/contest_relation.rb +++ b/app/models/contest_relation.rb @@ -7,7 +7,7 @@ class ContestRelation < ActiveRecord::Base belongs_to :school belongs_to :supervisor, class_name: :User - scope :active, -> { where { (started_at <= DateTime.now) & (finish_at > DateTime.now) } } + scope :active, -> { where("started_at <= :now AND finish_at > :now", now: DateTime.now) } scope :absent, -> { where(checked_in: false) } scope :user, ->(u_id) { where(user_id: u_id) } diff --git a/app/models/contest_supervisor.rb b/app/models/contest_supervisor.rb index 771e02df..97849c35 100644 --- a/app/models/contest_supervisor.rb +++ b/app/models/contest_supervisor.rb @@ -36,7 +36,10 @@ def contest_relations def potential_contestants if site_type == "School" - User.where { |user| (user.school_id == site_id) & ((user.school_graduation >= contest.end_time) | ((user.school_graduation.nil?) & (user.created_at >= DateTime.now.advance(years: -1)))) & (user.id << contest.registrants) } + User + .where(school_id: site_id) + .where("(users.school_graduation >= :contest_end_time) OR ((users.school_graduation IS NULL) AND (users.created_at >= :year_ago))", contest_end_time: contest.end_time, year_ago: DateTime.now.advance(years: -1)) + .where.not(id: contest.registrants) else [] # not implemented end diff --git a/app/models/problem_set.rb b/app/models/problem_set.rb index 1baea68f..20f20b75 100644 --- a/app/models/problem_set.rb +++ b/app/models/problem_set.rb @@ -17,11 +17,19 @@ class ProblemSet < ActiveRecord::Base validates :name, presence: true def problems_with_scores_by_user(user_id) - problems.joins("LEFT OUTER JOIN user_problem_relations ON user_problem_relations.problem_id = problems.id AND user_problem_relations.user_id = #{user_id} LEFT OUTER JOIN submissions ON submissions.id = user_problem_relations.submission_id").select([:id, :name, :test_error_count, :test_warning_count, :test_status, {submissions: [:points, :maximum_points], problem_set_problems: :weighting}]) + problems + .joins("LEFT OUTER JOIN user_problem_relations ON user_problem_relations.problem_id = problems.id AND user_problem_relations.user_id = #{user_id} LEFT OUTER JOIN submissions ON submissions.id = user_problem_relations.submission_id") + .select( + "id", "name", "test_error_count", "test_warning_count", "test_status", "submissions.points", "submissions.maximum_points", "problem_set_problems.weighting" + ) end - def for_contestant? u_id - contests.joins(:contest_relations).where(contest_relations: {user_id: u_id}).where { {contest_relations => sift(:active)} }.any? + def for_contestant?(u_id) + contests + .joins(:contest_relations) + .where(contest_relations: {user_id: u_id}) + .merge(ContestRelation.active) + .any? end def for_owner? u_id diff --git a/app/models/request.rb b/app/models/request.rb index 2126c1b2..63fde6e8 100644 --- a/app/models/request.rb +++ b/app/models/request.rb @@ -16,9 +16,23 @@ class Request < ActiveRecord::Base status == key end end - scope :pending, -> { where { (status == STATUS[:pending]) & (expired_at > DateTime.now) } } - scope :expired, -> { where { (status == STATUS[:pending]) & (expired_at <= DateTime.now) } } - scope :closed, -> { where { (status != STATUS[:pending]) | (expired_at <= DateTime.now) } } + + # I would prefer to just use expired and not_expired, but these + # names are in used below for a slightly different use. + scope :past_expiry, -> { where("expired_at <= ?", DateTime.now) } + scope :not_past_expiry, -> { where("expired_at > ?", DateTime.now) } + + # IMO a better name for this would be "pending", but see below + scope :status_pending, -> { where(status: STATUS[:pending]) } + + # These scopes have weird names. + # * Pending is redefined a scope defined above. + # * Expired you wouldn't usually expect to check status + scope :pending, -> { status_pending.merge(not_past_expiry) } + scope :expired, -> { status_pending.merge(past_expiry) } + + # TODO(Rails5): Convert to where.not(pending).or(past_expiry) + scope :closed, -> { where("status != ? OR expired_at <= ?", STATUS[:pending], DateTime.now) } def pending? status == STATUS[:pending] && (expired_at == Float::INFINITY || expired_at > DateTime.now) diff --git a/app/policies/contest_policy.rb b/app/policies/contest_policy.rb index 1c0b5f22..5c0f85bc 100644 --- a/app/policies/contest_policy.rb +++ b/app/policies/contest_policy.rb @@ -5,25 +5,30 @@ def resolve return scope.all end - # following uses advanced squeel - scope.where do |contests| - public_observation = contests.observation == Contest::OBSERVATION[:public] - grouped = contests.id.in(GroupContest.where do |gc| - group_contests = (gc.group_id == 0) - group_contests |= (gc.group_id >> user.groups.select(:id)) if user - group_contests - end.select(:contest_id)) - visible_contests = public_observation | grouped - - if user - registered = contests.id.in(user.contest_relations.select(:contest_id)) - owned = contests.owner_id == user.id - supervising = contests.id.in(user.contest_supervising.select(:contest_id)) - visible_contests |= registered | owned | supervising - end - - visible_contests - end + sql = <<~SQL + contests.observation = :public + OR contests.id IN ( + SELECT + contest_id + FROM + group_contests + WHERE + group_id = 0 + OR group_id IN (:user_groups) + ) + OR contests.id IN (:allowed_contest_ids) + OR contests.owner_id = :user_id + SQL + + allowed_contest_ids = [] + allowed_contest_ids = user.contest_relations.pluck(:contest_id) + user.contest_supervising.pluck(:contest_id) if user + + scope.where(sql, { + public: Contest::OBSERVATION[:public], + user_groups: user&.groups&.pluck(:id), + allowed_contest_ids: allowed_contest_ids, + user_id: user&.id, + }) end end @@ -34,12 +39,22 @@ def registered? def current_contestant? return false unless user # signed in - record.contest_relations.where { |relation| (relation.user_id == user.id) & (relation.started_at <= DateTime.now) & (relation.finish_at > DateTime.now) }.exists? + + record + .contest_relations + .where(user_id: user.id) + .where("started_at <= :now AND finish_at > :now", DateTime.now) + .exists? end def current_or_past_contestant? return false unless user # signed in - record.contest_relations.where { |relation| (relation.user_id == user.id) & (relation.started_at <= DateTime.now) }.exists? + + record + .contest_relations + .where(user_id: user.id) + .where("started_at <= ?", DateTime.now) + .exists? end def index? @@ -87,7 +102,7 @@ def unfinalize? def startable? return false unless user # signed in - user.is_staff? or registered? or record.groups.where(id: 0).exists? or record.groups.joins(:memberships).where(memberships: {member_id: user.id}).exists? + user.is_staff? || registered? || record.groups.where(id: 0).exists? || record.groups.joins(:memberships).where(group_memberships: { member_id: user.id }).exists? end def start? diff --git a/app/policies/group_policy.rb b/app/policies/group_policy.rb index 234d161d..d34a1aa2 100644 --- a/app/policies/group_policy.rb +++ b/app/policies/group_policy.rb @@ -4,7 +4,11 @@ def resolve(source = nil) if user.is_staff? scope.all else - scope.where { |groups| (groups.id == 0) | (groups.visibility == Group::VISIBILITY[:public]) | (groups.owner_id == user.id) } + scope.where( + "id = 0 OR visibility = :public OR owner_id = :user_id", + public: Group::VISIBILITY[:public], + user_id: user.id + ) end end end diff --git a/app/policies/problem_policy.rb b/app/policies/problem_policy.rb index 08267d77..c51ca2bf 100644 --- a/app/policies/problem_policy.rb +++ b/app/policies/problem_policy.rb @@ -32,12 +32,21 @@ def show? return true if user && user.is_staff? if user && user.competing? - return record.contest_relations.where { |relation| (relation.user_id == user.id) & (relation.started_at <= DateTime.now) & (relation.finish_at > DateTime.now) }.exists? + return record + .contest_relations + .where(contest_relations: { user_id: user.id }) + .where("started_at <= :now AND finish_at > :now", now: DateTime.now) + .exists? end return true if record.groups.where(id: 0).exists? return false unless user # signed in - user.owns(record) or record.group_memberships.where { |membership| (membership.member_id == user.id) }.exists? + return true if user.owns(record) + + record + .group_memberships + .where(group_memberships: { member_id: user.id }) + .exists? end def access? diff --git a/app/policies/problem_set_policy.rb b/app/policies/problem_set_policy.rb index ab39a2c8..5a9d4893 100644 --- a/app/policies/problem_set_policy.rb +++ b/app/policies/problem_set_policy.rb @@ -24,16 +24,20 @@ def manage? def show? return true if user && user.is_staff? if user && user.competing? - return user.contest_relations.where { |relation| (relation.started_at <= DateTime.now) & (relation.finish_at > DateTime.now) & (relation.contest_id >> record.contest_ids) }.exists? + return user + .contest_relations + .where("started_at <= :now AND finish_at > :now", now: DateTime.now) + .where("contest_relations.contest_id IN (:contest_ids)", contest_ids: record.contest_ids) + .exists? end return true if record.groups.where(id: 0).exists? return false unless user # signed in - user.owns(record) or record.group_memberships.where { |membership| (membership.member_id == user.id) }.exists? + user.owns(record) || record.group_memberships.where(member_id: user.id).exists? end def create? return false unless user # signed in - super or user.is_staff? or user.is_any?([:organiser, :author]) + super || user.is_staff? || user.is_any?([:organiser, :author]) end end diff --git a/app/policies/request_policy.rb b/app/policies/request_policy.rb index 641e86cb..8c073fe9 100644 --- a/app/policies/request_policy.rb +++ b/app/policies/request_policy.rb @@ -4,7 +4,7 @@ def resolve if user.is_admin? scope.all else - scope.where { (requester_id == user.id) | (requestee_id == user.id) } + scope.where("requester_id = :user_id OR requestee_id = :user_id", user_id: user.id) end end end diff --git a/app/policies/submission_policy.rb b/app/policies/submission_policy.rb index ce12dd79..e7d51e74 100644 --- a/app/policies/submission_policy.rb +++ b/app/policies/submission_policy.rb @@ -4,20 +4,31 @@ def resolve if user.is_staff? scope.all elsif user.competing? - problem_set_ids = ContestRelation.where { |contest_relations| (contest_relations.user_id == user.id) & (contest_relations.started_at <= DateTime.now) & (contest_relations.finish_at > DateTime.now) }.joins(:contest).select(contest: :problem_set_id) - scope.joins(problem: :problem_sets).where(problem: {problem_sets: {id: problem_set_ids}}, user_id: user.id) + problem_set_ids = ContestRelation + .where(user_id: user.id) + .where("contest_relations.started_at <= ?", DateTime.now) + .where("contest_relations.finish_at > ?", DateTime.now) + .joins(:contest) + .select("contests.problem_set_id") + + scope + .joins(problem: :problem_sets) + .where(user_id: user.id) + .where(problem_sets: {id: problem_set_ids}) else - scope.joins(:problem).where { |submission| (submission.user_id == user.id) | (submission.problem.owner_id == user.id) } + scope + .joins(:problem) + .where("submissions.user_id = :user_id OR problems.owner_id = :user_id", user_id: user.id) end end end def inspect? - super or (record.is_a?(Submission) && policy(record.problem).try(:inspect?) && !user.competing?) + super || (record.is_a?(Submission) && policy(record.problem).try(:inspect?) && !user.competing?) end def update? - super or (record.is_a?(Submission) && policy(record.problem).try(:update?) && !user.competing?) + super || (record.is_a?(Submission) && policy(record.problem).try(:update?) && !user.competing?) end def index? @@ -25,7 +36,7 @@ def index? end def show? - inspect? or scope.where(id: record.id).exists? + inspect? || scope.where(id: record.id).exists? end def rejudge? diff --git a/app/workers/judge_submission_worker.rb b/app/workers/judge_submission_worker.rb index 658881c2..a759846f 100644 --- a/app/workers/judge_submission_worker.rb +++ b/app/workers/judge_submission_worker.rb @@ -13,10 +13,16 @@ def self.judge(submission, queue: nil, delay: 0) user = submission.user if user.competing? && Pundit.policy(user, submission).show? if rejudging # re-judging, later submissions are more important - later = Submission.where(problem_id: submission.problem_id, user_id: submission.user_id).where { created_at > submission.created_at }.count + later = Submission + .where(problem_id: submission.problem_id, user_id: submission.user_id) + .where("created_at > ?", submission.created_at) + .count priority = 18 - [later * 2, 10].min else # first time judging, earlier submissions get better priority (so other people get a chance to have their stuff judged) - prev = Submission.where(problem_id: submission.problem_id, user_id: submission.user_id).where { created_at < submission.created_at }.count + prev = Submission + .where(problem_id: submission.problem_id, user_id: submission.user_id) + .where("created_at < ?", submission.created_at) + .count priority = 20 - [prev, 10].min end @@ -102,7 +108,7 @@ def judge resource_limits = {mem: memory_limit * 1024, time: time_limit, extra_time: extra_time, wall_time: wall_time, stack: stack_limit, processes: submission.language.processes} # prerequisites - prereqs = problem.test_cases.where(id: problem.prerequisite_sets.joins(:test_case_relations).select(test_case_relations: :test_case_id)) + prereqs = problem.test_cases.where(id: problem.prerequisite_sets.joins(:test_case_relations).select("test_case_relations.test_case_id")) prereqs.each do |test_case| result["test_cases"][test_case.id] = judge_test_case(test_case, run_command, eval_command, resource_limits) unless result["test_cases"].has_key?(test_case.id) diff --git a/db/migrate/20131221014009_create_user_problem_relation.rb b/db/migrate/20131221014009_create_user_problem_relation.rb index 8098d012..2a426baf 100644 --- a/db/migrate/20131221014009_create_user_problem_relation.rb +++ b/db/migrate/20131221014009_create_user_problem_relation.rb @@ -2,7 +2,7 @@ class CreateUserProblemRelation < ActiveRecord::Migration def change reversible do |dir| dir.up do - missing_user_ids = Submission.where { |submission| submission.user_id << User.select(:id) }.pluck(:user_id) + missing_user_ids = Submission.where("submissions.user_id NOT IN (SELECT id FROM users)").pluck(:user_id) Submission.where(user_id: missing_user_ids).delete_all ContestRelation.where(user_id: missing_user_ids).destroy_all From e4f6ecdded8b285bf40c3e78255e8b1dfad8e620 Mon Sep 17 00:00:00 2001 From: Ben Anderson Date: Sat, 17 May 2025 11:29:26 +1200 Subject: [PATCH 2/3] Add test for ContestSupervisor#potential_contestants One of the trickier Squeel removals, so a small spec seemed warranted. The first attempt to rewrite this had a bunch of issues I missed, too. --- spec/factories/contest_supervisors.rb | 9 +++--- spec/models/contest_supervisor_spec.rb | 42 +++++++++++++++++++++++++- 2 files changed, 45 insertions(+), 6 deletions(-) diff --git a/spec/factories/contest_supervisors.rb b/spec/factories/contest_supervisors.rb index 5eb068db..5f6cf9df 100644 --- a/spec/factories/contest_supervisors.rb +++ b/spec/factories/contest_supervisors.rb @@ -1,10 +1,9 @@ # Read about factories at https://github.com/thoughtbot/factory_bot FactoryBot.define do - factory :contest_supervisor, class: "ContestSupervisors" do - contest_id { 1 } - user_id { 1 } - site_type { "MyString" } - site_id { 1 } + factory :contest_supervisor, class: "ContestSupervisor" do + association :contest + association :user + association :site, factory: :school end end diff --git a/spec/models/contest_supervisor_spec.rb b/spec/models/contest_supervisor_spec.rb index 2a510328..512d4870 100644 --- a/spec/models/contest_supervisor_spec.rb +++ b/spec/models/contest_supervisor_spec.rb @@ -1,5 +1,45 @@ require "spec_helper" describe ContestSupervisor do - pending "add some examples to (or delete) #{__FILE__}" + subject(:contest_supervisor) { FactoryBot.create(:contest_supervisor, contest: contest) } + + let(:contest) { FactoryBot.create(:contest) } + + describe "#potential_contestants" do + let!(:students) do + 10.times.map { + FactoryBot.create(:user, country_code: "NZ", school: contest_supervisor.site) + } + end + + context "when no-one is registered for the contest" do + it "includes all the students at the school" do + expect(contest_supervisor.potential_contestants).to match_array(students) + end + end + + context "when some people are registered for the contest" do + before do + students.take(5).each do |student| + contest.registrants << student + end + end + + it "includes the remaining students at the school" do + expect(contest_supervisor.potential_contestants).to match_array(students.drop(5)) + end + end + + context "when no-one is registered for the contest" do + before do + students.each do |student| + contest.registrants << student + end + end + + it "return an empty list" do + expect(contest_supervisor.potential_contestants).to be_empty + end + end + end end From 86ff613ea9c87c4da8f67fcc19eeef3314bdc16d Mon Sep 17 00:00:00 2001 From: Ben Anderson Date: Wed, 21 May 2025 19:27:56 +1200 Subject: [PATCH 3/3] Add a test for SubmissionsController common use cases It's not a huge amount of coverage, but enough to exercise some interesting cases in the policies and controllers. --- spec/requests/submissions_request_spec.rb | 44 +++++++++++++++++++++++ 1 file changed, 44 insertions(+) create mode 100644 spec/requests/submissions_request_spec.rb diff --git a/spec/requests/submissions_request_spec.rb b/spec/requests/submissions_request_spec.rb new file mode 100644 index 00000000..c4f5388c --- /dev/null +++ b/spec/requests/submissions_request_spec.rb @@ -0,0 +1,44 @@ +require "spec_helper" + +RSpec.describe SubmissionsController, type: :request do + let(:problem) { FactoryBot.create(:problem) } + let(:problem_set) { FactoryBot.create(:problem_set, problems: [problem]) } + let(:user) { FactoryBot.create(:user, groups: [group]) } + let(:group) { FactoryBot.create(:group) } + let!(:submission) { FactoryBot.create(:submission, problem: problem, user: user) } + + before do + problem_set.groups << group + + sign_in(user) + end + + describe "GET /problems/:id/submissions" do + it "doesn't explode" do + get submissions_problem_path(problem) + + expect(response).to have_http_status(:success) + end + end + + describe "GET /submissions/my" do + context "when not in an active contest" do + it "doesn't explode" do + get my_submissions_path + + expect(response).to have_http_status(:success) + end + end + + context "when in an active contest" do + let(:contest) { FactoryBot.create(:contest, problem_set: problem_set) } + let!(:contest_relation) { FactoryBot.create(:contest_relation, user: user, contest: contest, finish_at: 99.years.from_now) } + + it "doesn't explode" do + get my_submissions_path + + expect(response).to have_http_status(:success) + end + end + end +end