# frozen_string_literal: true require 'spec_helper' RSpec.describe Ci::Maskable, feature_category: :pipeline_composition do let(:variable) { build(:ci_variable) } describe 'masked value validations' do subject { variable } context 'when variable is masked and expanded' do before do subject.masked = true subject.raw = false end it { is_expected.not_to allow_value('hello').for(:value) } it { is_expected.not_to allow_value('hello world').for(:value) } it { is_expected.not_to allow_value('hello$VARIABLEworld').for(:value) } it { is_expected.not_to allow_value('hello\rworld').for(:value) } it { is_expected.to allow_value('helloworld').for(:value) } end context 'when method :raw is not defined' do let(:test_var_class) do Struct.new(:masked?) do include ActiveModel::Validations include Ci::Maskable end end let(:variable) { test_var_class.new(true) } it 'evaluates masked variables as expanded' do expect(subject).not_to be_masked_and_raw expect(subject).to be_masked_and_expanded end end context 'when variable is masked and raw' do before do subject.masked = true subject.raw = true end it { is_expected.not_to allow_value('hello').for(:value) } it { is_expected.not_to allow_value('hello world').for(:value) } it { is_expected.to allow_value('hello\rworld').for(:value) } it { is_expected.to allow_value('hello$VARIABLEworld').for(:value) } it { is_expected.to allow_value('helloworld!!!').for(:value) } it { is_expected.to allow_value('hell******world').for(:value) } it { is_expected.to allow_value('helloworld123').for(:value) } end context 'when variable is not masked' do before do subject.masked = false end it { is_expected.to allow_value('hello').for(:value) } it { is_expected.to allow_value('hello world').for(:value) } it { is_expected.to allow_value('hello$VARIABLEworld').for(:value) } it { is_expected.to allow_value('hello\rworld').for(:value) } it { is_expected.to allow_value('helloworld').for(:value) } end end describe 'Regexes' do context 'with MASK_AND_RAW_REGEX' do subject { Ci::Maskable::MASK_AND_RAW_REGEX } it 'does not match strings shorter than 8 letters' do expect(subject.match?('hello')).to eq(false) end it 'does not match strings with spaces' do expect(subject.match?('hello world')).to eq(false) end it 'does not match strings that span more than one line' do string = <<~EOS hello world EOS expect(subject.match?(string)).to eq(false) end it 'matches valid strings' do expect(subject.match?('hello$VARIABLEworld')).to eq(true) expect(subject.match?('Hello+World_123/@:-~.')).to eq(true) expect(subject.match?('hello\rworld')).to eq(true) expect(subject.match?('HelloWorld%#^')).to eq(true) end end context 'with REGEX' do subject { Ci::Maskable::REGEX } it 'does not match strings shorter than 8 letters' do expect(subject.match?('hello')).to eq(false) end it 'does not match strings with spaces' do expect(subject.match?('hello world')).to eq(false) end it 'does not match strings with shell variables' do expect(subject.match?('hello$VARIABLEworld')).to eq(false) end it 'does not match strings with escape characters' do expect(subject.match?('hello\rworld')).to eq(false) end it 'does not match strings that span more than one line' do string = <<~EOS hello world EOS expect(subject.match?(string)).to eq(false) end it 'does not match strings using unsupported characters' do expect(subject.match?('HelloWorld%#^')).to eq(false) end it 'matches valid strings' do expect(subject.match?('Hello+World_123/@:-~.')).to eq(true) end end end describe '#to_runner_variable' do subject { variable.to_runner_variable } it 'exposes the masked attribute' do expect(subject).to include(:masked) end end end