Skip to content

Commit c1ebdc7

Browse files
committed
Merge #1705: ci(tracker-core): add DB compatibility matrix coverage for issue #1703
f523745 test(tracker-core): clarify db compatibility test usage (Jose Celano) 6342067 ci(tracker-core): add mysql compatibility matrix job (Jose Celano) 7b3d944 fix(protocol): saturate scrape counters and add regression tests (Jose Celano) fe8feda docs(issues): rename and link issue spec 1525-01 to GitHub issue #1703 (Jose Celano) Pull request description: ## Summary - add saturation regression tests for large download counters in HTTP and UDP scrape responses - add MySQL image-tag injection for tracker-core DB driver tests - add a dedicated `database-compatibility` job between unit and e2e with a MySQL 8.0/8.4 matrix - run compatibility tests via Rust test command with explicit env vars and feature-gated compatibility suite - update issue spec `docs/issues/1703-1525-01-persistence-test-coverage.md` to match the implemented workflow Closes #1703 ACKs for top commit: josecelano: ACK f523745 Tree-SHA512: b186d65212ef3ecad98005cde8c870693e1d66d2f97d8b783c6cb24e6dea2fd77bbf58c37f2b99a5dadb5cdd9563ba234574705e61a0c51cd768d3289e6fe3c1
2 parents d51ef29 + f523745 commit c1ebdc7

7 files changed

Lines changed: 106 additions & 45 deletions

File tree

.github/workflows/testing.yaml

Lines changed: 30 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -133,14 +133,41 @@ jobs:
133133
name: Run Unit Tests
134134
run: cargo test --tests --benches --examples --workspace --all-targets --all-features
135135

136+
database-compatibility:
137+
name: Database Compatibility (${{ matrix.mysql-version }})
138+
runs-on: ubuntu-latest
139+
needs: unit
140+
141+
strategy:
142+
matrix:
143+
mysql-version: ["8.0", "8.4"]
144+
145+
steps:
146+
- id: checkout
147+
name: Checkout Repository
148+
uses: actions/checkout@v6
149+
150+
- id: setup
151+
name: Setup Toolchain
152+
uses: dtolnay/rust-toolchain@stable
153+
with:
154+
toolchain: stable
155+
156+
- id: cache
157+
name: Enable Job Cache
158+
uses: Swatinem/rust-cache@v2
159+
136160
- id: database
137-
name: Run MySQL Database Tests
138-
run: TORRUST_TRACKER_CORE_RUN_MYSQL_DRIVER_TEST=true cargo test --package bittorrent-tracker-core
161+
name: Run Database Compatibility Test
162+
env:
163+
TORRUST_TRACKER_CORE_RUN_MYSQL_DRIVER_TEST: "true"
164+
TORRUST_TRACKER_CORE_MYSQL_DRIVER_IMAGE_TAG: ${{ matrix.mysql-version }}
165+
run: cargo test -p bittorrent-tracker-core --features db-compatibility-tests run_mysql_driver_tests -- --nocapture
139166

140167
e2e:
141168
name: E2E
142169
runs-on: ubuntu-latest
143-
needs: unit
170+
needs: database-compatibility
144171

145172
strategy:
146173
matrix:

docs/issues/1525-overhaul-persistence.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ You can then browse or search it while working in the main repository.
8686

8787
### 1) Add DB compatibility matrix
8888

89-
- Spec file: `docs/issues/1525-01-persistence-test-coverage.md`
89+
- Spec file: `docs/issues/1703-1525-01-persistence-test-coverage.md`
9090
- Outcome: compatibility matrix exercises SQLite and multiple MySQL versions; PostgreSQL slot
9191
reserved for subissue 8
9292

docs/issues/1525-01-persistence-test-coverage.md renamed to docs/issues/1703-1525-01-persistence-test-coverage.md

Lines changed: 26 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1-
# Subissue Draft for #1525-01: Add DB Compatibility Matrix
1+
# Subissue #1703 (Draft for #1525-01): Add DB Compatibility Matrix
2+
3+
- Issue: https://github.com/torrust/torrust-tracker/issues/1703
24

35
## Goal
46

@@ -42,7 +44,7 @@ The implementation must follow these quality rules for all new and modified test
4244

4345
The PR #1695 review branch includes a QA script that defines the expected behavior:
4446

45-
- `run-db-compatibility-matrix.sh`:
47+
- `database-compatibility` job in `.github/workflows/testing.yaml`:
4648
executes a compatibility matrix across SQLite, multiple MySQL versions, and multiple PostgreSQL
4749
versions.
4850

@@ -86,51 +88,44 @@ Steps:
8688
- PostgreSQL (reserved for subissue #1525-08): `TORRUST_TRACKER_CORE_POSTGRES_DRIVER_IMAGE_TAG`
8789

8890
When `TORRUST_TRACKER_CORE_MYSQL_DRIVER_IMAGE_TAG` is not set, the test falls back to the
89-
current hardcoded default (e.g. `8.0`), preserving existing behavior. The matrix script sets
91+
current hardcoded default (e.g. `8.0`), preserving existing behavior. The CI matrix job sets
9092
this variable explicitly for each version in the loop, so unset means "run as today" and the
9193
matrix just expands that into multiple combinations.
9294

93-
- Add `contrib/dev-tools/qa/run-db-compatibility-matrix.sh` modeled after the PR prototype:
94-
- `set -euo pipefail`
95-
- define default version sets from env vars:
96-
- `MYSQL_VERSIONS` defaulting to at least `8.0 8.4`
97-
- `POSTGRES_VERSIONS` reserved for subissue #1525-08
98-
- run pre-checks once (`cargo check --workspace --all-targets`)
99-
- run protocol/configuration tests once
100-
- run SQLite driver tests once
101-
- loop MySQL versions: `docker pull mysql:<version>`, then run MySQL driver tests with
102-
`TORRUST_TRACKER_CORE_RUN_MYSQL_DRIVER_TEST=1` and
103-
`TORRUST_TRACKER_CORE_MYSQL_DRIVER_IMAGE_TAG=<version>`
104-
- print a clear heading for each backend/version before executing tests
105-
- fail fast on first failure with the failing backend/version visible in logs
106-
- keep script complexity intentionally low; avoid re-implementing test logic already in test
107-
functions
108-
- Replace the current single MySQL `database` step in `.github/workflows/testing.yaml` with
109-
execution of the new script.
95+
- Add a dedicated `database-compatibility` workflow job (between unit and e2e) with matrix values for MySQL versions:
96+
- include matrix values for at least `8.0` and `8.4`
97+
- run `cargo test -p bittorrent-tracker-core --features db-compatibility-tests run_mysql_driver_tests -- --nocapture`
98+
- set `TORRUST_TRACKER_CORE_RUN_MYSQL_DRIVER_TEST=true`
99+
- set `TORRUST_TRACKER_CORE_MYSQL_DRIVER_IMAGE_TAG=<version>`
100+
- keep the test logic in Rust; use workflow matrix for version fan-out
101+
- Replace the current single MySQL `database` step in `.github/workflows/testing.yaml` with a
102+
dedicated `database-compatibility` job.
110103

111104
Acceptance criteria:
112105

113106
- [ ] DB image version injection is supported via `TORRUST_TRACKER_CORE_MYSQL_DRIVER_IMAGE_TAG`
114107
(and a reserved `POSTGRES` equivalent for subissue #1525-08).
115-
- [ ] `contrib/dev-tools/qa/run-db-compatibility-matrix.sh` exists and runs successfully.
116-
- [ ] The script exercises SQLite and at least two MySQL versions by default.
108+
- [ ] `database-compatibility` workflow job runs successfully for each configured MySQL version.
109+
- [ ] The workflow matrix exercises at least two MySQL versions by default.
117110
- [ ] Failures identify the backend/version combination that broke.
118-
- [ ] The `database` job step in `.github/workflows/testing.yaml` runs the matrix script instead
119-
of a single-version MySQL command.
120-
- [ ] The script structure allows PostgreSQL to be added in subissue #1525-08 without a redesign.
111+
- [ ] The dedicated `database-compatibility` job in `.github/workflows/testing.yaml` replaces the
112+
old single-version MySQL command.
113+
- [ ] The workflow matrix structure allows PostgreSQL to be added in subissue #1525-08 without a
114+
redesign.
121115
- [ ] Tests do not hard-code host ports; `testcontainers` assigns random ports automatically.
122116
- [ ] All containers started by tests are removed unconditionally on test completion or failure.
123117

124118
### 2) Document the workflow
125119

126120
Steps:
127121

128-
- Document the local invocation command for the matrix script.
129-
- Document that the CI `database` step runs the same script.
122+
- Document the local invocation command for the compatibility test using explicit feature + env
123+
vars.
124+
- Document that CI runs the same test through the `database-compatibility` workflow job matrix.
130125

131126
Acceptance criteria:
132127

133-
- [ ] The matrix script is documented and runnable without ad hoc manual steps.
128+
- [ ] The compatibility test command is documented and runnable without ad hoc manual steps.
134129

135130
## Out of Scope
136131

@@ -143,13 +138,13 @@ Acceptance criteria:
143138

144139
- [ ] `cargo test --workspace --all-targets` passes.
145140
- [ ] `linter all` exits with code `0`.
146-
- [ ] The matrix script has been executed successfully in a clean environment; a passing run log
147-
is included in the PR description.
141+
- [ ] The `database-compatibility` workflow job has been executed successfully in a clean
142+
environment; a passing run log is included in the PR description.
148143

149144
## References
150145

151146
- EPIC: #1525
152147
- Reference PR: #1695
153148
- Reference implementation branch: `josecelano:pr-1684-review` — see EPIC for checkout
154149
instructions (`docs/issues/1525-overhaul-persistence.md`)
155-
- Reference script: `contrib/dev-tools/qa/run-db-compatibility-matrix.sh`
150+
- Reference job: `.github/workflows/testing.yaml` `database-compatibility`

packages/http-protocol/src/v1/responses/scrape.rs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,5 +131,25 @@ mod tests {
131131
String::from_utf8(expected_bytes.to_vec()).unwrap()
132132
);
133133
}
134+
135+
#[test]
136+
fn should_encode_large_download_counts_as_i64() {
137+
let info_hash = InfoHash::from_bytes(&[0x69; 20]);
138+
let mut scrape_data = ScrapeData::empty();
139+
scrape_data.add_file(
140+
&info_hash,
141+
SwarmMetadata {
142+
complete: 1,
143+
downloaded: u32::MAX,
144+
incomplete: 3,
145+
},
146+
);
147+
148+
let response = Bencoded::from(scrape_data);
149+
let bytes = response.body();
150+
let body = String::from_utf8(bytes).unwrap();
151+
152+
assert!(body.contains(&format!("downloadedi{}e", i64::from(u32::MAX))));
153+
}
134154
}
135155
}

