Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'spec/services/terraform/remote_state_handler_spec.rb')
-rw-r--r--spec/services/terraform/remote_state_handler_spec.rb143
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