diff options
Diffstat (limited to 'spec/bin/feature_flag_spec.rb')
-rw-r--r-- | spec/bin/feature_flag_spec.rb | 273 |
1 files changed, 187 insertions, 86 deletions
diff --git a/spec/bin/feature_flag_spec.rb b/spec/bin/feature_flag_spec.rb index d1e4be5be28..f9caa5549ca 100644 --- a/spec/bin/feature_flag_spec.rb +++ b/spec/bin/feature_flag_spec.rb @@ -7,12 +7,28 @@ load File.expand_path('../../bin/feature-flag', __dir__) RSpec.describe 'bin/feature-flag', feature_category: :feature_flags do using RSpec::Parameterized::TableSyntax + let(:groups) do + { + geo: { label: 'group::geo' } + } + end + + before do + allow(HTTParty) + .to receive(:get) + .with(FeatureFlagOptionParser::WWW_GITLAB_COM_GROUPS_JSON, format: :plain) + .and_return(groups.to_json) + end + describe FeatureFlagCreator do - let(:argv) { %w[feature-flag-name -t development -g group::geo -i https://url -m http://url] } + let(:argv) { %w[feature-flag-name -t gitlab_com_derisk -g group::geo -a https://url -i https://url -m http://url -u username -M 16.6] } let(:options) { FeatureFlagOptionParser.parse(argv) } let(:creator) { described_class.new(options) } let(:existing_flags) do - { 'existing_feature_flag' => File.join('config', 'feature_flags', 'development', 'existing_feature_flag.yml') } + { + 'existing_feature_flag' => + File.join('config', 'feature_flags', 'gitlab_com_derisk', 'existing_feature_flag.yml') + } end before do @@ -31,7 +47,7 @@ RSpec.describe 'bin/feature-flag', feature_category: :feature_flags do it 'properly creates a feature flag' do expect(File).to receive(:write).with( - File.join('config', 'feature_flags', 'development', 'feature_flag_name.yml'), + File.join('config', 'feature_flags', 'gitlab_com_derisk', 'feature_flag_name.yml'), anything) expect do @@ -108,85 +124,97 @@ RSpec.describe 'bin/feature-flag', feature_category: :feature_flags do end describe '.read_type' do - let(:type) { 'development' } + before do + stub_const('FeatureFlagOptionParser::TYPES', + development: { description: 'short' }, + deprecated: { description: 'deprecated', deprecated: true }, + licensed: { description: 'licensed' } + ) + end - context 'when there is only a single type defined' do - before do - stub_const('FeatureFlagOptionParser::TYPES', - development: { description: 'short' } - ) - end + context 'when valid type is given' do + let(:type) { 'development' } - it 'returns that type' do - expect(described_class.read_type).to eq(:development) + it 'reads type from stdin' do + expect(Readline).to receive(:readline).and_return(type) + expect do + expect(described_class.read_type).to eq(:development) + end.to output(/Specify the feature flag type/).to_stdout end end - context 'when there is deprecated feature flag type' do - before do - stub_const('FeatureFlagOptionParser::TYPES', - development: { description: 'short' }, - deprecated: { description: 'deprecated', deprecated: true } - ) + context 'when valid index is given' do + it 'picks the type successfully' do + expect(Readline).to receive(:readline).and_return('3') + expect do + expect(described_class.read_type).to eq(:licensed) + end.to output(/Specify the feature flag type./).to_stdout end + end - context 'and deprecated type is given' do - let(:type) { 'deprecated' } + context 'when deprecated type is given' do + let(:type) { 'deprecated' } - it 'shows error message and retries' do - expect(Readline).to receive(:readline).and_return(type) - expect(Readline).to receive(:readline).and_raise('EOF') + it 'shows error message and retries' do + expect(Readline).to receive(:readline).and_return(type) + expect(Readline).to receive(:readline).and_raise('EOF') - expect do - expect { described_class.read_type }.to raise_error(/EOF/) - end.to output(/Specify the feature flag type/).to_stdout - .and output(/Invalid type specified/).to_stderr - end + expect do + expect { described_class.read_type }.to raise_error(/EOF/) + end.to output(/Specify the feature flag type/).to_stdout + .and output(/Invalid type specified/).to_stderr end end - context 'when there are many types defined' do - before do - stub_const('FeatureFlagOptionParser::TYPES', - development: { description: 'short' }, - licensed: { description: 'licensed' } - ) - end + context 'when invalid type is given' do + let(:type) { 'invalid' } - it 'reads type from stdin' do + it 'shows error message and retries' do expect(Readline).to receive(:readline).and_return(type) + expect(Readline).to receive(:readline).and_raise('EOF') + expect do - expect(described_class.read_type).to eq(:development) + expect { described_class.read_type }.to raise_error(/EOF/) end.to output(/Specify the feature flag type/).to_stdout + .and output(/Invalid type specified/).to_stderr end + end - context 'when invalid type is given' do - let(:type) { 'invalid' } - - it 'shows error message and retries' do - expect(Readline).to receive(:readline).and_return(type) - expect(Readline).to receive(:readline).and_raise('EOF') + context 'when invalid index is given' do + it 'shows error message and retries' do + expect(Readline).to receive(:readline).and_return('12') + expect(Readline).to receive(:readline).and_raise('EOF') - expect do - expect { described_class.read_type }.to raise_error(/EOF/) - end.to output(/Specify the feature flag type/).to_stdout - .and output(/Invalid type specified/).to_stderr - end + expect do + expect { described_class.read_type }.to raise_error(/EOF/) + end.to output(/Specify the feature flag type/).to_stdout + .and output(/Invalid type specified/).to_stderr end end end describe '.read_group' do - let(:group) { 'group::geo' } + context 'when valid group is given' do + let(:group) { 'group::geo' } - it 'reads type from stdin' do - expect(Readline).to receive(:readline).and_return(group) - expect do - expect(described_class.read_group).to eq('group::geo') - end.to output(/Specify the group introducing the feature flag/).to_stdout + it 'reads group from stdin' do + expect(Readline).to receive(:readline).and_return(group) + expect do + expect(described_class.read_group).to eq('group::geo') + end.to output(/Specify the group label to which the feature flag belongs, from the following list/).to_stdout + end end - context 'invalid group given' do + context 'when valid index is given' do + it 'picks the group successfully' do + expect(Readline).to receive(:readline).and_return('1') + expect do + expect(described_class.read_group).to eq('group::geo') + end.to output(/Specify the group label to which the feature flag belongs, from the following list/).to_stdout + end + end + + context 'with invalid group given' do let(:type) { 'invalid' } it 'shows error message and retries' do @@ -195,78 +223,151 @@ RSpec.describe 'bin/feature-flag', feature_category: :feature_flags do expect do expect { described_class.read_group }.to raise_error(/EOF/) - end.to output(/Specify the group introducing the feature flag/).to_stdout - .and output(/The group needs to include/).to_stderr + end.to output(/Specify the group label to which the feature flag belongs, from the following list/).to_stdout + .and output(/The group label isn't in the above labels list/).to_stderr + end + end + + context 'when invalid index is given' do + it 'shows error message and retries' do + expect(Readline).to receive(:readline).and_return('12') + expect(Readline).to receive(:readline).and_raise('EOF') + + expect do + expect { described_class.read_group }.to raise_error(/EOF/) + end.to output(/Specify the group label to which the feature flag belongs, from the following list/).to_stdout + .and output(/The group label isn't in the above labels list/).to_stderr end end end - describe '.read_introduced_by_url' do - let(:url) { 'https://merge-request' } + shared_examples 'read_url' do |method, prompt| + context 'with valid URL given' do + let(:url) { 'https://merge-request' } - it 'reads type from stdin' do - expect(Readline).to receive(:readline).and_return(url) - expect do - expect(described_class.read_introduced_by_url).to eq('https://merge-request') - end.to output(/URL of the MR introducing the feature flag/).to_stdout + it 'reads URL from stdin' do + expect(Readline).to receive(:readline).and_return(url) + expect(HTTParty).to receive(:head).with(url).and_return(instance_double(HTTParty::Response, success?: true)) + + expect do + expect(described_class.public_send(method)).to eq('https://merge-request') + end.to output(/#{prompt}/).to_stdout + end end - context 'empty URL given' do + context 'with invalid URL given' do + let(:url) { 'https://invalid' } + + it 'shows error message and retries' do + expect(Readline).to receive(:readline).and_return(url) + expect(HTTParty).to receive(:head).with(url).and_return(instance_double(HTTParty::Response, success?: false)) + expect(Readline).to receive(:readline).and_raise('EOF') + + expect do + expect { described_class.public_send(method) }.to raise_error(/EOF/) + end.to output(/#{prompt}/).to_stdout + .and output(/URL '#{url}' isn't valid/).to_stderr + end + end + + context 'with empty URL given' do let(:url) { '' } it 'skips entry' do expect(Readline).to receive(:readline).and_return(url) + expect do - expect(described_class.read_introduced_by_url).to be_nil - end.to output(/URL of the MR introducing the feature flag/).to_stdout + expect(described_class.public_send(method)).to be_nil + end.to output(/#{prompt}/).to_stdout end end - context 'invalid URL given' do - let(:url) { 'invalid' } + context 'with a non-URL given' do + let(:url) { 'malformed' } it 'shows error message and retries' do expect(Readline).to receive(:readline).and_return(url) expect(Readline).to receive(:readline).and_raise('EOF') expect do - expect { described_class.read_introduced_by_url }.to raise_error(/EOF/) - end.to output(/URL of the MR introducing the feature flag/).to_stdout + expect { described_class.public_send(method) }.to raise_error(/EOF/) + end.to output(/#{prompt}/).to_stdout .and output(/URL needs to start with/).to_stderr end end end + describe '.read_feature_issue_url' do + it_behaves_like 'read_url', :read_feature_issue_url, 'URL of the original feature issue' + end + + describe '.read_introduced_by_url' do + it_behaves_like 'read_url', :read_introduced_by_url, 'URL of the MR introducing the feature flag' + end + describe '.read_rollout_issue_url' do - let(:options) { double('options', name: 'foo', type: :development) } - let(:url) { 'https://issue' } + let(:options) do + FeatureFlagOptionParser::Options.new({ + name: 'foo', + username: 'joe', + type: :gitlab_com_derisk, + introduced_by_url: 'https://introduced_by_url', + feature_issue_url: 'https://feature_issue_url', + milestone: '16.6', + group: 'group::geo' + }) + end - it 'reads type from stdin' do - expect(Readline).to receive(:readline).and_return(url) - expect do - expect(described_class.read_rollout_issue_url(options)).to eq('https://issue') - end.to output(/URL of the rollout issue/).to_stdout + context 'with valid URL given' do + let(:url) { 'https://rollout_issue_url' } + + it 'reads type from stdin' do + expect(described_class).to receive(:copy_to_clipboard!).and_return(true) + expect(Readline).to receive(:readline).and_return('') # enter to open the new issue url + expect(described_class).to receive(:open_url!).and_return(true) + expect(Readline).to receive(:readline).and_return(url) + expect(HTTParty).to receive(:head).with(url).and_return(instance_double(HTTParty::Response, success?: true)) + + expect do + expect(described_class.read_rollout_issue_url(options)).to eq(url) + end.to output(/URL of the rollout issue/).to_stdout + end end - context 'invalid URL given' do - let(:type) { 'invalid' } + context 'with invalid URL given' do + let(:url) { 'https://invalid' } it 'shows error message and retries' do - expect(Readline).to receive(:readline).and_return(type) + expect(described_class).to receive(:copy_to_clipboard!).and_return(true) + expect(Readline).to receive(:readline).and_return('') # enter to open the new issue url + expect(described_class).to receive(:open_url!).and_return(true) + expect(Readline).to receive(:readline).and_return(url) + expect(HTTParty).to receive(:head).with(url).and_return(instance_double(HTTParty::Response, success?: false)) expect(Readline).to receive(:readline).and_raise('EOF') expect do expect { described_class.read_rollout_issue_url(options) }.to raise_error(/EOF/) end.to output(/URL of the rollout issue/).to_stdout - .and output(/URL needs to start/).to_stderr + .and output(/URL '#{url}' isn't valid/).to_stderr end end - end - describe '.read_ee_only' do - let(:options) { double('options', name: 'foo', type: :development) } + context 'with a non-URL given' do + let(:url) { 'malformed' } + + it 'shows error message and retries' do + expect(described_class).to receive(:copy_to_clipboard!).and_return(true) + expect(Readline).to receive(:readline).and_return('') # enter to open the new issue url + expect(described_class).to receive(:open_url!).and_return(true) + expect(Readline).to receive(:readline).and_return(url) + expect(Readline).to receive(:readline).and_raise('EOF') - it { expect(described_class.read_ee_only(options)).to eq(false) } + expect do + expect { described_class.read_rollout_issue_url(options) }.to raise_error(/EOF/) + end.to output(/URL of the rollout issue/).to_stdout + .and output(/URL needs to start/).to_stderr + end + end end end end |