mirror_spec.rb 8.17 KB
Newer Older
1 2
require 'spec_helper'

3
feature 'Project mirror', :js do
4 5
  include ReactiveCachingHelpers

6
  let(:project) { create(:project, :mirror, :import_finished, :repository, creator: user, name: 'Victorialand') }
7 8
  let(:user) { create(:user) }

9
  describe 'On a project' do
10
    before do
11
      project.add_master(user)
12
      sign_in user
13 14
    end

15 16 17 18 19 20
    context 'unlicensed' do
      before do
        stub_licensed_features(repository_mirrors: false)
      end

      it 'returns 404' do
21 22 23
        reqs = inspect_requests do
          visit project_mirror_path(project)
        end
24

25
        expect(reqs.first.status_code).to eq(404)
26 27 28
      end
    end

29 30
    context 'with Update now button' do
      let(:timestamp) { Time.now }
31

32 33
      before do
        project.mirror_data.update_attributes(next_execution_timestamp: timestamp + 10.minutes)
34
      end
35

36 37 38 39 40 41 42
      context 'when able to force update' do
        it 'forces import' do
          project.update_attributes(mirror_last_update_at: timestamp - 8.minutes)

          expect_any_instance_of(EE::Project).to receive(:force_import_job!)

          Timecop.freeze(timestamp) do
43
            visit project_mirror_path(project)
44 45
          end

46 47 48 49 50 51 52 53 54 55 56
          Sidekiq::Testing.fake! { click_link('Update Now') }
        end
      end

      context 'when unable to force update' do
        it 'does not force import' do
          project.update_attributes(mirror_last_update_at: timestamp - 3.minutes)

          expect_any_instance_of(EE::Project).not_to receive(:force_import_job!)

          Timecop.freeze(timestamp) do
57
            visit project_mirror_path(project)
58
          end
59 60 61

          expect(page).to have_content('Update Now')
          expect(page).to have_selector('.btn.disabled')
62 63 64
        end
      end
    end
65
  end
66 67 68 69 70 71 72 73 74 75 76 77 78

  describe 'configuration' do
    # Start from a project with no mirroring set up
    let(:project) { create(:project, :repository, creator: user) }
    let(:import_data) { project.import_data(true) }

    before do
      project.add_master(user)
      sign_in(user)
    end

    describe 'password authentication' do
      it 'can be set up' do
79 80
        visit project_settings_repository_path(project)

81 82 83 84 85 86 87 88 89 90 91 92 93 94
        page.within('.project-mirror-settings') do
          check 'Mirror repository'
          fill_in 'Git repository URL', with: 'http://user@example.com'
          fill_in 'Password', with: 'foo'
          click_without_sidekiq 'Save changes'
        end

        expect(page).to have_content('Mirroring settings were successfully updated')

        project.reload
        expect(project.mirror?).to be_truthy
        expect(import_data.auth_method).to eq('password')
        expect(project.import_url).to eq('http://user:foo@example.com')
      end
95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112

      it 'can be changed to unauthenticated' do
        project.update!(import_url: 'http://user:password@example.com')

        visit project_settings_repository_path(project)

        page.within('.project-mirror-settings') do
          fill_in 'Git repository URL', with: 'http://2.example.com'
          fill_in 'Password', with: ''
          click_without_sidekiq 'Save changes'
        end

        expect(page).to have_content('Mirroring settings were successfully updated')

        project.reload
        expect(import_data.auth_method).to eq('password')
        expect(project.import_url).to eq('http://2.example.com')
      end
113 114 115 116
    end

    describe 'SSH public key authentication' do
      it 'can be set up' do
