From 4299fe1648acf4fc3f49c026edef40a168c3a0c2 Mon Sep 17 00:00:00 2001 From: Ana-Maria Dumitrache Date: Fri, 31 Jul 2026 11:01:38 +0200 Subject: [PATCH 1/4] test(http): cover consent blocking for inproc/breakpad crash reporter --- tests/test_integration_http.py | 44 ++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/tests/test_integration_http.py b/tests/test_integration_http.py index 2aab22131..039436a3e 100644 --- a/tests/test_integration_http.py +++ b/tests/test_integration_http.py @@ -38,6 +38,7 @@ assert_attachment_view_hierarchy, assert_before_breadcrumb, assert_no_breadcrumbs, + wait_for_file, ) from .conditions import ( has_http, @@ -366,6 +367,49 @@ def test_external_crash_reporter_http(cmake, httpserver, build_args): assert_user_feedback(envelope) +@pytest.mark.parametrize( + "build_args", + [ + ({"SENTRY_BACKEND": "inproc"}), + pytest.param( + {"SENTRY_BACKEND": "breakpad"}, + marks=pytest.mark.skipif( + not has_breakpad or is_qemu, reason="test needs breakpad backend" + ), + ), + ], +) +def test_external_crash_reporter_consent_revoked(cmake, httpserver, build_args): + """With consent revoked, must not launch the external reporter. + + Before the fix, crash-reporter + user-consent-revoke still spawned + sentry_crash_reporter, which uploaded via HTTP. Consent should block that + the same way it blocks the normal transport path. + """ + tmp_path = cmake(["sentry_example", "sentry_crash_reporter"], build_args) + cache_dir = tmp_path.joinpath(".sentry-native/cache") + env = dict(os.environ, SENTRY_DSN=make_dsn(httpserver)) + + run( + tmp_path, + "sentry_example", + [ + "log", + "crash-reporter", + "cache-keep", + "require-user-consent", + "user-consent-revoke", + "crash", + ], + expect_failure=True, + env=env, + ) + + assert len(httpserver.log) == 0 + assert wait_for_file(cache_dir / "*.envelope") + assert len(list(cache_dir.glob("*.envelope"))) == 1 + + @pytest.mark.skipif(is_qemu, reason="unreliable under qemu-user") def test_exception_and_session_http(cmake, httpserver): tmp_path = cmake(["sentry_example"], {"SENTRY_BACKEND": "none"}) From 6329f995f0d9a5d503ad26e5c7d64749a0074e93 Mon Sep 17 00:00:00 2001 From: Ana-Maria Dumitrache Date: Fri, 31 Jul 2026 11:06:09 +0200 Subject: [PATCH 2/4] test(http): discard crash envelope when consent revoked without cache-keep --- tests/test_integration_http.py | 38 ++++++++++++++++++++++++++++++++ tests/test_integration_native.py | 25 +++++++++++++++++++++ 2 files changed, 63 insertions(+) diff --git a/tests/test_integration_http.py b/tests/test_integration_http.py index 039436a3e..beab44021 100644 --- a/tests/test_integration_http.py +++ b/tests/test_integration_http.py @@ -410,6 +410,44 @@ def test_external_crash_reporter_consent_revoked(cmake, httpserver, build_args): assert len(list(cache_dir.glob("*.envelope"))) == 1 +@pytest.mark.parametrize( + "build_args", + [ + ({"SENTRY_BACKEND": "inproc"}), + pytest.param( + {"SENTRY_BACKEND": "breakpad"}, + marks=pytest.mark.skipif( + not has_breakpad or is_qemu, reason="test needs breakpad backend" + ), + ), + ], +) +def test_external_crash_reporter_consent_revoked_no_cache( + cmake, httpserver, build_args +): + """With consent revoked and no cache_keep, the crash envelope is discarded.""" + tmp_path = cmake(["sentry_example", "sentry_crash_reporter"], build_args) + cache_dir = tmp_path.joinpath(".sentry-native/cache") + env = dict(os.environ, SENTRY_DSN=make_dsn(httpserver)) + + run( + tmp_path, + "sentry_example", + [ + "log", + "crash-reporter", + "require-user-consent", + "user-consent-revoke", + "crash", + ], + expect_failure=True, + env=env, + ) + + assert len(httpserver.log) == 0 + assert not cache_dir.exists() or len(list(cache_dir.glob("*.envelope"))) == 0 + + @pytest.mark.skipif(is_qemu, reason="unreliable under qemu-user") def test_exception_and_session_http(cmake, httpserver): tmp_path = cmake(["sentry_example"], {"SENTRY_BACKEND": "none"}) diff --git a/tests/test_integration_native.py b/tests/test_integration_native.py index 92b29c185..f9e490af4 100644 --- a/tests/test_integration_native.py +++ b/tests/test_integration_native.py @@ -989,6 +989,31 @@ def test_native_external_crash_reporter_consent_revoked(cmake, httpserver): assert len(list(cache_dir.glob("*.envelope"))) == 1 +def test_native_external_crash_reporter_consent_revoked_no_cache(cmake, httpserver): + """With consent revoked and no cache_keep, the daemon discards the crash envelope.""" + tmp_path = cmake( + ["sentry_example", "sentry_crash_reporter"], {"SENTRY_BACKEND": "native"} + ) + cache_dir = tmp_path / ".sentry-native" / "cache" + env = dict(os.environ, SENTRY_DSN=make_dsn(httpserver)) + + run_crash( + tmp_path, + "sentry_example", + [ + "log", + "crash-reporter", + "require-user-consent", + "user-consent-revoke", + "crash", + ], + env=env, + ) + + assert len(httpserver.log) == 0 + assert not cache_dir.exists() or len(list(cache_dir.glob("*.envelope"))) == 0 + + def test_crash_mode_minidump_only(cmake, httpserver): """Mode 1: Should produce envelope with minidump attachment only""" tmp_path = cmake(["sentry_example"], {"SENTRY_BACKEND": "native"}) From 719cd3051440e00a73ccdcc333f23351257e877d Mon Sep 17 00:00:00 2001 From: Ana-Maria Dumitrache Date: Fri, 31 Jul 2026 11:09:11 +0200 Subject: [PATCH 3/4] test: flush cached crash envelopes after consent is given --- tests/test_integration_http.py | 56 ++++++++++++++++++++++++++++++++ tests/test_integration_native.py | 45 +++++++++++++++++++++++++ 2 files changed, 101 insertions(+) diff --git a/tests/test_integration_http.py b/tests/test_integration_http.py index beab44021..6e8f99be0 100644 --- a/tests/test_integration_http.py +++ b/tests/test_integration_http.py @@ -448,6 +448,62 @@ def test_external_crash_reporter_consent_revoked_no_cache( assert not cache_dir.exists() or len(list(cache_dir.glob("*.envelope"))) == 0 +@pytest.mark.parametrize( + "build_args", + [ + ({"SENTRY_BACKEND": "inproc"}), + pytest.param( + {"SENTRY_BACKEND": "breakpad"}, + marks=pytest.mark.skipif( + not has_breakpad or is_qemu, reason="test needs breakpad backend" + ), + ), + ], +) +def test_external_crash_reporter_consent_flush(cmake, httpserver, build_args): + """Cached crash envelope uploads once consent is given.""" + tmp_path = cmake(["sentry_example", "sentry_crash_reporter"], build_args) + cache_dir = tmp_path.joinpath(".sentry-native/cache") + env = dict(os.environ, SENTRY_DSN=make_dsn(httpserver)) + + run( + tmp_path, + "sentry_example", + [ + "log", + "crash-reporter", + "cache-keep", + "http-retry", + "require-user-consent", + "user-consent-revoke", + "crash", + ], + expect_failure=True, + env=env, + ) + + assert wait_for_file(cache_dir / "*.envelope") + assert len(list(cache_dir.glob("*.envelope"))) == 1 + assert len(httpserver.log) == 0 + + httpserver.expect_oneshot_request("/api/123456/envelope/").respond_with_data("OK") + with httpserver.wait(timeout=10) as waiting: + run( + tmp_path, + "sentry_example", + [ + "log", + "cache-keep", + "http-retry", + "require-user-consent", + "user-consent-give", + ], + env=env, + ) + assert waiting.result + assert len(list(cache_dir.glob("*.envelope"))) == 0 + + @pytest.mark.skipif(is_qemu, reason="unreliable under qemu-user") def test_exception_and_session_http(cmake, httpserver): tmp_path = cmake(["sentry_example"], {"SENTRY_BACKEND": "none"}) diff --git a/tests/test_integration_native.py b/tests/test_integration_native.py index f9e490af4..787772f7d 100644 --- a/tests/test_integration_native.py +++ b/tests/test_integration_native.py @@ -1014,6 +1014,51 @@ def test_native_external_crash_reporter_consent_revoked_no_cache(cmake, httpserv assert not cache_dir.exists() or len(list(cache_dir.glob("*.envelope"))) == 0 +def test_native_external_crash_reporter_consent_flush(cmake, httpserver): + """Cached crash envelope uploads once consent is given.""" + tmp_path = cmake( + ["sentry_example", "sentry_crash_reporter"], {"SENTRY_BACKEND": "native"} + ) + cache_dir = tmp_path / ".sentry-native" / "cache" + env = dict(os.environ, SENTRY_DSN=make_dsn(httpserver)) + + run_crash( + tmp_path, + "sentry_example", + [ + "log", + "crash-reporter", + "cache-keep", + "http-retry", + "require-user-consent", + "user-consent-revoke", + "crash", + ], + env=env, + ) + + assert wait_for_file(cache_dir / "*.envelope") + assert len(list(cache_dir.glob("*.envelope"))) == 1 + assert len(httpserver.log) == 0 + + httpserver.expect_oneshot_request("/api/123456/envelope/").respond_with_data("OK") + with httpserver.wait(timeout=10) as waiting: + run( + tmp_path, + "sentry_example", + [ + "log", + "cache-keep", + "http-retry", + "require-user-consent", + "user-consent-give", + ], + env=env, + ) + assert waiting.result + assert len(list(cache_dir.glob("*.envelope"))) == 0 + + def test_crash_mode_minidump_only(cmake, httpserver): """Mode 1: Should produce envelope with minidump attachment only""" tmp_path = cmake(["sentry_example"], {"SENTRY_BACKEND": "native"}) From b0239dd59d421f73b6d6fa87cb270145777c5107 Mon Sep 17 00:00:00 2001 From: Ana-Maria Dumitrache Date: Mon, 3 Aug 2026 13:09:23 +0200 Subject: [PATCH 4/4] apply review suggestion --- tests/test_integration_native.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/test_integration_native.py b/tests/test_integration_native.py index 787772f7d..d1e36e4fa 100644 --- a/tests/test_integration_native.py +++ b/tests/test_integration_native.py @@ -1008,6 +1008,7 @@ def test_native_external_crash_reporter_consent_revoked_no_cache(cmake, httpserv "crash", ], env=env, + wait_for_daemon=True, ) assert len(httpserver.log) == 0