From e0574e5a8031b5b6eed8b5e35686c54d3e6b873e Mon Sep 17 00:00:00 2001 From: Jose Celano Date: Tue, 27 Oct 2026 12:43:49 +0000 Subject: [PATCH 01/12] docs(issues): [#2670] record the audit and the container build baseline Re-verify the audit of the commands that build shipped artifacts on develop and record the baseline the later comparison needs: 1531 tests in 40 binaries (list saved), a cold release build of 4 min 59 s, a cold debug build of 1 min 55 s and a warm release rebuild of 2 min 53 s. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../ISSUE.md | 9 +- .../baseline-test-list.txt | 1531 +++++++++++++++++ .../manual-verification-evidence.md | 75 + 3 files changed, 1611 insertions(+), 4 deletions(-) create mode 100644 docs/issues/open/2670-2482-build-shipped-artifacts-without-test-features/baseline-test-list.txt create mode 100644 docs/issues/open/2670-2482-build-shipped-artifacts-without-test-features/manual-verification-evidence.md diff --git a/docs/issues/open/2670-2482-build-shipped-artifacts-without-test-features/ISSUE.md b/docs/issues/open/2670-2482-build-shipped-artifacts-without-test-features/ISSUE.md index b71c2b3ed..b34b9e595 100644 --- a/docs/issues/open/2670-2482-build-shipped-artifacts-without-test-features/ISSUE.md +++ b/docs/issues/open/2670-2482-build-shipped-artifacts-without-test-features/ISSUE.md @@ -2,14 +2,14 @@ schema-version: 1 doc-type: issue issue-type: task -status: planned +status: in-progress priority: p2 epic: 2482 github-issue: 2670 spec-path: docs/issues/open/2670-2482-build-shipped-artifacts-without-test-features/ISSUE.md branch: "2670-2482-build-shipped-artifacts-without-test-features" related-pr: null -last-updated-utc: "2026-10-27 09:06" +last-updated-utc: "2026-10-27 12:43" semantic-links: skill-links: - create-issue @@ -266,7 +266,7 @@ Status values: `TODO`, `IN_PROGRESS`, `BLOCKED`, `DONE`. | ID | Status | Task | Notes / Expected Output | | --- | --- | --- | --- | -| T1 | TODO | Re-verify the audit table against `develop`; record a baseline: the list and count of the tests the image's `test` stage runs, and cold and warm build times | Table and baseline in the evidence; the list lets the later comparison show that every baseline test is still present | +| T1 | DONE | Re-verify the audit table against `develop`; record a baseline: the list and count of the tests the image's `test` stage runs, and cold and warm build times | Table and baseline in the evidence (V0, V1); the list lets the later comparison show that every baseline test is still present | | T2 | TODO | Add `TORRUST_TRACKER_BINARY` to `tracker_binary()`, test first | Unit tests for the resolution order; default behaviour unchanged | | T3 | TODO | Create the check crate, test first | Parser tests from real `cargo tree` output; scratch-workspace red and green tests; registered as a workspace member and admitted in `.dockerignore`; `cargo machete` and `cargo deny` pass | | T4 | TODO | Add the check as a job in `testing.yaml` | Job runs the check for `torrust-tracker`; follows `implement-workflow` | @@ -317,6 +317,7 @@ Status values: `TODO`, `IN_PROGRESS`, `BLOCKED`, `DONE`. - 2026-10-27 09:04 UTC - AI assistant (Copilot SDK in VS Code, #2482 subissue 9) - PR #2671 review round 1, F1: the Regression Test Strategy and the Commit Points now require the recorded prose-first Arrange-Act-Assert test-design review after each test increment and the maintainer-review stop after the final increment of T2 and of T3. - 2026-10-27 09:05 UTC - AI assistant (Copilot SDK in VS Code, #2482 subissue 9) - PR #2671 review round 1, F2: T1 now records the list and count of the baseline tests, and T5, AC2 and M2 require every baseline test to remain present and passing with the delta for new tests recorded, instead of an equal count (T2's tests are compiled into the targets that include `tests/common/native_tracker`). - 2026-10-27 09:06 UTC - AI assistant (Copilot SDK in VS Code, #2482 subissue 9) - PR #2671 review round 1, F3: scenario M1 now builds both targets with `--file Containerfile`, as `docs/containers.md` does, because the repository root has no `Dockerfile`. +- 2026-10-27 12:43 UTC - AI assistant (Copilot SDK in VS Code, #2482 subissue 9) - Started the implementation. T1 is done: the audit table still holds on `develop` after PR #2671 (V0), and the baseline is 1531 tests in 40 binaries with a cold `release` build of 4 min 59 s, a cold `debug` build of 1 min 55 s and a warm `release` rebuild of 2 min 53 s (V1, list in `baseline-test-list.txt`). ## Acceptance Criteria @@ -351,7 +352,7 @@ applies, except that this subissue moves no test code. The workflow changes foll | ID | Scenario | Human-oriented command/steps | Expected Result | Status | Evidence | | --- | --- | --- | --- | --- | --- | -| M1 | Baseline before the change | Build the `release` and `debug` targets of `develop`'s `Containerfile` (`docker build --target release --tag torrust-tracker:release --file Containerfile .` and the same with `--target debug --tag torrust-tracker:debug`) and record the `test` stage's test list and count and the cold and warm times | Numbers recorded | TODO | `manual-verification-evidence.md` V1 | +| M1 | Baseline before the change | Build the `release` and `debug` targets of `develop`'s `Containerfile` (`docker build --target release --tag torrust-tracker:release --file Containerfile .` and the same with `--target debug --tag torrust-tracker:debug`) and record the `test` stage's test list and count and the cold and warm times | Numbers recorded | DONE | `manual-verification-evidence.md` V1 | | M2 | The image builds and its tests run | Build both targets after the change | Both build; every baseline test is still present and passing; the delta for new tests is recorded (the count may rise by the tests T2 adds); times recorded | TODO | V2 | | M3 | The override is honoured | In the `test` stage, point `TORRUST_TRACKER_BINARY` at a missing file, build, then restore it | The native-tracker tests fail with the missing path (red), then pass (green) | TODO | V3 | | M4 | The shipped binary has no `testing` feature | `cargo run -p shipped-features-check -- --package torrust-tracker` and `cargo tree -p torrust-tracker -e normal,build -f "{p} {f}"` | Passes; no `testing` feature (vacuous today, since none exists) | TODO | V4 | diff --git a/docs/issues/open/2670-2482-build-shipped-artifacts-without-test-features/baseline-test-list.txt b/docs/issues/open/2670-2482-build-shipped-artifacts-without-test-features/baseline-test-list.txt new file mode 100644 index 000000000..7effb21b6 --- /dev/null +++ b/docs/issues/open/2670-2482-build-shipped-artifacts-without-test-features/baseline-test-list.txt @@ -0,0 +1,1531 @@ +torrust-tracker app::tests::it_should_cancel_the_health_check_api_component_through_the_job_manager +torrust-tracker app::tests::it_should_cancel_the_http_tracker_component_through_the_job_manager +torrust-tracker app::tests::it_should_cancel_the_rest_api_component_through_the_job_manager +torrust-tracker app::tests::it_should_cancel_the_udp_tracker_component_through_the_job_manager +torrust-tracker app::tests::it_should_count_a_download_published_during_shutdown_by_a_producer_that_stops_before_the_listener +torrust-tracker app::tests::it_should_not_start_udp_tracker_services_for_a_private_tracker +torrust-tracker app::tests::it_should_not_start_udp_tracker_services_with_an_empty_udp_tracker_list +torrust-tracker app::tests::it_should_not_start_udp_tracker_services_without_udp_trackers +torrust-tracker app::tests::it_should_register_peers_inactivity_update_as_a_direct_cancelled_component +torrust-tracker app::tests::it_should_register_torrent_cleanup_as_a_direct_cancelled_component +torrust-tracker app::tests::it_should_release_udp_listener_before_returning_from_start_after_setup_when_later_http_startup_fails +torrust-tracker app::tests::it_should_retain_the_loader_error_when_initial_peer_key_loading_fails +torrust-tracker app::tests::it_should_skip_persistence_loaders_when_persistence_is_absent +torrust-tracker app::tests::it_should_start_tracker_core_statistics_listener_without_persistence +torrust-tracker app::tests::it_should_start_udp_tracker_services_for_a_public_tracker_with_udp_trackers +torrust-tracker app::tests::it_should_stop_every_started_component_cooperatively_and_each_producer_before_its_listeners_when_the_manager_is_cancelled +torrust-tracker app::tests::it_should_subscribe_every_started_swarm_listener_before_its_task_runs_or_any_producer_starts +torrust-tracker-axum-health-check-api-server environment::tests::it_should_join_the_drain_controller_before_reporting_a_server_task_failure +torrust-tracker-axum-health-check-api-server environment::tests::it_should_release_the_health_check_api_binding_when_dropped_without_being_stopped +torrust-tracker-axum-health-check-api-server environment::tests::it_should_release_the_health_check_api_binding_when_stopped +torrust-tracker-axum-health-check-api-server environment::tests::it_should_remove_its_registration_even_when_stopped_with_a_failed_task +torrust-tracker-axum-health-check-api-server environment::tests::it_should_remove_only_its_own_registration_from_the_registar_when_stopped +torrust-tracker-axum-health-check-api-server environment::tests::it_should_report_a_drain_timeout_even_when_both_tasks_join +torrust-tracker-axum-health-check-api-server environment::tests::it_should_report_a_server_error_and_a_drain_timeout_together +torrust-tracker-axum-health-check-api-server environment::tests::it_should_return_an_error_naming_each_failing_task_when_stopped_with_failed_tasks +torrust-tracker-axum-health-check-api-server environment::tests::it_should_serve_requests_after_being_stopped_and_started_again +torrust-tracker-axum-health-check-api-server environment::tests::it_should_serve_requests_after_being_stopped_and_started_again_on_a_fixed_port +torrust-tracker-axum-health-check-api-server environment::tests::it_should_stop_without_a_failure_when_the_server_and_its_drain_controller_finish_through_cancellation +torrust-tracker-axum-health-check-api-server::integration server::contract::api::it_should_return_error_when_api_service_was_stopped_after_registration +torrust-tracker-axum-health-check-api-server::integration server::contract::api::it_should_return_good_health_for_api_service +torrust-tracker-axum-health-check-api-server::integration server::contract::health_check_endpoint_should_return_status_ok_when_there_is_no_services_registered +torrust-tracker-axum-health-check-api-server::integration server::contract::http::it_should_return_error_when_http_service_was_stopped_after_registration +torrust-tracker-axum-health-check-api-server::integration server::contract::http::it_should_return_good_health_for_http_service +torrust-tracker-axum-health-check-api-server::integration server::contract::http::it_should_return_good_health_for_https_service_with_a_trusted_test_certificate +torrust-tracker-axum-health-check-api-server::integration server::contract::udp::it_should_return_error_when_udp_service_was_stopped_after_registration +torrust-tracker-axum-health-check-api-server::integration server::contract::udp::it_should_return_good_health_for_udp_service +torrust-tracker-axum-health-check-api-server server::tests::it_should_drain_the_token_aware_health_check_api_when_its_cancellation_token_is_cancelled +torrust-tracker-axum-health-check-api-server server::tests::it_should_register_the_token_aware_health_check_api_service +torrust-tracker-axum-health-check-api-server server::tests::it_should_release_the_listener_when_token_aware_startup_registration_fails +torrust-tracker-axum-http-server::integration server::v1::contract::configured_as_private::and_receiving_an_announce_request::should_fail_if_the_key_query_param_cannot_be_parsed +torrust-tracker-axum-http-server::integration server::v1::contract::configured_as_private::and_receiving_an_announce_request::should_fail_if_the_peer_cannot_be_authenticated_with_the_provided_key +torrust-tracker-axum-http-server::integration server::v1::contract::configured_as_private::and_receiving_an_announce_request::should_fail_if_the_peer_has_not_provided_the_authentication_key +torrust-tracker-axum-http-server::integration server::v1::contract::configured_as_private::and_receiving_an_announce_request::should_respond_to_authenticated_peers +torrust-tracker-axum-http-server::integration server::v1::contract::configured_as_private_and_whitelisted::and_receiving_an_announce_request::it_should_respond_to_an_authenticated_peer_announcing_a_listed_torrent +torrust-tracker-axum-http-server::integration server::v1::contract::configured_as_private_and_whitelisted::and_receiving_an_announce_request::it_should_return_an_authentication_error_for_an_unauthenticated_peer_announcing_an_unlisted_torrent +torrust-tracker-axum-http-server::integration server::v1::contract::configured_as_private_and_whitelisted::and_receiving_an_announce_request::it_should_return_a_whitelist_error_for_an_authenticated_peer_announcing_an_unlisted_torrent +torrust-tracker-axum-http-server::integration server::v1::contract::configured_as_private_and_whitelisted::receiving_an_scrape_request::it_should_hide_listed_torrent_stats_from_an_unauthenticated_peer +torrust-tracker-axum-http-server::integration server::v1::contract::configured_as_private_and_whitelisted::receiving_an_scrape_request::it_should_hide_unlisted_torrent_stats_from_an_authenticated_peer +torrust-tracker-axum-http-server::integration server::v1::contract::configured_as_private_and_whitelisted::receiving_an_scrape_request::it_should_return_listed_torrent_stats_to_an_authenticated_peer +torrust-tracker-axum-http-server::integration server::v1::contract::configured_as_private::receiving_an_scrape_request::should_fail_if_the_key_query_param_cannot_be_parsed +torrust-tracker-axum-http-server::integration server::v1::contract::configured_as_private::receiving_an_scrape_request::should_return_the_real_file_stats_when_the_client_is_authenticated +torrust-tracker-axum-http-server::integration server::v1::contract::configured_as_private::receiving_an_scrape_request::should_return_the_zeroed_file_when_the_authentication_key_provided_by_the_client_is_invalid +torrust-tracker-axum-http-server::integration server::v1::contract::configured_as_private::receiving_an_scrape_request::should_return_the_zeroed_file_when_the_client_is_not_authenticated +torrust-tracker-axum-http-server::integration server::v1::contract::configured_as_whitelisted::and_receiving_an_announce_request::should_allow_announcing_a_whitelisted_torrent +torrust-tracker-axum-http-server::integration server::v1::contract::configured_as_whitelisted::and_receiving_an_announce_request::should_fail_if_the_torrent_is_not_in_the_whitelist +torrust-tracker-axum-http-server::integration server::v1::contract::configured_as_whitelisted::receiving_an_scrape_request::should_return_the_file_stats_when_the_requested_file_is_whitelisted +torrust-tracker-axum-http-server::integration server::v1::contract::configured_as_whitelisted::receiving_an_scrape_request::should_return_the_zeroed_file_when_the_requested_file_is_not_whitelisted +torrust-tracker-axum-http-server::integration server::v1::contract::environment_should_be_started_and_stopped +torrust-tracker-axum-http-server::integration server::v1::contract::for_all_config_modes::and_running_on_reverse_proxy::should_fail_when_the_http_request_does_not_include_the_xff_http_request_header +torrust-tracker-axum-http-server::integration server::v1::contract::for_all_config_modes::and_running_on_reverse_proxy::should_fail_when_the_xff_http_request_header_contains_an_invalid_ip +torrust-tracker-axum-http-server::integration server::v1::contract::for_all_config_modes::health_check_endpoint_should_return_ok_if_the_http_tracker_is_running +torrust-tracker-axum-http-server::integration server::v1::contract::for_all_config_modes::receiving_an_announce_request::it_should_return_a_failure_response_for_a_non_empty_peer_ip_when_overrides_are_disabled +torrust-tracker-axum-http-server::integration server::v1::contract::for_all_config_modes::receiving_an_announce_request::it_should_return_distinct_failure_reasons_for_non_literal_peer_ip_parameters +torrust-tracker-axum-http-server::integration server::v1::contract::for_all_config_modes::receiving_an_announce_request::it_should_start_and_stop +torrust-tracker-axum-http-server::integration server::v1::contract::for_all_config_modes::receiving_an_announce_request::should_consider_two_peers_to_be_the_same_when_they_have_the_same_connection_socket_address_even_if_the_peer_id_is_different +torrust-tracker-axum-http-server::integration server::v1::contract::for_all_config_modes::receiving_an_announce_request::should_fail_when_a_mandatory_field_is_missing +torrust-tracker-axum-http-server::integration server::v1::contract::for_all_config_modes::receiving_an_announce_request::should_fail_when_the_compact_param_is_invalid +torrust-tracker-axum-http-server::integration server::v1::contract::for_all_config_modes::receiving_an_announce_request::should_fail_when_the_downloaded_param_is_invalid +torrust-tracker-axum-http-server::integration server::v1::contract::for_all_config_modes::receiving_an_announce_request::should_fail_when_the_event_param_is_invalid +torrust-tracker-axum-http-server::integration server::v1::contract::for_all_config_modes::receiving_an_announce_request::should_fail_when_the_info_hash_param_is_invalid +torrust-tracker-axum-http-server::integration server::v1::contract::for_all_config_modes::receiving_an_announce_request::should_fail_when_the_left_param_is_invalid +torrust-tracker-axum-http-server::integration server::v1::contract::for_all_config_modes::receiving_an_announce_request::should_fail_when_the_numwant_param_is_invalid +torrust-tracker-axum-http-server::integration server::v1::contract::for_all_config_modes::receiving_an_announce_request::should_fail_when_the_peer_id_param_is_invalid +torrust-tracker-axum-http-server::integration server::v1::contract::for_all_config_modes::receiving_an_announce_request::should_fail_when_the_port_param_is_invalid +torrust-tracker-axum-http-server::integration server::v1::contract::for_all_config_modes::receiving_an_announce_request::should_fail_when_the_uploaded_param_is_invalid +torrust-tracker-axum-http-server::integration server::v1::contract::for_all_config_modes::receiving_an_announce_request::should_fail_when_the_url_query_component_is_empty +torrust-tracker-axum-http-server::integration server::v1::contract::for_all_config_modes::receiving_an_announce_request::should_fail_when_url_query_parameters_are_invalid +torrust-tracker-axum-http-server::integration server::v1::contract::for_all_config_modes::receiving_an_announce_request::should_increase_the_number_of_tcp4_announce_requests_handled_in_statistics +torrust-tracker-axum-http-server::integration server::v1::contract::for_all_config_modes::receiving_an_announce_request::should_increase_the_number_of_tcp6_announce_requests_handled_in_statistics +torrust-tracker-axum-http-server::integration server::v1::contract::for_all_config_modes::receiving_an_announce_request::should_reject_an_invalid_peer_ip_parameter +torrust-tracker-axum-http-server::integration server::v1::contract::for_all_config_modes::receiving_an_announce_request::should_reject_a_valid_ipv4_peer_ip_when_overrides_are_disabled +torrust-tracker-axum-http-server::integration server::v1::contract::for_all_config_modes::receiving_an_announce_request::should_reject_a_valid_ipv6_peer_ip_when_overrides_are_disabled +torrust-tracker-axum-http-server::integration server::v1::contract::for_all_config_modes::receiving_an_announce_request::should_respond_if_only_the_mandatory_fields_are_provided +torrust-tracker-axum-http-server::integration server::v1::contract::for_all_config_modes::receiving_an_announce_request::should_return_no_peers_if_the_announced_peer_is_the_first_one +torrust-tracker-axum-http-server::integration server::v1::contract::for_all_config_modes::receiving_an_announce_request::should_return_the_compact_response +torrust-tracker-axum-http-server::integration server::v1::contract::for_all_config_modes::receiving_an_announce_request::should_return_the_compact_response_by_default +torrust-tracker-axum-http-server::integration server::v1::contract::for_all_config_modes::receiving_an_announce_request::should_return_the_list_of_previously_announced_peers +torrust-tracker-axum-http-server::integration server::v1::contract::for_all_config_modes::receiving_an_announce_request::should_return_the_list_of_previously_announced_peers_including_peers_using_ipv4_and_ipv6 +torrust-tracker-axum-http-server::integration server::v1::contract::for_all_config_modes::receiving_an_announce_request::when_the_ip_parameter_is_not_accepted::a_loopback_ipv4_client_uses_the_external_ip_when_ip_is_absent +torrust-tracker-axum-http-server::integration server::v1::contract::for_all_config_modes::receiving_an_announce_request::when_the_ip_parameter_is_not_accepted::a_loopback_ipv6_client_uses_the_external_ip_when_ip_is_absent +torrust-tracker-axum-http-server::integration server::v1::contract::for_all_config_modes::receiving_an_announce_request::when_the_ip_parameter_is_not_accepted::a_reverse_proxy_client_uses_the_x_forwarded_for_ip_when_ip_is_absent +torrust-tracker-axum-http-server::integration server::v1::contract::for_all_config_modes::receiving_an_scrape_request::should_accept_multiple_infohashes +torrust-tracker-axum-http-server::integration server::v1::contract::for_all_config_modes::receiving_an_scrape_request::should_fail_when_the_info_hash_param_is_invalid +torrust-tracker-axum-http-server::integration server::v1::contract::for_all_config_modes::receiving_an_scrape_request::should_fail_when_the_request_is_empty +torrust-tracker-axum-http-server::integration server::v1::contract::for_all_config_modes::receiving_an_scrape_request::should_increase_the_number_ot_tcp4_scrape_requests_handled_in_statistics +torrust-tracker-axum-http-server::integration server::v1::contract::for_all_config_modes::receiving_an_scrape_request::should_increase_the_number_ot_tcp6_scrape_requests_handled_in_statistics +torrust-tracker-axum-http-server::integration server::v1::contract::for_all_config_modes::receiving_an_scrape_request::should_return_a_file_with_zeroed_values_when_there_are_no_peers +torrust-tracker-axum-http-server::integration server::v1::contract::for_all_config_modes::receiving_an_scrape_request::should_return_only_the_first_100_files_when_the_request_has_101_infohashes +torrust-tracker-axum-http-server::integration server::v1::contract::for_all_config_modes::receiving_an_scrape_request::should_return_the_file_with_the_complete_peer_when_there_is_one_peer_with_no_bytes_pending_to_download +torrust-tracker-axum-http-server::integration server::v1::contract::for_all_config_modes::receiving_an_scrape_request::should_return_the_file_with_the_incomplete_peer_when_there_is_one_peer_with_bytes_pending_to_download +torrust-tracker-axum-http-server::integration server::v1::contract::using_ipv6_v6only::should_accept_ipv6_connections_with_ipv6_v6only_enabled +torrust-tracker-axum-http-server server::tests::it_should_build_a_health_check_url_using_the_service_binding_protocol +torrust-tracker-axum-http-server server::tests::it_should_drain_the_token_aware_http_server_when_its_cancellation_token_is_cancelled +torrust-tracker-axum-http-server server::tests::it_should_preserve_the_launcher_bind_address_after_starting_and_stopping +torrust-tracker-axum-http-server server::tests::it_should_register_the_injected_health_check_when_starting_the_token_aware_http_server +torrust-tracker-axum-http-server server::tests::it_should_release_the_listener_and_preserve_the_duplicate_binding_error_when_registration_fails +torrust-tracker-axum-http-server server::tests::it_should_release_the_listener_when_token_aware_startup_registration_fails +torrust-tracker-axum-http-server server::tests::it_should_return_a_typed_bind_error_when_the_listener_address_is_already_in_use +torrust-tracker-axum-http-server testing::environment::tests::it_should_cancel_the_event_listener_only_after_the_server_and_its_drain_controller_have_finished +torrust-tracker-axum-http-server testing::environment::tests::it_should_cancel_the_event_listener_when_the_join_is_abandoned_while_the_server_runs +torrust-tracker-axum-http-server testing::environment::tests::it_should_count_the_statistics_event_of_a_request_that_completes_while_stop_drains_the_server +torrust-tracker-axum-http-server testing::environment::tests::it_should_join_the_event_listener_before_reporting_an_http_server_task_failure +torrust-tracker-axum-http-server testing::environment::tests::it_should_not_leave_the_statistics_listener_running_when_the_http_server_fails_to_start +torrust-tracker-axum-http-server testing::environment::tests::it_should_release_the_http_binding_when_dropped_without_being_stopped +torrust-tracker-axum-http-server testing::environment::tests::it_should_release_the_http_binding_when_stopped +torrust-tracker-axum-http-server testing::environment::tests::it_should_report_a_drain_timeout_even_when_every_task_joins +torrust-tracker-axum-http-server testing::environment::tests::it_should_serve_requests_after_being_stopped_and_started_again +torrust-tracker-axum-http-server testing::environment::tests::it_should_stop_the_event_listener_when_dropped_without_being_stopped +torrust-tracker-axum-http-server testing::environment::tests::it_should_stop_without_a_join_failure_when_every_owned_task_finishes_through_cancellation +torrust-tracker-axum-http-server v1::extractors::announce_request::tests::it_should_extract_the_announce_request_from_the_url_query_params +torrust-tracker-axum-http-server v1::extractors::announce_request::tests::it_should_reject_a_request_with_a_query_that_cannot_be_parsed +torrust-tracker-axum-http-server v1::extractors::announce_request::tests::it_should_reject_a_request_with_a_query_that_cannot_be_parsed_into_an_announce_request +torrust-tracker-axum-http-server v1::extractors::announce_request::tests::it_should_reject_a_request_without_query_params +torrust-tracker-axum-http-server v1::extractors::authentication_key::tests::it_should_encode_an_invalid_key_format_failure_response_for_an_invalid_path_key +torrust-tracker-axum-http-server v1::extractors::authentication_key::tests::it_should_map_an_invalid_path_key_to_an_invalid_key_format_authentication_failure +torrust-tracker-axum-http-server v1::extractors::authentication_key::tests::it_should_parse_a_valid_path_key +torrust-tracker-axum-http-server v1::extractors::scrape_request::tests::it_should_extract_the_scrape_request_from_the_url_query_params +torrust-tracker-axum-http-server v1::extractors::scrape_request::tests::it_should_extract_the_scrape_request_from_the_url_query_params_with_more_than_one_info_hash +torrust-tracker-axum-http-server v1::extractors::scrape_request::tests::it_should_reject_a_request_with_a_query_that_cannot_be_parsed +torrust-tracker-axum-http-server v1::extractors::scrape_request::tests::it_should_reject_a_request_with_a_query_that_cannot_be_parsed_into_a_scrape_request +torrust-tracker-axum-http-server v1::extractors::scrape_request::tests::it_should_reject_a_request_without_query_params +torrust-tracker-axum-http-server v1::handlers::announce::tests::it_should_encode_a_compact_bencoded_response_when_compact_is_accepted +torrust-tracker-axum-http-server v1::handlers::announce::tests::it_should_encode_a_compact_bencoded_response_when_compact_is_omitted +torrust-tracker-axum-http-server v1::handlers::announce::tests::it_should_encode_a_non_compact_bencoded_response_when_compact_is_not_accepted +torrust-tracker-axum-http-server v1::handlers::announce::tests::with_tracker_in_listed_mode::it_should_fail_when_the_announced_torrent_is_not_whitelisted +torrust-tracker-axum-http-server v1::handlers::announce::tests::with_tracker_in_private_mode::it_should_fail_when_the_authentication_key_is_invalid +torrust-tracker-axum-http-server v1::handlers::announce::tests::with_tracker_in_private_mode::it_should_fail_when_the_authentication_key_is_missing +torrust-tracker-axum-http-server v1::handlers::announce::tests::with_tracker_not_on_reverse_proxy::it_should_fail_when_the_client_ip_from_the_connection_info_is_not_available +torrust-tracker-axum-http-server v1::handlers::announce::tests::with_tracker_on_reverse_proxy::it_should_fail_when_the_right_most_x_forwarded_for_header_ip_is_not_available +torrust-tracker-axum-http-server v1::handlers::scrape::tests::it_should_encode_a_bencoded_failure_response_when_the_client_ip_cannot_be_resolved +torrust-tracker-axum-http-server v1::handlers::scrape::tests::it_should_encode_domain_scrape_data_as_a_bencoded_response +torrust-tracker-axum-http-server v1::handlers::scrape::tests::it_should_encode_each_file_when_scrape_data_contains_multiple_files +torrust-tracker-axum-http-server v1::handlers::scrape::tests::with_tracker_in_listed_mode::it_should_return_zeroed_swarm_metadata_when_the_torrent_is_not_whitelisted +torrust-tracker-axum-http-server v1::handlers::scrape::tests::with_tracker_in_private_mode::it_should_return_zeroed_swarm_metadata_when_the_authentication_key_is_invalid +torrust-tracker-axum-http-server v1::handlers::scrape::tests::with_tracker_in_private_mode::it_should_return_zeroed_swarm_metadata_when_the_authentication_key_is_missing +torrust-tracker-axum-http-server v1::handlers::scrape::tests::with_tracker_not_on_reverse_proxy::it_should_fail_when_the_client_ip_from_the_connection_info_is_not_available +torrust-tracker-axum-http-server v1::handlers::scrape::tests::with_tracker_on_reverse_proxy::it_should_fail_when_the_right_most_x_forwarded_for_header_ip_is_not_available +torrust-tracker-axum-http-server v1::routes::tests::it_should_add_a_uuid_request_id_when_the_client_does_not_supply_one +torrust-tracker-axum-http-server v1::routes::tests::it_should_propagate_a_client_supplied_request_id +torrust-tracker-axum-rest-api-server::integration server::v1::contract::authentication::given_that_not_token_is_provided::it_should_not_authenticate_requests_when_the_token_is_missing +torrust-tracker-axum-rest-api-server::integration server::v1::contract::authentication::given_that_the_token_is_only_provided_in_the_authentication_header::it_should_authenticate_requests_when_the_token_is_provided_in_the_authentication_header +torrust-tracker-axum-rest-api-server::integration server::v1::contract::authentication::given_that_the_token_is_only_provided_in_the_authentication_header::it_should_not_authenticate_requests_when_the_token_is_empty +torrust-tracker-axum-rest-api-server::integration server::v1::contract::authentication::given_that_the_token_is_only_provided_in_the_authentication_header::it_should_not_authenticate_requests_when_the_token_is_invalid +torrust-tracker-axum-rest-api-server::integration server::v1::contract::authentication::given_that_the_token_is_only_provided_in_the_query_param::it_should_allow_the_token_query_param_to_be_at_any_position_in_the_url_query +torrust-tracker-axum-rest-api-server::integration server::v1::contract::authentication::given_that_the_token_is_only_provided_in_the_query_param::it_should_authenticate_requests_when_the_token_is_provided_as_a_query_param +torrust-tracker-axum-rest-api-server::integration server::v1::contract::authentication::given_that_the_token_is_only_provided_in_the_query_param::it_should_not_authenticate_requests_when_the_token_is_empty +torrust-tracker-axum-rest-api-server::integration server::v1::contract::authentication::given_that_the_token_is_only_provided_in_the_query_param::it_should_not_authenticate_requests_when_the_token_is_invalid +torrust-tracker-axum-rest-api-server::integration server::v1::contract::authentication::given_that_token_is_provided_via_get_param_and_authentication_header::it_should_authenticate_requests_using_the_token_provided_in_the_authentication_header +torrust-tracker-axum-rest-api-server::integration server::v1::contract::context::auth_key::deprecated_generate_key_endpoint::should_allow_generating_a_new_auth_key +torrust-tracker-axum-rest-api-server::integration server::v1::contract::context::auth_key::deprecated_generate_key_endpoint::should_fail_generating_a_new_auth_key_when_the_key_duration_is_invalid +torrust-tracker-axum-rest-api-server::integration server::v1::contract::context::auth_key::deprecated_generate_key_endpoint::should_fail_when_the_auth_key_cannot_be_generated +torrust-tracker-axum-rest-api-server::integration server::v1::contract::context::auth_key::deprecated_generate_key_endpoint::should_not_allow_generating_a_new_auth_key_for_unauthenticated_users +torrust-tracker-axum-rest-api-server::integration server::v1::contract::context::auth_key::should_allow_deleting_an_auth_key +torrust-tracker-axum-rest-api-server::integration server::v1::contract::context::auth_key::should_allow_generating_a_new_random_auth_key +torrust-tracker-axum-rest-api-server::integration server::v1::contract::context::auth_key::should_allow_reloading_keys +torrust-tracker-axum-rest-api-server::integration server::v1::contract::context::auth_key::should_allow_uploading_a_preexisting_auth_key +torrust-tracker-axum-rest-api-server::integration server::v1::contract::context::auth_key::should_fail_deleting_an_auth_key_when_the_key_id_is_invalid +torrust-tracker-axum-rest-api-server::integration server::v1::contract::context::auth_key::should_fail_generating_a_new_auth_key_when_the_key_duration_is_invalid +torrust-tracker-axum-rest-api-server::integration server::v1::contract::context::auth_key::should_fail_generating_a_new_auth_key_when_the_provided_key_is_invalid +torrust-tracker-axum-rest-api-server::integration server::v1::contract::context::auth_key::should_fail_when_keys_cannot_be_reloaded +torrust-tracker-axum-rest-api-server::integration server::v1::contract::context::auth_key::should_fail_when_the_auth_key_cannot_be_deleted +torrust-tracker-axum-rest-api-server::integration server::v1::contract::context::auth_key::should_fail_when_the_auth_key_cannot_be_generated +torrust-tracker-axum-rest-api-server::integration server::v1::contract::context::auth_key::should_not_allow_deleting_an_auth_key_for_unauthenticated_users +torrust-tracker-axum-rest-api-server::integration server::v1::contract::context::auth_key::should_not_allow_generating_a_new_auth_key_for_unauthenticated_users +torrust-tracker-axum-rest-api-server::integration server::v1::contract::context::auth_key::should_not_allow_reloading_keys_for_unauthenticated_users +torrust-tracker-axum-rest-api-server::integration server::v1::contract::context::auth_key::should_reject_auth_key_requests_when_private_mode_is_disabled_without_database_access +torrust-tracker-axum-rest-api-server::integration server::v1::contract::context::health_check::health_check_endpoint_should_return_status_ok_if_api_is_running +torrust-tracker-axum-rest-api-server::integration server::v1::contract::context::stats::should_allow_getting_tracker_statistics +torrust-tracker-axum-rest-api-server::integration server::v1::contract::context::stats::should_expose_an_enabled_zero_value_persisted_metric +torrust-tracker-axum-rest-api-server::integration server::v1::contract::context::stats::should_expose_completed_download_availability_and_omit_the_persisted_metric_when_disabled +torrust-tracker-axum-rest-api-server::integration server::v1::contract::context::stats::should_not_allow_getting_tracker_statistics_for_unauthenticated_users +torrust-tracker-axum-rest-api-server::integration server::v1::contract::context::torrent::should_allow_getting_a_list_of_torrents_providing_infohashes +torrust-tracker-axum-rest-api-server::integration server::v1::contract::context::torrent::should_allow_getting_all_torrents +torrust-tracker-axum-rest-api-server::integration server::v1::contract::context::torrent::should_allow_getting_a_torrent_info +torrust-tracker-axum-rest-api-server::integration server::v1::contract::context::torrent::should_allow_limiting_the_torrents_in_the_result +torrust-tracker-axum-rest-api-server::integration server::v1::contract::context::torrent::should_allow_the_torrents_result_pagination +torrust-tracker-axum-rest-api-server::integration server::v1::contract::context::torrent::should_fail_getting_a_torrent_info_when_the_provided_infohash_is_invalid +torrust-tracker-axum-rest-api-server::integration server::v1::contract::context::torrent::should_fail_getting_torrents_when_the_info_hash_parameter_is_invalid +torrust-tracker-axum-rest-api-server::integration server::v1::contract::context::torrent::should_fail_getting_torrents_when_the_limit_query_parameter_cannot_be_parsed +torrust-tracker-axum-rest-api-server::integration server::v1::contract::context::torrent::should_fail_getting_torrents_when_the_offset_query_parameter_cannot_be_parsed +torrust-tracker-axum-rest-api-server::integration server::v1::contract::context::torrent::should_fail_while_getting_a_torrent_info_when_the_torrent_does_not_exist +torrust-tracker-axum-rest-api-server::integration server::v1::contract::context::torrent::should_include_equal_v1_peer_timestamp_fields_in_the_raw_torrent_json_response +torrust-tracker-axum-rest-api-server::integration server::v1::contract::context::torrent::should_not_allow_getting_a_torrent_info_for_unauthenticated_users +torrust-tracker-axum-rest-api-server::integration server::v1::contract::context::torrent::should_not_allow_getting_torrents_for_unauthenticated_users +torrust-tracker-axum-rest-api-server::integration server::v1::contract::context::whitelist::should_allow_reload_the_whitelist_from_the_database +torrust-tracker-axum-rest-api-server::integration server::v1::contract::context::whitelist::should_allow_removing_a_torrent_from_the_whitelist +torrust-tracker-axum-rest-api-server::integration server::v1::contract::context::whitelist::should_allow_whitelisting_a_torrent +torrust-tracker-axum-rest-api-server::integration server::v1::contract::context::whitelist::should_allow_whitelisting_a_torrent_that_has_been_already_whitelisted +torrust-tracker-axum-rest-api-server::integration server::v1::contract::context::whitelist::should_fail_removing_a_torrent_from_the_whitelist_when_the_provided_infohash_is_invalid +torrust-tracker-axum-rest-api-server::integration server::v1::contract::context::whitelist::should_fail_when_the_torrent_cannot_be_removed_from_the_whitelist +torrust-tracker-axum-rest-api-server::integration server::v1::contract::context::whitelist::should_fail_when_the_torrent_cannot_be_whitelisted +torrust-tracker-axum-rest-api-server::integration server::v1::contract::context::whitelist::should_fail_when_the_whitelist_cannot_be_reloaded_from_the_database +torrust-tracker-axum-rest-api-server::integration server::v1::contract::context::whitelist::should_fail_whitelisting_a_torrent_when_the_provided_infohash_is_invalid +torrust-tracker-axum-rest-api-server::integration server::v1::contract::context::whitelist::should_not_allow_removing_a_torrent_from_the_whitelist_for_unauthenticated_users +torrust-tracker-axum-rest-api-server::integration server::v1::contract::context::whitelist::should_not_allow_whitelisting_a_torrent_for_unauthenticated_users +torrust-tracker-axum-rest-api-server::integration server::v1::contract::context::whitelist::should_not_fail_trying_to_remove_a_non_whitelisted_torrent_from_the_whitelist +torrust-tracker-axum-rest-api-server::integration server::v1::contract::context::whitelist::should_reject_whitelist_requests_when_listed_mode_is_disabled_without_database_access +torrust-tracker-axum-rest-api-server server::tests::it_should_be_able_to_start_and_stop +torrust-tracker-axum-rest-api-server server::tests::it_should_drain_the_token_aware_rest_api_when_its_cancellation_token_is_cancelled +torrust-tracker-axum-rest-api-server server::tests::it_should_release_the_listener_when_token_aware_startup_registration_fails +torrust-tracker-axum-rest-api-server testing::environment::tests::it_should_join_the_drain_controller_before_reporting_a_server_task_failure +torrust-tracker-axum-rest-api-server testing::environment::tests::it_should_panic_naming_each_failing_task_when_stopped_with_failed_tasks +torrust-tracker-axum-rest-api-server testing::environment::tests::it_should_release_the_rest_api_binding_when_dropped_without_being_stopped +torrust-tracker-axum-rest-api-server testing::environment::tests::it_should_release_the_rest_api_binding_when_stopped +torrust-tracker-axum-rest-api-server testing::environment::tests::it_should_report_a_drain_timeout_even_when_both_tasks_join +torrust-tracker-axum-rest-api-server testing::environment::tests::it_should_report_a_server_error_and_a_drain_timeout_together +torrust-tracker-axum-rest-api-server testing::environment::tests::it_should_serve_requests_after_being_stopped_and_started_again +torrust-tracker-axum-rest-api-server testing::environment::tests::it_should_stop_without_a_failure_when_the_server_and_its_drain_controller_finish_through_cancellation +torrust-tracker-axum-rest-api-server v1::middlewares::auth::tests::it_should_authenticate_a_configured_token::case_1_the_first_configured_token +torrust-tracker-axum-rest-api-server v1::middlewares::auth::tests::it_should_authenticate_a_configured_token::case_2_the_second_configured_token +torrust-tracker-axum-rest-api-server v1::middlewares::auth::tests::it_should_let_the_request_reach_the_handler_when_a_configured_token_is_provided::case_1_the_token_in_the_header +torrust-tracker-axum-rest-api-server v1::middlewares::auth::tests::it_should_let_the_request_reach_the_handler_when_a_configured_token_is_provided::case_2_the_other_configured_token_in_the_header +torrust-tracker-axum-rest-api-server v1::middlewares::auth::tests::it_should_let_the_request_reach_the_handler_when_a_configured_token_is_provided::case_3_the_token_in_the_query +torrust-tracker-axum-rest-api-server v1::middlewares::auth::tests::it_should_let_the_request_reach_the_handler_when_a_configured_token_is_provided::case_4_the_token_in_the_query_at_any_position +torrust-tracker-axum-rest-api-server v1::middlewares::auth::tests::it_should_let_the_request_reach_the_handler_when_a_configured_token_is_provided::case_5_a_valid_header_and_an_invalid_query_token +torrust-tracker-axum-rest-api-server v1::middlewares::auth::tests::it_should_not_authenticate_a_token_that_is_not_a_configured_one::case_1_a_different_first_character +torrust-tracker-axum-rest-api-server v1::middlewares::auth::tests::it_should_not_authenticate_a_token_that_is_not_a_configured_one::case_2_a_different_last_character +torrust-tracker-axum-rest-api-server v1::middlewares::auth::tests::it_should_not_authenticate_a_token_that_is_not_a_configured_one::case_3_a_shorter_token +torrust-tracker-axum-rest-api-server v1::middlewares::auth::tests::it_should_not_authenticate_a_token_that_is_not_a_configured_one::case_4_a_longer_token +torrust-tracker-axum-rest-api-server v1::middlewares::auth::tests::it_should_not_authenticate_a_token_that_is_not_a_configured_one::case_5_an_empty_token +torrust-tracker-axum-rest-api-server v1::middlewares::auth::tests::it_should_reject_a_duplicated_token_query_parameter_before_authenticating +torrust-tracker-axum-rest-api-server v1::middlewares::auth::tests::it_should_reject_an_unreadable_authorization_header_even_with_a_valid_token_query +torrust-tracker-axum-rest-api-server v1::middlewares::auth::tests::it_should_reject_the_request_as_token_not_valid_when_the_provided_token_is_not_a_configured_one::case_1_an_invalid_token_in_the_header +torrust-tracker-axum-rest-api-server v1::middlewares::auth::tests::it_should_reject_the_request_as_token_not_valid_when_the_provided_token_is_not_a_configured_one::case_2_an_invalid_token_in_the_query +torrust-tracker-axum-rest-api-server v1::middlewares::auth::tests::it_should_reject_the_request_as_token_not_valid_when_the_provided_token_is_not_a_configured_one::case_3_an_empty_token_in_the_query +torrust-tracker-axum-rest-api-server v1::middlewares::auth::tests::it_should_reject_the_request_as_token_not_valid_when_the_provided_token_is_not_a_configured_one::case_4_an_invalid_header_even_with_a_valid_query_token +torrust-tracker-axum-rest-api-server v1::middlewares::auth::tests::it_should_reject_the_request_as_token_not_valid_when_the_provided_token_is_not_a_configured_one::case_5_the_bearer_scheme_without_a_token +torrust-tracker-axum-rest-api-server v1::middlewares::auth::tests::it_should_reject_the_request_as_token_not_valid_when_the_provided_token_is_not_a_configured_one::case_6_the_bearer_scheme_followed_by_a_space_and_no_token +torrust-tracker-axum-rest-api-server v1::middlewares::auth::tests::it_should_reject_the_request_as_unauthorized_when_no_token_is_provided::case_1_no_query +torrust-tracker-axum-rest-api-server v1::middlewares::auth::tests::it_should_reject_the_request_as_unauthorized_when_no_token_is_provided::case_2_a_query_without_a_token +torrust-tracker-axum-rest-api-server v1::middlewares::auth::tests::it_should_reject_the_request_as_unknown_token_provided_when_the_header_is_not_a_bearer_credential::case_1_another_scheme +torrust-tracker-axum-rest-api-server v1::middlewares::auth::tests::it_should_reject_the_request_as_unknown_token_provided_when_the_header_is_not_a_bearer_credential::case_2_a_lower_case_bearer_scheme +torrust-tracker-axum-rest-api-server v1::middlewares::auth::tests::it_should_reject_the_request_as_unknown_token_provided_when_the_header_is_not_a_bearer_credential::case_3_the_scheme_glued_to_the_token +torrust-tracker-axum-rest-api-server v1::middlewares::auth::tests::it_should_reject_the_request_as_unknown_token_provided_when_the_header_is_not_a_bearer_credential::case_4_an_empty_header +torrust-tracker-axum-rest-api-server v1::middlewares::auth::tests::it_should_reject_the_request_as_unknown_token_provided_when_the_header_is_not_a_bearer_credential::case_5_another_scheme_even_with_a_valid_query_token +torrust-tracker-axum-rest-api-server v1::responses::tests::it_should_answer_a_bad_request_with_the_message_as_plain_text::case_1_an_invalid_info_hash +torrust-tracker-axum-rest-api-server v1::responses::tests::it_should_answer_a_bad_request_with_the_message_as_plain_text::case_2_an_invalid_auth_key +torrust-tracker-axum-rest-api-server v1::responses::tests::it_should_answer_a_bad_request_with_the_message_as_plain_text::case_3_a_custom_message +torrust-tracker-axum-rest-api-server v1::responses::tests::it_should_answer_an_internal_server_error_with_the_legacy_debug_text_of_the_reason::case_1_a_plain_reason +torrust-tracker-axum-rest-api-server v1::responses::tests::it_should_answer_an_internal_server_error_with_the_legacy_debug_text_of_the_reason::case_2_a_reason_with_quotes +torrust-tracker-axum-rest-api-server v1::responses::tests::it_should_answer_conflict_with_the_reason_in_a_json_error_body_when_a_capability_is_disabled::case_1_a_plain_reason +torrust-tracker-axum-rest-api-server v1::responses::tests::it_should_answer_conflict_with_the_reason_in_a_json_error_body_when_a_capability_is_disabled::case_2_a_reason_with_quotes +torrust-tracker-axum-rest-api-server v1::responses::tests::it_should_answer_ok_with_a_json_status_ok_body +torrust-tracker-axum-server signals::tests::it_should_force_close_a_connection_still_open_when_the_drain_times_out +torrust-tracker-axum-server signals::tests::it_should_return_drained_when_an_in_flight_connection_closes_after_cancellation +torrust-tracker-axum-server signals::tests::it_should_return_drained_when_the_token_is_cancelled_with_no_active_connections +torrust-tracker-axum-server signals::tests::it_should_return_timed_out_when_a_connection_remains_active_after_cancellation +torrust-tracker-axum-server tls::tests::it_should_error_on_bad_tls_config +torrust-tracker-axum-server tls::tests::it_should_error_on_missing_cert_or_key_paths +torrust-tracker::banning-udp-metrics-disabled-port-zero it_should_increase_banned_ip_metric_for_metrics_disabled_port_zero_udp_listener +torrust-tracker::banning-udp-shared-connection-id-error-limit it_should_share_the_v3_connection_id_error_limit_across_udp_listeners +torrust-tracker::banning-udp-shared-connection-id-error-limit-reverse-order it_should_share_the_v3_connection_id_error_limit_across_udp_listeners_in_reverse_declaration_order +torrust-tracker::bin/torrust-tracker tests::it_should_not_bind_the_config_toml_path_argument_from_the_environment +torrust-tracker::bin/torrust-tracker tests::it_should_parse_a_long_config_toml_path_argument +torrust-tracker::bin/torrust-tracker tests::it_should_parse_a_short_config_toml_path_argument +torrust-tracker::bin/torrust-tracker tests::it_should_render_help +torrust-tracker::bin/torrust-tracker tests::it_should_return_a_usage_error_when_an_argument_is_unknown +torrust-tracker::bin/torrust-tracker tests::it_should_return_a_usage_error_when_the_config_toml_path_value_is_empty +torrust-tracker::bin/torrust-tracker tests::it_should_return_a_usage_error_when_the_config_toml_path_value_is_missing +torrust-tracker bootstrap::app::tests::it_should_preserve_the_persistence_requirement_error_before_setup_composes_the_application +torrust-tracker bootstrap::app::tests::it_should_preserve_the_semantic_validation_error_before_setup_composes_the_application +torrust-tracker bootstrap::config::tests::it_should_load_an_explicit_path_without_mutating_environment_sources +torrust-tracker bootstrap::config::tests::it_should_load_every_shipped_configuration_template +torrust-tracker bootstrap::config::tests::it_should_load_with_default_config +torrust-tracker bootstrap::config::tests::it_should_name_the_cli_argument_when_an_explicit_configuration_source_cannot_be_prepared +torrust-tracker bootstrap::config::tests::it_should_prefer_an_explicit_path_over_both_environment_base_sources +torrust-tracker bootstrap::config::tests::it_should_prefer_an_explicit_path_over_the_complete_toml_environment_source +torrust-tracker bootstrap::config::tests::it_should_prefer_an_explicit_path_over_the_path_environment_source +torrust-tracker bootstrap::config::tests::it_should_return_a_typed_load_error_when_the_configured_source_file_is_missing +torrust-tracker bootstrap::event_flows::tests::it_should_accept_a_consumer_of_a_bus_that_has_no_producer_yet +torrust-tracker bootstrap::event_flows::tests::it_should_not_return_listeners_that_were_never_registered +torrust-tracker bootstrap::event_flows::tests::it_should_reject_a_consumer_registered_after_a_producer_of_its_bus +torrust-tracker bootstrap::event_flows::tests::it_should_return_the_consumers_of_every_bus_a_producer_publishes_on_without_duplicates +torrust-tracker bootstrap::jobs::health_check_api::tests::it_should_complete_after_draining_when_the_health_check_api_server_stops_independently +torrust-tracker bootstrap::jobs::health_check_api::tests::it_should_fail_after_draining_when_the_health_check_api_server_returns_an_error +torrust-tracker bootstrap::jobs::health_check_api::tests::it_should_fail_after_draining_when_the_health_check_api_server_task_panics +torrust-tracker bootstrap::jobs::health_check_api::tests::it_should_release_the_listener_when_the_component_is_dropped_before_it_runs +torrust-tracker bootstrap::jobs::http_tracker::tests::it_should_complete_after_draining_when_the_server_stops_independently +torrust-tracker bootstrap::jobs::http_tracker::tests::it_should_fail_after_draining_when_the_server_task_panics +torrust-tracker bootstrap::jobs::http_tracker::tests::it_should_return_a_listener_error_through_the_public_http_starter +torrust-tracker bootstrap::jobs::http_tracker::tests::it_should_return_a_tls_error_before_starting_the_http_listener +torrust-tracker bootstrap::jobs::http_tracker::tests::it_should_start_http_tracker +torrust-tracker bootstrap::jobs::manager::tests::it_should_abort_a_consumer_busy_in_a_handler_at_the_shared_deadline_even_when_it_was_cancelled_late +torrust-tracker bootstrap::jobs::manager::tests::it_should_abort_and_join_a_legacy_pre_spawned_job_after_the_deadline +torrust-tracker bootstrap::jobs::manager::tests::it_should_abort_and_name_a_consumer_that_was_never_asked_to_stop_when_the_deadline_expires +torrust-tracker bootstrap::jobs::manager::tests::it_should_abort_a_nested_server_task_when_the_component_is_aborted_after_the_deadline +torrust-tracker bootstrap::jobs::manager::tests::it_should_abort_a_nested_shutdown_controller_when_the_component_is_aborted_after_the_deadline +torrust-tracker bootstrap::jobs::manager::tests::it_should_apply_one_deadline_to_concurrently_blocked_jobs_and_join_them_after_aborting +torrust-tracker bootstrap::jobs::manager::tests::it_should_apply_the_same_deadline_to_direct_and_legacy_jobs +torrust-tracker bootstrap::jobs::manager::tests::it_should_cancel_a_component_that_waits_for_nobody_at_once_while_another_waits_for_its_producer +torrust-tracker bootstrap::jobs::manager::tests::it_should_cancel_a_consumer_only_after_every_producer_that_stops_before_it_has_completed +torrust-tracker bootstrap::jobs::manager::tests::it_should_cancel_a_consumer_when_its_producer_fails_and_keep_the_failure_visible +torrust-tracker bootstrap::jobs::manager::tests::it_should_cancel_a_consumer_when_its_producer_panics_and_keep_the_panic_visible +torrust-tracker bootstrap::jobs::manager::tests::it_should_cancel_a_consumer_whose_producer_completed_before_shutdown_was_requested +torrust-tracker bootstrap::jobs::manager::tests::it_should_cancel_a_reserved_component_that_is_spawned_after_the_manager_is_cancelled +torrust-tracker bootstrap::jobs::manager::tests::it_should_cancel_a_spawned_components_own_token_when_the_manager_is_cancelled +torrust-tracker bootstrap::jobs::manager::tests::it_should_cancel_each_consumer_when_the_producers_that_stop_before_it_have_completed +torrust-tracker bootstrap::jobs::manager::tests::it_should_issue_a_cancelled_token_when_a_component_is_reserved_after_the_manager_is_cancelled +torrust-tracker bootstrap::jobs::manager::tests::it_should_leave_no_edge_and_no_running_component_when_a_spawn_with_edges_is_rejected +torrust-tracker bootstrap::jobs::manager::tests::it_should_not_cancel_a_consumer_when_its_producers_complete_before_shutdown_is_requested +torrust-tracker bootstrap::jobs::manager::tests::it_should_not_cancel_a_reserved_component_when_only_the_legacy_root_token_is_cancelled +torrust-tracker bootstrap::jobs::manager::tests::it_should_refuse_to_issue_a_reused_component_id_when_the_id_space_is_exhausted +torrust-tracker bootstrap::jobs::manager::tests::it_should_reject_a_component_that_stops_before_itself +torrust-tracker bootstrap::jobs::manager::tests::it_should_reject_a_reservation_issued_by_another_manager +torrust-tracker bootstrap::jobs::manager::tests::it_should_reject_a_stop_before_target_issued_by_another_manager +torrust-tracker bootstrap::jobs::manager::tests::it_should_reject_a_stop_before_target_reserved_after_the_component +torrust-tracker bootstrap::jobs::manager::tests::it_should_reject_a_stop_before_target_whose_reservation_was_dropped +torrust-tracker bootstrap::jobs::manager::tests::it_should_reject_stop_before_edges_after_shutdown_was_requested +torrust-tracker bootstrap::jobs::manager::tests::it_should_release_a_dropped_reservation_without_affecting_other_components +torrust-tracker bootstrap::jobs::manager::tests::it_should_report_a_named_component_failure +torrust-tracker bootstrap::jobs::manager::tests::it_should_report_a_named_panic +torrust-tracker bootstrap::jobs::manager::tests::it_should_report_jobs_in_completion_order +torrust-tracker bootstrap::jobs::manager::tests::it_should_report_which_components_the_shutdown_cancels_and_which_wait +torrust-tracker bootstrap::jobs::manager::tests::it_should_signal_and_drop_an_uncooperative_running_future_when_the_component_is_aborted_after_the_deadline +torrust-tracker bootstrap::jobs::manager::tests::it_should_stop_components_and_legacy_jobs_once_when_the_manager_is_cancelled_twice +torrust-tracker bootstrap::jobs::torrent_cleanup::tests::it_should_return_cancelled_when_the_token_is_cancelled +torrust-tracker bootstrap::jobs::torrent_cleanup::tests::it_should_return_completed_when_the_torrents_manager_is_dropped +torrust-tracker bootstrap::jobs::tracker_apis::tests::it_should_complete_after_draining_when_the_rest_api_server_stops_independently +torrust-tracker bootstrap::jobs::tracker_apis::tests::it_should_fail_after_draining_when_the_rest_api_server_returns_an_error +torrust-tracker bootstrap::jobs::tracker_apis::tests::it_should_fail_after_draining_when_the_rest_api_server_task_panics +torrust-tracker bootstrap::jobs::tracker_apis::tests::it_should_start_http_tracker +torrust-tracker bootstrap::jobs::udp_tracker_server::tests::it_should_publish_the_new_banned_ips_total_when_it_resets_the_bans +torrust-tracker bootstrap::jobs::udp_tracker_server::tests::it_should_stop_the_ban_cleanup_job_when_cancelled +torrust-tracker bootstrap::jobs::udp_tracker::tests::it_should_abort_the_receive_loop_when_the_component_is_dropped_while_waiting_for_it +torrust-tracker bootstrap::jobs::udp_tracker::tests::it_should_fail_when_the_receive_loop_stops_with_an_error +torrust-tracker bootstrap::jobs::udp_tracker::tests::it_should_fail_when_the_receive_loop_task_panics +torrust-tracker bootstrap::jobs::udp_tracker::tests::it_should_release_the_socket_when_the_component_is_dropped_before_it_runs +torrust-tracker bootstrap::jobs::udp_tracker::tests::it_should_report_cancelled_when_the_receive_loop_returns_ok +torrust-tracker bootstrap::persistence::tests::it_should_allow_persistence_free_core_configuration +torrust-tracker bootstrap::persistence::tests::it_should_reject_listing_without_a_database +torrust-tracker bootstrap::persistence::tests::it_should_reject_persistent_completed_metrics_without_a_database +torrust-tracker bootstrap::persistence::tests::it_should_reject_persistent_completed_metrics_without_tracker_usage_statistics +torrust-tracker bootstrap::persistence::tests::it_should_reject_private_mode_without_a_database +torrust-tracker::cli-configuration base_source_precedence::it_should_select_the_cli_configuration_when_both_child_environment_base_sources_are_set +torrust-tracker::cli-configuration invalid_sources::it_should_exit_with_a_usage_error_when_the_config_toml_path_value_is_empty +torrust-tracker::cli-configuration invalid_sources::it_should_exit_with_a_usage_error_when_the_config_toml_path_value_is_missing +torrust-tracker::cli-configuration invalid_sources::it_should_fail_startup_naming_the_path_when_the_cli_configuration_file_is_missing +torrust-tracker::cli-configuration invalid_sources::it_should_fail_startup_naming_the_path_when_the_cli_configuration_file_is_unreadable +torrust-tracker::cli-configuration invalid_sources::it_should_fail_startup_naming_the_path_when_the_cli_configuration_source_is_a_directory +torrust-tracker::cli-configuration invalid_sources::it_should_fail_startup_naming_the_path_when_the_cli_configuration_toml_is_malformed +torrust-tracker::cli-configuration invalid_sources::it_should_not_search_parent_directories_for_a_relative_cli_configuration_file +torrust-tracker::cli-configuration native_tracker::command::tests::it_should_select_its_configuration_with_the_cli_and_remove_legacy_base_source_variables +torrust-tracker::cli-configuration native_tracker::command::tests::it_should_set_the_child_only_health_check_bind_address_override_after_removing_the_inherited_value +torrust-tracker::cli-configuration native_tracker::command::tests::it_should_write_a_port_zero_configuration_with_workspace_local_sqlite_storage +torrust-tracker::cli-configuration native_tracker::failed_start::tests::it_should_construct_a_parent_only_relative_source_from_the_child_working_directory +torrust-tracker::cli-configuration native_tracker::failed_start::tests::it_should_reap_a_failed_start_dropped_without_a_tokio_runtime +torrust-tracker::cli-configuration native_tracker::failed_start::tests::it_should_restore_permissions_when_the_restore_guard_is_dropped_without_a_tokio_runtime +torrust-tracker::cli-configuration native_tracker::failed_start::tests::it_should_restore_the_unreadable_source_permissions_after_waiting_for_exit +torrust-tracker::cli-configuration native_tracker::health::tests::it_should_extract_the_assigned_health_check_address_from_its_startup_log +torrust-tracker::cli-configuration native_tracker::health::tests::it_should_reject_non_health_check_startup_logs +torrust-tracker::cli-configuration per_value_overrides::it_should_select_the_health_check_bind_address_override_over_the_cli_configuration +torrust-tracker-client-lib http::client::tests::it_appends_auth_key_to_existing_announce_path +torrust-tracker-client-lib http::client::tests::it_does_not_append_auth_key_when_path_already_ends_with_same_key +torrust-tracker-client-lib http::client::tests::it_keeps_custom_path_unchanged_for_announce +torrust-tracker-client-lib http::client::tests::it_keeps_existing_announce_path_unchanged +torrust-tracker-client-lib http::client::tests::it_keeps_existing_scrape_path_unchanged +torrust-tracker-client-lib http::client::tests::it_uses_announce_for_base_url_without_trailing_slash +torrust-tracker-client-lib http::client::tests::it_uses_announce_for_base_url_with_trailing_slash +torrust-tracker-client-lib http::client::tests::it_uses_scrape_for_base_url_without_trailing_slash +torrust-tracker-client-lib peer_id::tests::default_production_peer_id_should_be_stable_within_a_process +torrust-tracker-client-lib peer_id::tests::default_test_peer_id_should_use_rc_prefix_and_3000_version +torrust-tracker-client-lib udp::tests::it_should_display_unrecognized_udp_tracker_response_without_debug_noise +torrust-tracker-configuration tests::it_should_apply_an_environment_override_to_an_explicit_file +torrust-tracker-configuration tests::it_should_apply_an_environment_override_to_a_path_environment_source +torrust-tracker-configuration tests::it_should_apply_existing_defaults_when_only_mandatory_options_are_provided_by_an_explicit_file +torrust-tracker-configuration tests::it_should_include_the_explicit_path_but_not_contents_when_explicit_toml_is_malformed +torrust-tracker-configuration tests::it_should_load_the_content_read_when_the_explicit_file_changes_after_info_is_created +torrust-tracker-configuration tests::it_should_not_search_parent_directories_for_a_relative_explicit_path +torrust-tracker-configuration tests::it_should_preserve_a_non_utf8_explicit_path_when_explicit_toml_is_malformed +torrust-tracker-configuration tests::it_should_report_the_first_mandatory_option_when_the_path_environment_file_is_missing +torrust-tracker-configuration tests::it_should_require_each_mandatory_option_when_loading_an_explicit_file +torrust-tracker-configuration tests::it_should_return_a_path_specific_error_when_an_explicit_file_is_missing +torrust-tracker-configuration tests::it_should_return_a_path_specific_error_when_an_explicit_path_is_a_directory +torrust-tracker-configuration tests::it_should_search_parent_directories_for_a_relative_path_environment_source +torrust-tracker-configuration tests::it_should_select_an_explicit_file_when_no_environment_base_source_is_set +torrust-tracker-configuration tests::it_should_select_an_explicit_file_without_merging_complete_toml_or_path_environment_sources +torrust-tracker-configuration tests::it_should_select_complete_toml_when_complete_toml_and_path_environment_sources_are_set +torrust-tracker-configuration tests::it_should_select_complete_toml_when_only_complete_toml_environment_source_is_set +torrust-tracker-configuration tests::it_should_select_the_given_default_file_when_no_environment_base_source_is_set +torrust-tracker-configuration tests::it_should_select_the_path_file_when_only_path_environment_source_is_set +torrust-tracker-configuration v2_0_0::database::tests::it_should_allow_masking_the_mysql_user_password +torrust-tracker-configuration v2_0_0::database::tests::it_should_allow_masking_the_postgresql_user_password +torrust-tracker-configuration v2_0_0::tests::configuration_json_output_should_redact_access_tokens +torrust-tracker-configuration v2_0_0::tests::configuration_should_allow_to_overwrite_the_default_tracker_api_token_for_admin_with_an_env_var +torrust-tracker-configuration v2_0_0::tests::configuration_should_be_saved_in_a_toml_config_file +torrust-tracker-configuration v2_0_0::tests::configuration_should_have_default_values +torrust-tracker-configuration v2_0_0::tests::configuration_should_not_contain_an_external_ip_by_default +torrust-tracker-configuration v2_0_0::tests::configuration_should_use_the_default_values_when_only_the_mandatory_options_are_provided_by_the_user_via_toml_content +torrust-tracker-configuration v2_0_0::tests::configuration_should_use_the_default_values_when_only_the_mandatory_options_are_provided_by_the_user_via_toml_file +torrust-tracker-configuration v2_0_0::tests::default_configuration_could_be_overwritten_from_a_single_env_var_with_toml_contents +torrust-tracker-configuration v2_0_0::tests::default_configuration_could_be_overwritten_from_a_toml_config_file +torrust-tracker-configuration v2_0_0::tests::deserialization::should_deserialize_valid_external_ip_from_toml +torrust-tracker-configuration v2_0_0::tests::deserialization::should_reject_unspecified_ipv4_external_ip_in_toml +torrust-tracker-configuration v2_0_0::tests::deserialization::should_reject_unspecified_ipv6_external_ip_in_toml +torrust-tracker-configuration v2_0_0::tests::external_ip_should_accept_valid_ipv4_address +torrust-tracker-configuration v2_0_0::tests::external_ip_should_parse_from_str +torrust-tracker-configuration v2_0_0::tests::external_ip_should_reject_unspecified_ipv4_address +torrust-tracker-configuration v2_0_0::tests::external_ip_should_reject_unspecified_ipv6_address +torrust-tracker-configuration v2_0_0::tests::persisted_configuration_toml_should_include_access_tokens +torrust-tracker-configuration v2_0_0::tracker_api::tests::default_http_api_configuration_should_not_contains_any_token +torrust-tracker-configuration v2_0_0::tracker_api::tests::http_api_configuration_should_allow_adding_tokens +torrust-tracker-configuration v2_0_0::tracker_api::tests::http_api_tokens_should_deserialize_from_toml_and_serialize_to_redacted_json +torrust-tracker-configuration v3_0_0::database::tests::it_should_deserialize_mysql_configuration_with_a_default_port +torrust-tracker-configuration v3_0_0::database::tests::it_should_deserialize_postgresql_configuration_with_a_default_port +torrust-tracker-configuration v3_0_0::database::tests::it_should_percent_encode_network_database_connection_components +torrust-tracker-configuration v3_0_0::database::tests::it_should_redact_password_when_serializing_a_network_database +torrust-tracker-configuration v3_0_0::database::tests::it_should_reject_a_path_for_network_database_drivers +torrust-tracker-configuration v3_0_0::database::tests::it_should_reject_fields_for_another_database_driver +torrust-tracker-configuration v3_0_0::database::tests::it_should_reject_missing_or_empty_network_database_password +torrust-tracker-configuration v3_0_0::database::tests::it_should_reject_network_only_and_unknown_fields_for_sqlite +torrust-tracker-configuration v3_0_0::database::tests::sqlite_database_path_should_be_publicly_constructible_and_readable +torrust-tracker-configuration v3_0_0::http_tracker::tests::it_should_accept_public_url_when_scheme_is_http +torrust-tracker-configuration v3_0_0::http_tracker::tests::it_should_accept_public_url_when_scheme_is_https +torrust-tracker-configuration v3_0_0::http_tracker::tests::it_should_default_public_url_to_none +torrust-tracker-configuration v3_0_0::http_tracker::tests::it_should_default_use_ip_from_query_string_to_false +torrust-tracker-configuration v3_0_0::http_tracker::tests::it_should_deserialize_use_ip_from_query_string +torrust-tracker-configuration v3_0_0::http_tracker::tests::it_should_reject_public_url_when_scheme_is_udp +torrust-tracker-configuration v3_0_0::http_tracker::tests::it_should_reject_public_url_when_url_is_malformed +torrust-tracker-configuration v3_0_0::http_tracker::tests::tls_config_should_deserialize_from_corrected_key +torrust-tracker-configuration v3_0_0::logging::tests::it_should_deserialize_all_supported_trace_styles +torrust-tracker-configuration v3_0_0::logging::tests::it_should_map_the_trace_filter_to_the_corresponding_tracing_level +torrust-tracker-configuration v3_0_0::logging::tests::it_should_reject_an_unsupported_trace_style +torrust-tracker-configuration v3_0_0::logging::tests::it_should_reject_the_removed_threshold_field +torrust-tracker-configuration v3_0_0::logging::tests::it_should_use_info_and_full_as_the_default_logging_configuration +torrust-tracker-configuration v3_0_0::public_url::tests::it_should_accept_http_url_when_scheme_is_http +torrust-tracker-configuration v3_0_0::public_url::tests::it_should_accept_http_url_when_scheme_is_https +torrust-tracker-configuration v3_0_0::public_url::tests::it_should_accept_udp_url_when_scheme_is_udp +torrust-tracker-configuration v3_0_0::public_url::tests::it_should_default_to_none_when_http_url_field_is_absent +torrust-tracker-configuration v3_0_0::public_url::tests::it_should_default_to_none_when_udp_url_field_is_absent +torrust-tracker-configuration v3_0_0::public_url::tests::it_should_reject_http_url_when_scheme_is_udp +torrust-tracker-configuration v3_0_0::public_url::tests::it_should_reject_http_url_when_value_is_not_a_valid_url +torrust-tracker-configuration v3_0_0::public_url::tests::it_should_reject_udp_url_when_scheme_is_http +torrust-tracker-configuration v3_0_0::public_url::tests::it_should_reject_udp_url_when_value_is_not_a_valid_url +torrust-tracker-configuration v3_0_0::public_url::tests::it_should_round_trip_http_url_through_toml_serialization +torrust-tracker-configuration v3_0_0::public_url::tests::it_should_round_trip_udp_url_through_toml_serialization +torrust-tracker-configuration v3_0_0::tests::configuration_json_output_should_redact_access_tokens +torrust-tracker-configuration v3_0_0::tests::configuration_should_allow_to_overwrite_the_default_tracker_api_token_for_admin_with_an_env_var +torrust-tracker-configuration v3_0_0::tests::configuration_should_apply_one_global_connection_id_error_limit_to_multiple_udp_trackers +torrust-tracker-configuration v3_0_0::tests::configuration_should_be_saved_in_a_toml_config_file +torrust-tracker-configuration v3_0_0::tests::configuration_should_deserialize_a_custom_ip_bans_reset_interval +torrust-tracker-configuration v3_0_0::tests::configuration_should_have_default_values +torrust-tracker-configuration v3_0_0::tests::configuration_should_reject_a_listener_scoped_connection_id_error_limit +torrust-tracker-configuration v3_0_0::tests::configuration_should_use_the_default_values_when_only_the_mandatory_options_are_provided_by_the_user_via_toml_content +torrust-tracker-configuration v3_0_0::tests::configuration_should_use_the_default_values_when_only_the_mandatory_options_are_provided_by_the_user_via_toml_file +torrust-tracker-configuration v3_0_0::tests::default_configuration_could_be_overwritten_from_a_single_env_var_with_toml_contents +torrust-tracker-configuration v3_0_0::tests::default_configuration_could_be_overwritten_from_a_toml_config_file +torrust-tracker-configuration v3_0_0::tests::deserialization::it_should_deserialize_network_settings_from_a_http_tracker_network_block +torrust-tracker-configuration v3_0_0::tests::deserialization::it_should_deserialize_network_settings_from_a_udp_tracker_network_block +torrust-tracker-configuration v3_0_0::tests::deserialization::it_should_reject_the_removed_core_network_layout +torrust-tracker-configuration v3_0_0::tests::deserialization::it_should_reject_the_removed_flat_tracker_ipv6_v6only_field +torrust-tracker-configuration v3_0_0::tests::deserialization::it_should_reject_the_removed_flat_udp_tracker_ipv6_v6only_field +torrust-tracker-configuration v3_0_0::tests::deserialization::it_should_use_safe_network_defaults_when_the_network_block_is_omitted +torrust-tracker-configuration v3_0_0::tests::external_ip_should_accept_valid_ipv4_address +torrust-tracker-configuration v3_0_0::tests::external_ip_should_parse_from_str +torrust-tracker-configuration v3_0_0::tests::external_ip_should_reject_unspecified_ipv4_address +torrust-tracker-configuration v3_0_0::tests::external_ip_should_reject_unspecified_ipv6_address +torrust-tracker-configuration v3_0_0::tests::it_should_deserialize_an_omitted_database_as_none +torrust-tracker-configuration v3_0_0::tests::it_should_persist_an_absent_database_without_a_database_table +torrust-tracker-configuration v3_0_0::tests::network_database_configuration_should_not_merge_the_sqlite_default_path +torrust-tracker-configuration v3_0_0::tests::persisted_configuration_toml_should_include_access_tokens +torrust-tracker-configuration v3_0_0::tests::persisted_configuration_toml_should_include_database_password +torrust-tracker-configuration v3_0_0::tests::persisted_configuration_toml_should_round_trip_network_database_passwords +torrust-tracker-configuration v3_0_0::tests::smoke::v3_configuration_should_load_when_schema_version_is_3_0_0 +torrust-tracker-configuration v3_0_0::tests::smoke::v3_configuration_should_reject_schema_version_2_0_0 +torrust-tracker-configuration v3_0_0::tests::tracker_defaults_should_not_contain_an_external_ip +torrust-tracker-configuration v3_0_0::tracker_api::tests::default_http_api_configuration_should_not_contains_any_token +torrust-tracker-configuration v3_0_0::tracker_api::tests::http_api_configuration_should_allow_adding_tokens +torrust-tracker-configuration v3_0_0::tracker_api::tests::http_api_tokens_should_deserialize_from_toml_and_serialize_to_redacted_json +torrust-tracker-configuration v3_0_0::tracker_api::tests::it_should_accept_public_url_when_scheme_is_https +torrust-tracker-configuration v3_0_0::tracker_api::tests::it_should_default_public_url_to_none +torrust-tracker-configuration v3_0_0::tracker_api::tests::it_should_reject_public_url_when_scheme_is_udp +torrust-tracker-configuration v3_0_0::tracker_api::tests::tls_config_should_deserialize_from_corrected_key +torrust-tracker-configuration v3_0_0::types::tests::it_should_accept_a_value_at_the_minimum +torrust-tracker-configuration v3_0_0::types::tests::it_should_reject_an_invalid_value_during_deserialization +torrust-tracker-configuration v3_0_0::types::tests::it_should_reject_a_value_below_the_minimum +torrust-tracker-configuration v3_0_0::udp_tracker_server::tests::it_should_accept_the_minimum_reset_interval +torrust-tracker-configuration v3_0_0::udp_tracker_server::tests::it_should_default_connection_id_validation_to_strict +torrust-tracker-configuration v3_0_0::udp_tracker_server::tests::it_should_default_max_connection_id_errors_per_ip_to_ten +torrust-tracker-configuration v3_0_0::udp_tracker_server::tests::it_should_default_to_a_24_hour_reset_interval +torrust-tracker-configuration v3_0_0::udp_tracker_server::tests::it_should_deserialize_and_serialize_max_connection_id_errors_per_ip +torrust-tracker-configuration v3_0_0::udp_tracker_server::tests::it_should_deserialize_disabled_connection_id_validation +torrust-tracker-configuration v3_0_0::udp_tracker_server::tests::it_should_deserialize_strict_connection_id_validation +torrust-tracker-configuration v3_0_0::udp_tracker_server::tests::it_should_reject_a_reset_interval_below_the_minimum +torrust-tracker-configuration v3_0_0::udp_tracker_server::tests::it_should_round_trip_disabled_connection_id_validation +torrust-tracker-configuration v3_0_0::udp_tracker_server::tests::it_should_round_trip_strict_connection_id_validation +torrust-tracker-configuration v3_0_0::udp_tracker_server::tests::it_should_use_strict_when_connection_id_validation_field_is_omitted +torrust-tracker-configuration v3_0_0::udp_tracker_server::tests::it_should_use_the_default_max_connection_id_errors_per_ip_when_omitted +torrust-tracker-configuration v3_0_0::udp_tracker::tests::it_should_accept_public_url_when_scheme_is_udp +torrust-tracker-configuration v3_0_0::udp_tracker::tests::it_should_default_public_url_to_none +torrust-tracker-configuration v3_0_0::udp_tracker::tests::it_should_reject_public_url_when_scheme_is_https +torrust-tracker-configuration v3_0_0::udp_tracker::tests::it_should_reject_public_url_when_url_is_malformed +torrust-tracker console::ci::e2e::logs_parser::tests::it_should_ignore_logs_with_no_matching_lines +torrust-tracker console::ci::e2e::logs_parser::tests::it_should_parse_from_logs_with_valid_logs +torrust-tracker console::ci::e2e::logs_parser::tests::it_should_parse_multiple_services +torrust-tracker console::ci::e2e::logs_parser::tests::it_should_replace_wildcard_ip_with_localhost +torrust-tracker console::ci::e2e::logs_parser::tests::it_should_support_colored_output +torrust-tracker console::ci::qbittorrent_e2e::bencode::tests::it_should_embed_raw_bytes_verbatim +torrust-tracker console::ci::qbittorrent_e2e::bencode::tests::it_should_embed_raw_inner_dict_inside_outer_dict +torrust-tracker console::ci::qbittorrent_e2e::bencode::tests::it_should_encode_a_byte_string +torrust-tracker console::ci::qbittorrent_e2e::bencode::tests::it_should_encode_a_dictionary_with_keys_sorted_lexicographically +torrust-tracker console::ci::qbittorrent_e2e::bencode::tests::it_should_encode_a_negative_integer +torrust-tracker console::ci::qbittorrent_e2e::bencode::tests::it_should_encode_an_empty_byte_string +torrust-tracker console::ci::qbittorrent_e2e::bencode::tests::it_should_encode_an_empty_dictionary +torrust-tracker console::ci::qbittorrent_e2e::bencode::tests::it_should_encode_a_positive_integer +torrust-tracker console::ci::qbittorrent_e2e::bencode::tests::it_should_encode_zero +torrust-tracker console::ci::qbittorrent_e2e::filesystem_setup::tests::it_should_create_the_sqlite_database_parent_directory +torrust-tracker console::ci::qbittorrent_e2e::filesystem_setup::tests::it_should_not_create_a_sqlite_database_directory_for_network_drivers +torrust-tracker console::ci::qbittorrent_e2e::qbittorrent::client::tests::it_should_extract_sid_cookie_when_present +torrust-tracker console::ci::qbittorrent_e2e::qbittorrent::client::tests::it_should_return_none_when_sid_cookie_is_missing +torrust-tracker console::ci::qbittorrent_e2e::qbittorrent::torrent::tests::it_should_deserialize_torrent_state_known_variant +torrust-tracker console::ci::qbittorrent_e2e::qbittorrent::torrent::tests::it_should_deserialize_unknown_torrent_state_preserving_raw_value +torrust-tracker console::ci::qbittorrent_e2e::qbittorrent::torrent::tests::it_should_display_known_and_unknown_torrent_state_values +torrust-tracker console::ci::qbittorrent_e2e::qbittorrent::torrent::tests::it_should_report_torrent_progress_completion_threshold +torrust-tracker console::ci::qbittorrent_e2e::torrent_artifacts::tests::it_should_build_payload_bytes_with_a_repeating_pattern +torrust-tracker console::ci::qbittorrent_e2e::torrent_artifacts::tests::it_should_build_payload_bytes_with_the_right_length +torrust-tracker console::ci::qbittorrent_e2e::torrent_artifacts::tests::it_should_build_payload_bytes_wrapping_around_the_pattern +torrust-tracker console::ci::qbittorrent_e2e::torrent_artifacts::tests::it_should_build_torrent_bytes_as_a_valid_bencode_dictionary +torrust-tracker console::ci::qbittorrent_e2e::torrent_artifacts::tests::it_should_embed_the_announce_url_verbatim_in_the_torrent_bytes +torrust-tracker console::ci::qbittorrent_e2e::torrent_artifacts::tests::it_should_embed_the_info_dict_raw_so_it_appears_as_a_nested_bencode_dict +torrust-tracker console::ci::qbittorrent_e2e::torrent_artifacts::tests::it_should_produce_a_40_character_lowercase_hex_info_hash +torrust-tracker console::ci::qbittorrent_e2e::torrent_artifacts::tests::it_should_produce_a_different_info_hash_when_only_the_payload_changes +torrust-tracker console::ci::qbittorrent_e2e::torrent_artifacts::tests::it_should_produce_deterministic_torrent_bytes_for_identical_inputs +torrust-tracker console::ci::qbittorrent_e2e::torrent_artifacts::tests::it_should_produce_different_torrent_bytes_for_different_payloads +torrust-tracker console::ci::qbittorrent_e2e::torrent_artifacts::tests::it_should_produce_the_same_info_hash_regardless_of_the_announce_url +torrust-tracker console::ci::qbittorrent_e2e::tracker::config_builder::tests::it_should_select_the_configured_database_driver_without_exposing_network_passwords +torrust-tracker console::ci::qbittorrent_e2e::tracker::config_builder::tests::write_to_should_persist_the_tracker_api_access_token +torrust-tracker console::ci::qbittorrent_e2e::types::compose_project_name::tests::it_should_generate_expected_shape +torrust-tracker console::ci::qbittorrent_e2e::types::container_path::tests::it_should_build_from_new_and_format_as_string +torrust-tracker console::ci::qbittorrent_e2e::types::container_path::tests::it_should_convert_from_string_and_str +torrust-tracker console::ci::qbittorrent_e2e::types::deadline::tests::it_should_round_trip_duration +torrust-tracker console::ci::qbittorrent_e2e::types::file_name::tests::it_should_build_from_new_and_format_as_string +torrust-tracker console::ci::qbittorrent_e2e::types::file_name::tests::it_should_convert_from_string_str_and_parse +torrust-tracker console::ci::qbittorrent_e2e::types::file_name::tests::it_should_implement_as_ref_path +torrust-tracker console::ci::qbittorrent_e2e::types::file_name::tests::it_should_reject_backslash +torrust-tracker console::ci::qbittorrent_e2e::types::file_name::tests::it_should_reject_double_dot +torrust-tracker console::ci::qbittorrent_e2e::types::file_name::tests::it_should_reject_forward_slash +torrust-tracker console::ci::qbittorrent_e2e::types::info_hash::tests::it_should_construct_info_hash_and_expose_accessors +torrust-tracker console::ci::qbittorrent_e2e::types::info_hash::tests::it_should_deserialize_info_hash_from_json_string +torrust-tracker console::ci::qbittorrent_e2e::types::payload_size::tests::it_should_round_trip_payload_size +torrust-tracker console::ci::qbittorrent_e2e::types::piece_length::tests::it_should_round_trip_piece_length +torrust-tracker console::ci::qbittorrent_e2e::types::poll_interval::tests::it_should_round_trip_duration +torrust-tracker console::ci::qbittorrent_e2e::types::qbittorrent_image::tests::it_should_round_trip_image_string +torrust-tracker console::ci::qbittorrent_e2e::types::tracker_image::tests::it_should_round_trip_image_string +torrust-tracker console::profiling::tests::it_should_cancel_and_join_jobs_after_the_profiling_duration_elapses +torrust-tracker container::tests::it_should_not_initialize_persistence_when_v3_omits_the_database +torrust-tracker container::tests::it_should_return_a_contextual_composition_error_when_persistent_statistics_lack_persistence +torrust-tracker-core announce_handler::tests::the_announce_handler::for_all_tracker_config_modes::handling_an_announce_request::it_should_allow_peers_to_get_only_a_subset_of_the_peers_in_the_swarm +torrust-tracker-core announce_handler::tests::the_announce_handler::for_all_tracker_config_modes::handling_an_announce_request::it_should_return_the_announce_data_with_an_empty_peer_list_when_it_is_the_first_announced_peer +torrust-tracker-core announce_handler::tests::the_announce_handler::for_all_tracker_config_modes::handling_an_announce_request::it_should_return_the_announce_data_with_the_previously_announced_peers +torrust-tracker-core announce_handler::tests::the_announce_handler::for_all_tracker_config_modes::handling_an_announce_request::it_should_update_the_swarm_stats_for_the_torrent::when_a_previously_announced_started_peer_has_completed_downloading +torrust-tracker-core announce_handler::tests::the_announce_handler::for_all_tracker_config_modes::handling_an_announce_request::it_should_update_the_swarm_stats_for_the_torrent::when_the_peer_is_a_leecher +torrust-tracker-core announce_handler::tests::the_announce_handler::for_all_tracker_config_modes::handling_an_announce_request::it_should_update_the_swarm_stats_for_the_torrent::when_the_peer_is_a_seeder +torrust-tracker-core announce_handler::tests::the_announce_handler::for_all_tracker_config_modes::handling_an_announce_request::should_assign_the_ip_to_the_peer::and_when_client_ip_is_a_ipv6_loopback_ip::it_should_use_the_external_ip_in_the_tracker_configuration_if_it_is_defined_even_if_the_external_ip_is_an_ipv4_ip +torrust-tracker-core announce_handler::tests::the_announce_handler::for_all_tracker_config_modes::handling_an_announce_request::should_assign_the_ip_to_the_peer::and_when_client_ip_is_a_ipv6_loopback_ip::it_should_use_the_external_ip_in_tracker_configuration_if_it_is_defined +torrust-tracker-core announce_handler::tests::the_announce_handler::for_all_tracker_config_modes::handling_an_announce_request::should_assign_the_ip_to_the_peer::and_when_client_ip_is_a_ipv6_loopback_ip::it_should_use_the_loopback_ip_if_the_tracker_does_not_have_the_external_ip_configuration +torrust-tracker-core announce_handler::tests::the_announce_handler::for_all_tracker_config_modes::handling_an_announce_request::should_assign_the_ip_to_the_peer::and_when_the_client_ip_is_a_ipv4_loopback_ip::it_should_use_the_external_ip_in_the_tracker_configuration_if_it_is_defined_even_if_the_external_ip_is_an_ipv6_ip +torrust-tracker-core announce_handler::tests::the_announce_handler::for_all_tracker_config_modes::handling_an_announce_request::should_assign_the_ip_to_the_peer::and_when_the_client_ip_is_a_ipv4_loopback_ip::it_should_use_the_external_tracker_ip_in_tracker_configuration_if_it_is_defined +torrust-tracker-core announce_handler::tests::the_announce_handler::for_all_tracker_config_modes::handling_an_announce_request::should_assign_the_ip_to_the_peer::and_when_the_client_ip_is_a_ipv4_loopback_ip::it_should_use_the_loopback_ip_if_the_tracker_does_not_have_the_external_ip_configuration +torrust-tracker-core announce_handler::tests::the_announce_handler::for_all_tracker_config_modes::handling_an_announce_request::should_assign_the_ip_to_the_peer::and_when_the_external_ip_is_unspecified::it_should_keep_the_ipv4_loopback_ip_when_the_external_ip_is_the_unspecified_ipv4_address +torrust-tracker-core announce_handler::tests::the_announce_handler::for_all_tracker_config_modes::handling_an_announce_request::should_assign_the_ip_to_the_peer::and_when_the_external_ip_is_unspecified::it_should_keep_the_ipv4_loopback_ip_when_the_external_ip_is_the_unspecified_ipv6_address +torrust-tracker-core announce_handler::tests::the_announce_handler::for_all_tracker_config_modes::handling_an_announce_request::should_assign_the_ip_to_the_peer::and_when_the_external_ip_is_unspecified::it_should_keep_the_ipv6_loopback_ip_when_the_external_ip_is_the_unspecified_ipv4_address +torrust-tracker-core announce_handler::tests::the_announce_handler::for_all_tracker_config_modes::handling_an_announce_request::should_assign_the_ip_to_the_peer::and_when_the_external_ip_is_unspecified::it_should_keep_the_ipv6_loopback_ip_when_the_external_ip_is_the_unspecified_ipv6_address +torrust-tracker-core announce_handler::tests::the_announce_handler::for_all_tracker_config_modes::handling_an_announce_request::should_assign_the_ip_to_the_peer::and_when_the_external_ip_is_unspecified::it_should_keep_the_non_loopback_ip_when_the_external_ip_is_unspecified_ipv4 +torrust-tracker-core announce_handler::tests::the_announce_handler::for_all_tracker_config_modes::handling_an_announce_request::should_assign_the_ip_to_the_peer::and_when_the_external_ip_is_unspecified::it_should_keep_the_non_loopback_ip_when_the_external_ip_is_unspecified_ipv6 +torrust-tracker-core announce_handler::tests::the_announce_handler::for_all_tracker_config_modes::handling_an_announce_request::should_assign_the_ip_to_the_peer::using_the_source_ip_instead_of_the_ip_in_the_announce_request +torrust-tracker-core announce_handler::tests::the_announce_handler::should_allow_the_client_peers_to_specified_the_number_of_peers_wanted::it_should_allow_limiting_the_peer_list +torrust-tracker-core announce_handler::tests::the_announce_handler::should_allow_the_client_peers_to_specified_the_number_of_peers_wanted::it_should_cap_only_peers_wanted_at_limit_time +torrust-tracker-core announce_handler::tests::the_announce_handler::should_allow_the_client_peers_to_specified_the_number_of_peers_wanted::it_should_convert_the_peers_wanted_number_from_i32_via_from_client_request +torrust-tracker-core announce_handler::tests::the_announce_handler::should_allow_the_client_peers_to_specified_the_number_of_peers_wanted::it_should_return_74_at_the_most_if_the_client_wants_them_all +torrust-tracker-core announce_handler::tests::the_announce_handler::should_allow_the_client_peers_to_specified_the_number_of_peers_wanted::it_should_return_the_maximin_number_of_peers_by_default +torrust-tracker-core announce_handler::tests::the_announce_handler::should_allow_the_client_peers_to_specified_the_number_of_peers_wanted::it_should_return_the_maximum_when_wanting_more_than_the_maximum +torrust-tracker-core announce_handler::tests::the_announce_handler::should_allow_the_client_peers_to_specified_the_number_of_peers_wanted::it_should_return_zero_when_wanting_only_zero +torrust-tracker-core authentication::handler::tests::the_keys_handler_when_the_tracker_is_configured_as_private::handling_expiring_peer_keys::it_should_generate_the_key +torrust-tracker-core authentication::handler::tests::the_keys_handler_when_the_tracker_is_configured_as_private::handling_expiring_peer_keys::pre_generated::it_should_add_a_pre_generated_key +torrust-tracker-core authentication::handler::tests::the_keys_handler_when_the_tracker_is_configured_as_private::handling_expiring_peer_keys::pre_generated::it_should_fail_adding_a_pre_generated_key_when_the_key_duration_exceeds_the_maximum_duration +torrust-tracker-core authentication::handler::tests::the_keys_handler_when_the_tracker_is_configured_as_private::handling_expiring_peer_keys::pre_generated::it_should_fail_adding_a_pre_generated_key_when_the_key_is_invalid +torrust-tracker-core authentication::handler::tests::the_keys_handler_when_the_tracker_is_configured_as_private::handling_expiring_peer_keys::pre_generated::it_should_fail_adding_a_pre_generated_key_when_there_is_a_database_error +torrust-tracker-core authentication::handler::tests::the_keys_handler_when_the_tracker_is_configured_as_private::handling_expiring_peer_keys::randomly_generated::it_should_add_a_randomly_generated_key +torrust-tracker-core authentication::handler::tests::the_keys_handler_when_the_tracker_is_configured_as_private::handling_expiring_peer_keys::randomly_generated::it_should_fail_adding_a_randomly_generated_key_when_there_is_a_database_error +torrust-tracker-core authentication::handler::tests::the_keys_handler_when_the_tracker_is_configured_as_private::handling_permanent_peer_keys::pre_generated_keys::it_should_add_a_pre_generated_key +torrust-tracker-core authentication::handler::tests::the_keys_handler_when_the_tracker_is_configured_as_private::handling_permanent_peer_keys::pre_generated_keys::it_should_fail_adding_a_pre_generated_key_when_the_key_is_invalid +torrust-tracker-core authentication::handler::tests::the_keys_handler_when_the_tracker_is_configured_as_private::handling_permanent_peer_keys::pre_generated_keys::it_should_fail_adding_a_pre_generated_key_when_there_is_a_database_error +torrust-tracker-core authentication::handler::tests::the_keys_handler_when_the_tracker_is_configured_as_private::handling_permanent_peer_keys::randomly_generated::it_should_add_a_randomly_generated_key +torrust-tracker-core authentication::handler::tests::the_keys_handler_when_the_tracker_is_configured_as_private::handling_permanent_peer_keys::randomly_generated::it_should_fail_adding_a_randomly_generated_key_when_there_is_a_database_error +torrust-tracker-core authentication::handler::tests::the_keys_handler_when_the_tracker_is_configured_as_private::handling_permanent_peer_keys::randomly_generated::it_should_generate_the_key +torrust-tracker-core authentication::key::peer_key::tests::key::length_should_be_32 +torrust-tracker-core authentication::key::peer_key::tests::key::should_be_generated_randomly +torrust-tracker-core authentication::key::peer_key::tests::key::should_be_parsed_from_an_string +torrust-tracker-core authentication::key::peer_key::tests::key::should_only_include_alphanumeric_chars +torrust-tracker-core authentication::key::peer_key::tests::key::should_return_a_reference_to_the_inner_string +torrust-tracker-core authentication::key::peer_key::tests::peer_key::could_be_permanent +torrust-tracker-core authentication::key::peer_key::tests::peer_key::could_have_an_expiration_time +torrust-tracker-core authentication::key::peer_key::tests::peer_key::expiring::should_be_displayed_when_it_is_expiring +torrust-tracker-core authentication::key::peer_key::tests::peer_key::permanent::should_be_displayed_when_it_is_permanent +torrust-tracker-core authentication::key::repository::in_memory::tests::the_in_memory_key_repository_should::clear_all_peer_keys +torrust-tracker-core authentication::key::repository::in_memory::tests::the_in_memory_key_repository_should::get_a_new_peer_key_by_its_internal_key +torrust-tracker-core authentication::key::repository::in_memory::tests::the_in_memory_key_repository_should::insert_a_new_peer_key +torrust-tracker-core authentication::key::repository::in_memory::tests::the_in_memory_key_repository_should::remove_a_new_peer_key +torrust-tracker-core authentication::key::repository::in_memory::tests::the_in_memory_key_repository_should::reset_the_peer_keys_with_a_new_list_of_keys +torrust-tracker-core authentication::key::repository::persisted::tests::the_persisted_key_repository_should::load_all_persisted_peer_keys +torrust-tracker-core authentication::key::repository::persisted::tests::the_persisted_key_repository_should::persist_a_new_peer_key +torrust-tracker-core authentication::key::repository::persisted::tests::the_persisted_key_repository_should::remove_a_persisted_peer_key +torrust-tracker-core authentication::key::tests::the_expiring_peer_key::expiration_verification_should_fail_when_the_key_has_expired +torrust-tracker-core authentication::key::tests::the_expiring_peer_key::should_be_displayed +torrust-tracker-core authentication::key::tests::the_expiring_peer_key::should_be_generated_with_a_expiration_time +torrust-tracker-core authentication::key::tests::the_key_verification_error::could_be_a_database_error +torrust-tracker-core authentication::key::tests::the_permanent_peer_key::expiration_verification_should_always_succeed +torrust-tracker-core authentication::key::tests::the_permanent_peer_key::should_be_displayed +torrust-tracker-core authentication::key::tests::the_permanent_peer_key::should_be_generated_without_expiration_time +torrust-tracker-core authentication::service::tests::the_authentication_service::when_the_tracker_is_private::but_the_key_expiration_check_is_disabled_by_configuration::it_should_authenticate_an_expired_registered_key +torrust-tracker-core authentication::service::tests::the_authentication_service::when_the_tracker_is_private::it_should_authenticate_a_registered_key +torrust-tracker-core authentication::service::tests::the_authentication_service::when_the_tracker_is_private::it_should_not_authenticate_an_unregistered_key +torrust-tracker-core authentication::service::tests::the_authentication_service::when_the_tracker_is_private::it_should_not_authenticate_a_registered_but_expired_key_by_default +torrust-tracker-core authentication::service::tests::the_authentication_service::when_the_tracker_is_private::it_should_not_authenticate_a_registered_but_expired_key_when_the_tracker_is_explicitly_configured_to_check_keys_expiration +torrust-tracker-core authentication::service::tests::the_authentication_service::when_the_tracker_is_public::it_should_always_authenticate_when_the_tracker_is_public +torrust-tracker-core authentication::tests::the_tracker_configured_as_private::it_should_load_authentication_keys_from_the_database +torrust-tracker-core authentication::tests::the_tracker_configured_as_private::it_should_remove_an_authentication_key +torrust-tracker-core authentication::tests::the_tracker_configured_as_private::with_expiring_and::pre_generated_keys::it_should_accept_an_expired_key_when_checking_expiration_is_disabled_in_configuration +torrust-tracker-core authentication::tests::the_tracker_configured_as_private::with_expiring_and::pre_generated_keys::it_should_authenticate_a_peer_with_the_key +torrust-tracker-core authentication::tests::the_tracker_configured_as_private::with_expiring_and::randomly_generated_keys::it_should_accept_an_expired_key_when_checking_expiration_is_disabled_in_configuration +torrust-tracker-core authentication::tests::the_tracker_configured_as_private::with_expiring_and::randomly_generated_keys::it_should_authenticate_a_peer_with_the_key +torrust-tracker-core authentication::tests::the_tracker_configured_as_private::with_permanent_and::pre_generated_keys::it_should_authenticate_a_peer_with_the_key +torrust-tracker-core authentication::tests::the_tracker_configured_as_private::with_permanent_and::randomly_generated_keys::it_should_authenticate_a_peer_with_the_key +torrust-tracker-core container::tests::it_should_construct_a_tracker_core_container_without_persistence +torrust-tracker-core container::tests::it_should_construct_a_tracker_core_container_with_supplied_persistence +torrust-tracker-core container::tests::it_should_load_persistent_completed_statistics_when_a_torrent_is_first_announced +torrust-tracker-core container::tests::it_should_return_a_typed_error_when_persistent_statistics_lack_persistence +torrust-tracker-core databases::driver::mysql::tests::run_mysql_driver_tests +torrust-tracker-core databases::driver::postgres::tests::run_postgres_driver_tests +torrust-tracker-core databases::driver::sqlite::schema_migrator::tests::bootstrap_legacy_schema_should_be_a_noop_on_a_fresh_database +torrust-tracker-core databases::driver::sqlite::schema_migrator::tests::bootstrap_legacy_schema_should_reject_partial_legacy_state +torrust-tracker-core databases::driver::sqlite::schema_migrator::tests::bootstrap_legacy_schema_should_seed_history_when_all_legacy_tables_exist +torrust-tracker-core databases::driver::sqlite::tests::create_database_tables_should_be_idempotent_on_a_fresh_database +torrust-tracker-core databases::driver::sqlite::tests::run_sqlite_driver_tests +torrust-tracker-core databases::error::tests::it_should_build_a_database_error_from_a_sqlx_io_error +torrust-tracker-core databases::error::tests::it_should_build_a_database_error_from_a_sqlx_row_not_found_error +torrust-tracker-core databases::setup::tests::it_should_initialize_the_sqlite_database +torrust-tracker-core error::tests::peer_key_error::duration_overflow +torrust-tracker-core error::tests::peer_key_error::parsing_from_string +torrust-tracker-core error::tests::peer_key_error::persisting_into_database +torrust-tracker-core error::tests::whitelist_error::torrent_not_whitelisted +torrust-tracker-core::integration it_should_cancel_every_listener_when_the_environment_is_dropped_without_being_stopped +torrust-tracker-core::integration it_should_handle_the_announce_request +torrust-tracker-core::integration it_should_handle_the_scrape_request +torrust-tracker-core::integration it_should_not_load_a_scraped_torrent_into_memory +torrust-tracker-core::integration it_should_not_return_the_peer_making_the_announce_request +torrust-tracker-core::integration it_should_not_scrape_persisted_downloads_when_the_persistent_completed_stat_is_disabled +torrust-tracker-core::integration it_should_persist_every_completed_download_queued_before_the_persistent_listener_is_cancelled +torrust-tracker-core::integration it_should_persist_the_global_number_of_completed_peers_into_the_database +torrust-tracker-core::integration it_should_persist_the_number_of_completed_peers_for_each_torrent_into_the_database +torrust-tracker-core::integration it_should_release_every_listener_receiver_when_the_environment_is_stopped +torrust-tracker-core::integration it_should_reset_in_session_completed_downloads_after_a_persistence_free_restart +torrust-tracker-core::integration it_should_scrape_the_persisted_downloads_of_a_torrent_absent_from_memory +torrust-tracker-core peer_tests::it_should_be_serializable +torrust-tracker-core scrape_handler::tests::it_should_allow_scraping_for_multiple_torrents +torrust-tracker-core scrape_handler::tests::it_should_fail_when_loading_the_persisted_downloads_fails +torrust-tracker-core scrape_handler::tests::it_should_load_the_persisted_downloads_of_all_torrents_absent_from_memory_in_one_query +torrust-tracker-core scrape_handler::tests::it_should_look_up_a_repeated_info_hash_absent_from_memory_only_once +torrust-tracker-core scrape_handler::tests::it_should_report_an_in_memory_swarm_without_reading_the_persisted_downloads +torrust-tracker-core scrape_handler::tests::it_should_report_every_requested_torrent_without_a_limit_on_the_number_of_info_hashes +torrust-tracker-core scrape_handler::tests::it_should_return_a_zeroed_swarm_metadata_for_the_requested_file_if_the_tracker_does_not_have_that_torrent +torrust-tracker-core statistics::event::handler::tests::it_should_increment_in_memory_completion_metrics_when_a_peer_download_is_completed +torrust-tracker-core statistics::event::handler::tests::it_should_not_increment_in_memory_completion_metrics_when_an_event_is_not_a_completion +torrust-tracker-core statistics::event::handler::tests::it_should_not_persist_completion_statistics_when_an_event_is_not_a_completion +torrust-tracker-core statistics::event::handler::tests::it_should_persist_completion_statistics_when_a_peer_download_is_completed +torrust-tracker-core statistics::event::handler::tests::it_should_persist_global_completion_statistics_when_torrent_persistence_fails +torrust-tracker-core statistics::event::listener::tests::it_should_continue_after_lag_then_handle_an_event_then_stop_when_the_in_memory_receiver_is_closed +torrust-tracker-core statistics::event::listener::tests::it_should_continue_after_lag_then_handle_an_event_then_stop_when_the_persistent_receiver_is_closed +torrust-tracker-core statistics::event::listener::tests::it_should_handle_an_event_then_stop_when_the_in_memory_receiver_is_closed +torrust-tracker-core statistics::event::listener::tests::it_should_handle_an_event_then_stop_when_the_persistent_receiver_is_closed +torrust-tracker-core statistics::event::listener::tests::it_should_handle_a_queued_event_then_return_cancelled_when_cancellation_is_already_pending +torrust-tracker-core statistics::event::listener::tests::it_should_leave_the_queued_events_unprocessed_and_return_cancelled_when_the_drain_timeout_has_expired +torrust-tracker-core statistics::event::listener::tests::it_should_leave_the_queued_event_unprocessed_and_return_cancelled_when_the_drain_timeout_has_expired +torrust-tracker-core statistics::event::listener::tests::it_should_persist_a_queued_completed_download_when_cancellation_is_already_pending +torrust-tracker-core statistics::event::listener::tests::it_should_persist_every_queued_event_then_return_cancelled_when_cancellation_is_already_pending +torrust-tracker-core statistics::event::listener::tests::it_should_stop_when_the_in_memory_receiver_is_closed +torrust-tracker-core statistics::event::listener::tests::it_should_stop_when_the_persistent_receiver_is_closed +torrust-tracker-core statistics::persisted::downloads::tests::it_increases_the_numbers_of_downloads_for_a_torrent_into_the_database +torrust-tracker-core statistics::persisted::downloads::tests::it_loads_the_numbers_of_downloads_for_all_torrents_from_the_database +torrust-tracker-core statistics::persisted::downloads::tests::it_saves_the_numbers_of_downloads_for_a_torrent_into_the_database +torrust-tracker-core statistics::repository::tests::it_should_export_persisted_metric_with_zero_value_when_persisted_completed_statistics_are_enabled +torrust-tracker-core statistics::repository::tests::it_should_omit_persisted_metric_when_persisted_completed_statistics_are_disabled +torrust-tracker-core tests::the_tracker::configured_as_whitelisted::handling_a_scrape_request::it_should_return_the_zeroed_swarm_metadata_for_the_requested_file_if_it_is_not_whitelisted +torrust-tracker-core tests::the_tracker::for_all_config_modes::handling_a_scrape_request::it_should_return_the_swarm_metadata_for_the_requested_file_if_the_tracker_has_that_torrent +torrust-tracker-core torrent::manager::tests::cleaning_torrents::it_should_remove_peers_that_have_not_been_updated_after_a_cutoff_time +torrust-tracker-core torrent::manager::tests::cleaning_torrents::it_should_remove_torrents_that_have_no_peers_when_it_is_configured_to_do_so +torrust-tracker-core torrent::manager::tests::cleaning_torrents::it_should_retain_peerless_torrents_when_it_is_configured_to_do_so +torrust-tracker-core torrent::manager::tests::it_should_load_the_numbers_of_downloads_for_all_torrents_from_the_database +torrust-tracker-core torrent::services::tests::getting_a_torrent_info::it_should_return_none_if_the_tracker_does_not_have_the_torrent +torrust-tracker-core torrent::services::tests::getting_a_torrent_info::it_should_return_the_torrent_info_if_the_tracker_has_the_torrent +torrust-tracker-core torrent::services::tests::getting_basic_torrent_info_for_multiple_torrents_at_once::it_should_return_a_list_with_basic_info_about_the_requested_torrents +torrust-tracker-core torrent::services::tests::getting_basic_torrent_info_for_multiple_torrents_at_once::it_should_return_an_empty_list_if_none_of_the_requested_torrents_is_found +torrust-tracker-core torrent::services::tests::searching_for_torrents::it_should_allow_limiting_the_number_of_torrents_in_the_result +torrust-tracker-core torrent::services::tests::searching_for_torrents::it_should_allow_using_pagination_in_the_result +torrust-tracker-core torrent::services::tests::searching_for_torrents::it_should_return_an_empty_result_if_the_tracker_does_not_have_any_torrent +torrust-tracker-core torrent::services::tests::searching_for_torrents::it_should_return_a_summarized_info_for_all_torrents +torrust-tracker-core torrent::services::tests::searching_for_torrents::it_should_return_torrents_ordered_by_info_hash +torrust-tracker-core whitelist::authorization::tests::the_whitelist_authorization_for_announce_and_scrape_actions::when_the_tacker_is_configured_as_listed::should_authorize_a_whitelisted_infohash +torrust-tracker-core whitelist::authorization::tests::the_whitelist_authorization_for_announce_and_scrape_actions::when_the_tacker_is_configured_as_listed::should_not_authorize_a_non_whitelisted_infohash +torrust-tracker-core whitelist::authorization::tests::the_whitelist_authorization_for_announce_and_scrape_actions::when_the_tacker_is_not_configured_as_listed::should_also_authorize_a_non_whitelisted_infohash +torrust-tracker-core whitelist::authorization::tests::the_whitelist_authorization_for_announce_and_scrape_actions::when_the_tacker_is_not_configured_as_listed::should_authorize_a_whitelisted_infohash +torrust-tracker-core whitelist::manager::tests::configured_as_whitelisted::handling_the_torrent_whitelist::it_should_add_a_torrent_to_the_whitelist +torrust-tracker-core whitelist::manager::tests::configured_as_whitelisted::handling_the_torrent_whitelist::it_should_remove_a_torrent_from_the_whitelist +torrust-tracker-core whitelist::manager::tests::configured_as_whitelisted::handling_the_torrent_whitelist::persistence::it_should_load_the_whitelist_from_the_database +torrust-tracker-core whitelist::repository::in_memory::tests::should_allow_adding_a_new_torrent_to_the_whitelist +torrust-tracker-core whitelist::repository::in_memory::tests::should_allow_checking_if_an_infohash_is_whitelisted +torrust-tracker-core whitelist::repository::in_memory::tests::should_allow_clearing_the_whitelist +torrust-tracker-core whitelist::repository::in_memory::tests::should_allow_removing_a_new_torrent_to_the_whitelist +torrust-tracker-core whitelist::repository::persisted::tests::the_persisted_whitelist_repository::should_add_a_new_infohash_to_the_list +torrust-tracker-core whitelist::repository::persisted::tests::the_persisted_whitelist_repository::should_load_all_infohashes_from_the_database +torrust-tracker-core whitelist::repository::persisted::tests::the_persisted_whitelist_repository::should_not_add_the_same_infohash_to_the_list_twice +torrust-tracker-core whitelist::repository::persisted::tests::the_persisted_whitelist_repository::should_not_fail_removing_an_infohash_that_is_not_in_the_list +torrust-tracker-core whitelist::repository::persisted::tests::the_persisted_whitelist_repository::should_remove_a_infohash_from_the_list +torrust-tracker-core whitelist::tests::configured_as_whitelisted::handling_authorization::it_should_authorize_the_announce_and_scrape_actions_on_whitelisted_torrents +torrust-tracker-core whitelist::tests::configured_as_whitelisted::handling_authorization::it_should_not_authorize_the_announce_and_scrape_actions_on_not_whitelisted_torrents +torrust-tracker-events broadcaster::tests::it_should_allow_sending_an_event_and_received_it +torrust-tracker-events broadcaster::tests::it_should_allow_subscribing_multiple_receivers +torrust-tracker-events broadcaster::tests::it_should_count_sent_positions_not_yet_received_including_overwritten_ones +torrust-tracker-events broadcaster::tests::it_should_fail_when_trying_tos_send_with_no_subscribers +torrust-tracker-events broadcaster::tests::it_should_report_a_receiver_as_empty_only_when_it_has_no_unread_events +torrust-tracker-events broadcaster::tests::it_should_report_closed_only_after_the_buffered_events_were_read +torrust-tracker-events broadcaster::tests::it_should_report_empty_without_waiting_when_the_receiver_has_read_every_event +torrust-tracker-events broadcaster::tests::it_should_report_the_number_of_skipped_events_then_continue_with_the_oldest_retained_one +torrust-tracker-events broadcaster::tests::it_should_return_the_number_of_receivers_when_and_event_is_sent +torrust-tracker-events bus::tests::it_should_allow_sending_events_that_are_received_by_receivers +torrust-tracker-events bus::tests::it_should_enabled_by_default +torrust-tracker-events bus::tests::it_should_not_provide_an_event_sender_when_disabled +torrust-tracker-events bus::tests::it_should_provide_an_event_sender_when_enabled +torrust-tracker-events bus::tests::it_should_send_a_closed_events_to_receivers_when_sender_is_dropped +torrust-tracker-events drain::tests::it_should_count_lagged_events_as_lost_and_keep_draining +torrust-tracker-events drain::tests::it_should_drain_every_event_when_the_timeout_is_too_large_to_add_to_the_clock +torrust-tracker-events drain::tests::it_should_finish_a_slow_handler_that_crosses_the_timeout_before_stopping +torrust-tracker-events drain::tests::it_should_handle_every_buffered_event_then_report_closed_when_the_senders_are_gone +torrust-tracker-events drain::tests::it_should_include_events_overwritten_before_the_first_lag_result_in_the_unread_count_at_timeout +torrust-tracker-events drain::tests::it_should_let_other_tasks_run_while_it_drains_a_ready_queue +torrust-tracker-events drain::tests::it_should_report_an_emptied_receiver_and_not_a_timeout_when_nothing_is_left_unread_at_the_deadline +torrust-tracker-events drain::tests::it_should_report_an_emptied_receiver_when_there_is_nothing_to_drain +torrust-tracker-events drain::tests::it_should_stop_admitting_events_and_report_how_many_are_left_when_the_timeout_expires +torrust-tracker-http-core container::tests::the_container_composed_from_services::it_should_accept_an_announce_of_a_torrent_added_to_the_whitelist_of_the_tracker_core +torrust-tracker-http-core container::tests::the_container_composed_from_services::it_should_authenticate_both_services_with_the_keys_of_the_tracker_core +torrust-tracker-http-core container::tests::the_container_composed_from_services::it_should_expose_the_collaborators_it_was_composed_from +torrust-tracker-http-core container::tests::the_container_composed_from_services::it_should_honor_an_ip_in_the_query_string_when_the_http_tracker_is_configured_to_use_it +torrust-tracker-http-core container::tests::the_container_composed_from_services::it_should_publish_the_announce_event_of_its_instance_with_the_public_url_to_the_services_sender::case_1_usage_statistics_enabled +torrust-tracker-http-core container::tests::the_container_composed_from_services::it_should_publish_the_announce_event_of_its_instance_with_the_public_url_to_the_services_sender::case_2_usage_statistics_disabled +torrust-tracker-http-core container::tests::the_container_composed_from_services::it_should_publish_the_scrape_event_of_its_instance_with_the_public_url_to_the_services_sender::case_1_usage_statistics_enabled +torrust-tracker-http-core container::tests::the_container_composed_from_services::it_should_publish_the_scrape_event_of_its_instance_with_the_public_url_to_the_services_sender::case_2_usage_statistics_disabled +torrust-tracker-http-core container::tests::the_container_composed_from_services::it_should_reject_an_announce_of_a_torrent_missing_from_the_whitelist_of_the_tracker_core +torrust-tracker-http-core container::tests::the_container_composed_from_services::it_should_reject_an_announce_without_a_key_when_the_tracker_core_is_private +torrust-tracker-http-core container::tests::the_container_composed_from_services::it_should_reject_an_ip_in_the_query_string_when_the_http_tracker_is_not_configured_to_use_it +torrust-tracker-http-core container::tests::the_container_composed_from_services::it_should_resolve_the_client_of_an_announce_by_the_reverse_proxy_policy_of_the_http_tracker::case_1_behind_a_reverse_proxy +torrust-tracker-http-core container::tests::the_container_composed_from_services::it_should_resolve_the_client_of_an_announce_by_the_reverse_proxy_policy_of_the_http_tracker::case_2_not_behind_a_reverse_proxy +torrust-tracker-http-core container::tests::the_container_composed_from_services::it_should_resolve_the_client_of_a_scrape_by_the_reverse_proxy_policy_of_the_http_tracker::case_1_behind_a_reverse_proxy +torrust-tracker-http-core container::tests::the_container_composed_from_services::it_should_resolve_the_client_of_a_scrape_by_the_reverse_proxy_policy_of_the_http_tracker::case_2_not_behind_a_reverse_proxy +torrust-tracker-http-core container::tests::the_container_composed_from_services::it_should_scrape_the_swarm_that_the_announce_service_registered_in_the_tracker_core +torrust-tracker-http-core container::tests::the_container_composed_from_services::it_should_zero_a_scrape_without_a_key_when_the_tracker_core_is_private +torrust-tracker-http-core container::tests::the_container_composed_from_tracker_core::it_should_publish_the_events_of_both_services_to_its_own_event_bus::case_1_usage_statistics_enabled +torrust-tracker-http-core container::tests::the_container_composed_from_tracker_core::it_should_publish_the_events_of_both_services_to_its_own_event_bus::case_2_usage_statistics_disabled +torrust-tracker-http-core container::tests::the_container_initialized_from_the_core_configuration::it_should_compose_the_tracker_core_over_the_configured_database_with_the_given_core_configuration +torrust-tracker-http-core container::tests::the_container_initialized_from_the_core_configuration::it_should_panic_when_the_configured_database_cannot_be_initialized +torrust-tracker-http-core container::tests::the_container_initialized_from_the_core_configuration::it_should_publish_the_events_of_its_instance_with_the_public_url_to_its_own_event_bus +torrust-tracker-http-core container::tests::the_http_tracker_core_services::it_should_deliver_the_events_of_the_services_sender_to_the_receivers_of_the_services_event_bus::case_1_usage_statistics_enabled +torrust-tracker-http-core container::tests::the_http_tracker_core_services::it_should_deliver_the_events_of_the_services_sender_to_the_receivers_of_the_services_event_bus::case_2_usage_statistics_disabled +torrust-tracker-http-core container::tests::the_http_tracker_core_services::it_should_not_share_the_event_bus_between_two_sets_of_services +torrust-tracker-http-core container::tests::the_http_tracker_core_services::it_should_not_share_the_statistics_repository_between_two_sets_of_services +torrust-tracker-http-core container::tests::the_http_tracker_core_services::it_should_start_with_a_fresh_statistics_repository +torrust-tracker-http-core event::tests::it_should_consider_a_cloned_event_equal_to_the_original +torrust-tracker-http-core event::tests::it_should_derive_the_client_ip_family_from_the_client_address_not_the_server_binding::case_1_direct_ipv4 +torrust-tracker-http-core event::tests::it_should_derive_the_client_ip_family_from_the_client_address_not_the_server_binding::case_2_direct_ipv6 +torrust-tracker-http-core event::tests::it_should_derive_the_client_ip_family_from_the_client_address_not_the_server_binding::case_3_ipv4_mapped_ipv6 +torrust-tracker-http-core event::tests::it_should_derive_the_client_ip_family_from_the_client_address_not_the_server_binding::case_4_ipv6_loopback +torrust-tracker-http-core event::tests::it_should_derive_the_client_ip_family_from_the_forwarded_address +torrust-tracker-http-core event::tests::it_should_derive_the_client_ip_type_from_the_client_address::case_1_direct_ipv4 +torrust-tracker-http-core event::tests::it_should_derive_the_client_ip_type_from_the_client_address::case_2_native_ipv6 +torrust-tracker-http-core event::tests::it_should_derive_the_client_ip_type_from_the_client_address::case_3_ipv6_loopback +torrust-tracker-http-core event::tests::it_should_derive_the_client_ip_type_from_the_client_address::case_4_ipv4_compatible_ipv6 +torrust-tracker-http-core event::tests::it_should_derive_the_client_ip_type_from_the_client_address::case_5_ipv4_mapped_ipv6 +torrust-tracker-http-core event::tests::it_should_expose_the_client_address_and_port::case_1_from_the_socket_address +torrust-tracker-http-core event::tests::it_should_expose_the_client_address_and_port::case_2_from_x_forwarded_for_without_a_port +torrust-tracker-http-core event::tests::it_should_expose_the_configuration_instance_id_it_was_created_with +torrust-tracker-http-core event::tests::it_should_expose_the_public_url_only_when_it_is_configured::case_1_configured +torrust-tracker-http-core event::tests::it_should_expose_the_public_url_only_when_it_is_configured::case_2_absent +torrust-tracker-http-core event::tests::it_should_expose_the_server_socket_address +torrust-tracker-http-core event::tests::it_should_not_consider_two_connection_contexts_equal_when_any_part_differs::case_1_another_instance +torrust-tracker-http-core event::tests::it_should_not_consider_two_connection_contexts_equal_when_any_part_differs::case_2_another_client_address +torrust-tracker-http-core event::tests::it_should_not_consider_two_connection_contexts_equal_when_any_part_differs::case_3_another_client_port +torrust-tracker-http-core event::tests::it_should_not_consider_two_connection_contexts_equal_when_any_part_differs::case_4_another_way_of_resolving_the_address +torrust-tracker-http-core event::tests::it_should_not_consider_two_connection_contexts_equal_when_any_part_differs::case_5_another_server_address +torrust-tracker-http-core event::tests::it_should_not_consider_two_connection_contexts_equal_when_any_part_differs::case_6_a_public_url +torrust-tracker-http-core event::tests::it_should_project_a_connection_context_to_exactly_these_metric_labels::case_1_ipv4_client_and_server +torrust-tracker-http-core event::tests::it_should_project_a_connection_context_to_exactly_these_metric_labels::case_2_ipv6_client_and_server +torrust-tracker-http-core event::tests::it_should_project_a_connection_context_to_exactly_these_metric_labels::case_3_ipv4_mapped_client_of_an_ipv4_server +torrust-tracker-http-core event::tests::it_should_project_a_connection_context_to_exactly_these_metric_labels::case_4_configured_public_url +torrust-tracker-http-core event::tests::it_should_project_a_connection_context_to_exactly_these_metric_labels::case_5_https_server_on_another_port +torrust-tracker-http-core event::tests::it_should_project_a_connection_context_to_exactly_these_metric_labels::case_6_server_bound_to_an_ipv4_mapped_ipv6_address +torrust-tracker-http-core root_tests::it_should_log_http_tracker_events_under_the_http_tracker_target +torrust-tracker-http-core root_tests::it_should_record_an_event_when_the_send_future_is_polled +torrust-tracker-http-core root_tests::it_should_record_events_in_the_order_they_are_sent +torrust-tracker-http-core root_tests::it_should_record_no_event_before_any_event_is_sent +torrust-tracker-http-core root_tests::it_should_record_nothing_when_the_send_future_is_dropped_without_being_polled +torrust-tracker-http-core root_tests::it_should_sample_a_fixed_info_hash +torrust-tracker-http-core root_tests::it_should_sample_an_ipv4_peer_that_is_the_default_sample_peer +torrust-tracker-http-core root_tests::it_should_sample_an_ipv6_peer_that_differs_from_the_ipv4_sample_only_by_its_address +torrust-tracker-http-core services::announce::tests::it_should_accept_an_announce_with_a_registered_key_on_a_private_tracker +torrust-tracker-http-core services::announce::tests::it_should_announce_without_publishing_when_there_is_no_event_sender +torrust-tracker-http-core services::announce::tests::it_should_authenticate_before_authorizing_the_torrent +torrust-tracker-http-core services::announce::tests::it_should_authenticate_before_resolving_the_client_ip +torrust-tracker-http-core services::announce::tests::it_should_authorize_the_torrent_before_resolving_the_client_ip +torrust-tracker-http-core services::announce::tests::it_should_copy_the_peer_id_address_byte_counters_and_time_into_the_announcement +torrust-tracker-http-core services::announce::tests::it_should_default_absent_byte_counters_to_zero_in_the_announcement +torrust-tracker-http-core services::announce::tests::it_should_ignore_a_presented_key_on_a_public_tracker +torrust-tracker-http-core services::announce::tests::it_should_keep_the_connection_address_of_a_non_loopback_client_when_an_external_ip_is_configured::case_1_ipv4 +torrust-tracker-http-core services::announce::tests::it_should_keep_the_connection_address_of_a_non_loopback_client_when_an_external_ip_is_configured::case_2_ipv6 +torrust-tracker-http-core services::announce::tests::it_should_map_the_announce_event_to_the_announcement_event::case_1_started +torrust-tracker-http-core services::announce::tests::it_should_map_the_announce_event_to_the_announcement_event::case_2_stopped +torrust-tracker-http-core services::announce::tests::it_should_map_the_announce_event_to_the_announcement_event::case_3_completed +torrust-tracker-http-core services::announce::tests::it_should_map_the_announce_event_to_the_announcement_event::case_4_empty +torrust-tracker-http-core services::announce::tests::it_should_map_the_announce_event_to_the_announcement_event::case_5_absent +torrust-tracker-http-core services::announce::tests::it_should_not_replace_a_loopback_literal_peer_ip_with_the_external_ip +torrust-tracker-http-core services::announce::tests::it_should_prefer_a_query_string_peer_ip_over_the_external_ip_for_a_loopback_client_when_overrides_are_enabled +torrust-tracker-http-core services::announce::tests::it_should_publish_the_connection_address_and_the_selected_peer_ip_in_the_announce_event::case_1_ipv4_client +torrust-tracker-http-core services::announce::tests::it_should_publish_the_connection_address_and_the_selected_peer_ip_in_the_announce_event::case_2_ipv6_client +torrust-tracker-http-core services::announce::tests::it_should_publish_the_connection_address_and_the_selected_peer_ip_in_the_announce_event::case_3_ipv4_loopback_client_and_an_ipv6_external_ip +torrust-tracker-http-core services::announce::tests::it_should_publish_the_connection_address_and_the_selected_peer_ip_in_the_announce_event::case_4_ipv6_loopback_client_and_an_ipv4_external_ip +torrust-tracker-http-core services::announce::tests::it_should_publish_the_public_url_of_the_http_tracker_in_the_announce_event +torrust-tracker-http-core services::announce::tests::it_should_reject_a_dns_name_peer_ip_under_any_override_policy::case_1_overrides_enabled +torrust-tracker-http-core services::announce::tests::it_should_reject_a_dns_name_peer_ip_under_any_override_policy::case_2_overrides_disabled +torrust-tracker-http-core services::announce::tests::it_should_reject_a_literal_peer_ip_when_the_policy_disables_the_override::case_1_ipv4 +torrust-tracker-http-core services::announce::tests::it_should_reject_a_literal_peer_ip_when_the_policy_disables_the_override::case_2_ipv6 +torrust-tracker-http-core services::announce::tests::it_should_reject_an_announce_with_an_unregistered_key_on_a_private_tracker +torrust-tracker-http-core services::announce::tests::it_should_reject_an_announce_without_a_key_on_a_private_tracker +torrust-tracker-http-core services::announce::tests::it_should_reject_an_invalid_peer_ip_under_any_override_policy::case_1_overrides_enabled +torrust-tracker-http-core services::announce::tests::it_should_reject_an_invalid_peer_ip_under_any_override_policy::case_2_overrides_disabled +torrust-tracker-http-core services::announce::tests::it_should_reject_a_torrent_that_is_not_whitelisted_on_a_listed_tracker +torrust-tracker-http-core services::announce::tests::it_should_replace_a_loopback_connection_address_with_the_external_ip_for_an_absent_or_empty_peer_ip::case_1_absent +torrust-tracker-http-core services::announce::tests::it_should_replace_a_loopback_connection_address_with_the_external_ip_for_an_absent_or_empty_peer_ip::case_2_empty +torrust-tracker-http-core services::announce::tests::it_should_report_a_failing_persisted_downloads_lookup_as_a_tracker_core_announce_error +torrust-tracker-http-core services::announce::tests::it_should_report_a_peer_ip_resolution_error_with_the_protocol_failure_reason +torrust-tracker-http-core services::announce::tests::it_should_report_a_rejected_peer_ip_with_its_failure_reason::case_1_override_disabled +torrust-tracker-http-core services::announce::tests::it_should_report_a_rejected_peer_ip_with_its_failure_reason::case_2_dns_name +torrust-tracker-http-core services::announce::tests::it_should_report_a_rejected_peer_ip_with_its_failure_reason::case_3_invalid +torrust-tracker-http-core services::announce::tests::it_should_report_a_tracker_core_error_with_the_tracker_core_failure_reason +torrust-tracker-http-core services::announce::tests::it_should_resolve_the_client_ip_before_selecting_the_peer_ip +torrust-tracker-http-core services::announce::tests::it_should_return_the_announce_data +torrust-tracker-http-core services::announce::tests::it_should_select_a_literal_peer_ip_when_the_policy_allows_the_override::case_1_ipv4 +torrust-tracker-http-core services::announce::tests::it_should_select_a_literal_peer_ip_when_the_policy_allows_the_override::case_2_ipv6 +torrust-tracker-http-core services::announce::tests::it_should_select_the_connection_address_for_an_absent_or_empty_peer_ip::case_1_absent +torrust-tracker-http-core services::announce::tests::it_should_select_the_connection_address_for_an_absent_or_empty_peer_ip::case_2_empty +torrust-tracker-http-core services::announce::tests::it_should_select_the_peer_ip_before_announcing +torrust-tracker-http-core services::announce::tests::it_should_translate_numwant_into_the_number_of_peers_wanted::case_1_absent +torrust-tracker-http-core services::announce::tests::it_should_translate_numwant_into_the_number_of_peers_wanted::case_2_a_number +torrust-tracker-http-core services::announce::tests::it_should_use_the_http_tracker_policy_for_query_string_and_reverse_proxy_ips +torrust-tracker-http-core services::announce::tests::it_should_wrap_a_tracker_core_error_without_changing_it +torrust-tracker-http-core services::error_mapping::tests::it_should_report_each_tracker_core_error_with_the_prefix_of_its_own_area::case_1_announce +torrust-tracker-http-core services::error_mapping::tests::it_should_report_each_tracker_core_error_with_the_prefix_of_its_own_area::case_2_scrape +torrust-tracker-http-core services::error_mapping::tests::it_should_report_each_tracker_core_error_with_the_prefix_of_its_own_area::case_3_whitelist +torrust-tracker-http-core services::error_mapping::tests::it_should_report_each_tracker_core_error_with_the_prefix_of_its_own_area::case_4_authentication +torrust-tracker-http-core services::scrape::tests::it_should_ignore_the_forwarded_address_when_the_http_tracker_is_not_behind_a_reverse_proxy +torrust-tracker-http-core services::scrape::tests::it_should_publish_the_connection_address_of_the_client_in_the_scrape_event::case_1_ipv4_client +torrust-tracker-http-core services::scrape::tests::it_should_publish_the_connection_address_of_the_client_in_the_scrape_event::case_2_ipv6_client +torrust-tracker-http-core services::scrape::tests::it_should_publish_the_public_url_of_the_http_tracker_in_the_scrape_event +torrust-tracker-http-core services::scrape::tests::it_should_publish_the_scrape_event_for_an_unauthenticated_peer_on_a_private_tracker +torrust-tracker-http-core services::scrape::tests::it_should_report_a_failing_persisted_downloads_lookup_as_a_tracker_core_scrape_error +torrust-tracker-http-core services::scrape::tests::it_should_report_an_unresolvable_client_ip_without_publishing_an_event::case_1_without_a_reverse_proxy +torrust-tracker-http-core services::scrape::tests::it_should_report_an_unresolvable_client_ip_without_publishing_an_event::case_2_behind_a_reverse_proxy +torrust-tracker-http-core services::scrape::tests::it_should_report_an_unresolvable_client_ip_without_publishing_an_event::case_3_on_a_private_tracker_without_a_key +torrust-tracker-http-core services::scrape::tests::it_should_report_a_peer_ip_resolution_error_with_the_protocol_failure_reason +torrust-tracker-http-core services::scrape::tests::it_should_report_a_tracker_core_error_with_the_tracker_core_failure_reason +torrust-tracker-http-core services::scrape::tests::it_should_return_the_persisted_downloads_of_a_torrent_absent_from_memory +torrust-tracker-http-core services::scrape::tests::it_should_return_the_scrape_data_for_a_registered_key_on_a_private_tracker +torrust-tracker-http-core services::scrape::tests::it_should_return_the_scrape_data_for_a_torrent +torrust-tracker-http-core services::scrape::tests::it_should_scrape_before_resolving_the_client_ip +torrust-tracker-http-core services::scrape::tests::it_should_scrape_without_publishing_when_there_is_no_event_sender +torrust-tracker-http-core services::scrape::tests::it_should_use_the_http_tracker_reverse_proxy_policy +torrust-tracker-http-core services::scrape::tests::it_should_wrap_an_authentication_error_in_the_tracker_core_variant +torrust-tracker-http-core services::scrape::tests::it_should_wrap_a_tracker_core_error_without_changing_it +torrust-tracker-http-core services::scrape::tests::it_should_wrap_a_whitelist_error_in_the_tracker_core_variant +torrust-tracker-http-core services::scrape::tests::it_should_zero_every_requested_torrent_for_an_unauthenticated_peer_on_a_private_tracker +torrust-tracker-http-core services::scrape::tests::it_should_zero_the_scrape_data_for_an_unregistered_key_on_a_private_tracker +torrust-tracker-http-core statistics::event::handler::tests::it_should_accumulate_events_with_the_same_labels_in_one_sample +torrust-tracker-http-core statistics::event::handler::tests::it_should_count_the_event_in_the_sample_with_exactly_the_connection_labels_and_the_request_kind::case_1_ipv4_announce +torrust-tracker-http-core statistics::event::handler::tests::it_should_count_the_event_in_the_sample_with_exactly_the_connection_labels_and_the_request_kind::case_2_ipv6_announce +torrust-tracker-http-core statistics::event::handler::tests::it_should_count_the_event_in_the_sample_with_exactly_the_connection_labels_and_the_request_kind::case_3_ipv4_scrape +torrust-tracker-http-core statistics::event::handler::tests::it_should_count_the_event_in_the_sample_with_exactly_the_connection_labels_and_the_request_kind::case_4_ipv6_scrape_of_an_ipv4_mapped_client_with_a_public_url_on_another_port +torrust-tracker-http-core statistics::event::handler::tests::it_should_increase_only_the_counter_of_the_request_kind_and_family_of_the_event::case_1_ipv4_announce +torrust-tracker-http-core statistics::event::handler::tests::it_should_increase_only_the_counter_of_the_request_kind_and_family_of_the_event::case_2_ipv4_scrape +torrust-tracker-http-core statistics::event::handler::tests::it_should_increase_only_the_counter_of_the_request_kind_and_family_of_the_event::case_3_ipv6_announce +torrust-tracker-http-core statistics::event::handler::tests::it_should_increase_only_the_counter_of_the_request_kind_and_family_of_the_event::case_4_ipv6_scrape +torrust-tracker-http-core statistics::event::handler::tests::it_should_keep_events_with_different_labels_in_separate_samples +torrust-tracker-http-core statistics::event::handler::tests::it_should_record_the_given_time_on_the_sample_it_increases +torrust-tracker-http-core statistics::event::listener::tests::it_should_apply_the_metrics_policy_of_the_instance_of_each_event_kind::case_1_announce_from_an_enabled_instance +torrust-tracker-http-core statistics::event::listener::tests::it_should_apply_the_metrics_policy_of_the_instance_of_each_event_kind::case_2_announce_from_a_disabled_instance +torrust-tracker-http-core statistics::event::listener::tests::it_should_apply_the_metrics_policy_of_the_instance_of_each_event_kind::case_3_scrape_from_an_enabled_instance +torrust-tracker-http-core statistics::event::listener::tests::it_should_apply_the_metrics_policy_of_the_instance_of_each_event_kind::case_4_scrape_from_a_disabled_instance +torrust-tracker-http-core statistics::event::listener::tests::it_should_apply_the_metrics_policy_to_a_drained_event +torrust-tracker-http-core statistics::event::listener::tests::it_should_continue_after_lag_and_process_an_enabled_event +torrust-tracker-http-core statistics::event::listener::tests::it_should_give_the_spawned_listener_the_cancellation_token_and_the_drain_timeout::case_1_drained_within_the_timeout +torrust-tracker-http-core statistics::event::listener::tests::it_should_give_the_spawned_listener_the_cancellation_token_and_the_drain_timeout::case_2_the_drain_timeout_has_already_expired +torrust-tracker-http-core statistics::event::listener::tests::it_should_handle_a_queued_event_then_return_cancelled_when_cancellation_is_already_pending +torrust-tracker-http-core statistics::event::listener::tests::it_should_handle_every_queued_event_after_cancellation_under_the_metrics_policy +torrust-tracker-http-core statistics::event::listener::tests::it_should_ignore_disabled_and_unknown_events_before_processing_an_enabled_event +torrust-tracker-http-core statistics::event::listener::tests::it_should_leave_the_queued_event_unprocessed_and_return_cancelled_when_the_drain_timeout_has_expired +torrust-tracker-http-core statistics::event::listener::tests::it_should_record_an_event_at_the_time_of_the_clock +torrust-tracker-http-core statistics::event::listener::tests::it_should_return_the_completion_that_says_why_the_unspawned_listener_stopped::case_1_the_receiver_closes +torrust-tracker-http-core statistics::event::listener::tests::it_should_return_the_completion_that_says_why_the_unspawned_listener_stopped::case_2_the_token_is_cancelled +torrust-tracker-http-core statistics::event::listener::tests::it_should_run_the_spawned_listener_until_its_receiver_closes +torrust-tracker-http-core statistics::event::listener::tests::it_should_stop_when_the_receiver_is_closed +torrust-tracker-http-core statistics::metrics::tests::it_should_count_by_the_family_of_the_server_binding_and_not_of_the_client +torrust-tracker-http-core statistics::metrics::tests::it_should_increase_a_counter_by_one_for_each_call +torrust-tracker-http-core statistics::metrics::tests::it_should_project_each_getter_from_its_own_family_and_request_kind +torrust-tracker-http-core statistics::metrics::tests::it_should_project_exactly_a_total_that_an_f64_cannot_represent +torrust-tracker-http-core statistics::metrics::tests::it_should_record_the_given_time_on_the_counter_it_increases +torrust-tracker-http-core statistics::metrics::tests::it_should_record_the_given_time_on_the_gauge_it_sets +torrust-tracker-http-core statistics::metrics::tests::it_should_return_the_error_of_the_collection_when_it_increases_a_gauge_as_a_counter +torrust-tracker-http-core statistics::metrics::tests::it_should_store_the_value_of_a_gauge_set_through_the_metrics +torrust-tracker-http-core statistics::metrics::tests::it_should_sum_the_samples_of_a_family_and_request_kind_whatever_their_other_labels +torrust-tracker-http-core statistics::metrics::tests::it_should_total_zero_when_there_is_no_request_counter_sample::case_1_no_metric +torrust-tracker-http-core statistics::metrics::tests::it_should_total_zero_when_there_is_no_request_counter_sample::case_2_described_without_samples +torrust-tracker-http-core statistics::metrics::tests::it_should_total_zero_when_there_is_no_request_counter_sample::case_3_a_gauge_under_the_counter_name +torrust-tracker-http-core statistics::repository::tests::it_should_add_one_to_the_sample_of_the_given_labels_and_to_no_other +torrust-tracker-http-core statistics::repository::tests::it_should_not_lose_increases_made_concurrently +torrust-tracker-http-core statistics::repository::tests::it_should_record_the_given_time_on_the_sample_it_increases +torrust-tracker-http-core statistics::repository::tests::it_should_return_the_error_of_the_collection_when_the_counter_name_belongs_to_a_gauge +torrust-tracker-http-core statistics::repository::tests::it_should_share_its_store_with_its_clones +torrust-tracker-http-core statistics::repository::tests::it_should_start_with_the_request_counter_described_and_at_zero::case_1_created_with_new +torrust-tracker-http-core statistics::repository::tests::it_should_start_with_the_request_counter_described_and_at_zero::case_2_created_by_default +torrust-tracker-http-core statistics::tests::it_should_describe_the_request_counter_with_its_description_and_type +torrust-tracker-http-core statistics::tests::it_should_describe_the_request_counter_without_any_sample +torrust-tracker-http-core statistics::tests::it_should_describe_the_request_counter_with_the_count_unit_and_its_description +torrust-tracker-http-core statistics::tests::it_should_name_the_request_counter_http_tracker_core_requests_received_total +torrust-tracker-http-protocol percent_encoding::tests::it_should_decode_a_percent_encoded_info_hash +torrust-tracker-http-protocol percent_encoding::tests::it_should_decode_a_percent_encoded_peer_id +torrust-tracker-http-protocol percent_encoding::tests::it_should_encode_a_20_byte_array +torrust-tracker-http-protocol percent_encoding::tests::it_should_fail_decoding_an_invalid_percent_encoded_info_hash +torrust-tracker-http-protocol percent_encoding::tests::it_should_fail_decoding_an_invalid_percent_encoded_peer_id +torrust-tracker-http-protocol v1::query::tests::url_query::param_name_value_pair::should_be_displayed +torrust-tracker-http-protocol v1::query::tests::url_query::param_name_value_pair::should_fail_parsing_an_invalid_query_param +torrust-tracker-http-protocol v1::query::tests::url_query::param_name_value_pair::should_parse_a_single_query_param +torrust-tracker-http-protocol v1::query::tests::url_query::should_allow_more_than_one_value_for_the_same_param::instantiated_from_a_vector +torrust-tracker-http-protocol v1::query::tests::url_query::should_allow_more_than_one_value_for_the_same_param::parsed_from_an_string +torrust-tracker-http-protocol v1::query::tests::url_query::should_be_displayed::with_multiple_params +torrust-tracker-http-protocol v1::query::tests::url_query::should_be_displayed::with_multiple_values_for_the_same_param +torrust-tracker-http-protocol v1::query::tests::url_query::should_be_displayed::with_one_param +torrust-tracker-http-protocol v1::query::tests::url_query::should_be_instantiated_from_a_string_pair_vector +torrust-tracker-http-protocol v1::query::tests::url_query::should_fail_parsing_an_invalid_query_string +torrust-tracker-http-protocol v1::query::tests::url_query::should_ignore_duplicate_param_values_when_asked_to_return_only_one_value +torrust-tracker-http-protocol v1::query::tests::url_query::should_ignore_the_preceding_question_mark_if_it_exists +torrust-tracker-http-protocol v1::query::tests::url_query::should_parse_the_query_params_from_an_url_query_string +torrust-tracker-http-protocol v1::query::tests::url_query::should_trim_whitespaces +torrust-tracker-http-protocol v1::requests::announce::tests::announce_request::it_should_preserve_all_peer_ip_parameter_states +torrust-tracker-http-protocol v1::requests::announce::tests::announce_request::it_should_reject_malformed_encoding_or_invalid_utf8_in_the_peer_ip_parameter +torrust-tracker-http-protocol v1::requests::announce::tests::announce_request::should_be_instantiated_from_the_url_query_params +torrust-tracker-http-protocol v1::requests::announce::tests::announce_request::should_be_instantiated_from_the_url_query_with_only_the_mandatory_params +torrust-tracker-http-protocol v1::requests::announce::tests::announce_request::should_percent_decode_a_valid_peer_ip_parameter +torrust-tracker-http-protocol v1::requests::announce::tests::announce_request::should_recognize_supported_dns_name_syntax +torrust-tracker-http-protocol v1::requests::announce::tests::announce_request::should_reject_invalid_dns_name_syntax +torrust-tracker-http-protocol v1::requests::announce::tests::announce_request::should_reject_invalid_peer_ip_parameter_encoding +torrust-tracker-http-protocol v1::requests::announce::tests::announce_request::should_serialize_an_empty_peer_ip_parameter +torrust-tracker-http-protocol v1::requests::announce::tests::announce_request::when_it_is_instantiated_from_the_url_query_params::it_should_fail_if_the_compact_param_is_invalid +torrust-tracker-http-protocol v1::requests::announce::tests::announce_request::when_it_is_instantiated_from_the_url_query_params::it_should_fail_if_the_downloaded_param_is_invalid +torrust-tracker-http-protocol v1::requests::announce::tests::announce_request::when_it_is_instantiated_from_the_url_query_params::it_should_fail_if_the_event_param_is_invalid +torrust-tracker-http-protocol v1::requests::announce::tests::announce_request::when_it_is_instantiated_from_the_url_query_params::it_should_fail_if_the_info_hash_param_is_invalid +torrust-tracker-http-protocol v1::requests::announce::tests::announce_request::when_it_is_instantiated_from_the_url_query_params::it_should_fail_if_the_left_param_is_invalid +torrust-tracker-http-protocol v1::requests::announce::tests::announce_request::when_it_is_instantiated_from_the_url_query_params::it_should_fail_if_the_numwant_param_is_invalid +torrust-tracker-http-protocol v1::requests::announce::tests::announce_request::when_it_is_instantiated_from_the_url_query_params::it_should_fail_if_the_peer_id_param_is_invalid +torrust-tracker-http-protocol v1::requests::announce::tests::announce_request::when_it_is_instantiated_from_the_url_query_params::it_should_fail_if_the_port_param_is_invalid +torrust-tracker-http-protocol v1::requests::announce::tests::announce_request::when_it_is_instantiated_from_the_url_query_params::it_should_fail_if_the_query_does_not_include_all_the_mandatory_params +torrust-tracker-http-protocol v1::requests::announce::tests::announce_request::when_it_is_instantiated_from_the_url_query_params::it_should_fail_if_the_uploaded_param_is_invalid +torrust-tracker-http-protocol v1::requests::scrape::tests::scrape_request::limiting_the_number_of_info_hashes::it_should_count_repeated_info_hashes_toward_the_limit_of_100 +torrust-tracker-http-protocol v1::requests::scrape::tests::scrape_request::limiting_the_number_of_info_hashes::it_should_ignore_an_invalid_info_hash_after_the_first_100_without_validating_it +torrust-tracker-http-protocol v1::requests::scrape::tests::scrape_request::limiting_the_number_of_info_hashes::it_should_keep_all_100_info_hashes_when_the_request_has_exactly_100 +torrust-tracker-http-protocol v1::requests::scrape::tests::scrape_request::limiting_the_number_of_info_hashes::it_should_keep_only_the_first_100_info_hashes_in_request_order_when_the_request_has_101 +torrust-tracker-http-protocol v1::requests::scrape::tests::scrape_request::limiting_the_number_of_info_hashes::it_should_still_fail_for_an_invalid_info_hash_within_the_first_100 +torrust-tracker-http-protocol v1::requests::scrape::tests::scrape_request::should_be_instantiated_from_the_url_query_with_only_one_infohash +torrust-tracker-http-protocol v1::requests::scrape::tests::scrape_request::when_it_is_instantiated_from_the_url_query_params::it_should_fail_if_the_info_hash_param_is_invalid +torrust-tracker-http-protocol v1::requests::scrape::tests::scrape_request::when_it_is_instantiated_from_the_url_query_params::it_should_fail_if_the_query_does_not_include_the_info_hash_param +torrust-tracker-http-protocol v1::responses::announce::encoding::tests::compact_announce_response_can_be_bencoded +torrust-tracker-http-protocol v1::responses::announce::encoding::tests::non_compact_announce_response_can_be_bencoded +torrust-tracker-http-protocol v1::responses::error::tests::http_tracker_errors_can_be_bencoded +torrust-tracker-http-protocol v1::responses::error::tests::it_should_map_a_peer_ip_resolution_error_into_an_error_response +torrust-tracker-http-protocol v1::responses::scrape::encoding::tests::scrape_response::should_be_bencoded +torrust-tracker-http-protocol v1::responses::scrape::encoding::tests::scrape_response::should_be_converted_from_scrape_data +torrust-tracker-http-protocol v1::responses::scrape::encoding::tests::scrape_response::should_encode_large_download_counts_as_i64 +torrust-tracker-http-protocol v1::services::peer_ip_resolver::tests::working_on_reverse_proxy_mode::it_should_get_the_remote_client_ip_from_the_right_most_ip_in_the_x_forwarded_for_header +torrust-tracker-http-protocol v1::services::peer_ip_resolver::tests::working_on_reverse_proxy_mode::it_should_return_an_error_if_it_cannot_get_the_right_most_ip_from_the_x_forwarded_for_header +torrust-tracker-http-protocol v1::services::peer_ip_resolver::tests::working_without_reverse_proxy::it_should_get_the_remote_client_address_from_the_connection_info +torrust-tracker-http-protocol v1::services::peer_ip_resolver::tests::working_without_reverse_proxy::it_should_return_an_error_if_it_cannot_get_the_remote_client_ip_from_the_connection_info +torrust-tracker::lifecycle-signals it_should_distinguish_sigint_from_sigterm_when_shutting_down_the_tracker_binary +torrust-tracker::lifecycle-signals it_should_force_kill_and_reap_the_tracker_binary_when_the_fixture_is_dropped +torrust-tracker::lifecycle-signals it_should_gracefully_shutdown_the_tracker_binary_when_sigterm_is_delivered_to_its_exact_pid +torrust-tracker::lifecycle-signals it_should_run_two_tracker_binaries_with_independent_cli_configurations +torrust-tracker::lifecycle-signals native_tracker::command::tests::it_should_select_its_configuration_with_the_cli_and_remove_legacy_base_source_variables +torrust-tracker::lifecycle-signals native_tracker::command::tests::it_should_set_the_child_only_health_check_bind_address_override_after_removing_the_inherited_value +torrust-tracker::lifecycle-signals native_tracker::command::tests::it_should_write_a_port_zero_configuration_with_workspace_local_sqlite_storage +torrust-tracker::lifecycle-signals native_tracker::failed_start::tests::it_should_construct_a_parent_only_relative_source_from_the_child_working_directory +torrust-tracker::lifecycle-signals native_tracker::failed_start::tests::it_should_reap_a_failed_start_dropped_without_a_tokio_runtime +torrust-tracker::lifecycle-signals native_tracker::failed_start::tests::it_should_restore_permissions_when_the_restore_guard_is_dropped_without_a_tokio_runtime +torrust-tracker::lifecycle-signals native_tracker::failed_start::tests::it_should_restore_the_unreadable_source_permissions_after_waiting_for_exit +torrust-tracker::lifecycle-signals native_tracker::health::tests::it_should_extract_the_assigned_health_check_address_from_its_startup_log +torrust-tracker::lifecycle-signals native_tracker::health::tests::it_should_reject_non_health_check_startup_logs +torrust-tracker::metrics-fixed-ports it_should_apply_metrics_policy_to_fixed_port_tracker_instances +torrust-tracker::metrics-port-zero it_should_apply_metrics_policy_to_port_zero_tracker_instances +torrust-tracker::metrics-udp-error-disabled-port-zero it_should_not_record_cookie_error_from_metrics_disabled_port_zero_udp_listener +torrust-tracker::metrics-udp-error-enabled-port-zero it_should_record_cookie_error_from_metrics_enabled_port_zero_udp_listener +torrust-tracker::persistence-completed-downloads-on-shutdown it_should_persist_every_download_answered_over_http_before_the_shutdown +torrust-tracker::persistence-scrape-after-restart it_should_scrape_the_persisted_downloads_of_a_torrent_after_a_restart +torrust-tracker-primitives configuration_instance_id::tests::it_should_distinguish_instances_with_the_same_index_and_different_roles +torrust-tracker-primitives configuration_instance_id::tests::it_should_distinguish_instances_with_the_same_role_and_different_indices +torrust-tracker-primitives configuration_instance_id::tests::it_should_expose_its_role_and_zero_based_index +torrust-tracker-primitives configuration_instance_id::tests::it_should_identify_equal_role_and_index_as_the_same_instance +torrust-tracker-primitives configuration_instance_id::tests::it_should_serialize_the_role_and_instance_index +torrust-tracker-primitives driver::tests::it_should_display_mysql +torrust-tracker-primitives driver::tests::it_should_display_postgresql +torrust-tracker-primitives driver::tests::it_should_display_sqlite3 +torrust-tracker-primitives driver::tests::it_should_fail_parsing_invalid_driver +torrust-tracker-primitives driver::tests::it_should_parse_mysql +torrust-tracker-primitives driver::tests::it_should_parse_postgresql +torrust-tracker-primitives driver::tests::it_should_parse_sqlite3 +torrust-tracker-primitives driver::tests::it_should_return_as_str_mysql +torrust-tracker-primitives driver::tests::it_should_return_as_str_postgresql +torrust-tracker-primitives driver::tests::it_should_return_as_str_sqlite3 +torrust-tracker-primitives driver::tests::it_should_serialize_mysql_to_lowercase +torrust-tracker-primitives driver::tests::it_should_serialize_postgresql_to_lowercase +torrust-tracker-primitives driver::tests::it_should_serialize_sqlite3_to_lowercase +torrust-tracker-primitives peer::test::peer::should_be_comparable +torrust-tracker-primitives peer::test::torrent_peer_id::should_be_converted_into_string_type_using_the_hex_string_format +torrust-tracker-primitives peer::test::torrent_peer_id::should_be_converted_to_hex_string +torrust-tracker-primitives peer::test::torrent_peer_id::should_fail_trying_to_convert_from_a_byte_vector_with_less_than_20_bytes +torrust-tracker-primitives peer::test::torrent_peer_id::should_fail_trying_to_convert_from_a_byte_vector_with_more_than_20_bytes +torrust-tracker-primitives peer::test::unix_time_value_serialization::it_should_fail_instead_of_truncating_a_timestamp_beyond_u64_milliseconds +torrust-tracker-primitives peer::test::unix_time_value_serialization::it_should_serialize_the_largest_timestamp_that_fits_in_u64_milliseconds +torrust-tracker-primitives peer::test::unix_time_value_serialization::it_should_serialize_the_timestamp_as_unix_milliseconds +torrust-tracker-primitives runtime_service_metadata::tests::it_should_derive_the_role_from_the_configuration_instance_identity +torrust-tracker-primitives runtime_service_metadata::tests::it_should_store_an_optional_configured_public_url +torrust-tracker-primitives scrape::tests::it_should_be_able_to_build_a_zeroed_scrape_data_for_a_list_of_info_hashes +torrust-tracker-primitives service_role::tests::it_should_display_the_canonical_identifier_for_each_role +torrust-tracker-primitives service_role::tests::it_should_return_the_canonical_identifier_for_each_role +torrust-tracker-primitives service_role::tests::it_should_serialize_each_role_to_its_canonical_identifier +torrust-tracker-rest-api-client connection_info::tests::origin::should_be_parsed_from_a_string_representing_a_url +torrust-tracker-rest-api-client connection_info::tests::origin::when_parsing_from_url_string::should_add_the_slash_after_the_host +torrust-tracker-rest-api-client connection_info::tests::origin::when_parsing_from_url_string::should_fail_when_the_host_is_missing +torrust-tracker-rest-api-client connection_info::tests::origin::when_parsing_from_url_string::should_fail_when_the_scheme_is_missing +torrust-tracker-rest-api-client connection_info::tests::origin::when_parsing_from_url_string::should_fail_when_the_scheme_is_not_supported +torrust-tracker-rest-api-client connection_info::tests::origin::when_parsing_from_url_string::should_ignore_default_ports +torrust-tracker-rest-api-client connection_info::tests::origin::when_parsing_from_url_string::should_remove_extra_path_and_query_parameters +torrust-tracker-rest-api-protocol v1::context::stats::resources::stats::tests::it_should_deserialize_a_legacy_stats_response +torrust-tracker-rest-api-protocol v1::context::torrent::resources::peer::tests::it_should_serialize_and_deserialize_the_required_updated_at_ms_timestamp +torrust-tracker-rest-api-runtime-adapter v1::conversion::tests::it_should_map_all_v1_peer_timestamps_from_the_domain_update_time +torrust-tracker-rest-api-runtime-adapter v1::conversion::tests::torrent_resource_list_item_should_be_converted_from_the_basic_torrent_info +torrust-tracker-rest-api-runtime-adapter v1::conversion::tests::torrent_resource_should_be_converted_from_torrent_info +torrust-tracker::scaffold the_stats_api_endpoint_should_aggregate_announces_across_multiple_trackers +torrust-tracker-swarm-coordination-registry event::test::events_should_be_comparable +torrust-tracker-swarm-coordination-registry statistics::activity_metrics_updater::tests::inactivity_cutoff::it_should_be_now_when_the_peer_timeout_is_zero +torrust-tracker-swarm-coordination-registry statistics::activity_metrics_updater::tests::inactivity_cutoff::it_should_be_the_peer_timeout_before_now +torrust-tracker-swarm-coordination-registry statistics::activity_metrics_updater::tests::inactivity_cutoff::it_should_fall_back_to_the_epoch_when_now_is_earlier_than_the_peer_timeout +torrust-tracker-swarm-coordination-registry statistics::activity_metrics_updater::tests::it_should_return_cancelled_when_the_token_is_cancelled +torrust-tracker-swarm-coordination-registry statistics::activity_metrics_updater::tests::it_should_return_completed_when_the_registry_is_dropped +torrust-tracker-swarm-coordination-registry statistics::activity_metrics_updater::tests::it_should_return_completed_when_the_statistics_repository_is_dropped +torrust-tracker-swarm-coordination-registry statistics::event::handler::tests::for_peer_metrics::it_should_increment_completed_state_reverted_when_a_completed_peer_is_updated_to_not_completed +torrust-tracker-swarm-coordination-registry statistics::event::handler::tests::for_peer_metrics::it_should_increment_the_number_of_peers_added_when_a_peer_added_event_is_received +torrust-tracker-swarm-coordination-registry statistics::event::handler::tests::for_peer_metrics::it_should_increment_the_number_of_peers_removed_when_a_peer_removed_event_is_received +torrust-tracker-swarm-coordination-registry statistics::event::handler::tests::for_peer_metrics::it_should_increment_the_number_of_peers_updated_when_a_peer_updated_event_is_received +torrust-tracker-swarm-coordination-registry statistics::event::handler::tests::for_peer_metrics::it_should_not_change_peer_connection_gauge_when_an_updated_peer_role_is_unchanged +torrust-tracker-swarm-coordination-registry statistics::event::handler::tests::for_peer_metrics::peer_connections_total::it_should_adjust_the_number_of_seeders_and_leechers_when_a_peer_updated_event_is_received_and_the_peer_changed_its_role::case_1 +torrust-tracker-swarm-coordination-registry statistics::event::handler::tests::for_peer_metrics::peer_connections_total::it_should_adjust_the_number_of_seeders_and_leechers_when_a_peer_updated_event_is_received_and_the_peer_changed_its_role::case_2 +torrust-tracker-swarm-coordination-registry statistics::event::handler::tests::for_peer_metrics::peer_connections_total::it_should_decrement_the_number_of_peer_connections_when_a_peer_removed_event_is_received::case_1 +torrust-tracker-swarm-coordination-registry statistics::event::handler::tests::for_peer_metrics::peer_connections_total::it_should_decrement_the_number_of_peer_connections_when_a_peer_removed_event_is_received::case_2 +torrust-tracker-swarm-coordination-registry statistics::event::handler::tests::for_peer_metrics::peer_connections_total::it_should_increment_the_number_of_peer_connections_when_a_peer_added_event_is_received::case_1 +torrust-tracker-swarm-coordination-registry statistics::event::handler::tests::for_peer_metrics::peer_connections_total::it_should_increment_the_number_of_peer_connections_when_a_peer_added_event_is_received::case_2 +torrust-tracker-swarm-coordination-registry statistics::event::handler::tests::for_peer_metrics::torrent_downloads_total::it_should_increment_the_number_of_downloads_when_a_peer_downloaded_event_is_received::case_1 +torrust-tracker-swarm-coordination-registry statistics::event::handler::tests::for_peer_metrics::torrent_downloads_total::it_should_increment_the_number_of_downloads_when_a_peer_downloaded_event_is_received::case_2 +torrust-tracker-swarm-coordination-registry statistics::event::handler::tests::for_torrent_metrics::it_should_decrement_the_number_of_torrents_when_a_torrent_removed_event_is_received +torrust-tracker-swarm-coordination-registry statistics::event::handler::tests::for_torrent_metrics::it_should_increment_the_number_of_torrents_added_when_a_torrent_added_event_is_received +torrust-tracker-swarm-coordination-registry statistics::event::handler::tests::for_torrent_metrics::it_should_increment_the_number_of_torrents_removed_when_a_torrent_removed_event_is_received +torrust-tracker-swarm-coordination-registry statistics::event::handler::tests::for_torrent_metrics::it_should_increment_the_number_of_torrents_when_a_torrent_added_event_is_received +torrust-tracker-swarm-coordination-registry statistics::event::listener::tests::it_should_continue_after_lag_then_handle_an_event_then_stop_when_the_receiver_is_closed +torrust-tracker-swarm-coordination-registry statistics::event::listener::tests::it_should_handle_an_event_then_stop_when_the_receiver_is_closed +torrust-tracker-swarm-coordination-registry statistics::event::listener::tests::it_should_handle_a_queued_event_then_return_cancelled_when_cancellation_is_already_pending +torrust-tracker-swarm-coordination-registry statistics::event::listener::tests::it_should_leave_the_queued_event_unprocessed_and_return_cancelled_when_the_drain_timeout_has_expired +torrust-tracker-swarm-coordination-registry statistics::event::listener::tests::it_should_stop_when_the_receiver_is_closed +torrust-tracker-swarm-coordination-registry statistics::tests::it_should_count_a_peer_announced_after_startup_inactive_after_the_timeout_elapses +torrust-tracker-swarm-coordination-registry statistics::tests::it_should_keep_a_peer_announced_after_startup_active_before_the_timeout_elapses +torrust-tracker-swarm-coordination-registry statistics::tests::it_should_recompute_the_inactivity_cutoff_on_every_update +torrust-tracker-swarm-coordination-registry swarm::coordinator::tests::for_retaining_policy::when_removing_peerless_torrents_is_disabled::it_should_not_be_removed_even_if_the_swarm_is_empty +torrust-tracker-swarm-coordination-registry swarm::coordinator::tests::for_retaining_policy::when_removing_peerless_torrents_is_disabled::it_should_not_be_removed_is_the_swarm_is_not_empty +torrust-tracker-swarm-coordination-registry swarm::coordinator::tests::for_retaining_policy::when_removing_peerless_torrents_is_enabled::it_should_be_removed_if_the_swarm_is_empty +torrust-tracker-swarm-coordination-registry swarm::coordinator::tests::for_retaining_policy::when_removing_peerless_torrents_is_enabled::it_should_not_be_removed_even_if_the_swarm_is_empty_if_we_need_to_track_stats_for_downloads_and_there_has_been_downloads +torrust-tracker-swarm-coordination-registry swarm::coordinator::tests::for_retaining_policy::when_removing_peerless_torrents_is_enabled::it_should_not_be_removed_is_the_swarm_is_not_empty +torrust-tracker-swarm-coordination-registry swarm::coordinator::tests::it_should_allow_getting_all_peers +torrust-tracker-swarm-coordination-registry swarm::coordinator::tests::it_should_allow_getting_all_peers_excluding_peers_with_a_given_address +torrust-tracker-swarm-coordination-registry swarm::coordinator::tests::it_should_allow_getting_one_peer_by_id +torrust-tracker-swarm-coordination-registry swarm::coordinator::tests::it_should_allow_inserting_a_new_peer +torrust-tracker-swarm-coordination-registry swarm::coordinator::tests::it_should_allow_inserting_two_identical_peers_except_for_the_socket_address +torrust-tracker-swarm-coordination-registry swarm::coordinator::tests::it_should_allow_removing_an_existing_peer +torrust-tracker-swarm-coordination-registry swarm::coordinator::tests::it_should_allow_removing_a_non_existing_peer +torrust-tracker-swarm-coordination-registry swarm::coordinator::tests::it_should_allow_updating_a_preexisting_peer +torrust-tracker-swarm-coordination-registry swarm::coordinator::tests::it_should_be_a_peerless_swarm_when_it_does_not_contain_any_peers +torrust-tracker-swarm-coordination-registry swarm::coordinator::tests::it_should_be_empty_when_no_peers_have_been_inserted +torrust-tracker-swarm-coordination-registry swarm::coordinator::tests::it_should_count_inactive_peers +torrust-tracker-swarm-coordination-registry swarm::coordinator::tests::it_should_decrease_the_number_of_peers_after_removing_one +torrust-tracker-swarm-coordination-registry swarm::coordinator::tests::it_should_have_zero_length_when_no_peers_have_been_inserted +torrust-tracker-swarm-coordination-registry swarm::coordinator::tests::it_should_increase_the_number_of_peers_after_inserting_a_new_one +torrust-tracker-swarm-coordination-registry swarm::coordinator::tests::it_should_not_allow_inserting_two_peers_with_different_peer_id_but_the_same_socket_address +torrust-tracker-swarm-coordination-registry swarm::coordinator::tests::it_should_not_remove_active_peers +torrust-tracker-swarm-coordination-registry swarm::coordinator::tests::it_should_remove_inactive_peers +torrust-tracker-swarm-coordination-registry swarm::coordinator::tests::it_should_return_the_number_of_leechers_in_the_list +torrust-tracker-swarm-coordination-registry swarm::coordinator::tests::it_should_return_the_number_of_seeders_in_the_list +torrust-tracker-swarm-coordination-registry swarm::coordinator::tests::it_should_return_the_swarm_metadata +torrust-tracker-swarm-coordination-registry swarm::coordinator::tests::triggering_events::it_should_trigger_an_event_when_a_new_peer_is_added +torrust-tracker-swarm-coordination-registry swarm::coordinator::tests::triggering_events::it_should_trigger_an_event_when_a_peer_completes_a_download +torrust-tracker-swarm-coordination-registry swarm::coordinator::tests::triggering_events::it_should_trigger_an_event_when_a_peer_is_directly_removed +torrust-tracker-swarm-coordination-registry swarm::coordinator::tests::triggering_events::it_should_trigger_an_event_when_a_peer_is_removed_due_to_inactivity +torrust-tracker-swarm-coordination-registry swarm::coordinator::tests::triggering_events::it_should_trigger_an_event_when_a_peer_is_updated +torrust-tracker-swarm-coordination-registry swarm::coordinator::tests::updating_the_swarm_metadata::for_changes_in_existing_peers::it_should_increase_leechers_and_decreasing_seeders_when_the_peer_changes_from_seeder_to_leecher +torrust-tracker-swarm-coordination-registry swarm::coordinator::tests::updating_the_swarm_metadata::for_changes_in_existing_peers::it_should_increase_seeders_and_decreasing_leechers_when_the_peer_changes_from_leecher_to_seeder_ +torrust-tracker-swarm-coordination-registry swarm::coordinator::tests::updating_the_swarm_metadata::for_changes_in_existing_peers::it_should_increase_the_number_of_downloads_when_the_peer_announces_completed_downloading +torrust-tracker-swarm-coordination-registry swarm::coordinator::tests::updating_the_swarm_metadata::for_changes_in_existing_peers::it_should_not_increasing_the_number_of_downloads_when_the_peer_announces_completed_downloading_twice_ +torrust-tracker-swarm-coordination-registry swarm::coordinator::tests::updating_the_swarm_metadata::when_a_new_peer_is_added::it_should_increase_the_number_of_leechers_if_the_new_peer_is_a_leecher_ +torrust-tracker-swarm-coordination-registry swarm::coordinator::tests::updating_the_swarm_metadata::when_a_new_peer_is_added::it_should_increase_the_number_of_seeders_if_the_new_peer_is_a_seeder +torrust-tracker-swarm-coordination-registry swarm::coordinator::tests::updating_the_swarm_metadata::when_a_new_peer_is_added::it_should_not_increasing_the_number_of_downloads_if_the_new_peer_has_completed_downloading_as_it_was_not_previously_known +torrust-tracker-swarm-coordination-registry swarm::coordinator::tests::updating_the_swarm_metadata::when_a_peer_is_removed_due_to_inactivity::it_should_decrease_the_number_of_leechers_when_a_removed_peer_is_a_leecher +torrust-tracker-swarm-coordination-registry swarm::coordinator::tests::updating_the_swarm_metadata::when_a_peer_is_removed_due_to_inactivity::it_should_decrease_the_number_of_seeders_when_the_removed_peer_is_a_seeder +torrust-tracker-swarm-coordination-registry swarm::coordinator::tests::updating_the_swarm_metadata::when_a_peer_is_removed::it_should_decrease_the_number_of_leechers_if_the_removed_peer_was_a_leecher +torrust-tracker-swarm-coordination-registry swarm::coordinator::tests::updating_the_swarm_metadata::when_a_peer_is_removed::it_should_decrease_the_number_of_seeders_if_the_removed_peer_was_a_seeder +torrust-tracker-swarm-coordination-registry swarm::registry::tests::the_swarm_repository::handling_persistence::it_should_allow_importing_persisted_torrent_entries +torrust-tracker-swarm-coordination-registry swarm::registry::tests::the_swarm_repository::handling_persistence::it_should_allow_overwriting_a_previously_imported_persisted_torrent +torrust-tracker-swarm-coordination-registry swarm::registry::tests::the_swarm_repository::handling_persistence::it_should_now_allow_importing_a_persisted_torrent_if_it_already_exists +torrust-tracker-swarm-coordination-registry swarm::registry::tests::the_swarm_repository::it_should_be_empty_when_it_has_no_swarms +torrust-tracker-swarm-coordination-registry swarm::registry::tests::the_swarm_repository::it_should_not_be_empty_when_it_has_at_least_one_swarm +torrust-tracker-swarm-coordination-registry swarm::registry::tests::the_swarm_repository::it_should_return_the_length_when_it_has_swarms +torrust-tracker-swarm-coordination-registry swarm::registry::tests::the_swarm_repository::it_should_return_zero_length_when_it_has_no_swarms +torrust-tracker-swarm-coordination-registry swarm::registry::tests::the_swarm_repository::maintaining_the_peer_lists::it_should_add_the_first_peer_to_the_torrent_peer_list +torrust-tracker-swarm-coordination-registry swarm::registry::tests::the_swarm_repository::maintaining_the_peer_lists::it_should_allow_adding_the_same_peer_twice_to_the_torrent_peer_list +torrust-tracker-swarm-coordination-registry swarm::registry::tests::the_swarm_repository::maintaining_the_torrent_entries::it_should_count_inactive_peers +torrust-tracker-swarm-coordination-registry swarm::registry::tests::the_swarm_repository::maintaining_the_torrent_entries::it_should_remove_a_torrent_entry +torrust-tracker-swarm-coordination-registry swarm::registry::tests::the_swarm_repository::maintaining_the_torrent_entries::it_should_remove_peers_that_have_not_been_updated_after_a_cutoff_time +torrust-tracker-swarm-coordination-registry swarm::registry::tests::the_swarm_repository::maintaining_the_torrent_entries::it_should_remove_torrents_without_peers +torrust-tracker-swarm-coordination-registry swarm::registry::tests::the_swarm_repository::returning_aggregate_swarm_metadata::it_should_count_peerless_torrents::no_peerless_torrents +torrust-tracker-swarm-coordination-registry swarm::registry::tests::the_swarm_repository::returning_aggregate_swarm_metadata::it_should_count_peerless_torrents::one_peerless_torrents +torrust-tracker-swarm-coordination-registry swarm::registry::tests::the_swarm_repository::returning_aggregate_swarm_metadata::it_should_count_peers::no_peers +torrust-tracker-swarm-coordination-registry swarm::registry::tests::the_swarm_repository::returning_aggregate_swarm_metadata::it_should_count_peers::one_peer +torrust-tracker-swarm-coordination-registry swarm::registry::tests::the_swarm_repository::returning_aggregate_swarm_metadata::it_should_get_empty_aggregate_swarm_metadata_when_there_are_no_torrents +torrust-tracker-swarm-coordination-registry swarm::registry::tests::the_swarm_repository::returning_aggregate_swarm_metadata::it_should_return_the_aggregate_swarm_metadata_when_there_are_multiple_torrents +torrust-tracker-swarm-coordination-registry swarm::registry::tests::the_swarm_repository::returning_aggregate_swarm_metadata::it_should_return_the_aggregate_swarm_metadata_when_there_is_a_completed_peer +torrust-tracker-swarm-coordination-registry swarm::registry::tests::the_swarm_repository::returning_aggregate_swarm_metadata::it_should_return_the_aggregate_swarm_metadata_when_there_is_a_leecher +torrust-tracker-swarm-coordination-registry swarm::registry::tests::the_swarm_repository::returning_aggregate_swarm_metadata::it_should_return_the_aggregate_swarm_metadata_when_there_is_a_seeder +torrust-tracker-swarm-coordination-registry swarm::registry::tests::the_swarm_repository::returning_peer_lists_for_a_torrent::excluding_the_client_peer::it_should_return_74_peers_at_the_most_for_a_given_torrent_when_it_filters_out_a_given_peer +torrust-tracker-swarm-coordination-registry swarm::registry::tests::the_swarm_repository::returning_peer_lists_for_a_torrent::excluding_the_client_peer::it_should_return_an_empty_peer_list_for_a_non_existing_torrent +torrust-tracker-swarm-coordination-registry swarm::registry::tests::the_swarm_repository::returning_peer_lists_for_a_torrent::excluding_the_client_peer::it_should_return_the_peers_for_a_given_torrent_excluding_a_given_peer +torrust-tracker-swarm-coordination-registry swarm::registry::tests::the_swarm_repository::returning_peer_lists_for_a_torrent::it_should_return_74_peers_at_the_most_for_a_given_torrent +torrust-tracker-swarm-coordination-registry swarm::registry::tests::the_swarm_repository::returning_peer_lists_for_a_torrent::it_should_return_an_empty_list_or_peers_for_a_non_existing_torrent +torrust-tracker-swarm-coordination-registry swarm::registry::tests::the_swarm_repository::returning_peer_lists_for_a_torrent::it_should_return_the_peers_for_a_given_torrent +torrust-tracker-swarm-coordination-registry swarm::registry::tests::the_swarm_repository::returning_swarm_metadata::it_should_get_swarm_metadata_for_an_existing_torrent +torrust-tracker-swarm-coordination-registry swarm::registry::tests::the_swarm_repository::returning_swarm_metadata::it_should_return_zeroed_swarm_metadata_for_a_non_existing_torrent +torrust-tracker-swarm-coordination-registry swarm::registry::tests::the_swarm_repository::returning_torrent_entries::it_should_return_many_torrent_entries::without_pagination +torrust-tracker-swarm-coordination-registry swarm::registry::tests::the_swarm_repository::returning_torrent_entries::it_should_return_many_torrent_entries::with_pagination::it_should_allow_changing_the_page_size +torrust-tracker-swarm-coordination-registry swarm::registry::tests::the_swarm_repository::returning_torrent_entries::it_should_return_many_torrent_entries::with_pagination::it_should_return_the_first_page +torrust-tracker-swarm-coordination-registry swarm::registry::tests::the_swarm_repository::returning_torrent_entries::it_should_return_many_torrent_entries::with_pagination::it_should_return_the_second_page +torrust-tracker-swarm-coordination-registry swarm::registry::tests::the_swarm_repository::returning_torrent_entries::it_should_return_one_torrent_entry_by_infohash +torrust-tracker-swarm-coordination-registry swarm::registry::tests::triggering_events::it_should_trigger_an_event_when_a_new_torrent_is_added +torrust-tracker-swarm-coordination-registry swarm::registry::tests::triggering_events::it_should_trigger_an_event_when_a_peerless_torrent_is_removed +torrust-tracker-swarm-coordination-registry swarm::registry::tests::triggering_events::it_should_trigger_an_event_when_a_torrent_is_directly_removed +torrust-tracker-udp-core connection_cookie::tests::it_should_accept_a_cookie_issued_exactly_at_the_end_of_the_valid_range +torrust-tracker-udp-core connection_cookie::tests::it_should_accept_a_cookie_issued_exactly_at_the_start_of_the_valid_range +torrust-tracker-udp-core connection_cookie::tests::it_should_create_different_cookies_for_different_fingerprints +torrust-tracker-udp-core connection_cookie::tests::it_should_create_different_cookies_for_different_issue_times +torrust-tracker-udp-core connection_cookie::tests::it_should_create_same_cookie_for_same_input +torrust-tracker-udp-core connection_cookie::tests::it_should_keep_the_cookie_encoding_stable_for_the_test_key +torrust-tracker-udp-core connection_cookie::tests::it_should_panic_when_the_valid_range_start_is_after_its_end +torrust-tracker-udp-core connection_cookie::tests::it_should_refuse_to_make_a_cookie_from_an_issue_time_that_is_not_normal +torrust-tracker-udp-core connection_cookie::tests::it_should_reject_a_cookie_from_the_future +torrust-tracker-udp-core connection_cookie::tests::it_should_reject_a_cookie_when_checked_with_another_client_address_fingerprint +torrust-tracker-udp-core connection_cookie::tests::it_should_reject_a_forged_cookie_that_decodes_to_an_issue_time_that_is_not_normal +torrust-tracker-udp-core connection_cookie::tests::it_should_reject_an_expired_cookie +torrust-tracker-udp-core connection_cookie::tests::it_should_return_the_issue_time_when_the_cookie_was_issued_inside_the_valid_range +torrust-tracker-udp-core connection_cookie::tests::it_should_round_trip_any_normal_issue_time_for_any_fingerprint +torrust-tracker-udp-core container::tests::it_should_accept_in_announce_a_connection_id_issued_by_the_connect_service_of_the_same_container +torrust-tracker-udp-core container::tests::it_should_accept_in_one_container_a_connection_id_issued_by_another_container_built_from_the_same_services +torrust-tracker-udp-core container::tests::it_should_accept_in_scrape_a_connection_id_issued_by_the_connect_service_of_the_same_container +torrust-tracker-udp-core container::tests::it_should_announce_a_loopback_client_with_the_configured_external_ip +torrust-tracker-udp-core container::tests::it_should_ban_an_ip_only_after_more_than_the_configured_connection_id_errors +torrust-tracker-udp-core container::tests::it_should_give_every_service_the_configured_public_url +torrust-tracker-udp-core container::tests::it_should_give_the_container_and_every_service_its_configuration_instance_id +torrust-tracker-udp-core crypto::cookie_cipher::tests::it_should_create_a_different_key_each_time_when_the_key_is_random +torrust-tracker-udp-core crypto::cookie_cipher::tests::it_should_not_encrypt_like_the_fixed_test_key_when_the_key_is_random +torrust-tracker-udp-core crypto::cookie_cipher::tests::it_should_redact_the_key_when_formatted_for_debugging +torrust-tracker-udp-core crypto::cookie_cipher::tests::it_should_wipe_the_key_schedule_when_the_cipher_is_dropped +torrust-tracker-udp-core event::tests::it_should_include_the_public_url_label_only_when_a_public_url_is_configured +torrust-tracker-udp-core event::tests::it_should_project_the_server_binding_and_the_client_address_into_metric_labels +torrust-tracker-udp-core event::tests::it_should_report_a_plain_ip_type_when_the_client_is_ipv4 +torrust-tracker-udp-core event::tests::it_should_report_a_plain_ip_type_when_the_client_is_native_ipv6 +torrust-tracker-udp-core event::tests::it_should_report_a_v4_mapped_v6_ip_type_when_the_client_is_ipv4_mapped_ipv6 +torrust-tracker-udp-core event::tests::it_should_report_the_inet6_family_when_the_client_is_ipv6 +torrust-tracker-udp-core event::tests::it_should_report_the_inet_family_when_the_client_is_ipv4 +torrust-tracker-udp-core event::tests::it_should_retain_an_optional_configured_public_url +torrust-tracker-udp-core event::tests::it_should_return_the_client_socket_address_not_the_server_one +torrust-tracker-udp-core peer_builder::tests::it_should_carry_the_request_peer_id +torrust-tracker-udp-core peer_builder::tests::it_should_map_each_transfer_counter_to_the_matching_peer_field +torrust-tracker-udp-core peer_builder::tests::it_should_map_each_wire_announce_event_to_the_matching_domain_event::case_1_started +torrust-tracker-udp-core peer_builder::tests::it_should_map_each_wire_announce_event_to_the_matching_domain_event::case_2_stopped +torrust-tracker-udp-core peer_builder::tests::it_should_map_each_wire_announce_event_to_the_matching_domain_event::case_3_completed +torrust-tracker-udp-core peer_builder::tests::it_should_map_each_wire_announce_event_to_the_matching_domain_event::case_4_none +torrust-tracker-udp-core peer_builder::tests::it_should_stamp_the_peer_with_the_current_time +torrust-tracker-udp-core peer_builder::tests::it_should_use_the_supplied_ip_and_the_request_port_instead_of_the_ip_in_the_request +torrust-tracker-udp-core services::announce::tests::it_should_accept_an_expired_connection_id_when_cookie_validation_is_skipped +torrust-tracker-udp-core services::announce::tests::it_should_announce_a_loopback_client_with_the_configured_tracker_external_ip +torrust-tracker-udp-core services::announce::tests::it_should_announce_the_peer_with_the_client_socket_ip_instead_of_the_ip_in_the_request +torrust-tracker-udp-core services::announce::tests::it_should_publish_an_announce_event_with_the_connection_context_and_the_announced_peer +torrust-tracker-udp-core services::announce::tests::it_should_reject_an_announce_for_a_torrent_that_is_not_whitelisted_on_a_listed_tracker +torrust-tracker-udp-core services::announce::tests::it_should_reject_an_expired_connection_id_when_cookie_validation_is_enabled +torrust-tracker-udp-core services::announce::tests::it_should_report_a_failing_persisted_downloads_lookup_as_a_tracker_core_announce_error +torrust-tracker-udp-core services::announce::tests::it_should_validate_the_connection_id_before_revealing_whether_the_torrent_is_whitelisted +torrust-tracker-udp-core services::banning::tests::it_should_ban_an_ip_exactly_when_its_errors_exceed_the_limit +torrust-tracker-udp-core services::banning::tests::it_should_ban_an_ip_exactly_when_its_errors_exceed_the_limit_for_counts_within_two_of_it +torrust-tracker-udp-core services::banning::tests::it_should_ban_ips_with_counters_exceeding_a_predefined_limit +torrust-tracker-udp-core services::banning::tests::it_should_clear_an_ips_error_count_when_the_bans_are_reset +torrust-tracker-udp-core services::banning::tests::it_should_count_a_banned_ip_only_once_when_it_keeps_sending_invalid_connection_ids +torrust-tracker-udp-core services::banning::tests::it_should_count_an_ip_as_banned_once_its_errors_exceed_the_limit +torrust-tracker-udp-core services::banning::tests::it_should_count_an_ip_banned_again_after_a_reset_only_once +torrust-tracker-udp-core services::banning::tests::it_should_count_an_ip_once_when_the_limit_is_one_below_the_maximum_count +torrust-tracker-udp-core services::banning::tests::it_should_increase_the_errors_counter_for_a_given_ip +torrust-tracker-udp-core services::banning::tests::it_should_keep_the_banned_ips_total_equal_to_the_ips_above_the_limit_for_any_errors_and_resets +torrust-tracker-udp-core services::banning::tests::it_should_not_ban_ips_whose_counters_do_not_exceed_the_predefined_limit +torrust-tracker-udp-core services::banning::tests::it_should_not_ban_ips_without_connection_id_errors +torrust-tracker-udp-core services::banning::tests::it_should_not_count_a_banned_ip_again_after_its_error_count_reaches_the_maximum +torrust-tracker-udp-core services::banning::tests::it_should_not_count_an_ip_as_banned_while_its_errors_do_not_exceed_the_limit +torrust-tracker-udp-core services::banning::tests::it_should_not_count_an_ip_whose_errors_after_a_reset_do_not_exceed_the_limit +torrust-tracker-udp-core services::banning::tests::it_should_not_count_any_ip_as_banned_after_the_bans_are_reset +torrust-tracker-udp-core services::banning::tests::it_should_not_count_any_ip_as_banned_when_several_ips_stay_within_the_limit +torrust-tracker-udp-core services::banning::tests::it_should_not_count_one_ips_errors_against_another_ip +torrust-tracker-udp-core services::connect::tests::it_should_issue_a_connection_id_that_validates_for_the_client_at_the_issue_time +torrust-tracker-udp-core services::connect::tests::it_should_panic_when_the_issue_time_is_not_a_normal_value +torrust-tracker-udp-core services::connect::tests::it_should_publish_a_connect_event_with_the_connection_context +torrust-tracker-udp-core services::scrape::tests::it_should_accept_an_expired_connection_id_when_cookie_validation_is_skipped +torrust-tracker-udp-core services::scrape::tests::it_should_publish_a_scrape_event_with_the_connection_context +torrust-tracker-udp-core services::scrape::tests::it_should_reject_an_expired_connection_id_when_cookie_validation_is_enabled +torrust-tracker-udp-core services::scrape::tests::it_should_report_a_failing_persisted_downloads_lookup_as_a_tracker_core_scrape_error +torrust-tracker-udp-core services::scrape::tests::it_should_return_the_swarm_statistics_under_the_requested_info_hash +torrust-tracker-udp-core services::scrape::tests::it_should_validate_the_connection_id_before_reading_any_persisted_downloads +torrust-tracker-udp-core statistics::event::handler::tests::it_should_count_each_event_as_one_request_of_its_kind::case_1_connect +torrust-tracker-udp-core statistics::event::handler::tests::it_should_count_each_event_as_one_request_of_its_kind::case_2_announce +torrust-tracker-udp-core statistics::event::handler::tests::it_should_count_each_event_as_one_request_of_its_kind::case_3_scrape +torrust-tracker-udp-core statistics::event::handler::tests::it_should_propagate_all_connection_labels_and_request_kind +torrust-tracker-udp-core statistics::event::listener::tests::it_should_apply_the_metrics_policy_to_a_drained_event +torrust-tracker-udp-core statistics::event::listener::tests::it_should_complete_when_the_receiver_is_closed +torrust-tracker-udp-core statistics::event::listener::tests::it_should_continue_after_lag_and_process_an_enabled_event +torrust-tracker-udp-core statistics::event::listener::tests::it_should_count_an_event_of_any_kind_from_a_metrics_enabled_listener::case_1_connect +torrust-tracker-udp-core statistics::event::listener::tests::it_should_count_an_event_of_any_kind_from_a_metrics_enabled_listener::case_2_announce +torrust-tracker-udp-core statistics::event::listener::tests::it_should_count_an_event_of_any_kind_from_a_metrics_enabled_listener::case_3_scrape +torrust-tracker-udp-core statistics::event::listener::tests::it_should_handle_a_queued_event_then_return_cancelled_when_cancellation_is_already_pending +torrust-tracker-udp-core statistics::event::listener::tests::it_should_ignore_disabled_and_unknown_events_before_processing_an_enabled_event +torrust-tracker-udp-core statistics::event::listener::tests::it_should_leave_the_queued_event_unprocessed_and_return_cancelled_when_the_drain_timeout_has_expired +torrust-tracker-udp-core statistics::metrics::tests::it_should_add_up_in_a_projection_the_requests_received_by_every_listener +torrust-tracker-udp-core statistics::metrics::tests::it_should_count_in_each_projection_only_its_own_ip_family_and_request_kind +torrust-tracker-udp-core statistics::metrics::tests::it_should_project_exactly_a_total_that_an_f64_cannot_represent +torrust-tracker-udp-core statistics::metrics::tests::it_should_project_zero_everywhere_when_no_request_was_recorded +torrust-tracker-udp-core statistics::repository::tests::it_should_describe_the_requests_received_counter_when_created::case_1_new +torrust-tracker-udp-core statistics::repository::tests::it_should_describe_the_requests_received_counter_when_created::case_2_default +torrust-tracker-udp-core statistics::repository::tests::it_should_make_an_increased_counter_visible_through_the_repository_stats +torrust-tracker-udp-core statistics::repository::tests::it_should_return_the_metric_collection_error_when_a_counter_cannot_be_increased +torrust-tracker-udp-core statistics::tests::it_should_declare_the_requests_received_counter_under_its_exported_name +torrust-tracker-udp-protocol request::tests::it_should_not_panic_when_parsing_all_action_codes_at_all_packet_lengths +torrust-tracker-udp-protocol request::tests::it_should_preserve_announce_request_when_encoded_and_parsed +torrust-tracker-udp-protocol request::tests::it_should_preserve_connect_request_when_encoded_and_parsed +torrust-tracker-udp-protocol request::tests::it_should_preserve_scrape_request_when_encoded_and_parsed +torrust-tracker-udp-protocol request::tests::it_should_reject_scrape_request_without_info_hashes +torrust-tracker-udp-protocol request::tests::limiting_the_number_of_scrape_info_hashes::it_should_fit_74_info_hashes_and_no_more_in_one_scrape_request_packet +torrust-tracker-udp-protocol request::tests::limiting_the_number_of_scrape_info_hashes::it_should_keep_all_74_info_hashes_when_the_request_has_exactly_74 +torrust-tracker-udp-protocol request::tests::limiting_the_number_of_scrape_info_hashes::it_should_keep_only_the_first_74_info_hashes_in_request_order_when_the_request_has_75 +torrust-tracker-udp-protocol response::tests::it_should_preserve_connect_response_when_encoded_and_parsed +torrust-tracker-udp-protocol response::tests::it_should_preserve_ipv4_announce_response_when_encoded_and_parsed +torrust-tracker-udp-protocol response::tests::it_should_preserve_ipv6_announce_response_when_encoded_and_parsed +torrust-tracker-udp-protocol response::tests::it_should_preserve_scrape_response_when_encoded_and_parsed +torrust-tracker-udp-server banning::event::handler::tests::it_should_hold_the_publication_order_until_it_has_published_the_banned_ip_total +torrust-tracker-udp-server banning::event::handler::tests::it_should_not_hold_the_ban_service_lock_while_waiting_to_publish_the_banned_ip_total +torrust-tracker-udp-server banning::event::handler::tests::it_should_publish_the_banned_ip_total_after_a_connection_cookie_error +torrust-tracker-udp-server banning::event::handler::tests::it_should_record_the_connection_cookie_error_for_its_client_ip +torrust-tracker-udp-server banning::event::listener::tests::it_should_handle_a_queued_event_then_return_cancelled_when_cancellation_is_already_pending +torrust-tracker-udp-server banning::event::listener::tests::it_should_ignore_non_connection_cookie_errors +torrust-tracker-udp-server banning::event::listener::tests::it_should_leave_the_queued_event_unprocessed_and_return_cancelled_when_the_drain_timeout_has_expired +torrust-tracker-udp-server banning::event::listener::tests::it_should_process_connection_cookie_errors_after_lagging +torrust-tracker-udp-server banning::event::listener::tests::it_should_stop_when_the_receiver_is_closed +torrust-tracker-udp-server banning::tests::it_should_hold_the_publication_order_until_it_has_published_the_reset_total +torrust-tracker-udp-server banning::tests::it_should_not_hold_the_ban_service_lock_while_waiting_to_publish_the_reset_total +torrust-tracker-udp-server banning::tests::it_should_publish_the_new_banned_ips_total_after_resetting_the_bans +torrust-tracker-udp-server container::tests::it_should_publish_events_through_the_enabled_server_event_bus +torrust-tracker-udp-server error::tests::it_should_clear_response_routing_identifiers_for_an_unsendable_parse_error +torrust-tracker-udp-server error::tests::it_should_preserve_response_routing_identifiers_for_a_sendable_parse_error +torrust-tracker-udp-server error::tests::it_should_wrap_an_announce_service_error_as_an_announce_failure +torrust-tracker-udp-server error::tests::it_should_wrap_a_scrape_service_error_as_a_scrape_failure +torrust-tracker-udp-server error::tests::it_should_wrap_a_sendable_parse_error_as_an_invalid_request +torrust-tracker-udp-server event::tests::it_should_classify_a_connection_cookie_error +torrust-tracker-udp-server event::tests::it_should_classify_a_database_error +torrust-tracker-udp-server event::tests::it_should_classify_a_direct_scrape_whitelist_error +torrust-tracker-udp-server event::tests::it_should_classify_an_authentication_error +torrust-tracker-udp-server event::tests::it_should_classify_a_nested_announce_whitelist_error +torrust-tracker-udp-server event::tests::it_should_classify_a_nested_scrape_whitelist_error +torrust-tracker-udp-server event::tests::it_should_classify_an_internal_error +torrust-tracker-udp-server event::tests::it_should_classify_an_invalid_request_as_a_request_parse_error +torrust-tracker-udp-server event::tests::it_should_classify_a_scrape_connection_cookie_error +torrust-tracker-udp-server event::tests::it_should_classify_a_scrape_database_error +torrust-tracker-udp-server event::tests::it_should_classify_a_whitelist_error +torrust-tracker-udp-server event::tests::it_should_convert_request_kinds_to_metric_labels +torrust-tracker-udp-server event::tests::it_should_render_request_kinds +torrust-tracker-udp-server handlers::announce::tests::announce_request::it_should_clamp_an_out_of_range_interval_and_peer_counts_for_both_address_families +torrust-tracker-udp-server handlers::announce::tests::announce_request::it_should_encode_only_peer_addresses_matching_the_request_family +torrust-tracker-udp-server handlers::announce::tests::announce_request::using_ipv4::an_announced_peer_should_be_added_to_the_tracker +torrust-tracker-udp-server handlers::announce::tests::announce_request::using_ipv4::from_a_loopback_ip::each_listener_should_use_its_own_configured_external_ip +torrust-tracker-udp-server handlers::announce::tests::announce_request::using_ipv4::it_should_preserve_an_announce_service_failure_for_packet_error_routing +torrust-tracker-udp-server handlers::announce::tests::announce_request::using_ipv4::should_send_the_udp4_announce_event +torrust-tracker-udp-server handlers::announce::tests::announce_request::using_ipv4::the_announced_peer_should_not_be_included_in_the_response +torrust-tracker-udp-server handlers::announce::tests::announce_request::using_ipv4::the_tracker_should_always_use_the_remote_client_ip_but_not_the_port_in_the_udp_request_header_instead_of_the_peer_address_in_the_announce_request +torrust-tracker-udp-server handlers::announce::tests::announce_request::using_ipv4::when_the_announce_request_comes_from_a_client_using_ipv4_the_response_should_not_include_peers_using_ipv6 +torrust-tracker-udp-server handlers::announce::tests::announce_request::using_ipv6::an_announced_peer_should_be_added_to_the_tracker +torrust-tracker-udp-server handlers::announce::tests::announce_request::using_ipv6::from_a_loopback_ip::the_peer_ip_should_be_changed_to_the_external_ip_in_the_tracker_configuration +torrust-tracker-udp-server handlers::announce::tests::announce_request::using_ipv6::should_send_the_udp6_announce_event +torrust-tracker-udp-server handlers::announce::tests::announce_request::using_ipv6::the_announced_peer_should_not_be_included_in_the_response +torrust-tracker-udp-server handlers::announce::tests::announce_request::using_ipv6::the_tracker_should_always_use_the_remote_client_ip_but_not_the_port_in_the_udp_request_header_instead_of_the_peer_address_in_the_announce_request +torrust-tracker-udp-server handlers::announce::tests::announce_request::using_ipv6::when_the_announce_request_comes_from_a_client_using_ipv6_the_response_should_not_include_peers_using_ipv4 +torrust-tracker-udp-server handlers::connect::tests::connect_request::a_connect_response_should_contain_a_new_connection_id_ipv6 +torrust-tracker-udp-server handlers::connect::tests::connect_request::a_connect_response_should_contain_the_same_transaction_id_as_the_connect_request +torrust-tracker-udp-server handlers::connect::tests::connect_request::it_should_send_the_udp4_connect_event_when_a_client_tries_to_connect_using_an_ipv4_socket_address +torrust-tracker-udp-server handlers::connect::tests::connect_request::it_should_send_the_udp6_connect_event_when_a_client_tries_to_connect_using_an_ipv6_socket_address +torrust-tracker-udp-server handlers::error::tests::it_should_answer_with_a_zero_transaction_id_when_the_request_has_no_transaction_id +torrust-tracker-udp-server handlers::error::tests::it_should_answer_with_the_error_message_and_the_request_transaction_id +torrust-tracker-udp-server handlers::error::tests::it_should_classify_announce_and_scrape_cookie_failures_as_connection_cookie_errors +torrust-tracker-udp-server handlers::error::tests::it_should_not_classify_an_internal_failure_as_a_connection_cookie_error +torrust-tracker-udp-server handlers::error::tests::it_should_publish_an_error_event_naming_the_connection_request_kind_and_error +torrust-tracker-udp-server handlers::scrape::tests::scrape_request::it_should_preserve_a_scrape_service_failure_for_packet_error_routing +torrust-tracker-udp-server handlers::scrape::tests::scrape_request::it_should_preserve_the_order_of_eight_requested_info_hashes +torrust-tracker-udp-server handlers::scrape::tests::scrape_request::it_should_publish_a_cookie_error_and_answer_when_cookie_validation_is_disabled +torrust-tracker-udp-server handlers::scrape::tests::scrape_request::it_should_publish_an_accepted_scrape_event_for_an_ipv4_client +torrust-tracker-udp-server handlers::scrape::tests::scrape_request::it_should_publish_an_accepted_scrape_event_for_an_ipv6_client +torrust-tracker-udp-server handlers::scrape::tests::scrape_request::it_should_return_an_entry_for_each_duplicate_requested_info_hash +torrust-tracker-udp-server handlers::scrape::tests::scrape_request::it_should_return_the_persisted_downloads_of_torrents_absent_from_memory_in_request_order +torrust-tracker-udp-server handlers::scrape::tests::scrape_request::it_should_return_zeroed_statistics_when_scrape_data_does_not_contain_a_requested_hash +torrust-tracker-udp-server handlers::scrape::tests::scrape_request::it_should_return_zeroed_statistics_when_the_tracker_does_not_have_the_requested_torrent +torrust-tracker-udp-server handlers::scrape::tests::scrape_request::with_a_public_tracker::it_should_return_statistics_when_the_public_tracker_has_the_requested_torrent +torrust-tracker-udp-server handlers::scrape::tests::scrape_request::with_a_whitelisted_tracker::it_should_return_statistics_when_the_listed_tracker_has_a_whitelisted_torrent +torrust-tracker-udp-server handlers::scrape::tests::scrape_request::with_a_whitelisted_tracker::it_should_return_zeroed_statistics_when_the_listed_tracker_has_not_whitelisted_the_torrent +torrust-tracker-udp-server handlers::tests::it_should_build_a_cookie_validation_range_around_the_issue_time +torrust-tracker-udp-server handlers::tests::it_should_dispatch_a_connect_request +torrust-tracker-udp-server handlers::tests::it_should_preserve_the_transaction_id_for_a_sendable_parse_error_without_a_request_kind +torrust-tracker-udp-server handlers::tests::it_should_scrape_only_the_first_74_info_hashes_of_a_payload_with_75 +torrust-tracker-udp-server handlers::tests::wire_i32_conversion::it_should_clamp_instead_of_wrapping_a_value_beyond_the_signed_wire_field +torrust-tracker-udp-server handlers::tests::wire_i32_conversion::it_should_keep_a_value_that_fits_in_the_signed_wire_field +torrust-tracker-udp-server handlers::tests::wire_i32_conversion::it_should_keep_the_largest_value_that_fits_in_the_signed_wire_field +torrust-tracker-udp-server::integration server::contract::publishing_the_banned_ips_total::with_disabled_connection_id_validation::should_not_publish_a_client_as_banned_before_it_exceeds_the_connection_id_error_limit +torrust-tracker-udp-server::integration server::contract::publishing_the_banned_ips_total::with_disabled_connection_id_validation::should_publish_a_client_that_exceeds_the_connection_id_error_limit_as_would_be_banned +torrust-tracker-udp-server::integration server::contract::publishing_the_banned_ips_total::with_strict_connection_id_validation::should_not_publish_a_client_as_banned_before_it_exceeds_the_connection_id_error_limit +torrust-tracker-udp-server::integration server::contract::publishing_the_banned_ips_total::with_strict_connection_id_validation::should_publish_a_client_as_banned_once_it_exceeds_the_connection_id_error_limit +torrust-tracker-udp-server::integration server::contract::receiving_a_connection_request::should_return_a_connect_response +torrust-tracker-udp-server::integration server::contract::receiving_an_announce_request::should_ban_the_client_ip_if_it_sends_more_than_10_requests_with_a_cookie_value_not_normal +torrust-tracker-udp-server::integration server::contract::receiving_an_announce_request::should_return_an_announce_response +torrust-tracker-udp-server::integration server::contract::receiving_an_announce_request::should_return_many_announce_response +torrust-tracker-udp-server::integration server::contract::receiving_an_scrape_request::should_return_a_scrape_response +torrust-tracker-udp-server::integration server::contract::receiving_an_scrape_request::should_return_only_the_first_74_torrent_stats_when_the_request_has_75_info_hashes +torrust-tracker-udp-server::integration server::contract::should_return_a_bad_request_response_when_the_client_sends_an_empty_request +torrust-tracker-udp-server::integration server::contract::using_disabled_connection_id_validation::announce_succeeds_with_an_arbitrary_connection_id +torrust-tracker-udp-server::integration server::contract::using_disabled_connection_id_validation::connect_still_issues_a_valid_connection_id +torrust-tracker-udp-server::integration server::contract::using_disabled_connection_id_validation::many_invalid_connection_ids_do_not_cause_ban_in_disabled_mode +torrust-tracker-udp-server::integration server::contract::using_disabled_connection_id_validation::scrape_succeeds_with_an_arbitrary_connection_id +torrust-tracker-udp-server::integration server::contract::using_ipv6_v6only::should_accept_ipv6_connections_with_ipv6_v6only_enabled +torrust-tracker-udp-server server::bound_socket::tests::it_should_bind_to_a_non_zero_port_when_port_zero_is_requested +torrust-tracker-udp-server server::bound_socket::tests::it_should_report_consistent_udp_endpoint_metadata +torrust-tracker-udp-server server::bound_socket::tests::it_should_restrict_an_ipv6_socket_to_ipv6_when_ipv6_v6only_is_requested +torrust-tracker-udp-server server::bound_socket::tests::it_should_return_an_error_when_the_address_is_already_in_use +torrust-tracker-udp-server server::launcher::tests::it_should_admit_a_request_from_a_banned_client_when_connection_id_validation_is_disabled +torrust-tracker-udp-server server::launcher::tests::it_should_admit_a_request_from_a_client_that_is_not_banned +torrust-tracker-udp-server server::launcher::tests::it_should_discard_a_request_from_a_banned_client_in_strict_mode +torrust-tracker-udp-server server::launcher::tests::it_should_discard_a_request_whose_source_port_is_zero +torrust-tracker-udp-server server::launcher::tests::it_should_publish_a_request_banned_event_when_its_client_ip_is_banned_in_strict_mode +torrust-tracker-udp-server server::launcher::tests::it_should_publish_a_request_discarded_event_when_its_source_port_is_zero +torrust-tracker-udp-server server::launcher::tests::it_should_release_the_socket_when_the_legacy_launcher_task_is_aborted +torrust-tracker-udp-server server::launcher::tests::it_should_release_the_socket_when_the_startup_notification_receiver_is_dropped +torrust-tracker-udp-server server::launcher::tests::it_should_report_a_broken_pipe_when_the_startup_notification_receiver_is_dropped +torrust-tracker-udp-server server::launcher::tests::it_should_stop_without_panicking_and_release_the_socket_when_the_legacy_halt_sender_is_dropped +torrust-tracker-udp-server server::launcher::tests::receive_loop_admission::it_should_admit_a_received_datagram +torrust-tracker-udp-server server::launcher::tests::receive_loop_admission::it_should_stop_with_an_unexpected_end_error_when_the_receive_stream_ends +torrust-tracker-udp-server server::launcher::tests::receive_loop_admission::it_should_stop_with_the_receive_error_when_receiving_fails +torrust-tracker-udp-server server::launcher::tests::receive_loop_admission::it_should_stop_with_the_receive_error_when_receiving_is_interrupted +torrust-tracker-udp-server server::launcher::tests::receive_loop_shutdown::it_should_let_an_accepted_request_finish_and_answer_before_returning_on_cancellation +torrust-tracker-udp-server server::launcher::tests::receive_loop_shutdown::it_should_not_return_while_processors_orphaned_by_the_request_ring_are_still_running +torrust-tracker-udp-server server::launcher::tests::request_drain::it_should_abort_and_join_processors_still_running_at_the_deadline +torrust-tracker-udp-server server::launcher::tests::request_drain::it_should_count_a_processor_aborted_before_the_drain_as_evicted +torrust-tracker-udp-server server::launcher::tests::request_drain::it_should_count_failed_processors_and_keep_draining_the_rest +torrust-tracker-udp-server server::launcher::tests::request_drain::it_should_count_processors_that_finish_before_the_deadline_as_completed +torrust-tracker-udp-server server::launcher::tests::request_drain::it_should_join_every_processor_before_returning_the_receive_error +torrust-tracker-udp-server server::launcher::tests::request_drain::it_should_name_the_listener_in_the_deadline_warning +torrust-tracker-udp-server server::launcher::tests::request_drain::it_should_return_immediately_when_there_are_no_processors +torrust-tracker-udp-server server::processor::tests::it_should_keep_the_request_kind_in_the_error_response_event_when_a_parsed_request_fails +torrust-tracker-udp-server server::processor::tests::it_should_publish_a_discard_event_when_a_client_uses_port_zero +torrust-tracker-udp-server server::processor::tests::it_should_publish_an_error_response_event_without_a_request_kind_when_the_payload_is_unparsable +torrust-tracker-udp-server server::processor::tests::it_should_publish_a_response_sent_event_when_it_answers_a_connect_request +torrust-tracker-udp-server server::processor::tests::it_should_return_a_send_error_when_the_response_cannot_be_sent +torrust-tracker-udp-server server::receiver::tests::it_should_yield_a_raw_request_with_the_received_datagram_and_sender_address +torrust-tracker-udp-server server::request_buffer::tests::it_should_abort_a_pending_task_when_the_request_buffer_is_dropped +torrust-tracker-udp-server server::request_buffer::tests::it_should_evict_the_oldest_pending_task_when_the_buffer_is_full +torrust-tracker-udp-server server::request_buffer::tests::it_should_not_evict_a_pending_task_when_the_buffer_has_available_capacity +torrust-tracker-udp-server server::states::tests::it_should_preserve_a_broken_pipe_launcher_error_when_startup_notification_fails +torrust-tracker-udp-server server::states::tests::it_should_return_a_server_failure_error_when_the_launcher_task_is_aborted +torrust-tracker-udp-server server::states::tests::it_should_return_a_startup_notification_error_when_the_launcher_finishes_successfully +torrust-tracker-udp-server server::tests::it_should_be_able_to_start_and_stop +torrust-tracker-udp-server server::tests::it_should_be_able_to_start_and_stop_with_wait +torrust-tracker-udp-server server::tests::it_should_preserve_registration_error_and_release_listener_when_registration_fails +torrust-tracker-udp-server server::tests::token_aware_start::it_should_register_the_service_with_a_health_check_that_reaches_the_running_server +torrust-tracker-udp-server server::tests::token_aware_start::it_should_release_the_socket_when_registration_fails +torrust-tracker-udp-server server::tests::token_aware_start::it_should_stop_the_receive_loop_and_release_the_socket_when_its_cancellation_token_is_cancelled +torrust-tracker-udp-server statistics::event::handler::error::tests::it_should_increase_the_udp4_errors_counter_when_it_receives_a_udp4_error_event +torrust-tracker-udp-server statistics::event::handler::error::tests::it_should_label_a_connection_id_error_metric_with_qbittorrent_client_software +torrust-tracker-udp-server statistics::event::handler::error::tests::it_should_label_a_general_error_metric_with_connect_request_kind +torrust-tracker-udp-server statistics::event::handler::request_aborted::tests::it_should_increase_the_number_of_aborted_requests_when_it_receives_a_udp_request_aborted_event +torrust-tracker-udp-server statistics::event::handler::request_accepted::tests::it_should_increase_the_udp4_announce_requests_counter_when_it_receives_a_udp4_request_event_of_announce_kind +torrust-tracker-udp-server statistics::event::handler::request_accepted::tests::it_should_increase_the_udp4_connect_requests_counter_when_it_receives_a_udp4_request_event_of_connect_kind +torrust-tracker-udp-server statistics::event::handler::request_accepted::tests::it_should_increase_the_udp4_scrape_requests_counter_when_it_receives_a_udp4_request_event_of_scrape_kind +torrust-tracker-udp-server statistics::event::handler::request_accepted::tests::it_should_increase_the_udp6_announce_requests_counter_when_it_receives_a_udp6_request_event_of_announce_kind +torrust-tracker-udp-server statistics::event::handler::request_accepted::tests::it_should_increase_the_udp6_connect_requests_counter_when_it_receives_a_udp6_request_event_of_connect_kind +torrust-tracker-udp-server statistics::event::handler::request_accepted::tests::it_should_increase_the_udp6_scrape_requests_counter_when_it_receives_a_udp6_request_event_of_scrape_kind +torrust-tracker-udp-server statistics::event::handler::request_banned::tests::it_should_increase_the_number_of_banned_requests_when_it_receives_a_udp_request_banned_event +torrust-tracker-udp-server statistics::event::handler::request_discarded::tests::it_should_increase_the_number_of_discarded_requests_when_it_receives_a_udp_request_discarded_event +torrust-tracker-udp-server statistics::event::handler::request_received::tests::it_should_increase_the_number_of_incoming_requests_when_it_receives_a_udp4_incoming_request_event +torrust-tracker-udp-server statistics::event::handler::response_sent::tests::it_should_increase_the_udp4_responses_counter_when_it_receives_a_udp4_response_event +torrust-tracker-udp-server statistics::event::handler::response_sent::tests::it_should_increase_the_udp6_response_counter_when_it_receives_a_udp6_response_event +torrust-tracker-udp-server statistics::event::handler::response_sent::tests::it_should_update_the_connect_processing_time_average_for_a_successful_connect_response +torrust-tracker-udp-server statistics::event::listener::tests::it_should_apply_the_metrics_policy_to_a_drained_event +torrust-tracker-udp-server statistics::event::listener::tests::it_should_handle_a_queued_event_then_return_cancelled_when_cancellation_is_already_pending +torrust-tracker-udp-server statistics::event::listener::tests::it_should_leave_the_queued_event_unprocessed_and_return_cancelled_when_the_drain_timeout_has_expired +torrust-tracker-udp-server statistics::event::listener::tests::it_should_process_enabled_events_after_lagging +torrust-tracker-udp-server statistics::event::listener::tests::it_should_stop_when_the_receiver_is_closed +torrust-tracker-udp-server statistics::event::listener::tests::it_should_update_metrics_only_for_an_enabled_configuration_instance +torrust-tracker-udp-server statistics::metrics::tests::averaged_processing_time_metrics::it_should_handle_fractional_averages_with_truncation +torrust-tracker-udp-server statistics::metrics::tests::averaged_processing_time_metrics::it_should_handle_single_server_averaged_metrics +torrust-tracker-udp-server statistics::metrics::tests::averaged_processing_time_metrics::it_should_only_average_matching_request_kinds +torrust-tracker-udp-server statistics::metrics::tests::averaged_processing_time_metrics::it_should_return_averaged_value_for_udp_avg_announce_processing_time_ns_averaged +torrust-tracker-udp-server statistics::metrics::tests::averaged_processing_time_metrics::it_should_return_averaged_value_for_udp_avg_connect_processing_time_ns_averaged +torrust-tracker-udp-server statistics::metrics::tests::averaged_processing_time_metrics::it_should_return_averaged_value_for_udp_avg_scrape_processing_time_ns_averaged +torrust-tracker-udp-server statistics::metrics::tests::averaged_processing_time_metrics::it_should_return_zero_for_udp_avg_announce_processing_time_ns_averaged_when_no_data +torrust-tracker-udp-server statistics::metrics::tests::averaged_processing_time_metrics::it_should_return_zero_for_udp_avg_connect_processing_time_ns_averaged_when_no_data +torrust-tracker-udp-server statistics::metrics::tests::averaged_processing_time_metrics::it_should_return_zero_for_udp_avg_scrape_processing_time_ns_averaged_when_no_data +torrust-tracker-udp-server statistics::metrics::tests::combined_metrics::it_should_distinguish_between_different_request_kinds +torrust-tracker-udp-server statistics::metrics::tests::combined_metrics::it_should_distinguish_between_ipv4_and_ipv6_metrics +torrust-tracker-udp-server statistics::metrics::tests::combined_metrics::it_should_handle_mixed_ipv4_and_ipv6_for_different_request_kinds +torrust-tracker-udp-server statistics::metrics::tests::edge_cases::it_should_handle_empty_label_sets +torrust-tracker-udp-server statistics::metrics::tests::edge_cases::it_should_handle_large_counter_values +torrust-tracker-udp-server statistics::metrics::tests::edge_cases::it_should_handle_large_gauge_values +torrust-tracker-udp-server statistics::metrics::tests::edge_cases::it_should_handle_multiple_labels_on_same_metric +torrust-tracker-udp-server statistics::metrics::tests::edge_cases::it_should_handle_zero_gauge_values +torrust-tracker-udp-server statistics::metrics::tests::edge_cases::it_should_overwrite_gauge_values_when_set_multiple_times +torrust-tracker-udp-server statistics::metrics::tests::error_handling::it_should_handle_unknown_metric_names_gracefully +torrust-tracker-udp-server statistics::metrics::tests::error_handling::it_should_return_ok_result_for_valid_counter_operations +torrust-tracker-udp-server statistics::metrics::tests::error_handling::it_should_return_ok_result_for_valid_gauge_operations +torrust-tracker-udp-server statistics::metrics::tests::it_should_implement_debug +torrust-tracker-udp-server statistics::metrics::tests::it_should_implement_default +torrust-tracker-udp-server statistics::metrics::tests::it_should_implement_partial_eq +torrust-tracker-udp-server statistics::metrics::tests::it_should_increase_counter_metric +torrust-tracker-udp-server statistics::metrics::tests::it_should_increase_counter_metric_with_labels +torrust-tracker-udp-server statistics::metrics::tests::it_should_increment_processed_requests_total +torrust-tracker-udp-server statistics::metrics::tests::it_should_return_zero_for_udp_processed_requests_total_when_no_data +torrust-tracker-udp-server statistics::metrics::tests::it_should_set_gauge_metric +torrust-tracker-udp-server statistics::metrics::tests::it_should_set_gauge_metric_with_labels +torrust-tracker-udp-server statistics::metrics::tests::processing_time_moving_average::it_should_keep_the_fractional_part_of_the_previous_average +torrust-tracker-udp-server statistics::metrics::tests::processing_time_moving_average::it_should_not_drift_toward_the_fastest_requests_over_many_requests +torrust-tracker-udp-server statistics::metrics::tests::udp_general_metrics::it_should_return_exactly_a_udp_requests_aborted_total_that_an_f64_cannot_represent +torrust-tracker-udp-server statistics::metrics::tests::udp_general_metrics::it_should_return_gauge_value_for_udp_banned_ips_total +torrust-tracker-udp-server statistics::metrics::tests::udp_general_metrics::it_should_return_sum_of_udp_requests_aborted +torrust-tracker-udp-server statistics::metrics::tests::udp_general_metrics::it_should_return_sum_of_udp_requests_banned +torrust-tracker-udp-server statistics::metrics::tests::udp_general_metrics::it_should_return_zero_for_udp_banned_ips_total_when_no_data +torrust-tracker-udp-server statistics::metrics::tests::udp_general_metrics::it_should_return_zero_for_udp_requests_aborted_when_no_data +torrust-tracker-udp-server statistics::metrics::tests::udp_general_metrics::it_should_return_zero_for_udp_requests_banned_when_no_data +torrust-tracker-udp-server statistics::metrics::tests::udpv4_metrics::it_should_return_sum_of_udp4_announces_handled +torrust-tracker-udp-server statistics::metrics::tests::udpv4_metrics::it_should_return_sum_of_udp4_connections_handled +torrust-tracker-udp-server statistics::metrics::tests::udpv4_metrics::it_should_return_sum_of_udp4_errors_handled +torrust-tracker-udp-server statistics::metrics::tests::udpv4_metrics::it_should_return_sum_of_udp4_requests +torrust-tracker-udp-server statistics::metrics::tests::udpv4_metrics::it_should_return_sum_of_udp4_responses +torrust-tracker-udp-server statistics::metrics::tests::udpv4_metrics::it_should_return_sum_of_udp4_scrapes_handled +torrust-tracker-udp-server statistics::metrics::tests::udpv4_metrics::it_should_return_zero_for_udp4_announces_handled_when_no_data +torrust-tracker-udp-server statistics::metrics::tests::udpv4_metrics::it_should_return_zero_for_udp4_connections_handled_when_no_data +torrust-tracker-udp-server statistics::metrics::tests::udpv4_metrics::it_should_return_zero_for_udp4_errors_handled_when_no_data +torrust-tracker-udp-server statistics::metrics::tests::udpv4_metrics::it_should_return_zero_for_udp4_requests_when_no_data +torrust-tracker-udp-server statistics::metrics::tests::udpv4_metrics::it_should_return_zero_for_udp4_responses_when_no_data +torrust-tracker-udp-server statistics::metrics::tests::udpv4_metrics::it_should_return_zero_for_udp4_scrapes_handled_when_no_data +torrust-tracker-udp-server statistics::metrics::tests::udpv6_metrics::it_should_return_sum_of_udp6_announces_handled +torrust-tracker-udp-server statistics::metrics::tests::udpv6_metrics::it_should_return_sum_of_udp6_connections_handled +torrust-tracker-udp-server statistics::metrics::tests::udpv6_metrics::it_should_return_sum_of_udp6_errors_handled +torrust-tracker-udp-server statistics::metrics::tests::udpv6_metrics::it_should_return_sum_of_udp6_requests +torrust-tracker-udp-server statistics::metrics::tests::udpv6_metrics::it_should_return_sum_of_udp6_responses +torrust-tracker-udp-server statistics::metrics::tests::udpv6_metrics::it_should_return_sum_of_udp6_scrapes_handled +torrust-tracker-udp-server statistics::metrics::tests::udpv6_metrics::it_should_return_zero_for_udp6_announces_handled_when_no_data +torrust-tracker-udp-server statistics::metrics::tests::udpv6_metrics::it_should_return_zero_for_udp6_connections_handled_when_no_data +torrust-tracker-udp-server statistics::metrics::tests::udpv6_metrics::it_should_return_zero_for_udp6_errors_handled_when_no_data +torrust-tracker-udp-server statistics::metrics::tests::udpv6_metrics::it_should_return_zero_for_udp6_requests_when_no_data +torrust-tracker-udp-server statistics::metrics::tests::udpv6_metrics::it_should_return_zero_for_udp6_responses_when_no_data +torrust-tracker-udp-server statistics::metrics::tests::udpv6_metrics::it_should_return_zero_for_udp6_scrapes_handled_when_no_data +torrust-tracker-udp-server statistics::repository::tests::it_should_allow_increasing_a_counter_metric_successfully +torrust-tracker-udp-server statistics::repository::tests::it_should_allow_increasing_a_counter_multiple_times +torrust-tracker-udp-server statistics::repository::tests::it_should_allow_increasing_a_counter_with_different_labels +torrust-tracker-udp-server statistics::repository::tests::it_should_allow_setting_a_gauge_with_different_labels +torrust-tracker-udp-server statistics::repository::tests::it_should_be_initialized_with_described_metrics +torrust-tracker-udp-server statistics::repository::tests::it_should_handle_concurrent_access +torrust-tracker-udp-server statistics::repository::tests::it_should_handle_error_cases_gracefully +torrust-tracker-udp-server statistics::repository::tests::it_should_handle_large_processing_times +torrust-tracker-udp-server statistics::repository::tests::it_should_initialize_metrics_when_constructed_with_default +torrust-tracker-udp-server statistics::repository::tests::it_should_maintain_consistency_across_operations +torrust-tracker-udp-server statistics::repository::tests::it_should_overwrite_previous_value_when_setting_a_gauge_with_a_previous_value +torrust-tracker-udp-server statistics::repository::tests::it_should_recalculate_separate_moving_averages_for_each_request_kind +torrust-tracker-udp-server statistics::repository::tests::it_should_run_the_closure_on_the_metrics_and_return_its_result +torrust-tracker-udp-server statistics::repository::tests::it_should_set_a_gauge_metric_successfully +torrust-tracker-udp-server statistics::repository::tests::race_conditions::it_should_handle_race_conditions_when_updating_udp_performance_metrics_in_parallel +torrust-tracker-udp-server statistics::repository::tests::recalculate_average_methods_should_handle_zero_connections_gracefully +torrust-tracker-udp-server testing::environment::tests::it_should_cancel_the_event_listeners_only_after_the_receive_loop_has_finished +torrust-tracker-udp-server testing::environment::tests::it_should_cancel_the_event_listeners_when_the_join_is_abandoned_while_the_receive_loop_runs +torrust-tracker-udp-server testing::environment::tests::it_should_count_the_statistics_event_of_a_request_that_is_published_while_stop_drains_the_receive_loop +torrust-tracker-udp-server testing::environment::tests::it_should_count_the_statistics_events_of_a_request_answered_just_before_stop +torrust-tracker-udp-server testing::environment::tests::it_should_join_every_event_listener_before_reporting_a_receive_loop_failure +torrust-tracker-udp-server testing::environment::tests::it_should_map_disabled_connection_id_validation_policy +torrust-tracker-udp-server testing::environment::tests::it_should_map_strict_connection_id_validation_policy +torrust-tracker-udp-server testing::environment::tests::it_should_not_leave_event_listeners_running_when_the_udp_server_fails_to_start +torrust-tracker-udp-server testing::environment::tests::it_should_release_the_udp_socket_when_dropped_without_being_stopped +torrust-tracker-udp-server testing::environment::tests::it_should_release_the_udp_socket_when_stopped +torrust-tracker-udp-server testing::environment::tests::it_should_serve_requests_after_being_stopped_and_started_again +torrust-tracker-udp-server testing::environment::tests::it_should_stop_the_event_listeners_when_dropped_without_being_stopped +torrust-tracker-udp-server testing::environment::tests::it_should_stop_without_a_join_failure_when_every_owned_task_finishes_through_cancellation diff --git a/docs/issues/open/2670-2482-build-shipped-artifacts-without-test-features/manual-verification-evidence.md b/docs/issues/open/2670-2482-build-shipped-artifacts-without-test-features/manual-verification-evidence.md new file mode 100644 index 000000000..de153b9f7 --- /dev/null +++ b/docs/issues/open/2670-2482-build-shipped-artifacts-without-test-features/manual-verification-evidence.md @@ -0,0 +1,75 @@ +--- +doc-type: manual-verification-evidence +issue-spec: docs/issues/open/2670-2482-build-shipped-artifacts-without-test-features/ISSUE.md +last-updated-utc: "2026-10-27 12:43" +--- + +# Manual Verification Evidence + +## Purpose + +Record the commands run to verify the scenarios of the [spec](ISSUE.md) and the test-design reviews of +its test-producing tasks. Every output below is the real output of the command shown; paths under the +home directory are replaced by `~`. + +## Environment and Prerequisites + +- Host: Linux, 32 CPUs, 61 GiB of memory, Docker 28.3.3 (BuildKit 0.23.2, `docker` driver). +- Baseline tree: `develop` at the merge commit of PR #2671 (`docs(issues): [#2670] specify building shipped + artifacts without test features`), in a throwaway worktree, before any change of this subissue. +- A `/tmp` cleanup removed the first baseline attempt's worktree and logs while it was building; the + attempt exited with code 1 and its numbers are not used. The runs below were redone with the worktree and + logs under `~/.cache/r9-2670/`. + +## V0 - Audit of the commands that build shipped artifacts (task T1) + +Re-verified on `develop` at the merge commit of PR #2671. `git diff --stat` of `Containerfile`, +`.dockerignore`, `Cargo.toml`, `docs/containers.md` and `contrib/dev-tools/containers` between the commit the +spec was audited on and this baseline shows no change; the workflow diffs between the two are action version +bumps only. The audit table of the spec therefore holds. Command: + +```bash +grep -rnE "all-features|all-targets|--tests|--examples|--benches" Containerfile \ + .github/workflows/{container,deployment,deployment-packages,testing,os-compatibility,security-scan}.yaml \ + contrib/dev-tools/containers/*.sh +``` + +Output, reduced to file, line and the flags (the `Containerfile` lines are the cook and archive commands of the +spec's table): + +```text +Containerfile:85,96,108,116 cargo chef cook --tests --workspace --all-features (third-party and full recipe, debug and release) +Containerfile:100,120 cargo nextest archive --tests --all-features (discarded warm-ups, debug and release) +Containerfile:128 cargo nextest archive --tests --all-features (Build Archive (debug)) - ships torrust-tracker +Containerfile:135 cargo nextest archive --tests --all-features --release (Build Archive (release)) - ships both binaries +.github/workflows/deployment.yaml:40 cargo test --tests --benches --examples --workspace --all-targets --all-features +.github/workflows/deployment-packages.yaml:122 cargo test -p --all-targets --all-features +.github/workflows/testing.yaml:110 cargo doc --no-deps --lib --workspace --all-features +.github/workflows/testing.yaml:114 cargo test --tests --benches --examples --workspace --all-targets --all-features +``` + +The two additions to the spec's table are both `testing.yaml` jobs that build documentation or run tests and +publish nothing, so they are outside the shipped-artifact scope. `container.yaml`, `security-scan.yaml` and +`contrib/dev-tools/containers/docker-build.sh` pass no feature flag of their own: they run `docker build`, so +they inherit the `Containerfile`. Only `Containerfile:128` and `:135` produce a shipped binary, as the spec +says. + +## V1 - Baseline before the change (scenario M1, task T1) + +Commands (run from the baseline worktree): + +```bash +docker build --no-cache --progress=plain --target release --tag r9-baseline:release --file Containerfile . +docker build --no-cache --progress=plain --target debug --tag r9-baseline:debug --file Containerfile . +# one line appended to src/lib.rs, then a rebuild with the layer cache kept +docker build --progress=plain --target release --tag r9-baseline:release --file Containerfile . +``` + +| Build | Started (UTC, 2026-10-27) | Finished (UTC) | Wall time | Tests run by the `test` stage | +| --- | --- | --- | --- | --- | +| `release`, cold (`--no-cache`) | 12:01:49 | 12:06:48 | 4 min 59 s | 1531 passed, 0 skipped, 40 binaries | +| `debug`, cold (`--no-cache`) | 12:07:01 | 12:08:56 | 1 min 55 s | 1531 passed, 0 skipped, 40 binaries | +| `release`, warm (one source line changed) | 12:09:02 | 12:11:56 | 2 min 53 s | 1531 passed, 0 skipped, 40 binaries | + +The `release` test list, sorted, is in [`baseline-test-list.txt`](baseline-test-list.txt) (1531 lines, one +` ` per line). The later comparison checks that every line is still present. From e53369639c45fed19221a617b15716577d70433d Mon Sep 17 00:00:00 2001 From: Jose Celano Date: Tue, 27 Oct 2026 12:44:59 +0000 Subject: [PATCH 02/12] test(tracker): [#2670] let the native-tracker tests run a shipped binary tracker_binary() now reads TORRUST_TRACKER_BINARY before the variables nextest and Cargo set, so the container test stages can point the black-box tests at the separately built shipped binary. Without the variable the resolution is unchanged. Three unit tests cover the order, and the test-design review and the red and green runs are recorded in the evidence. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../ISSUE.md | 5 +- .../manual-verification-evidence.md | 31 +++++++- tests/common/native_tracker/command.rs | 78 +++++++++++++++++-- 3 files changed, 106 insertions(+), 8 deletions(-) diff --git a/docs/issues/open/2670-2482-build-shipped-artifacts-without-test-features/ISSUE.md b/docs/issues/open/2670-2482-build-shipped-artifacts-without-test-features/ISSUE.md index b34b9e595..f53e01428 100644 --- a/docs/issues/open/2670-2482-build-shipped-artifacts-without-test-features/ISSUE.md +++ b/docs/issues/open/2670-2482-build-shipped-artifacts-without-test-features/ISSUE.md @@ -9,7 +9,7 @@ github-issue: 2670 spec-path: docs/issues/open/2670-2482-build-shipped-artifacts-without-test-features/ISSUE.md branch: "2670-2482-build-shipped-artifacts-without-test-features" related-pr: null -last-updated-utc: "2026-10-27 12:43" +last-updated-utc: "2026-10-27 12:44" semantic-links: skill-links: - create-issue @@ -267,7 +267,7 @@ Status values: `TODO`, `IN_PROGRESS`, `BLOCKED`, `DONE`. | ID | Status | Task | Notes / Expected Output | | --- | --- | --- | --- | | T1 | DONE | Re-verify the audit table against `develop`; record a baseline: the list and count of the tests the image's `test` stage runs, and cold and warm build times | Table and baseline in the evidence (V0, V1); the list lets the later comparison show that every baseline test is still present | -| T2 | TODO | Add `TORRUST_TRACKER_BINARY` to `tracker_binary()`, test first | Unit tests for the resolution order; default behaviour unchanged | +| T2 | DONE | Add `TORRUST_TRACKER_BINARY` to `tracker_binary()`, test first | Unit tests for the resolution order; default behaviour unchanged | | T3 | TODO | Create the check crate, test first | Parser tests from real `cargo tree` output; scratch-workspace red and green tests; registered as a workspace member and admitted in `.dockerignore`; `cargo machete` and `cargo deny` pass | | T4 | TODO | Add the check as a job in `testing.yaml` | Job runs the check for `torrust-tracker`; follows `implement-workflow` | | T5 | TODO | Add the shipped cook and build stages for the release and debug images; point `test` and `test_debug` at the shipped binary; take `/app/bin` from it | Both targets build; every baseline test is still present and passing, and the delta for new tests (T2 adds some, compiled into the targets that include `tests/common/native_tracker`) is recorded; times recorded | @@ -318,6 +318,7 @@ Status values: `TODO`, `IN_PROGRESS`, `BLOCKED`, `DONE`. - 2026-10-27 09:05 UTC - AI assistant (Copilot SDK in VS Code, #2482 subissue 9) - PR #2671 review round 1, F2: T1 now records the list and count of the baseline tests, and T5, AC2 and M2 require every baseline test to remain present and passing with the delta for new tests recorded, instead of an equal count (T2's tests are compiled into the targets that include `tests/common/native_tracker`). - 2026-10-27 09:06 UTC - AI assistant (Copilot SDK in VS Code, #2482 subissue 9) - PR #2671 review round 1, F3: scenario M1 now builds both targets with `--file Containerfile`, as `docs/containers.md` does, because the repository root has no `Dockerfile`. - 2026-10-27 12:43 UTC - AI assistant (Copilot SDK in VS Code, #2482 subissue 9) - Started the implementation. T1 is done: the audit table still holds on `develop` after PR #2671 (V0), and the baseline is 1531 tests in 40 binaries with a cold `release` build of 4 min 59 s, a cold `debug` build of 1 min 55 s and a warm `release` rebuild of 2 min 53 s (V1, list in `baseline-test-list.txt`). +- 2026-10-27 12:44 UTC - AI assistant (Copilot SDK in VS Code, #2482 subissue 9) - T2 is done: `tracker_binary()` reads `TORRUST_TRACKER_BINARY` before the nextest and Cargo variables, with three unit tests (red with the override discarded, green on the `lifecycle-signals` and `cli-configuration` targets). The test-design review is recorded in the evidence; the maintainer (josecelano) reviewed the increment and approved it through `ask_user` before the commit. ## Acceptance Criteria diff --git a/docs/issues/open/2670-2482-build-shipped-artifacts-without-test-features/manual-verification-evidence.md b/docs/issues/open/2670-2482-build-shipped-artifacts-without-test-features/manual-verification-evidence.md index de153b9f7..9cfcb0357 100644 --- a/docs/issues/open/2670-2482-build-shipped-artifacts-without-test-features/manual-verification-evidence.md +++ b/docs/issues/open/2670-2482-build-shipped-artifacts-without-test-features/manual-verification-evidence.md @@ -1,7 +1,7 @@ --- doc-type: manual-verification-evidence issue-spec: docs/issues/open/2670-2482-build-shipped-artifacts-without-test-features/ISSUE.md -last-updated-utc: "2026-10-27 12:43" +last-updated-utc: "2026-10-27 12:44" --- # Manual Verification Evidence @@ -73,3 +73,32 @@ docker build --progress=plain --target release --tag r9-baseline:release --file The `release` test list, sorted, is in [`baseline-test-list.txt`](baseline-test-list.txt) (1531 lines, one ` ` per line). The later comparison checks that every line is still present. + +## T2 - Test-design review of the `TORRUST_TRACKER_BINARY` tests + +Change: `tracker_binary()` in `tests/common/native_tracker/command.rs` delegates to +`resolve_tracker_binary(variable, compiled)`, which reads `TORRUST_TRACKER_BINARY`, then +`NEXTEST_BIN_EXE_torrust-tracker`, then `CARGO_BIN_EXE_torrust-tracker`, then the path compiled into the test. +The three unit tests pass the variables as a slice of pairs, so no test touches the process environment. + +Prose-first Arrange-Act-Assert comparison, written before the code was refactored to express it: + +| Test | Arrange (the one causal initial-state difference) | Act | Assert (independently specified result) | +| --- | --- | --- | --- | +| `it_should_run_the_binary_named_by_the_shipped_binary_override_before_the_one_nextest_or_cargo_built` | The override variable is set alongside the nextest and Cargo variables | Resolve the binary | The override's path wins | +| `it_should_run_the_binary_nextest_built_when_no_override_is_set` | Only the nextest and Cargo variables are set | Resolve the binary | The nextest path wins (today's behaviour is unchanged) | +| `it_should_run_the_binary_compiled_into_the_test_when_no_variable_is_set` | No variable is set | Resolve the binary | The compiled-in path is returned | + +Review result: each test differs from the previous one by exactly one fact (the override present, then +absent with the nextest variable present, then every variable absent). The fixture is the `lookup_in` helper, +which only turns a slice of pairs into a lookup function (incidental mechanics). The production Act +(`resolve_tracker_binary`) and the expected path stay visible in each test. The helper is used by all three +tests and hides no decision. No redundant prose was left in the code. + +Red and green runs (Cargo 1.99.1, `cargo test -p torrust-tracker --test lifecycle-signals it_should_run_the_binary`): + +- Red: with the override read but its value discarded (`variable(SHIPPED_BINARY_VARIABLE).filter(|_| false)`, + a scratch change that was reverted), the override test fails with `left: "/nextest/torrust-tracker"`, + `right: "/shipped/torrust-tracker"` and the other two pass: `2 passed; 1 failed`. +- Green: with the change, `lifecycle-signals` reports `16 passed` and `cli-configuration` reports + `21 passed`. The three new tests compile into both targets, so the image's test count rises by 6. diff --git a/tests/common/native_tracker/command.rs b/tests/common/native_tracker/command.rs index eb627aac8..aa7245694 100644 --- a/tests/common/native_tracker/command.rs +++ b/tests/common/native_tracker/command.rs @@ -2,6 +2,7 @@ //! //! This module does not own child processes or make running or failed-start lifecycle decisions. +use std::ffi::OsString; use std::net::SocketAddr; use std::path::{Path, PathBuf}; use std::process::Stdio; @@ -209,19 +210,86 @@ fn write_configuration_at(config_path: &Path, storage_path: &Path, health_check_ std::fs::write(config_path, config).expect("write tracker configuration"); } +/// Environment variable naming the tracker executable to run instead of the one Cargo built. +/// +/// The container's `test` stages set it to the separately built shipped binary, so the black-box +/// tests exercise the executable the image ships. It is read before the variables nextest and Cargo +/// set, which cannot carry an override. +const SHIPPED_BINARY_VARIABLE: &str = "TORRUST_TRACKER_BINARY"; + pub(super) fn tracker_binary() -> PathBuf { - std::env::var_os("NEXTEST_BIN_EXE_torrust-tracker") - .or_else(|| std::env::var_os("CARGO_BIN_EXE_torrust-tracker")) - .map_or_else(|| PathBuf::from(env!("CARGO_BIN_EXE_torrust-tracker")), PathBuf::from) + resolve_tracker_binary( + |name| std::env::var_os(name), + PathBuf::from(env!("CARGO_BIN_EXE_torrust-tracker")), + ) +} + +fn resolve_tracker_binary(variable: impl Fn(&str) -> Option, compiled: PathBuf) -> PathBuf { + variable(SHIPPED_BINARY_VARIABLE) + .or_else(|| variable("NEXTEST_BIN_EXE_torrust-tracker")) + .or_else(|| variable("CARGO_BIN_EXE_torrust-tracker")) + .map_or(compiled, PathBuf::from) } #[cfg(test)] mod tests { use std::ffi::{OsStr, OsString}; use std::net::{IpAddr, Ipv4Addr, SocketAddr}; - use std::path::Path; + use std::path::{Path, PathBuf}; + + use super::{resolve_tracker_binary, tracker_command, write_configuration}; + + fn lookup_in<'a>(variables: &'a [(&'a str, &'a str)]) -> impl Fn(&str) -> Option + 'a { + move |name| { + variables + .iter() + .find(|(variable, _)| *variable == name) + .map(|(_, value)| OsString::from(*value)) + } + } + + #[test] + fn it_should_run_the_binary_named_by_the_shipped_binary_override_before_the_one_nextest_or_cargo_built() { + // Arrange + let variables = [ + ("TORRUST_TRACKER_BINARY", "/shipped/torrust-tracker"), + ("NEXTEST_BIN_EXE_torrust-tracker", "/nextest/torrust-tracker"), + ("CARGO_BIN_EXE_torrust-tracker", "/cargo/torrust-tracker"), + ]; + + // Act + let binary = resolve_tracker_binary(lookup_in(&variables), PathBuf::from("/compiled/torrust-tracker")); + + // Assert + assert_eq!(binary, PathBuf::from("/shipped/torrust-tracker")); + } - use super::{tracker_command, write_configuration}; + #[test] + fn it_should_run_the_binary_nextest_built_when_no_override_is_set() { + // Arrange + let variables = [ + ("NEXTEST_BIN_EXE_torrust-tracker", "/nextest/torrust-tracker"), + ("CARGO_BIN_EXE_torrust-tracker", "/cargo/torrust-tracker"), + ]; + + // Act + let binary = resolve_tracker_binary(lookup_in(&variables), PathBuf::from("/compiled/torrust-tracker")); + + // Assert + assert_eq!(binary, PathBuf::from("/nextest/torrust-tracker")); + } + + #[test] + fn it_should_run_the_binary_compiled_into_the_test_when_no_variable_is_set() { + // Arrange + let variables: [(&str, &str); 0] = []; + + // Act + let binary = resolve_tracker_binary(lookup_in(&variables), PathBuf::from("/compiled/torrust-tracker")); + + // Assert + assert_eq!(binary, PathBuf::from("/compiled/torrust-tracker")); + } #[test] fn it_should_select_its_configuration_with_the_cli_and_remove_legacy_base_source_variables() { From cededdd41c643890e93b478268ff64d13bbbd5fc Mon Sep 17 00:00:00 2001 From: Jose Celano Date: Tue, 27 Oct 2026 14:56:28 +0000 Subject: [PATCH 03/12] feat(shipped-features-check): [#2670] fail when a testing feature reaches a shipped package Add the shipped-features-check developer tool. It runs cargo tree for the edges and features of a shipped package's build (normal and build dependencies, without dev-dependencies, which cargo metadata cannot separate) and fails when a workspace package has the testing feature enabled in that tree. It follows the global CLI output contract and is tested against scratch workspaces with the real cargo tree, because no real crate has a testing feature yet. The crate is a workspace member and not a default member, is admitted in .dockerignore and is listed in the AGENTS.md catalogue and the CLI contract ADR. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .dockerignore | 1 + AGENTS.md | 1 + Cargo.lock | 9 + Cargo.toml | 1 + .../checks/shipped-features-check/Cargo.toml | 19 ++ .../checks/shipped-features-check/src/lib.rs | 215 ++++++++++++++++++ .../checks/shipped-features-check/src/main.rs | 202 ++++++++++++++++ .../shipped-features-check/tests/cli.rs | 200 ++++++++++++++++ ...00000_define_global_cli_output_contract.md | 1 + .../ISSUE.md | 9 +- .../manual-verification-evidence.md | 117 +++++++++- 11 files changed, 770 insertions(+), 5 deletions(-) create mode 100644 contrib/dev-tools/checks/shipped-features-check/Cargo.toml create mode 100644 contrib/dev-tools/checks/shipped-features-check/src/lib.rs create mode 100644 contrib/dev-tools/checks/shipped-features-check/src/main.rs create mode 100644 contrib/dev-tools/checks/shipped-features-check/tests/cli.rs diff --git a/.dockerignore b/.dockerignore index bb8615d54..e0b9acd59 100644 --- a/.dockerignore +++ b/.dockerignore @@ -42,6 +42,7 @@ !/contrib/dev-tools/checks/clippy-allow-reasons/ !/contrib/dev-tools/checks/frontmatter-validator/ !/contrib/dev-tools/checks/package-coverage-check/ +!/contrib/dev-tools/checks/shipped-features-check/ !/contrib/dev-tools/github/github-review-threads/ # ── Re-exclusions inside admitted directories ───────────────────────────────── diff --git a/AGENTS.md b/AGENTS.md index 2e5d40d37..3e4636f18 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -118,6 +118,7 @@ package's `Cargo.toml` holds its version. Rows marked _(unpublished)_ set `publi | `checks/clippy-allow-reasons` | `clippy-allow-reasons` | Validates documented Clippy allow attributes | | `checks/frontmatter-validator` | `frontmatter-validator` | Validates Markdown frontmatter | | `checks/package-coverage-check` | `package-coverage-check` | Checks changed workspace packages for coverage regressions | +| `checks/shipped-features-check` | `shipped-features-check` | Fails if a `testing` feature reaches a shipped binary | | `github/github-review-threads` | `github-review-threads` | Fetches pull-request review threads through the GitHub CLI | **Community contributions** (under `contrib/`): diff --git a/Cargo.lock b/Cargo.lock index 14870cbaa..d3814e0aa 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4042,6 +4042,15 @@ dependencies = [ "lazy_static", ] +[[package]] +name = "shipped-features-check" +version = "0.1.0" +dependencies = [ + "serde", + "serde_json", + "tempfile", +] + [[package]] name = "shlex" version = "2.0.1" diff --git a/Cargo.toml b/Cargo.toml index 1c5eadabb..1f647a231 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -147,6 +147,7 @@ members = [ "contrib/dev-tools/checks/clippy-allow-reasons", "contrib/dev-tools/checks/frontmatter-validator", "contrib/dev-tools/checks/package-coverage-check", + "contrib/dev-tools/checks/shipped-features-check", "contrib/dev-tools/github/github-review-threads", "packages/axum-health-check-api-server", "packages/axum-http-server", diff --git a/contrib/dev-tools/checks/shipped-features-check/Cargo.toml b/contrib/dev-tools/checks/shipped-features-check/Cargo.toml new file mode 100644 index 000000000..0e96558a4 --- /dev/null +++ b/contrib/dev-tools/checks/shipped-features-check/Cargo.toml @@ -0,0 +1,19 @@ +[package] +description = "Fails when a workspace `testing` feature is enabled in the dependency tree of a shipped package." +name = "shipped-features-check" +publish = false + +authors.workspace = true +edition.workspace = true +rust-version.workspace = true +version = "0.1.0" + +[lints] +workspace = true + +[dependencies] +serde = { version = "1", features = [ "derive" ] } +serde_json = "1" + +[dev-dependencies] +tempfile = "3" diff --git a/contrib/dev-tools/checks/shipped-features-check/src/lib.rs b/contrib/dev-tools/checks/shipped-features-check/src/lib.rs new file mode 100644 index 000000000..490ad9f2b --- /dev/null +++ b/contrib/dev-tools/checks/shipped-features-check/src/lib.rs @@ -0,0 +1,215 @@ +//! Finds enabled features of workspace packages in the output of `cargo tree`. +//! +//! The input is the output of +//! `cargo tree -p -e normal,build --prefix none -f "{p}|{f}"`: one package per +//! line, ` v[ ()][ (proc-macro)]|[ (*)]`. That command resolves +//! features for the selected package the way `cargo build -p ` does, without the +//! dev-dependencies, which `cargo metadata` cannot do. + +use std::collections::BTreeSet; + +/// A workspace package that has the checked feature enabled. +#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)] +pub struct Violation { + /// The package name. + pub package: String, + /// The package version. + pub version: String, + /// The enabled feature that is not allowed. + pub feature: String, +} + +/// Returns the workspace packages of `tree` that have `feature` enabled, each once and in order. +/// +/// A package is a workspace package when its line carries a local path. Registry and Git +/// dependencies are not checked: a third-party crate's feature is outside the repository's rules. +#[must_use] +pub fn workspace_packages_with_feature(tree: &str, feature: &str) -> Vec { + let violations: BTreeSet = tree + .lines() + .filter_map(|line| { + let (package, features) = line.split_once('|')?; + let (name, version, location) = split_package(package)?; + (is_local_path(location) && has_feature(features, feature)).then(|| Violation { + package: name.to_owned(), + version: version.to_owned(), + feature: feature.to_owned(), + }) + }) + .collect(); + + violations.into_iter().collect() +} + +/// Splits ` v[ ()][ (proc-macro)]` into its name, version and location. +/// +/// The location is the text inside the parentheses that follow the version, or an empty string when +/// the line has none (a registry package) or only the `(proc-macro)` marker. +fn split_package(package: &str) -> Option<(&str, &str, &str)> { + let (name, rest) = package.trim().split_once(' ')?; + let (version, rest) = rest.split_once(' ').unwrap_or((rest, "")); + let rest = rest.trim_end().strip_suffix("(proc-macro)").unwrap_or(rest).trim(); + let location = rest.strip_prefix('(').and_then(|rest| rest.strip_suffix(')')).unwrap_or(""); + + version.starts_with('v').then_some((name, version, location)) +} + +/// Whether `location` is a file system path, as opposed to the URL of a Git dependency. +fn is_local_path(location: &str) -> bool { + let mut characters = location.chars(); + let windows_drive = matches!( + (characters.next(), characters.next(), characters.next()), + (Some(letter), Some(':'), Some('\\' | '/')) if letter.is_ascii_alphabetic() + ); + + location.starts_with('/') || windows_drive +} + +/// Whether `feature` is one of the comma-separated features that `cargo tree` printed for a package. +fn has_feature(features: &str, feature: &str) -> bool { + features + .trim() + .trim_end_matches("(*)") + .split(',') + .any(|enabled| enabled.trim() == feature) +} + +#[cfg(test)] +mod tests { + use super::{Violation, workspace_packages_with_feature}; + + // Output of `cargo tree -p b -e normal,build --prefix none -f "{p}|{f}"` on a scratch workspace where + // `b` depends on `a` and on `c`, and only `c` enables `a`'s `testing` feature. + const TRANSITIVE_TREE: &str = "\ +b v0.1.0 (/scratch/b)| +a v0.1.0 (/scratch/a)|testing +c v0.1.0 (/scratch/c)| +a v0.1.0 (/scratch/a)|testing +"; + + // The first lines of the same command on this repository, with its path replaced by `/repo`. + const REPOSITORY_TREE: &str = "\ +torrust-tracker v3.0.0-develop (/repo)| +anyhow v1.0.104|default,std +axum-server v0.8.0|arc-swap,default,rustls,rustls-pki-types,tls-rustls-no-provider,tokio-rustls +arc-swap v1.9.3| +rustversion v1.0.23 (proc-macro)| +serde_derive v1.0.229 (proc-macro)|default +proc-macro2 v1.0.107|default,proc-macro +proc-macro2 v1.0.107|default,proc-macro (*) +"; + + fn violation(package: &str, version: &str) -> Violation { + Violation { + package: package.to_owned(), + version: version.to_owned(), + feature: "testing".to_owned(), + } + } + + #[test] + fn it_should_report_a_workspace_package_whose_testing_feature_a_dependency_enables_once() { + // Arrange: `a` appears twice in the tree with `testing` on, because `c` enables it. + + // Act + let violations = workspace_packages_with_feature(TRANSITIVE_TREE, "testing"); + + // Assert + assert_eq!(violations, vec![violation("a", "v0.1.0")]); + } + + #[test] + fn it_should_report_nothing_when_no_package_has_the_feature_enabled() { + // Arrange: a real tree whose lines carry `(proc-macro)` and `(*)` markers and no `testing` feature. + + // Act + let violations = workspace_packages_with_feature(REPOSITORY_TREE, "testing"); + + // Assert + assert_eq!(violations, vec![]); + } + + #[test] + fn it_should_report_the_shipped_package_itself_when_it_enables_the_feature() { + // Arrange: the root line of the tree is the shipped package. + let tree = "shipped v1.2.3 (/repo/shipped)|testing\n"; + + // Act + let violations = workspace_packages_with_feature(tree, "testing"); + + // Assert + assert_eq!(violations, vec![violation("shipped", "v1.2.3")]); + } + + #[test] + fn it_should_ignore_a_registry_package_that_has_a_feature_with_the_same_name() { + // Arrange: a line in the format of a registry package (no path) with a `testing` feature. + let tree = "third-party v1.0.0|default,testing\n"; + + // Act + let violations = workspace_packages_with_feature(tree, "testing"); + + // Assert + assert_eq!(violations, vec![]); + } + + #[test] + fn it_should_ignore_a_git_package_that_has_a_feature_with_the_same_name() { + // Arrange: a line in the format of a Git dependency, whose location is a URL and not a path. + let tree = "forked v0.3.0 (https://example.org/forked.git#0123abc)|testing\n"; + + // Act + let violations = workspace_packages_with_feature(tree, "testing"); + + // Assert + assert_eq!(violations, vec![]); + } + + #[test] + fn it_should_match_the_feature_name_exactly() { + // Arrange: features whose names contain `testing` but are not `testing`. + let tree = "a v0.1.0 (/scratch/a)|testing-utils,db-testing\n"; + + // Act + let violations = workspace_packages_with_feature(tree, "testing"); + + // Assert + assert_eq!(violations, vec![]); + } + + #[test] + fn it_should_read_a_workspace_path_that_contains_spaces() { + // Arrange: a checkout under a directory with a space in its name. + let tree = "a v0.1.0 (/home/some user/my repo/a)|testing\n"; + + // Act + let violations = workspace_packages_with_feature(tree, "testing"); + + // Assert + assert_eq!(violations, vec![violation("a", "v0.1.0")]); + } + + #[test] + fn it_should_report_a_workspace_procedural_macro_crate_that_has_the_feature_enabled() { + // Arrange: a workspace crate that is also a procedural macro carries both a path and `(proc-macro)`. + let tree = "macros v0.1.0 (/repo/macros) (proc-macro)|testing\n"; + + // Act + let violations = workspace_packages_with_feature(tree, "testing"); + + // Assert + assert_eq!(violations, vec![violation("macros", "v0.1.0")]); + } + + #[test] + fn it_should_skip_lines_that_are_not_package_lines() { + // Arrange: blank lines and a line without the `|` separator. + let tree = "\nnot a package line\n\n"; + + // Act + let violations = workspace_packages_with_feature(tree, "testing"); + + // Assert + assert_eq!(violations, vec![]); + } +} diff --git a/contrib/dev-tools/checks/shipped-features-check/src/main.rs b/contrib/dev-tools/checks/shipped-features-check/src/main.rs new file mode 100644 index 000000000..ccf235974 --- /dev/null +++ b/contrib/dev-tools/checks/shipped-features-check/src/main.rs @@ -0,0 +1,202 @@ +//! Command-line adapter: fails when a workspace `testing` feature reaches a shipped package. +//! +//! Output follows the global CLI output contract (`no-stdout-result`): nothing on stdout, one JSON +//! object per line on stderr, exit code 0 when every shipped package is clean, 1 on a violation or a +//! runtime failure, and 2 on a usage error. + +use std::env; +use std::ffi::OsString; +use std::io::{self, Write}; +use std::process::{Command, ExitCode}; + +use serde::Serialize; +use shipped_features_check::workspace_packages_with_feature; + +const EXIT_FAILURE: u8 = 1; +const EXIT_USAGE_ERROR: u8 = 2; +const DEFAULT_FORBIDDEN_FEATURE: &str = "testing"; + +fn main() -> ExitCode { + let records = match check() { + Ok(records) => records, + Err(failure) => vec![failure.into_record()], + }; + let exit_code = records.iter().map(|record| record.exit_code).max().unwrap_or(0); + + for record in &records { + if write_record(record).is_err() { + emit_output_failure(exit_code); + break; + } + } + + ExitCode::from(exit_code) +} + +/// One JSON line on stderr. +#[derive(Serialize)] +struct Record { + kind: &'static str, + message: String, + #[serde(skip_serializing_if = "Option::is_none")] + shipped_package: Option, + #[serde(skip_serializing_if = "Option::is_none")] + package: Option, + #[serde(skip_serializing_if = "Option::is_none")] + feature: Option, + exit_code: u8, +} + +/// A failure that stops the check before it can decide. +enum Failure { + Usage(String), + Runtime(String), +} + +impl Failure { + fn into_record(self) -> Record { + let (kind, message, exit_code) = match self { + Self::Usage(message) => ("usage_error", message, EXIT_USAGE_ERROR), + Self::Runtime(message) => ("runtime_error", message, EXIT_FAILURE), + }; + + Record { + kind, + message, + shipped_package: None, + package: None, + feature: None, + exit_code, + } + } +} + +struct Arguments { + packages: Vec, + features: Option, + forbidden_feature: String, +} + +/// Checks every shipped package and returns one record per package and per violation. +fn check() -> Result, Failure> { + let arguments = parse_arguments(env::args().skip(1)).map_err(Failure::Usage)?; + let mut records = Vec::new(); + + for package in &arguments.packages { + let tree = cargo_tree(package, arguments.features.as_deref()).map_err(Failure::Runtime)?; + let violations = workspace_packages_with_feature(&tree, &arguments.forbidden_feature); + + if violations.is_empty() { + records.push(Record { + kind: "checked", + message: format!( + "no workspace package has the `{}` feature enabled in the dependency tree of shipped package `{package}`", + arguments.forbidden_feature + ), + shipped_package: Some(package.clone()), + package: None, + feature: Some(arguments.forbidden_feature.clone()), + exit_code: 0, + }); + } + records.extend(violations.into_iter().map(|violation| Record { + kind: "validation_error", + message: format!( + "workspace package `{}` {} has the `{}` feature enabled in the dependency tree of shipped package `{package}`", + violation.package, violation.version, violation.feature + ), + shipped_package: Some(package.clone()), + package: Some(violation.package), + feature: Some(violation.feature), + exit_code: EXIT_FAILURE, + })); + } + + Ok(records) +} + +fn parse_arguments(mut arguments: impl Iterator) -> Result { + let mut packages = Vec::new(); + let mut features = None; + let mut forbidden_feature = None; + + while let Some(argument) = arguments.next() { + let slot = match argument.as_str() { + "--package" => None, + "--features" if features.is_none() => Some(&mut features), + "--forbidden-feature" if forbidden_feature.is_none() => Some(&mut forbidden_feature), + "--features" | "--forbidden-feature" => return Err(format!("usage error: duplicate argument `{argument}`")), + _ => { + return Err(format!( + "usage error: unexpected argument `{argument}`; use `--package ` (repeatable), `--features ` and `--forbidden-feature `" + )); + } + }; + let value = arguments + .next() + .ok_or_else(|| format!("usage error: `{argument}` requires a value"))?; + + match slot { + Some(slot) => *slot = Some(value), + None => packages.push(value), + } + } + + if packages.is_empty() { + return Err(String::from( + "usage error: name at least one shipped package with `--package `", + )); + } + + Ok(Arguments { + packages, + features, + forbidden_feature: forbidden_feature.unwrap_or_else(|| DEFAULT_FORBIDDEN_FEATURE.to_owned()), + }) +} + +/// Runs `cargo tree` with the edges and features of a `cargo build -p ` of the shipped package. +fn cargo_tree(package: &str, features: Option<&str>) -> Result { + let cargo = env::var_os("CARGO").unwrap_or_else(|| OsString::from("cargo")); + let mut command = Command::new(cargo); + command.args([ + "tree", + "--package", + package, + "--edges", + "normal,build", + "--prefix", + "none", + "--format", + "{p}|{f}", + ]); + if let Some(features) = features { + command.args(["--features", features]); + } + + let output = command + .output() + .map_err(|error| format!("failed to run `cargo tree`: {error}"))?; + if !output.status.success() { + return Err(format!( + "`cargo tree --package {package}` failed: {}", + String::from_utf8_lossy(&output.stderr).trim() + )); + } + + String::from_utf8(output.stdout).map_err(|error| format!("`cargo tree` output was not valid UTF-8: {error}")) +} + +fn write_record(record: &Record) -> io::Result<()> { + let mut stderr = io::stderr().lock(); + serde_json::to_writer(&mut stderr, record)?; + stderr.write_all(b"\n") +} + +fn emit_output_failure(exit_code: u8) { + let mut stderr = io::stderr().lock(); + drop(writeln!( + stderr, + "{{\"kind\":\"output_error\",\"message\":\"failed to emit diagnostic\",\"exit_code\":{exit_code}}}" + )); +} diff --git a/contrib/dev-tools/checks/shipped-features-check/tests/cli.rs b/contrib/dev-tools/checks/shipped-features-check/tests/cli.rs new file mode 100644 index 000000000..4ac2cd863 --- /dev/null +++ b/contrib/dev-tools/checks/shipped-features-check/tests/cli.rs @@ -0,0 +1,200 @@ +//! End-to-end tests of the `shipped-features-check` binary against scratch Cargo workspaces. +//! +//! Real crates cannot get a `testing` feature while the check is introduced, so each test builds a +//! scratch workspace of path-only crates and runs the real binary, which runs the real `cargo tree`. + +use std::fs; +use std::path::Path; +use std::process::{Command, Output}; + +use serde_json::Value; +use tempfile::TempDir; + +/// A scratch workspace with the members `a` (has the `testing` and `internal` features), `c` (a library +/// that depends on `a`) and `b` (the shipped package). +struct Scratch { + directory: TempDir, +} + +impl Scratch { + fn new(b_manifest_sections: &str, c_dependencies: &str) -> Self { + let directory = tempfile::tempdir().expect("create scratch directory"); + let root = directory.path(); + + write( + root, + "Cargo.toml", + "[workspace]\nmembers = [\"a\", \"b\", \"c\"]\nresolver = \"3\"\n", + ); + write( + root, + "a/Cargo.toml", + &package("a", "[features]\ntesting = []\ninternal = []\n"), + ); + write(root, "a/src/lib.rs", "pub fn a() {}\n"); + write( + root, + "c/Cargo.toml", + &package("c", &format!("[dependencies]\n{c_dependencies}")), + ); + write(root, "c/src/lib.rs", "pub fn c() {}\n"); + write(root, "b/Cargo.toml", &package("b", b_manifest_sections)); + write(root, "b/src/main.rs", "fn main() {}\n"); + + Self { directory } + } + + fn run(&self, arguments: &[&str]) -> Output { + Command::new(env!("CARGO_BIN_EXE_shipped-features-check")) + .args(arguments) + .current_dir(self.directory.path()) + .output() + .expect("run shipped-features-check") + } +} + +fn package(name: &str, sections: &str) -> String { + format!("[package]\nname = \"{name}\"\nversion = \"0.1.0\"\nedition = \"2024\"\n\n{sections}") +} + +fn write(root: &Path, file: &str, content: &str) { + let path = root.join(file); + fs::create_dir_all(path.parent().expect("file has a parent")).expect("create directory"); + fs::write(path, content).expect("write scratch file"); +} + +fn diagnostics(output: &Output) -> Vec { + String::from_utf8(output.stderr.clone()) + .expect("stderr is UTF-8") + .lines() + .map(|line| serde_json::from_str(line).expect("every stderr line is a JSON object")) + .collect() +} + +fn of_kind<'a>(diagnostics: &'a [Value], kind: &str) -> Vec<&'a Value> { + diagnostics.iter().filter(|record| record["kind"] == kind).collect() +} + +#[test] +fn it_should_fail_when_a_normal_dependency_of_the_shipped_package_enables_the_testing_feature_of_a_workspace_package() { + // Arrange: `b` depends on `a` and on `c`, and `c` is the only one that enables `a`'s `testing` feature. + let scratch = Scratch::new( + "[dependencies]\na = { path = \"../a\" }\nc = { path = \"../c\" }\n", + "a = { path = \"../a\", features = [\"testing\"] }\n", + ); + + // Act + let output = scratch.run(&["--package", "b"]); + + // Assert + assert_eq!(output.status.code(), Some(1)); + assert_eq!(output.stdout, b""); + let records = diagnostics(&output); + let violations = of_kind(&records, "validation_error"); + assert_eq!(violations.len(), 1, "one violation: {records:?}"); + assert_eq!(violations[0]["package"], "a"); + assert_eq!(violations[0]["shipped_package"], "b"); + assert_eq!(violations[0]["feature"], "testing"); + assert_eq!(violations[0]["exit_code"], 1); + assert!(violations[0]["message"].as_str().unwrap().contains("`testing`")); +} + +#[test] +fn it_should_fail_when_the_shipped_package_enables_the_testing_feature_in_a_build_dependency() { + // Arrange: the feature reaches `b` only through its build script's dependencies. + let scratch = Scratch::new( + "[build-dependencies]\na = { path = \"../a\", features = [\"testing\"] }\n", + "", + ); + + // Act + let output = scratch.run(&["--package", "b"]); + + // Assert + assert_eq!(output.status.code(), Some(1)); + assert_eq!(of_kind(&diagnostics(&output), "validation_error").len(), 1); +} + +#[test] +fn it_should_pass_when_only_a_dev_dependency_of_the_shipped_package_enables_the_testing_feature() { + // Arrange: `b` depends on `a` plainly and enables `testing` only for its tests. + let scratch = Scratch::new( + "[dependencies]\na = { path = \"../a\" }\n\n[dev-dependencies]\na = { path = \"../a\", features = [\"testing\"] }\n", + "", + ); + + // Act + let output = scratch.run(&["--package", "b"]); + + // Assert + assert_eq!(output.status.code(), Some(0)); + assert_eq!(output.stdout, b""); + let records = diagnostics(&output); + assert_eq!(of_kind(&records, "validation_error").len(), 0); + let checked = of_kind(&records, "checked"); + assert_eq!(checked.len(), 1, "one record per checked package: {records:?}"); + assert_eq!(checked[0]["shipped_package"], "b"); +} + +#[test] +fn it_should_check_the_features_that_the_shipped_build_names_explicitly() { + // Arrange: `b`'s own feature `with-testing` turns on `a`'s `testing`; the default build does not use it. + let manifest = "[dependencies]\na = { path = \"../a\" }\n\n[features]\nwith-testing = [\"a/testing\"]\n"; + let scratch = Scratch::new(manifest, ""); + + // Act + let default_build = scratch.run(&["--package", "b"]); + let named_features = scratch.run(&["--package", "b", "--features", "with-testing"]); + + // Assert + assert_eq!(default_build.status.code(), Some(0)); + assert_eq!(named_features.status.code(), Some(1)); +} + +#[test] +fn it_should_check_the_feature_named_by_the_forbidden_feature_option() { + // Arrange: `b` enables `a`'s `internal` feature and not `testing`. + let scratch = Scratch::new("[dependencies]\na = { path = \"../a\", features = [\"internal\"] }\n", ""); + + // Act + let default_feature = scratch.run(&["--package", "b"]); + let named_feature = scratch.run(&["--package", "b", "--forbidden-feature", "internal"]); + + // Assert + assert_eq!(default_feature.status.code(), Some(0)); + assert_eq!(named_feature.status.code(), Some(1)); + assert_eq!( + of_kind(&diagnostics(&named_feature), "validation_error")[0]["feature"], + "internal" + ); +} + +#[test] +fn it_should_report_a_usage_error_when_no_package_is_named() { + // Arrange + let scratch = Scratch::new("", ""); + + // Act + let output = scratch.run(&[]); + + // Assert + assert_eq!(output.status.code(), Some(2)); + assert_eq!(output.stdout, b""); + let records = diagnostics(&output); + assert_eq!(records[0]["kind"], "usage_error"); + assert_eq!(records[0]["exit_code"], 2); +} + +#[test] +fn it_should_report_a_runtime_error_when_cargo_cannot_resolve_the_package() { + // Arrange: the workspace has no package named `missing`. + let scratch = Scratch::new("", ""); + + // Act + let output = scratch.run(&["--package", "missing"]); + + // Assert + assert_eq!(output.status.code(), Some(1)); + assert_eq!(output.stdout, b""); + assert_eq!(diagnostics(&output)[0]["kind"], "runtime_error"); +} diff --git a/docs/adrs/20260519000000_define_global_cli_output_contract.md b/docs/adrs/20260519000000_define_global_cli_output_contract.md index 4b0eeb0b8..7e0b3ebfb 100644 --- a/docs/adrs/20260519000000_define_global_cli_output_contract.md +++ b/docs/adrs/20260519000000_define_global_cli_output_contract.md @@ -76,6 +76,7 @@ All diagnostics go to stderr via the tracing subscriber or direct JSON stderr wr | `tracker_client` | `stdout-result-data` | Announce/scrape results as JSON; monitor progress as NDJSON on stderr | | `clippy-allow-reasons` | `no-stdout-result` | Prospective repository check; pass/fail via exit code | | `frontmatter-validator` | `no-stdout-result` | Markdown frontmatter check; NDJSON diagnostics on stderr | +| `shipped-features-check` | `no-stdout-result` | Shipped-binary feature check; NDJSON diagnostics on stderr | | `github-review-threads` | `stdout-result-data` | GitHub CLI review-thread results and JSON diagnostics | | `ci-gate` | `stdout-result-data` | Needed job groups or the merge verdict as JSON; JSON diagnostics | diff --git a/docs/issues/open/2670-2482-build-shipped-artifacts-without-test-features/ISSUE.md b/docs/issues/open/2670-2482-build-shipped-artifacts-without-test-features/ISSUE.md index f53e01428..4cc64d730 100644 --- a/docs/issues/open/2670-2482-build-shipped-artifacts-without-test-features/ISSUE.md +++ b/docs/issues/open/2670-2482-build-shipped-artifacts-without-test-features/ISSUE.md @@ -9,7 +9,7 @@ github-issue: 2670 spec-path: docs/issues/open/2670-2482-build-shipped-artifacts-without-test-features/ISSUE.md branch: "2670-2482-build-shipped-artifacts-without-test-features" related-pr: null -last-updated-utc: "2026-10-27 12:44" +last-updated-utc: "2026-10-27 14:55" semantic-links: skill-links: - create-issue @@ -268,7 +268,7 @@ Status values: `TODO`, `IN_PROGRESS`, `BLOCKED`, `DONE`. | --- | --- | --- | --- | | T1 | DONE | Re-verify the audit table against `develop`; record a baseline: the list and count of the tests the image's `test` stage runs, and cold and warm build times | Table and baseline in the evidence (V0, V1); the list lets the later comparison show that every baseline test is still present | | T2 | DONE | Add `TORRUST_TRACKER_BINARY` to `tracker_binary()`, test first | Unit tests for the resolution order; default behaviour unchanged | -| T3 | TODO | Create the check crate, test first | Parser tests from real `cargo tree` output; scratch-workspace red and green tests; registered as a workspace member and admitted in `.dockerignore`; `cargo machete` and `cargo deny` pass | +| T3 | DONE | Create the check crate, test first | Parser tests from real `cargo tree` output; scratch-workspace red and green tests; registered as a workspace member and admitted in `.dockerignore`; `cargo machete` and `cargo deny` pass | | T4 | TODO | Add the check as a job in `testing.yaml` | Job runs the check for `torrust-tracker`; follows `implement-workflow` | | T5 | TODO | Add the shipped cook and build stages for the release and debug images; point `test` and `test_debug` at the shipped binary; take `/app/bin` from it | Both targets build; every baseline test is still present and passing, and the delta for new tests (T2 adds some, compiled into the targets that include `tests/common/native_tracker`) is recorded; times recorded | | T6 | TODO | Amend ADR 20260603; update `docs/containers.md` | Docs describe the two builds, the override variable and the check | @@ -319,6 +319,7 @@ Status values: `TODO`, `IN_PROGRESS`, `BLOCKED`, `DONE`. - 2026-10-27 09:06 UTC - AI assistant (Copilot SDK in VS Code, #2482 subissue 9) - PR #2671 review round 1, F3: scenario M1 now builds both targets with `--file Containerfile`, as `docs/containers.md` does, because the repository root has no `Dockerfile`. - 2026-10-27 12:43 UTC - AI assistant (Copilot SDK in VS Code, #2482 subissue 9) - Started the implementation. T1 is done: the audit table still holds on `develop` after PR #2671 (V0), and the baseline is 1531 tests in 40 binaries with a cold `release` build of 4 min 59 s, a cold `debug` build of 1 min 55 s and a warm `release` rebuild of 2 min 53 s (V1, list in `baseline-test-list.txt`). - 2026-10-27 12:44 UTC - AI assistant (Copilot SDK in VS Code, #2482 subissue 9) - T2 is done: `tracker_binary()` reads `TORRUST_TRACKER_BINARY` before the nextest and Cargo variables, with three unit tests (red with the override discarded, green on the `lifecycle-signals` and `cli-configuration` targets). The test-design review is recorded in the evidence; the maintainer (josecelano) reviewed the increment and approved it through `ask_user` before the commit. +- 2026-10-27 14:55 UTC - AI assistant (Copilot SDK in VS Code, #2482 subissue 9) - T3 is done: the `shipped-features-check` crate parses `cargo tree -e normal,build` output and fails on a workspace `testing` feature in a shipped package's tree (9 parser tests and 7 scratch-workspace tests, each red before green, two mutations for the negative tests). The real workspace passes vacuously (M4) and the scratch fixture fails and passes as it should (M5). The crate is a workspace member but not a default member, is admitted in `.dockerignore`, and is listed in the `AGENTS.md` catalogue and the CLI output contract ADR. The maintainer (josecelano) reviewed the increments and approved them through `ask_user` before the commit. ## Acceptance Criteria @@ -356,8 +357,8 @@ applies, except that this subissue moves no test code. The workflow changes foll | M1 | Baseline before the change | Build the `release` and `debug` targets of `develop`'s `Containerfile` (`docker build --target release --tag torrust-tracker:release --file Containerfile .` and the same with `--target debug --tag torrust-tracker:debug`) and record the `test` stage's test list and count and the cold and warm times | Numbers recorded | DONE | `manual-verification-evidence.md` V1 | | M2 | The image builds and its tests run | Build both targets after the change | Both build; every baseline test is still present and passing; the delta for new tests is recorded (the count may rise by the tests T2 adds); times recorded | TODO | V2 | | M3 | The override is honoured | In the `test` stage, point `TORRUST_TRACKER_BINARY` at a missing file, build, then restore it | The native-tracker tests fail with the missing path (red), then pass (green) | TODO | V3 | -| M4 | The shipped binary has no `testing` feature | `cargo run -p shipped-features-check -- --package torrust-tracker` and `cargo tree -p torrust-tracker -e normal,build -f "{p} {f}"` | Passes; no `testing` feature (vacuous today, since none exists) | TODO | V4 | -| M5 | The check fails when it should | Run the scratch-workspace red case (a shipped package enabling a `testing` feature through a normal dependency) | The check fails and names the package and feature | TODO | V5 | +| M4 | The shipped binary has no `testing` feature | `cargo run -p shipped-features-check -- --package torrust-tracker` and `cargo tree -p torrust-tracker -e normal,build -f "{p} {f}"` | Passes; no `testing` feature (vacuous today, since none exists) | DONE | V4 | +| M5 | The check fails when it should | Run the scratch-workspace red case (a shipped package enabling a `testing` feature through a normal dependency) | The check fails and names the package and feature | DONE | V5 | | M6 | The workflow job runs | Open the pull request and read the `testing.yaml` job result | The job is green on the real workspace | TODO | V6 | ### Acceptance Verification diff --git a/docs/issues/open/2670-2482-build-shipped-artifacts-without-test-features/manual-verification-evidence.md b/docs/issues/open/2670-2482-build-shipped-artifacts-without-test-features/manual-verification-evidence.md index 9cfcb0357..00462cdaf 100644 --- a/docs/issues/open/2670-2482-build-shipped-artifacts-without-test-features/manual-verification-evidence.md +++ b/docs/issues/open/2670-2482-build-shipped-artifacts-without-test-features/manual-verification-evidence.md @@ -1,7 +1,7 @@ --- doc-type: manual-verification-evidence issue-spec: docs/issues/open/2670-2482-build-shipped-artifacts-without-test-features/ISSUE.md -last-updated-utc: "2026-10-27 12:44" +last-updated-utc: "2026-10-27 14:55" --- # Manual Verification Evidence @@ -102,3 +102,118 @@ Red and green runs (Cargo 1.99.1, `cargo test -p torrust-tracker --test lifecycl `right: "/shipped/torrust-tracker"` and the other two pass: `2 passed; 1 failed`. - Green: with the change, `lifecycle-signals` reports `16 passed` and `cli-configuration` reports `21 passed`. The three new tests compile into both targets, so the image's test count rises by 6. + +## T3 - Test-design review of the `shipped-features-check` tests + +Change: the new crate `contrib/dev-tools/checks/shipped-features-check`. `workspace_packages_with_feature(tree, feature)` +parses the output of `cargo tree -p -e normal,build --prefix none -f "{p}|{f}"` and returns +each workspace package (a line with a local path) that has the feature enabled. The binary runs that command per +`--package`, with `--features` and `--forbidden-feature` options, and reports through the global CLI output contract +(`no-stdout-result`: nothing on stdout, NDJSON on stderr, exit 0, 1 or 2). + +Two increments, each reviewed after it passed and before the next one started. + +### Increment 1 - the `cargo tree` parser (9 unit tests in `src/lib.rs`) + +The fixtures are real output: `TRANSITIVE_TREE` is the output on a scratch workspace and `REPOSITORY_TREE` is the +first lines of the same command on this repository (path replaced by `/repo`). The registry, Git, path-with-spaces, +procedural-macro and exact-match cases are lines in the real format with the one varying fact written by hand, which +the test names say. + +| Test | Arrange (the one causal initial-state difference) | Act | Assert | +| --- | --- | --- | --- | +| `..._a_dependency_enables_once` | `a` is in the tree twice with `testing` on (a dependency `c` enables it) | Parse for `testing` | One violation for `a` | +| `..._report_nothing_when_no_package_has_the_feature_enabled` | A real repository tree with `(proc-macro)` and `(*)` markers and no `testing` | Parse | No violation | +| `..._shipped_package_itself_...` | The root line of the tree has `testing` | Parse | One violation for the shipped package | +| `..._ignore_a_registry_package_...` | A line with no path and a `testing` feature | Parse | No violation | +| `..._ignore_a_git_package_...` | A line whose location is a URL | Parse | No violation | +| `..._match_the_feature_name_exactly` | Features `testing-utils` and `db-testing` | Parse | No violation | +| `..._path_that_contains_spaces` | The path has spaces | Parse | One violation | +| `..._workspace_procedural_macro_crate_...` | A path plus the `(proc-macro)` marker | Parse | One violation | +| `..._skip_lines_that_are_not_package_lines` | Blank lines and a line without `\|` | Parse | No violation | + +Review result: each test changes one fact of the line (location kind, feature name, marker, path), and the Act is the +same public call in all of them. There is no fixture helper beyond `violation()`, which builds the expected value. +The negative tests passed against the first stub, so they were checked by two scratch mutations that were reverted. +Treating every package as local failed the registry and Git tests (`7 passed; 2 failed`); matching the feature by +substring failed the exact-match test (`8 passed; 1 failed`). + +Red run before the implementation (stub returning no violation): `5 passed; 4 failed`, the four positive tests. +Green run after it: `9 passed; 0 failed`. + +### Increment 2 - the binary against scratch workspaces (7 tests in `tests/cli.rs`) + +Real crates cannot get a `testing` feature in this subissue, so every test builds a scratch workspace of path-only +crates in a temporary directory (`a` has the features `testing` and `internal`, `c` depends on `a`, `b` is the shipped +package) and runs the real binary, which runs the real `cargo tree`. + +| Test | Arrange | Act | Assert | +| --- | --- | --- | --- | +| `it_should_fail_when_a_normal_dependency_...` | `b` depends on `a` plainly and on `c`, which enables `a`'s `testing` (transitive) | `--package b` | Exit 1, empty stdout, one `validation_error` naming `a`, `b` and `testing` | +| `..._in_a_build_dependency` | `testing` reaches `b` only through a build dependency | `--package b` | Exit 1, one violation | +| `..._pass_when_only_a_dev_dependency_...` | `b` enables `testing` only in `[dev-dependencies]` | `--package b` | Exit 0, empty stdout, no violation, one `checked` record | +| `..._features_that_the_shipped_build_names_explicitly` | `b`'s feature `with-testing` enables `a/testing` | Without and with `--features with-testing` | Exit 0, then exit 1 | +| `..._forbidden_feature_option` | `b` enables `a`'s `internal` feature | Default, then `--forbidden-feature internal` | Exit 0, then exit 1 naming `internal` | +| `..._usage_error_when_no_package_is_named` | An empty argument list | Run | Exit 2, empty stdout, `usage_error` | +| `..._runtime_error_when_cargo_cannot_resolve_the_package` | No package `missing` | `--package missing` | Exit 1, empty stdout, `runtime_error` | + +Review result: the `Scratch` helper writes the manifests (incidental mechanics) and takes the two manifest sections +that carry the causal difference of each test, so the section that decides the result stays in the test. The Act is +one command line and the Assert states the exit code, the stdout and the diagnostic the contract requires. + +Red run before the CLI existed (an empty `main`): `0 passed; 7 failed`. Green run after it: `7 passed; 0 failed`. +`cargo clippy -p shipped-features-check --all-targets -- -D warnings` reports nothing. + +## V4 - The check on the real workspace (scenario M4) + +```bash +cargo run --quiet -p shipped-features-check -- --package torrust-tracker > out.txt 2> err.txt; echo $? +``` + +```text +exit 0 +stdout bytes: 0 +stderr: {"kind":"checked","message":"no workspace package has the `testing` feature enabled in the dependency tree of shipped package `torrust-tracker`","shipped_package":"torrust-tracker","feature":"testing","exit_code":0} +``` + +The check passes vacuously today: no workspace package has a `testing` feature yet. Its ability to fail is V5. + +The plain `cargo tree` view of the same build, counting enabled features named exactly `testing`: + +```bash +cargo tree -p torrust-tracker -e normal,build --prefix none -f '{p}|{f}' | awk -F'|' '{n=split($2,f,","); for(i=1;i<=n;i++){g=f[i]; sub(/ \(\*\)$/,"",g); if(g=="testing") c++}} END{print "lines:", NR, "packages with a feature named testing:", c+0}' +``` + +```text +lines: 1353 packages with a feature named testing: 0 +``` + +## V5 - The check fails when it should (scenario M5) + +The scratch workspaces `fx` (members `a`, `b`, `c`) and `fx-dev` (members `a`, `b`) were created under +`~/.cache/r9-2670/` and are not part of the repository. `a` has `[features] testing = []`. + +Red, `fx`: `b` depends on `a` and on `c`, and `c` depends on `a` with `features = ["testing"]`. + +```text +$ shipped-features-check --package b (exit 1, stdout empty) +{"kind":"validation_error","message":"workspace package `a` v0.1.0 has the `testing` feature enabled in the dependency tree of shipped package `b`","shipped_package":"b","package":"a","feature":"testing","exit_code":1} +``` + +Green, `fx-dev`: `b` depends on `a` plainly and enables `testing` only in `[dev-dependencies]`. + +```text +$ shipped-features-check --package b (exit 0, stdout empty) +{"kind":"checked","message":"no workspace package has the `testing` feature enabled in the dependency tree of shipped package `b`","shipped_package":"b","feature":"testing","exit_code":0} + +$ cargo tree -p b --prefix none -f '{p}|{f}' (dev-dependencies included: what --tests builds) +b v0.1.0 (~/.cache/r9-2670/fx-dev/b)| +a v0.1.0 (~/.cache/r9-2670/fx-dev/a)|testing +a v0.1.0 (~/.cache/r9-2670/fx-dev/a)|testing + +$ cargo tree -p b -e normal,build --prefix none -f '{p}|{f}' (what the check reads: what cargo build -p b builds) +b v0.1.0 (~/.cache/r9-2670/fx-dev/b)| +a v0.1.0 (~/.cache/r9-2670/fx-dev/a)| +``` + +The same scratch cases run automatically in `tests/cli.rs` on every CI run. From cf40462a5da327badcfaf87b4b0b356b7182e23c Mon Sep 17 00:00:00 2001 From: Jose Celano Date: Tue, 27 Oct 2026 14:58:00 +0000 Subject: [PATCH 04/12] ci(testing): [#2670] check that no testing feature reaches a shipped package Add the Shipped Features job, which runs shipped-features-check for the torrust-tracker package. The job and the Containerfile's shipped build must name the same package and features, so each names the other in a comment. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .github/workflows/testing.yaml | 25 +++++++++++++++++++ .../ISSUE.md | 5 ++-- .../manual-verification-evidence.md | 2 +- 3 files changed, 29 insertions(+), 3 deletions(-) diff --git a/.github/workflows/testing.yaml b/.github/workflows/testing.yaml index b476f09af..0fe076f1b 100644 --- a/.github/workflows/testing.yaml +++ b/.github/workflows/testing.yaml @@ -164,6 +164,31 @@ jobs: name: Check layer boundary bans run: cargo deny check bans + # issue: #2670 + # Rule A.5 of ADR 20261021190318: a `testing` feature of a workspace package must not be enabled in the + # dependency tree of a shipped package. The check reads `cargo tree` for the edges and features of + # `cargo build -p `, so it ignores the dev-dependencies that `--tests` would enable. Keep the + # package (and, once the shipped build names any, its features) in step with the "Build Shipped + # (release)" and "Build Shipped (debug)" stages of the `Containerfile`. + shipped-features: + name: Shipped Features + runs-on: ubuntu-latest + timeout-minutes: 15 + steps: + - id: checkout + name: Checkout Repository + uses: actions/checkout@v7.0.1 + + - id: setup + name: Setup Toolchain + uses: dtolnay/rust-toolchain@stable + with: + toolchain: stable + + - id: check + name: Check Shipped Features + run: cargo run --quiet --package shipped-features-check -- --package torrust-tracker + # issue: #2582 # Packages every workspace member so a crate that cannot be published (for example a versioned # dependency that crates.io cannot resolve) fails here instead of at publish time. `--no-verify` diff --git a/docs/issues/open/2670-2482-build-shipped-artifacts-without-test-features/ISSUE.md b/docs/issues/open/2670-2482-build-shipped-artifacts-without-test-features/ISSUE.md index 4cc64d730..6b649b402 100644 --- a/docs/issues/open/2670-2482-build-shipped-artifacts-without-test-features/ISSUE.md +++ b/docs/issues/open/2670-2482-build-shipped-artifacts-without-test-features/ISSUE.md @@ -9,7 +9,7 @@ github-issue: 2670 spec-path: docs/issues/open/2670-2482-build-shipped-artifacts-without-test-features/ISSUE.md branch: "2670-2482-build-shipped-artifacts-without-test-features" related-pr: null -last-updated-utc: "2026-10-27 14:55" +last-updated-utc: "2026-10-27 14:57" semantic-links: skill-links: - create-issue @@ -269,7 +269,7 @@ Status values: `TODO`, `IN_PROGRESS`, `BLOCKED`, `DONE`. | T1 | DONE | Re-verify the audit table against `develop`; record a baseline: the list and count of the tests the image's `test` stage runs, and cold and warm build times | Table and baseline in the evidence (V0, V1); the list lets the later comparison show that every baseline test is still present | | T2 | DONE | Add `TORRUST_TRACKER_BINARY` to `tracker_binary()`, test first | Unit tests for the resolution order; default behaviour unchanged | | T3 | DONE | Create the check crate, test first | Parser tests from real `cargo tree` output; scratch-workspace red and green tests; registered as a workspace member and admitted in `.dockerignore`; `cargo machete` and `cargo deny` pass | -| T4 | TODO | Add the check as a job in `testing.yaml` | Job runs the check for `torrust-tracker`; follows `implement-workflow` | +| T4 | IN_PROGRESS | Add the check as a job in `testing.yaml` | Job runs the check for `torrust-tracker`; follows `implement-workflow` | | T5 | TODO | Add the shipped cook and build stages for the release and debug images; point `test` and `test_debug` at the shipped binary; take `/app/bin` from it | Both targets build; every baseline test is still present and passing, and the delta for new tests (T2 adds some, compiled into the targets that include `tests/common/native_tracker`) is recorded; times recorded | | T6 | TODO | Amend ADR 20260603; update `docs/containers.md` | Docs describe the two builds, the override variable and the check | @@ -320,6 +320,7 @@ Status values: `TODO`, `IN_PROGRESS`, `BLOCKED`, `DONE`. - 2026-10-27 12:43 UTC - AI assistant (Copilot SDK in VS Code, #2482 subissue 9) - Started the implementation. T1 is done: the audit table still holds on `develop` after PR #2671 (V0), and the baseline is 1531 tests in 40 binaries with a cold `release` build of 4 min 59 s, a cold `debug` build of 1 min 55 s and a warm `release` rebuild of 2 min 53 s (V1, list in `baseline-test-list.txt`). - 2026-10-27 12:44 UTC - AI assistant (Copilot SDK in VS Code, #2482 subissue 9) - T2 is done: `tracker_binary()` reads `TORRUST_TRACKER_BINARY` before the nextest and Cargo variables, with three unit tests (red with the override discarded, green on the `lifecycle-signals` and `cli-configuration` targets). The test-design review is recorded in the evidence; the maintainer (josecelano) reviewed the increment and approved it through `ask_user` before the commit. - 2026-10-27 14:55 UTC - AI assistant (Copilot SDK in VS Code, #2482 subissue 9) - T3 is done: the `shipped-features-check` crate parses `cargo tree -e normal,build` output and fails on a workspace `testing` feature in a shipped package's tree (9 parser tests and 7 scratch-workspace tests, each red before green, two mutations for the negative tests). The real workspace passes vacuously (M4) and the scratch fixture fails and passes as it should (M5). The crate is a workspace member but not a default member, is admitted in `.dockerignore`, and is listed in the `AGENTS.md` catalogue and the CLI output contract ADR. The maintainer (josecelano) reviewed the increments and approved them through `ask_user` before the commit. +- 2026-10-27 14:57 UTC - AI assistant (Copilot SDK in VS Code, #2482 subissue 9) - Added the `Shipped Features` job to `testing.yaml` (T4), which runs `cargo run --package shipped-features-check -- --package torrust-tracker`; `linter yaml` and `git diff --check` pass and the same command passes locally (V4). The spec's commit point asks for the job to pass in a fork or pull request first, but the implementation pull request is the first hosted run, so T4 stays IN_PROGRESS and M6 stays TODO until that run is recorded. ## Acceptance Criteria diff --git a/docs/issues/open/2670-2482-build-shipped-artifacts-without-test-features/manual-verification-evidence.md b/docs/issues/open/2670-2482-build-shipped-artifacts-without-test-features/manual-verification-evidence.md index 00462cdaf..6c4de06d7 100644 --- a/docs/issues/open/2670-2482-build-shipped-artifacts-without-test-features/manual-verification-evidence.md +++ b/docs/issues/open/2670-2482-build-shipped-artifacts-without-test-features/manual-verification-evidence.md @@ -1,7 +1,7 @@ --- doc-type: manual-verification-evidence issue-spec: docs/issues/open/2670-2482-build-shipped-artifacts-without-test-features/ISSUE.md -last-updated-utc: "2026-10-27 14:55" +last-updated-utc: "2026-10-27 14:57" --- # Manual Verification Evidence From cfa365cd72ba1740e47a78c34ce098ecfad80d75 Mon Sep 17 00:00:00 2001 From: Jose Celano Date: Tue, 27 Oct 2026 15:06:19 +0000 Subject: [PATCH 05/12] test(tracker): [#2670] stop calling the spawned tracker executable Cargo-built With TORRUST_TRACKER_BINARY the native-tracker tests can spawn the shipped binary, so the spawn failure message and the doc comment no longer claim that Cargo built it. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- tests/common/native_tracker/failed_start.rs | 2 +- tests/common/native_tracker/mod.rs | 6 ++++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/tests/common/native_tracker/failed_start.rs b/tests/common/native_tracker/failed_start.rs index e61fa7b2d..a8669ff0b 100644 --- a/tests/common/native_tracker/failed_start.rs +++ b/tests/common/native_tracker/failed_start.rs @@ -105,7 +105,7 @@ impl NativeTrackerStartAttempt { } pub fn start(mut self) -> NativeTrackerFailedStart { - let mut child = self.command.spawn().expect("spawn Cargo-built tracker executable"); + let mut child = self.command.spawn().expect("spawn tracker executable"); let stdout = child.stdout.take().expect("tracker child stdout is piped"); let stderr = child.stderr.take().expect("tracker child stderr is piped"); diff --git a/tests/common/native_tracker/mod.rs b/tests/common/native_tracker/mod.rs index 358e7f0b7..44213e6ab 100644 --- a/tests/common/native_tracker/mod.rs +++ b/tests/common/native_tracker/mod.rs @@ -54,7 +54,9 @@ pub struct NativeTracker { } impl NativeTracker { - /// Spawns the Cargo-built tracker binary with an isolated CLI configuration and port-zero bindings. + /// Spawns the tracker binary with an isolated CLI configuration and port-zero bindings. + /// + /// The binary is the one Cargo built, or the one named by `TORRUST_TRACKER_BINARY`. // The configuration binary always supplies explicit source settings. #[allow(dead_code)] pub fn start() -> Self { @@ -78,7 +80,7 @@ impl NativeTracker { workspace.health_check_api_bind_address_override(), ); - let mut child = command.spawn().expect("spawn Cargo-built tracker executable"); + let mut child = command.spawn().expect("spawn tracker executable"); let stdout = child.stdout.take().expect("tracker child stdout is piped"); let stderr = child.stderr.take().expect("tracker child stderr is piped"); let output = TrackerOutputCapture::new(stdout, stderr); From 710cc66a7d40c31ab8681ac5b521a05a5e6e599a Mon Sep 17 00:00:00 2001 From: Jose Celano Date: Tue, 27 Oct 2026 20:13:30 +0000 Subject: [PATCH 06/12] ci(container): [#2670] build the shipped binaries without test features The release and debug images took torrust-tracker and http_health_check from the nextest archives, built with --tests and --all-features, which would ship every testing feature once rows 3 to 7 of #2482 add them. Add cook and build stages that run cargo build for the shipped binaries without a test flag or feature, take /app/bin from them, and run the native-tracker tests against the shipped binary through TORRUST_TRACKER_BINARY. The test archives keep --all-features. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- Containerfile | 61 +++++++++++++++--- .../ISSUE.md | 9 +-- .../manual-verification-evidence.md | 62 ++++++++++++++++++- 3 files changed, 120 insertions(+), 12 deletions(-) diff --git a/Containerfile b/Containerfile index 3b557a03a..d0c655ef2 100644 --- a/Containerfile +++ b/Containerfile @@ -121,6 +121,51 @@ RUN cargo nextest archive --tests --all-features \ --archive-file /build/temp.tar.zst --release && rm -f /build/temp.tar.zst +# The binaries that ship are built by `cargo build` invocations of their own, never taken from the +# nextest archives above: `--tests` and `--all-features` enable dev-dependency features and every +# feature, which would ship every `testing` feature (ADR 20261021190318, rule A.5). The shipped +# builds use no test flag and name no feature, because the shipped packages need none today; name +# any feature they need explicitly here and in the "Shipped Features" job of +# `.github/workflows/testing.yaml`, which checks that no `testing` feature reaches the build. +# +# The cook stages stack on the third-party layers of the test cooks, so third-party crates whose +# features do not differ are compiled once; they only rebuild when the external dependencies change. + +## Cook Third-party Shipped (debug) +FROM dependencies_thirdparty_debug AS dependencies_shipped_thirdparty_debug +WORKDIR /build/src +RUN cargo chef cook --package torrust-tracker --bin torrust-tracker --recipe-path /build/recipe.json + +## Cook Shipped (debug) +FROM dependencies_shipped_thirdparty_debug AS dependencies_shipped_debug +WORKDIR /build/src +COPY --from=recipe /build/recipe.json /build/recipe.json +RUN cargo chef cook --package torrust-tracker --bin torrust-tracker --recipe-path /build/recipe.json + +## Cook Third-party Shipped (release) +FROM dependencies_thirdparty AS dependencies_shipped_thirdparty +WORKDIR /build/src +RUN cargo chef cook --release --package torrust-tracker --bins --recipe-path /build/recipe.json + +## Cook Shipped (release) +FROM dependencies_shipped_thirdparty AS dependencies_shipped +WORKDIR /build/src +COPY --from=recipe /build/recipe.json /build/recipe.json +RUN cargo chef cook --release --package torrust-tracker --bins --recipe-path /build/recipe.json + +## Build Shipped (debug) +FROM dependencies_shipped_debug AS build_shipped_debug +WORKDIR /build/src +COPY . /build/src +RUN cargo build --package torrust-tracker --bin torrust-tracker + +## Build Shipped (release) +FROM dependencies_shipped AS build_shipped +WORKDIR /build/src +COPY . /build/src +RUN cargo build --release --package torrust-tracker --bins + + ## Build Archive (debug) FROM dependencies_debug AS build_debug WORKDIR /build/src @@ -145,10 +190,10 @@ COPY --from=build_debug \ /test/torrust-tracker-debug.tar.zst RUN cargo nextest run --workspace-remap /test/src/ --extract-to /test/src/ --no-run --archive-file /test/torrust-tracker-debug.tar.zst RUN mkdir -p /test/src/storage/tracker/lib/database -RUN cargo nextest run --workspace-remap /test/src/ --target-dir-remap /test/src/target/ --cargo-metadata /test/src/target/nextest/cargo-metadata.json --binaries-metadata /test/src/target/nextest/binaries-metadata.json +# The native-tracker tests start the shipped binary, not the one built with the test features. +COPY --from=build_shipped_debug /build/src/target/debug/torrust-tracker /app/bin/torrust-tracker +RUN TORRUST_TRACKER_BINARY=/app/bin/torrust-tracker cargo nextest run --workspace-remap /test/src/ --target-dir-remap /test/src/target/ --cargo-metadata /test/src/target/nextest/cargo-metadata.json --binaries-metadata /test/src/target/nextest/binaries-metadata.json -RUN time mkdir -p /app/bin/ \ - && time cp -l /test/src/target/debug/torrust-tracker /app/bin/torrust-tracker RUN time mkdir /app/lib/ \ && time cp -l $(realpath $(ldd /app/bin/torrust-tracker | grep "libz\.so\.1" | awk '{print $3}')) /app/lib/libz.so.1 RUN time chown -R root:root /app \ @@ -164,11 +209,13 @@ COPY --from=build \ /test/torrust-tracker.tar.zst RUN cargo nextest run --workspace-remap /test/src/ --extract-to /test/src/ --no-run --archive-file /test/torrust-tracker.tar.zst RUN mkdir -p /test/src/storage/tracker/lib/database -RUN cargo nextest run --workspace-remap /test/src/ --target-dir-remap /test/src/target/ --cargo-metadata /test/src/target/nextest/cargo-metadata.json --binaries-metadata /test/src/target/nextest/binaries-metadata.json +# The native-tracker tests start the shipped binary, not the one built with the test features. +COPY --from=build_shipped \ + /build/src/target/release/torrust-tracker \ + /build/src/target/release/http_health_check \ + /app/bin/ +RUN TORRUST_TRACKER_BINARY=/app/bin/torrust-tracker cargo nextest run --workspace-remap /test/src/ --target-dir-remap /test/src/target/ --cargo-metadata /test/src/target/nextest/cargo-metadata.json --binaries-metadata /test/src/target/nextest/binaries-metadata.json -RUN time mkdir -p /app/bin/ \ - && time cp -l /test/src/target/release/torrust-tracker /app/bin/torrust-tracker \ - && time cp -l /test/src/target/release/http_health_check /app/bin/http_health_check RUN time mkdir -p /app/lib/ \ && time cp -l $(realpath $(ldd /app/bin/torrust-tracker | grep "libz\.so\.1" | awk '{print $3}')) /app/lib/libz.so.1 RUN time chown -R root:root /app \ diff --git a/docs/issues/open/2670-2482-build-shipped-artifacts-without-test-features/ISSUE.md b/docs/issues/open/2670-2482-build-shipped-artifacts-without-test-features/ISSUE.md index 6b649b402..dd1458a89 100644 --- a/docs/issues/open/2670-2482-build-shipped-artifacts-without-test-features/ISSUE.md +++ b/docs/issues/open/2670-2482-build-shipped-artifacts-without-test-features/ISSUE.md @@ -9,7 +9,7 @@ github-issue: 2670 spec-path: docs/issues/open/2670-2482-build-shipped-artifacts-without-test-features/ISSUE.md branch: "2670-2482-build-shipped-artifacts-without-test-features" related-pr: null -last-updated-utc: "2026-10-27 14:57" +last-updated-utc: "2026-10-27 20:13" semantic-links: skill-links: - create-issue @@ -270,7 +270,7 @@ Status values: `TODO`, `IN_PROGRESS`, `BLOCKED`, `DONE`. | T2 | DONE | Add `TORRUST_TRACKER_BINARY` to `tracker_binary()`, test first | Unit tests for the resolution order; default behaviour unchanged | | T3 | DONE | Create the check crate, test first | Parser tests from real `cargo tree` output; scratch-workspace red and green tests; registered as a workspace member and admitted in `.dockerignore`; `cargo machete` and `cargo deny` pass | | T4 | IN_PROGRESS | Add the check as a job in `testing.yaml` | Job runs the check for `torrust-tracker`; follows `implement-workflow` | -| T5 | TODO | Add the shipped cook and build stages for the release and debug images; point `test` and `test_debug` at the shipped binary; take `/app/bin` from it | Both targets build; every baseline test is still present and passing, and the delta for new tests (T2 adds some, compiled into the targets that include `tests/common/native_tracker`) is recorded; times recorded | +| T5 | DONE | Add the shipped cook and build stages for the release and debug images; point `test` and `test_debug` at the shipped binary; take `/app/bin` from it | Both targets build; every baseline test is still present and passing, and the delta for new tests (T2 adds some, compiled into the targets that include `tests/common/native_tracker`) is recorded; times recorded | | T6 | TODO | Amend ADR 20260603; update `docs/containers.md` | Docs describe the two builds, the override variable and the check | ## Commit Points @@ -321,6 +321,7 @@ Status values: `TODO`, `IN_PROGRESS`, `BLOCKED`, `DONE`. - 2026-10-27 12:44 UTC - AI assistant (Copilot SDK in VS Code, #2482 subissue 9) - T2 is done: `tracker_binary()` reads `TORRUST_TRACKER_BINARY` before the nextest and Cargo variables, with three unit tests (red with the override discarded, green on the `lifecycle-signals` and `cli-configuration` targets). The test-design review is recorded in the evidence; the maintainer (josecelano) reviewed the increment and approved it through `ask_user` before the commit. - 2026-10-27 14:55 UTC - AI assistant (Copilot SDK in VS Code, #2482 subissue 9) - T3 is done: the `shipped-features-check` crate parses `cargo tree -e normal,build` output and fails on a workspace `testing` feature in a shipped package's tree (9 parser tests and 7 scratch-workspace tests, each red before green, two mutations for the negative tests). The real workspace passes vacuously (M4) and the scratch fixture fails and passes as it should (M5). The crate is a workspace member but not a default member, is admitted in `.dockerignore`, and is listed in the `AGENTS.md` catalogue and the CLI output contract ADR. The maintainer (josecelano) reviewed the increments and approved them through `ask_user` before the commit. - 2026-10-27 14:57 UTC - AI assistant (Copilot SDK in VS Code, #2482 subissue 9) - Added the `Shipped Features` job to `testing.yaml` (T4), which runs `cargo run --package shipped-features-check -- --package torrust-tracker`; `linter yaml` and `git diff --check` pass and the same command passes locally (V4). The spec's commit point asks for the job to pass in a fork or pull request first, but the implementation pull request is the first hosted run, so T4 stays IN_PROGRESS and M6 stays TODO until that run is recorded. +- 2026-10-27 20:13 UTC - AI assistant (Copilot SDK in VS Code, #2482 subissue 9) - T5 is done: the `Containerfile` has shipped cook and build stages for the release and debug images (`cargo build` without test flags or features), the `test` stages run the native-tracker tests against the shipped binary through `TORRUST_TRACKER_BINARY`, and `/app/bin` takes the shipped binaries. Both targets build; 0 of the 1531 baseline tests are missing and 6 were added (T2's tests); cold builds cost +17 s (release) and +18 s (debug) and the warm release rebuild +8 s (V2). The override is honoured: pointing it at a missing file fails the native-tracker tests (V3). An interrupted warm run and a fully cached rerun were discarded and the warm run repeated with a different change. ## Acceptance Criteria @@ -356,8 +357,8 @@ applies, except that this subissue moves no test code. The workflow changes foll | ID | Scenario | Human-oriented command/steps | Expected Result | Status | Evidence | | --- | --- | --- | --- | --- | --- | | M1 | Baseline before the change | Build the `release` and `debug` targets of `develop`'s `Containerfile` (`docker build --target release --tag torrust-tracker:release --file Containerfile .` and the same with `--target debug --tag torrust-tracker:debug`) and record the `test` stage's test list and count and the cold and warm times | Numbers recorded | DONE | `manual-verification-evidence.md` V1 | -| M2 | The image builds and its tests run | Build both targets after the change | Both build; every baseline test is still present and passing; the delta for new tests is recorded (the count may rise by the tests T2 adds); times recorded | TODO | V2 | -| M3 | The override is honoured | In the `test` stage, point `TORRUST_TRACKER_BINARY` at a missing file, build, then restore it | The native-tracker tests fail with the missing path (red), then pass (green) | TODO | V3 | +| M2 | The image builds and its tests run | Build both targets after the change | Both build; every baseline test is still present and passing; the delta for new tests is recorded (the count may rise by the tests T2 adds); times recorded | DONE | V2 | +| M3 | The override is honoured | In the `test` stage, point `TORRUST_TRACKER_BINARY` at a missing file, build, then restore it | The native-tracker tests fail with the missing path (red), then pass (green) | DONE | V3 | | M4 | The shipped binary has no `testing` feature | `cargo run -p shipped-features-check -- --package torrust-tracker` and `cargo tree -p torrust-tracker -e normal,build -f "{p} {f}"` | Passes; no `testing` feature (vacuous today, since none exists) | DONE | V4 | | M5 | The check fails when it should | Run the scratch-workspace red case (a shipped package enabling a `testing` feature through a normal dependency) | The check fails and names the package and feature | DONE | V5 | | M6 | The workflow job runs | Open the pull request and read the `testing.yaml` job result | The job is green on the real workspace | TODO | V6 | diff --git a/docs/issues/open/2670-2482-build-shipped-artifacts-without-test-features/manual-verification-evidence.md b/docs/issues/open/2670-2482-build-shipped-artifacts-without-test-features/manual-verification-evidence.md index 6c4de06d7..1f6c022c3 100644 --- a/docs/issues/open/2670-2482-build-shipped-artifacts-without-test-features/manual-verification-evidence.md +++ b/docs/issues/open/2670-2482-build-shipped-artifacts-without-test-features/manual-verification-evidence.md @@ -1,7 +1,7 @@ --- doc-type: manual-verification-evidence issue-spec: docs/issues/open/2670-2482-build-shipped-artifacts-without-test-features/ISSUE.md -last-updated-utc: "2026-10-27 14:57" +last-updated-utc: "2026-10-27 20:13" --- # Manual Verification Evidence @@ -217,3 +217,63 @@ a v0.1.0 (~/.cache/r9-2670/fx-dev/a)| ``` The same scratch cases run automatically in `tests/cli.rs` on every CI run. + +## V2 - The image builds and its tests run after the change (scenario M2, task T5) + +Tree: the working tree after the `Containerfile` change of task T5 (the stages "Cook Third-party Shipped", +"Cook Shipped" and "Build Shipped", for the release and debug images), before its commit. Commands, as in V1: + +```bash +docker build --no-cache --progress=plain --target release --tag r9-after:release --file Containerfile . +docker build --no-cache --progress=plain --target debug --tag r9-after:debug --file Containerfile . +# one line appended to src/lib.rs (then reverted), a rebuild with the layer cache kept +docker build --progress=plain --target release --tag r9-after:release --file Containerfile . +``` + +| Build | Started (UTC) | Wall time | Baseline (V1) | Difference | Tests run by the `test` stage | +| --- | --- | --- | --- | --- | --- | +| `release`, cold (`--no-cache`) | 2026-10-27 14:59:31 | 5 min 16 s | 4 min 59 s | +17 s | 1537 passed, 0 skipped, 40 binaries | +| `debug`, cold (`--no-cache`) | 2026-10-27 15:06:54 | 2 min 13 s | 1 min 55 s | +18 s | 1537 passed, 0 skipped, 40 binaries | +| `release`, warm (one source line changed) | 2026-10-27 19:50:47 | 3 min 01 s | 2 min 53 s | +8 s | 1537 passed, 0 skipped, 40 binaries | + +The first warm run was interrupted and a rerun with the same change took 2 s because the layers were cached, so +neither is used; the row above is a run with a line that differs from both. + +Test comparison with the baseline (the sorted `release` lists, `LC_ALL=C comm`): 1531 baseline tests, 1537 after +the change, **0 baseline tests missing, 6 added**. The six are the three tests of T2 compiled into each of the +`cli-configuration` and `lifecycle-signals` binaries, as the spec predicted: + +```text +torrust-tracker::cli-configuration native_tracker::command::tests::it_should_run_the_binary_compiled_into_the_test_when_no_variable_is_set +torrust-tracker::cli-configuration native_tracker::command::tests::it_should_run_the_binary_named_by_the_shipped_binary_override_before_the_one_nextest_or_cargo_built +torrust-tracker::cli-configuration native_tracker::command::tests::it_should_run_the_binary_nextest_built_when_no_override_is_set +torrust-tracker::lifecycle-signals native_tracker::command::tests::it_should_run_the_binary_compiled_into_the_test_when_no_variable_is_set +torrust-tracker::lifecycle-signals native_tracker::command::tests::it_should_run_the_binary_named_by_the_shipped_binary_override_before_the_one_nextest_or_cargo_built +torrust-tracker::lifecycle-signals native_tracker::command::tests::it_should_run_the_binary_nextest_built_when_no_override_is_set +``` + +The shipped binaries come from their own invocations. The build log of the `release` image shows the stages +`dependencies_shipped_thirdparty` and `dependencies_shipped` running +`cargo chef cook --release --package torrust-tracker --bins`, and `build_shipped` running +`cargo build --release --package torrust-tracker --bins`, none with `--tests`, `--all-features` or a feature +list. The cook layers stack on the third-party test cooks, and the cold-build cost of the extra layers is the +17 s and 18 s above. + +## V3 - The override is honoured (scenario M3) + +Red: a copy of the `Containerfile` in which the `test` stage sets `TORRUST_TRACKER_BINARY=/app/bin/does-not-exist` +(a scratch change, not committed), built with `docker build --progress=plain --target test --file .`: + +```text +exit 1 +#43 0.287 spawn Cargo-built tracker executable: Os { code: 2, kind: NotFound, message: "No such file or directory" } +#43 2.367 Summary [ 2.170s] 162/1537 tests run: 155 passed, 7 failed, 0 skipped +``` + +The native-tracker tests fail because the variable points at a missing file, which shows that they now run the +binary the variable names and not the one Cargo built. The message said "Cargo-built" because the spawn message +predates the override; the commit `test(tracker): [#2670] stop calling the spawned tracker executable Cargo-built` +corrects it. + +Green: with the variable set to the shipped binary (the committed `Containerfile`), V2's `test` stage runs +1537 tests and all pass. From 706bb534a2088d11fa3efa63e8e4a761814bd709 Mon Sep 17 00:00:00 2001 From: Jose Celano Date: Tue, 27 Oct 2026 20:15:29 +0000 Subject: [PATCH 07/12] docs(containers): [#2670] describe the shipped build and amend the in-container test decision Describe the test build, the shipped build, the TORRUST_TRACKER_BINARY variable and the shipped-features check in docs/containers.md, and note in ADR 20260603 that the in-container tests no longer all run the exact shipped binary. Tick AC1, AC2 and AC4. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- ..._keep_unit_tests_inside_container_build.md | 15 ++++++++ docs/containers.md | 37 +++++++++++++++++++ .../ISSUE.md | 17 +++++---- .../manual-verification-evidence.md | 2 +- 4 files changed, 62 insertions(+), 9 deletions(-) diff --git a/docs/adrs/20260603000000_keep_unit_tests_inside_container_build.md b/docs/adrs/20260603000000_keep_unit_tests_inside_container_build.md index 79be38d75..75c26d5e9 100644 --- a/docs/adrs/20260603000000_keep_unit_tests_inside_container_build.md +++ b/docs/adrs/20260603000000_keep_unit_tests_inside_container_build.md @@ -12,6 +12,21 @@ semantic-links: # Keep unit tests inside the container build process +> **Amendment of 2026-10-27 (issue #2670).** The decision to keep the unit tests inside the container +> build stands. One claim below no longer holds: the in-container tests do not all run "the exact +> binary" that ships. The shipped `torrust-tracker` and `http_health_check` are built by `cargo build` +> invocations of their own (the `Build Shipped` stages of the `Containerfile`), without `--tests` and +> `--all-features`, so that no `testing` feature reaches them (rule A.5 of +> [ADR 20261021190318](20261021190318_share_workspace_test_support_through_testing_features.md)). The +> nextest archive, which keeps `--all-features`, still builds and runs every test. What the tests verify +> is kept close to what ships in three ways: the root crate's native-tracker tests start the shipped +> binary through the `TORRUST_TRACKER_BINARY` variable that the `test` stages set; the E2E suite still +> runs against the final `release` image; and both builds use the same image, compiler, profile and +> `RUSTFLAGS`, while the `Shipped Features` job of `testing.yaml` checks that the feature set is the +> only difference. The library code the other unit tests exercise is therefore built with the test +> features, and a shipped binary is not the one those tests link. The text below is kept unchanged as +> the record of the original decision. + ## Description The Torrust Tracker [Containerfile](../../Containerfile) runs unit tests inside the image build diff --git a/docs/containers.md b/docs/containers.md index f83c2a2de..d991c8efa 100644 --- a/docs/containers.md +++ b/docs/containers.md @@ -119,6 +119,43 @@ podman build --target release --tag torrust-tracker:release --file Containerfile podman build --target debug --tag torrust-tracker:debug --file Containerfile . ``` +### How the Image Is Built + +The `Containerfile` builds the tracker twice, and the two builds have different jobs: + +- **Test build.** The stages "Cook", "Build Archive" and "Extract and Test" compile every test with + `cargo nextest archive --tests --all-features` and run them. This build enables the dev-dependency features + and every feature, which test support code needs. +- **Shipped build.** The stages "Cook Shipped" and "Build Shipped" compile `torrust-tracker` and + `http_health_check` (the debug image ships only `torrust-tracker`) with `cargo build --package torrust-tracker`, + without `--tests`, `--all-targets`, `--examples`, `--benches` or `--all-features`. The image's `/app/bin` takes its + binaries from these stages, never from the test archive, so no `testing` feature of a workspace package reaches + them (rule A.5 of [ADR 20261021190318](adrs/20261021190318_share_workspace_test_support_through_testing_features.md)). + A shipped build that needs a feature names it explicitly in its `cargo build` command. + +The `test` stages still gate the image. They run the native-tracker tests against the shipped binary: the test +helper `tracker_binary()` starts the executable named by the `TORRUST_TRACKER_BINARY` environment variable when it is +set (the `test` stages set it), and otherwise the one Cargo or nextest built. Build a binary of your own and point the +variable at it to run those tests against it: + +```sh +cargo build --release --package torrust-tracker +TORRUST_TRACKER_BINARY=./target/release/torrust-tracker cargo test -p torrust-tracker --test lifecycle-signals +``` + +The `Shipped Features` job of `.github/workflows/testing.yaml` runs the `shipped-features-check` tool +(`contrib/dev-tools/checks/shipped-features-check`), which reads `cargo tree -p torrust-tracker -e normal,build` +and fails if a workspace package has its `testing` feature enabled in that tree. Keep the package and features it +names in step with the "Build Shipped" stages. Run the check locally with: + +```sh +cargo run --quiet --package shipped-features-check -- --package torrust-tracker +``` + +The shipped build costs about 17 seconds on a cold release image build, 18 seconds on a cold debug image build and 8 +seconds on a rebuild after a one-line source change, measured against the build before it existed (see the evidence of +issue #2670). + ## Running the Container ### Basic Run diff --git a/docs/issues/open/2670-2482-build-shipped-artifacts-without-test-features/ISSUE.md b/docs/issues/open/2670-2482-build-shipped-artifacts-without-test-features/ISSUE.md index dd1458a89..4f28a69f2 100644 --- a/docs/issues/open/2670-2482-build-shipped-artifacts-without-test-features/ISSUE.md +++ b/docs/issues/open/2670-2482-build-shipped-artifacts-without-test-features/ISSUE.md @@ -9,7 +9,7 @@ github-issue: 2670 spec-path: docs/issues/open/2670-2482-build-shipped-artifacts-without-test-features/ISSUE.md branch: "2670-2482-build-shipped-artifacts-without-test-features" related-pr: null -last-updated-utc: "2026-10-27 20:13" +last-updated-utc: "2026-10-27 20:14" semantic-links: skill-links: - create-issue @@ -271,7 +271,7 @@ Status values: `TODO`, `IN_PROGRESS`, `BLOCKED`, `DONE`. | T3 | DONE | Create the check crate, test first | Parser tests from real `cargo tree` output; scratch-workspace red and green tests; registered as a workspace member and admitted in `.dockerignore`; `cargo machete` and `cargo deny` pass | | T4 | IN_PROGRESS | Add the check as a job in `testing.yaml` | Job runs the check for `torrust-tracker`; follows `implement-workflow` | | T5 | DONE | Add the shipped cook and build stages for the release and debug images; point `test` and `test_debug` at the shipped binary; take `/app/bin` from it | Both targets build; every baseline test is still present and passing, and the delta for new tests (T2 adds some, compiled into the targets that include `tests/common/native_tracker`) is recorded; times recorded | -| T6 | TODO | Amend ADR 20260603; update `docs/containers.md` | Docs describe the two builds, the override variable and the check | +| T6 | DONE | Amend ADR 20260603; update `docs/containers.md` | Docs describe the two builds, the override variable and the check | ## Commit Points @@ -322,13 +322,14 @@ Status values: `TODO`, `IN_PROGRESS`, `BLOCKED`, `DONE`. - 2026-10-27 14:55 UTC - AI assistant (Copilot SDK in VS Code, #2482 subissue 9) - T3 is done: the `shipped-features-check` crate parses `cargo tree -e normal,build` output and fails on a workspace `testing` feature in a shipped package's tree (9 parser tests and 7 scratch-workspace tests, each red before green, two mutations for the negative tests). The real workspace passes vacuously (M4) and the scratch fixture fails and passes as it should (M5). The crate is a workspace member but not a default member, is admitted in `.dockerignore`, and is listed in the `AGENTS.md` catalogue and the CLI output contract ADR. The maintainer (josecelano) reviewed the increments and approved them through `ask_user` before the commit. - 2026-10-27 14:57 UTC - AI assistant (Copilot SDK in VS Code, #2482 subissue 9) - Added the `Shipped Features` job to `testing.yaml` (T4), which runs `cargo run --package shipped-features-check -- --package torrust-tracker`; `linter yaml` and `git diff --check` pass and the same command passes locally (V4). The spec's commit point asks for the job to pass in a fork or pull request first, but the implementation pull request is the first hosted run, so T4 stays IN_PROGRESS and M6 stays TODO until that run is recorded. - 2026-10-27 20:13 UTC - AI assistant (Copilot SDK in VS Code, #2482 subissue 9) - T5 is done: the `Containerfile` has shipped cook and build stages for the release and debug images (`cargo build` without test flags or features), the `test` stages run the native-tracker tests against the shipped binary through `TORRUST_TRACKER_BINARY`, and `/app/bin` takes the shipped binaries. Both targets build; 0 of the 1531 baseline tests are missing and 6 were added (T2's tests); cold builds cost +17 s (release) and +18 s (debug) and the warm release rebuild +8 s (V2). The override is honoured: pointing it at a missing file fails the native-tracker tests (V3). An interrupted warm run and a fully cached rerun were discarded and the warm run repeated with a different change. +- 2026-10-27 20:14 UTC - AI assistant (Copilot SDK in VS Code, #2482 subissue 9) - T6 is done: `docs/containers.md` describes the test build, the shipped build, `TORRUST_TRACKER_BINARY` and the check, and ADR 20260603 carries an amendment note that narrows its "exact binary" claim. Ticked AC1, AC2 and AC4 (validated by the AI assistant against V0 to V3). AC3 stays open until the `Shipped Features` job has run on the implementation pull request (M6). ## Acceptance Criteria -- [ ] AC1: Every command that produces a shipped artifact is listed with its flags (the audit +- [x] AC1: Every command that produces a shipped artifact is listed with its flags (the audit table), and none of the shipped-artifact builds uses `--all-features`, `--all-targets`, `--tests`, `--examples` or `--benches`. -- [ ] AC2: The image's shipped binaries are built by `cargo build` invocations of their own with +- [x] AC2: The image's shipped binaries are built by `cargo build` invocations of their own with explicit features; the `test` and `test_debug` stages still run the nextest archive, every baseline test (T1) is still present and passing, the delta for new tests is recorded, and the native-tracker tests run against the shipped binary. @@ -336,7 +337,7 @@ Status values: `TODO`, `IN_PROGRESS`, `BLOCKED`, `DONE`. normal or build dependency tree (shown by an automated scratch-workspace test), passes when only a dev-dependency enables it, and passes on the real workspace; the `testing.yaml` job runs it. -- [ ] AC4: `docs/containers.md` describes the builds, the `TORRUST_TRACKER_BINARY` variable and the +- [x] AC4: `docs/containers.md` describes the builds, the `TORRUST_TRACKER_BINARY` variable and the check; ADR 20260603 carries its amendment note. - [ ] `linter all` exits with code `0`; pre-push checks pass; the container workflow is green. @@ -367,10 +368,10 @@ applies, except that this subissue moves no test code. The workflow changes foll | AC ID | Status (`TODO`/`DONE`) | Evidence | | ----- | ---------------------- | -------- | -| AC1 | TODO | | -| AC2 | TODO | | +| AC1 | DONE | V0 (audit) and V2 (build log); validated by the AI assistant | +| AC2 | DONE | V2 (builds, 0 of 1531 baseline tests missing, 6 added) and V3 (override); validated by the AI assistant | | AC3 | TODO | | -| AC4 | TODO | | +| AC4 | DONE | `docs/containers.md` and the ADR 20260603 amendment; validated by the AI assistant | ## Design evidence diff --git a/docs/issues/open/2670-2482-build-shipped-artifacts-without-test-features/manual-verification-evidence.md b/docs/issues/open/2670-2482-build-shipped-artifacts-without-test-features/manual-verification-evidence.md index 1f6c022c3..a47b0c103 100644 --- a/docs/issues/open/2670-2482-build-shipped-artifacts-without-test-features/manual-verification-evidence.md +++ b/docs/issues/open/2670-2482-build-shipped-artifacts-without-test-features/manual-verification-evidence.md @@ -1,7 +1,7 @@ --- doc-type: manual-verification-evidence issue-spec: docs/issues/open/2670-2482-build-shipped-artifacts-without-test-features/ISSUE.md -last-updated-utc: "2026-10-27 20:13" +last-updated-utc: "2026-10-27 20:14" --- # Manual Verification Evidence From fef7eda68aa98b48e3af8a2d85be1d91e6cc625b Mon Sep 17 00:00:00 2001 From: Jose Celano Date: Tue, 27 Oct 2026 20:52:20 +0000 Subject: [PATCH 08/12] fix(tracker): [#2670] ignore an empty TORRUST_TRACKER_BINARY An empty TORRUST_TRACKER_BINARY made the native-tracker tests spawn an empty path and fail with a confusing NotFound. The empty value is now ignored, like an unset variable. Found by the independent pre-PR review. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- tests/common/native_tracker/command.rs | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/tests/common/native_tracker/command.rs b/tests/common/native_tracker/command.rs index aa7245694..2f31c44a3 100644 --- a/tests/common/native_tracker/command.rs +++ b/tests/common/native_tracker/command.rs @@ -226,6 +226,7 @@ pub(super) fn tracker_binary() -> PathBuf { fn resolve_tracker_binary(variable: impl Fn(&str) -> Option, compiled: PathBuf) -> PathBuf { variable(SHIPPED_BINARY_VARIABLE) + .filter(|path| !path.is_empty()) .or_else(|| variable("NEXTEST_BIN_EXE_torrust-tracker")) .or_else(|| variable("CARGO_BIN_EXE_torrust-tracker")) .map_or(compiled, PathBuf::from) @@ -279,6 +280,21 @@ mod tests { assert_eq!(binary, PathBuf::from("/nextest/torrust-tracker")); } + #[test] + fn it_should_ignore_a_shipped_binary_override_that_is_empty() { + // Arrange: the override is set to the empty string, as `TORRUST_TRACKER_BINARY= cargo test` does. + let variables = [ + ("TORRUST_TRACKER_BINARY", ""), + ("NEXTEST_BIN_EXE_torrust-tracker", "/nextest/torrust-tracker"), + ]; + + // Act + let binary = resolve_tracker_binary(lookup_in(&variables), PathBuf::from("/compiled/torrust-tracker")); + + // Assert + assert_eq!(binary, PathBuf::from("/nextest/torrust-tracker")); + } + #[test] fn it_should_run_the_binary_compiled_into_the_test_when_no_variable_is_set() { // Arrange From 6c75d65334dfb0412513b647e46edb74b1ed75cb Mon Sep 17 00:00:00 2001 From: Jose Celano Date: Tue, 27 Oct 2026 20:53:20 +0000 Subject: [PATCH 09/12] fix(shipped-features-check): [#2670] fail instead of passing on tree output it cannot read The check printed checked and exited 0 when no line of the cargo tree output parsed as a workspace package, which a change in how Cargo prints paths would cause, and then a real violation would pass. It now fails with a runtime error unless the shipped package itself is listed as a workspace package. A checkout path that contains the separator is now read correctly, and the scratch tests pin a renamed dependency and a feature that implies testing. Found by the independent pre-PR review. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../checks/shipped-features-check/src/lib.rs | 61 ++++++++++++++++++- .../checks/shipped-features-check/src/main.rs | 8 ++- .../shipped-features-check/tests/cli.rs | 59 +++++++++++++++++- 3 files changed, 124 insertions(+), 4 deletions(-) diff --git a/contrib/dev-tools/checks/shipped-features-check/src/lib.rs b/contrib/dev-tools/checks/shipped-features-check/src/lib.rs index 490ad9f2b..d85d29886 100644 --- a/contrib/dev-tools/checks/shipped-features-check/src/lib.rs +++ b/contrib/dev-tools/checks/shipped-features-check/src/lib.rs @@ -28,7 +28,7 @@ pub fn workspace_packages_with_feature(tree: &str, feature: &str) -> Vec = tree .lines() .filter_map(|line| { - let (package, features) = line.split_once('|')?; + let (package, features) = line.rsplit_once('|')?; let (name, version, location) = split_package(package)?; (is_local_path(location) && has_feature(features, feature)).then(|| Violation { package: name.to_owned(), @@ -41,6 +41,17 @@ pub fn workspace_packages_with_feature(tree: &str, feature: &str) -> Vec bool { + tree.lines().any(|line| { + line.rsplit_once('|') + .and_then(|(package, _)| split_package(package)) + .is_some_and(|(package_name, _, location)| package_name == name && is_local_path(location)) + }) +} + /// Splits ` v[ ()][ (proc-macro)]` into its name, version and location. /// /// The location is the text inside the parentheses that follow the version, or an empty string when @@ -76,7 +87,7 @@ fn has_feature(features: &str, feature: &str) -> bool { #[cfg(test)] mod tests { - use super::{Violation, workspace_packages_with_feature}; + use super::{Violation, has_workspace_package, workspace_packages_with_feature}; // Output of `cargo tree -p b -e normal,build --prefix none -f "{p}|{f}"` on a scratch workspace where // `b` depends on `a` and on `c`, and only `c` enables `a`'s `testing` feature. @@ -201,6 +212,52 @@ proc-macro2 v1.0.107|default,proc-macro (*) assert_eq!(violations, vec![violation("macros", "v0.1.0")]); } + #[test] + fn it_should_read_a_workspace_path_that_contains_a_pipe() { + // Arrange: a checkout whose directory name contains the `|` that separates the package from its features. + let tree = "a v0.1.0 (/home/some|user/a)|testing\n"; + + // Act + let violations = workspace_packages_with_feature(tree, "testing"); + + // Assert + assert_eq!(violations, vec![violation("a", "v0.1.0")]); + } + + #[test] + fn it_should_find_a_workspace_package_by_name_in_the_tree() { + // Arrange: the shipped package `b` is the root line of a tree whose other packages are not it. + + // Act + let found = has_workspace_package(TRANSITIVE_TREE, "b"); + + // Assert + assert!(found); + } + + #[test] + fn it_should_not_find_a_package_that_is_absent_from_the_tree() { + // Arrange: the tree has the workspace packages `a`, `b` and `c`. + + // Act + let found = has_workspace_package(TRANSITIVE_TREE, "missing"); + + // Assert + assert!(!found); + } + + #[test] + fn it_should_not_find_a_package_whose_line_has_no_local_path() { + // Arrange: the tree's root line is not in the format of a workspace package, as if `cargo tree` changed it. + let tree = "b v0.1.0|\na v0.1.0 (/scratch/a)|\n"; + + // Act + let found = has_workspace_package(tree, "b"); + + // Assert + assert!(!found); + } + #[test] fn it_should_skip_lines_that_are_not_package_lines() { // Arrange: blank lines and a line without the `|` separator. diff --git a/contrib/dev-tools/checks/shipped-features-check/src/main.rs b/contrib/dev-tools/checks/shipped-features-check/src/main.rs index ccf235974..351b203d2 100644 --- a/contrib/dev-tools/checks/shipped-features-check/src/main.rs +++ b/contrib/dev-tools/checks/shipped-features-check/src/main.rs @@ -10,7 +10,7 @@ use std::io::{self, Write}; use std::process::{Command, ExitCode}; use serde::Serialize; -use shipped_features_check::workspace_packages_with_feature; +use shipped_features_check::{has_workspace_package, workspace_packages_with_feature}; const EXIT_FAILURE: u8 = 1; const EXIT_USAGE_ERROR: u8 = 2; @@ -84,6 +84,12 @@ fn check() -> Result, Failure> { for package in &arguments.packages { let tree = cargo_tree(package, arguments.features.as_deref()).map_err(Failure::Runtime)?; + if !has_workspace_package(&tree, package) { + return Err(Failure::Runtime(format!( + "`cargo tree --package {package}` did not list `{package}` as a workspace package, so the check cannot \ + tell whether a feature reaches it; the format of the `cargo tree` output may have changed" + ))); + } let violations = workspace_packages_with_feature(&tree, &arguments.forbidden_feature); if violations.is_empty() { diff --git a/contrib/dev-tools/checks/shipped-features-check/tests/cli.rs b/contrib/dev-tools/checks/shipped-features-check/tests/cli.rs index 4ac2cd863..7bf18d6ae 100644 --- a/contrib/dev-tools/checks/shipped-features-check/tests/cli.rs +++ b/contrib/dev-tools/checks/shipped-features-check/tests/cli.rs @@ -29,7 +29,7 @@ impl Scratch { write( root, "a/Cargo.toml", - &package("a", "[features]\ntesting = []\ninternal = []\n"), + &package("a", "[features]\ntesting = []\ninternal = []\nchained = [\"testing\"]\n"), ); write(root, "a/src/lib.rs", "pub fn a() {}\n"); write( @@ -169,6 +169,63 @@ fn it_should_check_the_feature_named_by_the_forbidden_feature_option() { ); } +#[test] +fn it_should_fail_when_the_shipped_package_enables_the_testing_feature_through_a_renamed_dependency() { + // Arrange: `b` depends on `a` under another name, with `testing` on. + let scratch = Scratch::new( + "[dependencies]\nrenamed = { package = \"a\", path = \"../a\", features = [\"testing\"] }\n", + "", + ); + + // Act + let output = scratch.run(&["--package", "b"]); + + // Assert + assert_eq!(output.status.code(), Some(1)); + assert_eq!(of_kind(&diagnostics(&output), "validation_error")[0]["package"], "a"); +} + +#[test] +fn it_should_fail_when_the_shipped_package_enables_a_feature_that_implies_the_testing_feature() { + // Arrange: `a`'s `chained` feature turns on its `testing` feature, and `b` enables only `chained`. + let scratch = Scratch::new("[dependencies]\na = { path = \"../a\", features = [\"chained\"] }\n", ""); + + // Act + let output = scratch.run(&["--package", "b"]); + + // Assert + assert_eq!(output.status.code(), Some(1)); + assert_eq!(of_kind(&diagnostics(&output), "validation_error")[0]["feature"], "testing"); +} + +#[cfg(unix)] +#[test] +fn it_should_report_a_runtime_error_when_the_shipped_package_is_not_in_the_cargo_tree_output() { + // Arrange: a `cargo` whose `tree` prints a line in a format the check does not read as a workspace package, as if + // a new Cargo version printed paths differently. Passing the check on that output would hide a real violation. + use std::os::unix::fs::PermissionsExt as _; + + let scratch = Scratch::new("", ""); + let fake_cargo = scratch.directory.path().join("fake-cargo"); + write(scratch.directory.path(), "fake-cargo", "#!/bin/sh\necho 'b v0.1.0|testing'\n"); + fs::set_permissions(&fake_cargo, fs::Permissions::from_mode(0o755)).expect("make the fake cargo executable"); + + // Act + let output = Command::new(env!("CARGO_BIN_EXE_shipped-features-check")) + .args(["--package", "b"]) + .env("CARGO", &fake_cargo) + .current_dir(scratch.directory.path()) + .output() + .expect("run shipped-features-check"); + + // Assert + assert_eq!(output.status.code(), Some(1)); + assert_eq!(output.stdout, b""); + let records = diagnostics(&output); + assert_eq!(records[0]["kind"], "runtime_error"); + assert!(records[0]["message"].as_str().unwrap().contains("`b`")); +} + #[test] fn it_should_report_a_usage_error_when_no_package_is_named() { // Arrange From 795b9b7a18c766e201da04c1cedd10c45bc118c9 Mon Sep 17 00:00:00 2001 From: Jose Celano Date: Tue, 27 Oct 2026 21:01:55 +0000 Subject: [PATCH 10/12] docs(issues): [#2670] record the pre-PR review, the final image and the completion review Record the independent pre-PR review and what was done with each finding, the final image evidence (1539 tests, binaries identical to the build_shipped stage, healthy), the implementation completion review and the retrospective, and tick the checkpoints that are true. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../ISSUE.md | 35 ++++++-- .../implementation-retrospective.md | 90 +++++++++++++++++++ .../manual-verification-evidence.md | 56 +++++++++++- 3 files changed, 173 insertions(+), 8 deletions(-) create mode 100644 docs/issues/open/2670-2482-build-shipped-artifacts-without-test-features/implementation-retrospective.md diff --git a/docs/issues/open/2670-2482-build-shipped-artifacts-without-test-features/ISSUE.md b/docs/issues/open/2670-2482-build-shipped-artifacts-without-test-features/ISSUE.md index 4f28a69f2..b62b32a3e 100644 --- a/docs/issues/open/2670-2482-build-shipped-artifacts-without-test-features/ISSUE.md +++ b/docs/issues/open/2670-2482-build-shipped-artifacts-without-test-features/ISSUE.md @@ -9,7 +9,7 @@ github-issue: 2670 spec-path: docs/issues/open/2670-2482-build-shipped-artifacts-without-test-features/ISSUE.md branch: "2670-2482-build-shipped-artifacts-without-test-features" related-pr: null -last-updated-utc: "2026-10-27 20:14" +last-updated-utc: "2026-10-27 21:00" semantic-links: skill-links: - create-issue @@ -291,12 +291,12 @@ Status values: `TODO`, `IN_PROGRESS`, `BLOCKED`, `DONE`. - [x] Folder-style spec drafted in `docs/issues/open/2670-2482-build-shipped-artifacts-without-test-features/ISSUE.md` - [x] Spec reviewed and approved by user/maintainer (approved by the maintainer through `ask_user` and chat on 2026-10-27, including the `cargo tree` refinement of D3) - [x] GitHub issue created, linked as a sub-issue of #2482 (`parent_issue_url` verified), and its number added to this spec -- [ ] Spec-only PR merged into `develop` before implementation -- [ ] Implementation completed -- [ ] Automatic verification completed (`linter all`, relevant tests, and pre-push checks) +- [x] Spec-only PR merged into `develop` before implementation (PR #2671) +- [x] Implementation completed +- [x] Automatic verification completed (`linter all` through the pre-commit gate on every commit, `cargo test` of the check crate and the native-tracker targets, and the full pre-push suite on 2026-10-27) - [ ] Manual verification scenarios executed and recorded in issue-local `manual-verification-evidence.md` - [ ] Acceptance criteria reviewed after implementation and updated with evidence -- [ ] Evidence-based implementation completion review recorded +- [x] Evidence-based implementation completion review recorded (retrospective created) - [ ] Issue closed and spec moved from `docs/issues/open/` to `docs/issues/closed/` ### Progress Log @@ -323,6 +323,7 @@ Status values: `TODO`, `IN_PROGRESS`, `BLOCKED`, `DONE`. - 2026-10-27 14:57 UTC - AI assistant (Copilot SDK in VS Code, #2482 subissue 9) - Added the `Shipped Features` job to `testing.yaml` (T4), which runs `cargo run --package shipped-features-check -- --package torrust-tracker`; `linter yaml` and `git diff --check` pass and the same command passes locally (V4). The spec's commit point asks for the job to pass in a fork or pull request first, but the implementation pull request is the first hosted run, so T4 stays IN_PROGRESS and M6 stays TODO until that run is recorded. - 2026-10-27 20:13 UTC - AI assistant (Copilot SDK in VS Code, #2482 subissue 9) - T5 is done: the `Containerfile` has shipped cook and build stages for the release and debug images (`cargo build` without test flags or features), the `test` stages run the native-tracker tests against the shipped binary through `TORRUST_TRACKER_BINARY`, and `/app/bin` takes the shipped binaries. Both targets build; 0 of the 1531 baseline tests are missing and 6 were added (T2's tests); cold builds cost +17 s (release) and +18 s (debug) and the warm release rebuild +8 s (V2). The override is honoured: pointing it at a missing file fails the native-tracker tests (V3). An interrupted warm run and a fully cached rerun were discarded and the warm run repeated with a different change. - 2026-10-27 20:14 UTC - AI assistant (Copilot SDK in VS Code, #2482 subissue 9) - T6 is done: `docs/containers.md` describes the test build, the shipped build, `TORRUST_TRACKER_BINARY` and the check, and ADR 20260603 carries an amendment note that narrows its "exact binary" claim. Ticked AC1, AC2 and AC4 (validated by the AI assistant against V0 to V3). AC3 stays open until the `Shipped Features` job has run on the implementation pull request (M6). +- 2026-10-27 21:00 UTC - AI assistant (Copilot SDK in VS Code, #2482 subissue 9) - The independent Task Reviewer's pre-PR review (PASS WITH FINDINGS, no blocking defect) led to three fixes: the check now fails when `cargo tree` output does not list the shipped package as a workspace package, it reads a checkout path that contains the separator, and an empty `TORRUST_TRACKER_BINARY` is ignored; the maintainer approved the test increments through `ask_user`. Rebased onto `develop`, rebuilt the final image (1539 tests, binaries identical to the `build_shipped` stage, healthy; V2b) and ran the full pre-push suite. Wrote the implementation completion review and the retrospective. M6 and AC3 stay open until the `Shipped Features` job has run on the pull request. ## Acceptance Criteria @@ -413,8 +414,28 @@ reports `testing`, so it would flag a build that does not have the feature. ## Implementation Completion Review -- Retrospective: `Not yet assessed` -- If no retrospective is needed, add a progress-log entry saying why. +Reviewed by the AI assistant against the acceptance criteria and the evidence on 2026-10-27, after an +independent Task Reviewer pre-PR review (evidence, "Pre-PR review"); the maintainer reviews it in the pull +request. + +- Scope: the change is in `Containerfile` (shipped cook and build stages, test stages), `tests/common/native_tracker/` + (the override), the new `contrib/dev-tools/checks/shipped-features-check` crate (registered in `Cargo.toml` and + `.dockerignore`, listed in `AGENTS.md` and the CLI output contract ADR), the `Shipped Features` job of + `testing.yaml`, `docs/containers.md` and the amendment note of ADR 20260603. No test support code moved, no + `testing` feature was added and the test archives keep `--all-features` (D5). +- Design: the shipped binaries come from `build_shipped` stages stacked on the third-party test cooks (D6); the + measured cost is +17 s (release) and +18 s (debug) cold and +8 s on a warm rebuild. The check reads + `cargo tree -e normal,build` because `cargo metadata` counts dev-dependency features (D3). It follows the global + CLI output contract. +- Known limits: the check covers workspace packages only (third-party features are outside it) and only the + features `cargo build -p torrust-tracker` enables on the host, so target-specific dependencies of another + platform are not seen. The job names the shipped package, and the `Containerfile` names it too; only two comments + keep them in step (D4, review finding 6). The shipped build names no feature today. `AC3` is open until the + `Shipped Features` job has run on the pull request (M6); the check passes vacuously on the real workspace because + no `testing` feature exists yet, and the scratch tests prove it fails when one reaches a shipped package. +- Retrospective: created, [`implementation-retrospective.md`](implementation-retrospective.md): `cargo metadata` + proved unusable for the check, and the independent review found that the check could pass on output it could not + read. ## References diff --git a/docs/issues/open/2670-2482-build-shipped-artifacts-without-test-features/implementation-retrospective.md b/docs/issues/open/2670-2482-build-shipped-artifacts-without-test-features/implementation-retrospective.md new file mode 100644 index 000000000..b6935c2b2 --- /dev/null +++ b/docs/issues/open/2670-2482-build-shipped-artifacts-without-test-features/implementation-retrospective.md @@ -0,0 +1,90 @@ +--- +semantic-links: + skill-links: + - write-markdown-docs + related-artifacts: + - docs/issues/open/2670-2482-build-shipped-artifacts-without-test-features/ISSUE.md + - docs/issues/open/2670-2482-build-shipped-artifacts-without-test-features/manual-verification-evidence.md + - docs/adrs/20261021190318_share_workspace_test_support_through_testing_features.md +--- + +# Implementation Retrospective - Build the shipped container binary without test features + +## Purpose + +Record evidence-based process improvements discovered while implementing issue #2670 (subissue 9 +of #2482). This is a blameless review of the implementation approach; it does not replace the +acceptance-criteria verification. + +## Outcome + +The `Containerfile` builds `torrust-tracker` and `http_health_check` with `cargo build` invocations +of their own, without `--tests` or `--all-features`, and ships those binaries; the nextest archive +still builds and runs the tests. The native-tracker tests run against the shipped binary through +`TORRUST_TRACKER_BINARY`. A new Rust tool, `shipped-features-check`, run by the `Shipped Features` +job of `testing.yaml`, fails when a workspace `testing` feature is enabled in the shipped package's +tree. Evidence: [`manual-verification-evidence.md`](manual-verification-evidence.md). + +## What Went Well + +1. The spec asked for a scratch-workspace red case because no real crate has a `testing` feature + yet, so the check was proved able to fail before it had anything real to fail on. +2. Every test was run red before green, and the negative tests that passed against the first stub + were checked with two mutations, so they are known to fail when the code is wrong. +3. The measured baseline (1531 tests, their list, and the build times) made the later claims + checkable: 0 baseline tests missing, and the image binaries byte-identical to the build stage. + +## What Changed During Implementation + +1. **`cargo metadata` could not be the check's input.** The maintainer chose "a Rust check over + `cargo metadata`" before any experiment existed. A scratch workspace showed that `cargo metadata` + unifies features across the workspace and counts a dev-dependency's request, so it reports + `testing` where `cargo build -p` does not enable it. The design moved to `cargo tree + -e normal,build` and the maintainer confirmed the change at spec approval. The experiment cost + a few minutes and avoided a check that would fail correct builds. +2. **The check could pass silently.** The independent pre-PR review found that if `cargo tree` ever + printed paths differently, no line would parse as a workspace package and the check would print + `checked` and exit 0. That is the worst failure for a guard, and no test of mine covered it + because every test used output the parser already understood. The check now fails unless the + shipped package itself is listed. +3. **The same review found a path that contains the field separator**, and an empty + `TORRUST_TRACKER_BINARY` that spawned an empty path. +4. **Process losses.** A `/tmp` cleanup removed the first baseline build's worktree and logs + mid-build, so the baseline was redone; an interrupted warm build made its rerun a full cache + hit (2 s), which would have shown a meaningless "warm" time had I not compared it with the + build's own log. A GPG timeout on one commit needed a retry with fresh stamps. + +## Root Cause + +1. The input of the check was chosen from the tool's name and its documentation, not from its + behaviour on the one case that matters (a feature requested only by a dev-dependency). +2. Tests of a parser describe the output the author has seen. A guard has a second duty: to refuse + output it has not seen, and nothing in the spec or the test plan named that duty. +3. Scratch files for a long build went under `/tmp`, which this host cleans. + +## Improvements for Future Work + +1. Before choosing the input of a check, run each candidate on the one input where the candidates + disagree (here: a feature only a dev-dependency enables) and record the outputs in the spec. +2. For a check that passes when it finds nothing, add a test where the input is unreadable and + require the check to fail, and make that a line of the spec's test plan. A pass must mean "I + looked and found nothing", never "I could not look". +3. Keep scratch checkouts, logs and measurements for a long build under a directory the host does + not clean (here `~/.cache//`), and take a baseline before touching the tree. +4. When a measurement looks too good (a "warm" build in 2 s), read the build log for cache hits + before recording it. + +## Avoiding Overcorrection + +A lint that compares the `Containerfile`'s shipped `cargo build` flags with the `testing.yaml` job +(review finding 6) is not justified yet: the shipped build names no feature today, two comments +name each other, and `docs/containers.md` says to keep them in step. Revisit it when a shipped +build first names a feature. + +## Evidence + +- [`ISSUE.md`](ISSUE.md) and [`manual-verification-evidence.md`](manual-verification-evidence.md) +- Commits `ci(container): [#2670] build the shipped binaries without test features`, + `feat(shipped-features-check): [#2670] fail when a testing feature reaches a shipped package`, + `fix(shipped-features-check): [#2670] fail instead of passing on tree output it cannot read` and + `fix(tracker): [#2670] ignore an empty TORRUST_TRACKER_BINARY` diff --git a/docs/issues/open/2670-2482-build-shipped-artifacts-without-test-features/manual-verification-evidence.md b/docs/issues/open/2670-2482-build-shipped-artifacts-without-test-features/manual-verification-evidence.md index a47b0c103..c0852b377 100644 --- a/docs/issues/open/2670-2482-build-shipped-artifacts-without-test-features/manual-verification-evidence.md +++ b/docs/issues/open/2670-2482-build-shipped-artifacts-without-test-features/manual-verification-evidence.md @@ -1,7 +1,7 @@ --- doc-type: manual-verification-evidence issue-spec: docs/issues/open/2670-2482-build-shipped-artifacts-without-test-features/ISSUE.md -last-updated-utc: "2026-10-27 20:14" +last-updated-utc: "2026-10-27 21:00" --- # Manual Verification Evidence @@ -277,3 +277,57 @@ corrects it. Green: with the variable set to the shipped binary (the committed `Containerfile`), V2's `test` stage runs 1537 tests and all pass. + +## V2b - The final image on the final tree (task T5, review finding 7) + +Tree: the branch after the rebase onto `develop` and after the fixes of the pre-PR review below. Commands: + +```bash +docker build --progress=plain --target release --tag r9-final:release --file Containerfile . +docker build --target build_shipped --tag r9-final:build-shipped --file Containerfile . +# copy the binaries out of the final image and out of the build_shipped stage with `docker create` and `docker cp` +sha256sum image-* stage-* +docker run -d --name r9-health -e USER_ID=$(id -u) -v :/var/lib/torrust/tracker \ + -v :/var/log/torrust/tracker -v :/etc/torrust/tracker r9-final:release +docker inspect --format '{{.State.Health.Status}}' r9-health +``` + +```text +#48 0.203 Starting 1539 tests across 40 binaries +#48 10.99 Summary [ 10.792s] 1539 tests run: 1539 passed, 0 skipped + +12a7bb377d719976d141521060f4499fa0940ac9ad2eff352759913cc8218c17 image-http_health_check +68396f9f62c08e59a5e5878fcd04c4d4d301e976199816f393624abda4ce0a11 image-torrust-tracker +12a7bb377d719976d141521060f4499fa0940ac9ad2eff352759913cc8218c17 stage-http_health_check +68396f9f62c08e59a5e5878fcd04c4d4d301e976199816f393624abda4ce0a11 stage-torrust-tracker + +t+5s health=starting +t+10s health=healthy +running=true exit=0 +``` + +The test count is the 1531 of V1 plus the 8 tests this subissue adds (the 3 tests of T2 and the empty-override test, each +compiled into two test binaries). The binaries in the final image are byte-identical to the ones the `build_shipped` +stage built, so the image ships the build without test features, and the image's HEALTHCHECK passes with the shipped +`http_health_check`. + +## Pre-PR review (independent Task Reviewer) + +Verdict: PASS WITH FINDINGS, no blocking defect. The reviewer ran the check crate's tests, clippy, `linter all`, and a +full `docker build --target release` (1537 tests passed, the container healthy). Findings and what was done: + +| # | Finding | Severity | Disposition | +| --- | --- | --- | --- | +| 1 | AC3, T4 and M6 need the hosted run; completion review and retrospective missing | Medium (process) | The spec already left them open; the hosted run is recorded after the pull request's first run, and the completion review and retrospective are written in this subissue | +| 2 | The check passed silently when `cargo tree` output stopped parsing as workspace packages | Low | Fixed: `has_workspace_package` and a runtime error unless the shipped package is listed as a workspace package; test red (exit 0) then green (exit 1) | +| 3 | `split_once('\|')` mis-read a checkout path that contains `\|` | Low | Fixed: `rsplit_once`; unit test red then green | +| 4 | No scratch case for a renamed dependency or a feature that implies `testing` | Low | Two CLI tests added; both passed at once, so they pin existing behaviour. A `default = ["testing"]` case was not added: it would put the feature in every scratch test, and the chained feature exercises the same Cargo resolution | +| 5 | An empty `TORRUST_TRACKER_BINARY` spawned an empty path | Low | Fixed: the empty value is ignored; unit test red (`left: ""`) then green | +| 6 | The workflow and the `Containerfile` can drift (D4) | Low | Accepted as decided in D4: comments in both files name each other. A lint comparing them is possible follow-up work | +| 7 | No evidence on the final image | Low | V2b above | +| 8 | Branch behind `develop` | Low | Rebased; none of the changed files had changed on `develop` | + +The maintainer (josecelano) reviewed the test increments of findings 2 to 5 and approved them through `ask_user` before +their commits. Results after the fixes: `cargo test -p shipped-features-check` reports 13 unit and 10 CLI tests passing, +`cargo clippy -p shipped-features-check --all-targets -- -D warnings` reports nothing, and the check still passes on the +real workspace. From 3d08dd5320ed33e148d150af06fdbf2c317ea671 Mon Sep 17 00:00:00 2001 From: Jose Celano Date: Wed, 28 Oct 2026 07:02:36 +0000 Subject: [PATCH 11/12] fix(tracker): [#2670] anchor a relative TORRUST_TRACKER_BINARY at the test working directory A relative override such as ./target/release/torrust-tracker made the configuration test that changes the child's working directory fail to spawn, because the child looks the path up inside that directory. The resolved path is now joined to the test process's working directory when it is relative and has a directory part; an absolute path and a bare name keep their behaviour. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- tests/common/native_tracker/command.rs | 58 ++++++++++++++++++++++++-- 1 file changed, 55 insertions(+), 3 deletions(-) diff --git a/tests/common/native_tracker/command.rs b/tests/common/native_tracker/command.rs index 2f31c44a3..5a358ae29 100644 --- a/tests/common/native_tracker/command.rs +++ b/tests/common/native_tracker/command.rs @@ -218,10 +218,25 @@ fn write_configuration_at(config_path: &Path, storage_path: &Path, health_check_ const SHIPPED_BINARY_VARIABLE: &str = "TORRUST_TRACKER_BINARY"; pub(super) fn tracker_binary() -> PathBuf { - resolve_tracker_binary( + let binary = resolve_tracker_binary( |name| std::env::var_os(name), PathBuf::from(env!("CARGO_BIN_EXE_torrust-tracker")), - ) + ); + + anchored_at(binary, &std::env::current_dir().expect("read the test working directory")) +} + +/// Makes a relative executable path that has a directory part independent of the child's working directory. +/// +/// A child whose working directory is changed (`Command::current_dir`) looks a relative path up inside that +/// directory, so `./target/release/torrust-tracker` must be anchored at the test's working directory first. A bare +/// name has no directory part and keeps its `PATH` lookup. +fn anchored_at(binary: PathBuf, working_directory: &Path) -> PathBuf { + if binary.is_relative() && binary.components().count() > 1 { + working_directory.join(binary) + } else { + binary + } } fn resolve_tracker_binary(variable: impl Fn(&str) -> Option, compiled: PathBuf) -> PathBuf { @@ -238,7 +253,7 @@ mod tests { use std::net::{IpAddr, Ipv4Addr, SocketAddr}; use std::path::{Path, PathBuf}; - use super::{resolve_tracker_binary, tracker_command, write_configuration}; + use super::{anchored_at, resolve_tracker_binary, tracker_command, write_configuration}; fn lookup_in<'a>(variables: &'a [(&'a str, &'a str)]) -> impl Fn(&str) -> Option + 'a { move |name| { @@ -280,6 +295,43 @@ mod tests { assert_eq!(binary, PathBuf::from("/nextest/torrust-tracker")); } + #[test] + fn it_should_anchor_a_relative_binary_path_at_the_test_working_directory() { + // Arrange: a path with a directory part, such as `./target/release/torrust-tracker`; a child that changes + // its working directory would otherwise look the executable up inside that directory. + let binary = PathBuf::from("./target/release/torrust-tracker"); + + // Act + let anchored = anchored_at(binary, Path::new("/workspace")); + + // Assert + assert_eq!(anchored, PathBuf::from("/workspace/./target/release/torrust-tracker")); + } + + #[test] + fn it_should_leave_an_absolute_binary_path_as_it_is() { + // Arrange + let binary = PathBuf::from("/shipped/torrust-tracker"); + + // Act + let anchored = anchored_at(binary, Path::new("/workspace")); + + // Assert + assert_eq!(anchored, PathBuf::from("/shipped/torrust-tracker")); + } + + #[test] + fn it_should_leave_a_bare_binary_name_for_the_path_lookup() { + // Arrange: no directory part, so the operating system finds the executable through `PATH`. + let binary = PathBuf::from("torrust-tracker"); + + // Act + let anchored = anchored_at(binary, Path::new("/workspace")); + + // Assert + assert_eq!(anchored, PathBuf::from("torrust-tracker")); + } + #[test] fn it_should_ignore_a_shipped_binary_override_that_is_empty() { // Arrange: the override is set to the empty string, as `TORRUST_TRACKER_BINARY= cargo test` does. From 248f95836e6592f36ff2c3739a190b983dffaeb3 Mon Sep 17 00:00:00 2001 From: Jose Celano Date: Wed, 28 Oct 2026 07:07:35 +0000 Subject: [PATCH 12/12] docs(pr-reviews): [#2670] record the first review round of PR 2676 Record the Copilot finding about relative TORRUST_TRACKER_BINARY paths and its fix, the hosted run of the Shipped Features job (V6) and the relative-path reproduction (V7), set M6 and AC3 to done and the spec status to in-review. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../ISSUE.md | 23 ++-- .../manual-verification-evidence.md | 39 ++++++- docs/pr-reviews/pr-2676-review/PR-REVIEW.md | 100 ++++++++++++++++++ 3 files changed, 150 insertions(+), 12 deletions(-) create mode 100644 docs/pr-reviews/pr-2676-review/PR-REVIEW.md diff --git a/docs/issues/open/2670-2482-build-shipped-artifacts-without-test-features/ISSUE.md b/docs/issues/open/2670-2482-build-shipped-artifacts-without-test-features/ISSUE.md index b62b32a3e..a0d1af9ff 100644 --- a/docs/issues/open/2670-2482-build-shipped-artifacts-without-test-features/ISSUE.md +++ b/docs/issues/open/2670-2482-build-shipped-artifacts-without-test-features/ISSUE.md @@ -2,14 +2,14 @@ schema-version: 1 doc-type: issue issue-type: task -status: in-progress +status: in-review priority: p2 epic: 2482 github-issue: 2670 spec-path: docs/issues/open/2670-2482-build-shipped-artifacts-without-test-features/ISSUE.md branch: "2670-2482-build-shipped-artifacts-without-test-features" -related-pr: null -last-updated-utc: "2026-10-27 21:00" +related-pr: 2676 +last-updated-utc: "2026-10-28 07:07" semantic-links: skill-links: - create-issue @@ -294,8 +294,8 @@ Status values: `TODO`, `IN_PROGRESS`, `BLOCKED`, `DONE`. - [x] Spec-only PR merged into `develop` before implementation (PR #2671) - [x] Implementation completed - [x] Automatic verification completed (`linter all` through the pre-commit gate on every commit, `cargo test` of the check crate and the native-tracker targets, and the full pre-push suite on 2026-10-27) -- [ ] Manual verification scenarios executed and recorded in issue-local `manual-verification-evidence.md` -- [ ] Acceptance criteria reviewed after implementation and updated with evidence +- [x] Manual verification scenarios executed and recorded in issue-local `manual-verification-evidence.md` (M1 to M6) +- [x] Acceptance criteria reviewed after implementation and updated with evidence (AC1 to AC4; the container workflow criterion stays open until that job is green) - [x] Evidence-based implementation completion review recorded (retrospective created) - [ ] Issue closed and spec moved from `docs/issues/open/` to `docs/issues/closed/` @@ -324,6 +324,7 @@ Status values: `TODO`, `IN_PROGRESS`, `BLOCKED`, `DONE`. - 2026-10-27 20:13 UTC - AI assistant (Copilot SDK in VS Code, #2482 subissue 9) - T5 is done: the `Containerfile` has shipped cook and build stages for the release and debug images (`cargo build` without test flags or features), the `test` stages run the native-tracker tests against the shipped binary through `TORRUST_TRACKER_BINARY`, and `/app/bin` takes the shipped binaries. Both targets build; 0 of the 1531 baseline tests are missing and 6 were added (T2's tests); cold builds cost +17 s (release) and +18 s (debug) and the warm release rebuild +8 s (V2). The override is honoured: pointing it at a missing file fails the native-tracker tests (V3). An interrupted warm run and a fully cached rerun were discarded and the warm run repeated with a different change. - 2026-10-27 20:14 UTC - AI assistant (Copilot SDK in VS Code, #2482 subissue 9) - T6 is done: `docs/containers.md` describes the test build, the shipped build, `TORRUST_TRACKER_BINARY` and the check, and ADR 20260603 carries an amendment note that narrows its "exact binary" claim. Ticked AC1, AC2 and AC4 (validated by the AI assistant against V0 to V3). AC3 stays open until the `Shipped Features` job has run on the implementation pull request (M6). - 2026-10-27 21:00 UTC - AI assistant (Copilot SDK in VS Code, #2482 subissue 9) - The independent Task Reviewer's pre-PR review (PASS WITH FINDINGS, no blocking defect) led to three fixes: the check now fails when `cargo tree` output does not list the shipped package as a workspace package, it reads a checkout path that contains the separator, and an empty `TORRUST_TRACKER_BINARY` is ignored; the maintainer approved the test increments through `ask_user`. Rebased onto `develop`, rebuilt the final image (1539 tests, binaries identical to the `build_shipped` stage, healthy; V2b) and ran the full pre-push suite. Wrote the implementation completion review and the retrospective. M6 and AC3 stay open until the `Shipped Features` job has run on the pull request. +- 2026-10-28 07:07 UTC - AI assistant (Copilot SDK in VS Code, #2482 subissue 9) - Opened PR #2676. The `Shipped Features` job ran green on a hosted runner (M6, V6), so AC3 and M6 are done and the status is `in-review`. Copilot's review found that a relative `TORRUST_TRACKER_BINARY` broke the configuration test that changes the child's working directory (F1 of the audit of PR #2676): reproduced (V7) and fixed by anchoring a relative path at the test working directory, which also fixes the example of `docs/containers.md`. The container workflow criterion stays open until that job is green. ## Acceptance Criteria @@ -334,7 +335,7 @@ Status values: `TODO`, `IN_PROGRESS`, `BLOCKED`, `DONE`. explicit features; the `test` and `test_debug` stages still run the nextest archive, every baseline test (T1) is still present and passing, the delta for new tests is recorded, and the native-tracker tests run against the shipped binary. -- [ ] AC3: The check fails when a workspace `testing` feature is enabled in a shipped package's +- [x] AC3: The check fails when a workspace `testing` feature is enabled in a shipped package's normal or build dependency tree (shown by an automated scratch-workspace test), passes when only a dev-dependency enables it, and passes on the real workspace; the `testing.yaml` job runs it. @@ -363,7 +364,7 @@ applies, except that this subissue moves no test code. The workflow changes foll | M3 | The override is honoured | In the `test` stage, point `TORRUST_TRACKER_BINARY` at a missing file, build, then restore it | The native-tracker tests fail with the missing path (red), then pass (green) | DONE | V3 | | M4 | The shipped binary has no `testing` feature | `cargo run -p shipped-features-check -- --package torrust-tracker` and `cargo tree -p torrust-tracker -e normal,build -f "{p} {f}"` | Passes; no `testing` feature (vacuous today, since none exists) | DONE | V4 | | M5 | The check fails when it should | Run the scratch-workspace red case (a shipped package enabling a `testing` feature through a normal dependency) | The check fails and names the package and feature | DONE | V5 | -| M6 | The workflow job runs | Open the pull request and read the `testing.yaml` job result | The job is green on the real workspace | TODO | V6 | +| M6 | The workflow job runs | Open the pull request and read the `testing.yaml` job result | The job is green on the real workspace | DONE | V6 | ### Acceptance Verification @@ -371,7 +372,7 @@ applies, except that this subissue moves no test code. The workflow changes foll | ----- | ---------------------- | -------- | | AC1 | DONE | V0 (audit) and V2 (build log); validated by the AI assistant | | AC2 | DONE | V2 (builds, 0 of 1531 baseline tests missing, 6 added) and V3 (override); validated by the AI assistant | -| AC3 | TODO | | +| AC3 | DONE | V4, V5 and V6 (hosted run); validated by the AI assistant | | AC4 | DONE | `docs/containers.md` and the ADR 20260603 amendment; validated by the AI assistant | ## Design evidence @@ -430,9 +431,9 @@ request. - Known limits: the check covers workspace packages only (third-party features are outside it) and only the features `cargo build -p torrust-tracker` enables on the host, so target-specific dependencies of another platform are not seen. The job names the shipped package, and the `Containerfile` names it too; only two comments - keep them in step (D4, review finding 6). The shipped build names no feature today. `AC3` is open until the - `Shipped Features` job has run on the pull request (M6); the check passes vacuously on the real workspace because - no `testing` feature exists yet, and the scratch tests prove it fails when one reaches a shipped package. + keep them in step (D4, review finding 6). The shipped build names no feature today. The `Shipped Features` job + has run green on the pull request (M6, V6) and the check passes vacuously on the real workspace because no + `testing` feature exists yet; the scratch tests prove it fails when one reaches a shipped package. - Retrospective: created, [`implementation-retrospective.md`](implementation-retrospective.md): `cargo metadata` proved unusable for the check, and the independent review found that the check could pass on output it could not read. diff --git a/docs/issues/open/2670-2482-build-shipped-artifacts-without-test-features/manual-verification-evidence.md b/docs/issues/open/2670-2482-build-shipped-artifacts-without-test-features/manual-verification-evidence.md index c0852b377..46ae95a26 100644 --- a/docs/issues/open/2670-2482-build-shipped-artifacts-without-test-features/manual-verification-evidence.md +++ b/docs/issues/open/2670-2482-build-shipped-artifacts-without-test-features/manual-verification-evidence.md @@ -1,7 +1,7 @@ --- doc-type: manual-verification-evidence issue-spec: docs/issues/open/2670-2482-build-shipped-artifacts-without-test-features/ISSUE.md -last-updated-utc: "2026-10-27 21:00" +last-updated-utc: "2026-10-28 07:07" --- # Manual Verification Evidence @@ -331,3 +331,40 @@ The maintainer (josecelano) reviewed the test increments of findings 2 to 5 and their commits. Results after the fixes: `cargo test -p shipped-features-check` reports 13 unit and 10 CLI tests passing, `cargo clippy -p shipped-features-check --all-targets -- -D warnings` reports nothing, and the check still passes on the real workspace. + +## V6 - The workflow job runs on a hosted runner (scenario M6) + +The `Shipped Features` job of `testing.yaml` ran on the pull request, on `ubuntu-latest` with the stable toolchain, +on two heads of the branch: + +| Head (commit subject) | Run | Job result | +| --- | --- | --- | +| `docs(issues): [#2670] record the pre-PR review, the final image and the completion review` | [run 40583603727](https://github.com/torrust/torrust-tracker/actions/runs/40583603727/job/122469179536) | pass, 22 s | +| `fix(tracker): [#2670] anchor a relative TORRUST_TRACKER_BINARY at the test working directory` | [run 40647792189](https://github.com/torrust/torrust-tracker/actions/runs/40647792189/job/122680559895) | pass, 19 s | + +The job runs `cargo run --quiet --package shipped-features-check -- --package torrust-tracker`, the command of V4, so +it is green on the real workspace. As in V4 it passes because no `testing` feature exists yet; the cases that make it +fail are the scratch-workspace tests of `tests/cli.rs` (V5), which the `Unit` jobs of the same workflow run. + +## V7 - A relative `TORRUST_TRACKER_BINARY` (PR review finding F1) + +Copilot's review of the pull request reported that a relative override breaks the configuration test that changes the +child's working directory. Reproduced before the fix, on a release binary built with +`cargo build --release --package torrust-tracker --bin torrust-tracker`: + +```bash +TORRUST_TRACKER_BINARY=./target/release/torrust-tracker cargo test -p torrust-tracker --test cli-configuration +TORRUST_TRACKER_BINARY=$PWD/target/release/torrust-tracker cargo test -p torrust-tracker --test cli-configuration +``` + +```text +relative: test invalid_sources::it_should_not_search_parent_directories_for_a_relative_cli_configuration_file ... FAILED + spawn tracker executable: Os { code: 2, kind: NotFound, message: "No such file or directory" } + test result: FAILED. 21 passed; 1 failed +absolute: test result: ok. 22 passed; 0 failed +``` + +After the fix, `tracker_binary()` anchors a relative path that has a directory part at the test's working directory (the +unit test of that shape was red against a stub returning the path unchanged, `left: "./target/release/torrust-tracker"`, +then green). The same relative invocation now passes: `test result: ok. 25 passed; 0 failed` for `cli-configuration`, +and `lifecycle-signals` reports `20 passed`. The three new tests add 6 to the image's test count. diff --git a/docs/pr-reviews/pr-2676-review/PR-REVIEW.md b/docs/pr-reviews/pr-2676-review/PR-REVIEW.md new file mode 100644 index 000000000..175e16818 --- /dev/null +++ b/docs/pr-reviews/pr-2676-review/PR-REVIEW.md @@ -0,0 +1,100 @@ +--- +semantic-links: + skill-links: + - process-pr-review + related-artifacts: + - .github/skills/dev/pr-reviews/process-pr-review/SKILL.md + - docs/issues/open/2670-2482-build-shipped-artifacts-without-test-features/ISSUE.md +--- + + + +# PR #2676 Review Audit + +Source: pull-request reviews and inline review threads for . + +## Ownership + +The PR author owns this tracked audit record. Reviewers, including repository review agents, +deliver findings through GitHub and have no repository-artifact obligation. + +- Post-merge workflow approval: N/A + +## Status Values + +- Relationship: `ORIGINAL`, `RE_RAISE_OF:` +- Disposition: `FIXED`, `NO_ACTION`, `SUPERSEDED`, `FOLLOW_UP` +- Thread state: `OPEN`, `RESOLVED`, `NON_RESOLVABLE`, `SUPERSEDED`. `OPEN` applies prospectively + to audits created or updated for approved follow-up work; historical audits remain valid without + bulk migration. +- Severity: `Blocker`, `Major`, `Minor`, `Nit`, `Suggestion`; append `(inferred)` when derived + from free prose. +- Author class: `Copilot`, `Human`, `Unknown` +- Category: `link-integrity`, `formatting`, `metadata`, `testing`, `correctness`, + `documentation`, `maintainability`, `security`, `other` +- An outdated thread whose concern was fixed is `FIXED`/`RESOLVED`, even when GitHub marks the + original thread outdated after the push. For in-PR feedback, use `NO_ACTION`/`SUPERSEDED` only + for a duplicate, superseded, or no-change concern. A post-merge `NO_ACTION` requires maintainer + approval to decline the follow-up work. + +## Findings + +One review was submitted so far: the review of `copilot-pull-request-reviewer` (5674519258) on the head +whose subject is `docs(issues): [#2670] record the pre-PR review, the final image and the completion review`. +It carries no "did not start before the timeout" warning, so it was not re-requested. Its body repeats its +single inline thread, F1, and adds no other row. The Copilot review was requested through the GraphQL +`requestReviews` mutation with the Copilot reviewer bot because the `request_copilot_review` MCP tool failed +with `header mismatch: missing Mcp-Param-owner header` (the same error as on PR #2671 for `repo` and `owner`). + +| Finding ID | Review finding reference | Author class | Severity | Category | Relationship | Disposition | Thread state | +| ---------- | ------------------------ | ------------ | -------- | -------- | ------------ | ----------- | ------------ | +| F1 | review-finding:pr-2676-f1 | Copilot | Major | correctness | ORIGINAL | FIXED | RESOLVED | + +## Finding Details + +### F1 - Resolve relative tracker binary paths before changing working directory + +- PR number: 2676 +- Source review ID: 5674519258 +- Reviewer finding ID: F1 +- Source URL: +- Concern: a relative `TORRUST_TRACKER_BINARY`, such as `./target/release/torrust-tracker`, makes the + `cli-configuration` parent-directory test fail to spawn, because that test changes the child's working directory + and the executable is then looked up inside the temporary fixture. The reviewer asks to resolve relative + executable paths against the test process's working directory, keeping the `PATH` lookup for bare names, and to + add a regression case for the changed-directory fixture. +- Solution: the finding was right, and the documented example in `docs/containers.md` used a relative path, so it + would have documented a failing invocation. `tracker_binary()` now joins a relative path that has a directory part + to the test process's working directory (`anchored_at`), and leaves an absolute path and a bare name as they are. + Three unit tests cover the three shapes. The regression for the changed-directory fixture is the existing + `it_should_not_search_parent_directories_for_a_relative_cli_configuration_file` test run with a relative override + (evidence V7); a second test would need to set a process-wide environment variable, which the unit tests avoid. +- Current-tree verification: `TORRUST_TRACKER_BINARY=./target/release/torrust-tracker cargo test -p torrust-tracker + --test cli-configuration` failed with `NotFound` (21 passed, 1 failed) before the fix and passes 25 tests after it; + an absolute path passed before and after. +- Resolution reference: fix(tracker): [#2670] anchor a relative TORRUST_TRACKER_BINARY at the test working directory +- Follow-up PR URL: N/A +- Reply URL: + +## Processing Log + +- 2026-10-28 07:07 UTC - The review of `copilot-pull-request-reviewer` arrived with one inline thread, F1. It was + reproduced against the tree and confirmed. Fixed in its own signed commit and pushed, then replied on the thread. + The thread is resolved after this record is committed and pushed. + +## Completion Rules + +- Re-derive the reply claim against the current tree before replying or resolving a thread. +- Reply on every resolvable thread before resolving it. +- For an outdated thread whose concern was fixed, record `Disposition=FIXED` and + `Thread state=RESOLVED`, even if GitHub marks the thread outdated after the push. For a + duplicate, superseded, or no-change in-PR thread, reply exactly + `Superseded by : .`, record `Disposition=NO_ACTION` and + `Thread state=SUPERSEDED`, then resolve it. A post-merge `NO_ACTION` requires maintainer + approval to decline the follow-up work. +- A consolidated PR conversation response may cover multiple review rounds only when it names + every review ID and every finding ID with its disposition and resolution reference. Record its + durable URL in each related row. +- Cite a fix by its unique Conventional Commit subject or durable reply URL, never by a branch SHA + that can change after a rebase. +- Refresh review threads using GraphQL and confirm that no unresolved actionable thread remains.