From 9e171a530fe885eca784ed6c5a602ab796a5790c Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?R=C3=A9my=20Coutable?= <remy@rymai.me>
Date: Wed, 20 Mar 2019 10:30:06 +0100
Subject: [PATCH] [CE] Reduce differences with EE in snippets_finder_spec.rb
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Signed-off-by: Rémy Coutable <remy@rymai.me>
---
 spec/finders/snippets_finder_spec.rb          | 221 +++++-------
 .../shared_examples/snippet_visibility.rb     | 322 ------------------
 .../snippet_visibility_shared_examples.rb     | 322 ++++++++++++++++++
 3 files changed, 416 insertions(+), 449 deletions(-)
 delete mode 100644 spec/support/shared_examples/snippet_visibility.rb
 create mode 100644 spec/support/shared_examples/snippet_visibility_shared_examples.rb

diff --git a/spec/finders/snippets_finder_spec.rb b/spec/finders/snippets_finder_spec.rb
index 134fb5f2c04..93287f3e9b8 100644
--- a/spec/finders/snippets_finder_spec.rb
+++ b/spec/finders/snippets_finder_spec.rb
@@ -2,7 +2,6 @@ require 'spec_helper'
 
 describe SnippetsFinder do
   include Gitlab::Allowable
-  using RSpec::Parameterized::TableSyntax
 
   describe '#initialize' do
     it 'raises ArgumentError when a project and author are given' do
@@ -14,174 +13,142 @@ describe SnippetsFinder do
     end
   end
 
-  context 'filter by scope' do
-    let(:user) { create :user }
-    let!(:snippet1) { create(:personal_snippet, :private, author: user) }
-    let!(:snippet2) { create(:personal_snippet, :internal, author: user) }
-    let!(:snippet3) { create(:personal_snippet, :public, author: user) }
-
-    it "returns all snippets for 'all' scope" do
-      snippets = described_class.new(user, scope: :all).execute
-
-      expect(snippets).to include(snippet1, snippet2, snippet3)
-    end
-
-    it "returns all snippets for 'are_private' scope" do
-      snippets = described_class.new(user, scope: :are_private).execute
+  describe '#execute' do
+    set(:user) { create(:user) }
+    set(:private_personal_snippet) { create(:personal_snippet, :private, author: user) }
+    set(:internal_personal_snippet) { create(:personal_snippet, :internal, author: user) }
+    set(:public_personal_snippet) { create(:personal_snippet, :public, author: user) }
 
-      expect(snippets).to include(snippet1)
-      expect(snippets).not_to include(snippet2, snippet3)
-    end
+    context 'filter by scope' do
+      it "returns all snippets for 'all' scope" do
+        snippets = described_class.new(user, scope: :all).execute
 
-    it "returns all snippets for 'are_internal' scope" do
-      snippets = described_class.new(user, scope: :are_internal).execute
+        expect(snippets).to contain_exactly(private_personal_snippet, internal_personal_snippet, public_personal_snippet)
+      end
 
-      expect(snippets).to include(snippet2)
-      expect(snippets).not_to include(snippet1, snippet3)
-    end
+      it "returns all snippets for 'are_private' scope" do
+        snippets = described_class.new(user, scope: :are_private).execute
 
-    it "returns all snippets for 'are_private' scope" do
-      snippets = described_class.new(user, scope: :are_public).execute
+        expect(snippets).to contain_exactly(private_personal_snippet)
+      end
 
-      expect(snippets).to include(snippet3)
-      expect(snippets).not_to include(snippet1, snippet2)
-    end
-  end
+      it "returns all snippets for 'are_internal' scope" do
+        snippets = described_class.new(user, scope: :are_internal).execute
 
-  context 'filter by author' do
-    let(:user) { create :user }
-    let(:user1) { create :user }
-    let!(:snippet1) { create(:personal_snippet, :private, author: user) }
-    let!(:snippet2) { create(:personal_snippet, :internal, author: user) }
-    let!(:snippet3) { create(:personal_snippet, :public, author: user) }
+        expect(snippets).to contain_exactly(internal_personal_snippet)
+      end
 
-    it "returns all public and internal snippets" do
-      snippets = described_class.new(user1, author: user).execute
+      it "returns all snippets for 'are_private' scope" do
+        snippets = described_class.new(user, scope: :are_public).execute
 
-      expect(snippets).to include(snippet2, snippet3)
-      expect(snippets).not_to include(snippet1)
+        expect(snippets).to contain_exactly(public_personal_snippet)
+      end
     end
 
-    it "returns internal snippets" do
-      snippets = described_class.new(user, author: user, scope: :are_internal).execute
+    context 'filter by author' do
+      it 'returns all public and internal snippets' do
+        snippets = described_class.new(create(:user), author: user).execute
 
-      expect(snippets).to include(snippet2)
-      expect(snippets).not_to include(snippet1, snippet3)
-    end
+        expect(snippets).to contain_exactly(internal_personal_snippet, public_personal_snippet)
+      end
 
-    it "returns private snippets" do
-      snippets = described_class.new(user, author: user, scope: :are_private).execute
+      it 'returns internal snippets' do
+        snippets = described_class.new(user, author: user, scope: :are_internal).execute
 
-      expect(snippets).to include(snippet1)
-      expect(snippets).not_to include(snippet2, snippet3)
-    end
+        expect(snippets).to contain_exactly(internal_personal_snippet)
+      end
 
-    it "returns public snippets" do
-      snippets = described_class.new(user, author: user, scope: :are_public).execute
+      it 'returns private snippets' do
+        snippets = described_class.new(user, author: user, scope: :are_private).execute
 
