Merge pull request #939 from openHPI/refactor_proforma_import_export

Refactor Proforma Import/Export
This commit is contained in:
Sebastian Serth
2022-10-26 17:58:48 +02:00
committed by GitHub
20 changed files with 315 additions and 120 deletions

View File

@ -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}"} }

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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)