Render repository blob when it exists

If the feature flag is enabled, the snippet has
a repository attached and it's not empty, then we'll use
the blob stored in the repository. If not, we show the snippet
stored in the database.

This behavior will change once we perform the migration.
parent 8de9414e
......@@ -6,20 +6,18 @@ module SnippetsActions
def edit
end
# rubocop:disable Gitlab/ModuleWithInstanceVariables
def raw
disposition = params[:inline] == 'false' ? 'attachment' : 'inline'
workhorse_set_content_type!
send_data(
convert_line_endings(@snippet.content),
convert_line_endings(blob.data),
type: 'text/plain; charset=utf-8',
disposition: disposition,
filename: @snippet.sanitized_file_name
filename: Snippet.sanitized_file_name(blob.name)
)
end
# rubocop:enable Gitlab/ModuleWithInstanceVariables
def js_request?
request.format.js?
......
......@@ -66,7 +66,6 @@ class Projects::SnippetsController < Projects::ApplicationController
end
def show
blob = @snippet.blob
conditionally_expand_blob(blob)
respond_to do |format|
......@@ -115,6 +114,16 @@ class Projects::SnippetsController < Projects::ApplicationController
alias_method :awardable, :snippet
alias_method :spammable, :snippet
def blob
return unless snippet
@blob ||= if Feature.enabled?(:version_snippets, current_user) && !snippet.repository.empty?
snippet.blobs.first
else
snippet.blob
end
end
def spammable_path
project_snippet_path(@project, @snippet)
end
......
......@@ -68,17 +68,15 @@ class SnippetsController < ApplicationController
end
def show
blob = @snippet.blob
conditionally_expand_blob(blob)
respond_to do |format|
format.html do
@note = Note.new(noteable: @snippet)
@noteable = @snippet
@discussions = @snippet.discussions
@notes = prepare_notes_for_rendering(@discussions.flat_map(&:notes), @noteable)
respond_to do |format|
format.html do
render 'show'
end
......@@ -121,6 +119,16 @@ class SnippetsController < ApplicationController
alias_method :awardable, :snippet
alias_method :spammable, :snippet
def blob
return unless snippet
@blob ||= if Feature.enabled?(:version_snippets, current_user) && !snippet.repository.empty?
snippet.blobs.first
else
snippet.blob
end
end
def spammable_path
snippet_path(@snippet)
end
......
......@@ -62,6 +62,7 @@ module Types
field :blob, type: Types::Snippets::BlobType,
description: 'Snippet blob',
calls_gitaly: true,
null: false
markdown_field :description_html, null: true, method: :description
......
# frozen_string_literal: true
# Applicable for blob classes with project attribute
module BlobLanguageFromGitAttributes
extend ActiveSupport::Concern
def language_from_gitattributes
return unless project
return unless repository&.exists?
repository = project.repository
repository.gitattribute(path, 'gitlab-language')
end
end
......@@ -197,7 +197,11 @@ class Snippet < ApplicationRecord
end
def blob
@blob ||= Blob.decorate(SnippetBlob.new(self), nil)
@blob ||= Blob.decorate(SnippetBlob.new(self), self)
end
def blobs
repository.ls_files(repository.root_ref).map { |file| Blob.lazy(self, repository.root_ref, file) }
end
def hook_attrs
......@@ -208,7 +212,7 @@ class Snippet < ApplicationRecord
super.to_s
end
def sanitized_file_name
def self.sanitized_file_name(file_name)
file_name.gsub(/[^a-zA-Z0-9_\-\.]+/, '')
end
......
......@@ -32,7 +32,7 @@ class SnippetBlobPresenter < BlobPresenter
end
def snippet
blob.snippet
blob.container
end
def language
......
......@@ -27,6 +27,14 @@ class SnippetPresenter < Gitlab::View::Presenter::Delegated
snippet.submittable_as_spam_by?(current_user)
end
def blob
if Feature.enabled?(:version_snippets, current_user) && !snippet.repository.empty?
snippet.blobs.first
else
snippet.blob
end
end
private
def can_access_resource?(ability_prefix)
......
......@@ -9,8 +9,7 @@
= render 'shared/snippets/header'
.project-snippets
%article.file-holder.snippet-file-content
= render 'shared/snippets/blob'
= render 'shared/snippets/blob', blob: @blob
.row-content-block.top-block.content-component-block
= render 'award_emoji/awards_block', awardable: @snippet, inline: true
......
- blob = @snippet.blob
.js-file-title.file-title-flex-parent
%article.file-holder.snippet-file-content
.js-file-title.file-title-flex-parent
= render 'projects/blob/header_content', blob: blob
.file-actions.d-none.d-sm-block
......@@ -10,4 +10,4 @@
= open_raw_blob_button(blob)
= download_raw_snippet_button(@snippet)
= render 'projects/blob/content', blob: blob
= render 'projects/blob/content', blob: blob
- blob = @snippet.blob
.gitlab-embed-snippets
.js-file-title.file-title-flex-parent
.file-header-content
......@@ -6,10 +5,10 @@
%strong.file-title-name
%a.gitlab-embedded-snippets-title{ href: url_for(only_path: false, overwrite_params: nil) }
= blob.name
= @blob.name
%small
= number_to_human_size(blob.raw_size)
= number_to_human_size(@blob.raw_size)
%a.gitlab-logo-wrapper{ href: url_for(only_path: false, overwrite_params: nil), title: 'view on gitlab' }
%img.gitlab-logo{ src: image_url('ext_snippet_icons/logo.svg'), alt: "GitLab logo" }
......@@ -19,4 +18,4 @@
= embedded_snippet_download_button
%article.file-holder.snippet-file-content
= render 'projects/blob/viewer', viewer: @snippet.blob.simple_viewer, load_async: false, external_embed: true
= render 'projects/blob/viewer', viewer: @blob.simple_viewer, load_async: false, external_embed: true
......@@ -10,8 +10,7 @@
= render 'shared/snippets/header'
.personal-snippets
%article.file-holder.snippet-file-content
= render 'shared/snippets/blob'
= render 'shared/snippets/blob', blob: @blob
.row-content-block.top-block.content-component-block
= render 'award_emoji/awards_block', awardable: @snippet, inline: true
......
---
title: Render single snippet blob in repository
merge_request: 23848
author:
type: added
......@@ -155,7 +155,7 @@ module Gitlab
end
def repository
@repository ||= project.repository
@repository ||= project&.repository
end
end
end
......
......@@ -3,9 +3,11 @@
require 'spec_helper'
describe Projects::SnippetsController do
include Gitlab::Routing
let_it_be(:user) { create(:user) }
let_it_be(:user2) { create(:user) }
let(:project) { create(:project_empty_repo, :public) }
let(:user) { create(:user) }
let(:user2) { create(:user) }
before do
project.add_maintainer(user)
......@@ -318,14 +320,45 @@ describe Projects::SnippetsController do
end
end
shared_examples 'successful response' do
it 'renders the snippet' do
subject
expect(assigns(:snippet)).to eq(project_snippet)
expect(response).to have_gitlab_http_status(:ok)
end
it 'renders the blob from the repository' do
subject
expect(assigns(:blob)).to eq(project_snippet.blobs.first)
end
context 'when feature flag version_snippets is disabled' do
before do
stub_feature_flags(version_snippets: false)
end
it 'returns the snippet database content' do
subject
blob = assigns(:blob)
expect(blob.data).to eq(project_snippet.content)
end
end
end
%w[show raw].each do |action|
describe "GET ##{action}" do
context 'when the project snippet is private' do
let(:project_snippet) { create(:project_snippet, :private, project: project, author: user) }
let(:project_snippet) { create(:project_snippet, :private, :repository, project: project, author: user) }
subject { get action, params: { namespace_id: project.namespace, project_id: project, id: project_snippet.to_param } }
context 'when anonymous' do
it 'responds with status 404' do
get action, params: { namespace_id: project.namespace, project_id: project, id: project_snippet.to_param }
subject
expect(response).to have_gitlab_http_status(:not_found)
end
......@@ -336,12 +369,7 @@ describe Projects::SnippetsController do
sign_in(user)
end
it 'renders the snippet' do
get action, params: { namespace_id: project.namespace, project_id: project, id: project_snippet.to_param }
expect(assigns(:snippet)).to eq(project_snippet)
expect(response).to have_gitlab_http_status(:ok)
end
it_behaves_like 'successful response'
end
context 'when signed in as a project member' do
......@@ -349,19 +377,16 @@ describe Projects::SnippetsController do
sign_in(user2)
end
it 'renders the snippet' do
get action, params: { namespace_id: project.namespace, project_id: project, id: project_snippet.to_param }
expect(assigns(:snippet)).to eq(project_snippet)
expect(response).to have_gitlab_http_status(:ok)
end
it_behaves_like 'successful response'
end
end
context 'when the project snippet does not exist' do
subject { get action, params: { namespace_id: project.namespace, project_id: project, id: 42 } }
context 'when anonymous' do
it 'responds with status 404' do
get action, params: { namespace_id: project.namespace, project_id: project, id: 42 }
subject
expect(response).to have_gitlab_http_status(:not_found)
end
......@@ -373,7 +398,7 @@ describe Projects::SnippetsController do
end
it 'responds with status 404' do
get action, params: { namespace_id: project.namespace, project_id: project, id: 42 }
subject
expect(response).to have_gitlab_http_status(:not_found)
end
......@@ -383,18 +408,20 @@ describe Projects::SnippetsController do
end
describe "GET #show for embeddable content" do
let(:project_snippet) { create(:project_snippet, snippet_permission, project: project, author: user) }
let(:project_snippet) { create(:project_snippet, :repository, snippet_permission, project: project, author: user) }
before do
sign_in(user)
get :show, params: { namespace_id: project.namespace, project_id: project, id: project_snippet.to_param }, format: :js
end
subject { get :show, params: { namespace_id: project.namespace, project_id: project, id: project_snippet.to_param }, format: :js }
context 'when snippet is private' do
let(:snippet_permission) { :private }
it 'responds with status 404' do
subject
expect(response).to have_gitlab_http_status(:not_found)
end
end
......@@ -402,10 +429,7 @@ describe Projects::SnippetsController do
context 'when snippet is public' do
let(:snippet_permission) { :public }
it 'responds with status 200' do
expect(assigns(:snippet)).to eq(project_snippet)
expect(response).to have_gitlab_http_status(:ok)
end
it_behaves_like 'successful response'
end
context 'when the project is private' do
......@@ -415,6 +439,8 @@ describe Projects::SnippetsController do
let(:project_snippet) { create(:project_snippet, :public, project: project, author: user) }
it 'responds with status 404' do
subject
expect(assigns(:snippet)).to eq(project_snippet)
expect(response).to have_gitlab_http_status(:not_found)
end
......@@ -423,14 +449,17 @@ describe Projects::SnippetsController do
end
describe 'GET #raw' do
let(:content) { "first line\r\nsecond line\r\nthird line" }
let(:formatted_content) { content.gsub(/\r\n/, "\n") }
let(:project_snippet) do
create(
:project_snippet, :public,
:project_snippet, :public, :repository,
project: project,
author: user,
content: "first line\r\nsecond line\r\nthird line"
content: content
)
end
let(:blob) { project_snippet.blobs.first }
context 'CRLF line ending' do
let(:params) do
......@@ -441,16 +470,22 @@ describe Projects::SnippetsController do
}
end
before do
allow_next_instance_of(Blob) do |instance|
allow(instance).to receive(:data).and_return(content)
end
end
it 'returns LF line endings by default' do
get :raw, params: params
expect(response.body).to eq("first line\nsecond line\nthird line")
expect(response.body).to eq(formatted_content)
end
it 'does not convert line endings when parameter present' do
get :raw, params: params.merge(line_ending: :raw)
expect(response.body).to eq("first line\r\nsecond line\r\nthird line")
expect(response.body).to eq(content)
end
end
end
......
......@@ -3,11 +3,9 @@
require 'spec_helper'
describe SnippetsController do
let(:user) { create(:user) }
let_it_be(:user) { create(:user) }
describe 'GET #index' do
let(:user) { create(:user) }
context 'when username parameter is present' do
it_behaves_like 'paginated collection' do
let(:collection) { Snippet.all }
......@@ -75,8 +73,37 @@ describe SnippetsController do
end
describe 'GET #show' do
shared_examples 'successful response' do
it 'renders the snippet' do
subject
expect(assigns(:snippet)).to eq(personal_snippet)
expect(response).to have_gitlab_http_status(:ok)
end
it 'renders the blob from the repository' do
subject
expect(assigns(:blob)).to eq(personal_snippet.blobs.first)
end
context 'when feature flag version_snippets is disabled' do
before do
stub_feature_flags(version_snippets: false)
end
it 'returns the snippet database content' do
subject
blob = assigns(:blob)
expect(blob.data).to eq(personal_snippet.content)
end
end
end
context 'when the personal snippet is private' do
let(:personal_snippet) { create(:personal_snippet, :private, author: user) }
let_it_be(:personal_snippet) { create(:personal_snippet, :private, :repository, author: user) }
context 'when signed in' do
before do
......@@ -95,11 +122,8 @@ describe SnippetsController do
end
context 'when signed in user is the author' do
it 'renders the snippet' do
get :show, params: { id: personal_snippet.to_param }
expect(assigns(:snippet)).to eq(personal_snippet)
expect(response).to have_gitlab_http_status(:ok)
it_behaves_like 'successful response' do
subject { get :show, params: { id: personal_snippet.to_param } }
end
it 'responds with status 404 when embeddable content is requested' do
......@@ -120,18 +144,15 @@ describe SnippetsController do
end
context 'when the personal snippet is internal' do
let(:personal_snippet) { create(:personal_snippet, :internal, author: user) }
let_it_be(:personal_snippet) { create(:personal_snippet, :internal, :repository, author: user) }
context 'when signed in' do
before do
sign_in(user)
end
it 'renders the snippet' do
get :show, params: { id: personal_snippet.to_param }
expect(assigns(:snippet)).to eq(personal_snippet)
expect(response).to have_gitlab_http_status(:ok)
it_behaves_like 'successful response' do
subject { get :show, params: { id: personal_snippet.to_param } }
end
it 'responds with status 404 when embeddable content is requested' do
......@@ -151,18 +172,15 @@ describe SnippetsController do
end
context 'when the personal snippet is public' do
let(:personal_snippet) { create(:personal_snippet, :public, author: user) }
let_it_be(:personal_snippet) { create(:personal_snippet, :public, :repository, author: user) }
context 'when signed in' do
before do
sign_in(user)
end
it 'renders the snippet' do
get :show, params: { id: personal_snippet.to_param }
expect(assigns(:snippet)).to eq(personal_snippet)
expect(response).to have_gitlab_http_status(:ok)
it_behaves_like 'successful response' do
subject { get :show, params: { id: personal_snippet.to_param } }
end
it 'responds with status 200 when embeddable content is requested' do
......@@ -481,8 +499,82 @@ describe SnippetsController do
end
describe "GET #raw" do
shared_examples '200 status' do
before do
subject
end
it 'responds with status 200' do
expect(assigns(:snippet)).to eq(snippet)
expect(response).to have_gitlab_http_status(:ok)
end
it 'has expected headers' do
expect(response.header['Content-Type']).to eq('text/plain; charset=utf-8')
expect(response.header['Content-Disposition']).to match(/inline/)
end
it "sets #{Gitlab::Workhorse::DETECT_HEADER} header" do
expect(response.header[Gitlab::Workhorse::DETECT_HEADER]).to eq 'true'
end
end
shared_examples 'CRLF line ending' do
let(:content) { "first line\r\nsecond line\r\nthird line" }
let(:formatted_content) { content.gsub(/\r\n/, "\n") }
let(:snippet) do
create(:personal_snippet, :public, :repository, author: user, content: content)
end
before do
allow_next_instance_of(Blob) do |instance|
allow(instance).to receive(:data).and_return(content)
end
subject
end
it 'returns LF line endings by default' do
expect(response.body).to eq(formatted_content)
end
context 'when parameter present' do
let(:params) { { id: snippet.to_param, line_ending: :raw } }
it 'does not convert line endings when parameter present' do
expect(response.body).to eq(content)
end
end
end
shared_examples 'successful response' do
it_behaves_like '200 status'
it_behaves_like 'CRLF line ending'
it 'returns snippet first blob data' do
subject
expect(response.body).to eq snippet.blobs.first.data
end
context 'when feature flag version_snippets is disabled' do
before do
stub_feature_flags(version_snippets: false)
end
it_behaves_like '200 status'
it_behaves_like 'CRLF line ending'
it 'returns snippet database content' do
subject
expect(response.body).to eq snippet.content
end
end
end
context 'when the personal snippet is private' do
let(:personal_snippet) { create(:personal_snippet, :private, author: user) }
let_it_be(:personal_snippet) { create(:personal_snippet, :private, :repository, author: user) }
context 'when signed in' do
before do
......@@ -501,24 +593,11 @@ describe SnippetsController do
end
context 'when signed in user is the author' do
before do
get :raw, params: { id: personal_snippet.to_param }
end
it 'responds with status 200' do
expect(assigns(:snippet)).to eq(personal_snippet)
expect(response).to have_gitlab_http_status(:ok)
end
it_behaves_like 'successful response' do
let(:snippet) { personal_snippet }
let(:params) { { id: snippet.to_param } }
it 'has expected headers' do
expect(response.header['Content-Type']).to eq('text/plain; charset=utf-8')
expect(response.header['Content-Disposition']).to match(/inline/)
end
it "sets #{Gitlab::Workhorse::DETECT_HEADER} header" do
expect(response).to have_gitlab_http_status(:ok)
expect(response.header[Gitlab::Workhorse::DETECT_HEADER]).to eq "true"
subject { get :raw, params: params }
end
end
end
......@@ -533,18 +612,18 @@ describe SnippetsController do
end
context 'when the personal snippet is internal' do
let(:personal_snippet) { create(:personal_snippet, :internal, author: user) }
let_it_be(:personal_snippet) { create(:personal_snippet, :internal, :repository, author: user) }
context 'when signed in' do
before do
sign_in(user)
end
it 'responds with status 200' do
get :raw, params: { id: personal_snippet.to_param }
it_behaves_like 'successful response' do
let(:snippet) { personal_snippet }
let(:params) { { id: snippet.to_param } }
expect(assigns(:snippet)).to eq(personal_snippet)
expect(response).to have_gitlab_http_status(:ok)
subject { get :raw, params: params }
end
end
......@@ -558,36 +637,18 @@ describe SnippetsController do
end
context 'when the personal snippet is public' do
let(:personal_snippet) { create(:personal_snippet, :public, author: user) }
let_it_be(:personal_snippet) { create(:personal_snippet, :public, :repository, author: user) }
context 'when signed in' do
before do
sign_in(user)
end
it 'responds with status 200' do
get :raw, params: { id: personal_snippet.to_param }
it_behaves_like 'successful response' do
let(:snippet) { personal_snippet }
let(:params) { { id: snippet.to_param } }
expect(assigns(:snippet)).to eq(personal_snippet)
expect(response).to have_gitlab_http_status(:ok)
end
context 'CRLF line ending' do
let(:personal_snippet) do
create(:personal_snippet, :public, author: user, content: "first line\r\nsecond line\r\nthird line")
end
it 'returns LF line endings by default' do
get :raw, params: { id: personal_snippet.to_param }
expect(response.body).to eq("first line\nsecond line\nthird line")
end
it 'does not convert line endings when parameter present' do
get :raw, params: { id: personal_snippet.to_param, line_ending: :raw }
expect(response.body).to eq("first line\r\nsecond line\r\nthird line")
end
subject { get :raw, params: params }
end
end
......
......@@ -3,9 +3,9 @@
require 'spec_helper'
describe 'Thread Comments Snippet', :js do
let(:user) { create(:user) }
let(:project) { create(:project) }
let(:snippet) { create(:project_snippet, :private, project: project, author: user) }
let_it_be(:user) { create(:user) }
let_it_be(:project) { create(:project) }
let_it_be(:snippet) { create(:project_snippet, :private, :repository, project: project, author: user) }
before do
stub_feature_flags(snippets_vue: false)
......
......@@ -5,8 +5,8 @@ require 'spec_helper'
describe 'Projects > Snippets > Create Snippet', :js do
include DropzoneHelper
let(:user) { create(:user) }
let(:project) { create(:project, :public) }
let_it_be(:user) { create(:user) }
let_it_be(:project) { create(:project, :public) }
def description_field
find('.js-description-input input,textarea')
......@@ -102,7 +102,7 @@ describe 'Projects > Snippets > Create Snippet', :js do
end
it 'shows a public snippet on the index page but not the New snippet button' do
snippet = create(:project_snippet, :public, project: project)
snippet = create(:project_snippet, :public, :repository, project: project)
visit project_snippets_path(project)
......
......@@ -3,9 +3,9 @@
require 'spec_helper'
describe 'Projects > Snippets > User comments on a snippet', :js do
let(:project) { create(:project) }
let!(:snippet) { create(:project_snippet, project: project, author: user) }
let(:user) { create(:user) }
let_it_be(:project) { create(:project) }
let_it_be(:user) { create(:user) }
let_it_be(:snippet) { create(:project_snippet, :repository, project: project, author: user) }
before do
stub_feature_flags(snippets_vue: false)
......
......@@ -3,8 +3,8 @@
require 'spec_helper'
describe 'Reportable note on snippets', :js do
let(:user) { create(:user) }
let(:project) { create(:project) }
let_it_be(:user) { create(:user) }
let_it_be(:project) { create(:project) }
before do
stub_feature_flags(snippets_vue: false)
......@@ -13,8 +13,8 @@ describe 'Reportable note on snippets', :js do
end
describe 'on project snippet' do
let(:snippet) { create(:project_snippet, :public, project: project, author: user) }
let!(:note) { create(:note_on_project_snippet, noteable: snippet, project: project) }
let_it_be(:snippet) { create(:project_snippet, :public, :repository, project: project, author: user) }
let_it_be(:note) { create(:note_on_project_snippet, noteable: snippet, project: project) }
before do
visit project_snippet_path(project, snippet)
......
......@@ -16,4 +16,47 @@ describe GitlabSchema.types['Snippet'] do
describe 'authorizations' do
it { expect(described_class).to require_graphql_authorizations(:read_snippet) }
end
describe '#blob' do
let_it_be(:user) { create(:user) }
let(:query_blob) { subject.dig('data', 'snippets', 'edges')[0]['node']['blob'] }
let(:query) do
%(
{
snippets {
edges {
node {
blob {
name
path
}
}
}
}
}
)
end
subject { GitlabSchema.execute(query, context: { current_user: user }).as_json }
context 'when snippet has repository' do
let!(:snippet) { create(:personal_snippet, :repository, :public, author: user) }
let(:blob) { snippet.blobs.first }
it 'returns blob from the repository' do
expect(query_blob['name']).to eq blob.name
expect(query_blob['path']).to eq blob.path
end
end
context 'when snippet does not have a repository' do
let!(:snippet) { create(:personal_snippet, :public, author: user) }
let(:blob) { snippet.blob }
it 'returns SnippetBlob type' do
expect(query_blob['name']).to eq blob.name
expect(query_blob['path']).to eq blob.path
end
end
end
end
......@@ -11,13 +11,20 @@ describe BlobLanguageFromGitAttributes do
subject(:blob) { fake_blob(path: 'file.md') }
it 'returns return value from gitattribute' do
expect(blob.project.repository).to receive(:gitattribute).with(blob.path, 'gitlab-language').and_return('erb?parent=json')
allow(blob.repository).to receive(:exists?).and_return(true)
expect(blob.repository).to receive(:gitattribute).with(blob.path, 'gitlab-language').and_return('erb?parent=json')
expect(blob.language_from_gitattributes).to eq('erb?parent=json')
end
it 'returns nil if project is absent' do
allow(blob).to receive(:project).and_return(nil)
it 'returns nil if repository is absent' do
allow(blob).to receive(:repository).and_return(nil)
expect(blob.language_from_gitattributes).to eq(nil)
end
it 'returns nil if repository does not exist' do
allow(blob.repository).to receive(:exists?).and_return(false)
expect(blob.language_from_gitattributes).to eq(nil)
end
......
......@@ -76,18 +76,18 @@ describe SnippetBlobPresenter do
context 'with ProjectSnippet' do
let!(:project) { create(:project) }
let(:snippet) { build(:project_snippet, project: project, id: 1) }
let(:snippet) { create(:project_snippet, project: project) }
it 'returns the raw path' do
expect(subject).to eq "/#{snippet.project.full_path}/snippets/1/raw"
expect(subject).to eq "/#{snippet.project.full_path}/snippets/#{snippet.id}/raw"
end
end
context 'with PersonalSnippet' do
let(:snippet) { build(:personal_snippet, id: 1) }
let(:snippet) { create(:personal_snippet) }
it 'returns the raw path' do
expect(subject).to eq "/snippets/1/raw"
expect(subject).to eq "/snippets/#{snippet.id}/raw"
end
end
end
......
......@@ -143,4 +143,24 @@ describe SnippetPresenter do
expect(subject).to be_truthy
end
end
describe '#blob' do
let(:snippet) { personal_snippet }
subject { presenter.blob }
context 'when snippet does not have a repository' do
it 'returns SnippetBlob' do
expect(subject).to eq snippet.blob
end
end
context 'when snippet has a repository' do
let(:snippet) { create(:snippet, :repository, author: user) }
it 'returns repository first blob' do
expect(subject).to eq snippet.blobs.first
end
end
end
end
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment