coverage: Clean up coverage configuration.

Signed-off-by: Anders Kaseorg <anders@zulip.com>
This commit is contained in:
Anders Kaseorg 2023-05-29 15:01:44 -07:00 committed by Tim Abbott
parent 19dee5450d
commit b5e5728112
14 changed files with 53 additions and 79 deletions

View File

@ -152,7 +152,7 @@ def communities_view(request: HttpRequest) -> HttpResponse:
org_types.pop("unspecified", None) org_types.pop("unspecified", None)
# Change display name of non-profit orgs. # Change display name of non-profit orgs.
if org_types.get("nonprofit"): if org_types.get("nonprofit"): # nocoverage
org_types["nonprofit"]["name"] = "Non-profit" org_types["nonprofit"]["name"] = "Non-profit"
return TemplateResponse( return TemplateResponse(

View File

@ -6,7 +6,7 @@ exclude_lines =
# Don't complain if non-runnable code isn't run: # Don't complain if non-runnable code isn't run:
if False: if False:
# Don't require coverage for base class NotImplementedErrors # Don't require coverage for base class NotImplementedErrors
raise NotImplementedError() raise NotImplementedError
# Don't require coverage for test suite AssertionError -- they're usually for clarity # Don't require coverage for test suite AssertionError -- they're usually for clarity
raise AssertionError raise AssertionError
# Don't require coverage for __str__ statements just used for printing # Don't require coverage for __str__ statements just used for printing
@ -31,27 +31,11 @@ data_file=var/.coverage
# overhead but is very useful for finding existing tests for a code path. # overhead but is very useful for finding existing tests for a code path.
dynamic_context=test_function dynamic_context=test_function
omit = source =
*/zulip-venv-cache/* analytics/
*/migrations/* confirmation/
*/management/commands/* corporate/
# Parts of the test runner infrastructure pgroonga/
tools/test-backend zerver/
zerver/lib/test_fixtures.py zilencer/
zerver/lib/test_runner.py zproject/
# Has its own independent test suite
zerver/openapi/python_examples.py
# Debugging tools that don't lend themselves well to unit tests
zerver/lib/debug.py
# Part of provisioning/populate_db
zerver/lib/generate_test_data.py
# Excluded because its coverage state is flaky.
zerver/tornado/ioloop_logging.py
# Zulip's library for use in scripts
scripts/lib/zulip_tools.py
# Used only for the legacy Zephyr integration, and unlikely to ever be unit-tested
zerver/lib/ccache.py
# Settings.py files are hard to test
zproject/*settings.py
# https://github.com/davidhalter/jedi/issues/1122
blub

View File

@ -31,39 +31,19 @@ import responses
from django.conf import settings from django.conf import settings
from django.test.utils import get_runner from django.test.utils import get_runner
target_fully_covered = [ source_files = [
"analytics/lib/*.py", "analytics/**/*.py",
"analytics/models.py", "confirmation/**/*.py",
"analytics/tests/*.py", "corporate/**/*.py",
"analytics/views/*.py", "pgroonga/**/*.py",
# zerver/ and zerver/lib/ are important core files "zerver/**/*.py",
"zerver/*.py", "zilencer/**/*.py",
"zerver/actions/*.py", "zproject/**/*.py",
"zerver/lib/*.py",
"zerver/lib/*/*.py",
"zerver/lib/*/*/*.py",
"zerver/data_import/*.py",
"zerver/templatetags/*.py",
"zerver/tornado/*.py",
# Billing files require 100% test coverage
"corporate/lib/stripe.py",
"corporate/views.py",
# Test files should have 100% coverage; test code that isn't run
# is likely a bug in the test.
"zerver/tests/*.py",
"corporate/tests/*.py",
# As a project, we require 100% test coverage in the views files.
"zerver/views/*.py",
"zproject/backends.py",
"confirmation/*.py",
"zerver/webhooks/*/*.py",
# Once we have a nice negative tests system, we can add these:
# 'zerver/webhooks/*/*.py',
# 'zerver/webhooks/*/*/*.py',
"zerver/worker/*.py",
] ]
not_yet_fully_covered = [ not_yet_fully_covered = [
"*/migrations/*.py",
"*/management/commands/*.py",
# Analytics fixtures library is used to generate test fixtures; # Analytics fixtures library is used to generate test fixtures;
# isn't properly accounted for in test coverage analysis since it # isn't properly accounted for in test coverage analysis since it
# runs before tests. # runs before tests.
@ -100,14 +80,12 @@ not_yet_fully_covered = [
"zerver/lib/fix_unreads.py", "zerver/lib/fix_unreads.py",
"zerver/lib/import_realm.py", "zerver/lib/import_realm.py",
"zerver/lib/logging_util.py", "zerver/lib/logging_util.py",
"zerver/lib/migrate.py",
"zerver/lib/profile.py", "zerver/lib/profile.py",
"zerver/lib/queue.py", "zerver/lib/queue.py",
"zerver/lib/sqlalchemy_utils.py", "zerver/lib/sqlalchemy_utils.py",
"zerver/lib/storage.py", "zerver/lib/storage.py",
"zerver/lib/zephyr.py", "zerver/lib/zephyr.py",
"zerver/lib/templates.py", "zerver/lib/templates.py",
"zerver/templatetags/minified_js.py",
# Low priority for coverage # Low priority for coverage
"zerver/lib/ccache.py", "zerver/lib/ccache.py",
"zerver/lib/generate_test_data.py", "zerver/lib/generate_test_data.py",
@ -115,7 +93,10 @@ not_yet_fully_covered = [
"zerver/lib/test_fixtures.py", "zerver/lib/test_fixtures.py",
"zerver/lib/test_runner.py", "zerver/lib/test_runner.py",
"zerver/lib/test_console_output.py", "zerver/lib/test_console_output.py",
"zerver/openapi/curl_param_value_generators.py",
"zerver/openapi/javascript_examples.py",
"zerver/openapi/python_examples.py", "zerver/openapi/python_examples.py",
"zerver/openapi/test_curl_examples.py",
# Tornado should ideally have full coverage, but we're not there. # Tornado should ideally have full coverage, but we're not there.
"zerver/tornado/descriptors.py", "zerver/tornado/descriptors.py",
"zerver/tornado/django_api.py", "zerver/tornado/django_api.py",
@ -142,11 +123,18 @@ not_yet_fully_covered = [
# Cannot have coverage, as tests run in a transaction # Cannot have coverage, as tests run in a transaction
"zerver/lib/safe_session_cached_db.py", "zerver/lib/safe_session_cached_db.py",
"zerver/lib/singleton_bmemcached.py", "zerver/lib/singleton_bmemcached.py",
# Branches in Django settings files are hard to test
"zproject/computed_settings.py",
"zproject/dev_settings.py",
"zproject/test_extra_settings.py",
# Better tested in a full deployment
"zproject/sentry.py",
"zproject/wsgi.py",
] ]
enforce_fully_covered = sorted( enforce_fully_covered = sorted(
{path for target in target_fully_covered for path in glob.glob(target)} {path for target in source_files for path in glob.glob(target, recursive=True)}
- {path for target in not_yet_fully_covered for path in glob.glob(target)} - {path for target in not_yet_fully_covered for path in glob.glob(target, recursive=True)}
) )
FAILED_TEST_PATH = "var/last_test_failure.json" FAILED_TEST_PATH = "var/last_test_failure.json"
@ -473,7 +461,7 @@ def main() -> None:
for path in not_yet_fully_covered: for path in not_yet_fully_covered:
try: try:
missing_lines = cov.analysis2(path)[3] missing_lines = cov.analysis2(path)[3]
if len(missing_lines) == 0 and path != "zerver/lib/migrate.py": if not missing_lines:
print( print(
f"ERROR: {path} has complete backend test coverage but is still in not_yet_fully_covered." f"ERROR: {path} has complete backend test coverage but is still in not_yet_fully_covered."
) )

View File

@ -386,12 +386,12 @@ def validate_against_openapi_schema(
if (endpoint, method) in EXCLUDE_UNDOCUMENTED_ENDPOINTS: if (endpoint, method) in EXCLUDE_UNDOCUMENTED_ENDPOINTS:
return False return False
# Return true for endpoints with only response documentation remaining # Return true for endpoints with only response documentation remaining
if (endpoint, method) in EXCLUDE_DOCUMENTED_ENDPOINTS: if (endpoint, method) in EXCLUDE_DOCUMENTED_ENDPOINTS: # nocoverage
return True return True
# Check if the response matches its code # Check if the response matches its code
if status_code.startswith("2") and ( if status_code.startswith("2") and (
content.get("result", "success").lower() not in ["success", "partially_completed"] content.get("result", "success").lower() not in ["success", "partially_completed"]
): ): # nocoverage
raise SchemaError("Response is not 200 but is validating against 200 schema") raise SchemaError("Response is not 200 but is validating against 200 schema")
# Code is not declared but appears in various 400 responses. If # Code is not declared but appears in various 400 responses. If
# common, it can be added to 400 response schema # common, it can be added to 400 response schema

View File

@ -14,7 +14,7 @@ ZULIP_PATH = os.path.join(os.path.dirname(os.path.abspath(__file__)), "../../../
# This is used only by the Puppeteer tests to clear all the cache after each run. # This is used only by the Puppeteer tests to clear all the cache after each run.
@csrf_exempt @csrf_exempt
@require_post @require_post
def remove_caches(request: HttpRequest) -> HttpResponse: def remove_caches(request: HttpRequest) -> HttpResponse: # nocoverage
cache = get_cache_backend(None) cache = get_cache_backend(None)
cache.clear() cache.clear()
clear_client_cache() clear_client_cache()

View File

@ -5,7 +5,9 @@ from zerver.lib.camo import is_camo_url_valid
from zerver.lib.thumbnail import generate_thumbnail_url from zerver.lib.thumbnail import generate_thumbnail_url
def handle_camo_url(request: HttpRequest, digest: str, received_url: str) -> HttpResponse: def handle_camo_url(
request: HttpRequest, digest: str, received_url: str
) -> HttpResponse: # nocoverage
original_url = bytes.fromhex(received_url).decode() original_url = bytes.fromhex(received_url).decode()
if is_camo_url_valid(digest, original_url): if is_camo_url_valid(digest, original_url):
return redirect(generate_thumbnail_url(original_url)) return redirect(generate_thumbnail_url(original_url))

View File

@ -124,7 +124,7 @@ def api_dev_fetch_api_key(request: HttpRequest, username: str = REQ()) -> HttpRe
raise RealmDeactivatedError raise RealmDeactivatedError
if return_data.get("inactive_user"): if return_data.get("inactive_user"):
raise UserDeactivatedError raise UserDeactivatedError
if return_data.get("invalid_subdomain"): if return_data.get("invalid_subdomain"): # nocoverage
raise InvalidSubdomainError raise InvalidSubdomainError
if user_profile is None: if user_profile is None:
# Since we're not actually checking passwords, this condition # Since we're not actually checking passwords, this condition

View File

@ -93,7 +93,7 @@ def get_fixtures(request: HttpRequest, integration_name: str = REQ()) -> HttpRes
valid_integration_name, "".join(fixture.split(".")[:-1]) valid_integration_name, "".join(fixture.split(".")[:-1])
) )
def fix_name(header: str) -> str: def fix_name(header: str) -> str: # nocoverage
if header.startswith("HTTP_"): # HTTP_ is a prefix intended for Django. if header.startswith("HTTP_"): # HTTP_ is a prefix intended for Django.
return header[len("HTTP_") :] return header[len("HTTP_") :]
return header return header
@ -114,7 +114,7 @@ def check_send_webhook_fixture_message(
) -> HttpResponseBase: ) -> HttpResponseBase:
try: try:
custom_headers_dict = orjson.loads(custom_headers) custom_headers_dict = orjson.loads(custom_headers)
except orjson.JSONDecodeError as ve: except orjson.JSONDecodeError as ve: # nocoverage
raise JsonableError(f"Custom HTTP headers are not in a valid JSON format. {ve}") # nolint raise JsonableError(f"Custom HTTP headers are not in a valid JSON format. {ve}") # nolint
response = send_webhook_fixture_message(url, body, is_json, custom_headers_dict) response = send_webhook_fixture_message(url, body, is_json, custom_headers_dict)
@ -130,7 +130,7 @@ def send_all_webhook_fixture_messages(
request: HttpRequest, url: str = REQ(), integration_name: str = REQ() request: HttpRequest, url: str = REQ(), integration_name: str = REQ()
) -> HttpResponse: ) -> HttpResponse:
valid_integration_name = get_valid_integration_name(integration_name) valid_integration_name = get_valid_integration_name(integration_name)
if not valid_integration_name: if not valid_integration_name: # nocoverage
raise ResourceNotFoundError(f'"{integration_name}" is not a valid webhook integration.') raise ResourceNotFoundError(f'"{integration_name}" is not a valid webhook integration.')
fixtures_dir = os.path.join(ZULIP_PATH, f"zerver/webhooks/{valid_integration_name}/fixtures") fixtures_dir = os.path.join(ZULIP_PATH, f"zerver/webhooks/{valid_integration_name}/fixtures")

View File

@ -40,7 +40,7 @@ def generate_demo_realm_name() -> str:
@csrf_exempt @csrf_exempt
def register_development_user(request: HttpRequest) -> HttpResponse: def register_development_user(request: HttpRequest) -> HttpResponse:
realm = get_realm_from_request(request) realm = get_realm_from_request(request)
if realm is None: if realm is None: # nocoverage
return HttpResponseRedirect( return HttpResponseRedirect(
f"{settings.EXTERNAL_URI_SCHEME}{settings.REALM_HOSTS['zulip']}/devtools/register_user/", f"{settings.EXTERNAL_URI_SCHEME}{settings.REALM_HOSTS['zulip']}/devtools/register_user/",
status=307, status=307,

View File

@ -16,7 +16,7 @@ config_file.read("/etc/zulip/zulip.conf")
PRODUCTION = config_file.has_option("machine", "deploy_type") PRODUCTION = config_file.has_option("machine", "deploy_type")
DEVELOPMENT = not PRODUCTION DEVELOPMENT = not PRODUCTION
secrets_file = configparser.RawConfigParser() secrets_file = configparser.RawConfigParser()
if PRODUCTION: if PRODUCTION: # nocoverage
secrets_file.read("/etc/zulip/zulip-secrets.conf") secrets_file.read("/etc/zulip/zulip-secrets.conf")
else: else:
secrets_file.read(os.path.join(DEPLOY_ROOT, "zproject/dev-secrets.conf")) secrets_file.read(os.path.join(DEPLOY_ROOT, "zproject/dev-secrets.conf"))
@ -37,7 +37,7 @@ def get_secret(
def get_secret( def get_secret(
key: str, default_value: Optional[str] = None, development_only: bool = False key: str, default_value: Optional[str] = None, development_only: bool = False
) -> Optional[str]: ) -> Optional[str]:
if development_only and PRODUCTION: if development_only and PRODUCTION: # nocoverage
return default_value return default_value
return secrets_file.get("secrets", key, fallback=default_value) return secrets_file.get("secrets", key, fallback=default_value)

View File

@ -11,7 +11,7 @@ from .default_settings import * # noqa: F403 isort: skip
# Import prod_settings after determining the deployment/machine type # Import prod_settings after determining the deployment/machine type
from .config import PRODUCTION from .config import PRODUCTION
if PRODUCTION: if PRODUCTION: # nocoverage
from .prod_settings import * # noqa: F403 isort: skip from .prod_settings import * # noqa: F403 isort: skip
else: else:
# For the Dev VM environment, we use the same settings as the # For the Dev VM environment, we use the same settings as the

View File

@ -10,7 +10,7 @@ from .config import DEVELOPMENT, PRODUCTION, get_secret
if TYPE_CHECKING: if TYPE_CHECKING:
from django_auth_ldap.config import LDAPSearch from django_auth_ldap.config import LDAPSearch
if PRODUCTION: if PRODUCTION: # nocoverage
from .prod_settings import EXTERNAL_HOST, ZULIP_ADMINISTRATOR from .prod_settings import EXTERNAL_HOST, ZULIP_ADMINISTRATOR
else: else:
from .dev_settings import EXTERNAL_HOST, ZULIP_ADMINISTRATOR from .dev_settings import EXTERNAL_HOST, ZULIP_ADMINISTRATOR

View File

@ -109,7 +109,7 @@ if use_prod_static:
urls += [ urls += [
path("static/<path:path>", serve, {"document_root": settings.STATIC_ROOT}), path("static/<path:path>", serve, {"document_root": settings.STATIC_ROOT}),
] ]
else: else: # nocoverage
def serve_static(request: HttpRequest, path: str) -> FileResponse: def serve_static(request: HttpRequest, path: str) -> FileResponse:
response = staticfiles_serve(request, path) response = staticfiles_serve(request, path)

View File

@ -225,7 +225,7 @@ from zerver.views.zephyr import webathena_kerberos_login
from zproject import dev_urls from zproject import dev_urls
from zproject.legacy_urls import legacy_urls from zproject.legacy_urls import legacy_urls
if settings.TWO_FACTOR_AUTHENTICATION_ENABLED: if settings.TWO_FACTOR_AUTHENTICATION_ENABLED: # nocoverage
from two_factor.gateways.twilio.urls import urlpatterns as tf_twilio_urls from two_factor.gateways.twilio.urls import urlpatterns as tf_twilio_urls
from two_factor.urls import urlpatterns as tf_urls from two_factor.urls import urlpatterns as tf_urls
@ -785,7 +785,7 @@ urls += [
] ]
# Front-end Sentry requests tunnel through the server, if enabled # Front-end Sentry requests tunnel through the server, if enabled
if settings.SENTRY_FRONTEND_DSN: if settings.SENTRY_FRONTEND_DSN: # nocoverage
urls += [path("error_tracing", sentry_tunnel)] urls += [path("error_tracing", sentry_tunnel)]
# User documentation site # User documentation site
@ -818,7 +818,7 @@ urls += [
path("policies/<slug:article>", policy_documentation_view), path("policies/<slug:article>", policy_documentation_view),
] ]
if not settings.CORPORATE_ENABLED: if not settings.CORPORATE_ENABLED: # nocoverage
# This conditional behavior cannot be tested directly, since # This conditional behavior cannot be tested directly, since
# urls.py is not readily reloaded in Django tests. See the block # urls.py is not readily reloaded in Django tests. See the block
# comment inside apps_view for details. # comment inside apps_view for details.
@ -827,7 +827,7 @@ if not settings.CORPORATE_ENABLED:
] ]
# Two-factor URLs # Two-factor URLs
if settings.TWO_FACTOR_AUTHENTICATION_ENABLED: if settings.TWO_FACTOR_AUTHENTICATION_ENABLED: # nocoverage
urls += [path("", include(tf_urls)), path("", include(tf_twilio_urls))] urls += [path("", include(tf_urls)), path("", include(tf_twilio_urls))]
if settings.DEVELOPMENT: if settings.DEVELOPMENT: