summaryrefslogtreecommitdiff
path: root/spec/validators/namespace_validator_spec.rb
blob: 589175a2ced9c967b12d32ce66375566d7109839 (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
require 'spec_helper'

describe NamespaceValidator do
  let(:validator) { described_class.new(attributes: [:path]) }

  # Pass in a full path to remove the format segment:
  # `/ci/lint(.:format)` -> `/ci/lint`
  def without_format(path)
    path.split('(', 2)[0]
  end

  # Pass in a full path and get the last segment before a wildcard
  # That's not a parameter
  # `/*namespace_id/:project_id/builds/artifacts/*ref_name_and_path`
  #    -> 'artifacts'
  def segment_before_last_wildcard(path)
    path_segments = path.split('/').reject { |segment| segment.empty?  }
    last_wildcard_index = path_segments.rindex { |part| part.starts_with?('*') }

    index_of_non_param_segment = last_wildcard_index - 1
    part_before_wildcard = path_segments[index_of_non_param_segment]
    while parameter?(part_before_wildcard)
      index_of_non_param_segment -= 1
      part_before_wildcard = path_segments[index_of_non_param_segment]
    end

    part_before_wildcard
  end

  def parameter?(path_segment)
    path_segment.starts_with?(':') || path_segment.starts_with?('*')
  end

  let(:all_routes) do
    Rails.application.routes.routes.routes.
      map { |r| r.path.spec.to_s }
  end

  let(:routes_without_format) { all_routes.map { |path| without_format(path) } }

  # Routes not starting with `/:` or `/*`
  # all routes not starting with a param
  let(:routes_not_starting_in_wildcard) { routes_without_format.select { |p| p !~ %r{^/[:*]} } }

  # All routes that start with a namespaced path, that have 1 or more
  # path-segments before having another wildcard parameter.
  # - Starting with paths:
  #   - `/*namespace_id/:project_id/`
  #   - `/*namespace_id/:id/`
  # - Followed by one or more path-parts not starting with `:` or `/`
  # - Followed by a path-part that includes a wildcard parameter `*`
  # At the time of writing these routes match: http://rubular.com/r/QDxulzZlxZ
  STARTING_WITH_NAMESPACE = /^\/\*namespace_id\/:(project_)?id/
  NON_PARAM_PARTS = /[^:*][a-z\-_\/]*/
  ANY_OTHER_PATH_PART = /[a-z\-_\/:]*/
  WILDCARD_SEGMENT = /\*/
  let(:namespaced_wildcard_routes) do
    routes_without_format.select do |p|
      p =~ %r{#{STARTING_WITH_NAMESPACE}\/#{NON_PARAM_PARTS}\/#{ANY_OTHER_PATH_PART}#{WILDCARD_SEGMENT}}
    end
  end

  describe 'TOP_LEVEL_ROUTES' do
    it 'includes all the top level namespaces' do
      top_level_words =  routes_not_starting_in_wildcard.
                           map { |p| p.split('/')[1] }. # Get the first part of the path
                           compact.
                           uniq

      expect(described_class::TOP_LEVEL_ROUTES).to include(*top_level_words)
    end
  end

  describe 'WILDCARD_ROUTES' do
    it 'includes all paths that can be used after a namespace/project path' do
      all_wildcard_paths = namespaced_wildcard_routes.map do |path|
        segment_before_last_wildcard(path)
      end.uniq

      expect(described_class::WILDCARD_ROUTES).to include(*all_wildcard_paths)
    end
  end

  describe '#valid_full_path' do
    it "isn't valid when the top level is reserved" do
      test_path = 'u/should-be-a/reserved-word'

      expect(described_class.valid_full_path?(test_path)).to be(false)
    end

    it "isn't valid if any of the path segments is reserved" do
      test_path = 'the-wildcard/wikis/is-a-reserved-path'

      expect(described_class.valid_full_path?(test_path)).to be(false)
    end

    it "is valid if the path doen't contain reserved words" do
      test_path = 'there-are/no-wildcards/in-this-path'

      expect(described_class.valid_full_path?(test_path)).to be(true)
    end
  end

  describe '#validation_type' do
    it 'uses top level validation for groups without parent' do
      group = build(:group)

      type = validator.validation_type(group)

      expect(type).to eq(:top_level)
    end

    it 'uses wildcard validation for groups with a parent' do
      group = build(:group, parent: create(:group))

      type = validator.validation_type(group)

      expect(type).to eq(:wildcard)
    end

    it 'uses wildcard validation for a project' do
      project = build(:project)

      type = validator.validation_type(project)

      expect(type).to eq(:wildcard)
    end

    it 'uses strict validation for everything else' do
      type = validator.validation_type(double)

      expect(type).to eq(:strict)
    end
  end
end