
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.
244 lines
8.9 KiB
Ruby
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
|