-      expect(snippets).to include(snippet3)
-      expect(snippets).not_to include(snippet1, snippet2)
-    end
+        expect(snippets).to contain_exactly(private_personal_snippet)
+      end
 
-    it "returns all snippets" do
-      snippets = described_class.new(user, author: user).execute
+      it 'returns public snippets' do
+        snippets = described_class.new(user, author: user, scope: :are_public).execute
 
-      expect(snippets).to include(snippet1, snippet2, snippet3)
-    end
+        expect(snippets).to contain_exactly(public_personal_snippet)
+      end
 
-    it "returns only public snippets if unauthenticated user" do
-      snippets = described_class.new(nil, author: user).execute
+      it 'returns all snippets' do
+        snippets = described_class.new(user, author: user).execute
 
-      expect(snippets).to include(snippet3)
-      expect(snippets).not_to include(snippet2, snippet1)
-    end
+        expect(snippets).to contain_exactly(private_personal_snippet, internal_personal_snippet, public_personal_snippet)
+      end
 
-    it 'returns all snippets for an admin' do
-      admin = create(:user, :admin)
-      snippets = described_class.new(admin, author: user).execute
+      it 'returns only public snippets if unauthenticated user' do
+        snippets = described_class.new(nil, author: user).execute
 
-      expect(snippets).to include(snippet1, snippet2, snippet3)
-    end
-  end
+        expect(snippets).to contain_exactly(public_personal_snippet)
+      end
 
-  context 'filter by project' do
-    let(:user) { create :user }
-    let(:group) { create :group, :public }
-    let(:project1) { create(:project, :public, group: group) }
+      it 'returns all snippets for an admin' do
+        admin = create(:user, :admin)
+        snippets = described_class.new(admin, author: user).execute
 
-    before do
-      @snippet1 = create(:project_snippet, :private,  project: project1)
-      @snippet2 = create(:project_snippet, :internal, project: project1)
-      @snippet3 = create(:project_snippet, :public,   project: project1)
+        expect(snippets).to contain_exactly(private_personal_snippet, internal_personal_snippet, public_personal_snippet)
+      end
     end
 
-    it "returns public snippets for unauthorized user" do
-      snippets = described_class.new(nil, project: project1).execute
+    context 'project snippets' do
+      let(:group) { create(:group, :public) }
+      let(:project) { create(:project, :public, group: group) }
+      let!(:private_project_snippet) { create(:project_snippet, :private, project: project) }
+      let!(:internal_project_snippet) { create(:project_snippet, :internal, project: project) }
+      let!(:public_project_snippet) { create(:project_snippet, :public, project: project) }
 
-      expect(snippets).to include(@snippet3)
-      expect(snippets).not_to include(@snippet1, @snippet2)
-    end
+      it 'returns public personal and project snippets for unauthorized user' do
+        snippets = described_class.new(nil, project: project).execute
 
-    it "returns public and internal snippets for non project members" do
-      snippets = described_class.new(user, project: project1).execute
+        expect(snippets).to contain_exactly(public_project_snippet)
+      end
 
-      expect(snippets).to include(@snippet2, @snippet3)
-      expect(snippets).not_to include(@snippet1)
-    end
+      it 'returns public and internal snippets for non project members' do
+        snippets = described_class.new(user, project: project).execute
 
-    it "returns public snippets for non project members" do
-      snippets = described_class.new(user, project: project1, scope: :are_public).execute
+        expect(snippets).to contain_exactly(internal_project_snippet, public_project_snippet)
+      end
 
-      expect(snippets).to include(@snippet3)
-      expect(snippets).not_to include(@snippet1, @snippet2)
-    end
+      it 'returns public snippets for non project members' do
+        snippets = described_class.new(user, project: project, scope: :are_public).execute
 
-    it "returns internal snippets for non project members" do
-      snippets = described_class.new(user, project: project1, scope: :are_internal).execute
+        expect(snippets).to contain_exactly(public_project_snippet)
+      end
 
-      expect(snippets).to include(@snippet2)
-      expect(snippets).not_to include(@snippet1, @snippet3)
-    end
+      it 'returns internal snippets for non project members' do
+        snippets = described_class.new(user, project: project, scope: :are_internal).execute
 
-    it "does not return private snippets for non project members" do
-      snippets = described_class.new(user, project: project1, scope: :are_private).execute
+        expect(snippets).to contain_exactly(internal_project_snippet)
+      end
 
-      expect(snippets).not_to include(@snippet1, @snippet2, @snippet3)
-    end
+      it 'does not return private snippets for non project members' do
+        snippets = described_class.new(user, project: project, scope: :are_private).execute
 
-    it "returns all snippets for project members" do
-      project1.add_developer(user)
+        expect(snippets).to be_empty
+      end
 
-      snippets = described_class.new(user, project: project1).execute
+      it 'returns all snippets for project members' do
+        project.add_developer(user)
 
-      expect(snippets).to include(@snippet1, @snippet2, @snippet3)
-    end
+        snippets = described_class.new(user, project: project).execute
 
-    it "returns private snippets for project members" do
-      project1.add_developer(user)
+        expect(snippets).to contain_exactly(private_project_snippet, internal_project_snippet, public_project_snippet)
+      end
 
-      snippets = described_class.new(user, project: project1, scope: :are_private).execute
+      it 'returns private snippets for project members' do
+        project.add_developer(user)
 
-      expect(snippets).to include(@snippet1)
-    end
+        snippets = described_class.new(user, project: project, scope: :are_private).execute
 
