Skip to content
This repository has been archived by the owner on Jan 3, 2025. It is now read-only.

Updated get_registrations and post_attendee tests #133

Merged
merged 74 commits into from
Oct 6, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
74 commits
Select commit Hold shift + click to select a range
5a9b51b
split tests into different folders
dunkOnIT Jun 14, 2023
9d5cb1f
set up dynamoid test/dev env and added db tests
dunkOnIT Jun 14, 2023
4833a0a
fixed context naming and syntax errors
dunkOnIT Jun 23, 2023
ee1faac
Made database_functions_spec.rb pass
FinnIckler Jun 23, 2023
c37d55c
added parametrized tests for post_registration
dunkOnIT Jun 23, 2023
7db9537
merged with origin
dunkOnIT Jun 23, 2023
0e67f01
rubocop changes
dunkOnIT Jun 23, 2023
10e9760
Made get_registration_spec pass
FinnIckler Jun 23, 2023
1903b36
merged main into rswag-testing
dunkOnIT Jun 26, 2023
817efa3
merged with rswag-testing changes
dunkOnIT Jun 26, 2023
eb56ce7
missed merge conflict
dunkOnIT Jun 26, 2023
1f3b99a
added back post_attendee_spec.rb
dunkOnIT Jun 26, 2023
b9a122a
post attendee spec tests added
dunkOnIT Jun 26, 2023
cf52323
fixed parametrization of tests
dunkOnIT Jun 26, 2023
69d0290
removed old db test and started PATCH tests
dunkOnIT Jun 27, 2023
a755c10
working PATCH cancel happy path tests
dunkOnIT Jun 27, 2023
8da0f8f
updated routes and finished cancel tests (against wrng path)
dunkOnIT Jun 28, 2023
459d14d
working reg cancel tests
dunkOnIT Jun 28, 2023
f9ad227
made cancel into a separate file
dunkOnIT Jun 28, 2023
2b4a54b
made cancel into a separate file and added it
dunkOnIT Jun 28, 2023
78b9fc4
updated spec with new request format, added update registrations
dunkOnIT Jul 10, 2023
36e9a57
merged with main
dunkOnIT Jul 11, 2023
58b7156
trying to pass post tests
dunkOnIT Jul 11, 2023
b40cbf2
working JWT token submission
dunkOnIT Jul 12, 2023
c82a397
jwt working, code cleaned up
dunkOnIT Jul 12, 2023
5be91ae
cursed attempts to make tests work
dunkOnIT Jul 12, 2023
d80debc
various failed attempts to make shared examples work
dunkOnIT Jul 13, 2023
e395fef
dead-end: testing to make shared examples work
dunkOnIT Jul 17, 2023
e97ad94
DEAD-END: testing to make shared examples work
dunkOnIT Jul 17, 2023
160d6f8
post_attendee first test passes controller
dunkOnIT Jul 17, 2023
a1e38e5
minor test changes
dunkOnIT Jul 17, 2023
94918c0
merged with main
dunkOnIT Jul 17, 2023
7f54e3d
changed create reg to return 202, other minor changes
dunkOnIT Jul 18, 2023
c16e2df
rubocop changes and removed failing tests
dunkOnIT Jul 18, 2023
603b6ba
Removed unnecessary files, fixes from diff analysis
dunkOnIT Jul 18, 2023
7356416
removed shared examples
dunkOnIT Jul 18, 2023
4afc87f
fixed tests
dunkOnIT Jul 18, 2023
732947d
db and all but 1 get_reg test passing
dunkOnIT Jul 18, 2023
af394f3
missed some files in previous commit
dunkOnIT Jul 18, 2023
58632aa
everything working except post test in compose.test.yml
dunkOnIT Jul 19, 2023
4e4a20d
ALL TESTS WORKING YAY
dunkOnIT Jul 19, 2023
2722eb3
rubocop changes
dunkOnIT Jul 19, 2023
300560a
added back deleted spec files
dunkOnIT Jul 19, 2023
a94c753
refactored test paths
dunkOnIT Jul 19, 2023
a87cebd
test refactors, and added reg spec for ease of testing completed specs
dunkOnIT Jul 19, 2023
6d9c314
working RSwag security setup for JWT auth
dunkOnIT Jul 20, 2023
bc1c1b9
added TODO get registration tests
dunkOnIT Jul 27, 2023
04a679d
swagger updated and new post reg test
dunkOnIT Jul 27, 2023
2288987
merged with main
dunkOnIT Jul 27, 2023
f34da4e
PASSING tests passing after main merge
dunkOnIT Jul 27, 2023
21f187e
POST tests PASSING, added comp not open and imeprsonation
dunkOnIT Jul 27, 2023
59d6670
code ready for PR, still needs to be swaggerized
dunkOnIT Aug 1, 2023
050e77e
reverted dev cache change, removed commented code
dunkOnIT Aug 1, 2023
9c59d95
added tests for POSTing attendes
dunkOnIT Aug 10, 2023
abaa189
working cancel registration test
dunkOnIT Aug 11, 2023
5690f24
admin and user cancellation tests in all states
dunkOnIT Aug 12, 2023
4db2411
added update tests
dunkOnIT Aug 21, 2023
4d42c23
added cancel and update tests
dunkOnIT Aug 21, 2023
06448a9
tests failing but added lots of update tests
dunkOnIT Aug 22, 2023
54ceb33
basic factory implementation working
dunkOnIT Sep 18, 2023
e4528cb
more work with factorybot
dunkOnIT Sep 18, 2023
7d05570
adding factory stuff and trying to resolve database existing issue
dunkOnIT Oct 3, 2023
f9427fc
Merge branch 'main' into rswag-testing
FinnIckler Oct 3, 2023
5756af6
removed puts and moved changed_event_ids to lanes.rb
dunkOnIT Oct 3, 2023
182670f
removed registration_status and enable_traces
dunkOnIT Oct 3, 2023
7e3aadf
removed registration_status and enable_traces
dunkOnIT Oct 3, 2023
44ff150
PR changes and added overcommit
dunkOnIT Oct 4, 2023
819f86d
changed overcommit config
dunkOnIT Oct 4, 2023
6c29044
changed overcommit ymlspacing
dunkOnIT Oct 4, 2023
287f8d7
rubocop changes
dunkOnIT Oct 4, 2023
e73873c
final review changes
dunkOnIT Oct 4, 2023
1b9fff4
removed unnecessary puts
dunkOnIT Oct 4, 2023
9188b64
fixed error codes
dunkOnIT Oct 4, 2023
ac9e633
revie changes during Finn session
dunkOnIT Oct 6, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions .overcommit.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# Use this file to configure the Overcommit hooks you wish to use. This will
# extend the default configuration defined in:
# https://github.com/sds/overcommit/blob/master/config/default.yml
#
# At the topmost level of this YAML file is a key representing type of hook
# being run (e.g. pre-commit, commit-msg, etc.). Within each type you can
# customize each hook, such as whether to only run it on certain files (via
# `include`), whether to only display output if it fails (via `quiet`), etc.
#
# For a complete list of hooks, see:
# https://github.com/sds/overcommit/tree/master/lib/overcommit/hook
#
# For a complete list of options that you can use to customize hooks, see:
# https://github.com/sds/overcommit#configuration
#
# Uncomment the following lines to make the configuration take effect.

