routable_spec.rb 6.7 KB
Newer Older
1 2 3
require 'spec_helper'

describe Group, 'Routable' do
4
  let!(:group) { create(:group, name: 'foo') }
5

6 7 8 9
  describe 'Validations' do
    it { is_expected.to validate_presence_of(:route) }
  end

10 11
  describe 'Associations' do
    it { is_expected.to have_one(:route).dependent(:destroy) }
12
    it { is_expected.to have_many(:redirect_routes).dependent(:destroy) }
13
  end
T
Toon Claes 已提交
14 15 16 17 18 19 20 21 22 23

  describe 'GitLab read-only instance' do
    it 'does not save route if route is not present' do
      group.route.path = ''
      allow(Gitlab::Database).to receive(:read_only?).and_return(true)
      expect(group).to receive(:update_route).and_call_original

      expect { group.full_path }.to change { Route.count }.by(0)
    end
  end
24 25 26 27

  describe 'Callbacks' do
    it 'creates route record on create' do
      expect(group.route.path).to eq(group.path)
28
      expect(group.route.name).to eq(group.name)
29 30 31
    end

    it 'updates route record on path change' do
32
      group.update_attributes(path: 'wow', name: 'much')
33 34

      expect(group.route.path).to eq('wow')
35
      expect(group.route.name).to eq('much')
36 37 38 39
    end

    it 'ensure route path uniqueness across different objects' do
      create(:group, parent: group, path: 'xyz')
40
      duplicate = build(:project, namespace: group, path: 'xyz')
41

42
      expect { duplicate.save! }.to raise_error(ActiveRecord::RecordInvalid, 'Validation failed: Path has already been taken')
43 44 45 46 47 48
    end
  end

  describe '.find_by_full_path' do
    let!(:nested_group) { create(:group, parent: group) }

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
    context 'without any redirect routes' do
      it { expect(described_class.find_by_full_path(group.to_param)).to eq(group) }
      it { expect(described_class.find_by_full_path(group.to_param.upcase)).to eq(group) }
      it { expect(described_class.find_by_full_path(nested_group.to_param)).to eq(nested_group) }
      it { expect(described_class.find_by_full_path('unknown')).to eq(nil) }
    end

    context 'with redirect routes' do
      let!(:group_redirect_route) { group.redirect_routes.create!(path: 'bar') }
      let!(:nested_group_redirect_route) { nested_group.redirect_routes.create!(path: nested_group.path.sub('foo', 'bar')) }

      context 'without follow_redirects option' do
        context 'with the given path not matching any route' do
          it { expect(described_class.find_by_full_path('unknown')).to eq(nil) }
        end

        context 'with the given path matching the canonical route' do
          it { expect(described_class.find_by_full_path(group.to_param)).to eq(group) }
          it { expect(described_class.find_by_full_path(group.to_param.upcase)).to eq(group) }
          it { expect(described_class.find_by_full_path(nested_group.to_param)).to eq(nested_group) }
        end

        context 'with the given path matching a redirect route' do
          it { expect(described_class.find_by_full_path(group_redirect_route.path)).to eq(nil) }
          it { expect(described_class.find_by_full_path(group_redirect_route.path.upcase)).to eq(nil) }
          it { expect(described_class.find_by_full_path(nested_group_redirect_route.path)).to eq(nil) }
        end
      end

      context 'with follow_redirects option set to true' do
        context 'with the given path not matching any route' do
          it { expect(described_class.find_by_full_path('unknown', follow_redirects: true)).to eq(nil) }
        end

        context 'with the given path matching the canonical route' do
          it { expect(described_class.find_by_full_path(group.to_param, follow_redirects: true)).to eq(group) }
          it { expect(described_class.find_by_full_path(group.to_param.upcase, follow_redirects: true)).to eq(group) }
          it { expect(described_class.find_by_full_path(nested_group.to_param, follow_redirects: true)).to eq(nested_group) }
        end

        context 'with the given path matching a redirect route' do
          it { expect(described_class.find_by_full_path(group_redirect_route.path, follow_redirects: true)).to eq(group) }
          it { expect(described_class.find_by_full_path(group_redirect_route.path.upcase, follow_redirects: true)).to eq(group) }
          it { expect(described_class.find_by_full_path(nested_group_redirect_route.path, follow_redirects: true)).to eq(nested_group) }
        end
      end
    end
96 97
  end

98
  describe '.where_full_path_in' do
99 100
    context 'without any paths' do
      it 'returns an empty relation' do
101
        expect(described_class.where_full_path_in([])).to eq([])
102 103 104 105 106
      end
    end

    context 'without any valid paths' do
      it 'returns an empty relation' do
107
        expect(described_class.where_full_path_in(%w[unknown])).to eq([])
108 109 110 111 112 113 114
      end
    end

    context 'with valid paths' do
      let!(:nested_group) { create(:group, parent: group) }

      it 'returns the projects matching the paths' do
115
        result = described_class.where_full_path_in([group.to_param, nested_group.to_param])
116 117 118 119 120

        expect(result).to contain_exactly(group, nested_group)
      end

      it 'returns projects regardless of the casing of paths' do
121
        result = described_class.where_full_path_in([group.to_param.upcase, nested_group.to_param.upcase])
122 123 124 125 126

        expect(result).to contain_exactly(group, nested_group)
      end
    end
  end
127

128 129 130 131 132
  describe '#full_path' do
    let(:group) { create(:group) }
    let(:nested_group) { create(:group, parent: group) }

    it { expect(group.full_path).to eq(group.path) }
133
    it { expect(nested_group.full_path).to eq("#{group.full_path}/#{nested_group.path}") }
134

135
    context 'with RequestStore active', :request_store do
136
      it 'does not load the route table more than once' do
137
        group.expires_full_path_cache
138 139 140 141 142 143
        expect(group).to receive(:uncached_full_path).once.and_call_original

        3.times { group.full_path }
        expect(group.full_path).to eq(group.path)
      end
    end
144 145
  end

146 147 148
  describe '#expires_full_path_cache' do
    context 'with RequestStore active', :request_store do
      it 'expires the full_path cache' do
149
        expect(group.full_path).to eq('foo')
150

151
        group.route.update(path: 'bar', name: 'bar')
152
        group.expires_full_path_cache
153 154

        expect(group.full_path).to eq('bar')
155 156 157 158
      end
    end
  end

159 160 161 162 163 164 165 166 167 168 169
  describe '#full_name' do
    let(:group) { create(:group) }
    let(:nested_group) { create(:group, parent: group) }

    it { expect(group.full_name).to eq(group.name) }
    it { expect(nested_group.full_name).to eq("#{group.name} / #{nested_group.name}") }
  end
end

describe Project, 'Routable' do
  describe '#full_path' do
170
    let(:project) { build_stubbed(:project) }
171

172
    it { expect(project.full_path).to eq "#{project.namespace.full_path}/#{project.path}" }
173 174 175
  end

  describe '#full_name' do
176
    let(:project) { build_stubbed(:project) }
177 178 179

    it { expect(project.full_name).to eq "#{project.namespace.human_name} / #{project.name}" }
  end
180
end