diff --git a/app/calculation/clone_environment.rb b/app/calculation/clone_environment.rb index 65111eb8..3c68fc50 100644 --- a/app/calculation/clone_environment.rb +++ b/app/calculation/clone_environment.rb @@ -2,10 +2,14 @@ class CloneEnvironment < Patterns::Calculation def cloned_environment - @cloned_environment ||= source_environment.dup.tap do |dest| - dest.name = options[:name] - dest.abbreviation = options[:abbreviation] - dest.slug = nil + @cloned_environment ||= begin + Exercise.insert( + source_environment.attributes.merge( + name: options[:name], + abbreviation: options[:abbreviation], + slug: nil + ).except('id') + ).then { Exercise.find(_1.to_a.dig(0, 'id')) } end end @@ -16,6 +20,7 @@ def result cloned_environment.save! cloned_environment.reload + clone_actors(source_environment.actors.arrange) clone_capabilities clone_networks clone_vms @@ -30,6 +35,27 @@ def source_environment @source_environment ||= Exercise.find(subject) end + def clone_actors(tree, parent: nil) + tree.each do |node, children| + new_actor = cloned_environment.actors.create( + node.attributes.merge( + 'exercise_id' => cloned_environment.id, + 'created_at' => Time.now, + 'updated_at' => Time.now, + 'parent' => parent + ).except('id') + ) + + ActorNumberConfig.insert_all( + node.actor_number_configs.map do |config| + config.attributes.merge('actor_id' => new_actor.id).except('id') + end + ) + + clone_actors(children, parent: new_actor) + end + end + def clone_networks source_environment.networks.each do |network| new_network = Network.insert( @@ -47,7 +73,13 @@ def clone_networks def clone_vms source_environment.virtual_machines.includes(network_interfaces: [:addresses]).each do |vm| new_vm = VirtualMachine.insert( - vm.attributes.merge('exercise_id' => cloned_environment.id, 'created_at' => Time.now, 'updated_at' => Time.now).except('id') + vm.attributes.merge( + 'exercise_id' => cloned_environment.id, + 'actor_id' => find_actor_in_cloned_environment(vm.actor).id, + 'numbered_by_id' => find_numerable_in_cloned_environment(vm.numbered_by)&.id, + 'created_at' => Time.now, + 'updated_at' => Time.now + ).except('id') ) clone_customization_specs( @@ -64,7 +96,7 @@ def clone_vms def clone_customization_specs(current_specs:, vm_id:) new_specs = CustomizationSpec.insert_all( current_specs.map do |spec| - spec.attributes.merge('virtual_machine_id' => vm_id).except('id') + spec.attributes.merge('virtual_machine_id' => vm_id).except('id', 'tag_list') end ) @@ -72,6 +104,7 @@ def clone_customization_specs(current_specs:, vm_id:) capabilities: [] } current_specs.zip(new_specs).each_with_object(combined_data) do |(spec, new_spec), memo| + CustomizationSpec.find(new_spec['id']).update(tag_list: spec.tag_list.join(', ')) cloned_environment .capabilities .where(slug: spec.capabilities.pluck(:slug)) @@ -201,14 +234,24 @@ def find_network_in_cloned_environment(source_net) end def find_actor_in_cloned_environment(source_actor) - cloned_environment.actors.detect { |actor| actor.abbreviation == source_actor.abbreviation } + cloned_environment.actors.detect { _1.abbreviation == source_actor.abbreviation } end def find_spec_in_cloned_environment(source_spec) - cloned_environment.customization_specs.detect { |spec| spec.slug == source_spec.slug } + cloned_environment.customization_specs.detect { _1.slug == source_spec.slug } end def find_capability_in_cloned_environment(source_cap) - cloned_environment.capabilities.detect { |cap| cap.abbreviation == source_cap.abbreviation } + cloned_environment.capabilities.detect { _1.abbreviation == source_cap.abbreviation } + end + + def find_numerable_in_cloned_environment(source_numberable) + case source_numberable + when Actor + find_actor_in_cloned_environment(source_numberable) + when ActorNumberConfig + find_actor_in_cloned_environment(source_numberable.actor) + .actor_number_configs.detect { _1.name == source_numberable.name } + end end end diff --git a/spec/calculations/clone_environment_spec.rb b/spec/calculations/clone_environment_spec.rb new file mode 100644 index 00000000..2771b885 --- /dev/null +++ b/spec/calculations/clone_environment_spec.rb @@ -0,0 +1,71 @@ +# frozen_string_literal: true + +require 'rails_helper' + +RSpec.describe CloneEnvironment do + subject { described_class.result_for(source.id, options) } + let(:source) { create(:exercise) } + let!(:actor1) { create(:actor, exercise: source, name: 'Root') } + let!(:actor2) { create(:actor, exercise: source, name: 'Child', parent: actor1) } + let!(:actor3) { create(:actor, exercise: source, name: 'Child2', parent: actor2) } + let(:options) { + { + name: 'Cloned exercise', + abbreviation: 'CE' + } + } + + context 'cloning' do + it { is_expected.to be_a(Exercise) } + + it 'should have new attributes' do + expect(subject.name).to eq options[:name] + expect(subject.abbreviation).to eq options[:abbreviation] + end + end + + context 'actors' do + it 'should have cloned the actor with children' do + new_root = subject.actors.detect { _1.name == actor1.name } + new_child = subject.actors.detect { _1.name == actor2.name } + new_sub_child = subject.actors.detect { _1.name == actor3.name } + expect(new_root).to_not be_nil + expect(new_child).to_not be_nil + expect(new_child.parent).to eq(new_root) + expect(new_sub_child).to_not be_nil + expect(new_sub_child.parent).to eq(new_child) + end + + context 'with numbered configuration' do + let!(:actor1) { create(:actor, :numbered, exercise: source, name: 'Root') } + let!(:config) { create(:actor_number_config, actor: actor1, matcher: [1], config_map: { hello: 'world' }) } + + it 'should clone numbered config' do + new_root = subject.actors.detect { _1.name == actor1.name } + + expect(new_root.number).to eq actor1.number + expect(new_root.actor_number_configs).to_not be_empty + new_config = new_root.actor_number_configs.first + expect(new_config.matcher).to eq config.matcher + expect(new_config.config_map).to eq config.config_map + end + end + end + + context 'vm' do + context 'numberable' do + let!(:actor1) { create(:actor, :numbered, exercise: source, name: 'Root') } + let!(:config) { create(:actor_number_config, actor: actor1, matcher: [1], config_map: { hello: 'world' }) } + let!(:source_vm) { create(:virtual_machine, exercise: source, actor: actor2, numbered_by: config) } + + it 'should clone vm with correct numberable config and actor' do + new_vm = subject.virtual_machines.reload.find_by(name: source_vm.name) + + expect(new_vm.actor.name).to eq source_vm.actor.name + expect(new_vm.numbered_by).to be_a(ActorNumberConfig) + expect(new_vm.numbered_by.actor.name).to eq actor1.name + expect(new_vm.numbered_by.name).to eq config.name + end + end + end +end