datasette/tests
Alex Garcia 74cd01860f Name the request span after the route it matched
The request span was created at the ASGI edge, before anything knew which
route would match, so it carried nothing but the method: every request in a
trace UI showed up as "GET", and the only URL on it was url.path, which is
unbounded on a public instance and useless as a grouping key. Routing
resolves in DatasetteRouter, so that is where the span gets http.route and
its semconv `{method} {route}` name.

http.route is the compiled route pattern, not a prettified
/{database}/{table} template. Datasette routes with compiled regexes and the
route table is fixed when the app is built, so the pattern is exact, bounded
and needs no parsing; the transform into something prettier accretes edge
cases, and Django's instrumentation ships regex-flavoured routes for the same
reason. A request that matches no route gets no http.route and keeps its bare
method name, which is what semantic conventions ask for.

Two things the obvious implementation gets wrong, both found by testing it:

- The router must not read `get_current_span()`. A plugin asgi_wrapper()
  runs *inside* the request middleware, so an instrumented plugin makes its
  own span current for the whole request - and the route then lands on that
  plugin's INTERNAL span, renaming it, while the actual request span never
  gets the one attribute a trace UI groups by. It reproduces with a five-line
  plugin. The span is passed through the ASGI scope instead, falling back to
  the current span so an externally-created SERVER span is still enriched.

- The method has to be clamped again here. The middleware clamps it for the
  attribute, but the name is rebuilt from request.method, which is the raw
  client string - so an unclamped rename put `FROB /(?P<database>...` back
  into the span name that the middleware had just kept it out of.

Both guards are `is_recording()`, not `get_span_context().is_valid`: with no
provider but an inbound traceparent the API returns a NonRecordingSpan
carrying the remote context, which is valid and records nothing, so an
is_valid guard would do the work on every request from a traced caller.

Tests cover the route and name, the unrouted 404 fallback, the full attribute
set, db.query spans reaching the request span by parent walk, a 500, an
inbound traceparent becoming a remote parent, ?sql= never reaching a span
attribute, and - in a subprocess, because the suite's provider fixture is
session-scoped and unavoidable - the no-provider fast path handing the app
the original `send`. The streaming test uses a table larger than one page so
the export genuinely issues queries during the body send; without that it
passes however early the span ends.

