BigW Consortium Gitlab

files_spec.rb 12 KB
Newer Older
1 2
require 'spec_helper'

3
describe API::Files do
4
  let(:user) { create(:user) }
5 6
  let!(:project) { create(:project, :repository, namespace: user.namespace ) }
  let(:guest) { create(:user) { |u| project.add_guest(u) } }
7
  let(:file_path) { "files%2Fruby%2Fpopen%2Erb" }
8 9 10 11 12
  let(:params) do
    {
      ref: 'master'
    }
  end
13 14
  let(:author_email) { 'user@example.org' }
  let(:author_name) { 'John Doe' }
15

16 17 18
  before do
    project.team << [user, :developer]
  end
19

20 21 22
  def route(file_path = nil)
    "/projects/#{project.id}/repository/files/#{file_path}"
  end
23

24
  describe "GET /projects/:id/repository/files/:file_path" do
25
    shared_examples_for 'repository files' do
26 27
      it 'returns file attributes as json' do
        get api(route(file_path), current_user), params
28 29

        expect(response).to have_http_status(200)
30
        expect(json_response['file_path']).to eq(CGI.unescape(file_path))
31 32 33 34
        expect(json_response['file_name']).to eq('popen.rb')
        expect(json_response['last_commit_id']).to eq('570e7b2abdd848b95f2f578043fc23bd6f6fd24d')
        expect(Base64.decode64(json_response['content']).lines.first).to eq("require 'fileutils'\n")
      end
35

36 37 38 39 40 41 42 43 44
      it 'returns json when file has txt extension' do
        file_path = "bar%2Fbranch-test.txt"

        get api(route(file_path), current_user), params

        expect(response).to have_http_status(200)
        expect(response.content_type).to eq('application/json')
      end

45 46 47 48 49 50 51 52 53 54 55 56
      it 'returns file by commit sha' do
        # This file is deleted on HEAD
        file_path = "files%2Fjs%2Fcommit%2Ejs%2Ecoffee"
        params[:ref] = "6f6d7e7ed97bb5f0054f2b1df789b39ca89b6ff9"

        get api(route(file_path), current_user), params

        expect(response).to have_http_status(200)
        expect(json_response['file_name']).to eq('commit.js.coffee')
        expect(Base64.decode64(json_response['content']).lines.first).to eq("class Commit\n")
      end

57 58 59 60 61 62 63 64 65 66
      it 'returns raw file info' do
        url = route(file_path) + "/raw"
        expect(Gitlab::Workhorse).to receive(:send_git_blob)

        get api(url, current_user), params

        expect(response).to have_http_status(200)
      end

      context 'when mandatory params are not given' do
67
        it_behaves_like '400 response' do
68
          let(:request) { get api(route("any%2Ffile"), current_user) }
69 70 71 72
        end
      end

      context 'when file_path does not exist' do
73
        let(:params) { { ref: 'master' } }
74 75

        it_behaves_like '404 response' do
76
          let(:request) { get api(route('app%2Fmodels%2Fapplication%2Erb'), current_user), params }
77 78 79 80 81 82 83 84
          let(:message) { '404 File Not Found' }
        end
      end

      context 'when repository is disabled' do
        include_context 'disabled repository'

        it_behaves_like '403 response' do
85
          let(:request) { get api(route(file_path), current_user), params }
86 87
        end
      end
88 89
    end

90
    context 'when unauthenticated', 'and project is public' do
91
      it_behaves_like 'repository files' do
92
        let(:project) { create(:project, :public, :repository) }
93 94 95
        let(:current_user) { nil }
      end
    end
96

97 98
    context 'when unauthenticated', 'and project is private' do
      it_behaves_like '404 response' do
99
        let(:request) { get api(route(file_path)), params }
100
        let(:message) { '404 Project Not Found' }
101
      end
102 103
    end

104 105 106 107
    context 'when authenticated', 'as a developer' do
      it_behaves_like 'repository files' do
        let(:current_user) { user }
      end
108 109
    end

110 111
    context 'when authenticated', 'as a guest' do
      it_behaves_like '403 response' do
112
        let(:request) { get api(route(file_path), guest), params }
113
      end
114 115
    end
  end
116