packages/tracker-core/Cargo.toml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,10 @@ repository.workspace = true
1313
rust-version.workspace = true
1414
version.workspace = true
1515

16+
[features]
17+
default = [ ]
18+
db-compatibility-tests = [ ]
19+
1620
[dependencies]
1721
aquatic_udp_protocol = "0"
1822
bittorrent-primitives = "0.1.0"

packages/tracker-core/src/databases/driver/mysql.rs

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -345,7 +345,7 @@ impl Database for Mysql {
345345
}
346346
}
347347

348-
#[cfg(test)]
348+
#[cfg(all(test, feature = "db-compatibility-tests"))]
349349
mod tests {
350350
use std::sync::Arc;
351351

@@ -355,7 +355,8 @@ mod tests {
355355
356356
Test for this driver are executed with:
357357
358-
`TORRUST_TRACKER_CORE_RUN_MYSQL_DRIVER_TEST=true cargo test`
358+
`TORRUST_TRACKER_CORE_RUN_MYSQL_DRIVER_TEST=true \
359+
cargo test -p bittorrent-tracker-core --features db-compatibility-tests run_mysql_driver_tests`
359360
360361
The `Database` trait is very simple and we only have one driver that needs
361362
a container. In the future we might want to use different approaches like:
@@ -379,7 +380,9 @@ mod tests {
379380

380381
impl StoppedMysqlContainer {
381382
async fn run(self, config: &MysqlConfiguration) -> Result<RunningMysqlContainer, Box<dyn std::error::Error + 'static>> {
382-
let container = GenericImage::new("mysql", "8.0")
383+
let image_tag = std::env::var("TORRUST_TRACKER_CORE_MYSQL_DRIVER_IMAGE_TAG").unwrap_or_else(|_| "8.0".to_string());
384+
385+
let container = GenericImage::new("mysql", image_tag.as_str())
383386
.with_exposed_port(config.internal_port.tcp())
384387
// todo: this does not work
385388
//.with_wait_for(WaitFor::message_on_stdout("ready for connections"))
@@ -454,6 +457,8 @@ mod tests {
454457
driver
455458
}
456459

460+
// This test is invoked by `.github/workflows/testing.yaml` in the
461+
// `database-compatibility` job to validate supported MySQL versions.
457462
#[tokio::test]
458463
async fn run_mysql_driver_tests() -> Result<(), Box<dyn std::error::Error + 'static>> {
459464
if std::env::var("TORRUST_TRACKER_CORE_RUN_MYSQL_DRIVER_TEST").is_err() {

packages/udp-tracker-server/src/handlers/scrape.rs

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -53,19 +53,22 @@ pub async fn handle_scrape(
5353
Ok(build_response(request, &scrape_data))
5454
}
5555

56+
fn udp_counter_from_u32(value: u32) -> i32 {
57+
// Temporary saturation guard for UDP i32 counters. Proper type alignment across Rust and DB layers
58+
// will be addressed in docs/issues/1525-07-align-rust-and-db-types.md.
59+
i32::try_from(value).unwrap_or(i32::MAX)
60+
}
61+
5662
fn build_response(request: &ScrapeRequest, scrape_data: &ScrapeData) -> Response {
5763
let mut torrent_stats: Vec<TorrentScrapeStatistics> = Vec::new();
5864

5965
for file in &scrape_data.files {
6066
let swarm_metadata = file.1;
6167

62-
#[allow(clippy::cast_possible_truncation)]
63-
let scrape_entry = {
64-
TorrentScrapeStatistics {
65-
seeders: NumberOfPeers(I32::new(i64::from(swarm_metadata.complete) as i32)),
66-
completed: NumberOfDownloads(I32::new(i64::from(swarm_metadata.downloaded) as i32)),
67-
leechers: NumberOfPeers(I32::new(i64::from(swarm_metadata.incomplete) as i32)),
68-
}
68+
let scrape_entry = TorrentScrapeStatistics {
69+
seeders: NumberOfPeers(I32::new(udp_counter_from_u32(swarm_metadata.complete))),
70+
completed: NumberOfDownloads(I32::new(udp_counter_from_u32(swarm_metadata.downloaded))),
71+
leechers: NumberOfPeers(I32::new(udp_counter_from_u32(swarm_metadata.incomplete))),
6972
};
7073

7174
torrent_stats.push(scrape_entry);
@@ -458,4 +461,11 @@ mod tests {
458461
}
459462
}
460463
}
464+
465+
#[test]
466+
fn should_saturate_large_download_counts_for_udp_protocol() {
467+
assert_eq!(super::udp_counter_from_u32(u32::MAX), i32::MAX);
468+
assert_eq!(super::udp_counter_from_u32((i32::MAX as u32) + 1), i32::MAX);
469+
assert_eq!(super::udp_counter_from_u32(42), 42);
470+
}
461471
}

0 commit comments

Comments
 (0)