diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2020-04-20 18:38:24 +0000 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2020-04-20 18:38:24 +0000 |
commit | 983a0bba5d2a042c4a3bbb22432ec192c7501d82 (patch) | |
tree | b153cd387c14ba23bd5a07514c7c01fddf6a78a0 /spec/services/terraform | |
parent | a2bddee2cdb38673df0e004d5b32d9f77797de64 (diff) | |
download | gitlab-ce-983a0bba5d2a042c4a3bbb22432ec192c7501d82.tar.gz |
Add latest changes from gitlab-org/gitlab@12-10-stable-ee
Diffstat (limited to 'spec/services/terraform')
-rw-r--r-- | spec/services/terraform/remote_state_handler_spec.rb | 143 |
1 files changed, 143 insertions, 0 deletions
diff --git a/spec/services/terraform/remote_state_handler_spec.rb b/spec/services/terraform/remote_state_handler_spec.rb new file mode 100644 index 00000000000..f4e1831b2e8 --- /dev/null +++ b/spec/services/terraform/remote_state_handler_spec.rb @@ -0,0 +1,143 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe Terraform::RemoteStateHandler do + let_it_be(:project) { create(:project) } + let_it_be(:user) { create(:user) } + + describe '#find_with_lock' do + context 'without a state name' do + subject { described_class.new(project, user) } + + it 'raises an exception' do + expect { subject.find_with_lock }.to raise_error(ArgumentError) + end + end + + context 'with a state name' do + subject { described_class.new(project, user, name: 'state') } + + context 'with no matching state' do + it 'raises an exception' do + expect { subject.find_with_lock }.to raise_error(ActiveRecord::RecordNotFound) + end + end + + context 'with a matching state' do + let!(:state) { create(:terraform_state, project: project, name: 'state') } + + it 'returns the state' do + expect(subject.find_with_lock).to eq(state) + end + end + end + end + + describe '#create_or_find!' do + it 'requires passing a state name' do + handler = described_class.new(project, user) + + expect { handler.create_or_find! }.to raise_error(ArgumentError) + end + + it 'allows to create states with same name in different projects' do + project_b = create(:project) + + state_a = described_class.new(project, user, name: 'my-state').create_or_find! + state_b = described_class.new(project_b, user, name: 'my-state').create_or_find! + + expect(state_a).to be_persisted + expect(state_b).to be_persisted + expect(state_a.id).not_to eq state_b.id + end + + it 'loads the same state upon subsequent call in the project scope' do + state_a = described_class.new(project, user, name: 'my-state').create_or_find! + state_b = described_class.new(project, user, name: 'my-state').create_or_find! + + expect(state_a).to be_persisted + expect(state_a.id).to eq state_b.id + end + end + + context 'when state locking is not being used' do + subject { described_class.new(project, user, name: 'my-state') } + + describe '#handle_with_lock' do + it 'allows to modify a state using database locking' do + state = subject.handle_with_lock do |state| + state.name = 'updated-name' + end + + expect(state.name).to eq 'updated-name' + end + + it 'returns the state object itself' do + state = subject.create_or_find! + + expect(state.name).to eq 'my-state' + end + end + + describe '#lock!' do + it 'raises an error' do + expect { subject.lock! }.to raise_error(ArgumentError) + end + end + end + + context 'when using locking' do + describe '#handle_with_lock' do + it 'handles a locked state using exclusive read lock' do + handler = described_class + .new(project, user, name: 'new-state', lock_id: 'abc-abc') + + handler.lock! + + state = handler.handle_with_lock do |state| + state.name = 'new-name' + end + + expect(state.name).to eq 'new-name' + end + end + + it 'raises exception if lock has not been acquired before' do + handler = described_class + .new(project, user, name: 'new-state', lock_id: 'abc-abc') + + expect { handler.handle_with_lock } + .to raise_error(described_class::StateLockedError) + end + + describe '#lock!' do + it 'allows to lock state if it does not exist yet' do + handler = described_class.new(project, user, name: 'new-state', lock_id: 'abc-abc') + + state = handler.lock! + + expect(state).to be_persisted + expect(state.name).to eq 'new-state' + end + + it 'allows to lock state if it exists and is not locked' do + state = described_class.new(project, user, name: 'new-state').create_or_find! + handler = described_class.new(project, user, name: 'new-state', lock_id: 'abc-abc') + + handler.lock! + + expect(state.reload.lock_xid).to eq 'abc-abc' + expect(state).to be_locked + end + + it 'raises an exception when trying to unlocked state locked by someone else' do + described_class.new(project, user, name: 'new-state', lock_id: 'abc-abc').lock! + + handler = described_class.new(project, user, name: 'new-state', lock_id: '12a-23f') + + expect { handler.lock! }.to raise_error(described_class::StateLockedError) + end + end + end +end |