Merge pull request #939 from openHPI/refactor_proforma_import_export
Refactor Proforma Import/Export
This commit is contained in:
@ -403,7 +403,7 @@ describe ExercisesController do
|
||||
|
||||
let(:post_request) { post :export_external_check, params: {id: exercise.id} }
|
||||
let!(:codeharbor_link) { create(:codeharbor_link, user: user) }
|
||||
let(:external_check_hash) { {message: message, exercise_found: true, update_right: update_right, error: error} }
|
||||
let(:external_check_hash) { {message: message, uuid_found: true, update_right: update_right, error: error} }
|
||||
let(:message) { 'message' }
|
||||
let(:update_right) { true }
|
||||
let(:error) { nil }
|
||||
@ -452,7 +452,7 @@ describe ExercisesController do
|
||||
end
|
||||
end
|
||||
|
||||
describe '#export_external_confirm' do
|
||||
describe 'POST #export_external_confirm' do
|
||||
render_views
|
||||
|
||||
let!(:codeharbor_link) { create(:codeharbor_link, user: user) }
|
||||
@ -489,7 +489,7 @@ describe ExercisesController do
|
||||
end
|
||||
end
|
||||
|
||||
describe '#import_uuid_check' do
|
||||
describe 'POST #import_uuid_check' do
|
||||
let(:exercise) { create(:dummy, uuid: SecureRandom.uuid) }
|
||||
let!(:codeharbor_link) { create(:codeharbor_link, user: user) }
|
||||
let(:uuid) { exercise.reload.uuid }
|
||||
@ -502,7 +502,7 @@ describe ExercisesController do
|
||||
post_request
|
||||
expect(response).to have_http_status(:success)
|
||||
|
||||
expect(JSON.parse(response.body).symbolize_keys[:exercise_found]).to be true
|
||||
expect(JSON.parse(response.body).symbolize_keys[:uuid_found]).to be true
|
||||
expect(JSON.parse(response.body).symbolize_keys[:update_right]).to be true
|
||||
end
|
||||
|
||||
@ -522,7 +522,7 @@ describe ExercisesController do
|
||||
post_request
|
||||
expect(response).to have_http_status(:success)
|
||||
|
||||
expect(JSON.parse(response.body).symbolize_keys[:exercise_found]).to be true
|
||||
expect(JSON.parse(response.body).symbolize_keys[:uuid_found]).to be true
|
||||
expect(JSON.parse(response.body).symbolize_keys[:update_right]).to be false
|
||||
end
|
||||
end
|
||||
@ -534,15 +534,15 @@ describe ExercisesController do
|
||||
post_request
|
||||
expect(response).to have_http_status(:success)
|
||||
|
||||
expect(JSON.parse(response.body).symbolize_keys[:exercise_found]).to be false
|
||||
expect(JSON.parse(response.body).symbolize_keys[:uuid_found]).to be false
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'POST #import_exercise' do
|
||||
describe 'POST #import_task' do
|
||||
let(:codeharbor_link) { create(:codeharbor_link, user: user) }
|
||||
let!(:imported_exercise) { create(:fibonacci) }
|
||||
let(:post_request) { post :import_exercise, body: zip_file_content }
|
||||
let(:post_request) { post :import_task, body: zip_file_content }
|
||||
let(:zip_file_content) { 'zipped task xml' }
|
||||
let(:headers) { {'Authorization' => "Bearer #{codeharbor_link.api_key}"} }
|
||||
|
||||
|
@ -42,25 +42,25 @@ describe ExerciseService::CheckExternal do
|
||||
end
|
||||
|
||||
context 'when response contains a JSON with expected keys' do
|
||||
let(:response) { {exercise_found: true, update_right: true}.to_json }
|
||||
let(:response) { {uuid_found: true, update_right: true}.to_json }
|
||||
|
||||
it 'returns the correct hash' do
|
||||
expect(check_external_service).to eql(error: false, message: I18n.t('exercises.export_codeharbor.check.exercise_found'), exercise_found: true, update_right: true)
|
||||
expect(check_external_service).to eql(error: false, message: I18n.t('exercises.export_codeharbor.check.task_found'), uuid_found: true, update_right: true)
|
||||
end
|
||||
|
||||
context 'with exercise_found: false and no update_right' do
|
||||
let(:response) { {exercise_found: false}.to_json }
|
||||
context 'with uuid_found: false and no update_right' do
|
||||
let(:response) { {uuid_found: false}.to_json }
|
||||
|
||||
it 'returns the correct hash' do
|
||||
expect(check_external_service).to eql(error: false, message: I18n.t('exercises.export_codeharbor.check.no_exercise'), exercise_found: false)
|
||||
expect(check_external_service).to eql(error: false, message: I18n.t('exercises.export_codeharbor.check.no_task'), uuid_found: false)
|
||||
end
|
||||
end
|
||||
|
||||
context 'with exercise_found: true and update_right: false' do
|
||||
let(:response) { {exercise_found: true, update_right: false}.to_json }
|
||||
context 'with uuid_found: true and update_right: false' do
|
||||
let(:response) { {uuid_found: true, update_right: false}.to_json }
|
||||
|
||||
it 'returns the correct hash' do
|
||||
expect(check_external_service).to eql(error: false, message: I18n.t('exercises.export_codeharbor.check.exercise_found_no_right'), exercise_found: true, update_right: false)
|
||||
expect(check_external_service).to eql(error: false, message: I18n.t('exercises.export_codeharbor.check.task_found_no_right'), uuid_found: true, update_right: false)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -19,31 +19,44 @@ RSpec.describe ProformaService::ConvertExerciseToTask do
|
||||
let(:convert_to_task) { described_class.new(exercise: exercise) }
|
||||
let(:exercise) do
|
||||
create(:dummy,
|
||||
execution_environment: execution_environment,
|
||||
instructions: 'instruction',
|
||||
uuid: SecureRandom.uuid,
|
||||
files: files + tests)
|
||||
end
|
||||
let(:files) { [] }
|
||||
let(:tests) { [] }
|
||||
let(:execution_environment) { create(:java) }
|
||||
|
||||
it 'creates a task with all basic attributes' do
|
||||
expect(task).to have_attributes(
|
||||
title: exercise.title,
|
||||
description: exercise.description,
|
||||
internal_description: exercise.instructions,
|
||||
# proglang: {
|
||||
# name: exercise.execution_environment.language,
|
||||
# version: exercise.execution_environment.version
|
||||
# },
|
||||
uuid: exercise.uuid,
|
||||
language: described_class::DEFAULT_LANGUAGE,
|
||||
# parent_uuid: exercise.clone_relations.first&.origin&.uuid,
|
||||
meta_data: {
|
||||
CodeOcean: {
|
||||
allow_auto_completion: exercise.allow_auto_completion,
|
||||
allow_file_creation: exercise.allow_file_creation,
|
||||
execution_environment_id: exercise.execution_environment_id,
|
||||
expected_difficulty: exercise.expected_difficulty,
|
||||
hide_file_tree: exercise.hide_file_tree,
|
||||
public: exercise.public,
|
||||
files: {},
|
||||
},
|
||||
},
|
||||
files: [],
|
||||
tests: [],
|
||||
model_solutions: []
|
||||
)
|
||||
end
|
||||
|
||||
context 'when exercise has execution_environment with correct docker-image name' do
|
||||
it 'creates a task with the correct proglang attribute' do
|
||||
expect(task).to have_attributes(proglang: {name: 'java', version: '8'})
|
||||
end
|
||||
end
|
||||
|
||||
context 'when exercise has a mainfile' do
|
||||
let(:files) { [file] }
|
||||
let(:file) { build(:file) }
|
||||
@ -57,7 +70,15 @@ RSpec.describe ProformaService::ConvertExerciseToTask do
|
||||
usage_by_lms: 'edit',
|
||||
visible: 'yes',
|
||||
binary: false,
|
||||
internal_description: 'main_file'
|
||||
internal_description: nil
|
||||
)
|
||||
end
|
||||
|
||||
it 'adds the file\'s role to the file hash in task-meta_data' do
|
||||
expect(task).to have_attributes(
|
||||
meta_data: {
|
||||
CodeOcean: a_hash_including(files: {"CO-#{file.id}" => {role: 'main_file'}}),
|
||||
}
|
||||
)
|
||||
end
|
||||
end
|
||||
@ -77,7 +98,15 @@ RSpec.describe ProformaService::ConvertExerciseToTask do
|
||||
usage_by_lms: 'display',
|
||||
visible: 'no',
|
||||
binary: false,
|
||||
internal_description: 'regular_file'
|
||||
internal_description: nil
|
||||
)
|
||||
end
|
||||
|
||||
it 'adds the file\'s role to the file hash in task-meta_data' do
|
||||
expect(task).to have_attributes(
|
||||
meta_data: {
|
||||
CodeOcean: a_hash_including(files: {"CO-#{file.id}" => {role: 'regular_file'}}),
|
||||
}
|
||||
)
|
||||
end
|
||||
|
||||
@ -134,7 +163,7 @@ RSpec.describe ProformaService::ConvertExerciseToTask do
|
||||
usage_by_lms: 'display',
|
||||
visible: 'yes',
|
||||
binary: false,
|
||||
internal_description: 'reference_implementation'
|
||||
internal_description: nil
|
||||
)
|
||||
end
|
||||
end
|
||||
@ -161,7 +190,12 @@ RSpec.describe ProformaService::ConvertExerciseToTask do
|
||||
id: test_file.id,
|
||||
title: test_file.name,
|
||||
files: have(1).item,
|
||||
meta_data: {'feedback-message' => test_file.feedback_message}
|
||||
meta_data: {
|
||||
CodeOcean: {
|
||||
'feedback-message': 'feedback_message',
|
||||
weight: test_file.weight,
|
||||
},
|
||||
}
|
||||
)
|
||||
end
|
||||
|
||||
@ -173,7 +207,7 @@ RSpec.describe ProformaService::ConvertExerciseToTask do
|
||||
used_by_grader: true,
|
||||
visible: 'no',
|
||||
binary: false,
|
||||
internal_description: 'teacher_defined_test'
|
||||
internal_description: nil
|
||||
)
|
||||
end
|
||||
|
||||
|
@ -3,8 +3,6 @@
|
||||
require 'rails_helper'
|
||||
|
||||
describe ProformaService::ConvertTaskToExercise do
|
||||
# TODO: Add teacher_defined_linter for tests
|
||||
|
||||
describe '.new' do
|
||||
subject(:convert_to_exercise_service) { described_class.new(task: task, user: user, exercise: exercise) }
|
||||
|
||||
@ -34,35 +32,94 @@ describe ProformaService::ConvertTaskToExercise do
|
||||
Proforma::Task.new(
|
||||
title: 'title',
|
||||
description: 'description',
|
||||
internal_description: 'internal_description',
|
||||
proglang: {name: 'proglang-name', version: 'proglang-version'},
|
||||
proglang: {name: 'python', version: '3.4'},
|
||||
uuid: 'uuid',
|
||||
parent_uuid: 'parent_uuid',
|
||||
language: 'language',
|
||||
meta_data: meta_data,
|
||||
model_solutions: model_solutions,
|
||||
files: files,
|
||||
tests: tests
|
||||
)
|
||||
end
|
||||
let(:user) { create(:teacher) }
|
||||
|
||||
let(:files) { [] }
|
||||
let(:tests) { [] }
|
||||
let(:model_solutions) { [] }
|
||||
let(:exercise) { nil }
|
||||
|
||||
let(:meta_data) { {} }
|
||||
let(:public) { 'true' }
|
||||
let(:hide_file_tree) { 'true' }
|
||||
let(:allow_file_creation) { 'true' }
|
||||
let(:allow_auto_completion) { 'true' }
|
||||
let(:expected_difficulty) { 7 }
|
||||
let!(:execution_environment) { create(:java) }
|
||||
|
||||
it 'creates an exercise with the correct attributes' do
|
||||
expect(convert_to_exercise_service).to have_attributes(
|
||||
title: 'title',
|
||||
description: 'description',
|
||||
instructions: 'internal_description',
|
||||
execution_environment: be_blank,
|
||||
uuid: be_blank,
|
||||
unpublished: true,
|
||||
user: user,
|
||||
files: be_empty
|
||||
files: be_empty,
|
||||
public: false,
|
||||
hide_file_tree: false,
|
||||
allow_file_creation: false,
|
||||
allow_auto_completion: false,
|
||||
expected_difficulty: 1,
|
||||
execution_environment_id: nil
|
||||
)
|
||||
end
|
||||
|
||||
it { is_expected.to be_valid }
|
||||
|
||||
context 'when meta_data is set' do
|
||||
let(:meta_data) do
|
||||
{
|
||||
CodeOcean: {
|
||||
public: public,
|
||||
hide_file_tree: hide_file_tree,
|
||||
allow_file_creation: allow_file_creation,
|
||||
allow_auto_completion: allow_auto_completion,
|
||||
expected_difficulty: expected_difficulty,
|
||||
execution_environment_id: execution_environment&.id,
|
||||
files: files_meta_data,
|
||||
},
|
||||
}
|
||||
end
|
||||
let(:files_meta_data) { {} }
|
||||
|
||||
it 'creates an exercise with the correct attributes' do
|
||||
expect(convert_to_exercise_service).to have_attributes(
|
||||
title: 'title',
|
||||
description: 'description',
|
||||
uuid: be_blank,
|
||||
unpublished: true,
|
||||
user: user,
|
||||
files: be_empty,
|
||||
public: true,
|
||||
hide_file_tree: true,
|
||||
allow_file_creation: true,
|
||||
allow_auto_completion: true,
|
||||
expected_difficulty: 7,
|
||||
execution_environment_id: execution_environment.id
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when execution environment is not set in meta_data' do
|
||||
let(:execution_environment) { nil }
|
||||
|
||||
before { create(:python) }
|
||||
|
||||
it 'sets the execution_environment based on proglang name and value' do
|
||||
expect(convert_to_exercise_service).to have_attributes(execution_environment: have_attributes(name: 'Python 3.4'))
|
||||
end
|
||||
end
|
||||
|
||||
context 'when task has a file' do
|
||||
let(:files) { [file] }
|
||||
let(:file) do
|
||||
@ -74,7 +131,6 @@ describe ProformaService::ConvertTaskToExercise do
|
||||
visible: 'yes',
|
||||
usage_by_lms: usage_by_lms,
|
||||
binary: binary,
|
||||
internal_description: 'regular_file',
|
||||
mimetype: mimetype
|
||||
)
|
||||
end
|
||||
@ -101,6 +157,21 @@ describe ProformaService::ConvertTaskToExercise do
|
||||
expect { convert_to_exercise_service.save! }.to change(Exercise, :count).by(1)
|
||||
end
|
||||
|
||||
context 'when file is a main_file' do
|
||||
let(:meta_data) do
|
||||
{
|
||||
CodeOcean: {
|
||||
files: files_meta_data,
|
||||
},
|
||||
}
|
||||
end
|
||||
let(:files_meta_data) { {"CO-#{file.id}".to_sym => {role: 'main_file'}} }
|
||||
|
||||
it 'creates an exercise with a file that has the correct attributes' do
|
||||
expect(convert_to_exercise_service.files.first).to have_attributes(role: 'main_file')
|
||||
end
|
||||
end
|
||||
|
||||
context 'when path is folder/' do
|
||||
let(:path) { 'folder/' }
|
||||
|
||||
@ -150,7 +221,7 @@ describe ProformaService::ConvertTaskToExercise do
|
||||
end
|
||||
end
|
||||
|
||||
context 'when file has an unkown file_type' do
|
||||
context 'when file has an unknown file_type' do
|
||||
let(:filename) { 'unknown_file_type.asdf' }
|
||||
|
||||
it 'creates a new Exercise on save' do
|
||||
@ -180,8 +251,7 @@ describe ProformaService::ConvertTaskToExercise do
|
||||
used_by_grader: 'used_by_grader',
|
||||
visible: 'yes',
|
||||
usage_by_lms: 'display',
|
||||
binary: false,
|
||||
internal_description: 'reference_implementation'
|
||||
binary: false
|
||||
)
|
||||
end
|
||||
|
||||
@ -226,13 +296,14 @@ describe ProformaService::ConvertTaskToExercise do
|
||||
id: 'test-id',
|
||||
title: 'title',
|
||||
description: 'description',
|
||||
internal_description: 'internal_description',
|
||||
test_type: 'test_type',
|
||||
files: test_files,
|
||||
meta_data: {
|
||||
'feedback-message' => 'feedback-message',
|
||||
'testing-framework' => 'testing-framework',
|
||||
'testing-framework-version' => 'testing-framework-version',
|
||||
CodeOcean: {
|
||||
'feedback-message': 'feedback-message',
|
||||
'testing-framework': 'testing-framework',
|
||||
'testing-framework-version': 'testing-framework-version',
|
||||
},
|
||||
}
|
||||
)
|
||||
end
|
||||
@ -267,15 +338,32 @@ describe ProformaService::ConvertTaskToExercise do
|
||||
)
|
||||
end
|
||||
|
||||
context 'when test file is a teacher_defined_linter' do
|
||||
let(:meta_data) do
|
||||
{
|
||||
CodeOcean: {
|
||||
files: files_meta_data,
|
||||
},
|
||||
}
|
||||
end
|
||||
let(:files_meta_data) { {"CO-#{test_file.id}".to_sym => {role: 'teacher_defined_linter'}} }
|
||||
|
||||
it 'creates an exercise with a test' do
|
||||
expect(convert_to_exercise_service.files.select {|file| file.role == 'teacher_defined_linter' }).to have(1).item
|
||||
end
|
||||
end
|
||||
|
||||
context 'when task has multiple tests' do
|
||||
let(:tests) { [test, test2] }
|
||||
let(:test2) do
|
||||
Proforma::Test.new(
|
||||
files: test_files2,
|
||||
meta_data: {
|
||||
'feedback-message' => 'feedback-message',
|
||||
'testing-framework' => 'testing-framework',
|
||||
'testing-framework-version' => 'testing-framework-version',
|
||||
CodeOcean: {
|
||||
'feedback-message': 'feedback-message',
|
||||
'testing-framework': 'testing-framework',
|
||||
'testing-framework-version': 'testing-framework-version',
|
||||
},
|
||||
}
|
||||
)
|
||||
end
|
||||
@ -304,8 +392,7 @@ describe ProformaService::ConvertTaskToExercise do
|
||||
create(
|
||||
:files,
|
||||
title: 'exercise-title',
|
||||
description: 'exercise-description',
|
||||
instructions: 'exercise-instruction'
|
||||
description: 'exercise-description'
|
||||
)
|
||||
end
|
||||
|
||||
@ -315,9 +402,8 @@ describe ProformaService::ConvertTaskToExercise do
|
||||
convert_to_exercise_service.save
|
||||
expect(exercise.reload).to have_attributes(
|
||||
id: exercise.id,
|
||||
title: task.title,
|
||||
description: task.description,
|
||||
instructions: task.internal_description,
|
||||
title: exercise.title,
|
||||
description: exercise.description,
|
||||
execution_environment: exercise.execution_environment,
|
||||
uuid: exercise.uuid,
|
||||
user: exercise.user,
|
||||
@ -339,8 +425,7 @@ describe ProformaService::ConvertTaskToExercise do
|
||||
used_by_grader: 'used_by_grader',
|
||||
visible: 'yes',
|
||||
usage_by_lms: 'display',
|
||||
binary: false,
|
||||
internal_description: 'regular_file'
|
||||
binary: false
|
||||
)
|
||||
end
|
||||
let(:tests) { [test] }
|
||||
@ -349,13 +434,14 @@ describe ProformaService::ConvertTaskToExercise do
|
||||
id: 'test-id',
|
||||
title: 'title',
|
||||
description: 'description',
|
||||
internal_description: 'regular_file',
|
||||
test_type: 'test_type',
|
||||
files: test_files,
|
||||
meta_data: {
|
||||
'feedback-message' => 'feedback-message',
|
||||
'testing-framework' => 'testing-framework',
|
||||
'testing-framework-version' => 'testing-framework-version',
|
||||
CodeOcean: {
|
||||
'feedback-message': 'feedback-message',
|
||||
'testing-framework': 'testing-framework',
|
||||
'testing-framework-version': 'testing-framework-version',
|
||||
},
|
||||
}
|
||||
)
|
||||
end
|
||||
|
@ -30,7 +30,7 @@ describe ProformaService::ExportTask do
|
||||
|
||||
before do
|
||||
allow(ProformaService::ConvertExerciseToTask).to receive(:call).with(exercise: exercise).and_return(task)
|
||||
allow(Proforma::Exporter).to receive(:new).with(task).and_return(exporter)
|
||||
allow(Proforma::Exporter).to receive(:new).with(task: task, custom_namespaces: [{prefix: 'CodeOcean', uri: 'codeocean.openhpi.de'}]).and_return(exporter)
|
||||
end
|
||||
|
||||
it do
|
||||
|
@ -29,6 +29,10 @@ describe ProformaService::Import do
|
||||
instructions: 'instruction',
|
||||
execution_environment: execution_environment,
|
||||
files: files + tests,
|
||||
hide_file_tree: true,
|
||||
allow_file_creation: false,
|
||||
allow_auto_completion: true,
|
||||
expected_difficulty: 7,
|
||||
uuid: uuid,
|
||||
user: user)
|
||||
end
|
||||
|
@ -31,7 +31,11 @@ RSpec::Matchers.define :be_an_equal_exercise_as do |exercise|
|
||||
return true if object == other # for []
|
||||
return false if object.length != other.length
|
||||
|
||||
object.to_a.product(other.to_a).map {|k, v| equal?(k, v) }.any?
|
||||
object.map do |element|
|
||||
other.map {|other_element| equal?(element, other_element) }.any?
|
||||
end.all? && other.map do |element|
|
||||
object.map {|other_element| equal?(element, other_element) }.any?
|
||||
end.all?
|
||||
end
|
||||
|
||||
def attributes_and_associations(object)
|
||||
|
Reference in New Issue
Block a user