-    it 'returns all snippets for an admin' do
-      admin = create(:user, :admin)
-      snippets = described_class.new(admin, project: project1).execute
+        expect(snippets).to contain_exactly(private_project_snippet)
+      end
 
-      expect(snippets).to include(@snippet1, @snippet2, @snippet3)
-    end
-  end
+      it 'returns all snippets for an admin' do
+        admin = create(:user, :admin)
+        snippets = described_class.new(admin, project: project).execute
 
-  describe '#execute' do
-    let(:project) { create(:project, :public) }
-    let!(:project_snippet) { create(:project_snippet, :public, project: project) }
-    let!(:personal_snippet) { create(:personal_snippet, :public) }
-    let(:user) { create(:user) }
-    subject(:finder) { described_class.new(user) }
-
-    it 'returns project- and personal snippets' do
-      expect(finder.execute).to contain_exactly(project_snippet, personal_snippet)
+        expect(snippets).to contain_exactly(private_project_snippet, internal_project_snippet, public_project_snippet)
+      end
     end
 
     context 'when the user cannot read cross project' do
@@ -191,7 +158,7 @@ describe SnippetsFinder do
       end
 
       it 'returns only personal snippets when the user cannot read cross project' do
-        expect(finder.execute).to contain_exactly(personal_snippet)
+        expect(described_class.new(user).execute).to contain_exactly(private_personal_snippet, internal_personal_snippet, public_personal_snippet)
       end
     end
   end