PreCommit:
RuboCop:
enabled: true
on_warn: fail # Treat all warnings as failures

6 changes: 6 additions & 0 deletions Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,9 @@ group :development, :test do
# See https://guides.rubyonrails.org/debugging_rails_applications.html#debugging-with-the-debug-gem
gem "debug", platforms: %i[mri mingw x64_mingw]

# run pre-commit hooks
gem 'overcommit'

# rspec-rails for creating tests
gem 'rspec-rails'

Expand All @@ -72,6 +75,9 @@ group :development, :test do
gem 'webmock', require: false

gem 'rubocop', require: false

# Use factories instead of fixtures
gem "factory_bot_rails"
end

group :development do
Expand Down
13 changes: 13 additions & 0 deletions Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ GEM
bootsnap (1.16.0)
msgpack (~> 1.2)
builder (3.2.4)
childprocess (4.1.0)
coderay (1.1.3)
concurrent-ruby (1.2.2)
connection_pool (2.4.1)
Expand All @@ -104,6 +105,11 @@ GEM
aws-sdk-dynamodb (~> 1.0)
concurrent-ruby (>= 1.0)
erubi (1.12.0)
factory_bot (6.2.1)
activesupport (>= 5.0.0)
factory_bot_rails (6.2.0)
factory_bot (~> 6.2.0)
railties (>= 5.0.0)
globalid (1.2.1)
activesupport (>= 6.1)
hashdiff (1.0.1)
Expand All @@ -113,6 +119,7 @@ GEM
multi_xml (>= 0.5.2)
i18n (1.14.1)
concurrent-ruby (~> 1.0)
iniparse (1.5.0)
io-console (0.6.0)
irb (1.7.0)
reline (>= 0.3.0)
Expand Down Expand Up @@ -157,6 +164,10 @@ GEM
racc (~> 1.4)
nokogiri (1.15.4-x86_64-linux)
racc (~> 1.4)
overcommit (0.60.0)
childprocess (>= 0.6.3, < 5)
iniparse (~> 1.4)
rexml (~> 3.2)
parallel (1.23.0)
parser (3.2.2.3)
ast (~> 2.4.1)
Expand Down Expand Up @@ -293,11 +304,13 @@ DEPENDENCIES
connection_pool
debug
dynamoid (= 3.8.0)
factory_bot_rails
hiredis
httparty
jbuilder
jwt
kredis
overcommit
prometheus_exporter
pry
puma (~> 6.4)
Expand Down
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,3 +46,7 @@ We use [RSwag](https://github.com/rswag/RSwag) to generate the API docs from the
Tests are grouped by "context" into success/fail groups. Add the `-e` flag to run tests matching search terms. So:
- To run success tests only: `bundle exec rspec -e success`
- To run failure tests only: `bundle exec rspec -e failure`

### Resources for Generating Hashes with FactoryBot

https://medium.com/@josisusan/factorygirl-as-json-response-a70f4a4e92a0
11 changes: 8 additions & 3 deletions app/controllers/application_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,17 @@ class ApplicationController < ActionController::API
def validate_token
auth_header = request.headers["Authorization"]
unless auth_header.present?
return render json: { error: ErrorCodes::MISSING_AUTHENTICATION }, status: :forbidden
return render json: { error: ErrorCodes::MISSING_AUTHENTICATION }, status: :unauthorized
end
token = request.headers["Authorization"].split[1]
begin
decoded_token = (JWT.decode token, JwtOptions.secret, true, { algorithm: JwtOptions.algorithm })[0]
@current_user = decoded_token["data"]["user_id"]
rescue JWT::VerificationError, JWT::InvalidJtiError
Metrics.jwt_verification_error_counter.increment
render json: { error: ErrorCodes::INVALID_TOKEN }, status: :forbidden
render json: { error: ErrorCodes::INVALID_TOKEN }, status: :unauthorized
rescue JWT::ExpiredSignature
render json: { error: ErrorCodes::EXPIRED_TOKEN }, status: :forbidden
render json: { error: ErrorCodes::EXPIRED_TOKEN }, status: :unauthorized
end
end

Expand All @@ -32,4 +32,9 @@ def performance_profile(&)
yield
end
end

def render_error(status, error)
Metrics.registration_validation_errors_counter.increment
render json: { error: error }, status: status
end
end
160 changes: 128 additions & 32 deletions app/controllers/registration_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

require 'securerandom'
require 'jwt'
require 'time'
require_relative '../helpers/competition_api'
require_relative '../helpers/user_api'
require_relative '../helpers/error_codes'
Expand Down Expand Up @@ -29,33 +30,42 @@ def validate_create_request
@user_id = registration_params[:user_id]
@competition_id = registration_params[:competition_id]
@event_ids = registration_params[:competing]["event_ids"]
status = ""
cannot_register_reason = nil

unless @current_user == @user_id.to_s
# This could be split out into a "validate competition exists" method
FinnIckler marked this conversation as resolved.
Show resolved Hide resolved
# Validations could also be restructured to be a bunch of private methods that validators call
@competition = CompetitionApi.get_competition_info(@competition_id)

unless CompetitionApi.competition_exists?(@competition_id) == true
return render_error(:not_found, ErrorCodes::COMPETITION_NOT_FOUND)
end

unless @current_user == @user_id.to_s || UserApi.can_administer?(@current_user, @competition_id)
Metrics.registration_impersonation_attempt_counter.increment
return render json: { error: ErrorCodes::USER_IMPERSONATION }, status: :forbidden
return render json: { error: ErrorCodes::USER_IMPERSONATION }, status: :unauthorized
end

can_compete, reasons = UserApi.can_compete?(@user_id)
unless can_compete
status = :forbidden
cannot_register_reason = reasons
if reasons == ErrorCodes::USER_IS_BANNED
return render_error(:forbidden, ErrorCodes::USER_IS_BANNED)
else
return render_error(:unauthorized, ErrorCodes::USER_PROFILE_INCOMPLETE)
end
# status = :forbidden
# cannot_register_reason = reasons
end

unless CompetitionApi.competition_open?(@competition_id)
status = :forbidden
cannot_register_reason = ErrorCodes::COMPETITION_CLOSED
if !CompetitionApi.competition_open?(@competition_id) && !(UserApi.can_administer?(@current_user, @competition_id) && @current_user == @user_id.to_s)
# Admin can only pre-regiser for themselves, not for other users
return render_error(:forbidden, ErrorCodes::REGISTRATION_CLOSED)
end

if @event_ids.empty? || !CompetitionApi.events_held?(@event_ids, @competition_id)
status = :bad_request
cannot_register_reason = ErrorCodes::COMPETITION_INVALID_EVENTS
return render_error(:unprocessable_entity, ErrorCodes::INVALID_EVENT_SELECTION)
end

unless cannot_register_reason.nil?
Metrics.registration_validation_errors_counter.increment
render json: { error: cannot_register_reason }, status: status
if params.key?(:guests) && !guests_valid?
render_error(:unprocessable_entity, ErrorCodes::GUEST_LIMIT_EXCEEDED)
end
end

Expand Down Expand Up @@ -119,26 +129,67 @@ def create
# You can either update your own registration or one for a competition you administer
def validate_update_request
@user_id, @competition_id = update_params
@admin_comment = params[:admin_comment]

# Check if competition exists
if CompetitionApi.competition_exists?(@competition_id) == true
@competition = CompetitionApi.get_competition_info(@competition_id)
else
return render_error(:not_found, ErrorCodes::COMPETITION_NOT_FOUND)
end

# Check if competition exists
unless registration_exists?(@user_id, @competition_id)
return render_error(:not_found, ErrorCodes::REGISTRATION_NOT_FOUND)
end

@registration = Registration.find("#{@competition_id}-#{@user_id}")

# Only the user or an admin can update a user's registration
unless @current_user == @user_id || UserApi.can_administer?(@current_user, @competition_id)
Metrics.registration_validation_errors_counter.increment
render json: { error: ErrorCodes::USER_INSUFFICIENT_PERMISSIONS }, status: :forbidden
return render_error(:unauthorized, ErrorCodes::USER_IMPERSONATION)
end

# User must be an admin if they're changing admin properties
admin_fields = [@admin_comment]
if (admin_fields.any? { |field| !(field.nil?) }) && !(UserApi.can_administer?(@current_user, @competition_id))
return render_error(:unauthorized, ErrorCodes::USER_INSUFFICIENT_PERMISSIONS)
end

if params.key?(:status)
validate_status_or_render_error
end

if params.key?(:event_ids)
validate_events_or_render_error
end

params.key?(:guests)
if params.key?(:guests) && !guests_valid?
return render_error(:unprocessable_entity, ErrorCodes::GUEST_LIMIT_EXCEEDED)
end

if params.key?(:comment) && !comment_valid?
return render_error(:unprocessable_entity, ErrorCodes::USER_COMMENT_TOO_LONG)
end

if !params.key?(:comment) && @competition[:competition_info]["force_comment_in_registration"]
render_error(:unprocessable_entity, ErrorCodes::REQUIRED_COMMENT_MISSING)
end
end

def update
status = params[:status]
comment = params[:comment]
admin_comment = params[:admin_comment]
guests = params[:guests]
event_ids = params[:event_ids]

begin
registration = Registration.find("#{@competition_id}-#{@user_id}")
updated_registration = registration.update_competing_lane!({ status: status, comment: comment, event_ids: event_ids, admin_comment: admin_comment, guests: guests })
updated_registration = registration.update_competing_lane!({ status: status, comment: comment, event_ids: event_ids, admin_comment: @admin_comment, guests: guests })
render json: { status: 'ok', registration: {
user_id: updated_registration["user_id"],
event_ids: updated_registration.event_ids,
registered_event_ids: updated_registration.registered_event_ids,
registration_status: updated_registration.competing_status,
registered_on: updated_registration["created_at"],
comment: updated_registration.competing_comment,
Expand All @@ -158,8 +209,7 @@ def validate_entry_request
@user_id, @competition_id = entry_params

unless @current_user == @user_id || UserApi.can_administer?(@current_user, @competition_id)
Metrics.registration_validation_errors_counter.increment
render json: { error: ErrorCodes::USER_INSUFFICIENT_PERMISSIONS }, status: :forbidden
render_error(:unauthorized, ErrorCodes::USER_IMPERSONATION)
end
end

Expand Down Expand Up @@ -197,16 +247,17 @@ def payment_ticket

def list
competition_id = list_params
competition_exists = CompetitionApi.competition_exists?(competition_id)
competition_info = CompetitionApi.get_competition_info(competition_id)

if competition_info[:competition_exists?] == false
return render json: { error: competition_info[:error] }, status: competition_info[:status]
end

registrations = get_registrations(competition_id, only_attending: true)
if competition_exists[:error]
# Even if the competition service is down, we still return the registrations if they exists
if registrations.count != 0 && competition_exists[:error] == ErrorCodes::COMPETITION_API_5XX
return render json: registrations
end
return render json: { error: competition_exists[:error] }, status: competition_exists[:status]

if registrations.count == 0 && competition_info[:error] == ErrorCodes::COMPETITION_API_5XX
return render json: { error: competition_info[:error] }, status: competition_info[:status]
end
# Render a success response
render json: registrations
rescue StandardError => e
# Render an error response
Expand All @@ -222,7 +273,7 @@ def validate_list_admin

unless UserApi.can_administer?(@current_user, @competition_id)
Metrics.registration_validation_errors_counter.increment
render json: { error: ErrorCodes::USER_INSUFFICIENT_PERMISSIONS }, status: 403
render json: { error: ErrorCodes::USER_INSUFFICIENT_PERMISSIONS }, status: 401
end
end

Expand All @@ -241,8 +292,6 @@ def list_admin

private

REGISTRATION_STATUS = %w[incoming waitlist accepted deleted].freeze

def registration_params
params.require([:user_id, :competition_id])
params.require(:competing).require(:event_ids)
Expand Down Expand Up @@ -292,4 +341,51 @@ def get_single_registration(user_id, competition_id)
guests: registration.guests,
}
end

def registration_exists?(user_id, competition_id)
Registration.find("#{competition_id}-#{user_id}")
true
rescue Dynamoid::Errors::RecordNotFound
false
end

def guests_valid?
@competition[:competition_info]["guest_entry_status"] != "restricted" || @competition[:competition_info]["guests_per_registration_limit"] >= params[:guests]
end

def comment_valid?
params[:comment].length <= 240
end

def validate_events_or_render_error
event_ids = params[:event_ids]
status = params.key?(:status) ? params[:status] : @registration.competing_status

# Events list can only be empty if the status is deleted
if event_ids == [] && status != "deleted"
return render_error(:unprocessable_entity, ErrorCodes::INVALID_EVENT_SELECTION)
end

if !CompetitionApi.events_held?(event_ids, @competition_id) && status != "deleted"
return render_error(:unprocessable_entity, ErrorCodes::INVALID_EVENT_SELECTION)
end

events_edit_deadline = Time.parse(@competition[:competition_info]["event_change_deadline_date"])
render_error(:forbidden, ErrorCodes::EVENT_EDIT_DEADLINE_PASSED) if events_edit_deadline < Time.now
end

def validate_status_or_render_error
unless Registration::REGISTRATION_STATES.include?(params[:status])
return render_error(:unprocessable_entity, ErrorCodes::INVALID_REQUEST_DATA)
end

if Registration::ADMIN_ONLY_STATES.include?(params[:status]) && !UserApi.can_administer?(@current_user, @competition_id)
return render_error(:unauthorized, ErrorCodes::USER_INSUFFICIENT_PERMISSIONS)
end

competitor_limit = @competition[:competition_info]["competitor_limit"]
if params[:status] == 'accepted' && Registration.count > competitor_limit
render_error(:forbidden, ErrorCodes::COMPETITOR_LIMIT_REACHED)
end
end
end
Loading