transferred Code Ocean from original repository to GitHub
This commit is contained in:
45
spec/controllers/application_controller_spec.rb
Normal file
45
spec/controllers/application_controller_spec.rb
Normal file
@@ -0,0 +1,45 @@
|
||||
require 'rails_helper'
|
||||
|
||||
describe ApplicationController do
|
||||
describe '#current_user' do
|
||||
context 'with an external user' do
|
||||
let(:external_user) { FactoryGirl.create(:external_user) }
|
||||
before(:each) { session[:external_user_id] = external_user.id }
|
||||
|
||||
it 'returns the external user' do
|
||||
expect(controller.current_user).to eq(external_user)
|
||||
end
|
||||
end
|
||||
|
||||
context 'without an external user' do
|
||||
let(:internal_user) { FactoryGirl.create(:teacher) }
|
||||
before(:each) { login_user(internal_user) }
|
||||
|
||||
it 'returns the internal user' do
|
||||
expect(controller.current_user).to eq(internal_user)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#render_not_authorized' do
|
||||
let(:render_not_authorized) { controller.send(:render_not_authorized) }
|
||||
|
||||
it 'displays a flash message' do
|
||||
expect(controller).to receive(:redirect_to)
|
||||
render_not_authorized
|
||||
expect(flash[:danger]).to eq(I18n.t('application.not_authorized'))
|
||||
end
|
||||
|
||||
it 'redirects to the root URL' do
|
||||
expect(controller).to receive(:redirect_to).with(:root)
|
||||
render_not_authorized
|
||||
end
|
||||
end
|
||||
|
||||
describe 'GET #welcome' do
|
||||
before(:each) { get :welcome }
|
||||
|
||||
expect_status(200)
|
||||
expect_template(:welcome)
|
||||
end
|
||||
end
|
21
spec/controllers/code_ocean/files_controller_spec.rb
Normal file
21
spec/controllers/code_ocean/files_controller_spec.rb
Normal file
@@ -0,0 +1,21 @@
|
||||
require 'rails_helper'
|
||||
|
||||
describe CodeOcean::FilesController do
|
||||
let(:user) { FactoryGirl.build(:admin) }
|
||||
before(:each) { allow(controller).to receive(:current_user).and_return(user) }
|
||||
|
||||
describe 'DELETE #destroy' do
|
||||
let(:exercise) { FactoryGirl.create(:fibonacci) }
|
||||
let(:request) { Proc.new { delete :destroy, id: exercise.files.first.id } }
|
||||
before(:each) { request.call }
|
||||
|
||||
expect_assigns(file: CodeOcean::File)
|
||||
|
||||
it 'destroys the file' do
|
||||
exercise = FactoryGirl.create(:fibonacci)
|
||||
expect { request.call }.to change(CodeOcean::File, :count).by(-1)
|
||||
end
|
||||
|
||||
expect_redirect
|
||||
end
|
||||
end
|
93
spec/controllers/consumers_controller_spec.rb
Normal file
93
spec/controllers/consumers_controller_spec.rb
Normal file
@@ -0,0 +1,93 @@
|
||||
require 'rails_helper'
|
||||
|
||||
describe ConsumersController do
|
||||
let(:consumer) { FactoryGirl.create(:consumer) }
|
||||
let(:user) { FactoryGirl.create(:admin) }
|
||||
before(:each) { allow(controller).to receive(:current_user).and_return(user) }
|
||||
|
||||
describe 'POST #create' do
|
||||
context 'with a valid consumer' do
|
||||
let(:request) { Proc.new { post :create, consumer: FactoryGirl.attributes_for(:consumer) } }
|
||||
before(:each) { request.call }
|
||||
|
||||
expect_assigns(consumer: Consumer)
|
||||
|
||||
it 'creates the consumer' do
|
||||
expect { request.call }.to change(Consumer, :count).by(1)
|
||||
end
|
||||
|
||||
expect_redirect
|
||||
end
|
||||
|
||||
context 'with an invalid consumer' do
|
||||
before(:each) { post :create, consumer: {} }
|
||||
|
||||
expect_assigns(consumer: Consumer)
|
||||
expect_status(200)
|
||||
expect_template(:new)
|
||||
end
|
||||
end
|
||||
|
||||
describe 'DELETE #destroy' do
|
||||
before(:each) { delete :destroy, id: consumer.id }
|
||||
|
||||
expect_assigns(consumer: Consumer)
|
||||
|
||||
it 'destroys the consumer' do
|
||||
consumer = FactoryGirl.create(:consumer)
|
||||
expect { delete :destroy, id: consumer.id }.to change(Consumer, :count).by(-1)
|
||||
end
|
||||
|
||||
expect_redirect(:consumers)
|
||||
end
|
||||
|
||||
describe 'GET #edit' do
|
||||
before(:each) { get :edit, id: consumer.id }
|
||||
|
||||
expect_assigns(consumer: Consumer)
|
||||
expect_status(200)
|
||||
expect_template(:edit)
|
||||
end
|
||||
|
||||
describe 'GET #index' do
|
||||
let!(:consumers) { FactoryGirl.create_pair(:consumer) }
|
||||
before(:each) { get :index }
|
||||
|
||||
expect_assigns(consumers: Consumer.all)
|
||||
expect_status(200)
|
||||
expect_template(:index)
|
||||
end
|
||||
|
||||
describe 'GET #new' do
|
||||
before(:each) { get :new }
|
||||
|
||||
expect_assigns(consumer: Consumer)
|
||||
expect_status(200)
|
||||
expect_template(:new)
|
||||
end
|
||||
|
||||
describe 'GET #show' do
|
||||
before(:each) { get :show, id: consumer.id }
|
||||
|
||||
expect_assigns(consumer: :consumer)
|
||||
expect_status(200)
|
||||
expect_template(:show)
|
||||
end
|
||||
|
||||
describe 'PUT #update' do
|
||||
context 'with a valid consumer' do
|
||||
before(:each) { put :update, consumer: FactoryGirl.attributes_for(:consumer), id: consumer.id }
|
||||
|
||||
expect_assigns(consumer: Consumer)
|
||||
expect_redirect
|
||||
end
|
||||
|
||||
context 'with an invalid consumer' do
|
||||
before(:each) { put :update, consumer: {name: ''}, id: consumer.id }
|
||||
|
||||
expect_assigns(consumer: Consumer)
|
||||
expect_status(200)
|
||||
expect_template(:edit)
|
||||
end
|
||||
end
|
||||
end
|
85
spec/controllers/errors_controller_spec.rb
Normal file
85
spec/controllers/errors_controller_spec.rb
Normal file
@@ -0,0 +1,85 @@
|
||||
require 'rails_helper'
|
||||
|
||||
describe ErrorsController do
|
||||
let(:error) { FactoryGirl.create(:error) }
|
||||
let(:execution_environment) { error.execution_environment }
|
||||
let(:user) { FactoryGirl.create(:admin) }
|
||||
before(:each) { allow(controller).to receive(:current_user).and_return(user) }
|
||||
|
||||
describe 'POST #create' do
|
||||
context 'with a valid error' do
|
||||
let(:request) { Proc.new { post :create, execution_environment_id: FactoryGirl.build(:error).execution_environment.id, error: FactoryGirl.attributes_for(:error), format: :json } }
|
||||
|
||||
context 'when a hint can be matched' do
|
||||
let(:hint) { FactoryGirl.build(:ruby_syntax_error).message }
|
||||
|
||||
before(:each) do
|
||||
expect_any_instance_of(Whistleblower).to receive(:generate_hint).and_return(hint)
|
||||
request.call
|
||||
end
|
||||
|
||||
expect_assigns(execution_environment: :execution_environment)
|
||||
|
||||
it 'does not create the error' do
|
||||
allow_any_instance_of(Whistleblower).to receive(:generate_hint).and_return(hint)
|
||||
expect { request.call }.not_to change(Error, :count)
|
||||
end
|
||||
|
||||
it 'returns the hint' do
|
||||
expect(response.body).to eq({hint: hint}.to_json)
|
||||
end
|
||||
|
||||
expect_content_type('application/json')
|
||||
expect_status(200)
|
||||
end
|
||||
|
||||
context 'when no hint can be matched' do
|
||||
before(:each) do
|
||||
expect_any_instance_of(Whistleblower).to receive(:generate_hint).and_return(nil)
|
||||
request.call
|
||||
end
|
||||
|
||||
expect_assigns(execution_environment: :execution_environment)
|
||||
|
||||
it 'creates the error' do
|
||||
allow_any_instance_of(Whistleblower).to receive(:generate_hint)
|
||||
expect { request.call }.to change(Error, :count).by(1)
|
||||
end
|
||||
|
||||
expect_content_type('application/json')
|
||||
expect_status(201)
|
||||
end
|
||||
end
|
||||
|
||||
context 'with an invalid error' do
|
||||
before(:each) { post :create, execution_environment_id: FactoryGirl.build(:error).execution_environment.id, error: {}, format: :json }
|
||||
|
||||
expect_assigns(error: Error)
|
||||
expect_content_type('application/json')
|
||||
expect_status(422)
|
||||
end
|
||||
end
|
||||
|
||||
describe 'GET #index' do
|
||||
let!(:errors) { FactoryGirl.create_pair(:error) }
|
||||
before(:each) { get :index, execution_environment_id: execution_environment.id }
|
||||
|
||||
expect_assigns(execution_environment: :execution_environment)
|
||||
|
||||
it 'aggregates errors by message' do
|
||||
expect(errors.count).to be_a(Numeric)
|
||||
end
|
||||
|
||||
expect_status(200)
|
||||
expect_template(:index)
|
||||
end
|
||||
|
||||
describe 'GET #show' do
|
||||
before(:each) { get :show, execution_environment_id: execution_environment.id, id: error.id }
|
||||
|
||||
expect_assigns(error: :error)
|
||||
expect_assigns(execution_environment: :execution_environment)
|
||||
expect_status(200)
|
||||
expect_template(:show)
|
||||
end
|
||||
end
|
152
spec/controllers/execution_environments_controller_spec.rb
Normal file
152
spec/controllers/execution_environments_controller_spec.rb
Normal file
@@ -0,0 +1,152 @@
|
||||
require 'rails_helper'
|
||||
|
||||
describe ExecutionEnvironmentsController do
|
||||
let(:execution_environment) { FactoryGirl.create(:ruby) }
|
||||
let(:user) { FactoryGirl.create(:admin) }
|
||||
before(:each) { allow(controller).to receive(:current_user).and_return(user) }
|
||||
|
||||
describe 'POST #create' do
|
||||
before(:each) { expect(DockerClient).to receive(:image_tags).at_least(:once).and_return([]) }
|
||||
|
||||
context 'with a valid execution environment' do
|
||||
let(:request) { Proc.new { post :create, execution_environment: FactoryGirl.attributes_for(:ruby) } }
|
||||
before(:each) { request.call }
|
||||
|
||||
expect_assigns(docker_images: Array)
|
||||
expect_assigns(execution_environment: ExecutionEnvironment)
|
||||
|
||||
it 'creates the execution environment' do
|
||||
expect { request.call }.to change(ExecutionEnvironment, :count).by(1)
|
||||
end
|
||||
|
||||
expect_redirect
|
||||
end
|
||||
|
||||
context 'with an invalid execution environment' do
|
||||
before(:each) { post :create, execution_environment: {} }
|
||||
|
||||
expect_assigns(execution_environment: ExecutionEnvironment)
|
||||
expect_status(200)
|
||||
expect_template(:new)
|
||||
end
|
||||
end
|
||||
|
||||
describe 'DELETE #destroy' do
|
||||
before(:each) { delete :destroy, id: execution_environment.id }
|
||||
|
||||
expect_assigns(execution_environment: :execution_environment)
|
||||
|
||||
it 'destroys the execution environment' do
|
||||
execution_environment = FactoryGirl.create(:ruby)
|
||||
expect { delete :destroy, id: execution_environment.id }.to change(ExecutionEnvironment, :count).by(-1)
|
||||
end
|
||||
|
||||
expect_redirect(:execution_environments)
|
||||
end
|
||||
|
||||
describe 'GET #edit' do
|
||||
before(:each) do
|
||||
expect(DockerClient).to receive(:image_tags).at_least(:once).and_return([])
|
||||
get :edit, id: execution_environment.id
|
||||
end
|
||||
|
||||
expect_assigns(docker_images: Array)
|
||||
expect_assigns(execution_environment: :execution_environment)
|
||||
expect_status(200)
|
||||
expect_template(:edit)
|
||||
end
|
||||
|
||||
describe 'POST #execute_command' do
|
||||
let(:command) { 'which ruby' }
|
||||
|
||||
before(:each) do
|
||||
expect(DockerClient).to receive(:new).with(execution_environment: execution_environment, user: user).and_call_original
|
||||
expect_any_instance_of(DockerClient).to receive(:execute_command).with(command)
|
||||
post :execute_command, command: command, id: execution_environment.id
|
||||
end
|
||||
|
||||
expect_assigns(docker_client: DockerClient)
|
||||
expect_assigns(execution_environment: :execution_environment)
|
||||
expect_content_type('application/json')
|
||||
expect_status(200)
|
||||
end
|
||||
|
||||
describe 'GET #index' do
|
||||
let!(:execution_environments) { FactoryGirl.create_pair(:ruby) }
|
||||
before(:each) { get :index }
|
||||
|
||||
expect_assigns(execution_environments: ExecutionEnvironment.all)
|
||||
expect_status(200)
|
||||
expect_template(:index)
|
||||
end
|
||||
|
||||
describe 'GET #new' do
|
||||
before(:each) do
|
||||
expect(DockerClient).to receive(:image_tags).at_least(:once).and_return([])
|
||||
get :new
|
||||
end
|
||||
|
||||
expect_assigns(docker_images: Array)
|
||||
expect_assigns(execution_environment: ExecutionEnvironment)
|
||||
expect_status(200)
|
||||
expect_template(:new)
|
||||
end
|
||||
|
||||
describe '#set_docker_images', docker: true do
|
||||
context 'when Docker is unavailable' do
|
||||
let(:error_message) { 'Docker is unavailable' }
|
||||
|
||||
before(:each) do
|
||||
expect(DockerClient).to receive(:check_availability!).at_least(:once).and_raise(DockerClient::Error.new(error_message))
|
||||
controller.send(:set_docker_images)
|
||||
end
|
||||
|
||||
it 'fails gracefully' do
|
||||
expect { controller.send(:set_docker_images) }.not_to raise_error
|
||||
end
|
||||
|
||||
expect_assigns(docker_images: Array)
|
||||
|
||||
it 'displays a flash message' do
|
||||
expect(flash[:warning]).to eq(error_message)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'GET #shell' do
|
||||
before(:each) { get :shell, id: execution_environment.id }
|
||||
|
||||
expect_assigns(execution_environment: :execution_environment)
|
||||
expect_status(200)
|
||||
expect_template(:shell)
|
||||
end
|
||||
|
||||
describe 'GET #show' do
|
||||
before(:each) { get :show, id: execution_environment.id }
|
||||
|
||||
expect_assigns(execution_environment: :execution_environment)
|
||||
expect_status(200)
|
||||
expect_template(:show)
|
||||
end
|
||||
|
||||
describe 'PUT #update' do
|
||||
context 'with a valid execution environment' do
|
||||
before(:each) do
|
||||
expect(DockerClient).to receive(:image_tags).at_least(:once).and_return([])
|
||||
put :update, execution_environment: FactoryGirl.attributes_for(:ruby), id: execution_environment.id
|
||||
end
|
||||
|
||||
expect_assigns(docker_images: Array)
|
||||
expect_assigns(execution_environment: ExecutionEnvironment)
|
||||
expect_redirect
|
||||
end
|
||||
|
||||
context 'with an invalid execution environment' do
|
||||
before(:each) { put :update, execution_environment: {name: ''}, id: execution_environment.id }
|
||||
|
||||
expect_assigns(execution_environment: ExecutionEnvironment)
|
||||
expect_status(200)
|
||||
expect_template(:edit)
|
||||
end
|
||||
end
|
||||
end
|
213
spec/controllers/exercises_controller_spec.rb
Normal file
213
spec/controllers/exercises_controller_spec.rb
Normal file
@@ -0,0 +1,213 @@
|
||||
require 'rails_helper'
|
||||
|
||||
describe ExercisesController do
|
||||
let(:exercise) { FactoryGirl.create(:fibonacci) }
|
||||
let(:user) { FactoryGirl.create(:admin) }
|
||||
before(:each) { allow(controller).to receive(:current_user).and_return(user) }
|
||||
|
||||
describe 'POST #create' do
|
||||
let(:exercise_attributes) { FactoryGirl.build(:fibonacci).attributes }
|
||||
|
||||
context 'with a valid exercise' do
|
||||
let(:request) { Proc.new { post :create, exercise: exercise_attributes } }
|
||||
before(:each) { request.call }
|
||||
|
||||
expect_assigns(exercise: Exercise)
|
||||
|
||||
it 'creates the exercise' do
|
||||
expect { request.call }.to change(Exercise, :count).by(1)
|
||||
end
|
||||
|
||||
expect_redirect
|
||||
end
|
||||
|
||||
context 'when including a file' do
|
||||
let(:files_attributes) { {'0' => FactoryGirl.build(:file).attributes} }
|
||||
let(:request) { Proc.new { post :create, exercise: exercise_attributes.merge(files_attributes: files_attributes) } }
|
||||
|
||||
it 'creates the file' do
|
||||
expect { request.call }.to change(CodeOcean::File, :count)
|
||||
end
|
||||
end
|
||||
|
||||
context "with a file upload" do
|
||||
let(:files_attributes) { {'0' => FactoryGirl.build(:file, content: fixture_file_upload('upload.rb', 'text/x-ruby')).attributes} }
|
||||
let(:request) { Proc.new { post :create, exercise: exercise_attributes.merge(files_attributes: files_attributes) } }
|
||||
|
||||
it 'creates the file' do
|
||||
expect { request.call }.to change(CodeOcean::File, :count)
|
||||
end
|
||||
|
||||
it 'assigns the file content' do
|
||||
request.call
|
||||
file = File.new(Rails.root.join('spec', 'fixtures', 'upload.rb'), 'r')
|
||||
expect(Exercise.last.files.first.content).to eq(file.read)
|
||||
file.close
|
||||
end
|
||||
end
|
||||
|
||||
context 'with an invalid exercise' do
|
||||
before(:each) { post :create, exercise: {} }
|
||||
|
||||
expect_assigns(exercise: Exercise)
|
||||
expect_status(200)
|
||||
expect_template(:new)
|
||||
end
|
||||
end
|
||||
|
||||
describe 'DELETE #destroy' do
|
||||
before(:each) { delete :destroy, id: exercise.id }
|
||||
|
||||
expect_assigns(exercise: :exercise)
|
||||
|
||||
it 'destroys the exercise' do
|
||||
exercise = FactoryGirl.create(:fibonacci)
|
||||
expect { delete :destroy, id: exercise.id }.to change(Exercise, :count).by(-1)
|
||||
end
|
||||
|
||||
expect_redirect(:exercises)
|
||||
end
|
||||
|
||||
describe 'GET #edit' do
|
||||
before(:each) { get :edit, id: exercise.id }
|
||||
|
||||
expect_assigns(exercise: :exercise)
|
||||
expect_status(200)
|
||||
expect_template(:edit)
|
||||
end
|
||||
|
||||
describe 'GET #implement' do
|
||||
let(:request) { Proc.new { get :implement, id: exercise.id } }
|
||||
before(:each) { request.call }
|
||||
|
||||
expect_assigns(exercise: :exercise)
|
||||
|
||||
context 'with an existing submission' do
|
||||
let!(:submission) { FactoryGirl.create(:submission, exercise_id: exercise.id, user_id: user.id, user_type: InternalUser.class.name) }
|
||||
|
||||
it "populates the editors with the submission's files' content" do
|
||||
request.call
|
||||
expect(assigns(:files)).to eq(submission.files)
|
||||
end
|
||||
end
|
||||
|
||||
context 'without an existing submission' do
|
||||
it "populates the editors with the exercise's files' content" do
|
||||
expect(assigns(:files)).to eq(exercise.files.visible)
|
||||
end
|
||||
end
|
||||
|
||||
expect_status(200)
|
||||
expect_template(:implement)
|
||||
end
|
||||
|
||||
describe 'GET #index' do
|
||||
let!(:exercises) { FactoryGirl.create_pair(:fibonacci) }
|
||||
let(:scope) { Pundit.policy_scope!(user, Exercise) }
|
||||
before(:each) { get :index }
|
||||
|
||||
expect_assigns(exercises: :scope)
|
||||
expect_status(200)
|
||||
expect_template(:index)
|
||||
end
|
||||
|
||||
describe 'GET #new' do
|
||||
before(:each) { get :new }
|
||||
|
||||
expect_assigns(execution_environments: ExecutionEnvironment.all, exercise: Exercise)
|
||||
expect_assigns(exercise: Exercise)
|
||||
expect_status(200)
|
||||
expect_template(:new)
|
||||
end
|
||||
|
||||
describe 'GET #show' do
|
||||
before(:each) { get :show, id: exercise.id }
|
||||
|
||||
expect_assigns(exercise: :exercise)
|
||||
expect_status(200)
|
||||
expect_template(:show)
|
||||
end
|
||||
|
||||
describe 'POST #submit' do
|
||||
let(:output) { {} }
|
||||
let(:request) { post :submit, format: :json, id: exercise.id, submission: {cause: 'submit', exercise_id: exercise.id} }
|
||||
|
||||
before(:each) do
|
||||
expect(controller).to receive(:execute_test_files).and_return([{score: 1, weight: 1}])
|
||||
expect(controller).to receive(:score_submission).and_call_original
|
||||
end
|
||||
|
||||
context 'when LTI outcomes are supported' do
|
||||
before(:each) do
|
||||
expect(controller).to receive(:lti_outcome_service?).and_return(true)
|
||||
end
|
||||
|
||||
context 'when the score transmission succeeds' do
|
||||
before(:each) do
|
||||
expect(controller).to receive(:send_score).and_return({status: 'success'})
|
||||
request
|
||||
end
|
||||
|
||||
expect_assigns(exercise: :exercise)
|
||||
|
||||
it 'creates a submission' do
|
||||
expect(assigns(:submission)).to be_a(Submission)
|
||||
end
|
||||
|
||||
expect_content_type('application/json')
|
||||
expect_status(200)
|
||||
end
|
||||
|
||||
context 'when the score transmission fails' do
|
||||
before(:each) do
|
||||
expect(controller).to receive(:send_score).and_return({status: 'unsupported'})
|
||||
request
|
||||
end
|
||||
|
||||
expect_assigns(exercise: :exercise)
|
||||
|
||||
it 'creates a submission' do
|
||||
expect(assigns(:submission)).to be_a(Submission)
|
||||
end
|
||||
|
||||
expect_content_type('application/json')
|
||||
expect_status(503)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when LTI outcomes are not supported' do
|
||||
before(:each) do
|
||||
expect(controller).to receive(:lti_outcome_service?).and_return(false)
|
||||
expect(controller).not_to receive(:send_score)
|
||||
request
|
||||
end
|
||||
|
||||
expect_assigns(exercise: :exercise)
|
||||
|
||||
it 'creates a submission' do
|
||||
expect(assigns(:submission)).to be_a(Submission)
|
||||
end
|
||||
|
||||
expect_content_type('application/json')
|
||||
expect_status(200)
|
||||
end
|
||||
end
|
||||
|
||||
describe 'PUT #update' do
|
||||
context 'with a valid exercise' do
|
||||
let(:exercise_attributes) { FactoryGirl.build(:fibonacci).attributes }
|
||||
before(:each) { put :update, exercise: exercise_attributes, id: exercise.id }
|
||||
|
||||
expect_assigns(exercise: Exercise)
|
||||
expect_redirect
|
||||
end
|
||||
|
||||
context 'with an invalid exercise' do
|
||||
before(:each) { put :update, exercise: {title: ''}, id: exercise.id }
|
||||
|
||||
expect_assigns(exercise: Exercise)
|
||||
expect_status(200)
|
||||
expect_template(:edit)
|
||||
end
|
||||
end
|
||||
end
|
23
spec/controllers/external_users_controller_spec.rb
Normal file
23
spec/controllers/external_users_controller_spec.rb
Normal file
@@ -0,0 +1,23 @@
|
||||
require 'rails_helper'
|
||||
|
||||
describe ExternalUsersController do
|
||||
let(:user) { FactoryGirl.build(:admin) }
|
||||
let!(:users) { FactoryGirl.create_pair(:external_user) }
|
||||
before(:each) { allow(controller).to receive(:current_user).and_return(user) }
|
||||
|
||||
describe 'GET #index' do
|
||||
before(:each) { get :index }
|
||||
|
||||
expect_assigns(users: ExternalUser.all)
|
||||
expect_status(200)
|
||||
expect_template(:index)
|
||||
end
|
||||
|
||||
describe 'GET #show' do
|
||||
before(:each) { get :show, id: users.first.id }
|
||||
|
||||
expect_assigns(user: ExternalUser)
|
||||
expect_status(200)
|
||||
expect_template(:show)
|
||||
end
|
||||
end
|
99
spec/controllers/file_types_controller_spec.rb
Normal file
99
spec/controllers/file_types_controller_spec.rb
Normal file
@@ -0,0 +1,99 @@
|
||||
require 'rails_helper'
|
||||
|
||||
describe FileTypesController do
|
||||
let(:file_type) { FactoryGirl.create(:dot_rb) }
|
||||
let(:user) { FactoryGirl.create(:admin) }
|
||||
before(:each) { allow(controller).to receive(:current_user).and_return(user) }
|
||||
|
||||
describe 'POST #create' do
|
||||
context 'with a valid file type' do
|
||||
let(:request) { Proc.new { post :create, file_type: FactoryGirl.attributes_for(:dot_rb) } }
|
||||
before(:each) { request.call }
|
||||
|
||||
expect_assigns(editor_modes: Array)
|
||||
expect_assigns(file_type: FileType)
|
||||
|
||||
it 'creates the file type' do
|
||||
expect { request.call }.to change(FileType, :count).by(1)
|
||||
end
|
||||
|
||||
expect_redirect
|
||||
end
|
||||
|
||||
context 'with an invalid file type' do
|
||||
before(:each) { post :create, file_type: {} }
|
||||
|
||||
expect_assigns(editor_modes: Array)
|
||||
expect_assigns(file_type: FileType)
|
||||
expect_status(200)
|
||||
expect_template(:new)
|
||||
end
|
||||
end
|
||||
|
||||
describe 'DELETE #destroy' do
|
||||
before(:each) { delete :destroy, id: file_type.id }
|
||||
|
||||
expect_assigns(file_type: FileType)
|
||||
|
||||
it 'destroys the file type' do
|
||||
file_type = FactoryGirl.create(:dot_rb)
|
||||
expect { delete :destroy, id: file_type.id }.to change(FileType, :count).by(-1)
|
||||
end
|
||||
|
||||
expect_redirect(:file_types)
|
||||
end
|
||||
|
||||
describe 'GET #edit' do
|
||||
before(:each) { get :edit, id: file_type.id }
|
||||
|
||||
expect_assigns(editor_modes: Array)
|
||||
expect_assigns(file_type: FileType)
|
||||
expect_status(200)
|
||||
expect_template(:edit)
|
||||
end
|
||||
|
||||
describe 'GET #index' do
|
||||
let!(:file_types) { FactoryGirl.create_pair(:dot_rb) }
|
||||
before(:each) { get :index }
|
||||
|
||||
expect_assigns(file_types: FileType.all)
|
||||
expect_status(200)
|
||||
expect_template(:index)
|
||||
end
|
||||
|
||||
describe 'GET #new' do
|
||||
before(:each) { get :new }
|
||||
|
||||
expect_assigns(editor_modes: Array)
|
||||
expect_assigns(file_type: FileType)
|
||||
expect_status(200)
|
||||
expect_template(:new)
|
||||
end
|
||||
|
||||
describe 'GET #show' do
|
||||
before(:each) { get :show, id: file_type.id }
|
||||
|
||||
expect_assigns(file_type: :file_type)
|
||||
expect_status(200)
|
||||
expect_template(:show)
|
||||
end
|
||||
|
||||
describe 'PUT #update' do
|
||||
context 'with a valid file type' do
|
||||
before(:each) { put :update, file_type: FactoryGirl.attributes_for(:dot_rb), id: file_type.id }
|
||||
|
||||
expect_assigns(editor_modes: Array)
|
||||
expect_assigns(file_type: FileType)
|
||||
expect_redirect
|
||||
end
|
||||
|
||||
context 'with an invalid file type' do
|
||||
before(:each) { put :update, file_type: {name: ''}, id: file_type.id }
|
||||
|
||||
expect_assigns(editor_modes: Array)
|
||||
expect_assigns(file_type: FileType)
|
||||
expect_status(200)
|
||||
expect_template(:edit)
|
||||
end
|
||||
end
|
||||
end
|
103
spec/controllers/hints_controller_spec.rb
Normal file
103
spec/controllers/hints_controller_spec.rb
Normal file
@@ -0,0 +1,103 @@
|
||||
require 'rails_helper'
|
||||
|
||||
describe HintsController do
|
||||
let(:execution_environment) { FactoryGirl.create(:ruby) }
|
||||
let(:hint) { FactoryGirl.create(:ruby_syntax_error) }
|
||||
let(:user) { FactoryGirl.create(:admin) }
|
||||
before(:each) { allow(controller).to receive(:current_user).and_return(user) }
|
||||
|
||||
describe 'POST #create' do
|
||||
context 'with a valid hint' do
|
||||
let(:request) { Proc.new { post :create, execution_environment_id: execution_environment.id, hint: FactoryGirl.attributes_for(:ruby_syntax_error) } }
|
||||
before(:each) { request.call }
|
||||
|
||||
expect_assigns(execution_environment: :execution_environment)
|
||||
expect_assigns(hint: Hint)
|
||||
|
||||
it 'creates the hint' do
|
||||
expect { request.call }.to change(Hint, :count).by(1)
|
||||
end
|
||||
|
||||
expect_redirect
|
||||
end
|
||||
|
||||
context 'with an invalid hint' do
|
||||
before(:each) { post :create, execution_environment_id: execution_environment.id, hint: {} }
|
||||
|
||||
expect_assigns(execution_environment: :execution_environment)
|
||||
expect_assigns(hint: Hint)
|
||||
expect_status(200)
|
||||
expect_template(:new)
|
||||
end
|
||||
end
|
||||
|
||||
describe 'DELETE #destroy' do
|
||||
before(:each) { delete :destroy, execution_environment_id: execution_environment.id, id: hint.id }
|
||||
|
||||
expect_assigns(execution_environment: :execution_environment)
|
||||
expect_assigns(hint: Hint)
|
||||
|
||||
it 'destroys the hint' do
|
||||
hint = FactoryGirl.create(:ruby_syntax_error)
|
||||
expect { delete :destroy, execution_environment_id: execution_environment.id, id: hint.id }.to change(Hint, :count).by(-1)
|
||||
end
|
||||
|
||||
expect_redirect
|
||||
end
|
||||
|
||||
describe 'GET #edit' do
|
||||
before(:each) { get :edit, execution_environment_id: execution_environment.id, id: hint.id }
|
||||
|
||||
expect_assigns(execution_environment: :execution_environment)
|
||||
expect_assigns(hint: Hint)
|
||||
expect_status(200)
|
||||
expect_template(:edit)
|
||||
end
|
||||
|
||||
describe 'GET #index' do
|
||||
let!(:hints) { FactoryGirl.create_pair(:ruby_syntax_error) }
|
||||
before(:each) { get :index, execution_environment_id: execution_environment.id }
|
||||
|
||||
expect_assigns(execution_environment: :execution_environment)
|
||||
expect_assigns(hints: Hint.all)
|
||||
expect_status(200)
|
||||
expect_template(:index)
|
||||
end
|
||||
|
||||
describe 'GET #new' do
|
||||
before(:each) { get :new, execution_environment_id: execution_environment.id }
|
||||
|
||||
expect_assigns(execution_environment: :execution_environment)
|
||||
expect_assigns(hint: Hint)
|
||||
expect_status(200)
|
||||
expect_template(:new)
|
||||
end
|
||||
|
||||
describe 'GET #show' do
|
||||
before(:each) { get :show, execution_environment_id: execution_environment.id, id: hint.id }
|
||||
|
||||
expect_assigns(execution_environment: :execution_environment)
|
||||
expect_assigns(hint: :hint)
|
||||
expect_status(200)
|
||||
expect_template(:show)
|
||||
end
|
||||
|
||||
describe 'PUT #update' do
|
||||
context 'with a valid hint' do
|
||||
before(:each) { put :update, execution_environment_id: execution_environment.id, hint: FactoryGirl.attributes_for(:ruby_syntax_error), id: hint.id }
|
||||
|
||||
expect_assigns(execution_environment: :execution_environment)
|
||||
expect_assigns(hint: Hint)
|
||||
expect_redirect
|
||||
end
|
||||
|
||||
context 'with an invalid hint' do
|
||||
before(:each) { put :update, execution_environment_id: execution_environment.id, hint: {name: ''}, id: hint.id }
|
||||
|
||||
expect_assigns(execution_environment: :execution_environment)
|
||||
expect_assigns(hint: Hint)
|
||||
expect_status(200)
|
||||
expect_template(:edit)
|
||||
end
|
||||
end
|
||||
end
|
212
spec/controllers/internal_users_controller_spec.rb
Normal file
212
spec/controllers/internal_users_controller_spec.rb
Normal file
@@ -0,0 +1,212 @@
|
||||
require 'rails_helper'
|
||||
|
||||
describe InternalUsersController do
|
||||
let(:user) { FactoryGirl.build(:admin) }
|
||||
let!(:users) { FactoryGirl.create_pair(:teacher) }
|
||||
|
||||
describe 'GET #activate' do
|
||||
let(:user) { InternalUser.create(FactoryGirl.attributes_for(:teacher)) }
|
||||
|
||||
before(:each) do
|
||||
user.send(:setup_activation)
|
||||
user.save(validate: false)
|
||||
end
|
||||
|
||||
context 'without a valid activation token' do
|
||||
before(:each) { get :activate, id: user.id }
|
||||
|
||||
expect_redirect
|
||||
end
|
||||
|
||||
context 'with an already activated user' do
|
||||
before(:each) do
|
||||
user.activate!
|
||||
get :activate, id: user.id, token: user.activation_token
|
||||
end
|
||||
|
||||
expect_redirect
|
||||
end
|
||||
|
||||
context 'with valid preconditions' do
|
||||
before(:each) { get :activate, id: user.id, token: user.activation_token }
|
||||
|
||||
expect_assigns(user: InternalUser)
|
||||
expect_status(200)
|
||||
expect_template(:activate)
|
||||
end
|
||||
end
|
||||
|
||||
describe 'PUT #activate' do
|
||||
let(:user) { InternalUser.create(FactoryGirl.attributes_for(:teacher)) }
|
||||
let(:password) { SecureRandom.hex }
|
||||
|
||||
before(:each) do
|
||||
user.send(:setup_activation)
|
||||
user.save(validate: false)
|
||||
expect(user.activation_token).to be_present
|
||||
end
|
||||
|
||||
context 'without a valid activation token' do
|
||||
before(:each) { put :activate, id: user.id }
|
||||
|
||||
expect_redirect
|
||||
end
|
||||
|
||||
context 'with an already activated user' do
|
||||
before(:each) do
|
||||
user.activate!
|
||||
put :activate, id: user.id, internal_user: {activation_token: user.activation_token, password: password, password_confirmation: password}
|
||||
end
|
||||
|
||||
expect_redirect
|
||||
end
|
||||
|
||||
context 'without a password' do
|
||||
before(:each) { put :activate, id: user.id, internal_user: {activation_token: user.activation_token} }
|
||||
|
||||
expect_assigns(user: InternalUser)
|
||||
|
||||
it 'builds a user with errors' do
|
||||
expect(assigns(:user).errors).to be_present
|
||||
end
|
||||
|
||||
expect_template(:activate)
|
||||
end
|
||||
|
||||
context 'without a valid password confirmation' do
|
||||
before(:each) { put :activate, id: user.id, internal_user: {activation_token: user.activation_token, password: password, password_confirmation: ''} }
|
||||
|
||||
expect_assigns(user: InternalUser)
|
||||
|
||||
it 'builds a user with errors' do
|
||||
expect(assigns(:user).errors).to be_present
|
||||
end
|
||||
|
||||
expect_template(:activate)
|
||||
end
|
||||
|
||||
context 'with valid preconditions' do
|
||||
before(:each) { put :activate, id: user.id, internal_user: {activation_token: user.activation_token, password: password, password_confirmation: password} }
|
||||
|
||||
expect_assigns(user: InternalUser)
|
||||
|
||||
it 'activates the user' do
|
||||
expect(assigns[:user]).to be_activated
|
||||
end
|
||||
|
||||
expect_flash_message(:notice, :'internal_users.activate.success')
|
||||
expect_redirect
|
||||
end
|
||||
end
|
||||
|
||||
describe 'POST #create' do
|
||||
before(:each) { allow(controller).to receive(:current_user).and_return(user) }
|
||||
|
||||
context 'with a valid internal user' do
|
||||
let(:request) { Proc.new { post :create, internal_user: FactoryGirl.attributes_for(:teacher) } }
|
||||
before(:each) { request.call }
|
||||
|
||||
expect_assigns(user: InternalUser)
|
||||
|
||||
it 'creates the internal user' do
|
||||
expect { request.call }.to change(InternalUser, :count).by(1)
|
||||
end
|
||||
|
||||
it 'creates an inactive user' do
|
||||
expect(InternalUser.last).not_to be_activated
|
||||
end
|
||||
|
||||
it 'sets up an activation token' do
|
||||
expect(InternalUser.last.activation_token).to be_present
|
||||
end
|
||||
|
||||
expect_redirect
|
||||
end
|
||||
|
||||
context 'with an invalid internal user' do
|
||||
before(:each) { post :create, internal_user: {} }
|
||||
|
||||
expect_assigns(user: InternalUser)
|
||||
expect_status(200)
|
||||
expect_template(:new)
|
||||
end
|
||||
end
|
||||
|
||||
describe 'DELETE #destroy' do
|
||||
before(:each) do
|
||||
allow(controller).to receive(:current_user).and_return(user)
|
||||
delete :destroy, id: users.first.id
|
||||
end
|
||||
|
||||
expect_assigns(user: InternalUser)
|
||||
|
||||
it 'destroys the internal user' do
|
||||
expect { delete :destroy, id: InternalUser.last.id }.to change(InternalUser, :count).by(-1)
|
||||
end
|
||||
|
||||
expect_redirect(:internal_users)
|
||||
end
|
||||
|
||||
describe 'GET #edit' do
|
||||
before(:each) do
|
||||
allow(controller).to receive(:current_user).and_return(user)
|
||||
get :edit, id: users.first.id
|
||||
end
|
||||
|
||||
expect_assigns(user: InternalUser)
|
||||
expect_status(200)
|
||||
expect_template(:edit)
|
||||
end
|
||||
|
||||
describe 'GET #index' do
|
||||
before(:each) do
|
||||
allow(controller).to receive(:current_user).and_return(user)
|
||||
get :index
|
||||
end
|
||||
|
||||
expect_assigns(users: InternalUser.all)
|
||||
expect_status(200)
|
||||
expect_template(:index)
|
||||
end
|
||||
|
||||
describe 'GET #new' do
|
||||
before(:each) do
|
||||
allow(controller).to receive(:current_user).and_return(user)
|
||||
get :new
|
||||
end
|
||||
|
||||
expect_assigns(user: InternalUser)
|
||||
expect_status(200)
|
||||
expect_template(:new)
|
||||
end
|
||||
|
||||
describe 'GET #show' do
|
||||
before(:each) do
|
||||
allow(controller).to receive(:current_user).and_return(user)
|
||||
get :show, id: users.first.id
|
||||
end
|
||||
|
||||
expect_assigns(user: InternalUser)
|
||||
expect_status(200)
|
||||
expect_template(:show)
|
||||
end
|
||||
|
||||
describe 'PUT #update' do
|
||||
before(:each) { allow(controller).to receive(:current_user).and_return(user) }
|
||||
|
||||
context 'with a valid internal user' do
|
||||
before(:each) { put :update, internal_user: FactoryGirl.attributes_for(:teacher), id: users.first.id }
|
||||
|
||||
expect_assigns(user: InternalUser)
|
||||
expect_redirect
|
||||
end
|
||||
|
||||
context 'with an invalid internal user' do
|
||||
before(:each) { put :update, internal_user: {email: ''}, id: users.first.id }
|
||||
|
||||
expect_assigns(user: InternalUser)
|
||||
expect_status(200)
|
||||
expect_template(:edit)
|
||||
end
|
||||
end
|
||||
end
|
145
spec/controllers/sessions_controller_spec.rb
Normal file
145
spec/controllers/sessions_controller_spec.rb
Normal file
@@ -0,0 +1,145 @@
|
||||
require 'rails_helper'
|
||||
|
||||
describe SessionsController do
|
||||
let(:consumer) { FactoryGirl.create(:consumer) }
|
||||
|
||||
describe 'POST #create' do
|
||||
let(:password) { user_attributes[:password] }
|
||||
let(:user) { InternalUser.create(user_attributes) }
|
||||
let(:user_attributes) { FactoryGirl.attributes_for(:teacher) }
|
||||
|
||||
context 'with valid credentials' do
|
||||
before(:each) do
|
||||
user.activate!
|
||||
post :create, email: user.email, password: password, remember_me: 1
|
||||
end
|
||||
|
||||
expect_flash_message(:notice, :'sessions.create.success')
|
||||
expect_redirect
|
||||
end
|
||||
|
||||
context 'with invalid credentials' do
|
||||
before(:each) { post :create, 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) { FactoryGirl.create(:fibonacci) }
|
||||
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, 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, 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
|
||||
expect_any_instance_of(IMS::LTI::ToolProvider).to receive(:valid_request?).and_return(true)
|
||||
expect(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, 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
|
||||
expect_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, custom_token: '', oauth_consumer_key: consumer.oauth_key, oauth_nonce: nonce, oauth_signature: SecureRandom.hex
|
||||
end
|
||||
end
|
||||
|
||||
context 'with valid launch parameters' do
|
||||
let(:request) { post :create_through_lti, custom_token: exercise.token, oauth_consumer_key: consumer.oauth_key, oauth_nonce: nonce, oauth_signature: SecureRandom.hex, user_id: user.external_id }
|
||||
let(:user) { FactoryGirl.create(:external_user, consumer_id: consumer.id) }
|
||||
before(:each) { expect_any_instance_of(IMS::LTI::ToolProvider).to receive(:valid_request?).and_return(true) }
|
||||
|
||||
it 'assigns the current user' do
|
||||
request
|
||||
expect(assigns(:current_user)).to be_an(ExternalUser)
|
||||
expect(session[:external_user_id]).to eq(user.id)
|
||||
end
|
||||
|
||||
it 'assigns the exercise' do
|
||||
request
|
||||
expect(assigns(:exercise)).to eq(exercise)
|
||||
end
|
||||
|
||||
it 'stores LTI parameters in the session' do
|
||||
expect(controller).to receive(:store_lti_session_data)
|
||||
request
|
||||
end
|
||||
|
||||
it 'stores the OAuth nonce' do
|
||||
expect(controller).to receive(:store_nonce).with(nonce)
|
||||
request
|
||||
end
|
||||
|
||||
context 'when LTI outcomes are supported' do
|
||||
before(:each) do
|
||||
expect(controller).to receive(:lti_outcome_service?).and_return(true)
|
||||
request
|
||||
end
|
||||
|
||||
it 'displays a flash message' do
|
||||
expect(flash[:notice]).to eq(I18n.t('sessions.create_through_lti.session_with_outcome', consumer: consumer))
|
||||
end
|
||||
end
|
||||
|
||||
context 'when LTI outcomes are not supported' do
|
||||
before(:each) do
|
||||
expect(controller).to receive(:lti_outcome_service?).and_return(false)
|
||||
request
|
||||
end
|
||||
|
||||
it 'displays a flash message' do
|
||||
expect(flash[:notice]).to eq(I18n.t('sessions.create_through_lti.session_without_outcome', consumer: consumer))
|
||||
end
|
||||
end
|
||||
|
||||
it 'redirects to the requested exercise' do
|
||||
request
|
||||
expect(controller).to redirect_to(implement_exercise_path(exercise.id))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'GET #destroy_through_lti' do
|
||||
let(:request) { Proc.new { get :destroy_through_lti, consumer_id: consumer.id, submission_id: submission.id } }
|
||||
let(:submission) { FactoryGirl.create(:submission) }
|
||||
|
||||
before(:each) do
|
||||
session[:consumer_id] = consumer.id
|
||||
session[:lti_parameters] = {}
|
||||
end
|
||||
|
||||
before(:each) { request.call }
|
||||
|
||||
it 'clears the session' do
|
||||
expect(controller).to receive(:clear_lti_session_data)
|
||||
request.call
|
||||
end
|
||||
|
||||
expect_status(200)
|
||||
expect_template(:destroy_through_lti)
|
||||
end
|
||||
end
|
171
spec/controllers/submissions_controller_spec.rb
Normal file
171
spec/controllers/submissions_controller_spec.rb
Normal file
@@ -0,0 +1,171 @@
|
||||
require 'rails_helper'
|
||||
|
||||
describe SubmissionsController do
|
||||
let(:submission) { FactoryGirl.create(:submission) }
|
||||
let(:user) { FactoryGirl.create(:admin) }
|
||||
before(:each) { allow(controller).to receive(:current_user).and_return(user) }
|
||||
|
||||
describe 'POST #create' do
|
||||
before(:each) do
|
||||
controller.request.accept = 'application/json'
|
||||
end
|
||||
|
||||
context 'with a valid submission' do
|
||||
let(:exercise) { FactoryGirl.create(:hello_world) }
|
||||
let(:request) { Proc.new { post :create, format: :json, submission: FactoryGirl.attributes_for(:submission, exercise_id: exercise.id) } }
|
||||
before(:each) { request.call }
|
||||
|
||||
expect_assigns(submission: Submission)
|
||||
|
||||
it 'creates the submission' do
|
||||
expect { request.call }.to change(Submission, :count).by(1)
|
||||
end
|
||||
|
||||
expect_content_type('application/json')
|
||||
expect_status(201)
|
||||
end
|
||||
|
||||
context 'with an invalid submission' do
|
||||
before(:each) { post :create, submission: {} }
|
||||
|
||||
expect_assigns(submission: Submission)
|
||||
expect_content_type('application/json')
|
||||
expect_status(422)
|
||||
end
|
||||
end
|
||||
|
||||
describe 'GET #download_file' do
|
||||
context 'with an invalid filename' do
|
||||
before(:each) { get :download_file, filename: SecureRandom.hex, id: submission.id }
|
||||
|
||||
expect_status(404)
|
||||
end
|
||||
|
||||
context 'with a valid filename' do
|
||||
let(:file) { submission.files.first }
|
||||
before(:each) { get :download_file, filename: file.name_with_extension, id: submission.id }
|
||||
|
||||
expect_assigns(file: :file)
|
||||
expect_assigns(submission: :submission)
|
||||
expect_content_type('application/octet-stream')
|
||||
expect_status(200)
|
||||
|
||||
it 'sets the correct filename' do
|
||||
expect(response.headers['Content-Disposition']).to eq("attachment; filename=\"#{file.name_with_extension}\"")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'GET #index' do
|
||||
let!(:submissions) { FactoryGirl.create_pair(:submission) }
|
||||
before(:each) { get :index }
|
||||
|
||||
expect_assigns(submissions: Submission.all)
|
||||
expect_status(200)
|
||||
expect_template(:index)
|
||||
end
|
||||
|
||||
describe 'GET #render_file' do
|
||||
context 'with an invalid filename' do
|
||||
before(:each) { get :render_file, filename: SecureRandom.hex, id: submission.id }
|
||||
|
||||
expect_status(404)
|
||||
end
|
||||
|
||||
context 'with a valid filename' do
|
||||
let(:file) { submission.files.first }
|
||||
let(:request) { Proc.new { get :render_file, filename: file.name_with_extension, id: submission.id } }
|
||||
before(:each) { request.call }
|
||||
|
||||
expect_assigns(file: :file)
|
||||
expect_assigns(submission: :submission)
|
||||
expect_status(200)
|
||||
|
||||
it 'renders the file content' do
|
||||
expect(response.body).to eq(file.content)
|
||||
end
|
||||
|
||||
it 'sets the correct MIME type' do
|
||||
mime_type = Mime::Type.lookup_by_extension('css')
|
||||
expect(Mime::Type).to receive(:lookup_by_extension).at_least(:once).and_return(mime_type)
|
||||
request.call
|
||||
expect(response.headers['Content-Type']).to eq(mime_type.to_s)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'GET #run' do
|
||||
let(:filename) { submission.collect_files.detect(&:main_file?).name_with_extension }
|
||||
|
||||
before(:each) do
|
||||
expect_any_instance_of(ActionController::Live::SSE).to receive(:write).at_least(3).times
|
||||
end
|
||||
|
||||
context 'when no errors occur during execution' do
|
||||
before(:each) do
|
||||
expect_any_instance_of(DockerClient).to receive(:execute_run_command).with(submission, filename).and_return({})
|
||||
get :run, filename: filename, id: submission.id
|
||||
end
|
||||
|
||||
expect_assigns(docker_client: DockerClient)
|
||||
expect_assigns(server_sent_event: ActionController::Live::SSE)
|
||||
expect_assigns(submission: :submission)
|
||||
expect_content_type('text/event-stream')
|
||||
expect_status(200)
|
||||
end
|
||||
|
||||
context 'when an error occurs during execution' do
|
||||
let(:hint) { "Your object 'main' of class 'Object' does not understand the method 'foo'." }
|
||||
let(:stderr) { "undefined method `foo' for main:Object (NoMethodError)" }
|
||||
|
||||
before(:each) do
|
||||
expect_any_instance_of(DockerClient).to receive(:execute_run_command).with(submission, filename).and_yield(:stderr, stderr)
|
||||
end
|
||||
|
||||
after(:each) { get :run, filename: filename, id: submission.id }
|
||||
|
||||
context 'when the error is covered by a hint' do
|
||||
before(:each) do
|
||||
expect_any_instance_of(Whistleblower).to receive(:generate_hint).with(stderr).and_return(hint)
|
||||
end
|
||||
|
||||
it 'does not store the error' do
|
||||
expect(Error).not_to receive(:create)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when the error is not covered by a hint' do
|
||||
before(:each) do
|
||||
expect_any_instance_of(Whistleblower).to receive(:generate_hint).with(stderr)
|
||||
end
|
||||
|
||||
it 'stores the error' do
|
||||
expect(Error).to receive(:create).with(execution_environment_id: submission.exercise.execution_environment_id, message: stderr)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'GET #show' do
|
||||
before(:each) { get :show, id: submission.id }
|
||||
|
||||
expect_assigns(submission: :submission)
|
||||
expect_status(200)
|
||||
expect_template(:show)
|
||||
end
|
||||
|
||||
describe 'GET #test' do
|
||||
let(:filename) { submission.collect_files.detect(&:teacher_defined_test?).name_with_extension }
|
||||
let(:output) { {} }
|
||||
|
||||
before(:each) do
|
||||
expect_any_instance_of(DockerClient).to receive(:execute_test_command).with(submission, filename)
|
||||
get :test, filename: filename, id: submission.id
|
||||
end
|
||||
|
||||
expect_assigns(docker_client: DockerClient)
|
||||
expect_assigns(submission: :submission)
|
||||
expect_content_type('application/json')
|
||||
expect_status(200)
|
||||
end
|
||||
end
|
Reference in New Issue
Block a user