Skip to content

Commit

Permalink
fix: cloning functionality
Browse files Browse the repository at this point in the history
* correctly clone actors and numbered configs
* attach correct actors and numbered configs to vms
* fix tag handling
* add basic specs
  • Loading branch information
mromulus committed Jan 24, 2024
1 parent 34275a7 commit 3e2302c
Show file tree
Hide file tree
Showing 2 changed files with 123 additions and 9 deletions.
61 changes: 52 additions & 9 deletions app/calculation/clone_environment.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand All @@ -16,6 +20,7 @@ def result
cloned_environment.save!
cloned_environment.reload

clone_actors(source_environment.actors.arrange)
clone_capabilities
clone_networks
clone_vms
Expand All @@ -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(
Expand All @@ -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(
Expand All @@ -64,14 +96,15 @@ 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
)

combined_data = {
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))
Expand Down Expand Up @@ -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
71 changes: 71 additions & 0 deletions spec/calculations/clone_environment_spec.rb
Original file line number Diff line number Diff line change
@@ -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

0 comments on commit 3e2302c

Please sign in to comment.