117 118 119 120 121 122 123 124 125 126 127
  describe "GET /projects/:id/repository/files/:file_path/raw" do
    shared_examples_for 'repository raw files' do
      it 'returns raw file info' do
        url = route(file_path) + "/raw"
        expect(Gitlab::Workhorse).to receive(:send_git_blob)

        get api(url, current_user), params

        expect(response).to have_http_status(200)
      end

128 129 130 131 132 133 134 135 136 137 138
      it 'returns file by commit sha' do
        # This file is deleted on HEAD
        file_path = "files%2Fjs%2Fcommit%2Ejs%2Ecoffee"
        params[:ref] = "6f6d7e7ed97bb5f0054f2b1df789b39ca89b6ff9"
        expect(Gitlab::Workhorse).to receive(:send_git_blob)

        get api(route(file_path) + "/raw", current_user), params

        expect(response).to have_http_status(200)
      end

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
      context 'when mandatory params are not given' do
        it_behaves_like '400 response' do
          let(:request) { get api(route("any%2Ffile"), current_user) }
        end
      end

      context 'when file_path does not exist' do
        let(:params) { { ref: 'master' } }

        it_behaves_like '404 response' do
          let(:request) { get api(route('app%2Fmodels%2Fapplication%2Erb'), current_user), params }
          let(:message) { '404 File Not Found' }
        end
      end

      context 'when repository is disabled' do
        include_context 'disabled repository'

        it_behaves_like '403 response' do
          let(:request) { get api(route(file_path), current_user), params }
        end
      end
    end

    context 'when unauthenticated', 'and project is public' do
      it_behaves_like 'repository raw files' do
165
        let(:project) { create(:project, :public, :repository) }
166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191
        let(:current_user) { nil }
      end
    end

    context 'when unauthenticated', 'and project is private' do
      it_behaves_like '404 response' do
        let(:request) { get api(route(file_path)), params }
        let(:message) { '404 Project Not Found' }
      end
    end

    context 'when authenticated', 'as a developer' do
      it_behaves_like 'repository raw files' do
        let(:current_user) { user }
      end
    end

    context 'when authenticated', 'as a guest' do
      it_behaves_like '403 response' do
        let(:request) { get api(route(file_path), guest), params }
      end
    end
  end

  describe "POST /projects/:id/repository/files/:file_path" do
    let!(:file_path) { "new_subfolder%2Fnewfile%2Erb" }
192
    let(:valid_params) do
193
      {
194 195 196
        branch: "master",
        content: "puts 8",
        commit_message: "Added newfile"
197
      }
198
    end
199

200
    it "creates a new file in project repo" do
201
      post api(route(file_path), user), valid_params
202

203
      expect(response).to have_http_status(201)
204
      expect(json_response["file_path"]).to eq(CGI.unescape(file_path))
205 206 207
      last_commit = project.repository.commit.raw
      expect(last_commit.author_email).to eq(user.email)
      expect(last_commit.author_name).to eq(user.name)
208 209
    end

210 211
    it "returns a 400 bad request if no mandatory params given" do
      post api(route("any%2Etxt"), user)
212

213
      expect(response).to have_http_status(400)
214 215
    end

216
    it "returns a 400 if editor fails to create file" do
217 218
      allow_any_instance_of(Repository).to receive(:create_file)
        .and_raise(Repository::CommitError, 'Cannot create file')
219

220
      post api(route("any%2Etxt"), user), valid_params
221

222
      expect(response).to have_http_status(400)
223
    end
224 225 226 227 228

    context "when specifying an author" do
      it "creates a new file with the specified author" do
        valid_params.merge!(author_email: author_email, author_name: author_name)

229
        post api(route("new_file_with_author%2Etxt"), user), valid_params
230 231

        expect(response).to have_http_status(201)
232
        expect(response.content_type).to eq('application/json')
233 234 235 236 237
        last_commit = project.repository.commit.raw
        expect(last_commit.author_email).to eq(author_email)
        expect(last_commit.author_name).to eq(author_name)
      end
    end
238 239 240 241 242

    context 'when the repo is empty' do
      let!(:project) { create(:project_empty_repo, namespace: user.namespace ) }

      it "creates a new file in project repo" do
243
        post api(route("newfile%2Erb"), user), valid_params
244 245 246 247 248 249 250 251

        expect(response).to have_http_status(201)
        expect(json_response['file_path']).to eq('newfile.rb')
        last_commit = project.repository.commit.raw
        expect(last_commit.author_email).to eq(user.email)
        expect(last_commit.author_name).to eq(user.name)
      end
    end
