diff options
Diffstat (limited to 'spec/requests/api/terraform/state_spec.rb')
-rw-r--r-- | spec/requests/api/terraform/state_spec.rb | 238 |
1 files changed, 187 insertions, 51 deletions
diff --git a/spec/requests/api/terraform/state_spec.rb b/spec/requests/api/terraform/state_spec.rb index b0a963db684..88c277f4e08 100644 --- a/spec/requests/api/terraform/state_spec.rb +++ b/spec/requests/api/terraform/state_spec.rb @@ -3,95 +3,231 @@ require 'spec_helper' describe API::Terraform::State do - def auth_header_for(user) - auth_header = ActionController::HttpAuthentication::Basic.encode_credentials( - user.username, - create(:personal_access_token, user: user).token - ) - { 'HTTP_AUTHORIZATION' => auth_header } - end + let_it_be(:project) { create(:project) } + let_it_be(:developer) { create(:user, developer_projects: [project]) } + let_it_be(:maintainer) { create(:user, maintainer_projects: [project]) } + + let!(:state) { create(:terraform_state, :with_file, project: project) } - let!(:project) { create(:project) } - let(:developer) { create(:user) } - let(:maintainer) { create(:user) } - let(:state_name) { 'state' } + let(:current_user) { maintainer } + let(:auth_header) { basic_auth_header(current_user) } + let(:project_id) { project.id } + let(:state_name) { state.name } + let(:state_path) { "/projects/#{project_id}/terraform/state/#{state_name}" } before do - project.add_maintainer(maintainer) + stub_terraform_state_object_storage(Terraform::StateUploader) end describe 'GET /projects/:id/terraform/state/:name' do - it 'returns 401 if user is not authenticated' do - headers = { 'HTTP_AUTHORIZATION' => 'failing_token' } - get api("/projects/#{project.id}/terraform/state/#{state_name}"), headers: headers + subject(:request) { get api(state_path), headers: auth_header } - expect(response).to have_gitlab_http_status(:unauthorized) - end + context 'without authentication' do + let(:auth_header) { basic_auth_header('failing_token') } - it 'returns terraform state belonging to a project of given state name' do - get api("/projects/#{project.id}/terraform/state/#{state_name}"), headers: auth_header_for(maintainer) + it 'returns 401 if user is not authenticated' do + request - expect(response).to have_gitlab_http_status(:not_implemented) - expect(response.body).to eq('not implemented') + expect(response).to have_gitlab_http_status(:unauthorized) + end end - it 'returns not found if the project does not exists' do - get api("/projects/0000/terraform/state/#{state_name}"), headers: auth_header_for(maintainer) + context 'with maintainer permissions' do + let(:current_user) { maintainer } + + it 'returns terraform state belonging to a project of given state name' do + request + + expect(response).to have_gitlab_http_status(:ok) + expect(response.body).to eq(state.file.read) + end + + context 'for a project that does not exist' do + let(:project_id) { '0000' } + + it 'returns not found' do + request - expect(response).to have_gitlab_http_status(:not_found) + expect(response).to have_gitlab_http_status(:not_found) + end + end end - it 'returns forbidden if the user cannot access the state' do - project.add_developer(developer) - get api("/projects/#{project.id}/terraform/state/#{state_name}"), headers: auth_header_for(developer) + context 'with developer permissions' do + let(:current_user) { developer } + + it 'returns forbidden if the user cannot access the state' do + request - expect(response).to have_gitlab_http_status(:forbidden) + expect(response).to have_gitlab_http_status(:forbidden) + end end end describe 'POST /projects/:id/terraform/state/:name' do + let(:params) { { 'instance': 'example-instance' } } + + subject(:request) { post api(state_path), headers: auth_header, as: :json, params: params } + context 'when terraform state with a given name is already present' do - it 'updates the state' do - post api("/projects/#{project.id}/terraform/state/#{state_name}"), - params: '{ "instance": "example-instance" }', - headers: { 'Content-Type' => 'text/plain' }.merge(auth_header_for(maintainer)) + context 'with maintainer permissions' do + let(:current_user) { maintainer } - expect(response).to have_gitlab_http_status(:not_implemented) - expect(response.body).to eq('not implemented') + it 'updates the state' do + expect { request }.to change { Terraform::State.count }.by(0) + + expect(response).to have_gitlab_http_status(:ok) + end end - it 'returns forbidden if the user cannot access the state' do - project.add_developer(developer) - get api("/projects/#{project.id}/terraform/state/#{state_name}"), headers: auth_header_for(developer) + context 'without body' do + let(:params) { nil } - expect(response).to have_gitlab_http_status(:forbidden) + it 'returns no content if no body is provided' do + request + + expect(response).to have_gitlab_http_status(:no_content) + end + end + + context 'with developer permissions' do + let(:current_user) { developer } + + it 'returns forbidden' do + request + + expect(response).to have_gitlab_http_status(:forbidden) + end end end context 'when there is no terraform state of a given name' do - it 'creates a new state' do - post api("/projects/#{project.id}/terraform/state/example2"), - headers: auth_header_for(maintainer), - params: '{ "database": "example-database" }' + let(:state_name) { 'example2' } + + context 'with maintainer permissions' do + let(:current_user) { maintainer } + + it 'creates a new state' do + expect { request }.to change { Terraform::State.count }.by(1) + + expect(response).to have_gitlab_http_status(:ok) + end + end + + context 'without body' do + let(:params) { nil } + + it 'returns no content if no body is provided' do + request - expect(response).to have_gitlab_http_status(:not_implemented) - expect(response.body).to eq('not implemented') + expect(response).to have_gitlab_http_status(:no_content) + end + end + + context 'with developer permissions' do + let(:current_user) { developer } + + it 'returns forbidden' do + request + + expect(response).to have_gitlab_http_status(:forbidden) + end end end end describe 'DELETE /projects/:id/terraform/state/:name' do - it 'deletes the state' do - delete api("/projects/#{project.id}/terraform/state/#{state_name}"), headers: auth_header_for(maintainer) + subject(:request) { delete api(state_path), headers: auth_header } + + context 'with maintainer permissions' do + let(:current_user) { maintainer } + + it 'deletes the state' do + expect { request }.to change { Terraform::State.count }.by(-1) - expect(response).to have_gitlab_http_status(:not_implemented) + expect(response).to have_gitlab_http_status(:ok) + end + end + + context 'with developer permissions' do + let(:current_user) { developer } + + it 'returns forbidden' do + expect { request }.to change { Terraform::State.count }.by(0) + + expect(response).to have_gitlab_http_status(:forbidden) + end + end + end + + describe 'PUT /projects/:id/terraform/state/:name/lock' do + let(:params) do + { + ID: '123-456', + Version: '0.1', + Operation: 'OperationTypePlan', + Info: '', + Who: "#{current_user.username}", + Created: Time.now.utc.iso8601(6), + Path: '' + } + end + + subject(:request) { post api("#{state_path}/lock"), headers: auth_header, params: params } + + it 'locks the terraform state' do + request + + expect(response).to have_gitlab_http_status(:ok) end + end + + describe 'DELETE /projects/:id/terraform/state/:name/lock' do + before do + state.lock_xid = '123-456' + state.save! + end + + subject(:request) { delete api("#{state_path}/lock"), headers: auth_header, params: params } - it 'returns forbidden if the user cannot access the state' do - project.add_developer(developer) - get api("/projects/#{project.id}/terraform/state/#{state_name}"), headers: auth_header_for(developer) + context 'with the correct lock id' do + let(:params) { { ID: '123-456' } } - expect(response).to have_gitlab_http_status(:forbidden) + it 'removes the terraform state lock' do + request + + expect(response).to have_gitlab_http_status(:ok) + end + end + + context 'with no lock id (force-unlock)' do + let(:params) { {} } + + it 'removes the terraform state lock' do + request + + expect(response).to have_gitlab_http_status(:ok) + end + end + + context 'with an incorrect lock id' do + let(:params) { { ID: '456-789' } } + + it 'returns an error' do + request + + expect(response).to have_gitlab_http_status(:conflict) + end + end + + context 'with a longer than 255 character lock id' do + let(:params) { { ID: '0' * 256 } } + + it 'returns an error' do + request + + expect(response).to have_gitlab_http_status(:bad_request) + end end end end |