test(appsec): make server startup failures diagnosable and port reuse safe - #19674
Merged
gh-worker-dd-mergequeue-cf854d[bot] merged 2 commits intoAug 14, 2026
Conversation
Codeowners resolved asResolved from the full PR diff against No remaining files require a CODEOWNERS review. |
Circular import analysis
|
Dependency direction analysis
|
Contributor
🎉 All green!🧪 All tests passed 🔗 Commit SHA: a487a79 | Docs | Datadog PR Page | Give us feedback! |
… safe DD_KUBWIR DD_FQAL2Q DD_F20P7M DD_WQNXQ5 DD_D07T24 DD_6TE3BU DD_U55XLN DD_4FSP2S DD_G9FTB2 DD_W2G1F6 DD_3LVVS3 DD_UJ438X DD_1AVE6M Every one of these quarantined tests fails in appsec_application_server, not in its own body: the recorded error is the "Server failed to start" assertion, and the runs that recorded a duration cluster at 22.0-22.6s against a ~13s startup budget. Two problems: - The assertion interpolated getattr(server_process, "stdout", None), which is None for a multiprocessing.Process and a stream rather than text for a Popen, so all 13 report a literal "None" and cannot be debugged. Report instead the facts that are not already in the captured output: the exit code (None means too slow, non-zero means it died), whether the port is still taken, and the command. - 31 tests share port 8050 and these suites run serially, while teardown is best effort: SIGTERM to the process group then join(timeout=5), each step wrapped in except/pass. A gunicorn worker outliving that leaves the port taken and the next test cannot bind. Wait for the port before starting and after tearing down. The wait tests whether the port can be bound rather than whether it accepts connections: connect() reports a port as free once a bound server's listen backlog fills, which is exactly the wedged state worth detecting, and it opens real connections to a live server. Warn rather than raise when the port never frees; raising in teardown would turn a leak into a failure of every appsec server suite. APPSEC-69623 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…nges DD_KUBWIR DD_FQAL2Q DD_F20P7M DD_WQNXQ5 DD_D07T24 DD_6TE3BU DD_U55XLN DD_4FSP2S DD_G9FTB2 DD_W2G1F6 DD_3LVVS3 DD_UJ438X DD_1AVE6M The previous commit changed tests/appsec/appsec_utils.py and none of the 13 tests it targets ran: the file matched 15 suites but not one of the appsec_integrations_* suites that import it, because @appsec covers ddtrace/appsec/* source only and each suite lists just its own test directory. Add the file to the paths of the six suites whose tests import it, so a change to the shared server helper is exercised by the suites that depend on it. It now matches 21 suites instead of 15. The keys are the 13 quarantined tests this branch targets. The suites now triggered also hold quarantined tests this branch does not fix (the SCA reachability pair, fixed in another PR, and test_django_insecure_cookie_secure, an index_aspect bug), so those are deliberately left unkeyed rather than un-quarantined on a commit that would not fix them. APPSEC-69623 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
christophe-papazian
force-pushed
the
christophe-papazian/appsec-server-startup-flake
branch
from
August 13, 2026 14:52
075997e to
a487a79
Compare
christophe-papazian
marked this pull request as ready for review
August 13, 2026 15:16
florentinl
approved these changes
Aug 13, 2026
christophe-papazian
marked this pull request as draft
August 13, 2026 15:29
christophe-papazian
marked this pull request as ready for review
August 14, 2026 08:18
gh-worker-dd-mergequeue-cf854d
Bot
deleted the
christophe-papazian/appsec-server-startup-flake
branch
August 14, 2026 09:02
christophe-papazian
added a commit
that referenced
this pull request
Aug 14, 2026
tests/appsec/app.py matched only the threats suites, none of which run the integration tests that actually serve it, so the shutdown change in the previous commit shipped without any of them running. Same gap that #19674 closed for appsec_utils.py. 15 -> 19 matched suites. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
APPSEC-69623
All 13 quarantined tests here fail in
appsec_application_server, not in their own bodies. The recorded error is the "Server failed to start" assertion, and the runs with a duration cluster at 22.0–22.6s against a ~13s startup budget.The failures are undebuggable. The assertion interpolated
getattr(server_process, "stdout", None)—Nonefor amultiprocessing.Process, a stream rather than text for aPopen— so every one reports a literalNone. It now reports what is not already in the captured output: exit code (None= too slow, non-zero = died), whether the port is still taken, and the command.Port 8050 is shared by 31 tests, these suites run serially, and teardown is best effort (SIGTERM to the process group,
join(timeout=5), each step inexcept/pass). A gunicorn worker outliving that leaves the port taken and the next test can't bind. Now waits for the port both before starting and after tearing down.The suites never ran. The first push was green with zero test events for any of the 13 tests:
tests/appsec/appsec_utils.pymatched 15 suites but none of theappsec_integrations_*ones that import it, because@appseccoversddtrace/appsec/*source only and each suite lists just its own test directory. The second commit adds the file to the six suites whose tests import it (grep-verified:flask_tests7 files,fastapi_tests2,django_tests1, plusiast_packagesandiast_tdd_propagation). It now matches 21 suites;suitespec-checkpasses both gates.Notes:
connect()reports a port free once a bound server's listen backlog fills — exactly the wedged state worth catching — and opens real connections to a live server. My first version did useconnect()and wrongly reported "released" after 0.3s against a still-bound socket.-nin the riotfile, so the collision is sequential, not concurrent.test_django_insecure_cookie_secure(anindex_aspectbug). Deliberately left unkeyed rather than un-quarantined on a commit that would not fix them.Blast radius:
appsec_application_serverbacks all five server context managers, and the suitespec change widens what CI runs on such edits — both intended.🤖 Generated with Claude Code