test: deflake JWT tamper, fuzzy picker, tag routing, liveliness, redis stall burst, and pre-commit interrupt tests - #39306
Conversation
Tamper tests rewrote the last two base64url characters of the signature, which on roughly 1 in 250 RS256 tokens (1 in 1000 HS256) only touched padding bits, so the decoded signature was unchanged and still verified. Corrupt the decoded signature bytes instead. The fuzzy picker driver sent keys after fixed sleeps, so a slow worker could receive the filter text before the widget had highlighted the match. Wait on the widget's highlighted choice instead. Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
🤖 Devin AI EngineerI'll be helping with this pull request! Here's what you should know: ✅ I will automatically:
Note: I can only respond to comments from users who have write access to this repository. ⚙️ Control Options:
|
|
PR #39306 has no labels (missing required |
Greptile SummaryThis PR hardens several flaky tests and ensures the dashboard lint report is removed when pre-commit linting is interrupted.
Confidence Score: 5/5The PR appears safe to merge; no outstanding or newly introduced actionable failures were identified. Both previous findings are manually resolved and fully addressed by the current code: liveliness now checks the median rather than the fastest poll, and tag-routing tests once again exercise
|
| Filename | Overview |
|---|---|
| scripts/pre_commit_lint.sh | Adds TERM and EXIT handling so the temporary eslint report is removed on completion, failure, or interruption. |
| tests/test_litellm/proxy/_experimental/mcp_server/outbound_credentials/test_session_credentials.py | Makes the session-credential tamper test deterministically modify decoded signature bytes. |
| tests/test_litellm/proxy/_experimental/mcp_server/outbound_credentials/test_session_token.py | Replaces padding-sensitive JWT mutations with deterministic signature-byte corruption. |
| tests/test_litellm/proxy/client/cli/autoroute/test_wizard.py | Replaces fixed input delays with synchronization against the fuzzy control’s highlighted choice. |
| tests/test_litellm/proxy/health_endpoints/test_health_endpoints.py | Warms the client and checks representative steady-state latency using the median of five validated responses. |
| tests/test_litellm/router_strategy/test_router_tag_routing.py | Retains public routing-path coverage while making randomized deployment sampling effectively deterministic. |
| tests/test_litellm/test_pre_commit_lint.py | Forces interruption during eslint report generation and waits for asynchronous temporary-file cleanup. |
Reviews (7): Last reviewed commit: "Merge branch 'litellm_internal_staging' ..." | Re-trigger Greptile
Codecov Report✅ All modified and coverable lines are covered by tests. 📢 Thoughts on this report? Let us know! |
|
bugbot run |
…itellm_deflake_20260902
…eliness timing TestStreamingScanDedup restored the reduced module-level translation mapping on teardown via monkeypatch, so under --dist=loadscope the worker that ran only that class carried the reduced mapping into the streaming block test modules. Tag routing tests now assert the eligible deployment set directly instead of sampling ten random picks. The liveliness latency check measures steady-state polls after a warm-up request rather than the first request through a fresh app. Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
|
bugbot run |
…itellm_deflake_20260902 Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
…eanup The redis breaker test raced the event loop: the fake call had to still be pending when a real time.sleep stall began, which needs the loop to get from scheduling to the stall in under 1ms. The fake now holds its answer behind an asyncio.Event so the whole burst times out deterministically. The pre-commit interrupt test found a real leak: lint_dashboard creates its eslint report with mktemp and only removed it on the happy path, so an interrupt landing during the whole-folder eslint run left the file behind. The subshell now removes it from an EXIT trap, and the test drives the interrupt while that eslint run is in flight. Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
|
bugbot run |
…/litellm into litellm_deflake_20260902 # Conflicts: # tests/test_litellm/caching/test_redis_cache.py
…deployments are seen
|
bugbot run |
…/litellm into litellm_deflake_20260902
|
bugbot run |
There was a problem hiding this comment.
✅ Bugbot reviewed your changes and found no new issues!
Comment @cursor review or bugbot run to trigger another review on this PR
Reviewed by Cursor Bugbot for commit b6a3cba. Configure here.
377b87c
into
litellm_internal_staging
TLDR
Problem this solves:
How it solves it:
test_redis_cache.pyno longer differs from stagingpre_commit_lint.shremoves the eslint report from an EXIT trap in the dashboard subshellacompletionuntil both deployments are seen, capped at 100 picksUser Flow
Before: a developer presses Ctrl-C while
make checkis running the dashboard lintmake checkin a checkout with a dashboard file staged and press Ctrl-C while the eslint step is still runningls $TMPDIRshows a straytmp.XXXXXXXXXXfile left over from the eslint report, and one more appears every time this happensAfter: the same Ctrl-C leaves nothing behind
make checkwith a dashboard file staged and press Ctrl-C while the eslint step is still runningls $TMPDIRshows no leftover fileEverything else in this PR only edits unit tests, so requests against a proxy behave identically before and after. The affected workflows (MCP outbound session tokens, the
litellm autorouteconfigure wizard, tag based routing, and the liveliness probe) are covered by the tests below and are unchangedRelevant issues
Linear ticket
Pre-Submission checklist
Please complete all items before asking a LiteLLM maintainer to review your PR
uv run pytest tests/test_litellm/<your_test_file>.py -v. Leave the suites (make test-unit-*,make test-unit) to CI: it finishes in ~15 minutes where a laptop takes an hour or more@greptileaito re-request a review after pushing changes)Delays in PR merge?
If you're seeing a delay in your PR being merged, ping the LiteLLM Team on Slack (#pr-review).
Flake evidence and fixes
This is the rolling deflake PR. Each dated section below is one daily run. Evidence is either the pytest-rerunfailures
RERUNthenPASSEDpair for the same node id inside one job on one commit, or the same job on the same commit failing on attempt 1 and passing on attempt 2, read from the job logs of every Unit Tests run in that run's 24h window. Counts are the number of jobs where that happened2026-09-01
tests/test_litellm/proxy/_experimental/mcp_server/outbound_credentials/test_session_token.py::test_rotation_window_still_enforces_expiry_and_tamper_on_the_previous_key, 5 jobs in the window, 3 on this datetests/test_litellm/proxy/_experimental/mcp_server/outbound_credentials/test_session_token.py::test_rs256_tampered_signature_is_bad_signature, 2 jobstests/test_litellm/proxy/_experimental/mcp_server/outbound_credentials/test_session_credentials.py::test_resolve_fails_tampered_token_closed_without_expiry_flag, 1 jobMechanism (all three, unseeded randomness): every mint gets a fresh
jtifromsecrets.token_urlsafe, so the signature differs each run. The tests tampered by rewriting the last two base64url characters of the signature. A 256 byte RS256 signature encodes to 342 characters where the final character carries only 2 real bits, and a 32 byte HS256 signature to 43 characters where the final one carries 2 bits. When the second to last character was alreadyaand the last character shared its leading bits witha, the rewrite only changed padding bits and the decoded signature was identical, sojwt.decodeverified it and the test got a valid token instead ofSessionBadSignature. Measured on 3000 minted tokens: 14 RS256 and 4 HS256 tokens decoded to the same bytes after the old tamper. With several hundred proxy-infra jobs a day that matches the observed rateFix:
_corrupt_signaturebase64url-decodes the signature, XORs the low bit of its first byte, and re-encodes it. Every call now changes the bytes the verifier checks. Mutation check: settingverify_signature: Falseinopen_session_tokenmakes all four tamper tests fail, reverting restores themtests/test_litellm/proxy/client/cli/autoroute/test_wizard.py::TestFuzzyPickWidget::test_single_select_filters_and_returns_highlighted_match, 2 jobsMechanism (wall-clock dependence):
_drive_fuzzy_pickslept 50ms after starting the prompt in a worker thread, then sent each key and slept a fixed 0.3s or 0.1s before the next one. InquirerPy does not filter synchronously on the keystroke:_on_text_changedschedules_filter_choices, which sleeps a 50ms debounce (_calculate_wait_timefor 20 choices) and then awaits the asyncfuzzy_matchbefore the callback moves the highlight. Enter is handled synchronously and accepts whatever is highlighted at that instant. If the debounce plus filter plus callback take longer than the 0.3s gap, Enter accepts the defaultmodel-0. Probing the real widget with the old driver locally shows the outcome is decided purely by that gap: a 0.05s gap returnsmodel-05 of 5 times, a 0.1s gap or more returnsmodel-135 of 5 times. In CI the failing attempt took about 2.1s versus 0.45s for the passing rerun on the same worker, so the worker was running 4 to 5 times slower than normal at that moment and the filter overran the 0.3s gapFix: each key event names the choice the widget must have highlighted before the next key is sent. The driver reads the live
InquirerPyFuzzyControl.selectionfrom the app session and polls it (10ms, 5s cap) instead of sleeping. Mutation checks: flippingmultiselectin_fuzzy_pickand uppercasing the choice values both make the three widget tests fail, reverting restores them2026-09-02
test_rotation_window_still_enforces_expiry_and_tamper_on_the_previous_key, 2 more jobs, same mechanism and fix as above2026-09-03
Window: 2026-09-02 09:17 UTC to 2026-09-03 09:17 UTC. The JWT and fuzzy picker tests above kept flaking on branches that do not carry this PR yet (
test_resolve_fails_tampered_token_closed_without_expiry_flag2 jobs,TestFuzzyPickWidget::test_multiselect_can_pick_more_than_one_across_filters2 jobs,test_single_select_filters_and_returns_highlighted_match1 job), same mechanism and fix as above. Three new flakes were fixedStreaming guardrail block tests
tests/test_litellm/proxy/guardrails/guardrail_hooks/unified_guardrails/test_openai_streaming_block.py(test_chat_pre_stream_block_emits_standalone_completion,test_chat_mid_stream_block_continues_the_completion,test_chat_end_of_stream_block_terminates_cleanly),test_anthropic_streaming_block.py(test_mid_stream_block_emits_clean_anthropic_sse,test_mid_stream_block_after_prior_chunks_continues_message,test_end_of_stream_block_emits_clean_anthropic_sse,test_end_of_stream_only_block_does_not_append_after_message_stop) andtest_streaming_buffer_until_moderated.py::test_buffered_block_withholds_original_content, 4 jobs. Every failure wasno block SSE chunks were emittedand every job failed the whole group together, on both attempts inside the job, then passed on the next attempt of the same run or in the sibling job of the same runThe same mechanism also hit
tests/test_litellm/proxy/guardrails/guardrail_hooks/openai/test_openai_moderation_streaming.py(all five tests,Expected 2 moderation calls ... got 0) in run 33728475666 job 100563167873 onlitellm_heuristic_v2_licensecommit c9a6c11, whose neighbouring commits 43c56ab and a6ede47 on the same branch passed the same job with no guardrail files changed between themMechanism (shared module-level state between tests, made visible by test ordering):
unified_guardrail.pykeeps its call type to translation handler table in the module globalendpoint_guardrail_translation_mappings, lazily filled byload_guardrail_translation_mappings()when it isNone.test_unified_guardrail.pyhad a module-level autouse fixture that installed a reduced table and reset the global toNoneon teardown, whileTestStreamingScanDedupswapped in the full table throughmonkeypatch.setattr. Becausemonkeypatchis set up before the module fixture and torn down after it, the module fixture set the global toNoneand then monkeypatch's undo put the reduced table back. Once a worker had run that class, the reduced table stayed installed for the rest of the process, and any later module on the same worker that streams through the unified guardrail found no chat handler and emitted no block chunks. The proxy-endpoints shard runs-n 4 --dist=loadscope, so the outcome depended on which modules landed on which workerFix: the 2026-09-03 run rewrote
_use_real_mappingsto assign the full table directly and reset the global on teardown. Staging has since landed the same repair in a different shape (_patch_translation_mappings(monkeypatch, mappings)used by every fixture in the module), so after merging staging on 2026-09-04 this PR no longer carries a diff in that file. The mutation checks from that run still hold on the tipTag routing tests
tests/test_litellm/router_strategy/test_router_tag_routing.py::test_negation_regex_pattern_treated_as_literal, 3 jobstests/test_litellm/router_strategy/test_router_tag_routing.py::test_chain_enable_tag_filtering_false_overrides_router_level_true, 1 jobMechanism (unseeded randomness): both tests build a router with two deployments and no routing strategy, so
simple_shufflepicks withrandom.choice. They sent ten requests and asserted the set of picked model ids equalled both deployments. The chance that ten fair picks all land on the same deployment is 2 in 1024, about 0.2 percent per test per job, which matches four flakes across the several hundred enterprise-routing jobs in the windowFix: the tests keep routing real
acompletioncalls (mocked response, real router path) until both deployment ids have been seen, capped at 100 picks, and assert the seen set equals exactly the two deployments. A disallowed deployment fails the equality, a never-seen one fails at the cap, and the chance that 100 fair picks never land on one of two deployments is 2 in 2^100. An earlier revision calledget_deployments_for_tagdirectly, which Greptile pointed out skipped the router's own routing path, so the tests now go through it. Mutation checks: making the negation branch match the tag as a regex failstest_negation_regex_pattern_treated_as_literal(every deployment is excluded and the router raisesNot allowed to access model due to tags configuration), and ignoring the deployment-level override in_chain_tag_filtering_overridefailstest_chain_enable_tag_filtering_false_overrides_router_level_true, reverting restores bothLiveliness latency test
tests/test_litellm/proxy/health_endpoints/test_health_endpoints.py::test_health_liveliness_endpoint, 3 jobsMechanism (wall-clock dependence): the test sent one request to
/health/livelinessthrough a freshly builtTestClientapp and asserted it took under 100ms. The first request through a fresh Starlette app pays one-time costs (router compilation, middleware chain, anyio portal start, JSON encoder warm up) that later requests do not: locally the first request takes 11 to 39ms and requests two to five take about 1ms. On a proxy-endpoints shard running four xdist workers, with the worker also spinning up the app, that first request crossed 100msFix: the test makes one warm-up request (asserting 200), then times five polls, asserts every poll returned 200 with
"I'm alive!", and asserts the median of the five stayed under 100ms (a Greptile round asked for more than the fastest poll; the median still rides out a starved CI worker on two of five polls while failing on an endpoint that is slow on most). Mutation check: addingawait asyncio.sleep(0.15)tohealth_livelinessfails the test, reverting restores it2026-09-04
Window: 2026-09-03 09:17 UTC to 2026-09-04 09:17 UTC, 9904 Run tests job logs across 726 responses-caching-types, 650 misc and the other shards. Tests already carried by this PR kept flaking on branches that do not have it yet, same mechanism and fix as above:
test_rotation_window_still_enforces_expiry_and_tamper_on_the_previous_key3 jobs,test_health_liveliness_endpoint3 jobs,test_negation_regex_pattern_treated_as_literal2 jobs,TestFuzzyPickWidget::test_single_select_filters_and_returns_highlighted_match2 jobs,TestFuzzyPickWidget::test_multiselect_can_pick_more_than_one_across_filters2 jobs,test_rs256_tampered_signature_is_bad_signature2 jobs, and one job each fortest_multiselect_requires_tab_to_toggle_before_enter,test_resolve_fails_tampered_token_closed_without_expiry_flag,test_chain_enable_tag_filtering_false_overrides_router_level_trueandtest_tampered_signature_is_bad_signature. Two new flakes were investigated: the pre-commit one is fixed here, the Redis one landed on staging firstRedis loop-stall burst test
tests/test_litellm/caching/test_redis_cache.py::test_event_loop_stall_timeout_burst_keeps_breaker_closed, 32 jobs, all in the eleven hours after the test landed on staging in #38999 (942a46f, 2026-09-03 22:18 UTC), so roughly one in ten of the responses-caching-types jobs that ran it. First six:Mechanism (event-loop scheduling race, wall-clock dependence): the test gathered eight fake Redis calls, each
asyncio.wait_for(asyncio.sleep(0.001), timeout=0.05), together with a task that didawait asyncio.sleep(0)and then blocked the loop withtime.sleep(0.2). It then asserted at least three of the eight calls timed out. That only holds if the loop gets from scheduling the eightsleep(0.001)timers to running the blocking task in under 1ms. When the loop iteration takes longer (a GC pause, four xdist workers sharing a two-core runner), the 1ms timers fire first, the eight calls returnokbefore the stall starts, there are zero timeouts, and the assertion fails. Reproduced locally by adding one 2ms delay to the loop iteration before the stall: 8 timeouts with no delay, 0 timeouts with the delay, see Proof of FixFix: not carried by this PR any more. Staging fixed the same test in 7d3b03d (
test(caching): drive the redis stall burst off the clock, not asyncio.wait_for): the fake call checks a monotonic client deadline and raises the redisTimeoutErroritself instead of racing a 1ms sleep insideasyncio.wait_for, so a slow loop iteration cannot make the calls finish before the stall. The event-based rewrite this PR carried was dropped for staging's version when staging was merged in (822c862), sotest_redis_cache.pyis no longer part of this PRPre-commit interrupt test, and a real leak in
scripts/pre_commit_lint.shtests/test_litellm/test_pre_commit_lint.py::test_interrupt_kills_background_jobs_and_removes_logs, 4 jobs (plus 1 job in the 2026-09-03 window that was left alone then)Mechanism (temp files plus a process race, and the flake exposed a real bug in the script): the test starts the real
scripts/pre_commit_lint.shwith stubbedmake,npx,npmanduv, waits for the Python lint stub to report it is running, sends SIGINT to the process group, and asserts the exit code, that the hungmakeis gone, and that$TMPDIRis empty.on_interruptin the script removes the three job log files it knows about and kills the job process groups. Butlint_dashboardruns in a background subshell and creates a fourth temp file,report=$(mktemp), for the whole-folder eslint JSON output, and only removed it on the happy path aftercheck-lint-budgets.mjs. If SIGINT lands whilenpx eslint . -f jsonis still running, the subshell is killed byon_interrupt, the report file stays behind, andlist(tmp_dir.iterdir()) == []fails. Locally the stubs finish in a few milliseconds so the window is almost never hit; slowing the eslint stub by one second reproduces it every time (Proof of Fix). On a busy misc shard the dashboard job is slower than the Python job often enough to hit it four times a day. Outside the test, this is the same leak a developer hits when they Ctrl-Cmake checkduring the dashboard lintFix: the
lint_dashboardsubshell setstrap 'exit 143' TERMandtrap 'rm -f "${report:-}"' EXIT, so the report is removed on the happy path, on failure and on interrupt, and the explicitrm -f "$report"at the end goes away. The test now also makes thenpx eslint . -f jsonstub hang likemakedoes whenSTUB_HANG_DIRis set, and waits for it to start before sending SIGINT, so the interrupt always lands inside the window that flaked, and the cleanup assertion waits on the condition (5s cap) instead of sampling the directory once, sinceon_interruptdoes not wait for the job process groups it kills. Mutation checks: reverting the script's trap change fails the new test every time with the leftovertmp.*file in the assertion message, and replacing therm -fof the three job logs inon_interruptwith a no-op fails it too, reverting restores bothNot fixed here
Real breakages (failed on every attempt in every job that ran them, so not flakes):
tests/litellm-proxy-extras/test_litellm_proxy_extras_utils.py::TestMigrateDeployAttemptAccounting::*(four tests, 100 jobs across 40 branches, every branch that has not merged the proxy-extras fix yet),tests/test_litellm/router_strategy/test_complexity_router.py::test_every_routing_decision_field_is_classified(6 jobs onlitellm_auto_setup),tests/test_litellm/test_claude_fable_5_config.py::*andtests/test_litellm/test_utils.py::test_aaamodel_prices_and_context_window_json_is_valid(5 jobs each onlitellm_opencode_providerandlitellm_registry_audit_2026_09_02), plus single-branch failures intest_spend_counter_reseed.py,test_cloudzero.py,test_mcp_server.py::test_mcp_manager_merges_public_and_restricted_serversandtest_router_model_cost_isolation.py. The SAP transformation tests onlitellm_sap_*fail withNameError: name 'model_serializer' is not definedon every attemptNot reproduced:
tests/test_litellm/proxy/spend_tracking/test_spend_management_endpoints.py::TestSpendLogsPayload::test_spend_logs_payload_e2ererun to pass in 3 jobs, all onlitellm_internal_copy_38013(run 33822663839, run 33821535262, run 33824187901, proxy-infra). That branch does not touch spend tracking, but 20 local repeats of the node id passed and the rerun hides the traceback, so the mechanism is not pinned down. Seen once each and left alone:test_streaming_helpers.py::test_run_thread_pings_while_the_assistants_run_is_still_silent,test_io_token_rate_limits.py::test_itpm_estimate_failure_reserves_minimal_not_full_limit,test_spend_log_error_logger.py::test_spend_log_error_includes_traceback_by_default,tests/local_testing/test_caching_handler.py::test_async_get_cache_defers_streaming_completion_hit_callbacks,tests/local_testing/test_unit_test_caching.py::test_add_namespace_to_cache_key, andtest_health_endpoints.py::test_health_liveness_endpointon a branch that renamed the liveliness testInfrastructure: PR #39644 had a check fail then pass on the same SHA because the Rust native wheel smoke test (
tests/test_litellm/rust_bridge/native_route_wheel_test.py) timed out against its local127.0.0.1server with aBrokenPipeError. That is a harness timeout, not a pytest node flake.Validate PR titleflipped on #39300 and #39299Rolling PR bookkeeping: this PR was the only open
litellm_deflake_PR at the start of the 2026-09-04 run, so nothing was absorbed or dropped. Staging was merged in before adding the new fixes; the merge took staging's_patch_translation_mappingsversion of the guardrail fixture over this PR's earlier version, see aboveScreenshots / Proof of Fix
Nothing in the proxy runtime changed, so there is no proxy request to curl. Before shows each mechanism on the merge base, After shows it gone at the tip, using the same commands. The tag routing case runs the test body from a script because the trigger (ten coin flips landing the same way) has to be repeated to show up on a fast laptop
Before (c8635ec)
Ctrl-C during
make checkleaks the eslint reportThe User Flow above, run for real in a worktree at the merge base: an unformatted
layout.tsxstaged,TMPDIRpointed at an empty directory,make checkstarted in a pty, and^Ctyped oncenpx eslint . -f jsonshows up inpgrepCI job 100669406966 log:
RERUN ...test_interrupt_kills_background_jobs_and_removes_logsfollowed byPASSEDfor the same node id. The new regression test against the merge-base script:1 failed,assert _wait_until(lambda: not any(tmp_dir.iterdir()), 5)with thetmp.*file in the messageTag routing depends on ten random picks
test_negation_regex_pattern_treated_as_literalandtest_chain_enable_tag_filtering_false_overrides_router_level_true1000 times each, countingAssertionErrorruns per test: 1000 failures: {'test_negation_regex_pattern_treated_as_literal': 6, 'test_chain_enable_tag_filtering_false_overrides_router_level_true': 5}Liveliness test times the cold first request
TestClientthe test uses and time five consecutiveGET /health/livelinesscallsreq 0: 22.52ms,req 1: 1.74ms,req 2: 1.32ms,req 3: 0.99ms,req 4: 1.14ms. Only the first request carries the startup cost the old test measured against a 100ms budgetAfter (03a8282)
Commits since 03a8282: 822c862 merges staging and only drops this PR's
test_redis_cache.pyrewrite for staging's 7d3b03d, leaving the other files byte-identical, and 5bd4da0 changes only the liveliness assertion (median instead of fastest), so that leg is re-run at 5bd4da0 below; 41c8969 rewrites the two tag routing tests only, so that leg is re-run at 41c8969 below, and the other runs stand; b6a3cba merges staging (#39848'sintegrations / Run testsfix) with no diff changeCtrl-C during
make checkleaks the eslint reportSame steps in a worktree at the tip
The uninterrupted paths through the same subshell still report correctly at the tip:
make checkwith the unformattedlayout.tsxstaged printscheck: FAILand exits 2, and with a prettier-clean edit staged printscheck: PASSand exits 0, withls -A $TMPDIRempty after both. 20 repeats of the node id: 0 failures, andtests/test_litellm/test_pre_commit_lint.pyas a whole passesTag routing depends on ten random picks (re-run at 41c8969)
runs per test: 1000 failures: {'test_negation_regex_pattern_treated_as_literal': 0, 'test_chain_enable_tag_filtering_false_overrides_router_level_true': 0}Liveliness test times the cold first request (re-run at 5bd4da0)
req 0: 23.70ms,req 1: 7.13ms,req 2: 4.25ms,req 3: 2.32ms,req 4: 2.59ms. The first request is no longer scored, and the median of the warm polls is 4.25msThe 2026-09-01 and 2026-09-03 fixes were re-validated at 03a8282: 20 repeats of
test_session_token.py,test_session_credentials.py,test_wizard.py, the two tag routing node ids and the liveliness node id together, 0 failures. Since then only the liveliness test (5bd4da0) and the two tag routing tests (41c8969) changed, and both are re-run aboveproxy_store_model_in_db_testsfailure listed under Caveats)Type
Test
Bug Fix
Caveats (if any)
Low
pre_commit_lint.sh's interrupt handler still does not wait for the killed jobs, so the test waits up to 5s for cleanuptest_health_backlog_includes_admission_control_statsfails locally with a 401 on staging too; this PR does not touch itno-large-inline-object-argbudget (556 > 555), somake checkon the merge commit reports that pre-existing red; no dashboard file changes hereproxy_store_model_in_db_testsis red at b6a3cba ontest_chat_completion_bad_model_with_spend_logs(model_groupcomes back empty), the same single failure on every staging pipeline since 2026-09-04 12:09 (89087 through 89149); not a file this PR touchesFinal Attestation
Note
Low Risk
Changes are mostly test hardening plus a developer-script temp-file cleanup fix; no production proxy routing or auth logic is modified.
Overview
This PR tightens flaky unit tests and fixes a real cleanup bug in local
make checkdashboard linting.scripts/pre_commit_lint.sh— The dashboard lint subshell now usesEXIT/TERMtraps so the eslint JSONmktempreport is removed on success, failure, and Ctrl-C; previously interrupting duringnpx eslint . -f jsoncould leave stray files in$TMPDIR.Tests (no proxy runtime behavior changes):
_corrupt_signature(decode → flip a signature bit → re-encode) so tamper cases always fail verification instead of occasionally matching valid JWTs when only base64 padding changed.acompletionuntil both deployment IDs are seen (capped at 100 picks) instead of asserting after ten random shuffles./health/livelinesslatency test warms up once, then asserts median of five polls stays under 100ms rather than timing the cold first request.$TMPDIRis empty after SIGINT.Reviewed by Cursor Bugbot for commit b6a3cba. Bugbot is set up for automated code reviews on this repo. Configure here.
Link to Devin session: https://app.devin.ai/sessions/41e8c32159854ba790cdd93622ac60ca
Open in Devin Desktop: https://app.devin.ai/desktop/session/41e8c32159854ba790cdd93622ac60ca?variant=devin
Requested by: @mateo-berri