diff --git a/spec/support/shared_examples/snippet_visibility.rb b/spec/support/shared_examples/snippet_visibility.rb
deleted file mode 100644
index 3a7c69b7877..00000000000
--- a/spec/support/shared_examples/snippet_visibility.rb
+++ /dev/null
@@ -1,322 +0,0 @@
-RSpec.shared_examples 'snippet visibility' do
-  let!(:author) { create(:user) }
-  let!(:member) { create(:user) }
-  let!(:external) { create(:user, :external) }
-
-  let!(:snippet_type_visibilities) do
-    {
-      public: Snippet::PUBLIC,
-      internal: Snippet::INTERNAL,
-      private: Snippet::PRIVATE
-    }
-  end
-
-  context "For project snippets" do
-    let!(:users) do
-      {
-        unauthenticated: nil,
-        external: external,
-        non_member: create(:user),
-        member: member,
-        author: author
-      }
-    end
-
-    let!(:project_type_visibilities) do
-      {
-        public: Gitlab::VisibilityLevel::PUBLIC,
-        internal: Gitlab::VisibilityLevel::INTERNAL,
-        private: Gitlab::VisibilityLevel::PRIVATE
-      }
-    end
-
-    let(:project_feature_visibilities) do
-      {
-        enabled: ProjectFeature::ENABLED,
-        private: ProjectFeature::PRIVATE,
-        disabled: ProjectFeature::DISABLED
-      }
-    end
-
-    where(:project_type, :feature_visibility, :user_type, :snippet_type, :outcome) do
-      [
-        # Public projects
-        [:public, :enabled, :unauthenticated,    :public,   true],
-        [:public, :enabled, :unauthenticated,    :internal, false],
-        [:public, :enabled, :unauthenticated,    :private,  false],
-
-        [:public, :enabled, :external,           :public,   true],
-        [:public, :enabled, :external,           :internal, false],
-        [:public, :enabled, :external,           :private,  false],
-
-        [:public, :enabled, :non_member,         :public,   true],
-        [:public, :enabled, :non_member,         :internal, true],
-        [:public, :enabled, :non_member,         :private,  false],
-
-        [:public, :enabled, :member,             :public,   true],
-        [:public, :enabled, :member,             :internal, true],
-        [:public, :enabled, :member,             :private,  true],
-
-        [:public, :enabled, :author,             :public,   true],
-        [:public, :enabled, :author,             :internal, true],
-        [:public, :enabled, :author,             :private,  true],
-
-        [:public, :private, :unauthenticated,    :public,   false],
-        [:public, :private, :unauthenticated,    :internal, false],
-        [:public, :private, :unauthenticated,    :private,  false],
-
-        [:public, :private, :external,           :public,   false],
-        [:public, :private, :external,           :internal, false],
-        [:public, :private, :external,           :private,  false],
-
-        [:public, :private, :non_member,         :public,   false],
-        [:public, :private, :non_member,         :internal, false],
-        [:public, :private, :non_member,         :private,  false],
-
-        [:public, :private, :member,             :public,   true],
-        [:public, :private, :member,             :internal, true],
-        [:public, :private, :member,             :private,  true],
-
-        [:public, :private, :author,             :public,   true],
-        [:public, :private, :author,             :internal, true],
-        [:public, :private, :author,             :private,  true],
-
-        [:public, :disabled, :unauthenticated,   :public,   false],
-        [:public, :disabled, :unauthenticated,   :internal, false],
-        [:public, :disabled, :unauthenticated,   :private,  false],
-
-        [:public, :disabled, :external,          :public,   false],
-        [:public, :disabled, :external,          :internal, false],
-        [:public, :disabled, :external,          :private,  false],
-
-        [:public, :disabled, :non_member,        :public,   false],
-        [:public, :disabled, :non_member,        :internal, false],
-        [:public, :disabled, :non_member,        :private,  false],
-
-        [:public, :disabled, :member,            :public,   false],
-        [:public, :disabled, :member,            :internal, false],
-        [:public, :disabled, :member,            :private,  false],
-
-        [:public, :disabled, :author,            :public,   false],
-        [:public, :disabled, :author,            :internal, false],
-        [:public, :disabled, :author,            :private,  false],
-
-        # Internal projects
-        [:internal, :enabled, :unauthenticated,  :public,   false],
-        [:internal, :enabled, :unauthenticated,  :internal, false],
-        [:internal, :enabled, :unauthenticated,  :private,  false],
-
-        [:internal, :enabled, :external,         :public,   false],
-        [:internal, :enabled, :external,         :internal, false],
-        [:internal, :enabled, :external,         :private,  false],
-
-        [:internal, :enabled, :non_member,       :public,   true],
-        [:internal, :enabled, :non_member,       :internal, true],
-        [:internal, :enabled, :non_member,       :private,  false],
-
-        [:internal, :enabled, :member,           :public,   true],
-        [:internal, :enabled, :member,           :internal, true],
-        [:internal, :enabled, :member,           :private,  true],
-
-        [:internal, :enabled, :author,           :public,   true],
-        [:internal, :enabled, :author,           :internal, true],
-        [:internal, :enabled, :author,           :private,  true],
-
-        [:internal, :private, :unauthenticated,  :public,   false],
-        [:internal, :private, :unauthenticated,  :internal, false],
-        [:internal, :private, :unauthenticated,  :private,  false],
-
-        [:internal, :private, :external,         :public,   false],
-        [:internal, :private, :external,         :internal, false],
-        [:internal, :private, :external,         :private,  false],
-
-        [:internal, :private, :non_member,       :public,   false],
-        [:internal, :private, :non_member,       :internal, false],
-        [:internal, :private, :non_member,       :private,  false],
-
-        [:internal, :private, :member,           :public,   true],
-        [:internal, :private, :member,           :internal, true],
-        [:internal, :private, :member,           :private,  true],
-
-        [:internal, :private, :author,           :public,   true],
-        [:internal, :private, :author,           :internal, true],
-        [:internal, :private, :author,           :private,  true],
-
-        [:internal, :disabled, :unauthenticated, :public,   false],
-        [:internal, :disabled, :unauthenticated, :internal, false],
-        [:internal, :disabled, :unauthenticated, :private,  false],
-
-        [:internal, :disabled, :external,        :public,   false],
-        [:internal, :disabled, :external,        :internal, false],
-        [:internal, :disabled, :external,        :private,  false],
-
-        [:internal, :disabled, :non_member,      :public,   false],
-        [:internal, :disabled, :non_member,      :internal, false],
-        [:internal, :disabled, :non_member,      :private,  false],
-
-        [:internal, :disabled, :member,          :public,   false],
-        [:internal, :disabled, :member,          :internal, false],
-        [:internal, :disabled, :member,          :private,  false],
-
-        [:internal, :disabled, :author,          :public,   false],
-        [:internal, :disabled, :author,          :internal, false],
-        [:internal, :disabled, :author,          :private,  false],
-
-        # Private projects
-        [:private, :enabled, :unauthenticated,   :public,   false],
-        [:private, :enabled, :unauthenticated,   :internal, false],
-        [:private, :enabled, :unauthenticated,   :private,  false],
-
-        [:private, :enabled, :external,          :public,   true],
-        [:private, :enabled, :external,          :internal, true],
-        [:private, :enabled, :external,          :private,  true],
-
-        [:private, :enabled, :non_member,        :public,   false],
-        [:private, :enabled, :non_member,        :internal, false],
-        [:private, :enabled, :non_member,        :private,  false],
-
-        [:private, :enabled, :member,            :public,   true],
-        [:private, :enabled, :member,            :internal, true],
-        [:private, :enabled, :member,            :private,  true],
-
-        [:private, :enabled, :author,            :public,   true],
-        [:private, :enabled, :author,            :internal, true],
-        [:private, :enabled, :author,            :private,  true],
-
-        [:private, :private, :unauthenticated,   :public,   false],
-        [:private, :private, :unauthenticated,   :internal, false],
-        [:private, :private, :unauthenticated,   :private,  false],
-
-        [:private, :private, :external,          :public,   true],
-        [:private, :private, :external,          :internal, true],
-        [:private, :private, :external,          :private,  true],
-
-        [:private, :private, :non_member,        :public,   false],
-        [:private, :private, :non_member,        :internal, false],
-        [:private, :private, :non_member,        :private,  false],
-
-        [:private, :private, :member,            :public,   true],
-        [:private, :private, :member,            :internal, true],
-        [:private, :private, :member,            :private,  true],
-
-        [:private, :private, :author,            :public,   true],
-        [:private, :private, :author,            :internal, true],
-        [:private, :private, :author,            :private,  true],
-
-        [:private, :disabled, :unauthenticated,  :public,   false],
-        [:private, :disabled, :unauthenticated,  :internal, false],
-        [:private, :disabled, :unauthenticated,  :private,  false],
-
-        [:private, :disabled, :external,         :public,   false],
-        [:private, :disabled, :external,         :internal, false],
-        [:private, :disabled, :external,         :private,  false],
-
-        [:private, :disabled, :non_member,       :public,   false],
-        [:private, :disabled, :non_member,       :internal, false],
-        [:private, :disabled, :non_member,       :private,  false],
-
-        [:private, :disabled, :member,           :public,   false],
-        [:private, :disabled, :member,           :internal, false],
-        [:private, :disabled, :member,           :private,  false],
-
-        [:private, :disabled, :author,           :public,   false],
-        [:private, :disabled, :author,           :internal, false],
-        [:private, :disabled, :author,           :private,  false]
-      ]
-    end
-
-    with_them do
-      let!(:project) { create(:project, visibility_level: project_type_visibilities[project_type]) }
-      let!(:project_feature) { project.project_feature.update_column(:snippets_access_level, project_feature_visibilities[feature_visibility]) }
-      let!(:user) { users[user_type] }
-      let!(:snippet) { create(:project_snippet, visibility_level: snippet_type_visibilities[snippet_type], project: project, author: author) }
-      let!(:members) do
-        project.add_developer(author)
-        project.add_developer(member)
-        project.add_developer(external) if project.private?
-      end
-
-      context "For #{params[:project_type]} project and #{params[:user_type]} users" do
-        it 'should agree with the read_project_snippet policy' do
-          expect(can?(user, :read_project_snippet, snippet)).to eq(outcome)
-        end
-
-        it 'should return proper outcome' do
-          results = described_class.new(user, project: project).execute
-          expect(results.include?(snippet)).to eq(outcome)
-        end
-      end
-
-      context "Without a given project and #{params[:user_type]} users" do
-        it 'should return proper outcome' do
-          results = described_class.new(user).execute
-          expect(results.include?(snippet)).to eq(outcome)
-        end
-
-        it 'returns no snippets when the user cannot read cross project' do
-          allow(Ability).to receive(:allowed?).and_call_original
-          allow(Ability).to receive(:allowed?).with(user, :read_cross_project) { false }
-
-          snippets = described_class.new(user).execute
-
-          expect(snippets).to be_empty
-        end
-      end
-    end
-  end
-
-  context 'For personal snippets' do
-    let!(:users) do
-      {
-        unauthenticated: nil,
-        external: external,
-        non_member: create(:user),
-        author: author
-      }
-    end
-
-    where(:snippet_visibility, :user_type, :outcome) do
-      [
-        [:public,   :unauthenticated, true],
-        [:public,   :external,        true],
-        [:public,   :non_member,      true],
-        [:public,   :author,          true],
-
-        [:internal, :unauthenticated, false],
-        [:internal, :external,        false],
-        [:internal, :non_member,      true],
-        [:internal, :author,          true],
-
-        [:private,  :unauthenticated, false],
-        [:private,  :external,        false],
-        [:private,  :non_member,      false],
-        [:private,  :author,          true]
-      ]
-    end
-
-    with_them do
-      let!(:user) { users[user_type] }
-      let!(:snippet) { create(:personal_snippet, visibility_level: snippet_type_visibilities[snippet_visibility], author: author) }
-
-      context "For personal and #{params[:snippet_visibility]} snippets with #{params[:user_type]} user" do
-        it 'should agree with read_personal_snippet policy' do
-          expect(can?(user, :read_personal_snippet, snippet)).to eq(outcome)
-        end
-
-        it 'should return proper outcome' do
-          results = described_class.new(user).execute
-          expect(results.include?(snippet)).to eq(outcome)
-        end
-
-        it 'should return personal snippets when the user cannot read cross project' do
-          allow(Ability).to receive(:allowed?).and_call_original
-          allow(Ability).to receive(:allowed?).with(user, :read_cross_project) { false }
-
-          results = described_class.new(user).execute
-
-          expect(results.include?(snippet)).to eq(outcome)
-        end
-      end
-    end
-  end
-end
diff --git a/spec/support/shared_examples/snippet_visibility_shared_examples.rb b/spec/support/shared_examples/snippet_visibility_shared_examples.rb
new file mode 100644
index 00000000000..f622876d7a5
--- /dev/null
+++ b/spec/support/shared_examples/snippet_visibility_shared_examples.rb
@@ -0,0 +1,322 @@
+RSpec.shared_examples 'snippet visibility' do
+  using RSpec::Parameterized::TableSyntax
+
+  # Make sure no snippets exist prior to running the test matrix
+  before(:context) do
+    DatabaseCleaner.clean_with(:truncation)
+  end
+
+  set(:author) { create(:user) }
+  set(:member) { create(:user) }
+  set(:external) { create(:user, :external) }
+
+  let!(:snippet_type_visibilities) do
+    {
+      public: Snippet::PUBLIC,
+      internal: Snippet::INTERNAL,
+      private: Snippet::PRIVATE
+    }
+  end
+
+  context "For project snippets" do
+    let!(:users) do
+      {
+        unauthenticated: nil,
+        external: external,
+        non_member: create(:user),
+        member: member,
+        author: author
+      }
+    end
+
+    let(:project_feature_visibilities) do
+      {
+        enabled: ProjectFeature::ENABLED,
+        private: ProjectFeature::PRIVATE,
+        disabled: ProjectFeature::DISABLED
+      }
+    end
+
+    where(:project_type, :feature_visibility, :user_type, :snippet_type, :outcome) do
+      [
+        # Public projects
+        [:public, ProjectFeature::ENABLED, :unauthenticated,    Snippet::PUBLIC,   true],
+        [:public, ProjectFeature::ENABLED, :unauthenticated,    Snippet::INTERNAL, false],
+        [:public, ProjectFeature::ENABLED, :unauthenticated,    Snippet::PRIVATE,  false],
+
+        [:public, ProjectFeature::ENABLED, :external,           Snippet::PUBLIC,   true],
+        [:public, ProjectFeature::ENABLED, :external,           Snippet::INTERNAL, false],
+        [:public, ProjectFeature::ENABLED, :external,           Snippet::PRIVATE,  false],
+
+        [:public, ProjectFeature::ENABLED, :non_member,         Snippet::PUBLIC,   true],
+        [:public, ProjectFeature::ENABLED, :non_member,         Snippet::INTERNAL, true],
+        [:public, ProjectFeature::ENABLED, :non_member,         Snippet::PRIVATE,  false],
+
+        [:public, ProjectFeature::ENABLED, :member,             Snippet::PUBLIC,   true],
+        [:public, ProjectFeature::ENABLED, :member,             Snippet::INTERNAL, true],
+        [:public, ProjectFeature::ENABLED, :member,             Snippet::PRIVATE,  true],
+
+        [:public, ProjectFeature::ENABLED, :author,             Snippet::PUBLIC,   true],
+        [:public, ProjectFeature::ENABLED, :author,             Snippet::INTERNAL, true],
+        [:public, ProjectFeature::ENABLED, :author,             Snippet::PRIVATE,  true],
+
+        [:public, ProjectFeature::PRIVATE, :unauthenticated,    Snippet::PUBLIC,   false],
+        [:public, ProjectFeature::PRIVATE, :unauthenticated,    Snippet::INTERNAL, false],
+        [:public, ProjectFeature::PRIVATE, :unauthenticated,    Snippet::PRIVATE,  false],
+
+        [:public, ProjectFeature::PRIVATE, :external,           Snippet::PUBLIC,   false],
+        [:public, ProjectFeature::PRIVATE, :external,           Snippet::INTERNAL, false],
+        [:public, ProjectFeature::PRIVATE, :external,           Snippet::PRIVATE,  false],
+
+        [:public, ProjectFeature::PRIVATE, :non_member,         Snippet::PUBLIC,   false],
+        [:public, ProjectFeature::PRIVATE, :non_member,         Snippet::INTERNAL, false],
+        [:public, ProjectFeature::PRIVATE, :non_member,         Snippet::PRIVATE,  false],
+
+        [:public, ProjectFeature::PRIVATE, :member,             Snippet::PUBLIC,   true],
+        [:public, ProjectFeature::PRIVATE, :member,             Snippet::INTERNAL, true],
+        [:public, ProjectFeature::PRIVATE, :member,             Snippet::PRIVATE,  true],
+
+        [:public, ProjectFeature::PRIVATE, :author,             Snippet::PUBLIC,   true],
+        [:public, ProjectFeature::PRIVATE, :author,             Snippet::INTERNAL, true],
+        [:public, ProjectFeature::PRIVATE, :author,             Snippet::PRIVATE,  true],
+
+        [:public, ProjectFeature::DISABLED, :unauthenticated,   Snippet::PUBLIC,   false],
+        [:public, ProjectFeature::DISABLED, :unauthenticated,   Snippet::INTERNAL, false],
+        [:public, ProjectFeature::DISABLED, :unauthenticated,   Snippet::PRIVATE,  false],
+
+        [:public, ProjectFeature::DISABLED, :external,          Snippet::PUBLIC,   false],
+        [:public, ProjectFeature::DISABLED, :external,          Snippet::INTERNAL, false],
+        [:public, ProjectFeature::DISABLED, :external,          Snippet::PRIVATE,  false],
+
+        [:public, ProjectFeature::DISABLED, :non_member,        Snippet::PUBLIC,   false],
+        [:public, ProjectFeature::DISABLED, :non_member,        Snippet::INTERNAL, false],
+        [:public, ProjectFeature::DISABLED, :non_member,        Snippet::PRIVATE,  false],
+
+        [:public, ProjectFeature::DISABLED, :member,            Snippet::PUBLIC,   false],
+        [:public, ProjectFeature::DISABLED, :member,            Snippet::INTERNAL, false],
+        [:public, ProjectFeature::DISABLED, :member,            Snippet::PRIVATE,  false],
+
+        [:public, ProjectFeature::DISABLED, :author,            Snippet::PUBLIC,   false],
+        [:public, ProjectFeature::DISABLED, :author,            Snippet::INTERNAL, false],
+        [:public, ProjectFeature::DISABLED, :author,            Snippet::PRIVATE,  false],
+
+        # Internal projects
+        [:internal, ProjectFeature::ENABLED, :unauthenticated,  Snippet::PUBLIC,   false],
+        [:internal, ProjectFeature::ENABLED, :unauthenticated,  Snippet::INTERNAL, false],
+        [:internal, ProjectFeature::ENABLED, :unauthenticated,  Snippet::PRIVATE,  false],
+
+        [:internal, ProjectFeature::ENABLED, :external,         Snippet::PUBLIC,   false],
+        [:internal, ProjectFeature::ENABLED, :external,         Snippet::INTERNAL, false],
+        [:internal, ProjectFeature::ENABLED, :external,         Snippet::PRIVATE,  false],
+
+        [:internal, ProjectFeature::ENABLED, :non_member,       Snippet::PUBLIC,   true],
+        [:internal, ProjectFeature::ENABLED, :non_member,       Snippet::INTERNAL, true],
+        [:internal, ProjectFeature::ENABLED, :non_member,       Snippet::PRIVATE,  false],
+
+        [:internal, ProjectFeature::ENABLED, :member,           Snippet::PUBLIC,   true],
+        [:internal, ProjectFeature::ENABLED, :member,           Snippet::INTERNAL, true],
+        [:internal, ProjectFeature::ENABLED, :member,           Snippet::PRIVATE,  true],
+
+        [:internal, ProjectFeature::ENABLED, :author,           Snippet::PUBLIC,   true],
+        [:internal, ProjectFeature::ENABLED, :author,           Snippet::INTERNAL, true],
+        [:internal, ProjectFeature::ENABLED, :author,           Snippet::PRIVATE,  true],
+
+        [:internal, ProjectFeature::PRIVATE, :unauthenticated,  Snippet::PUBLIC,   false],
+        [:internal, ProjectFeature::PRIVATE, :unauthenticated,  Snippet::INTERNAL, false],
+        [:internal, ProjectFeature::PRIVATE, :unauthenticated,  Snippet::PRIVATE,  false],
+
+        [:internal, ProjectFeature::PRIVATE, :external,         Snippet::PUBLIC,   false],
+        [:internal, ProjectFeature::PRIVATE, :external,         Snippet::INTERNAL, false],
+        [:internal, ProjectFeature::PRIVATE, :external,         Snippet::PRIVATE,  false],
+
+        [:internal, ProjectFeature::PRIVATE, :non_member,       Snippet::PUBLIC,   false],
+        [:internal, ProjectFeature::PRIVATE, :non_member,       Snippet::INTERNAL, false],
+        [:internal, ProjectFeature::PRIVATE, :non_member,       Snippet::PRIVATE,  false],
+
+        [:internal, ProjectFeature::PRIVATE, :member,           Snippet::PUBLIC,   true],
+        [:internal, ProjectFeature::PRIVATE, :member,           Snippet::INTERNAL, true],
+        [:internal, ProjectFeature::PRIVATE, :member,           Snippet::PRIVATE,  true],
+
+        [:internal, ProjectFeature::PRIVATE, :author,           Snippet::PUBLIC,   true],
+        [:internal, ProjectFeature::PRIVATE, :author,           Snippet::INTERNAL, true],
+        [:internal, ProjectFeature::PRIVATE, :author,           Snippet::PRIVATE,  true],
+
+        [:internal, ProjectFeature::DISABLED, :unauthenticated, Snippet::PUBLIC,   false],
+        [:internal, ProjectFeature::DISABLED, :unauthenticated, Snippet::INTERNAL, false],
+        [:internal, ProjectFeature::DISABLED, :unauthenticated, Snippet::PRIVATE,  false],
+
+        [:internal, ProjectFeature::DISABLED, :external,        Snippet::PUBLIC,   false],
+        [:internal, ProjectFeature::DISABLED, :external,        Snippet::INTERNAL, false],
+        [:internal, ProjectFeature::DISABLED, :external,        Snippet::PRIVATE,  false],
+
+        [:internal, ProjectFeature::DISABLED, :non_member,      Snippet::PUBLIC,   false],
+        [:internal, ProjectFeature::DISABLED, :non_member,      Snippet::INTERNAL, false],
+        [:internal, ProjectFeature::DISABLED, :non_member,      Snippet::PRIVATE,  false],
+
+        [:internal, ProjectFeature::DISABLED, :member,          Snippet::PUBLIC,   false],
+        [:internal, ProjectFeature::DISABLED, :member,          Snippet::INTERNAL, false],
+        [:internal, ProjectFeature::DISABLED, :member,          Snippet::PRIVATE,  false],
+
+        [:internal, ProjectFeature::DISABLED, :author,          Snippet::PUBLIC,   false],
+        [:internal, ProjectFeature::DISABLED, :author,          Snippet::INTERNAL, false],
+        [:internal, ProjectFeature::DISABLED, :author,          Snippet::PRIVATE,  false],
+
+        # Private projects
+        [:private, ProjectFeature::ENABLED, :unauthenticated,   Snippet::PUBLIC,   false],
+        [:private, ProjectFeature::ENABLED, :unauthenticated,   Snippet::INTERNAL, false],
+        [:private, ProjectFeature::ENABLED, :unauthenticated,   Snippet::PRIVATE,  false],
+
+        [:private, ProjectFeature::ENABLED, :external,          Snippet::PUBLIC,   true],
+        [:private, ProjectFeature::ENABLED, :external,          Snippet::INTERNAL, true],
+        [:private, ProjectFeature::ENABLED, :external,          Snippet::PRIVATE,  true],
+
+        [:private, ProjectFeature::ENABLED, :non_member,        Snippet::PUBLIC,   false],
+        [:private, ProjectFeature::ENABLED, :non_member,        Snippet::INTERNAL, false],
+        [:private, ProjectFeature::ENABLED, :non_member,        Snippet::PRIVATE,  false],
+
+        [:private, ProjectFeature::ENABLED, :member,            Snippet::PUBLIC,   true],
+        [:private, ProjectFeature::ENABLED, :member,            Snippet::INTERNAL, true],
+        [:private, ProjectFeature::ENABLED, :member,            Snippet::PRIVATE,  true],
+
+        [:private, ProjectFeature::ENABLED, :author,            Snippet::PUBLIC,   true],
+        [:private, ProjectFeature::ENABLED, :author,            Snippet::INTERNAL, true],
+        [:private, ProjectFeature::ENABLED, :author,            Snippet::PRIVATE,  true],
+
+        [:private, ProjectFeature::PRIVATE, :unauthenticated,   Snippet::PUBLIC,   false],
+        [:private, ProjectFeature::PRIVATE, :unauthenticated,   Snippet::INTERNAL, false],
+        [:private, ProjectFeature::PRIVATE, :unauthenticated,   Snippet::PRIVATE,  false],
+
+        [:private, ProjectFeature::PRIVATE, :external,          Snippet::PUBLIC,   true],
+        [:private, ProjectFeature::PRIVATE, :external,          Snippet::INTERNAL, true],
+        [:private, ProjectFeature::PRIVATE, :external,          Snippet::PRIVATE,  true],
+
+        [:private, ProjectFeature::PRIVATE, :non_member,        Snippet::PUBLIC,   false],
+        [:private, ProjectFeature::PRIVATE, :non_member,        Snippet::INTERNAL, false],
+        [:private, ProjectFeature::PRIVATE, :non_member,        Snippet::PRIVATE,  false],
+
+        [:private, ProjectFeature::PRIVATE, :member,            Snippet::PUBLIC,   true],
+        [:private, ProjectFeature::PRIVATE, :member,            Snippet::INTERNAL, true],
+        [:private, ProjectFeature::PRIVATE, :member,            Snippet::PRIVATE,  true],
+
+        [:private, ProjectFeature::PRIVATE, :author,            Snippet::PUBLIC,   true],
+        [:private, ProjectFeature::PRIVATE, :author,            Snippet::INTERNAL, true],
+        [:private, ProjectFeature::PRIVATE, :author,            Snippet::PRIVATE,  true],
+
+        [:private, ProjectFeature::DISABLED, :unauthenticated,  Snippet::PUBLIC,   false],
+        [:private, ProjectFeature::DISABLED, :unauthenticated,  Snippet::INTERNAL, false],
+        [:private, ProjectFeature::DISABLED, :unauthenticated,  Snippet::PRIVATE,  false],
+
+        [:private, ProjectFeature::DISABLED, :external,         Snippet::PUBLIC,   false],
+        [:private, ProjectFeature::DISABLED, :external,         Snippet::INTERNAL, false],
+        [:private, ProjectFeature::DISABLED, :external,         Snippet::PRIVATE,  false],
+
+        [:private, ProjectFeature::DISABLED, :non_member,       Snippet::PUBLIC,   false],
+        [:private, ProjectFeature::DISABLED, :non_member,       Snippet::INTERNAL, false],
+        [:private, ProjectFeature::DISABLED, :non_member,       Snippet::PRIVATE,  false],
+
+        [:private, ProjectFeature::DISABLED, :member,           Snippet::PUBLIC,   false],
+        [:private, ProjectFeature::DISABLED, :member,           Snippet::INTERNAL, false],
+        [:private, ProjectFeature::DISABLED, :member,           Snippet::PRIVATE,  false],
+
+        [:private, ProjectFeature::DISABLED, :author,           Snippet::PUBLIC,   false],
+        [:private, ProjectFeature::DISABLED, :author,           Snippet::INTERNAL, false],
+        [:private, ProjectFeature::DISABLED, :author,           Snippet::PRIVATE,  false]
+      ]
+    end
+
+    with_them do
+      let!(:project) { create(:project, visibility_level: Gitlab::VisibilityLevel.level_value(project_type.to_s)) }
+      let!(:project_feature) { project.project_feature.update_column(:snippets_access_level, feature_visibility) }
+      let!(:user) { users[user_type] }
+      let!(:snippet) { create(:project_snippet, visibility_level: snippet_type, project: project, author: author) }
+      let!(:members) do
+        project.add_developer(author)
+        project.add_developer(member)
+        project.add_developer(external) if project.private?
+      end
+
+      context "For #{params[:project_type]} project and #{params[:user_type]} users" do
+        it 'should agree with the read_project_snippet policy' do
+          expect(can?(user, :read_project_snippet, snippet)).to eq(outcome)
+        end
+
+        it 'should return proper outcome' do
+          results = described_class.new(user, project: project).execute
+
+          expect(results.include?(snippet)).to eq(outcome)
+        end
+      end
+
+      context "Without a given project and #{params[:user_type]} users" do
+        it 'should return proper outcome' do
+          results = described_class.new(user).execute
+          expect(results.include?(snippet)).to eq(outcome)
+        end
+
+        it 'returns no snippets when the user cannot read cross project' do
+          allow(Ability).to receive(:allowed?).and_call_original
+          allow(Ability).to receive(:allowed?).with(user, :read_cross_project) { false }
+
+          snippets = described_class.new(user).execute
+
+          expect(snippets).to be_empty
+        end
+      end
+    end
+  end
+
+  context 'For personal snippets' do
+    let!(:users) do
+      {
+        unauthenticated: nil,
+        external: external,
+        non_member: create(:user),
+        author: author
+      }
+    end
+
+    where(:snippet_visibility, :user_type, :outcome) do
+      [
+        [:public,   :unauthenticated, true],
+        [:public,   :external,        true],
+        [:public,   :non_member,      true],
+        [:public,   :author,          true],
+
+        [:internal, :unauthenticated, false],
+        [:internal, :external,        false],
+        [:internal, :non_member,      true],
+        [:internal, :author,          true],
+
+        [:private,  :unauthenticated, false],
+        [:private,  :external,        false],
+        [:private,  :non_member,      false],
+        [:private,  :author,          true]
+      ]
+    end
+
+    with_them do
+      let!(:user) { users[user_type] }
+      let!(:snippet) { create(:personal_snippet, visibility_level: snippet_type_visibilities[snippet_visibility], author: author) }
+
+      context "For personal and #{params[:snippet_visibility]} snippets with #{params[:user_type]} user" do
+        it 'should agree with read_personal_snippet policy' do
+          expect(can?(user, :read_personal_snippet, snippet)).to eq(outcome)
+        end
+
+        it 'should return proper outcome' do
+          results = described_class.new(user).execute
+          expect(results.include?(snippet)).to eq(outcome)
+        end
+
+        it 'should return personal snippets when the user cannot read cross project' do
+          allow(Ability).to receive(:allowed?).and_call_original
+          allow(Ability).to receive(:allowed?).with(user, :read_cross_project) { false }
+
+          results = described_class.new(user).execute
+
+          expect(results.include?(snippet)).to eq(outcome)
+        end
+      end
+    end
+  end
+end
-- 
2.30.9