Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
path: root/spec/lib
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2019-10-16 21:08:01 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2019-10-16 21:08:01 +0300
commit8e45d25f7dde6508839ffee719c0ddc2cf6b12d3 (patch)
tree9839e7fe63b36904d40995ebf519124c9a8f7681 /spec/lib
parent00c78fb814d7ce00989ac04edd6cdaa3239da284 (diff)
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'spec/lib')
-rw-r--r--spec/lib/gitlab/ci/ansi2json/line_spec.rb168
-rw-r--r--spec/lib/gitlab/ci/ansi2json/parser_spec.rb30
-rw-r--r--spec/lib/gitlab/ci/ansi2json/style_spec.rb166
-rw-r--r--spec/lib/gitlab/ci/ansi2json_spec.rb544
-rw-r--r--spec/lib/gitlab/diff/position_collection_spec.rb13
-rw-r--r--spec/lib/gitlab/health_checks/probes/collection_spec.rb62
-rw-r--r--spec/lib/gitlab/health_checks/probes/liveness_spec.rb17
-rw-r--r--spec/lib/gitlab/health_checks/probes/readiness_spec.rb39
-rw-r--r--spec/lib/gitlab_spec.rb78
9 files changed, 1032 insertions, 85 deletions
diff --git a/spec/lib/gitlab/ci/ansi2json/line_spec.rb b/spec/lib/gitlab/ci/ansi2json/line_spec.rb
new file mode 100644
index 00000000000..4b5c3f9489e
--- /dev/null
+++ b/spec/lib/gitlab/ci/ansi2json/line_spec.rb
@@ -0,0 +1,168 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe Gitlab::Ci::Ansi2json::Line do
+ let(:offset) { 0 }
+ let(:style) { Gitlab::Ci::Ansi2json::Style.new }
+
+ subject { described_class.new(offset: offset, style: style) }
+
+ describe '#<<' do
+ it 'appends new data to the current segment' do
+ expect { subject << 'test 1' }.to change { subject.current_segment.text }
+ expect(subject.current_segment.text).to eq('test 1')
+
+ expect { subject << ', test 2' }.to change { subject.current_segment.text }
+ expect(subject.current_segment.text).to eq('test 1, test 2')
+ end
+ end
+
+ describe '#style' do
+ context 'when style is passed to the initializer' do
+ let(:style) { double }
+
+ it 'returns the same style' do
+ expect(subject.style).to eq(style)
+ end
+ end
+
+ context 'when style is not passed to the initializer' do
+ it 'returns the default style' do
+ expect(subject.style.set?).to be_falsey
+ end
+ end
+ end
+
+ describe '#update_style' do
+ let(:expected_style) do
+ Gitlab::Ci::Ansi2json::Style.new(
+ fg: 'term-fg-l-yellow',
+ bg: 'term-bg-blue',
+ mask: 1)
+ end
+
+ it 'sets the style' do
+ subject.update_style(%w[1 33 44])
+
+ expect(subject.style).to eq(expected_style)
+ end
+ end
+
+ describe '#add_section' do
+ it 'appends a new section to the list' do
+ subject.add_section('section_1')
+ subject.add_section('section_2')
+
+ expect(subject.sections).to eq(%w[section_1 section_2])
+ end
+ end
+
+ describe '#set_as_section_header' do
+ it 'change the section_header to true' do
+ expect { subject.set_as_section_header }
+ .to change { subject.section_header }
+ .to be_truthy
+ end
+ end
+
+ describe '#set_section_duration' do
+ it 'sets and formats the section_duration' do
+ subject.set_section_duration(75)
+
+ expect(subject.section_duration).to eq('01:15')
+ end
+ end
+
+ describe '#flush_current_segment!' do
+ context 'when current segment is not empty' do
+ before do
+ subject << 'some data'
+ end
+
+ it 'adds the segment to the list' do
+ expect { subject.flush_current_segment! }.to change { subject.segments.count }.by(1)
+
+ expect(subject.segments.map { |s| s[:text] }).to eq(['some data'])
+ end
+
+ it 'updates the current segment pointer propagating the style' do
+ previous_segment = subject.current_segment
+
+ subject.flush_current_segment!
+
+ expect(subject.current_segment).not_to eq(previous_segment)
+ expect(subject.current_segment.style).to eq(previous_segment.style)
+ end
+ end
+
+ context 'when current segment is empty' do
+ it 'does not add any segments to the list' do
+ expect { subject.flush_current_segment! }.not_to change { subject.segments.count }
+ end
+
+ it 'does not change the current segment' do
+ expect { subject.flush_current_segment! }.not_to change { subject.current_segment }
+ end
+ end
+ end
+
+ describe '#to_h' do
+ before do
+ subject << 'some data'
+ subject.update_style(['1'])
+ end
+
+ context 'when sections are present' do
+ before do
+ subject.add_section('section_1')
+ subject.add_section('section_2')
+ end
+
+ context 'when section header is set' do
+ before do
+ subject.set_as_section_header
+ end
+
+ it 'serializes the attributes set' do
+ result = {
+ offset: 0,
+ content: [{ text: 'some data', style: 'term-bold' }],
+ section: 'section_2',
+ section_header: true
+ }
+
+ expect(subject.to_h).to eq(result)
+ end
+ end
+
+ context 'when section duration is set' do
+ before do
+ subject.set_section_duration(75)
+ end
+
+ it 'serializes the attributes set' do
+ result = {
+ offset: 0,
+ content: [{ text: 'some data', style: 'term-bold' }],
+ section: 'section_2',
+ section_duration: '01:15'
+ }
+
+ expect(subject.to_h).to eq(result)
+ end
+ end
+ end
+
+ context 'when there are no sections' do
+ it 'serializes the attributes set' do
+ result = {
+ offset: 0,
+ content: [{ text: 'some data', style: 'term-bold' }]
+ }
+
+ expect(subject.to_h).to eq(result)
+ end
+ end
+ end
+end
diff --git a/spec/lib/gitlab/ci/ansi2json/parser_spec.rb b/spec/lib/gitlab/ci/ansi2json/parser_spec.rb
new file mode 100644
index 00000000000..e161e74c1ff
--- /dev/null
+++ b/spec/lib/gitlab/ci/ansi2json/parser_spec.rb
@@ -0,0 +1,30 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+# The rest of the specs for this class are covered in style_spec.rb
+describe Gitlab::Ci::Ansi2json::Parser do
+ subject { described_class }
+
+ describe 'bold?' do
+ it 'returns true if style mask matches bold format' do
+ expect(subject.bold?(0x01)).to be_truthy
+ end
+
+ it 'returns false if style mask does not match bold format' do
+ expect(subject.bold?(0x02)).to be_falsey
+ end
+ end
+
+ describe 'matching_formats' do
+ it 'returns matching formats given a style mask' do
+ expect(subject.matching_formats(0x01)).to eq(%w[term-bold])
+ expect(subject.matching_formats(0x03)).to eq(%w[term-bold term-italic])
+ expect(subject.matching_formats(0x07)).to eq(%w[term-bold term-italic term-underline])
+ end
+
+ it 'returns an empty array if no formats match the style mask' do
+ expect(subject.matching_formats(0)).to eq([])
+ end
+ end
+end
diff --git a/spec/lib/gitlab/ci/ansi2json/style_spec.rb b/spec/lib/gitlab/ci/ansi2json/style_spec.rb
new file mode 100644
index 00000000000..88a0ca35859
--- /dev/null
+++ b/spec/lib/gitlab/ci/ansi2json/style_spec.rb
@@ -0,0 +1,166 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe Gitlab::Ci::Ansi2json::Style do
+ describe '#set?' do
+ subject { described_class.new(params).set? }
+
+ context 'when fg color is set' do
+ let(:params) { { fg: 'term-fg-black' } }
+
+ it { is_expected.to be_truthy }
+ end
+
+ context 'when bg color is set' do
+ let(:params) { { bg: 'term-bg-black' } }
+
+ it { is_expected.to be_truthy }
+ end
+
+ context 'when mask is set' do
+ let(:params) { { mask: 0x01 } }
+
+ it { is_expected.to be_truthy }
+ end
+
+ context 'nothing is set' do
+ let(:params) { {} }
+
+ it { is_expected.to be_falsey }
+ end
+ end
+
+ describe '#reset!' do
+ let(:style) { described_class.new(fg: 'term-fg-black', bg: 'term-bg-yellow', mask: 0x01) }
+
+ it 'set the style params to default' do
+ style.reset!
+
+ expect(style.fg).to be_nil
+ expect(style.bg).to be_nil
+ expect(style.mask).to be_zero
+ end
+ end
+
+ describe 'update formats to mimic terminals' do
+ subject { described_class.new(params) }
+
+ context 'when fg color present' do
+ let(:params) { { fg: 'term-fg-black', mask: mask } }
+
+ context 'when mask is set to bold' do
+ let(:mask) { 0x01 }
+
+ it 'changes the fg color to a lighter version' do
+ expect(subject.fg).to eq('term-fg-l-black')
+ end
+ end
+
+ context 'when mask set to another format' do
+ let(:mask) { 0x02 }
+
+ it 'does not change the fg color' do
+ expect(subject.fg).to eq('term-fg-black')
+ end
+ end
+
+ context 'when mask is not set' do
+ let(:mask) { 0 }
+
+ it 'does not change the fg color' do
+ expect(subject.fg).to eq('term-fg-black')
+ end
+ end
+ end
+ end
+
+ describe '#update' do
+ where(:initial_state, :ansi_commands, :result, :description) do
+ [
+ # add format
+ [[], %w[0], '', 'does not set any style'],
+ [[], %w[1], 'term-bold', 'enables format bold'],
+ [[], %w[3], 'term-italic', 'enables format italic'],
+ [[], %w[4], 'term-underline', 'enables format underline'],
+ [[], %w[8], 'term-conceal', 'enables format conceal'],
+ [[], %w[9], 'term-cross', 'enables format cross'],
+ # remove format
+ [%w[1], %w[21], '', 'disables format bold'],
+ [%w[1 3], %w[21], 'term-italic', 'disables format bold and leaves italic'],
+ [%w[1], %w[22], '', 'disables format bold using command 22'],
+ [%w[1 3], %w[22], 'term-italic', 'disables format bold and leaves italic using command 22'],
+ [%w[3], %w[23], '', 'disables format italic'],
+ [%w[1 3], %w[23], 'term-bold', 'disables format italic and leaves bold'],
+ [%w[4], %w[24], '', 'disables format underline'],
+ [%w[1 4], %w[24], 'term-bold', 'disables format underline and leaves bold'],
+ [%w[8], %w[28], '', 'disables format conceal'],
+ [%w[1 8], %w[28], 'term-bold', 'disables format conceal and leaves bold'],
+ [%w[9], %w[29], '', 'disables format cross'],
+ [%w[1 9], %w[29], 'term-bold', 'disables format cross and leaves bold'],
+ # set fg color
+ [[], %w[30], 'term-fg-black', 'sets fg color black'],
+ [[], %w[31], 'term-fg-red', 'sets fg color red'],
+ [[], %w[32], 'term-fg-green', 'sets fg color green'],
+ [[], %w[33], 'term-fg-yellow', 'sets fg color yellow'],
+ [[], %w[34], 'term-fg-blue', 'sets fg color blue'],
+ [[], %w[35], 'term-fg-magenta', 'sets fg color magenta'],
+ [[], %w[36], 'term-fg-cyan', 'sets fg color cyan'],
+ [[], %w[37], 'term-fg-white', 'sets fg color white'],
+ # sets xterm fg color
+ [[], %w[38 5 1], 'xterm-fg-1', 'sets xterm fg color 1'],
+ [[], %w[38 5 2], 'xterm-fg-2', 'sets xterm fg color 2'],
+ [[], %w[38 1], 'term-bold', 'ignores 38 command if not followed by 5 and sets format bold'],
+ # set bg color
+ [[], %w[40], 'term-bg-black', 'sets bg color black'],
+ [[], %w[41], 'term-bg-red', 'sets bg color red'],
+ [[], %w[42], 'term-bg-green', 'sets bg color green'],
+ [[], %w[43], 'term-bg-yellow', 'sets bg color yellow'],
+ [[], %w[44], 'term-bg-blue', 'sets bg color blue'],
+ [[], %w[45], 'term-bg-magenta', 'sets bg color magenta'],
+ [[], %w[46], 'term-bg-cyan', 'sets bg color cyan'],
+ [[], %w[47], 'term-bg-white', 'sets bg color white'],
+ # set xterm bg color
+ [[], %w[48 5 1], 'xterm-bg-1', 'sets xterm bg color 1'],
+ [[], %w[48 5 2], 'xterm-bg-2', 'sets xterm bg color 2'],
+ [[], %w[48 1], 'term-bold', 'ignores 48 command if not followed by 5 and sets format bold'],
+ # set light fg color
+ [[], %w[90], 'term-fg-l-black', 'sets fg color light black'],
+ [[], %w[91], 'term-fg-l-red', 'sets fg color light red'],
+ [[], %w[92], 'term-fg-l-green', 'sets fg color light green'],
+ [[], %w[93], 'term-fg-l-yellow', 'sets fg color light yellow'],
+ [[], %w[94], 'term-fg-l-blue', 'sets fg color light blue'],
+ [[], %w[95], 'term-fg-l-magenta', 'sets fg color light magenta'],
+ [[], %w[96], 'term-fg-l-cyan', 'sets fg color light cyan'],
+ [[], %w[97], 'term-fg-l-white', 'sets fg color light white'],
+ # set light bg color
+ [[], %w[100], 'term-bg-l-black', 'sets bg color light black'],
+ [[], %w[101], 'term-bg-l-red', 'sets bg color light red'],
+ [[], %w[102], 'term-bg-l-green', 'sets bg color light green'],
+ [[], %w[103], 'term-bg-l-yellow', 'sets bg color light yellow'],
+ [[], %w[104], 'term-bg-l-blue', 'sets bg color light blue'],
+ [[], %w[105], 'term-bg-l-magenta', 'sets bg color light magenta'],
+ [[], %w[106], 'term-bg-l-cyan', 'sets bg color light cyan'],
+ [[], %w[107], 'term-bg-l-white', 'sets bg color light white'],
+ # reset
+ [%w[1], %w[0], '', 'resets style from format bold'],
+ [%w[1 3], %w[0], '', 'resets style from format bold and italic'],
+ [%w[1 3 term-fg-l-red term-bg-yellow], %w[0], '', 'resets all formats and colors'],
+ # misc
+ [[], %w[1 30 42 3], 'term-fg-l-black term-bg-green term-bold term-italic', 'adds fg color, bg color and formats from no style'],
+ [%w[3 31], %w[23 1 43], 'term-fg-l-red term-bg-yellow term-bold', 'replaces format italic with bold and adds a yellow background']
+ ]
+ end
+
+ with_them do
+ it 'change the style' do
+ style = described_class.new
+ style.update(initial_state)
+
+ style.update(ansi_commands)
+
+ expect(style.to_s).to eq(result)
+ end
+ end
+ end
+end
diff --git a/spec/lib/gitlab/ci/ansi2json_spec.rb b/spec/lib/gitlab/ci/ansi2json_spec.rb
new file mode 100644
index 00000000000..3c6bc46436b
--- /dev/null
+++ b/spec/lib/gitlab/ci/ansi2json_spec.rb
@@ -0,0 +1,544 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe Gitlab::Ci::Ansi2json do
+ subject { described_class }
+
+ describe 'lines' do
+ it 'prints non-ansi as-is' do
+ expect(convert_json('Hello')).to eq([
+ { offset: 0, content: [{ text: 'Hello' }] }
+ ])
+ end
+
+ it 'adds new line in a separate element' do
+ expect(convert_json("Hello\nworld")).to eq([
+ { offset: 0, content: [{ text: 'Hello' }] },
+ { offset: 6, content: [{ text: 'world' }] }
+ ])
+ end
+
+ it 'recognizes color changing ANSI sequences' do
+ expect(convert_json("\e[31mHello\e[0m")).to eq([
+ { offset: 0, content: [{ text: 'Hello', style: 'term-fg-red' }] }
+ ])
+ end
+
+ it 'recognizes color changing ANSI sequences across multiple lines' do
+ expect(convert_json("\e[31mHello\nWorld\e[0m")).to eq([
+ { offset: 0, content: [{ text: 'Hello', style: 'term-fg-red' }] },
+ { offset: 11, content: [{ text: 'World', style: 'term-fg-red' }] }
+ ])
+ end
+
+ it 'recognizes background and foreground colors' do
+ expect(convert_json("\e[31;44mHello")).to eq([
+ { offset: 0, content: [{ text: 'Hello', style: 'term-fg-red term-bg-blue' }] }
+ ])
+ end
+
+ it 'recognizes style changes within the same line' do
+ expect(convert_json("\e[31;44mHello\e[0m world")).to eq([
+ { offset: 0, content: [
+ { text: 'Hello', style: 'term-fg-red term-bg-blue' },
+ { text: ' world' }
+ ] }
+ ])
+ end
+
+ context 'with section markers' do
+ let(:section_name) { 'prepare-script' }
+ let(:section_duration) { 63.seconds }
+ let(:section_start_time) { Time.new(2019, 9, 17).utc }
+ let(:section_end_time) { section_start_time + section_duration }
+ let(:section_start) { "section_start:#{section_start_time.to_i}:#{section_name}\r\033[0K"}
+ let(:section_end) { "section_end:#{section_end_time.to_i}:#{section_name}\r\033[0K"}
+
+ it 'marks the first line of the section as header' do
+ expect(convert_json("Hello#{section_start}world!")).to eq([
+ {
+ offset: 0,
+ content: [{ text: 'Hello' }]
+ },
+ {
+ offset: 5,
+ content: [{ text: 'world!' }],
+ section: 'prepare-script',
+ section_header: true
+ }
+ ])
+ end
+
+ it 'does not marks the other lines of the section as header' do
+ expect(convert_json("outside section#{section_start}Hello\nworld!")).to eq([
+ {
+ offset: 0,
+ content: [{ text: 'outside section' }]
+ },
+ {
+ offset: 15,
+ content: [{ text: 'Hello' }],
+ section: 'prepare-script',
+ section_header: true
+ },
+ {
+ offset: 65,
+ content: [{ text: 'world!' }],
+ section: 'prepare-script'
+ }
+ ])
+ end
+
+ it 'marks the last line of the section as footer' do
+ expect(convert_json("#{section_start}Good\nmorning\nworld!#{section_end}")).to eq([
+ {
+ offset: 0,
+ content: [{ text: 'Good' }],
+ section: 'prepare-script',
+ section_header: true
+ },
+ {
+ offset: 49,
+ content: [{ text: 'morning' }],
+ section: 'prepare-script'
+ },
+ {
+ offset: 57,
+ content: [{ text: 'world!' }],
+ section: 'prepare-script'
+ },
+ {
+ offset: 63,
+ content: [],
+ section_duration: '01:03',
+ section: 'prepare-script'
+ },
+ {
+ offset: 63,
+ content: []
+ }
+ ])
+ end
+
+ it 'marks the first line as header and footer if is the only line in the section' do
+ expect(convert_json("#{section_start}Hello world!#{section_end}")).to eq([
+ {
+ offset: 0,
+ content: [{ text: 'Hello world!' }],
+ section: 'prepare-script',
+ section_header: true
+ },
+ {
+ offset: 56,
+ content: [],
+ section: 'prepare-script',
+ section_duration: '01:03'
+ },
+ {
+ offset: 56,
+ content: []
+ }
+ ])
+ end
+
+ it 'does not add sections attribute to lines after the section is closed' do
+ expect(convert_json("#{section_start}Hello#{section_end}world")).to eq([
+ {
+ offset: 0,
+ content: [{ text: 'Hello' }],
+ section: 'prepare-script',
+ section_header: true
+ },
+ {
+ offset: 49,
+ content: [],
+ section: 'prepare-script',
+ section_duration: '01:03'
+ },
+ {
+ offset: 49,
+ content: [{ text: 'world' }]
+ }
+ ])
+ end
+
+ it 'ignores section_end marker if no section_start exists' do
+ expect(convert_json("Hello #{section_end}world")).to eq([
+ {
+ offset: 0,
+ content: [{ text: 'Hello world' }]
+ }
+ ])
+ end
+
+ context 'when section name contains .-_ and capital letters' do
+ let(:section_name) { 'a.Legit-SeCtIoN_namE' }
+
+ it 'sanitizes the section name' do
+ expect(convert_json("Hello#{section_start}world!")).to eq([
+ {
+ offset: 0,
+ content: [{ text: 'Hello' }]
+ },
+ {
+ offset: 5,
+ content: [{ text: 'world!' }],
+ section: 'a-legit-section-name',
+ section_header: true
+ }
+ ])
+ end
+ end
+
+ context 'when section name includes $' do
+ let(:section_name) { 'my_$ection' }
+
+ it 'ignores the section' do
+ expect(convert_json("#{section_start}hello")).to eq([
+ {
+ offset: 0,
+ content: [{ text: "#{section_start.gsub("\033[0K", '')}hello" }]
+ }
+ ])
+ end
+ end
+
+ context 'when section name includes <' do
+ let(:section_name) { '<a_tag>' }
+
+ it 'ignores the section' do
+ expect(convert_json("#{section_start}hello")).to eq([
+ {
+ offset: 0,
+ content: [{ text: "#{section_start.gsub("\033[0K", '').gsub('<', '&lt;')}hello" }]
+ }
+ ])
+ end
+ end
+
+ it 'prevents XSS injection' do
+ trace = "#{section_start}section_end:1:2<script>alert('XSS Hack!');</script>#{section_end}"
+ expect(convert_json(trace)).to eq([
+ {
+ offset: 0,
+ content: [{ text: "section_end:1:2&lt;script>alert('XSS Hack!');&lt;/script>" }],
+ section: 'prepare-script',
+ section_header: true
+ },
+ {
+ offset: 95,
+ content: [],
+ section: 'prepare-script',
+ section_duration: '01:03'
+ },
+ {
+ offset: 95,
+ content: []
+ }
+ ])
+ end
+
+ context 'with nested section' do
+ let(:nested_section_name) { 'prepare-script-nested' }
+ let(:nested_section_duration) { 2.seconds }
+ let(:nested_section_start_time) { Time.new(2019, 9, 17).utc }
+ let(:nested_section_end_time) { nested_section_start_time + nested_section_duration }
+ let(:nested_section_start) { "section_start:#{nested_section_start_time.to_i}:#{nested_section_name}\r\033[0K"}
+ let(:nested_section_end) { "section_end:#{nested_section_end_time.to_i}:#{nested_section_name}\r\033[0K"}
+
+ it 'adds multiple sections to the lines inside the nested section' do
+ trace = "Hello#{section_start}foo#{nested_section_start}bar#{nested_section_end}baz#{section_end}world"
+
+ expect(convert_json(trace)).to eq([
+ {
+ offset: 0,
+ content: [{ text: 'Hello' }]
+ },
+ {
+ offset: 5,
+ content: [{ text: 'foo' }],
+ section: 'prepare-script',
+ section_header: true
+ },
+ {
+ offset: 52,
+ content: [{ text: 'bar' }],
+ section: 'prepare-script-nested',
+ section_header: true
+ },
+ {
+ offset: 106,
+ content: [],
+ section: 'prepare-script-nested',
+ section_duration: '00:02'
+ },
+ {
+ offset: 106,
+ content: [{ text: 'baz' }],
+ section: 'prepare-script'
+ },
+ {
+ offset: 158,
+ content: [],
+ section: 'prepare-script',
+ section_duration: '01:03'
+ },
+ {
+ offset: 158,
+ content: [{ text: 'world' }]
+ }
+ ])
+ end
+
+ it 'adds multiple sections to the lines inside the nested section and closes all sections together' do
+ trace = "Hello#{section_start}\e[91mfoo\e[0m#{nested_section_start}bar#{nested_section_end}#{section_end}"
+
+ expect(convert_json(trace)).to eq([
+ {
+ offset: 0,
+ content: [{ text: 'Hello' }]
+ },
+ {
+ offset: 5,
+ content: [{ text: 'foo', style: 'term-fg-l-red' }],
+ section: 'prepare-script',
+ section_header: true
+ },
+ {
+ offset: 61,
+ content: [{ text: 'bar' }],
+ section: 'prepare-script-nested',
+ section_header: true
+ },
+ {
+ offset: 115,
+ content: [],
+ section: 'prepare-script-nested',
+ section_duration: '00:02'
+ },
+ {
+ offset: 115,
+ content: [],
+ section: 'prepare-script',
+ section_duration: '01:03'
+ },
+ {
+ offset: 164,
+ content: []
+ }
+ ])
+ end
+ end
+ end
+
+ describe 'incremental updates' do
+ 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) }
+
+ context 'with split word' do
+ let(:pre_text) { "\e[1mHello " }
+ let(:text) { "World" }
+
+ let(:lines) do
+ [
+ { offset: 0, content: [{ text: 'Hello World', style: 'term-bold' }] }
+ ]
+ end
+
+ it 'returns the full line' do
+ expect(pass2.lines).to eq(lines)
+ expect(pass2.append).to be_falsey
+ end
+ end
+
+ context 'with split word on second line' do
+ let(:pre_text) { "Good\nmorning " }
+ let(:text) { "World" }
+
+ let(:lines) do
+ [
+ { offset: 5, content: [{ text: 'morning World' }] }
+ ]
+ end
+
+ it 'returns all lines since last partially processed line' do
+ expect(pass2.lines).to eq(lines)
+ expect(pass2.append).to be_truthy
+ end
+ end
+
+ context 'with split sequence across multiple lines' do
+ let(:pre_text) { "\e[1mgood\nmorning\n" }
+ let(:text) { "\e[3mworld" }
+
+ let(:lines) do
+ [
+ { offset: 17, content: [{ text: 'world', style: 'term-bold term-italic' }] }
+ ]
+ end
+
+ it 'returns the full line' do
+ expect(pass2.lines).to eq(lines)
+ expect(pass2.append).to be_truthy
+ end
+ end
+
+ context 'with split partial sequence' do
+ let(:pre_text) { "hello\e" }
+ let(:text) { "[1m world" }
+
+ let(:lines) do
+ [
+ { offset: 0, content: [
+ { text: 'hello' },
+ { text: ' world', style: 'term-bold' }
+ ] }
+ ]
+ end
+
+ it 'returns the full line' do
+ expect(pass2.lines).to eq(lines)
+ expect(pass2.append).to be_falsey
+ end
+ end
+
+ context 'with split new line' do
+ let(:pre_text) { "hello\r" }
+ let(:text) { "\nworld" }
+
+ let(:lines) do
+ [
+ { offset: 0, content: [{ text: 'hello' }] },
+ { offset: 7, content: [{ text: 'world' }] }
+ ]
+ end
+
+ it 'returns the full line' do
+ expect(pass2.lines).to eq(lines)
+ expect(pass2.append).to be_falsey
+ end
+ end
+
+ context 'with split section' do
+ let(:section_name) { 'prepare-script' }
+ let(:section_duration) { 63.seconds }
+ let(:section_start_time) { Time.new(2019, 9, 17).utc }
+ let(:section_end_time) { section_start_time + section_duration }
+ let(:section_start) { "section_start:#{section_start_time.to_i}:#{section_name}\r\033[0K"}
+ let(:section_end) { "section_end:#{section_end_time.to_i}:#{section_name}\r\033[0K"}
+
+ context 'with split section body' do
+ let(:pre_text) { "#{section_start}this is a header\nand " }
+ let(:text) { "this\n is a body" }
+
+ let(:lines) do
+ [
+ {
+ offset: 61,
+ content: [{ text: 'and this' }],
+ section: 'prepare-script'
+ },
+ {
+ offset: 70,
+ content: [{ text: ' is a body' }],
+ section: 'prepare-script'
+ }
+ ]
+ end
+
+ it 'returns the full line' do
+ expect(pass2.lines).to eq(lines)
+ expect(pass2.append).to be_truthy
+ end
+ end
+
+ context 'with split section where header is also split' do
+ let(:pre_text) { "#{section_start}this is " }
+ let(:text) { "a header\nand body" }
+
+ let(:lines) do
+ [
+ {
+ offset: 0,
+ content: [{ text: 'this is a header' }],
+ section: 'prepare-script',
+ section_header: true
+ },
+ {
+ offset: 61,
+ content: [{ text: 'and body' }],
+ section: 'prepare-script'
+ }
+ ]
+ end
+
+ it 'returns the full line' do
+ expect(pass2.lines).to eq(lines)
+ expect(pass2.append).to be_falsey
+ end
+ end
+
+ context 'with split section end' do
+ let(:pre_text) { "#{section_start}this is a header\nthe" }
+ let(:text) { " body\nthe end#{section_end}" }
+
+ let(:lines) do
+ [
+ {
+ offset: 61,
+ content: [{ text: 'the body' }],
+ section: 'prepare-script'
+ },
+ {
+ offset: 70,
+ content: [{ text: 'the end' }],
+ section: 'prepare-script'
+ },
+ {
+ offset: 77,
+ content: [],
+ section: 'prepare-script',
+ section_duration: '01:03'
+ },
+ {
+ offset: 77,
+ content: []
+ }
+ ]
+ end
+
+ it 'returns the full line' do
+ expect(pass2.lines).to eq(lines)
+ expect(pass2.append).to be_truthy
+ end
+ end
+ end
+ end
+
+ describe 'trucates' 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_json(data)
+ stream = StringIO.new(data)
+ subject.convert(stream).lines
+ end
+ end
+end
diff --git a/spec/lib/gitlab/diff/position_collection_spec.rb b/spec/lib/gitlab/diff/position_collection_spec.rb
index de0e631ab03..f2a8312587c 100644
--- a/spec/lib/gitlab/diff/position_collection_spec.rb
+++ b/spec/lib/gitlab/diff/position_collection_spec.rb
@@ -35,14 +35,15 @@ describe Gitlab::Diff::PositionCollection do
let(:text_position) { build_text_position }
let(:folded_text_position) { build_text_position(old_line: 1, new_line: 1) }
let(:image_position) { build_image_position }
+ let(:invalid_position) { 'a position' }
let(:head_sha) { merge_request.diff_head_sha }
let(:collection) do
- described_class.new([text_position, folded_text_position, image_position], head_sha)
+ described_class.new([text_position, folded_text_position, image_position, invalid_position], head_sha)
end
describe '#to_a' do
- it 'returns all positions' do
+ it 'returns all positions that are Gitlab::Diff::Position' do
expect(collection.to_a).to eq([text_position, folded_text_position, image_position])
end
end
@@ -59,6 +60,14 @@ describe Gitlab::Diff::PositionCollection do
expect(collection.unfoldable).to be_empty
end
end
+
+ context 'when given head_sha is nil' do
+ let(:head_sha) { nil }
+
+ it 'returns unfoldable diff positions unfiltered by head_sha' do
+ expect(collection.unfoldable).to eq([folded_text_position])
+ end
+ end
end
describe '#concat' do
diff --git a/spec/lib/gitlab/health_checks/probes/collection_spec.rb b/spec/lib/gitlab/health_checks/probes/collection_spec.rb
new file mode 100644
index 00000000000..33efc640257
--- /dev/null
+++ b/spec/lib/gitlab/health_checks/probes/collection_spec.rb
@@ -0,0 +1,62 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe Gitlab::HealthChecks::Probes::Collection do
+ let(:readiness) { described_class.new(*checks) }
+
+ describe '#call' do
+ subject { readiness.execute }
+
+ context 'with all checks' do
+ let(:checks) do
+ [
+ Gitlab::HealthChecks::DbCheck,
+ Gitlab::HealthChecks::Redis::RedisCheck,
+ Gitlab::HealthChecks::Redis::CacheCheck,
+ Gitlab::HealthChecks::Redis::QueuesCheck,
+ Gitlab::HealthChecks::Redis::SharedStateCheck,
+ Gitlab::HealthChecks::GitalyCheck
+ ]
+ end
+
+ it 'responds with readiness checks data' do
+ expect(subject.http_status).to eq(200)
+
+ expect(subject.json[:status]).to eq('ok')
+ expect(subject.json['db_check']).to contain_exactly(status: 'ok')
+ expect(subject.json['cache_check']).to contain_exactly(status: 'ok')
+ expect(subject.json['queues_check']).to contain_exactly(status: 'ok')
+ expect(subject.json['shared_state_check']).to contain_exactly(status: 'ok')
+ expect(subject.json['gitaly_check']).to contain_exactly(
+ status: 'ok', labels: { shard: 'default' })
+ end
+
+ context 'when Redis fails' do
+ before do
+ allow(Gitlab::HealthChecks::Redis::RedisCheck).to receive(:readiness).and_return(
+ Gitlab::HealthChecks::Result.new('redis_check', false, "check error"))
+ end
+
+ it 'responds with failure' do
+ expect(subject.http_status).to eq(503)
+
+ expect(subject.json[:status]).to eq('failed')
+ expect(subject.json['cache_check']).to contain_exactly(status: 'ok')
+ expect(subject.json['redis_check']).to contain_exactly(
+ status: 'failed', message: 'check error')
+ end
+ end
+ end
+
+ context 'without checks' do
+ let(:checks) { [] }
+
+ it 'responds with success' do
+ expect(subject.http_status).to eq(200)
+
+ expect(subject.json).to eq(status: 'ok')
+ end
+ end
+ end
+end
diff --git a/spec/lib/gitlab/health_checks/probes/liveness_spec.rb b/spec/lib/gitlab/health_checks/probes/liveness_spec.rb
deleted file mode 100644
index 91066cb8ba0..00000000000
--- a/spec/lib/gitlab/health_checks/probes/liveness_spec.rb
+++ /dev/null
@@ -1,17 +0,0 @@
-# frozen_string_literal: true
-
-require 'spec_helper'
-
-describe Gitlab::HealthChecks::Probes::Liveness do
- let(:liveness) { described_class.new }
-
- describe '#call' do
- subject { liveness.execute }
-
- it 'responds with liveness checks data' do
- expect(subject.http_status).to eq(200)
-
- expect(subject.json[:status]).to eq('ok')
- end
- end
-end
diff --git a/spec/lib/gitlab/health_checks/probes/readiness_spec.rb b/spec/lib/gitlab/health_checks/probes/readiness_spec.rb
deleted file mode 100644
index d88ffd984c2..00000000000
--- a/spec/lib/gitlab/health_checks/probes/readiness_spec.rb
+++ /dev/null
@@ -1,39 +0,0 @@
-# frozen_string_literal: true
-
-require 'spec_helper'
-
-describe Gitlab::HealthChecks::Probes::Readiness do
- let(:readiness) { described_class.new }
-
- describe '#call' do
- subject { readiness.execute }
-
- it 'responds with readiness checks data' do
- expect(subject.http_status).to eq(200)
-
- expect(subject.json[:status]).to eq('ok')
- expect(subject.json['db_check']).to contain_exactly(status: 'ok')
- expect(subject.json['cache_check']).to contain_exactly(status: 'ok')
- expect(subject.json['queues_check']).to contain_exactly(status: 'ok')
- expect(subject.json['shared_state_check']).to contain_exactly(status: 'ok')
- expect(subject.json['gitaly_check']).to contain_exactly(
- status: 'ok', labels: { shard: 'default' })
- end
-
- context 'when Redis fails' do
- before do
- allow(Gitlab::HealthChecks::Redis::RedisCheck).to receive(:readiness).and_return(
- Gitlab::HealthChecks::Result.new('redis_check', false, "check error"))
- end
-
- it 'responds with failure' do
- expect(subject.http_status).to eq(503)
-
- expect(subject.json[:status]).to eq('failed')
- expect(subject.json['cache_check']).to contain_exactly(status: 'ok')
- expect(subject.json['redis_check']).to contain_exactly(
- status: 'failed', message: 'check error')
- end
- end
- end
-end
diff --git a/spec/lib/gitlab_spec.rb b/spec/lib/gitlab_spec.rb
index c1d171815ba..6bf837f1d3f 100644
--- a/spec/lib/gitlab_spec.rb
+++ b/spec/lib/gitlab_spec.rb
@@ -146,7 +146,7 @@ describe Gitlab do
describe '.ee?' do
before do
- stub_env('IS_GITLAB_EE', nil) # Make sure the ENV is clean
+ stub_env('FOSS_ONLY', nil) # Make sure the ENV is clean
described_class.instance_variable_set(:@is_ee, nil)
end
@@ -154,42 +154,66 @@ describe Gitlab do
described_class.instance_variable_set(:@is_ee, nil)
end
- it 'returns true when using Enterprise Edition' do
- root = Pathname.new('dummy')
- license_path = double(:path, exist?: true)
+ context 'for EE' do
+ before do
+ root = Pathname.new('dummy')
+ license_path = double(:path, exist?: true)
- allow(described_class)
- .to receive(:root)
- .and_return(root)
+ allow(described_class)
+ .to receive(:root)
+ .and_return(root)
- allow(root)
- .to receive(:join)
- .with('ee/app/models/license.rb')
- .and_return(license_path)
+ allow(root)
+ .to receive(:join)
+ .with('ee/app/models/license.rb')
+ .and_return(license_path)
+ end
- expect(described_class.ee?).to eq(true)
- end
+ context 'when using FOSS_ONLY=1' do
+ before do
+ stub_env('FOSS_ONLY', '1')
+ end
- it 'returns false when using Community Edition' do
- root = double(:path)
- license_path = double(:path, exists?: false)
+ it 'returns not to be EE' do
+ expect(described_class).not_to be_ee
+ end
+ end
- allow(described_class)
- .to receive(:root)
- .and_return(Pathname.new('dummy'))
+ context 'when using FOSS_ONLY=0' do
+ before do
+ stub_env('FOSS_ONLY', '0')
+ end
- allow(root)
- .to receive(:join)
- .with('ee/app/models/license.rb')
- .and_return(license_path)
+ it 'returns to be EE' do
+ expect(described_class).to be_ee
+ end
+ end
- expect(described_class.ee?).to eq(false)
+ context 'when using default FOSS_ONLY' do
+ it 'returns to be EE' do
+ expect(described_class).to be_ee
+ end
+ end
end
- it 'returns true when the IS_GITLAB_EE variable is not empty' do
- stub_env('IS_GITLAB_EE', '1')
+ context 'for CE' do
+ before do
+ root = double(:path)
+ license_path = double(:path, exists?: false)
- expect(described_class.ee?).to eq(true)
+ allow(described_class)
+ .to receive(:root)
+ .and_return(Pathname.new('dummy'))
+
+ allow(root)
+ .to receive(:join)
+ .with('ee/app/models/license.rb')
+ .and_return(license_path)
+ end
+
+ it 'returns not to be EE' do
+ expect(described_class).not_to be_ee
+ end
end
end