117 118
        visit project_settings_repository_path(project)

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
        page.within('.project-mirror-settings') do
          check 'Mirror repository'
          fill_in 'Git repository URL', with: 'ssh://user@example.com'
          select 'SSH public key authentication', from: 'Authentication method'

          # Generates an SSH public key with an asynchronous PUT and displays it
          wait_for_requests

          expect(import_data.ssh_public_key).not_to be_nil
          expect(page).to have_content(import_data.ssh_public_key)

          click_without_sidekiq 'Save changes'
        end

        # We didn't set any host keys
        expect(page).to have_content('Mirroring settings were successfully updated')
        expect(page).not_to have_content('Verified by')

        project.reload
        import_data.reload
        expect(project.mirror?).to be_truthy
        expect(project.username_only_import_url).to eq('ssh://user@example.com')
        expect(import_data.auth_method).to eq('ssh_public_key')
        expect(import_data.password).to be_blank

        first_key = import_data.ssh_public_key
        expect(page).to have_content(first_key)

        # Check regenerating the public key works
148
        accept_confirm { click_without_sidekiq 'Regenerate key' }
149 150 151 152 153 154 155
        wait_for_requests

        expect(page).not_to have_content(first_key)
        expect(page).to have_content(import_data.reload.ssh_public_key)
      end
    end

156
    describe 'host key management', :use_clean_rails_memory_store_caching do
157
      let(:key) { Gitlab::SSHPublicKey.new(SSHKeygen.generate) }
158 159 160
      let(:cache) { SshHostKey.new(project: project, url: "ssh://example.com:22") }

      it 'fills fingerprints and host keys when detecting' do
161
        stub_reactive_cache(cache, known_hosts: key.key_text)
162

163 164
        visit project_settings_repository_path(project)

165 166 167 168 169 170 171 172 173
        page.within('.project-mirror-settings') do
          fill_in 'Git repository URL', with: 'ssh://example.com'
          click_on 'Detect host keys'
          wait_for_requests

          expect(page).to have_content(key.fingerprint)

          click_on 'Show advanced'

174
          expect(page).to have_field('SSH host keys', with: key.key_text)
175 176 177 178 179 180
        end
      end

      it 'displays error if detection fails' do
        stub_reactive_cache(cache, error: 'Some error text here')

181 182
        visit project_settings_repository_path(project)

183 184 185 186 187 188 189 190 191 192 193
        page.within('.project-mirror-settings') do
          fill_in 'Git repository URL', with: 'ssh://example.com'
          click_on 'Detect host keys'
          wait_for_requests
        end

        # Appears in the flash
        expect(page).to have_content('Some error text here')
      end

      it 'allows manual host keys entry' do
194 195
        visit project_settings_repository_path(project)

196 197 198
        page.within('.project-mirror-settings') do
          fill_in 'Git repository URL', with: 'ssh://example.com'
          click_on 'Show advanced'
199
          fill_in 'SSH host keys', with: "example.com #{key.key_text}"
200 201 202
          click_without_sidekiq 'Save changes'

          expect(page).to have_content(key.fingerprint)
203
          expect(page).to have_content("Verified by #{h(user.name)} less than a minute ago")
204 205 206 207 208 209
        end
      end
    end

    describe 'authentication methods' do
      it 'shows SSH related fields for an SSH URL' do
210 211
        visit project_settings_repository_path(project)

212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231
        page.within('.project-mirror-settings') do
          fill_in 'Git repository URL', with: 'ssh://example.com'

          expect(page).to have_select('Authentication method')

          # SSH can use password authentication but needs host keys
          select 'Password authentication', from: 'Authentication method'
          expect(page).to have_field('Password')
          expect(page).to have_button('Detect host keys')
          expect(page).to have_button('Show advanced')

          # SSH public key authentication also needs host keys but no password
          select 'SSH public key authentication', from: 'Authentication method'
          expect(page).not_to have_field('Password')
          expect(page).to have_button('Detect host keys')
          expect(page).to have_button('Show advanced')
        end
      end

      it 'hides SSH-related fields for a HTTP URL' do
232 233
        visit project_settings_repository_path(project)

234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249
        page.within('.project-mirror-settings') do
          fill_in 'Git repository URL', with: 'https://example.com'

          # HTTPS can't use public key authentication and doesn't need host keys
          expect(page).to have_field('Password')
          expect(page).not_to have_select('Authentication method')
          expect(page).not_to have_button('Detect host keys')
          expect(page).not_to have_button('Show advanced')
        end
      end
    end

    def click_without_sidekiq(*args)
      Sidekiq::Testing.fake! { click_on(*args) }
    end
  end
250
end