summaryrefslogtreecommitdiff
path: root/spec/requests/lfs_locks_api_spec.rb
blob: 34e345cb1cfef3b1b2685bf48b5a95b5f820769d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
# frozen_string_literal: true

require 'spec_helper'

RSpec.describe 'Git LFS File Locking API' do
  include WorkhorseHelpers

  let(:project) { create(:project) }
  let(:maintainer) { create(:user) }
  let(:developer) { create(:user) }
  let(:guest)     { create(:user) }
  let(:path)      { 'README.md' }
  let(:headers) do
    {
      'Authorization' => authorization
    }.compact
  end

  shared_examples 'unauthorized request' do
    context 'when user is not authorized' do
      let(:authorization) { authorize_user(guest) }

      it 'returns a forbidden 403 response' do
        post_lfs_json url, body, headers

        expect(response).to have_gitlab_http_status(:forbidden)
      end
    end
  end

  before do
    allow(Gitlab.config.lfs).to receive(:enabled).and_return(true)

    project.add_developer(maintainer)
    project.add_developer(developer)
    project.add_guest(guest)
  end

  describe 'Create File Lock endpoint' do
    let(:url)           { "#{project.http_url_to_repo}/info/lfs/locks" }
    let(:authorization) { authorize_user(developer) }
    let(:body)          { { path: path } }

    include_examples 'unauthorized request'

    context 'with an existent lock' do
      before do
        lock_file('README.md', developer)
      end

      it 'return an error message' do
        post_lfs_json url, body, headers

        expect(response).to have_gitlab_http_status(:conflict)

        expect(json_response.keys).to match_array(%w(lock message documentation_url))
        expect(json_response['message']).to match(/already locked/)
      end

      it 'returns the existen lock' do
        post_lfs_json url, body, headers

        expect(json_response['lock']['path']).to eq('README.md')
      end
    end

    context 'without an existent lock' do
      it 'creates the lock' do
        post_lfs_json url, body, headers

        expect(response).to have_gitlab_http_status(:created)

        expect(json_response['lock'].keys).to match_array(%w(id path locked_at owner))
      end
    end
  end

  describe 'Listing File Locks endpoint' do
    let(:url)           { "#{project.http_url_to_repo}/info/lfs/locks" }
    let(:authorization) { authorize_user(developer) }

    include_examples 'unauthorized request'

    it 'returns the list of locked files' do
      lock_file('README.md', developer)
      lock_file('README', developer)

      do_get url, nil, headers

      expect(response).to have_gitlab_http_status(:ok)

      expect(json_response['locks'].size).to eq(2)
      expect(json_response['locks'].first.keys).to match_array(%w(id path locked_at owner))
    end
  end

  describe 'List File Locks for verification endpoint' do
    let(:url)           { "#{project.http_url_to_repo}/info/lfs/locks/verify" }
    let(:authorization) { authorize_user(developer) }

    include_examples 'unauthorized request'

    it 'returns the list of locked files grouped by owner' do
      lock_file('README.md', maintainer)
      lock_file('README', developer)

      post_lfs_json url, nil, headers

      expect(response).to have_gitlab_http_status(:ok)

      expect(json_response['ours'].size).to eq(1)
      expect(json_response['ours'].first['path']).to eq('README')
      expect(json_response['theirs'].size).to eq(1)
      expect(json_response['theirs'].first['path']).to eq('README.md')
    end
  end

  describe 'Delete File Lock endpoint' do
    let!(:lock)         { lock_file('README.md', developer) }
    let(:url)           { "#{project.http_url_to_repo}/info/lfs/locks/#{lock[:id]}/unlock" }
    let(:authorization) { authorize_user(developer) }

    include_examples 'unauthorized request'

    context 'with an existent lock' do
      it 'deletes the lock' do
        post_lfs_json url, nil, headers

        expect(response).to have_gitlab_http_status(:ok)
      end

      it 'returns the deleted lock' do
        post_lfs_json url, nil, headers

        expect(json_response['lock'].keys).to match_array(%w(id path locked_at owner))
      end

      context 'when a maintainer uses force' do
        let(:authorization) { authorize_user(maintainer) }

        it 'deletes the lock' do
          project.add_maintainer(maintainer)
          post_lfs_json url, { force: true }, headers

          expect(response).to have_gitlab_http_status(:ok)
        end
      end
    end
  end

  def lock_file(path, author)
    result = Lfs::LockFileService.new(project, author, { path: path }).execute

    result[:lock]
  end

  def authorize_user(user)
    ActionController::HttpAuthentication::Basic.encode_credentials(user.username, user.password)
  end

  def post_lfs_json(url, body = nil, headers = nil)
    post(url, params: body.try(:to_json), headers: (headers || {}).merge('Content-Type' => LfsRequest::CONTENT_TYPE))
  end

  def do_get(url, params = nil, headers = nil)
    get(url, params: (params || {}), headers: (headers || {}).merge('Content-Type' => LfsRequest::CONTENT_TYPE))
  end
end