def test_conditional_expected_secret(self, mock_safe_load): mock_safe_load.return_value = {"secrets": {}} self._setting_overrides["ADDRESS_LOOKUP_API_AUTH_ENABLED"] = True with self.assertRaises(Exception) as ex: create_app(self._setting_overrides) assert "Missing Secret [ADDRESS_LOOKUP_API_AUTH_TOKEN_SECRET]" in str( ex.exception)
def test_eq_submission_backend_not_set(self): # Given self._setting_overrides["EQ_SUBMISSION_BACKEND"] = "" # When with self.assertRaises(Exception) as ex: create_app(self._setting_overrides) # Then assert "Unknown EQ_SUBMISSION_BACKEND" in str(ex.exception)
def test_eq_publisher_backend_not_set(self): # Given self._setting_overrides["EQ_PUBLISHER_BACKEND"] = "" # When with self.assertRaises(Exception) as ex: create_app(self._setting_overrides) # Then assert "Unknown EQ_PUBLISHER_BACKEND" in str(ex.exception)
def test_eq_feedback_backend_not_set(self): # Given self._setting_overrides["EQ_FEEDBACK_BACKEND"] = "" # When with self.assertRaises(Exception) as ex: create_app(self._setting_overrides) # Then assert "Unknown EQ_FEEDBACK_BACKEND" in str(ex.exception)
def test_gcs_feedback_bucket_id_not_set_raises_exception(self): # Given self._setting_overrides["EQ_FEEDBACK_BACKEND"] = "gcs" # When with self.assertRaises(Exception) as ex: create_app(self._setting_overrides) # Then assert "Setting EQ_GCS_FEEDBACK_BUCKET_ID Missing" in str(ex.exception)
def test_rabbit_submitter_host_not_set_raises_exception(self): # Given self._setting_overrides["EQ_SUBMISSION_BACKEND"] = "rabbitmq" self._setting_overrides["EQ_RABBITMQ_HOST"] = "" # When with self.assertRaises(Exception) as ex: create_app(self._setting_overrides) # Then assert "Setting EQ_RABBITMQ_HOST Missing" in str(ex.exception)
def test_gcs_submitter_bucket_id_not_set_raises_exception(self): # Given self._setting_overrides["EQ_SUBMISSION_BACKEND"] = "gcs" # WHEN with self.assertRaises(Exception) as ex: create_app(self._setting_overrides) # Then assert "Setting EQ_GCS_SUBMISSION_BUCKET_ID Missing" in str( ex.exception)
def test_enforces_secure_headers(self): with create_app(self._setting_overrides).test_client() as client: headers = client.get( '/', headers={ 'X-Forwarded-Proto': 'https' } # set protocal so that talisman sets HSTS headers ).headers self.assertEqual('no-cache, no-store, must-revalidate', headers['Cache-Control']) self.assertEqual('no-cache', headers['Pragma']) self.assertEqual('max-age=31536000; includeSubDomains', headers['Strict-Transport-Security']) self.assertEqual('DENY', headers['X-Frame-Options']) self.assertEqual('1; mode=block', headers['X-Xss-Protection']) self.assertEqual('nosniff', headers['X-Content-Type-Options']) csp_policy_parts = headers['Content-Security-Policy'].split('; ') self.assertIn("default-src 'self' https://cdn.ons.gov.uk", csp_policy_parts) self.assertIn( "script-src 'self' https://www.google-analytics.com https://cdn.ons.gov.uk 'nonce-{}'" .format(request.csp_nonce), csp_policy_parts) self.assertIn( "img-src 'self' data: https://www.google-analytics.com https://cdn.ons.gov.uk", csp_policy_parts) self.assertIn("font-src 'self' data: https://cdn.ons.gov.uk", csp_policy_parts)
def test_setup_datastore(self): self._setting_overrides["EQ_STORAGE_BACKEND"] = "datastore" with patch("google.cloud.datastore.Client"): application = create_app(self._setting_overrides) self.assertIsInstance(application.eq["storage"], Datastore)
def test_enforces_secure_session(self): application = create_app(self._setting_overrides) self.assertTrue(application.secret_key) self.assertTrue(application.session_interface) # This is derived from EQ_ENABLE_SECURE_SESSION_COOKIE which is false # when running tests self.assertFalse(application.config['SESSION_COOKIE_SECURE'])
def test_submission_backend_not_set_raises_exception(self): # Given self._setting_overrides["EQ_SUBMISSION_CONFIRMATION_BACKEND"] = "" self._setting_overrides[ "EQ_SUBMISSION_CONFIRMATION_CLOUD_FUNCTION_NAME"] = "test" # When with patch( "google.auth._default._get_explicit_environ_credentials", return_value=(Mock(), "test-project-id"), ): with self.assertRaises(Exception) as ex: create_app(self._setting_overrides) # Then assert "Unknown EQ_SUBMISSION_CONFIRMATION_BACKEND" in str( ex.exception)
def test_versioned_url_for_with_version(self): self._setting_overrides['EQ_APPLICATION_VERSION'] = 'abc123' application = create_app(self._setting_overrides) application.config['SERVER_NAME'] = 'test' with application.app_context(), self.override_settings(): self.assertEqual('http://test/s/a.jpg?q=abc123', versioned_url_for('static', filename='a.jpg'))
def test_adds_logging_of_request_ids(self): with patch("app.setup.logger") as logger: self._setting_overrides.update({"EQ_APPLICATION_VERSION": False}) application = create_app(self._setting_overrides) application.test_client().get("/") self.assertEqual(1, logger.new.call_count) _, kwargs = logger.new.call_args self.assertTrue(UUID(kwargs["request_id"], version=4))
def app(mocker): setting_overrides = {"LOGIN_DISABLED": True} mocker.patch("app.setup.datastore.Client", MockDatastore) mocker.patch("app.setup.redis.Redis", fakeredis.FakeStrictRedis) the_app = create_app(setting_overrides=setting_overrides) the_app.config["SERVER_NAME"] = "test.localdomain" app_context = the_app.app_context() app_context.push() yield the_app app_context.pop()
def test_versioned_url_for_without_version(self): self._setting_overrides.update({ 'EQ_APPLICATION_VERSION': False, }) application = create_app(self._setting_overrides) application.config['SERVER_NAME'] = 'test' # Patches the application version, since it's used in `versioned_url_for` with application.app_context(), self.override_settings(): self.assertEqual('http://test/s/a.jpg?q=False', versioned_url_for('static', filename='a.jpg'))
def test_adds_rabbit_submitter_to_the_application(self): # Given self._setting_overrides["EQ_SUBMISSION_BACKEND"] = "rabbitmq" self._setting_overrides["EQ_RABBITMQ_HOST"] = "host-1" self._setting_overrides["EQ_RABBITMQ_HOST_SECONDARY"] = "host-2" # When application = create_app(self._setting_overrides) # Then assert isinstance(application.eq["submitter"], RabbitMQSubmitter)
def test_adds_gcs_submitter_to_the_application(self): # Given self._setting_overrides["EQ_SUBMISSION_BACKEND"] = "gcs" self._setting_overrides["EQ_GCS_SUBMISSION_BUCKET_ID"] = "123" # When with patch("google.cloud.storage.Client"): application = create_app(self._setting_overrides) # Then assert isinstance(application.eq["submitter"], GCSSubmitter)
def test_setup_sql(self): self._setting_overrides['EQ_STORAGE_BACKEND'] = 'sql' db_string = 'sqlite:////tmp/test.db' self._setting_overrides['SQLALCHEMY_DATABASE_URI'] = db_string application = create_app(self._setting_overrides) self.assertFalse(application.config['SQLALCHEMY_TRACK_MODIFICATIONS']) self.assertEqual(60, application.config['SQLALCHEMY_POOL_RECYCLE']) self.assertEqual(db_string, application.config['SQLALCHEMY_DATABASE_URI'])
def test_schema_cache_on_app_start_up(): _load_schema_from_name.cache_clear() cache_info = _load_schema_from_name.cache_info() assert cache_info.currsize == 0 assert cache_info.hits == 0 # create app and load schemas into cache create_app() total_schemas = sum( len(schemas) for schemas in get_schema_path_map(include_test_schemas=True).values()) cache_info = _load_schema_from_name.cache_info() assert cache_info.currsize > 0 and cache_info.currsize == total_schemas assert cache_info.hits == 0 # loads schema again to fetch from cache cache_questionnaire_schemas() cache_info = _load_schema_from_name.cache_info() assert cache_info.currsize == total_schemas assert cache_info.hits == total_schemas
def test_adds_gcs_feedback_to_the_application(self): # Given self._setting_overrides["EQ_FEEDBACK_BACKEND"] = "gcs" self._setting_overrides["EQ_GCS_FEEDBACK_BUCKET_ID"] = "123456" # When with patch("google.cloud.storage.Client"): application = create_app(self._setting_overrides) # Then assert isinstance(application.eq["feedback_submitter"], GCSFeedbackSubmitter)
def test_adds_logging_of_request_ids(self): with patch('app.setup.logger') as logger: self._setting_overrides.update({ 'EQ_DEV_MODE': True, 'EQ_APPLICATION_VERSION': False }) application = create_app(self._setting_overrides) application.test_client().get('/') self.assertEqual(1, logger.new.call_count) _, kwargs = logger.new.call_args self.assertTrue(UUID(kwargs['request_id'], version=4))
def test_csp_policy_headers(self): cdn_url = "https://cdn.test.domain" address_lookup_api_url = "https://ai.test.domain" self._setting_overrides = { "EQ_ENABLE_LIVE_RELOAD": False, "CDN_URL": cdn_url, "ADDRESS_LOOKUP_API_URL": address_lookup_api_url, } with create_app(self._setting_overrides).test_client() as client: headers = client.get( "/", headers={ "X-Forwarded-Proto": "https" }, # set protocal so that talisman sets HSTS headers ).headers csp_policy_parts = headers["Content-Security-Policy"].split("; ") self.assertIn(f"default-src 'self' {cdn_url}", csp_policy_parts) self.assertIn( "script-src 'self' https://www.googletagmanager.com https://www.google-analytics.com " f"https://ssl.google-analytics.com 'unsafe-inline' {cdn_url} 'nonce-{request.csp_nonce}'", csp_policy_parts, ) self.assertIn( f"style-src 'self' https://tagmanager.google.com https://fonts.googleapis.com 'unsafe-inline' {cdn_url}", csp_policy_parts, ) self.assertIn( f"img-src 'self' data: https://www.google-analytics.com https://ssl.gstatic.com https://www.gstatic.com {cdn_url}", csp_policy_parts, ) self.assertIn( f"font-src 'self' data: https://fonts.gstatic.com {cdn_url}", csp_policy_parts, ) self.assertIn("frame-src https://www.googletagmanager.com", csp_policy_parts) self.assertIn( f"connect-src 'self' https://www.google-analytics.com {cdn_url} {address_lookup_api_url}", csp_policy_parts, ) self.assertIn( "object-src 'none'", csp_policy_parts, ) self.assertIn( "base-uri 'none'", csp_policy_parts, )
def setUp(self): self._ds = patch("app.setup.datastore.Client", MockDatastore) self._ds.start() self._redis = patch("app.setup.redis.Redis", fakeredis.FakeStrictRedis) self._redis.start() setting_overrides = {"LOGIN_DISABLED": self.LOGIN_DISABLED} setting_overrides.update(self.setting_overrides) self._app = create_app(setting_overrides) self._app.config["SERVER_NAME"] = "test.localdomain" self._app_context = self._app.app_context() self._app_context.push()
def test_versioned_url_for_regular_assets(self): self._setting_overrides.update({ 'EQ_MINIMIZE_ASSETS': False, 'EQ_APPLICATION_VERSION': False, }) application = create_app(self._setting_overrides) application.config['SERVER_NAME'] = 'test' with application.app_context(), self.override_settings(): self.assertEqual('http://test/s/some.css?q=False', versioned_url_for('static', filename='some.css')) self.assertEqual('http://test/s/some.js?q=False', versioned_url_for('static', filename='some.js'))
def test_adds_pub_sub_to_the_application(self): # Given self._setting_overrides["EQ_PUBLISHER_BACKEND"] = "pubsub" self._setting_overrides["EQ_FULFILMENT_TOPIC_ID"] = "123" # When with patch( "app.publisher.publisher.google.auth._default._get_explicit_environ_credentials", return_value=(Mock(), "test-project-id"), ): application = create_app(self._setting_overrides) # Then assert isinstance(application.eq["publisher"], PubSubPublisher)
def test_adds_logging_of_span_and_trace(self): with patch("app.setup.logger") as logger: self._setting_overrides.update({"EQ_APPLICATION_VERSION": False}) application = create_app(self._setting_overrides) x_cloud_headers = { "X-Cloud-Trace-Context": "0123456789/0123456789012345678901;o=1" } application.test_client().get("/", headers=x_cloud_headers) self.assertEqual(1, logger.bind.call_count) _, kwargs = logger.bind.call_args self.assertTrue(kwargs["span"] == "0123456789012345678901") self.assertTrue(kwargs["trace"] == "0123456789")
def test_adds_cloud_task_publisher_to_the_application(self): self._setting_overrides[ "EQ_SUBMISSION_CONFIRMATION_BACKEND"] = "cloud-tasks" self._setting_overrides[ "EQ_SUBMISSION_CONFIRMATION_CLOUD_FUNCTION_NAME"] = "test" # When with patch( "google.auth._default._get_explicit_environ_credentials", return_value=(Mock(), "test-project-id"), ): application = create_app(self._setting_overrides) # Then assert isinstance(application.eq["cloud_tasks"], CloudTaskPublisher)
def setUp(self): self._ddb = mock_dynamodb2() self._ddb.start() setting_overrides = { 'SQLALCHEMY_DATABASE_URI': 'sqlite://', 'LOGIN_DISABLED': self.LOGIN_DISABLED, 'EQ_DYNAMODB_ENDPOINT': None, } self._app = create_app(setting_overrides) self._app.config['SERVER_NAME'] = 'test.localdomain' self._app_context = self._app.app_context() self._app_context.push() setup_tables()
def test_enforces_secure_headers(self): self._setting_overrides["EQ_ENABLE_LIVE_RELOAD"] = False with create_app(self._setting_overrides).test_client() as client: headers = client.get( "/", headers={ "X-Forwarded-Proto": "https" }, # set protocal so that talisman sets HSTS headers ).headers self.assertEqual("no-cache, no-store, must-revalidate", headers["Cache-Control"]) self.assertEqual("no-cache", headers["Pragma"]) self.assertEqual( "max-age=31536000; includeSubDomains", headers["Strict-Transport-Security"], ) self.assertEqual("DENY", headers["X-Frame-Options"]) self.assertEqual("1; mode=block", headers["X-Xss-Protection"]) self.assertEqual("nosniff", headers["X-Content-Type-Options"]) csp_policy_parts = headers["Content-Security-Policy"].split("; ") self.assertIn("default-src 'self' https://cdn.ons.gov.uk", csp_policy_parts) self.assertIn( f"script-src 'self' https://cdn.ons.gov.uk https://www.googletagmanager.com 'unsafe-inline' 'unsafe-eval' 'nonce-{request.csp_nonce}'", csp_policy_parts, ) self.assertIn( "style-src 'self' https://cdn.ons.gov.uk https://tagmanager.google.com https://fonts.googleapis.com 'unsafe-inline'", csp_policy_parts, ) self.assertIn( "img-src 'self' data: https://cdn.ons.gov.uk https://www.google-analytics.com https://ssl.gstatic.com https://www.gstatic.com", csp_policy_parts, ) self.assertIn( "font-src 'self' data: https://cdn.ons.gov.uk https://fonts.gstatic.com", csp_policy_parts, ) self.assertIn("frame-src https://www.googletagmanager.com", csp_policy_parts) self.assertIn( "connect-src 'self' https://cdn.ons.gov.uk https://cdn.eq.census-gcp.onsdigital.uk", csp_policy_parts, )
def test_enforces_secure_headers(self): self._setting_overrides["EQ_ENABLE_LIVE_RELOAD"] = False with create_app(self._setting_overrides).test_client() as client: headers = client.get( "/", headers={ "X-Forwarded-Proto": "https" }, # set protocal so that talisman sets HSTS headers ).headers self.assertEqual("no-cache, no-store, must-revalidate", headers["Cache-Control"]) self.assertEqual("no-cache", headers["Pragma"]) self.assertEqual( "max-age=31536000; includeSubDomains", headers["Strict-Transport-Security"], ) self.assertEqual("DENY", headers["X-Frame-Options"]) self.assertEqual("1; mode=block", headers["X-Xss-Protection"]) self.assertEqual("nosniff", headers["X-Content-Type-Options"])