Measured on this branch against fixtures.db: a faceted table page went from
112 spans in 56 traces to 113 spans in 1.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-01 16:24:35 -07:00
..
plugins Upgrade to ruff>=0.16.0 (#2857) 2026-07-25 15:47:08 -07:00
test_templates Fix handling of nested custom page wildcard paths, closes #996 2020-10-07 15:51:11 -07:00
__init__.py Broke up test_app into test_api and test_html 2017-12-15 04:08:24 -08:00
build_small_spatialite_db.py New run_sanity_checks mechanism, for SpatiLite 2019-05-11 15:55:30 -07:00
conftest.py Name the request span after the route it matched 2026-09-01 16:24:35 -07:00
ext.c Add new entrypoint option to --load-extensions. (#1789) 2022-08-23 11:34:30 -07:00
fixtures.py Upgrade to ruff>=0.16.0 (#2857) 2026-07-25 15:47:08 -07:00
spatialite.db New run_sanity_checks mechanism, for SpatiLite 2019-05-11 15:55:30 -07:00
test-datasette-load-plugins.sh fix (typo): Corrected spelling of 'environments' (#2268) 2024-02-19 14:41:32 -08:00
test_actions_sql.py Upgrade to ruff>=0.16.0 (#2857) 2026-07-25 15:47:08 -07:00
test_actor_restriction_bug.py Upgrade to ruff>=0.16.0 (#2857) 2026-07-25 15:47:08 -07:00
test_allowed_many.py Upgrade to ruff>=0.16.0 (#2857) 2026-07-25 15:47:08 -07:00
test_allowed_resources.py Upgrade to ruff>=0.16.0 (#2857) 2026-07-25 15:47:08 -07:00
test_api.py Upgrade to ruff>=0.16.0 (#2857) 2026-07-25 15:47:08 -07:00
test_api_write.py disable_fts() before dropping table 2026-08-10 15:03:29 -07:00
test_auth.py Upgrade to ruff>=0.16.0 (#2857) 2026-07-25 15:47:08 -07:00
test_autocomplete.py Add "ok": true to every JSON object success response 2026-07-04 13:40:05 +00:00
test_base_view.py Upgrade to ruff>=0.16.0 (#2857) 2026-07-25 15:47:08 -07:00
test_cli.py Upgrade to ruff>=0.16.0 (#2857) 2026-07-25 15:47:08 -07:00
test_cli_serve_get.py Upgrade to ruff>=0.16.0 (#2857) 2026-07-25 15:47:08 -07:00
test_cli_serve_server.py Run datasette serve startup and uvicorn on a single event loop (#2886) 2026-09-01 09:32:37 -07:00
test_column_types.py Upgrade to ruff>=0.16.0 (#2857) 2026-07-25 15:47:08 -07:00
test_config_dir.py Upgrade to ruff>=0.16.0 (#2857) 2026-07-25 15:47:08 -07:00
test_config_permission_rules.py Run black formatter 2025-10-25 15:38:07 -07:00
test_crossdb.py Upgrade to ruff>=0.16.0 (#2857) 2026-07-25 15:47:08 -07:00
test_csrf_middleware.py Upgrade to ruff>=0.16.0 (#2857) 2026-07-25 15:47:08 -07:00
test_csv.py Upgrade to ruff>=0.16.0 (#2857) 2026-07-25 15:47:08 -07:00
test_custom_pages.py Upgrade to ruff>=0.16.0 (#2857) 2026-07-25 15:47:08 -07:00
test_datasette_https_server.sh More robust test_datasette_https_server.sh test 2026-06-22 10:11:56 -07:00
test_debug_autocomplete.py Autocomplete widget and /-/debug/autocomplete test page 2026-06-13 22:59:37 -07:00
test_default_deny.py Upgrade to ruff>=0.16.0 (#2857) 2026-07-25 15:47:08 -07:00
test_docs.py Upgrade to ruff>=0.16.0 (#2857) 2026-07-25 15:47:08 -07:00
test_docs_plugins.py Upgrade to ruff>=0.16.0 (#2857) 2026-07-25 15:47:08 -07:00
test_error_shape.py Upgrade to ruff>=0.16.0 (#2857) 2026-07-25 15:47:08 -07:00
test_extras.py Upgrade to ruff>=0.16.0 (#2857) 2026-07-25 15:47:08 -07:00
test_facets.py Upgrade to ruff>=0.16.0 (#2857) 2026-07-25 15:47:08 -07:00
test_fd_leak.py FD-leak regression test for Datasette.close() 2026-04-16 20:18:05 -07:00
test_filters.py Fix for SQL injection issue in table filters, refs #2868 2026-08-06 11:19:50 -07:00
test_fixtures.py datasette.fixtures module, closes #2733 2026-05-21 23:05:37 -07:00
test_html.py Upgrade to ruff>=0.16.0 (#2857) 2026-07-25 15:47:08 -07:00
test_http_span.py Name the request span after the route it matched 2026-09-01 16:24:35 -07:00
test_internal_db.py Upgrade to ruff>=0.16.0 (#2857) 2026-07-25 15:47:08 -07:00
test_internals_database.py Propagate otel context across the thread boundaries 2026-09-01 16:24:35 -07:00
test_internals_datasette.py Upgrade to ruff>=0.16.0 (#2857) 2026-07-25 15:47:08 -07:00
test_internals_datasette_client.py Upgrade to ruff>=0.16.0 (#2857) 2026-07-25 15:47:08 -07:00
test_internals_request.py Upgrade to ruff>=0.16.0 (#2857) 2026-07-25 15:47:08 -07:00
test_internals_response.py Upgrade to ruff>=0.16.0 (#2857) 2026-07-25 15:47:08 -07:00
test_internals_urls.py Upgrade to ruff>=0.16.0 (#2857) 2026-07-25 15:47:08 -07:00
test_jump.py Autocomplete widget and /-/debug/autocomplete test page 2026-06-13 22:59:37 -07:00
test_label_column_for_table.py Upgrade to ruff>=0.16.0 (#2857) 2026-07-25 15:47:08 -07:00
test_lifespan.py Run startup via ASGI lifespan instead of waiting for the first request (#2887) 2026-09-01 09:39:25 -07:00
test_load_extensions.py Upgrade to ruff>=0.16.0 (#2857) 2026-07-25 15:47:08 -07:00
test_messages.py Upgrade to ruff>=0.16.0 (#2857) 2026-07-25 15:47:08 -07:00
test_multipart.py Upgrade to ruff>=0.16.0 (#2857) 2026-07-25 15:47:08 -07:00
test_package.py Upgrade to ruff>=0.16.0 (#2857) 2026-07-25 15:47:08 -07:00
test_permission_endpoints.py Upgrade to ruff>=0.16.0 (#2857) 2026-07-25 15:47:08 -07:00
test_permissions.py Upgrade to ruff>=0.16.0 (#2857) 2026-07-25 15:47:08 -07:00
test_playwright.py Make binary playwright test more robust 2026-07-05 22:46:00 -07:00
test_plugins.py Upgrade to ruff>=0.16.0 (#2857) 2026-07-25 15:47:08 -07:00
test_publish_cloudrun.py Upgrade to ruff>=0.16.0 (#2857) 2026-07-25 15:47:08 -07:00
test_publish_heroku.py Upgrade to ruff>=0.16.0 (#2857) 2026-07-25 15:47:08 -07:00
test_pytest_autoclose_plugin.py Upgrade to ruff>=0.16.0 (#2857) 2026-07-25 15:47:08 -07:00
test_queries.py Upgrade to ruff>=0.16.0 (#2857) 2026-07-25 15:47:08 -07:00
test_restriction_sql.py Upgrade to ruff>=0.16.0 (#2857) 2026-07-25 15:47:08 -07:00
test_routes.py Upgrade to ruff>=0.16.0 (#2857) 2026-07-25 15:47:08 -07:00
test_schema_endpoints.py Upgrade to ruff>=0.16.0 (#2857) 2026-07-25 15:47:08 -07:00
test_search_tables.py Upgrade to ruff>=0.16.0 (#2857) 2026-07-25 15:47:08 -07:00
test_spatialite.py Upgrade to ruff>=0.16.0 (#2857) 2026-07-25 15:47:08 -07:00
test_stored_queries.py Upgrade to ruff>=0.16.0 (#2857) 2026-07-25 15:47:08 -07:00
test_success_envelope.py Upgrade to ruff>=0.16.0 (#2857) 2026-07-25 15:47:08 -07:00
test_table_api.py Fix for SQL injection issue in table filters, refs #2868 2026-08-06 11:19:50 -07:00
test_table_html.py Upgrade to ruff>=0.16.0 (#2857) 2026-07-25 15:47:08 -07:00
test_telemetry.py Stop marking a deliberately-short query budget as a span error 2026-09-01 16:24:35 -07:00
test_telemetry_registry.py Name the request span after the route it matched 2026-09-01 16:24:35 -07:00
test_template_context.py Upgrade to ruff>=0.16.0 (#2857) 2026-07-25 15:47:08 -07:00
test_token_handler.py Upgrade to ruff>=0.16.0 (#2857) 2026-07-25 15:47:08 -07:00
test_tracer.py Upgrade to ruff>=0.16.0 (#2857) 2026-07-25 15:47:08 -07:00
test_utils.py Upgrade to ruff>=0.16.0 (#2857) 2026-07-25 15:47:08 -07:00
test_utils_check_callable.py Upgrade to ruff>=0.16.0 (#2857) 2026-07-25 15:47:08 -07:00
test_utils_permissions.py Upgrade to ruff>=0.16.0 (#2857) 2026-07-25 15:47:08 -07:00
test_utils_sql_analysis.py Upgrade to ruff>=0.16.0 (#2857) 2026-07-25 15:47:08 -07:00
test_write_sql_operation_decisions.py Refactored write decision tests 2026-05-28 12:09:20 -07:00
test_write_wrapper.py Upgrade to ruff>=0.16.0 (#2857) 2026-07-25 15:47:08 -07:00
utils.py From 409 warnings down to 52 warnings. 2026-04-14 18:46:47 -07:00