summaryrefslogtreecommitdiff
path: root/spec/lib
diff options
context:
space:
mode:
authorDouwe Maan <douwe@selenight.nl>2017-04-06 15:00:26 -0500
committerDouwe Maan <douwe@selenight.nl>2017-04-06 15:00:26 -0500
commit792f6ed16fbbbfcb7badc00ab3bffa7498bb6407 (patch)
tree40250aa0b51e7e3ca560a8106f8c084ffa130674 /spec/lib
parent1065ba4097bd50720ef8777d05c36f2e79048d46 (diff)
parent00e00cacf8cb4ce3bfb733bae47e7e594e91e294 (diff)
downloadgitlab-ce-792f6ed16fbbbfcb7badc00ab3bffa7498bb6407.tar.gz
Merge branch 'master' into new-resolvable-discussion
# Conflicts: # app/assets/javascripts/filtered_search/dropdown_hint.js # app/views/shared/issuable/_search_bar.html.haml
Diffstat (limited to 'spec/lib')
-rw-r--r--spec/lib/ci/ansi2html_spec.rb110
-rw-r--r--spec/lib/container_registry/blob_spec.rb115
-rw-r--r--spec/lib/container_registry/path_spec.rb212
-rw-r--r--spec/lib/container_registry/registry_spec.rb2
-rw-r--r--spec/lib/container_registry/repository_spec.rb65
-rw-r--r--spec/lib/container_registry/tag_spec.rb86
-rw-r--r--spec/lib/gitlab/ci/cron_parser_spec.rb116
-rw-r--r--spec/lib/gitlab/ci/trace/stream_spec.rb201
-rw-r--r--spec/lib/gitlab/ci/trace_reader_spec.rb52
-rw-r--r--spec/lib/gitlab/ci/trace_spec.rb216
-rw-r--r--spec/lib/gitlab/etag_caching/middleware_spec.rb16
-rw-r--r--spec/lib/gitlab/import_export/all_models.yml9
-rw-r--r--spec/lib/gitlab/import_export/safe_model_attributes.yml11
-rw-r--r--spec/lib/microsoft_teams/activity_spec.rb16
-rw-r--r--spec/lib/microsoft_teams/notifier_spec.rb55
15 files changed, 1035 insertions, 247 deletions
diff --git a/spec/lib/ci/ansi2html_spec.rb b/spec/lib/ci/ansi2html_spec.rb
index 0762fd7e56a..a5dfb49478a 100644
--- a/spec/lib/ci/ansi2html_spec.rb
+++ b/spec/lib/ci/ansi2html_spec.rb
@@ -1,159 +1,160 @@
require 'spec_helper'
describe Ci::Ansi2html, lib: true do
- subject { Ci::Ansi2html }
+ subject { described_class }
it "prints non-ansi as-is" do
- expect(subject.convert("Hello")[:html]).to eq('Hello')
+ expect(convert_html("Hello")).to eq('Hello')
end
it "strips non-color-changing controll sequences" do
- expect(subject.convert("Hello \e[2Kworld")[:html]).to eq('Hello world')
+ expect(convert_html("Hello \e[2Kworld")).to eq('Hello world')
end
it "prints simply red" do
- expect(subject.convert("\e[31mHello\e[0m")[:html]).to eq('<span class="term-fg-red">Hello</span>')
+ expect(convert_html("\e[31mHello\e[0m")).to eq('<span class="term-fg-red">Hello</span>')
end
it "prints simply red without trailing reset" do
- expect(subject.convert("\e[31mHello")[:html]).to eq('<span class="term-fg-red">Hello</span>')
+ expect(convert_html("\e[31mHello")).to eq('<span class="term-fg-red">Hello</span>')
end
it "prints simply yellow" do
- expect(subject.convert("\e[33mHello\e[0m")[:html]).to eq('<span class="term-fg-yellow">Hello</span>')
+ expect(convert_html("\e[33mHello\e[0m")).to eq('<span class="term-fg-yellow">Hello</span>')
end
it "prints default on blue" do
- expect(subject.convert("\e[39;44mHello")[:html]).to eq('<span class="term-bg-blue">Hello</span>')
+ expect(convert_html("\e[39;44mHello")).to eq('<span class="term-bg-blue">Hello</span>')
end
it "prints red on blue" do
- expect(subject.convert("\e[31;44mHello")[:html]).to eq('<span class="term-fg-red term-bg-blue">Hello</span>')
+ expect(convert_html("\e[31;44mHello")).to eq('<span class="term-fg-red term-bg-blue">Hello</span>')
end
it "resets colors after red on blue" do
- expect(subject.convert("\e[31;44mHello\e[0m world")[:html]).to eq('<span class="term-fg-red term-bg-blue">Hello</span> world')
+ expect(convert_html("\e[31;44mHello\e[0m world")).to eq('<span class="term-fg-red term-bg-blue">Hello</span> world')
end
it "performs color change from red/blue to yellow/blue" do
- expect(subject.convert("\e[31;44mHello \e[33mworld")[:html]).to eq('<span class="term-fg-red term-bg-blue">Hello </span><span class="term-fg-yellow term-bg-blue">world</span>')
+ expect(convert_html("\e[31;44mHello \e[33mworld")).to eq('<span class="term-fg-red term-bg-blue">Hello </span><span class="term-fg-yellow term-bg-blue">world</span>')
end
it "performs color change from red/blue to yellow/green" do
- expect(subject.convert("\e[31;44mHello \e[33;42mworld")[:html]).to eq('<span class="term-fg-red term-bg-blue">Hello </span><span class="term-fg-yellow term-bg-green">world</span>')
+ expect(convert_html("\e[31;44mHello \e[33;42mworld")).to eq('<span class="term-fg-red term-bg-blue">Hello </span><span class="term-fg-yellow term-bg-green">world</span>')
end
it "performs color change from red/blue to reset to yellow/green" do
- expect(subject.convert("\e[31;44mHello\e[0m \e[33;42mworld")[:html]).to eq('<span class="term-fg-red term-bg-blue">Hello</span> <span class="term-fg-yellow term-bg-green">world</span>')
+ expect(convert_html("\e[31;44mHello\e[0m \e[33;42mworld")).to eq('<span class="term-fg-red term-bg-blue">Hello</span> <span class="term-fg-yellow term-bg-green">world</span>')
end
it "ignores unsupported codes" do
- expect(subject.convert("\e[51mHello\e[0m")[:html]).to eq('Hello')
+ expect(convert_html("\e[51mHello\e[0m")).to eq('Hello')
end
it "prints light red" do
- expect(subject.convert("\e[91mHello\e[0m")[:html]).to eq('<span class="term-fg-l-red">Hello</span>')
+ expect(convert_html("\e[91mHello\e[0m")).to eq('<span class="term-fg-l-red">Hello</span>')
end
it "prints default on light red" do
- expect(subject.convert("\e[101mHello\e[0m")[:html]).to eq('<span class="term-bg-l-red">Hello</span>')
+ expect(convert_html("\e[101mHello\e[0m")).to eq('<span class="term-bg-l-red">Hello</span>')
end
it "performs color change from red/blue to default/blue" do
- expect(subject.convert("\e[31;44mHello \e[39mworld")[:html]).to eq('<span class="term-fg-red term-bg-blue">Hello </span><span class="term-bg-blue">world</span>')
+ expect(convert_html("\e[31;44mHello \e[39mworld")).to eq('<span class="term-fg-red term-bg-blue">Hello </span><span class="term-bg-blue">world</span>')
end
it "performs color change from light red/blue to default/blue" do
- expect(subject.convert("\e[91;44mHello \e[39mworld")[:html]).to eq('<span class="term-fg-l-red term-bg-blue">Hello </span><span class="term-bg-blue">world</span>')
+ expect(convert_html("\e[91;44mHello \e[39mworld")).to eq('<span class="term-fg-l-red term-bg-blue">Hello </span><span class="term-bg-blue">world</span>')
end
it "prints bold text" do
- expect(subject.convert("\e[1mHello")[:html]).to eq('<span class="term-bold">Hello</span>')
+ expect(convert_html("\e[1mHello")).to eq('<span class="term-bold">Hello</span>')
end
it "resets bold text" do
- expect(subject.convert("\e[1mHello\e[21m world")[:html]).to eq('<span class="term-bold">Hello</span> world')
- expect(subject.convert("\e[1mHello\e[22m world")[:html]).to eq('<span class="term-bold">Hello</span> world')
+ expect(convert_html("\e[1mHello\e[21m world")).to eq('<span class="term-bold">Hello</span> world')
+ expect(convert_html("\e[1mHello\e[22m world")).to eq('<span class="term-bold">Hello</span> world')
end
it "prints italic text" do
- expect(subject.convert("\e[3mHello")[:html]).to eq('<span class="term-italic">Hello</span>')
+ expect(convert_html("\e[3mHello")).to eq('<span class="term-italic">Hello</span>')
end
it "resets italic text" do
- expect(subject.convert("\e[3mHello\e[23m world")[:html]).to eq('<span class="term-italic">Hello</span> world')
+ expect(convert_html("\e[3mHello\e[23m world")).to eq('<span class="term-italic">Hello</span> world')
end
it "prints underlined text" do
- expect(subject.convert("\e[4mHello")[:html]).to eq('<span class="term-underline">Hello</span>')
+ expect(convert_html("\e[4mHello")).to eq('<span class="term-underline">Hello</span>')
end
it "resets underlined text" do
- expect(subject.convert("\e[4mHello\e[24m world")[:html]).to eq('<span class="term-underline">Hello</span> world')
+ expect(convert_html("\e[4mHello\e[24m world")).to eq('<span class="term-underline">Hello</span> world')
end
it "prints concealed text" do
- expect(subject.convert("\e[8mHello")[:html]).to eq('<span class="term-conceal">Hello</span>')
+ expect(convert_html("\e[8mHello")).to eq('<span class="term-conceal">Hello</span>')
end
it "resets concealed text" do
- expect(subject.convert("\e[8mHello\e[28m world")[:html]).to eq('<span class="term-conceal">Hello</span> world')
+ expect(convert_html("\e[8mHello\e[28m world")).to eq('<span class="term-conceal">Hello</span> world')
end
it "prints crossed-out text" do
- expect(subject.convert("\e[9mHello")[:html]).to eq('<span class="term-cross">Hello</span>')
+ expect(convert_html("\e[9mHello")).to eq('<span class="term-cross">Hello</span>')
end
it "resets crossed-out text" do
- expect(subject.convert("\e[9mHello\e[29m world")[:html]).to eq('<span class="term-cross">Hello</span> world')
+ expect(convert_html("\e[9mHello\e[29m world")).to eq('<span class="term-cross">Hello</span> world')
end
it "can print 256 xterm fg colors" do
- expect(subject.convert("\e[38;5;16mHello")[:html]).to eq('<span class="xterm-fg-16">Hello</span>')
+ expect(convert_html("\e[38;5;16mHello")).to eq('<span class="xterm-fg-16">Hello</span>')
end
it "can print 256 xterm fg colors on normal magenta background" do
- expect(subject.convert("\e[38;5;16;45mHello")[:html]).to eq('<span class="xterm-fg-16 term-bg-magenta">Hello</span>')
+ expect(convert_html("\e[38;5;16;45mHello")).to eq('<span class="xterm-fg-16 term-bg-magenta">Hello</span>')
end
it "can print 256 xterm bg colors" do
- expect(subject.convert("\e[48;5;240mHello")[:html]).to eq('<span class="xterm-bg-240">Hello</span>')
+ expect(convert_html("\e[48;5;240mHello")).to eq('<span class="xterm-bg-240">Hello</span>')
end
it "can print 256 xterm bg colors on normal magenta foreground" do
- expect(subject.convert("\e[48;5;16;35mHello")[:html]).to eq('<span class="term-fg-magenta xterm-bg-16">Hello</span>')
+ expect(convert_html("\e[48;5;16;35mHello")).to eq('<span class="term-fg-magenta xterm-bg-16">Hello</span>')
end
it "prints bold colored text vividly" do
- expect(subject.convert("\e[1;31mHello\e[0m")[:html]).to eq('<span class="term-fg-l-red term-bold">Hello</span>')
+ expect(convert_html("\e[1;31mHello\e[0m")).to eq('<span class="term-fg-l-red term-bold">Hello</span>')
end
it "prints bold light colored text correctly" do
- expect(subject.convert("\e[1;91mHello\e[0m")[:html]).to eq('<span class="term-fg-l-red term-bold">Hello</span>')
+ expect(convert_html("\e[1;91mHello\e[0m")).to eq('<span class="term-fg-l-red term-bold">Hello</span>')
end
it "prints &lt;" do
- expect(subject.convert("<")[:html]).to eq('&lt;')
+ expect(convert_html("<")).to eq('&lt;')
end
it "replaces newlines with line break tags" do
- expect(subject.convert("\n")[:html]).to eq('<br>')
+ expect(convert_html("\n")).to eq('<br>')
end
it "groups carriage returns with newlines" do
- expect(subject.convert("\r\n")[:html]).to eq('<br>')
+ expect(convert_html("\r\n")).to eq('<br>')
end
describe "incremental update" do
shared_examples 'stateable converter' do
- let(:pass1) { subject.convert(pre_text) }
- let(:pass2) { subject.convert(pre_text + text, pass1[:state]) }
+ let(:pass1_stream) { StringIO.new(pre_text) }
+ let(:pass2_stream) { StringIO.new(pre_text + text) }
+ let(:pass1) { subject.convert(pass1_stream) }
+ let(:pass2) { subject.convert(pass2_stream, pass1.state) }
it "to returns html to append" do
- expect(pass2[:append]).to be_truthy
- expect(pass2[:html]).to eq(html)
- expect(pass1[:text] + pass2[:text]).to eq(pre_text + text)
- expect(pass1[:html] + pass2[:html]).to eq(pre_html + html)
+ expect(pass2.append).to be_truthy
+ expect(pass2.html).to eq(html)
+ expect(pass1.html + pass2.html).to eq(pre_html + html)
end
end
@@ -193,4 +194,27 @@ describe Ci::Ansi2html, lib: true do
it_behaves_like 'stateable converter'
end
end
+
+ describe "truncates" do
+ let(:text) { "Hello World" }
+ let(:stream) { StringIO.new(text) }
+ let(:subject) { described_class.convert(stream) }
+
+ before do
+ stream.seek(3, IO::SEEK_SET)
+ end
+
+ it "returns truncated output" do
+ expect(subject.truncated).to be_truthy
+ end
+
+ it "does not append output" do
+ expect(subject.append).to be_falsey
+ end
+ end
+
+ def convert_html(data)
+ stream = StringIO.new(data)
+ subject.convert(stream).html
+ end
end
diff --git a/spec/lib/container_registry/blob_spec.rb b/spec/lib/container_registry/blob_spec.rb
index bbacdc67ebd..f06e5fd54a2 100644
--- a/spec/lib/container_registry/blob_spec.rb
+++ b/spec/lib/container_registry/blob_spec.rb
@@ -1,110 +1,121 @@
require 'spec_helper'
describe ContainerRegistry::Blob do
- let(:digest) { 'sha256:0123456789012345' }
+ let(:group) { create(:group, name: 'group') }
+ let(:project) { create(:empty_project, path: 'test', group: group) }
+
+ let(:repository) do
+ create(:container_repository, name: 'image',
+ tags: %w[latest rc1],
+ project: project)
+ end
+
let(:config) do
- {
- 'digest' => digest,
+ { 'digest' => 'sha256:0123456789012345',
'mediaType' => 'binary',
- 'size' => 1000
- }
+ 'size' => 1000 }
+ end
+
+ let(:blob) { described_class.new(repository, config) }
+
+ before do
+ stub_container_registry_config(enabled: true,
+ api_url: 'http://registry.gitlab',
+ host_port: 'registry.gitlab')
end
- let(:token) { 'authorization-token' }
-
- let(:registry) { ContainerRegistry::Registry.new('http://example.com', token: token) }
- let(:repository) { registry.repository('group/test') }
- let(:blob) { repository.blob(config) }
it { expect(blob).to respond_to(:repository) }
it { expect(blob).to delegate_method(:registry).to(:repository) }
it { expect(blob).to delegate_method(:client).to(:repository) }
- context '#path' do
- subject { blob.path }
-
- it { is_expected.to eq('example.com/group/test@sha256:0123456789012345') }
+ describe '#path' do
+ it 'returns a valid path to the blob' do
+ expect(blob.path).to eq('group/test/image@sha256:0123456789012345')
+ end
end
- context '#digest' do
- subject { blob.digest }
-
- it { is_expected.to eq(digest) }
+ describe '#digest' do
+ it 'return correct digest value' do
+ expect(blob.digest).to eq 'sha256:0123456789012345'
+ end
end
- context '#type' do
- subject { blob.type }
-
- it { is_expected.to eq('binary') }
+ describe '#type' do
+ it 'returns a correct type' do
+ expect(blob.type).to eq 'binary'
+ end
end
- context '#revision' do
- subject { blob.revision }
-
- it { is_expected.to eq('0123456789012345') }
+ describe '#revision' do
+ it 'returns a correct blob SHA' do
+ expect(blob.revision).to eq '0123456789012345'
+ end
end
- context '#short_revision' do
- subject { blob.short_revision }
-
- it { is_expected.to eq('012345678') }
+ describe '#short_revision' do
+ it 'return a short SHA' do
+ expect(blob.short_revision).to eq '012345678'
+ end
end
- context '#delete' do
+ describe '#delete' do
before do
- stub_request(:delete, 'http://example.com/v2/group/test/blobs/sha256:0123456789012345').
- to_return(status: 200)
+ stub_request(:delete, 'http://registry.gitlab/v2/group/test/image/blobs/sha256:0123456789012345')
+ .to_return(status: 200)
end
- subject { blob.delete }
-
- it { is_expected.to be_truthy }
+ it 'returns true when blob has been successfuly deleted' do
+ expect(blob.delete).to be_truthy
+ end
end
- context '#data' do
- let(:data) { '{"key":"value"}' }
-
- subject { blob.data }
-
+ describe '#data' do
context 'when locally stored' do
before do
- stub_request(:get, 'http://example.com/v2/group/test/blobs/sha256:0123456789012345').
+ stub_request(:get, 'http://registry.gitlab/v2/group/test/image/blobs/sha256:0123456789012345').
to_return(
status: 200,
headers: { 'Content-Type' => 'application/json' },
- body: data)
+ body: '{"key":"value"}')
end
- it { is_expected.to eq(data) }
+ it 'returns a correct blob data' do
+ expect(blob.data).to eq '{"key":"value"}'
+ end
end
context 'when externally stored' do
+ let(:location) { 'http://external.com/blob/file' }
+
before do
- stub_request(:get, 'http://example.com/v2/group/test/blobs/sha256:0123456789012345').
- with(headers: { 'Authorization' => "bearer #{token}" }).
- to_return(
+ stub_request(:get, 'http://registry.gitlab/v2/group/test/image/blobs/sha256:0123456789012345')
+ .with(headers: { 'Authorization' => 'bearer token' })
+ .to_return(
status: 307,
headers: { 'Location' => location })
end
context 'for a valid address' do
- let(:location) { 'http://external.com/blob/file' }
-
before do
stub_request(:get, location).
with(headers: { 'Authorization' => nil }).
to_return(
status: 200,
headers: { 'Content-Type' => 'application/json' },
- body: data)
+ body: '{"key":"value"}')
end
- it { is_expected.to eq(data) }
+ it 'returns correct data' do
+ expect(blob.data).to eq '{"key":"value"}'
+ end
end
context 'for invalid file' do
let(:location) { 'file:///etc/passwd' }
- it { expect{ subject }.to raise_error(ArgumentError, 'invalid address') }
+ it 'raises an error' do
+ expect { blob.data }.to raise_error(ArgumentError, 'invalid address')
+ end
end
end
end
diff --git a/spec/lib/container_registry/path_spec.rb b/spec/lib/container_registry/path_spec.rb
new file mode 100644
index 00000000000..b9c4572c269
--- /dev/null
+++ b/spec/lib/container_registry/path_spec.rb
@@ -0,0 +1,212 @@
+require 'spec_helper'
+
+describe ContainerRegistry::Path do
+ subject { described_class.new(path) }
+
+ describe '#components' do
+ let(:path) { 'path/to/some/project' }
+
+ it 'splits components by a forward slash' do
+ expect(subject.components).to eq %w[path to some project]
+ end
+ end
+
+ describe '#nodes' do
+ context 'when repository path is valid' do
+ let(:path) { 'path/to/some/project' }
+
+ it 'return all project path like node in reverse order' do
+ expect(subject.nodes).to eq %w[path/to/some/project
+ path/to/some
+ path/to]
+ end
+ end
+
+ context 'when repository path is invalid' do
+ let(:path) { '' }
+
+ it 'rasises en error' do
+ expect { subject.nodes }
+ .to raise_error described_class::InvalidRegistryPathError
+ end
+ end
+ end
+
+ describe '#to_s' do
+ let(:path) { 'some/image' }
+
+ it 'return a string with a repository path' do
+ expect(subject.to_s).to eq path
+ end
+ end
+
+ describe '#valid?' do
+ context 'when path has less than two components' do
+ let(:path) { 'something/' }
+
+ it { is_expected.not_to be_valid }
+ end
+
+ context 'when path has more than allowed number of components' do
+ let(:path) { 'a/b/c/d/e/f/g/h/i/j/k/l/m/n/o/p/r/s/t/u/w/y/z' }
+
+ it { is_expected.not_to be_valid }
+ end
+
+ context 'when path has invalid characters' do
+ let(:path) { 'some\path' }
+
+ it { is_expected.not_to be_valid }
+ end
+
+ context 'when path has two or more components' do
+ let(:path) { 'some/path' }
+
+ it { is_expected.to be_valid }
+ end
+
+ context 'when path is related to multi-level image' do
+ let(:path) { 'some/path/my/image' }
+
+ it { is_expected.to be_valid }
+ end
+ end
+
+ describe '#has_repository?' do
+ context 'when project exists' do
+ let(:project) { create(:empty_project) }
+ let(:path) { "#{project.full_path}/my/image" }
+
+ context 'when path already has matching repository' do
+ before do
+ create(:container_repository, project: project, name: 'my/image')
+ end
+
+ it { is_expected.to have_repository }
+ it { is_expected.to have_project }
+ end
+
+ context 'when path does not have matching repository' do
+ it { is_expected.not_to have_repository }
+ it { is_expected.to have_project }
+ end
+ end
+
+ context 'when project does not exist' do
+ let(:path) { 'some/project/my/image' }
+
+ it { is_expected.not_to have_repository }
+ it { is_expected.not_to have_project }
+ end
+ end
+
+ describe '#repository_project' do
+ let(:group) { create(:group, path: 'some_group') }
+
+ context 'when project for given path exists' do
+ let(:path) { 'some_group/some_project' }
+
+ before do
+ create(:empty_project, group: group, name: 'some_project')
+ create(:empty_project, name: 'some_project')
+ end
+
+ it 'returns a correct project' do
+ expect(subject.repository_project.group).to eq group
+ end
+ end
+
+ context 'when project for given path does not exist' do
+ let(:path) { 'not/matching' }
+
+ it 'returns nil' do
+ expect(subject.repository_project).to be_nil
+ end
+ end
+
+ context 'when matching multi-level path' do
+ let(:project) do
+ create(:empty_project, group: group, name: 'some_project')
+ end
+
+ context 'when using the zero-level path' do
+ let(:path) { project.full_path }
+
+ it 'supports zero-level path' do
+ expect(subject.repository_project).to eq project
+ end
+ end
+
+ context 'when using first-level path' do
+ let(:path) { "#{project.full_path}/repository" }
+
+ it 'supports first-level path' do
+ expect(subject.repository_project).to eq project
+ end
+ end
+
+ context 'when using second-level path' do
+ let(:path) { "#{project.full_path}/repository/name" }
+
+ it 'supports second-level path' do
+ expect(subject.repository_project).to eq project
+ end
+ end
+
+ context 'when using too deep nesting in the path' do
+ let(:path) { "#{project.full_path}/repository/name/invalid" }
+
+ it 'does not support three-levels of nesting' do
+ expect(subject.repository_project).to be_nil
+ end
+ end
+ end
+ end
+
+ describe '#repository_name' do
+ context 'when project does not exist' do
+ let(:path) { 'some/name' }
+
+ it 'returns nil' do
+ expect(subject.repository_name).to be_nil
+ end
+ end
+
+ context 'when project exists' do
+ let(:group) { create(:group, path: 'some_group') }
+
+ let(:project) do
+ create(:empty_project, group: group, name: 'some_project')
+ end
+
+ before do
+ allow(path).to receive(:repository_project)
+ .and_return(project)
+ end
+
+ context 'when project path equal repository path' do
+ let(:path) { 'some_group/some_project' }
+
+ it 'returns an empty string' do
+ expect(subject.repository_name).to eq ''
+ end
+ end
+
+ context 'when repository path has one additional level' do
+ let(:path) { 'some_group/some_project/repository' }
+
+ it 'returns a correct repository name' do
+ expect(subject.repository_name).to eq 'repository'
+ end
+ end
+
+ context 'when repository path has two additional levels' do
+ let(:path) { 'some_group/some_project/repository/image' }
+
+ it 'returns a correct repository name' do
+ expect(subject.repository_name).to eq 'repository/image'
+ end
+ end
+ end
+ end
+end
diff --git a/spec/lib/container_registry/registry_spec.rb b/spec/lib/container_registry/registry_spec.rb
index 4f3f8b24fc4..4d6eea94bf0 100644
--- a/spec/lib/container_registry/registry_spec.rb
+++ b/spec/lib/container_registry/registry_spec.rb
@@ -10,7 +10,7 @@ describe ContainerRegistry::Registry do
it { is_expected.to respond_to(:uri) }
it { is_expected.to respond_to(:path) }
- it { expect(subject.repository('test')).not_to be_nil }
+ it { expect(subject).not_to be_nil }
context '#path' do
subject { registry.path }
diff --git a/spec/lib/container_registry/repository_spec.rb b/spec/lib/container_registry/repository_spec.rb
deleted file mode 100644
index c364e759108..00000000000
--- a/spec/lib/container_registry/repository_spec.rb
+++ /dev/null
@@ -1,65 +0,0 @@
-require 'spec_helper'
-
-describe ContainerRegistry::Repository do
- let(:registry) { ContainerRegistry::Registry.new('http://example.com') }
- let(:repository) { registry.repository('group/test') }
-
- it { expect(repository).to respond_to(:registry) }
- it { expect(repository).to delegate_method(:client).to(:registry) }
- it { expect(repository.tag('test')).not_to be_nil }
-
- context '#path' do
- subject { repository.path }
-
- it { is_expected.to eq('example.com/group/test') }
- end
-
- context 'manifest processing' do
- before do
- stub_request(:get, 'http://example.com/v2/group/test/tags/list').
- with(headers: { 'Accept' => 'application/vnd.docker.distribution.manifest.v2+json' }).
- to_return(
- status: 200,
- body: JSON.dump(tags: ['test']),
- headers: { 'Content-Type' => 'application/json' })
- end
-
- context '#manifest' do
- subject { repository.manifest }
-
- it { is_expected.not_to be_nil }
- end
-
- context '#valid?' do
- subject { repository.valid? }
-
- it { is_expected.to be_truthy }
- end
-
- context '#tags' do
- subject { repository.tags }
-
- it { is_expected.not_to be_empty }
- end
- end
-
- context '#delete_tags' do
- let(:tag) { ContainerRegistry::Tag.new(repository, 'tag') }
-
- before { expect(repository).to receive(:tags).twice.and_return([tag]) }
-
- subject { repository.delete_tags }
-
- context 'succeeds' do
- before { expect(tag).to receive(:delete).and_return(true) }
-
- it { is_expected.to be_truthy }
- end
-
- context 'any fails' do
- before { expect(tag).to receive(:delete).and_return(false) }
-
- it { is_expected.to be_falsey }
- end
- end
-end
diff --git a/spec/lib/container_registry/tag_spec.rb b/spec/lib/container_registry/tag_spec.rb
index c5e31ae82b6..bc1912d8e6c 100644
--- a/spec/lib/container_registry/tag_spec.rb
+++ b/spec/lib/container_registry/tag_spec.rb
@@ -1,25 +1,59 @@
require 'spec_helper'
describe ContainerRegistry::Tag do
- let(:registry) { ContainerRegistry::Registry.new('http://example.com') }
- let(:repository) { registry.repository('group/test') }
- let(:tag) { repository.tag('tag') }
- let(:headers) { { 'Accept' => 'application/vnd.docker.distribution.manifest.v2+json' } }
+ let(:group) { create(:group, name: 'group') }
+ let(:project) { create(:project, path: 'test', group: group) }
+
+ let(:repository) do
+ create(:container_repository, name: '', project: project)
+ end
+
+ let(:headers) do
+ { 'Accept' => 'application/vnd.docker.distribution.manifest.v2+json' }
+ end
+
+ let(:tag) { described_class.new(repository, 'tag') }
+
+ before do
+ stub_container_registry_config(enabled: true,
+ api_url: 'http://registry.gitlab',
+ host_port: 'registry.gitlab')
+ end
it { expect(tag).to respond_to(:repository) }
it { expect(tag).to delegate_method(:registry).to(:repository) }
it { expect(tag).to delegate_method(:client).to(:repository) }
- context '#path' do
- subject { tag.path }
+ describe '#path' do
+ context 'when tag belongs to zero-level repository' do
+ let(:repository) do
+ create(:container_repository, name: '',
+ tags: %w[rc1],
+ project: project)
+ end
+
+ it 'returns path to the image' do
+ expect(tag.path).to eq('group/test:tag')
+ end
+ end
+
+ context 'when tag belongs to first-level repository' do
+ let(:repository) do
+ create(:container_repository, name: 'my_image',
+ tags: %w[tag],
+ project: project)
+ end
- it { is_expected.to eq('example.com/group/test:tag') }
+ it 'returns path to the image' do
+ expect(tag.path).to eq('group/test/my_image:tag')
+ end
+ end
end
context 'manifest processing' do
context 'schema v1' do
before do
- stub_request(:get, 'http://example.com/v2/group/test/manifests/tag').
+ stub_request(:get, 'http://registry.gitlab/v2/group/test/manifests/tag').
with(headers: headers).
to_return(
status: 200,
@@ -56,7 +90,7 @@ describe ContainerRegistry::Tag do
context 'schema v2' do
before do
- stub_request(:get, 'http://example.com/v2/group/test/manifests/tag').
+ stub_request(:get, 'http://registry.gitlab/v2/group/test/manifests/tag').
with(headers: headers).
to_return(
status: 200,
@@ -93,7 +127,7 @@ describe ContainerRegistry::Tag do
context 'when locally stored' do
before do
- stub_request(:get, 'http://example.com/v2/group/test/blobs/sha256:d7a513a663c1a6dcdba9ed832ca53c02ac2af0c333322cd6ca92936d1d9917ac').
+ stub_request(:get, 'http://registry.gitlab/v2/group/test/blobs/sha256:d7a513a663c1a6dcdba9ed832ca53c02ac2af0c333322cd6ca92936d1d9917ac').
with(headers: { 'Accept' => 'application/octet-stream' }).
to_return(
status: 200,
@@ -105,7 +139,7 @@ describe ContainerRegistry::Tag do
context 'when externally stored' do
before do
- stub_request(:get, 'http://example.com/v2/group/test/blobs/sha256:d7a513a663c1a6dcdba9ed832ca53c02ac2af0c333322cd6ca92936d1d9917ac').
+ stub_request(:get, 'http://registry.gitlab/v2/group/test/blobs/sha256:d7a513a663c1a6dcdba9ed832ca53c02ac2af0c333322cd6ca92936d1d9917ac').
with(headers: { 'Accept' => 'application/octet-stream' }).
to_return(
status: 307,
@@ -123,29 +157,29 @@ describe ContainerRegistry::Tag do
end
end
- context 'manifest digest' do
+ context 'with stubbed digest' do
before do
- stub_request(:head, 'http://example.com/v2/group/test/manifests/tag').
- with(headers: headers).
- to_return(status: 200, headers: { 'Docker-Content-Digest' => 'sha256:digest' })
+ stub_request(:head, 'http://registry.gitlab/v2/group/test/manifests/tag')
+ .with(headers: headers)
+ .to_return(status: 200, headers: { 'Docker-Content-Digest' => 'sha256:digest' })
end
- context '#digest' do
- subject { tag.digest }
-
- it { is_expected.to eq('sha256:digest') }
+ describe '#digest' do
+ it 'returns a correct tag digest' do
+ expect(tag.digest).to eq 'sha256:digest'
+ end
end
- context '#delete' do
+ describe '#delete' do
before do
- stub_request(:delete, 'http://example.com/v2/group/test/manifests/sha256:digest').
- with(headers: headers).
- to_return(status: 200)
+ stub_request(:delete, 'http://registry.gitlab/v2/group/test/manifests/sha256:digest')
+ .with(headers: headers)
+ .to_return(status: 200)
end
- subject { tag.delete }
-
- it { is_expected.to be_truthy }
+ it 'correctly deletes the tag' do
+ expect(tag.delete).to be_truthy
+ end
end
end
end
diff --git a/spec/lib/gitlab/ci/cron_parser_spec.rb b/spec/lib/gitlab/ci/cron_parser_spec.rb
new file mode 100644
index 00000000000..0864bc7258d
--- /dev/null
+++ b/spec/lib/gitlab/ci/cron_parser_spec.rb
@@ -0,0 +1,116 @@
+require 'spec_helper'
+
+describe Gitlab::Ci::CronParser do
+ shared_examples_for "returns time in the future" do
+ it { is_expected.to be > Time.now }
+ end
+
+ describe '#next_time_from' do
+ subject { described_class.new(cron, cron_timezone).next_time_from(Time.now) }
+
+ context 'when cron and cron_timezone are valid' do
+ context 'when specific time' do
+ let(:cron) { '3 4 5 6 *' }
+ let(:cron_timezone) { 'UTC' }
+
+ it_behaves_like "returns time in the future"
+
+ it 'returns exact time' do
+ expect(subject.min).to eq(3)
+ expect(subject.hour).to eq(4)
+ expect(subject.day).to eq(5)
+ expect(subject.month).to eq(6)
+ end
+ end
+
+ context 'when specific day of week' do
+ let(:cron) { '* * * * 0' }
+ let(:cron_timezone) { 'UTC' }
+
+ it_behaves_like "returns time in the future"
+
+ it 'returns exact day of week' do
+ expect(subject.wday).to eq(0)
+ end
+ end
+
+ context 'when slash used' do
+ let(:cron) { '*/10 */6 */10 */10 *' }
+ let(:cron_timezone) { 'UTC' }
+
+ it_behaves_like "returns time in the future"
+
+ it 'returns specific time' do
+ expect(subject.min).to be_in([0, 10, 20, 30, 40, 50])
+ expect(subject.hour).to be_in([0, 6, 12, 18])
+ expect(subject.day).to be_in([1, 11, 21, 31])
+ expect(subject.month).to be_in([1, 11])
+ end
+ end
+
+ context 'when range used' do
+ let(:cron) { '0,20,40 * 1-5 * *' }
+ let(:cron_timezone) { 'UTC' }
+
+ it_behaves_like "returns time in the future"
+
+ it 'returns specific time' do
+ expect(subject.min).to be_in([0, 20, 40])
+ expect(subject.day).to be_in((1..5).to_a)
+ end
+ end
+
+ context 'when cron_timezone is US/Pacific' do
+ let(:cron) { '0 0 * * *' }
+ let(:cron_timezone) { 'US/Pacific' }
+
+ it_behaves_like "returns time in the future"
+
+ it 'converts time in server time zone' do
+ expect(subject.hour).to eq((Time.zone.now.in_time_zone(cron_timezone).utc_offset / 60 / 60).abs)
+ end
+ end
+ end
+
+ context 'when cron and cron_timezone are invalid' do
+ let(:cron) { 'invalid_cron' }
+ let(:cron_timezone) { 'invalid_cron_timezone' }
+
+ it 'returns nil' do
+ is_expected.to be_nil
+ end
+ end
+ end
+
+ describe '#cron_valid?' do
+ subject { described_class.new(cron, Gitlab::Ci::CronParser::VALID_SYNTAX_SAMPLE_TIME_ZONE).cron_valid? }
+
+ context 'when cron is valid' do
+ let(:cron) { '* * * * *' }
+
+ it { is_expected.to eq(true) }
+ end
+
+ context 'when cron is invalid' do
+ let(:cron) { '*********' }
+
+ it { is_expected.to eq(false) }
+ end
+ end
+
+ describe '#cron_timezone_valid?' do
+ subject { described_class.new(Gitlab::Ci::CronParser::VALID_SYNTAX_SAMPLE_CRON, cron_timezone).cron_timezone_valid? }
+
+ context 'when cron is valid' do
+ let(:cron_timezone) { 'Europe/Istanbul' }
+
+ it { is_expected.to eq(true) }
+ end
+
+ context 'when cron is invalid' do
+ let(:cron_timezone) { 'Invalid-zone' }
+
+ it { is_expected.to eq(false) }
+ end
+ end
+end
diff --git a/spec/lib/gitlab/ci/trace/stream_spec.rb b/spec/lib/gitlab/ci/trace/stream_spec.rb
new file mode 100644
index 00000000000..f1a1a71c528
--- /dev/null
+++ b/spec/lib/gitlab/ci/trace/stream_spec.rb
@@ -0,0 +1,201 @@
+require 'spec_helper'
+
+describe Gitlab::Ci::Trace::Stream do
+ describe 'delegates' do
+ subject { described_class.new { nil } }
+
+ it { is_expected.to delegate_method(:close).to(:stream) }
+ it { is_expected.to delegate_method(:tell).to(:stream) }
+ it { is_expected.to delegate_method(:seek).to(:stream) }
+ it { is_expected.to delegate_method(:size).to(:stream) }
+ it { is_expected.to delegate_method(:path).to(:stream) }
+ it { is_expected.to delegate_method(:truncate).to(:stream) }
+ it { is_expected.to delegate_method(:valid?).to(:stream).as(:present?) }
+ it { is_expected.to delegate_method(:file?).to(:path).as(:present?) }
+ end
+
+ describe '#limit' do
+ let(:stream) do
+ described_class.new do
+ StringIO.new("12345678")
+ end
+ end
+
+ it 'if size is larger we start from beggining' do
+ stream.limit(10)
+
+ expect(stream.tell).to eq(0)
+ end
+
+ it 'if size is smaller we start from the end' do
+ stream.limit(2)
+
+ expect(stream.tell).to eq(6)
+ end
+ end
+
+ describe '#append' do
+ let(:stream) do
+ described_class.new do
+ StringIO.new("12345678")
+ end
+ end
+
+ it "truncates and append content" do
+ stream.append("89", 4)
+ stream.seek(0)
+
+ expect(stream.size).to eq(6)
+ expect(stream.raw).to eq("123489")
+ end
+ end
+
+ describe '#set' do
+ let(:stream) do
+ described_class.new do
+ StringIO.new("12345678")
+ end
+ end
+
+ before do
+ stream.set("8901")
+ end
+
+ it "overwrite content" do
+ stream.seek(0)
+
+ expect(stream.size).to eq(4)
+ expect(stream.raw).to eq("8901")
+ end
+ end
+
+ describe '#raw' do
+ let(:path) { __FILE__ }
+ let(:lines) { File.readlines(path) }
+ let(:stream) do
+ described_class.new do
+ File.open(path)
+ end
+ end
+
+ it 'returns all contents if last_lines is not specified' do
+ result = stream.raw
+
+ expect(result).to eq(lines.join)
+ expect(result.encoding).to eq(Encoding.default_external)
+ end
+
+ context 'limit max lines' do
+ before do
+ # specifying BUFFER_SIZE forces to seek backwards
+ allow(described_class).to receive(:BUFFER_SIZE)
+ .and_return(2)
+ end
+
+ it 'returns last few lines' do
+ result = stream.raw(last_lines: 2)
+
+ expect(result).to eq(lines.last(2).join)
+ expect(result.encoding).to eq(Encoding.default_external)
+ end
+
+ it 'returns everything if trying to get too many lines' do
+ result = stream.raw(last_lines: lines.size * 2)
+
+ expect(result).to eq(lines.join)
+ expect(result.encoding).to eq(Encoding.default_external)
+ end
+ end
+ end
+
+ describe '#html_with_state' do
+ let(:stream) do
+ described_class.new do
+ StringIO.new("1234")
+ end
+ end
+
+ it 'returns html content with state' do
+ result = stream.html_with_state
+
+ expect(result.html).to eq("1234")
+ end
+
+ context 'follow-up state' do
+ let!(:last_result) { stream.html_with_state }
+
+ before do
+ stream.append("5678", 4)
+ stream.seek(0)
+ end
+
+ it "returns appended trace" do
+ result = stream.html_with_state(last_result.state)
+
+ expect(result.append).to be_truthy
+ expect(result.html).to eq("5678")
+ end
+ end
+ end
+
+ describe '#html' do
+ let(:stream) do
+ described_class.new do
+ StringIO.new("12\n34\n56")
+ end
+ end
+
+ it "returns html" do
+ expect(stream.html).to eq("12<br>34<br>56")
+ end
+
+ it "returns html for last line only" do
+ expect(stream.html(last_lines: 1)).to eq("56")
+ end
+ end
+
+ describe '#extract_coverage' do
+ let(:stream) do
+ described_class.new do
+ StringIO.new(data)
+ end
+ end
+
+ subject { stream.extract_coverage(regex) }
+
+ context 'valid content & regex' do
+ let(:data) { 'Coverage 1033 / 1051 LOC (98.29%) covered' }
+ let(:regex) { '\(\d+.\d+\%\) covered' }
+
+ it { is_expected.to eq(98.29) }
+ end
+
+ context 'valid content & bad regex' do
+ let(:data) { 'Coverage 1033 / 1051 LOC (98.29%) covered\n' }
+ let(:regex) { 'very covered' }
+
+ it { is_expected.to be_nil }
+ end
+
+ context 'no coverage content & regex' do
+ let(:data) { 'No coverage for today :sad:' }
+ let(:regex) { '\(\d+.\d+\%\) covered' }
+
+ it { is_expected.to be_nil }
+ end
+
+ context 'multiple results in content & regex' do
+ let(:data) { ' (98.39%) covered. (98.29%) covered' }
+ let(:regex) { '\(\d+.\d+\%\) covered' }
+
+ it { is_expected.to eq(98.29) }
+ end
+
+ context 'using a regex capture' do
+ let(:data) { 'TOTAL 9926 3489 65%' }
+ let(:regex) { 'TOTAL\s+\d+\s+\d+\s+(\d{1,3}\%)' }
+
+ it { is_expected.to eq(65) }
+ end
+ end
+end
diff --git a/spec/lib/gitlab/ci/trace_reader_spec.rb b/spec/lib/gitlab/ci/trace_reader_spec.rb
deleted file mode 100644
index ff5551bf703..00000000000
--- a/spec/lib/gitlab/ci/trace_reader_spec.rb
+++ /dev/null
@@ -1,52 +0,0 @@
-require 'spec_helper'
-
-describe Gitlab::Ci::TraceReader do
- let(:path) { __FILE__ }
- let(:lines) { File.readlines(path) }
- let(:bytesize) { lines.sum(&:bytesize) }
-
- it 'returns last few lines' do
- 10.times do
- subject = build_subject
- last_lines = random_lines
-
- expected = lines.last(last_lines).join
- result = subject.read(last_lines: last_lines)
-
- expect(result).to eq(expected)
- expect(result.encoding).to eq(Encoding.default_external)
- end
- end
-
- it 'returns everything if trying to get too many lines' do
- result = build_subject.read(last_lines: lines.size * 2)
-
- expect(result).to eq(lines.join)
- expect(result.encoding).to eq(Encoding.default_external)
- end
-
- it 'returns all contents if last_lines is not specified' do
- result = build_subject.read
-
- expect(result).to eq(lines.join)
- expect(result.encoding).to eq(Encoding.default_external)
- end
-
- it 'raises an error if not passing an integer for last_lines' do
- expect do
- build_subject.read(last_lines: lines)
- end.to raise_error(ArgumentError)
- end
-
- def random_lines
- Random.rand(lines.size) + 1
- end
-
- def random_buffer
- Random.rand(bytesize) + 1
- end
-
- def build_subject
- described_class.new(__FILE__, buffer_size: random_buffer)
- end
-end
diff --git a/spec/lib/gitlab/ci/trace_spec.rb b/spec/lib/gitlab/ci/trace_spec.rb
new file mode 100644
index 00000000000..69e8dc9220d
--- /dev/null
+++ b/spec/lib/gitlab/ci/trace_spec.rb
@@ -0,0 +1,216 @@
+require 'spec_helper'
+
+describe Gitlab::Ci::Trace do
+ let(:build) { create(:ci_build) }
+ let(:trace) { described_class.new(build) }
+
+ describe "associations" do
+ it { expect(trace).to respond_to(:job) }
+ it { expect(trace).to delegate_method(:old_trace).to(:job) }
+ end
+
+ describe '#html' do
+ before do
+ trace.set("12\n34")
+ end
+
+ it "returns formatted html" do
+ expect(trace.html).to eq("12<br>34")
+ end
+
+ it "returns last line of formatted html" do
+ expect(trace.html(last_lines: 1)).to eq("34")
+ end
+ end
+
+ describe '#raw' do
+ before do
+ trace.set("12\n34")
+ end
+
+ it "returns raw output" do
+ expect(trace.raw).to eq("12\n34")
+ end
+
+ it "returns last line of raw output" do
+ expect(trace.raw(last_lines: 1)).to eq("34")
+ end
+ end
+
+ describe '#extract_coverage' do
+ let(:regex) { '\(\d+.\d+\%\) covered' }
+
+ before do
+ trace.set('Coverage 1033 / 1051 LOC (98.29%) covered')
+ end
+
+ it "returns valid coverage" do
+ expect(trace.extract_coverage(regex)).to eq(98.29)
+ end
+ end
+
+ describe '#set' do
+ before do
+ trace.set("12")
+ end
+
+ it "returns trace" do
+ expect(trace.raw).to eq("12")
+ end
+
+ context 'overwrite trace' do
+ before do
+ trace.set("34")
+ end
+
+ it "returns new trace" do
+ expect(trace.raw).to eq("34")
+ end
+ end
+
+ context 'runners token' do
+ let(:token) { 'my_secret_token' }
+
+ before do
+ build.project.update(runners_token: token)
+ trace.set(token)
+ end
+
+ it "hides token" do
+ expect(trace.raw).not_to include(token)
+ end
+ end
+
+ context 'hides build token' do
+ let(:token) { 'my_secret_token' }
+
+ before do
+ build.update(token: token)
+ trace.set(token)
+ end
+
+ it "hides token" do
+ expect(trace.raw).not_to include(token)
+ end
+ end
+ end
+
+ describe '#append' do
+ before do
+ trace.set("1234")
+ end
+
+ it "returns correct trace" do
+ expect(trace.append("56", 4)).to eq(6)
+ expect(trace.raw).to eq("123456")
+ end
+
+ context 'tries to append trace at different offset' do
+ it "fails with append" do
+ expect(trace.append("56", 2)).to eq(-4)
+ expect(trace.raw).to eq("1234")
+ end
+ end
+
+ context 'runners token' do
+ let(:token) { 'my_secret_token' }
+
+ before do
+ build.project.update(runners_token: token)
+ trace.append(token, 0)
+ end
+
+ it "hides token" do
+ expect(trace.raw).not_to include(token)
+ end
+ end
+
+ context 'build token' do
+ let(:token) { 'my_secret_token' }
+
+ before do
+ build.update(token: token)
+ trace.append(token, 0)
+ end
+
+ it "hides token" do
+ expect(trace.raw).not_to include(token)
+ end
+ end
+ end
+
+ describe 'trace handling' do
+ context 'trace does not exist' do
+ it { expect(trace.exist?).to be(false) }
+ end
+
+ context 'new trace path is used' do
+ before do
+ trace.send(:ensure_directory)
+
+ File.open(trace.send(:default_path), "w") do |file|
+ file.write("data")
+ end
+ end
+
+ it "trace exist" do
+ expect(trace.exist?).to be(true)
+ end
+
+ it "can be erased" do
+ trace.erase!
+ expect(trace.exist?).to be(false)
+ end
+ end
+
+ context 'deprecated path' do
+ let(:path) { trace.send(:deprecated_path) }
+
+ context 'with valid ci_id' do
+ before do
+ build.project.update(ci_id: 1000)
+
+ FileUtils.mkdir_p(File.dirname(path))
+
+ File.open(path, "w") do |file|
+ file.write("data")
+ end
+ end
+
+ it "trace exist" do
+ expect(trace.exist?).to be(true)
+ end
+
+ it "can be erased" do
+ trace.erase!
+ expect(trace.exist?).to be(false)
+ end
+ end
+
+ context 'without valid ci_id' do
+ it "does not return deprecated path" do
+ expect(path).to be_nil
+ end
+ end
+ end
+
+ context 'stored in database' do
+ before do
+ build.send(:write_attribute, :trace, "data")
+ end
+
+ it "trace exist" do
+ expect(trace.exist?).to be(true)
+ end
+
+ it "can be erased" do
+ trace.erase!
+ expect(trace.exist?).to be(false)
+ end
+
+ it "returns database data" do
+ expect(trace.raw).to eq("data")
+ end
+ end
+ end
+end
diff --git a/spec/lib/gitlab/etag_caching/middleware_spec.rb b/spec/lib/gitlab/etag_caching/middleware_spec.rb
index 6ec4360adc2..c872d8232b0 100644
--- a/spec/lib/gitlab/etag_caching/middleware_spec.rb
+++ b/spec/lib/gitlab/etag_caching/middleware_spec.rb
@@ -47,9 +47,9 @@ describe Gitlab::EtagCaching::Middleware do
it 'tracks "etag_caching_key_not_found" event' do
expect(Gitlab::Metrics).to receive(:add_event)
- .with(:etag_caching_middleware_used)
+ .with(:etag_caching_middleware_used, endpoint: 'issue_notes')
expect(Gitlab::Metrics).to receive(:add_event)
- .with(:etag_caching_key_not_found)
+ .with(:etag_caching_key_not_found, endpoint: 'issue_notes')
middleware.call(build_env(path, if_none_match))
end
@@ -93,9 +93,9 @@ describe Gitlab::EtagCaching::Middleware do
it 'tracks "etag_caching_cache_hit" event' do
expect(Gitlab::Metrics).to receive(:add_event)
- .with(:etag_caching_middleware_used)
+ .with(:etag_caching_middleware_used, endpoint: 'issue_notes')
expect(Gitlab::Metrics).to receive(:add_event)
- .with(:etag_caching_cache_hit)
+ .with(:etag_caching_cache_hit, endpoint: 'issue_notes')
middleware.call(build_env(path, if_none_match))
end
@@ -132,9 +132,9 @@ describe Gitlab::EtagCaching::Middleware do
mock_app_response
expect(Gitlab::Metrics).to receive(:add_event)
- .with(:etag_caching_middleware_used)
+ .with(:etag_caching_middleware_used, endpoint: 'issue_notes')
expect(Gitlab::Metrics).to receive(:add_event)
- .with(:etag_caching_resource_changed)
+ .with(:etag_caching_resource_changed, endpoint: 'issue_notes')
middleware.call(build_env(path, if_none_match))
end
@@ -150,9 +150,9 @@ describe Gitlab::EtagCaching::Middleware do
it 'tracks "etag_caching_header_missing" event' do
expect(Gitlab::Metrics).to receive(:add_event)
- .with(:etag_caching_middleware_used)
+ .with(:etag_caching_middleware_used, endpoint: 'issue_notes')
expect(Gitlab::Metrics).to receive(:add_event)
- .with(:etag_caching_header_missing)
+ .with(:etag_caching_header_missing, endpoint: 'issue_notes')
middleware.call(build_env(path, if_none_match))
end
diff --git a/spec/lib/gitlab/import_export/all_models.yml b/spec/lib/gitlab/import_export/all_models.yml
index 24654bf6afd..5aa2a6d4b1b 100644
--- a/spec/lib/gitlab/import_export/all_models.yml
+++ b/spec/lib/gitlab/import_export/all_models.yml
@@ -99,6 +99,9 @@ triggers:
- project
- trigger_requests
- owner
+- trigger_schedule
+trigger_schedule:
+- trigger
deploy_keys:
- user
- deploy_keys_projects
@@ -116,6 +119,9 @@ merge_access_levels:
- protected_branch
push_access_levels:
- protected_branch
+container_repositories:
+- project
+- name
project:
- taggings
- base_tags
@@ -143,6 +149,7 @@ project:
- asana_service
- gemnasium_service
- slack_service
+- microsoft_teams_service
- mattermost_service
- buildkite_service
- bamboo_service
@@ -194,6 +201,7 @@ project:
- runners
- variables
- triggers
+- trigger_schedules
- environments
- deployments
- project_feature
@@ -202,6 +210,7 @@ project:
- project_authorizations
- route
- statistics
+- container_repositories
- uploads
award_emoji:
- awardable
diff --git a/spec/lib/gitlab/import_export/safe_model_attributes.yml b/spec/lib/gitlab/import_export/safe_model_attributes.yml
index 1ad16a9b57d..0c43c5662e8 100644
--- a/spec/lib/gitlab/import_export/safe_model_attributes.yml
+++ b/spec/lib/gitlab/import_export/safe_model_attributes.yml
@@ -240,6 +240,17 @@ Ci::Trigger:
- updated_at
- owner_id
- description
+- ref
+Ci::TriggerSchedule:
+- id
+- project_id
+- trigger_id
+- deleted_at
+- created_at
+- updated_at
+- cron
+- cron_timezone
+- next_run_at
DeployKey:
- id
- user_id
diff --git a/spec/lib/microsoft_teams/activity_spec.rb b/spec/lib/microsoft_teams/activity_spec.rb
new file mode 100644
index 00000000000..7890ae2e7b0
--- /dev/null
+++ b/spec/lib/microsoft_teams/activity_spec.rb
@@ -0,0 +1,16 @@
+require 'spec_helper'
+
+describe MicrosoftTeams::Activity do
+ subject { described_class.new(title: 'title', subtitle: 'subtitle', text: 'text', image: 'image') }
+
+ describe '#prepare' do
+ it 'returns the correct JSON object' do
+ expect(subject.prepare).to eq({
+ 'activityTitle' => 'title',
+ 'activitySubtitle' => 'subtitle',
+ 'activityText' => 'text',
+ 'activityImage' => 'image'
+ })
+ end
+ end
+end
diff --git a/spec/lib/microsoft_teams/notifier_spec.rb b/spec/lib/microsoft_teams/notifier_spec.rb
new file mode 100644
index 00000000000..3035693812f
--- /dev/null
+++ b/spec/lib/microsoft_teams/notifier_spec.rb
@@ -0,0 +1,55 @@
+require 'spec_helper'
+
+describe MicrosoftTeams::Notifier do
+ subject { described_class.new(webhook_url) }
+
+ let(:webhook_url) { 'https://example.gitlab.com/'}
+ let(:header) { { 'Content-Type' => 'application/json' } }
+ let(:options) do
+ {
+ title: 'JohnDoe4/project2',
+ pretext: '[[JohnDoe4/project2](http://localhost/namespace2/gitlabhq)] Issue [#1 Awesome issue](http://localhost/namespace2/gitlabhq/issues/1) opened by user6',
+ activity: {
+ title: 'Issue opened by user6',
+ subtitle: 'in [JohnDoe4/project2](http://localhost/namespace2/gitlabhq)',
+ text: '[#1 Awesome issue](http://localhost/namespace2/gitlabhq/issues/1)',
+ image: 'http://someimage.com'
+ },
+ attachments: 'please fix'
+ }
+ end
+
+ let(:body) do
+ {
+ 'sections' => [
+ {
+ 'activityTitle' => 'Issue opened by user6',
+ 'activitySubtitle' => 'in [JohnDoe4/project2](http://localhost/namespace2/gitlabhq)',
+ 'activityText' => '[#1 Awesome issue](http://localhost/namespace2/gitlabhq/issues/1)',
+ 'activityImage' => 'http://someimage.com'
+ },
+ {
+ 'title' => 'Details',
+ 'facts' => [
+ {
+ 'name' => 'Attachments',
+ 'value' => 'please fix'
+ }
+ ]
+ }
+ ],
+ 'title' => 'JohnDoe4/project2',
+ 'summary' => '[[JohnDoe4/project2](http://localhost/namespace2/gitlabhq)] Issue [#1 Awesome issue](http://localhost/namespace2/gitlabhq/issues/1) opened by user6'
+ }
+ end
+
+ describe '#ping' do
+ before do
+ stub_request(:post, webhook_url).with(body: JSON(body), headers: { 'Content-Type' => 'application/json' }).to_return(status: 200, body: "", headers: {})
+ end
+
+ it 'expects to receive successfull answer' do
+ expect(subject.ping(options)).to be true
+ end
+ end
+end