require 'chef_helper'

RSpec.describe 'gitlab::gitlab-pages' do
  let(:chef_run) { ChefSpec::SoloRunner.new(step_into: %w(runit_service env_dir)).converge('gitlab::default') }

  before do
    allow(Gitlab).to receive(:[]).and_call_original
    allow(Etc).to receive(:getpwnam).with('git').and_return(spy('getpwnam spy', uid: 1000, gid: 1000))
  end

  context 'with default values' do
    it 'does not include Pages recipe' do
      expect(chef_run).not_to include_recipe('gitlab-pages::enable')
      expect(chef_run).to include_recipe('gitlab-pages::disable')
    end
  end

  context 'with Pages enabled' do
    before do
      stub_gitlab_rb(
        external_url: 'https://gitlab.example.com',
        pages_external_url: 'https://pages.example.com'
      )
    end

    it 'includes Pages recipe' do
      expect(chef_run).to include_recipe('gitlab-pages::enable')
    end

    it 'creates a VERSION file and restarts the service' do
      expect(chef_run).to create_version_file('Create version file for Gitlab Pages').with(
        version_file_path: '/var/opt/gitlab/gitlab-pages/VERSION',
        version_check_cmd: '/opt/gitlab/embedded/bin/gitlab-pages --version'
      )

      expect(chef_run.version_file('Create version file for Gitlab Pages')).to notify('runit_service[gitlab-pages]').to(:restart)
    end

    it 'renders the env dir files' do
      expect(chef_run).to render_file('/opt/gitlab/etc/gitlab-pages/env/SSL_CERT_DIR')
        .with_content('/opt/gitlab/embedded/ssl/certs')
    end

    it 'renders the pages service run file' do
      expect(chef_run).to render_file("/opt/gitlab/sv/gitlab-pages/run").with_content(%r{-config="/var/opt/gitlab/gitlab-pages/gitlab-pages-config"})
    end

    it 'deletes old admin.secret file' do
      expect(chef_run).to delete_file("/var/opt/gitlab/gitlab-pages/admin.secret")
    end

    it 'renders pages config file' do
      default_content = <<~EOS
       pages-domain=pages.example.com
       pages-root=/var/opt/gitlab/gitlab-rails/shared/pages
       api-secret-key=/var/opt/gitlab/gitlab-pages/.gitlab_pages_secret
       listen-proxy=localhost:8090
       log-format=json
       use-http2=true
       artifacts-server=https://gitlab.example.com/api/v4
       artifacts-server-timeout=10
       gitlab-server=https://gitlab.example.com
      EOS

      expect(chef_run).to render_file("/var/opt/gitlab/gitlab-pages/gitlab-pages-config").with_content(default_content)
    end

    it 'skips rendering the auth settings when access control is disabled' do
      stub_gitlab_rb(
        external_url: 'https://gitlab.example.com',
        pages_external_url: 'https://pages.example.com',
        gitlab_pages: {
          access_control: false,
          auth_secret: 'auth_secret'
        }
      )

      expect(chef_run).to render_file("/var/opt/gitlab/gitlab-pages/gitlab-pages-config").with_content { |content|
        expect(content).not_to match(%r{auth-secret=auth_secret})
      }
    end

    context 'when access control is enabled' do
      context 'when access control secrets are not specified' do
        before do
          stub_gitlab_rb(
            external_url: 'https://gitlab.example.com',
            pages_external_url: 'https://pages.example.com',
            gitlab_pages: {
              access_control: true,
              auth_secret: 'auth_secret'
            }
          )

          allow(GitlabPages).to receive(:authorize_with_gitlab) {
            Gitlab['gitlab_pages']['gitlab_secret'] = 'app_secret'
            Gitlab['gitlab_pages']['gitlab_id'] = 'app_id'
          }
        end

        it 'authorizes Pages with GitLab' do
          expect(chef_run).to run_ruby_block('authorize pages with gitlab')
            .at_converge_time
          expect(chef_run).to run_ruby_block('re-populate GitLab Pages configuration options')
            .at_converge_time
          expect(GitlabPages).to receive(:authorize_with_gitlab)

          chef_run.ruby_block('authorize pages with gitlab').block.call
          chef_run.ruby_block('re-populate GitLab Pages configuration options').block.call

          expect(chef_run).to render_file("/var/opt/gitlab/gitlab-pages/gitlab-pages-config").with_content { |content|
            expect(content).to match(%r{auth-client-id=app_id})
            expect(content).to match(%r{auth-client-secret=app_secret})
            expect(content).to match(%r{auth-redirect-uri=https://projects.pages.example.com/auth})
            expect(content).to match(%r{auth-secret=auth_secret})
          }
        end
      end

      context 'when access control secrets are specified' do
        before do
          stub_gitlab_rb(
            external_url: 'https://gitlab.example.com',
            pages_external_url: 'https://pages.example.com',
            gitlab_pages: {
              access_control: true,
              gitlab_id: 'app_id',
              gitlab_secret: 'app_secret',
              auth_secret: 'auth_secret',
              auth_redirect_uri: 'https://projects.pages.example.com/auth',
              auth_scope: 'read_api'
            }
          )
        end

        it 'attempt to authorize with GitLab even when oauth credentials are given' do
          expect(chef_run).to run_ruby_block('authorize pages with gitlab')
        end

        it 'renders pages config file' do
          expect(chef_run).to render_file("/var/opt/gitlab/gitlab-pages/gitlab-pages-config").with_content { |content|
            expect(content).to match(%r{auth-client-id=app_id})
            expect(content).to match(%r{auth-client-secret=app_secret})
            expect(content).to match(%r{auth-redirect-uri=https://projects.pages.example.com/auth})
            expect(content).to match(%r{auth-secret=auth_secret})
            expect(content).to match(%r{auth-scope=read_api})
          }
        end
      end

      context 'when generating gitlab-secrets.json file is disabled' do
        before do
          stub_gitlab_rb(
            package: {
              generate_secrets_json_file: false
            }
          )

          allow(LoggingHelper).to receive(:warning).and_call_original
        end

        it 'does not register as an oauth app with GitLab' do
          expect(chef_run).not_to run_ruby_block('authorize pages with gitlab')
        end

        it 'displays a warning about disabling automatic oauth registration' do
          expect(LoggingHelper).to receive(:warning).with(/not automatically registering GitLab Pages as an Oauth App/)

          chef_run
        end
      end

      context 'when namespace in path is enabled' do
        before do
          stub_gitlab_rb(
            pages_external_url: 'https://pages.example.com',
            gitlab_pages: {
              access_control: true,
              namespace_in_path: true,
            }
          )
        end

        it 'renders pages config file with default auth-redirect-uri' do
          expect(chef_run).to render_file("/var/opt/gitlab/gitlab-pages/gitlab-pages-config").with_content { |content|
            expect(content).to match(%r{auth-redirect-uri=https://pages.example.com/projects/auth})
            expect(content).to match(%r{namespace-in-path=true})
          }
        end
      end

      context 'with custom port and namespace in path is enabled' do
        before do
          stub_gitlab_rb(
            pages_external_url: 'https://pages.example.com:8443',
            gitlab_pages: {
              access_control: true,
              namespace_in_path: true,
            }
          )
        end

        it 'renders pages config file with default auth-redirect-uri' do
          expect(chef_run).to render_file("/var/opt/gitlab/gitlab-pages/gitlab-pages-config").with_content { |content|
            expect(content).to match(%r{auth-redirect-uri=https://pages.example.com:8443/projects/auth})
            expect(content).to match(%r{namespace-in-path=true})
          }
        end
      end
    end

    context 'with custom port' do
      before do
        stub_gitlab_rb(
          pages_external_url: 'https://pages.example.com:8443',
          gitlab_pages: {
            access_control: true
          }
        )
      end

      it 'sets the correct port number' do
        expect(chef_run).to render_file("/var/opt/gitlab/gitlab-pages/gitlab-pages-config").with_content { |content|
          expect(content).to match(%r{auth-redirect-uri=https://projects.pages.example.com:8443/auth})
        }
      end
    end

    context 'with custom values' do
      before do
        stub_gitlab_rb(
          external_url: 'https://gitlab.example.com',
          pages_external_url: 'https://pages.example.com',
          gitlab_pages: {
            external_http: ['external_pages.example.com', 'localhost:9000'],
            external_https: ['external_pages.example.com', 'localhost:9001'],
            external_https_proxyv2: ['external_pages.example.com', 'localhost:9002'],
            metrics_address: 'localhost:1234',
            redirect_http: true,
            dir: '/var/opt/gitlab/pages',
            cert: '/etc/gitlab/pages.crt',
            artifacts_server_url: "https://gitlab.elsewhere.com/api/v5",
            artifacts_server_timeout: 60,
            status_uri: '/@status',
            max_connections: 7500,
            max_uri_length: 2048,
            propagate_correlation_id: true,
            log_format: 'text',
            log_verbose: true,
            gitlab_id: 'app_id',
            gitlab_secret: 'app_secret',
            auth_secret: 'auth_secret',
            auth_redirect_uri: 'https://projects.pages.example.com/auth',
            auth_scope: 'read_api',
            auth_timeout: '10s',
            auth_cookie_session_timeout: '20m',
            access_control: true,
            insecure_ciphers: true,
            tls_min_version: "tls1.0",
            tls_max_version: "tls1.2",
            sentry_enabled: true,
            sentry_dsn: 'https://b44a0828b72421a6d8e99efd68d44fa8@example.com/40',
            sentry_environment: 'production',
            headers: ['X-XSS-Protection: 1; mode=block', 'X-Content-Type-Options: nosniff', 'Test: Header'],
            server_shutdown_timeout: "30s",
            gitlab_client_http_timeout: "10s",
            gitlab_client_jwt_expiry: "30s",
            zip_cache_expiration: "120s",
            zip_cache_cleanup: "1m",
            zip_cache_refresh: "60s",
            zip_open_timeout: "45s",
            zip_http_client_timeout: "30m",
            internal_gitlab_server: "https://int.gitlab.example.com",
            gitlab_cache_expiry: "1m",
            gitlab_cache_refresh: "500ms",
            gitlab_cache_cleanup: "100ms",
            gitlab_retrieval_timeout: "3s",
            gitlab_retrieval_interval: "500ms",
            gitlab_retrieval_retries: 5,
            rate_limit_source_ip: 100,
            rate_limit_source_ip_burst: 50,
            rate_limit_domain: 1000,
            rate_limit_domain_burst: 10000,
            rate_limit_tls_source_ip: 101,
            rate_limit_tls_source_ip_burst: 51,
            rate_limit_tls_domain: 1001,
            rate_limit_tls_domain_burst: 10001,
            rate_limit_subnets_allow_list: ["10.1.1.0/24", "10.1.2.0/24"],
            server_read_timeout: "1m",
            server_read_header_timeout: "2m",
            server_write_timeout: "3m",
            server_keep_alive: "4m",
            redirects_max_config_size: 128000,
            redirects_max_path_segments: 50,
            redirects_max_rule_count: 2000,
            enable_disk: true,
            namespace_in_path: true,
            client_cert: "/path/to/client.crt",
            client_key: "/path/to/client.key",
            client_ca_certs: "/path/to/ca.crt",
            env: {
              GITLAB_CONTINUOUS_PROFILING: "stackdriver?service=gitlab-pages",
            },
          }
        )
      end

      it 'renders pages config file in the specified directory' do
        expected_content = <<~EOS
            pages-domain=pages.example.com
            pages-root=/var/opt/gitlab/gitlab-rails/shared/pages
            api-secret-key=/var/opt/gitlab/pages/.gitlab_pages_secret
            auth-client-id=app_id
            auth-redirect-uri=https://projects.pages.example.com/auth
            auth-client-secret=app_secret
            auth-secret=auth_secret
            auth-scope=read_api
            auth-timeout=10s
            auth-cookie-session-timeout=20m
            zip-cache-expiration=120s
            zip-cache-cleanup=1m
            zip-cache-refresh=60s
            zip-open-timeout=45s
            zip-http-client-timeout=30m
            listen-proxy=localhost:8090
            metrics-address=localhost:1234
            pages-status=/@status
            max-conns=7500
            max-uri-length=2048
            propagate-correlation-id=true
            log-format=text
            log-verbose
            sentry-dsn=https://b44a0828b72421a6d8e99efd68d44fa8@example.com/40
            sentry-environment=production
            redirect-http=true
            use-http2=true
            artifacts-server=https://gitlab.elsewhere.com/api/v5
            artifacts-server-timeout=60
            gitlab-server=https://gitlab.example.com
            internal-gitlab-server=https://int.gitlab.example.com
            insecure-ciphers
            tls-min-version=tls1.0
            tls-max-version=tls1.2
            server-shutdown-timeout=30s
            gitlab-client-http-timeout=10s
            gitlab-client-jwt-expiry=30s
            listen-http=external_pages.example.com,localhost:9000
            listen-https=external_pages.example.com,localhost:9001
            listen-https-proxyv2=external_pages.example.com,localhost:9002
            root-cert=/etc/gitlab/pages.crt
            root-key=/etc/gitlab/ssl/pages.example.com.key
            gitlab-cache-expiry=1m
            gitlab-cache-refresh=500ms
            gitlab-cache-cleanup=100ms
            gitlab-retrieval-timeout=3s
            gitlab-retrieval-timeout=500ms
            gitlab-retrieval-retries=5
            enable-disk=true
            rate-limit-source-ip=100
            rate-limit-source-ip-burst=50
            rate-limit-domain=1000
            rate-limit-domain-burst=10000
            rate-limit-tls-source-ip=101
            rate-limit-tls-source-ip-burst=51
            rate-limit-tls-domain=1001
            rate-limit-tls-domain-burst=10001
            rate-limit-subnets-allow-list=10.1.1.0/24,10.1.2.0/24
            server-read-timeout=1m
            server-read-header-timeout=2m
            server-write-timeout=3m
            server-keep-alive=4m
            redirects-max-config-size=128000
            redirects-max-path-segments=50
            redirects-max-rule-count=2000
            header=X-XSS-Protection: 1; mode=block;;X-Content-Type-Options: nosniff;;Test: Header
            namespace-in-path=true
            client-cert=/path/to/client.crt
            client-key=/path/to/client.key
            client-ca-certs=/path/to/ca.crt
        EOS

        expect(chef_run).to render_file("/var/opt/gitlab/pages/gitlab-pages-config").with_content(expected_content)
      end

      it 'renders the env dir files' do
        expect(chef_run).to render_file("/opt/gitlab/etc/gitlab-pages/env/GITLAB_CONTINUOUS_PROFILING")
          .with_content('stackdriver?service=gitlab-pages')
        expect(chef_run).to render_file("/opt/gitlab/etc/gitlab-pages/env/SSL_CERT_DIR")
          .with_content('/opt/gitlab/embedded/ssl/certs')
        expect(chef_run).to render_file("/opt/gitlab/etc/gitlab-pages/env/GODEBUG")
          .with_content('tlsmlkem=0')
      end
    end

    context 'log directory and runit group' do
      context 'default values' do
        it_behaves_like 'enabled logged service', 'gitlab-pages', true, { log_directory_owner: 'git' }
      end

      context 'custom values' do
        before do
          stub_gitlab_rb(
            gitlab_pages: {
              log_group: 'fugee'
            }
          )
        end
        it_behaves_like 'configured logrotate service', 'gitlab-pages', 'git', 'fugee'
        it_behaves_like 'enabled logged service', 'gitlab-pages', true, { log_directory_owner: 'git', log_group: 'fugee' }
      end

      it_behaves_like 'a service with proper supervise directories', 'gitlab-pages'
    end
  end
end
