Files
codeocean/spec/controllers/sessions_controller_spec.rb
Sebastian Serth 99bd46af1a Align project files with CodeHarbor
Since both projects are developed together and by the same team, we also want to have the same code structure and utility methods available in both projects. Therefore, this commit changes many files, but without a functional change.
2023-10-11 00:18:33 +02:00

244 lines
8.9 KiB
Ruby

# frozen_string_literal: true
require 'rails_helper'
RSpec.describe SessionsController do
render_views
let(:consumer) { create(:consumer) }
describe 'POST #create' do
let(:password) { attributes_for(:teacher)[:password] }
let(:user) { InternalUser.create(user_attributes.merge(password:, consumer:)) }
let(:user_attributes) { build(:teacher).attributes }
context 'with valid credentials' do
before do
user.activate!
post :create, params: {email: user.email, password:, remember_me: 1}
end
expect_flash_message(:notice, :'sessions.create.success')
expect_redirect(:root)
end
context 'with invalid credentials' do
before { post :create, params: {email: user.email, password: '', remember_me: 1} }
expect_flash_message(:danger, :'sessions.create.failure')
expect_template(:new)
end
end
describe 'POST #create_through_lti' do
let(:exercise) { create(:dummy) }
let(:exercise2) { create(:dummy) }
let(:nonce) { SecureRandom.hex }
context 'without OAuth parameters' do
it 'refuses the LTI launch' do
expect(controller).to receive(:refuse_lti_launch).with(message: I18n.t('sessions.oauth.missing_parameters')).and_call_original
post :create_through_lti
end
end
context 'without a valid consumer key' do
it 'refuses the LTI launch' do
expect(controller).to receive(:refuse_lti_launch).with(message: I18n.t('sessions.oauth.invalid_consumer')).and_call_original
post :create_through_lti, params: {oauth_consumer_key: SecureRandom.hex, oauth_signature: SecureRandom.hex}
end
end
context 'with an invalid OAuth signature' do
it 'refuses the LTI launch' do
expect(controller).to receive(:refuse_lti_launch).with(message: I18n.t('sessions.oauth.invalid_signature')).and_call_original
post :create_through_lti, params: {oauth_consumer_key: consumer.oauth_key, oauth_signature: SecureRandom.hex}
end
end
context 'without a unique OAuth nonce' do
it 'refuses the LTI launch' do
allow_any_instance_of(IMS::LTI::ToolProvider).to receive(:valid_request?).and_return(true)
allow(NonceStore).to receive(:has?).with(nonce).and_return(true)
expect(controller).to receive(:refuse_lti_launch).with(message: I18n.t('sessions.oauth.used_nonce')).and_call_original
post :create_through_lti, params: {oauth_consumer_key: consumer.oauth_key, oauth_nonce: nonce, oauth_signature: SecureRandom.hex}
end
end
context 'without a valid exercise token' do
it 'refuses the LTI launch' do
allow_any_instance_of(IMS::LTI::ToolProvider).to receive(:valid_request?).and_return(true)
expect(controller).to receive(:refuse_lti_launch).with(message: I18n.t('sessions.oauth.invalid_exercise_token')).and_call_original
post :create_through_lti, params: {custom_token: '', oauth_consumer_key: consumer.oauth_key, oauth_nonce: nonce, oauth_signature: SecureRandom.hex, user_id: '123'}
end
end
context 'with valid launch parameters' do
let(:locale) { :de }
let(:perform_request) { post :create_through_lti, params: {custom_locale: locale, custom_token: exercise.token, oauth_consumer_key: consumer.oauth_key, oauth_nonce: nonce, oauth_signature: SecureRandom.hex, user_id: user.external_id} }
let(:user) { create(:external_user, consumer:) }
before { allow_any_instance_of(IMS::LTI::ToolProvider).to receive(:valid_request?).and_return(true) }
it 'assigns the current user' do
perform_request
expect(assigns(:current_user)).to be_an(ExternalUser)
expect(session[:external_user_id]).to eq(user.id)
end
it 'sets the specified locale' do
expect(controller).to receive(:switch_locale).and_call_original
i18n = class_double(I18n, locale:)
allow(I18n).to receive(:locale=).with(I18n.default_locale).and_call_original
allow(I18n).to receive(:locale=).with(locale).and_return(i18n)
perform_request
expect(i18n.locale.to_sym).to eq(locale)
end
it 'assigns the exercise' do
perform_request
expect(assigns(:exercise)).to eq(exercise)
end
it 'stores LTI parameters in the session' do
expect(controller).to receive(:store_lti_session_data)
perform_request
end
it 'stores the OAuth nonce' do
expect(controller).to receive(:store_nonce).with(nonce)
perform_request
end
context 'when LTI outcomes are supported' do
# The expected message should be localized in the requested localization
let(:message) { I18n.t('sessions.create_through_lti.session_with_outcome', consumer:, locale:) }
before do
allow(controller).to receive(:lti_outcome_service?).and_return(true)
perform_request
end
expect_flash_message(:notice, :message)
end
context 'when LTI outcomes are not supported' do
# The expected message should be localized in the requested localization
let(:message) { I18n.t('sessions.create_through_lti.session_without_outcome', consumer:, locale:) }
before do
allow(controller).to receive(:lti_outcome_service?).and_return(false)
perform_request
end
expect_flash_message(:notice, :message)
end
it 'redirects to the requested exercise' do
perform_request
expect(controller).to redirect_to(implement_exercise_path(exercise.id))
end
it 'redirects to recommended exercise if requested token of proxy exercise' do
create(:proxy_exercise, exercises: [exercise])
post :create_through_lti, params: {custom_locale: locale, custom_token: ProxyExercise.first.token, oauth_consumer_key: consumer.oauth_key, oauth_nonce: nonce, oauth_signature: SecureRandom.hex, user_id: user.external_id}
expect(controller).to redirect_to(implement_exercise_path(exercise.id))
end
it 'recommends only exercises who are 1 degree more complicated than what user has seen' do
# dummy user has no exercises finished, therefore his highest difficulty is 0
create(:proxy_exercise, exercises: [exercise, exercise2])
exercise.expected_difficulty = 3
exercise.save
exercise2.expected_difficulty = 1
exercise2.save
post :create_through_lti, params: {custom_locale: locale, custom_token: ProxyExercise.first.token, oauth_consumer_key: consumer.oauth_key, oauth_nonce: nonce, oauth_signature: SecureRandom.hex, user_id: user.external_id}
expect(controller).to redirect_to(implement_exercise_path(exercise2.id))
end
end
end
describe 'DELETE #destroy' do
let(:user) { double }
before do
allow(controller).to receive(:set_sentry_context).and_return(nil)
allow(controller).to receive(:current_user).at_least(:once).and_return(user)
end
context 'with an internal user' do
before do
allow(user).to receive(:external_user?).and_return(false)
allow(user).to receive(:forget_me!)
delete :destroy
end
it 'performs a logout' do
expect(controller).to receive(:logout)
delete :destroy
end
it 'redirects to the root path' do
expect(controller).to redirect_to(:root)
end
end
context 'with an external user' do
before do
allow(user).to receive(:external_user?).and_return(true)
delete :destroy
end
it 'clears the session' do
expect(controller.session).to receive(:delete).with(:external_user_id)
expect(controller.session).to receive(:delete).with(:study_group_id)
expect(controller.session).to receive(:delete).with(:embed_options)
expect(controller.session).to receive(:delete).with(:pg_id)
delete :destroy
end
it 'redirects to the root path' do
expect(controller).to redirect_to(:root)
end
end
end
describe 'GET #destroy_through_lti' do
let(:perform_request) { proc { get :destroy_through_lti, params: {submission_id: submission.id} } }
let(:submission) { create(:submission, exercise: create(:dummy)) }
before do
create(:lti_parameter, external_user: submission.contributor)
allow(controller).to receive(:current_user).and_return(submission.contributor)
perform_request.call
end
expect_http_status(:ok)
expect_template(:destroy_through_lti)
end
describe 'GET #new' do
context 'when no user is logged in' do
before do
allow(controller).to receive_messages(set_sentry_context: nil, current_user: nil)
get :new
end
expect_http_status(:ok)
expect_template(:new)
end
context 'when a user is already logged in' do
before do
allow(controller).to receive_messages(set_sentry_context: nil, current_user: build(:teacher))
get :new
end
expect_flash_message(:alert, :'shared.already_signed_in')
expect_redirect(:root)
end
end
end