# frozen_string_literal: true require 'spec_helper' describe Gitlab::Geo, :geo, :request_store do using RSpec::Parameterized::TableSyntax include ::EE::GeoHelpers let_it_be(:primary_node) { create(:geo_node, :primary) } let_it_be(:secondary_node) { create(:geo_node) } shared_examples 'a Geo cached value' do |method, key| it 'includes GitLab version and Rails.version in the cache key' do expanded_key = "geo:#{key}:#{Gitlab::VERSION}:#{Rails.version}" expect(Gitlab::ProcessMemoryCache.cache_backend).to receive(:write) .with(expanded_key, an_instance_of(String), expires_in: 1.minute).and_call_original expect(Rails.cache).to receive(:write) .with(expanded_key, an_instance_of(String), expires_in: 2.minutes) described_class.public_send(method) end end describe '.current_node' do it 'returns a GeoNode instance' do expect(GeoNode).to receive(:current_node).and_return(primary_node) expect(described_class.current_node).to eq(primary_node) end it_behaves_like 'a Geo cached value', :current_node, :current_node end describe '.primary_node' do it 'returns a GeoNode primary instance' do expect(described_class.primary_node).to eq(primary_node) end it_behaves_like 'a Geo cached value', :primary_node, :primary_node end describe '.secondary_nodes' do it 'returns a list of Geo secondary nodes' do expect(described_class.secondary_nodes).to match_array(secondary_node) end it_behaves_like 'a Geo cached value', :secondary_nodes, :secondary_nodes end describe '.primary?' do context 'when current node is a primary node' do before do stub_current_geo_node(primary_node) end it 'returns true' do expect(described_class.primary?).to be_truthy end it 'returns false when GeoNode is disabled' do allow(described_class).to receive(:enabled?) { false } expect(described_class.primary?).to be_falsey end end end describe '.primary_node_configured?' do context 'when current node is a primary node' do it 'returns true' do expect(described_class.primary_node_configured?).to be_truthy end it 'returns false when primary does not exist' do primary_node.destroy expect(described_class.primary_node_configured?).to be_falsey end end end describe '.current_node_misconfigured?' do it 'returns true when current node is not set' do expect(described_class.current_node_misconfigured?).to be_truthy end it 'returns false when primary' do stub_current_geo_node(primary_node) expect(described_class.current_node_misconfigured?).to be_falsey end it 'returns false when secondary' do stub_current_geo_node(secondary_node) expect(described_class.current_node_misconfigured?).to be_falsey end it 'returns false when Geo is disabled' do GeoNode.delete_all expect(described_class.current_node_misconfigured?).to be_falsey end end describe '.secondary?' do context 'when current node is a secondary node' do before do stub_current_geo_node(secondary_node) end it 'returns true' do expect(described_class.secondary?).to be_truthy end it 'returns false when GeoNode is disabled' do allow(described_class).to receive(:enabled?) { false } expect(described_class.secondary?).to be_falsey end end end describe '.enabled?' do it_behaves_like 'a Geo cached value', :enabled?, :node_enabled context 'when any GeoNode exists' do it 'returns true' do expect(described_class.enabled?).to be_truthy end end context 'when no GeoNode exists' do before do GeoNode.delete_all end it 'returns false' do expect(described_class.enabled?).to be_falsey end end end describe '.oauth_authentication' do before do stub_secondary_node stub_current_geo_node(secondary_node) end it_behaves_like 'a Geo cached value', :oauth_authentication, :oauth_application end describe '.connected?' do context 'when there is a database issue' do it 'returns false when database connection is down' do allow(GeoNode).to receive(:connected?) { false } expect(described_class.connected?).to be_falsey end it 'returns false when the table does not exist' do allow(GeoNode).to receive(:table_exists?) { false } expect(described_class.connected?).to be_falsey end end end describe '.secondary?' do context 'when current node is secondary' do it 'returns true' do stub_current_geo_node(secondary_node) expect(described_class.secondary?).to be_truthy end end context 'current node is primary' do it 'returns false' do expect(described_class.secondary?).to be_falsey end end end describe '.expire_cache!' do it 'clears the Geo cache keys', :request_store do described_class::CACHE_KEYS.each do |key| content = "#{key}-content" described_class.cache_value(key) { content } expect(described_class.cache_value(key)).to eq(content) end described_class.expire_cache! described_class::CACHE_KEYS.each do |key| expect(described_class.cache_value(key) { nil }).to be_nil end end end describe '.expire_cache_keys!' do it 'clears specified keys', :request_store do cache_data = { one: 1, two: 2 } cache_data.each do |key, value| described_class.cache_value(key) { value } expect(described_class.cache_value(key)).to eq(value) end described_class.expire_cache_keys!(cache_data.keys) cache_data.keys.each do |key| expect(described_class.cache_value(key) { nil }).to be_nil end end end describe '.license_allows?' do it 'returns true if license has Geo addon' do stub_licensed_features(geo: true) expect(described_class.license_allows?).to be_truthy end it 'returns false if license doesnt have Geo addon' do stub_licensed_features(geo: false) expect(described_class.license_allows?).to be_falsey end it 'returns false if no license is present' do allow(License).to receive(:current) { nil } expect(described_class.license_allows?).to be_falsey end end describe '.generate_access_keys' do it 'returns a public and secret access key' do keys = described_class.generate_access_keys expect(keys[:access_key].length).to eq(20) expect(keys[:secret_access_key].length).to eq(40) end end describe '.configure_cron_jobs!' do let(:manager) { double('cron_manager').as_null_object } before do allow(Gitlab::Geo::CronManager).to receive(:new) { manager } end it 'creates a cron watcher' do expect(manager).to receive(:create_watcher!) described_class.configure_cron_jobs! end it 'runs the cron manager' do expect(manager).to receive(:execute) described_class.configure_cron_jobs! end end describe '.repository_verification_enabled?' do context "when the feature flag hasn't been set" do it 'returns true' do expect(described_class.repository_verification_enabled?).to eq true end end context 'when the feature flag has been set' do context 'when the feature flag is set to enabled' do it 'returns true' do stub_feature_flags(geo_repository_verification: true) expect(described_class.repository_verification_enabled?).to eq true end end context 'when the feature flag is set to disabled' do it 'returns false' do stub_feature_flags(geo_repository_verification: false) expect(described_class.repository_verification_enabled?).to eq false end end end end describe '.allowed_ip?' do where(:allowed_ips, :ip, :allowed) do "192.1.1.1" | "192.1.1.1" | true "192.1.1.1, 192.1.2.1" | "192.1.2.1" | true "192.1.1.0/24" | "192.1.1.223" | true "192.1.0.0/16" | "192.1.223.223" | true "192.1.0.0/16, 192.1.2.0/24" | "192.1.2.223" | true "192.1.0.0/16" | "192.2.1.1" | false "192.1.0.1" | "192.2.1.1" | false end with_them do it do stub_application_setting(geo_node_allowed_ips: allowed_ips) expect(described_class.allowed_ip?(ip)).to eq(allowed) end end end describe '.proxying_to_primary_message' do it 'returns a message as a string' do url = 'ssh://git@primary.com/namespace/repo.git' message = <<~STR This request to a Geo secondary node will be forwarded to the Geo primary node: #{url} STR expect(described_class.interacting_with_primary_message(url)).to eq(message) end end describe '.redirecting_to_primary_message' do it 'returns a message as a string' do url = 'http://primary.com/namespace/repo.git' message = <<~STR This request to a Geo secondary node will be forwarded to the Geo primary node: #{url} STR expect(described_class.interacting_with_primary_message(url)).to eq(message) end end end