summaryrefslogtreecommitdiff
path: root/spec/models/ci/job_token/project_scope_link_spec.rb
blob: 310f9b550f4ea3619d3c4e6ba81b034f2adf5c8c (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
# frozen_string_literal: true

require 'spec_helper'

RSpec.describe Ci::JobToken::ProjectScopeLink, feature_category: :continuous_integration do
  let_it_be(:project) { create(:project) }
  let_it_be(:group) { create(:group) }

  it { is_expected.to belong_to(:source_project) }
  it { is_expected.to belong_to(:target_project) }
  it { is_expected.to belong_to(:added_by) }

  it_behaves_like 'cleanup by a loose foreign key' do
    let!(:parent) { create(:user) }
    let!(:model) { create(:ci_job_token_project_scope_link, added_by: parent) }
  end

  describe 'unique index' do
    let!(:link) { create(:ci_job_token_project_scope_link) }

    it 'raises an error, when not unique' do
      expect do
        create(:ci_job_token_project_scope_link,
          source_project: link.source_project,
          target_project: link.target_project,
          direction: link.direction)
      end.to raise_error(ActiveRecord::RecordNotUnique)
    end
  end

  describe '.create' do
    let_it_be(:target) { create(:project) }
    let(:new_link) { described_class.create(source_project: project, target_project: target) } # rubocop:disable Rails/SaveBang

    context 'when there are more than PROJECT_LINK_DIRECTIONAL_LIMIT existing links' do
      before do
        create_list(:ci_job_token_project_scope_link, 5, source_project: project)
        stub_const("#{described_class}::PROJECT_LINK_DIRECTIONAL_LIMIT", 3)
      end

      it 'invalidates new links and prevents them from being created' do
        expect { new_link }.not_to change { described_class.count }
        expect(new_link).not_to be_persisted
        expect(new_link.errors.full_messages)
          .to include('Source project exceeds the allowable number of project links in this direction')
      end

      it 'does not invalidate existing links' do
        expect(described_class.count).to be > described_class::PROJECT_LINK_DIRECTIONAL_LIMIT
        expect(described_class.all).to all(be_valid)
      end
    end
  end

  describe 'validations' do
    it 'must have a source project', :aggregate_failures do
      link = build(:ci_job_token_project_scope_link, source_project: nil)

      expect(link).not_to be_valid
      expect(link.errors[:source_project]).to contain_exactly("can't be blank")
    end

    it 'must have a target project', :aggregate_failures do
      link = build(:ci_job_token_project_scope_link, target_project: nil)

      expect(link).not_to be_valid
      expect(link.errors[:target_project]).to contain_exactly("can't be blank")
    end

    it 'must have a target project different than source project', :aggregate_failures do
      link = build(:ci_job_token_project_scope_link, target_project: project, source_project: project)

      expect(link).not_to be_valid
      expect(link.errors[:target_project]).to contain_exactly("can't be the same as the source project")
    end
  end

  describe '.with_source' do
    subject { described_class.with_source(project) }

    let!(:source_link) { create(:ci_job_token_project_scope_link, source_project: project) }
    let!(:target_link) { create(:ci_job_token_project_scope_link, target_project: project) }

    it 'returns only the links having the given source project' do
      expect(subject).to contain_exactly(source_link)
    end
  end

  describe '.with_target' do
    subject { described_class.with_target(project) }

    let!(:source_link) { create(:ci_job_token_project_scope_link, source_project: project) }
    let!(:target_link) { create(:ci_job_token_project_scope_link, target_project: project) }

    it 'returns only the links having the given target project' do
      expect(subject).to contain_exactly(target_link)
    end
  end

  describe '.for_source_and_target' do
    let_it_be(:link) { create(:ci_job_token_project_scope_link, source_project: project) }

    subject { described_class.for_source_and_target(project, target_project) }

    context 'when link is found' do
      let(:target_project) { link.target_project }

      it { is_expected.to eq(link) }
    end

    context 'when link is not found' do
      let(:target_project) { create(:project) }

      it { is_expected.to be_nil }
    end
  end

  describe 'enums' do
    let(:directions) { { outbound: 0, inbound: 1 } }

    it { is_expected.to define_enum_for(:direction).with_values(directions) }
  end

  context 'loose foreign key on ci_job_token_project_scope_links.source_project_id' do
    it_behaves_like 'cleanup by a loose foreign key' do
      let!(:parent) { create(:project, namespace: group) }
      let!(:model) { create(:ci_job_token_project_scope_link, source_project: parent) }
    end
  end

  context 'loose foreign key on ci_job_token_project_scope_links.target_project_id' do
    it_behaves_like 'cleanup by a loose foreign key' do
      let!(:parent) { create(:project, namespace: group) }
      let!(:model) { create(:ci_job_token_project_scope_link, target_project: parent) }
    end
  end
end