252 253
  end

254
  describe "PUT /projects/:id/repository/files" do
255
    let(:valid_params) do
256
      {
257
        branch: 'master',
258 259 260
        content: 'puts 8',
        commit_message: 'Changed file'
      }
261
    end
262

263
    it "updates existing file in project repo" do
264
      put api(route(file_path), user), valid_params
265

266
      expect(response).to have_http_status(200)
267
      expect(json_response['file_path']).to eq(CGI.unescape(file_path))
268 269 270
      last_commit = project.repository.commit.raw
      expect(last_commit.author_email).to eq(user.email)
      expect(last_commit.author_name).to eq(user.name)
271 272
    end

273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291
    it "returns a 400 bad request if update existing file with stale last commit id" do
      params_with_stale_id = valid_params.merge(last_commit_id: 'stale')

      put api(route(file_path), user), params_with_stale_id

      expect(response).to have_http_status(400)
      expect(json_response['message']).to eq('You are attempting to update a file that has changed since you started editing it.')
    end

    it "updates existing file in project repo with accepts correct last commit id" do
      last_commit = Gitlab::Git::Commit
                        .last_for_path(project.repository, 'master', URI.unescape(file_path))
      params_with_correct_id = valid_params.merge(last_commit_id: last_commit.id)

      put api(route(file_path), user), params_with_correct_id

      expect(response).to have_http_status(200)
    end

292
    it "returns a 400 bad request if no params given" do
293
      put api(route(file_path), user)
294

295
      expect(response).to have_http_status(400)
296
    end
297 298 299 300 301

    context "when specifying an author" do
      it "updates a file with the specified author" do
        valid_params.merge!(author_email: author_email, author_name: author_name, content: "New content")

302
        put api(route(file_path), user), valid_params
303 304 305 306 307 308 309

        expect(response).to have_http_status(200)
        last_commit = project.repository.commit.raw
        expect(last_commit.author_email).to eq(author_email)
        expect(last_commit.author_name).to eq(author_name)
      end
    end
310
  end
311 312

  describe "DELETE /projects/:id/repository/files" do
313
    let(:valid_params) do
314
      {
315
        branch: 'master',
316 317
        commit_message: 'Changed file'
      }
318
    end
319

320
    it "deletes existing file in project repo" do
321
      delete api(route(file_path), user), valid_params
322

323
      expect(response).to have_http_status(204)
324 325
    end

326
    it "returns a 400 bad request if no params given" do
327
      delete api(route(file_path), user)
328

329
      expect(response).to have_http_status(400)
330 331
    end

332 333
    it "returns a 400 if fails to delete file" do
      allow_any_instance_of(Repository).to receive(:delete_file).and_raise(Repository::CommitError, 'Cannot delete file')
334

335
      delete api(route(file_path), user), valid_params
336

337
      expect(response).to have_http_status(400)
338
    end
339 340 341 342 343

    context "when specifying an author" do
      it "removes a file with the specified author" do
        valid_params.merge!(author_email: author_email, author_name: author_name)

344
        delete api(route(file_path), user), valid_params
345

346
        expect(response).to have_http_status(204)
347 348
      end
    end
349
  end
350 351

  describe "POST /projects/:id/repository/files with binary file" do
352
    let(:file_path) { 'test%2Ebin' }
353 354
    let(:put_params) do
      {
355
        branch: 'master',
356 357 358 359 360 361 362
        content: 'iVBORw0KGgoAAAANSUhEUgAAAAEAAAABAQMAAAAl21bKAAAAA1BMVEUAAACnej3aAAAAAXRSTlMAQObYZgAAAApJREFUCNdjYAAAAAIAAeIhvDMAAAAASUVORK5CYII=',
        commit_message: 'Binary file with a \n should not be touched',
        encoding: 'base64'
      }
    end
    let(:get_params) do
      {
363
        ref: 'master'
364 365 366 367
      }
    end

    before do
368
      post api(route(file_path), user), put_params
369 370 371
    end

    it "remains unchanged" do
372
      get api(route(file_path), user), get_params
373

374
      expect(response).to have_http_status(200)
375 376
      expect(json_response['file_path']).to eq(CGI.unescape(file_path))
      expect(json_response['file_name']).to eq(CGI.unescape(file_path))
377 378 379
      expect(json_response['content']).to eq(put_params[:content])
    end
  end
380
end