Files
com_sportsmanager/tests/dtfb-player-sync/FINDINGS.md
Tim 5843fda2d6 QA: harden DTFB player sync receiver
Applies 6 fixes to sync.php found during QA of the player-sync feature:
1. Normalise non-UTF-8 (latin1/Win-1252) payloads -> fixes silent 0-row imports
2. Fail loudly (success=false) when N rows parse but nothing is added/updated
3. Remove dead \ block (undefined-variable notice)
4. Gate mass-deactivation: skip the sweep when a payload carries < 50% of an
   org's currently-active members (configurable via sync_deactivation_min_ratio,
   default 0.5); adds/updates still proceed, skipped sweeps return warnings
5. Use a single DB clock (NOW()) for staging session id/cleanup
6. Enforce Passnummer format ^[0-9]{2}-[0-9]{4,6}\$ (parity with manual import)

Adds tests/dtfb-player-sync/FINDINGS.md documenting the findings and fixes.
End-to-end validation is to be done on the staging environments.
2026-06-04 11:41:50 +02:00

2.7 KiB

DTFB Player Sync — QA findings & applied fixes

Concise record of the defects found while reviewing the player-sync receiver (syncReceiveSpielerImport() in sync.php) and the six fixes applied in this PR. The exploratory test harness used to find these has been removed — the authoritative next test is the staging end-to-end sync (see the PR description).

The six issues fixed

# Issue Impact Fix in sync.php
1 Receiver did not normalise input encoding. A latin1 / Windows-1252 CSV (the legacy manual export) has ß as the single byte 0xDF. Staging the org name into the utf8mb4 table truncated it at that byte, so the org lookup missed and every row was skipped. Critical — silent data loss (e.g. 2964 rows parsed, 0 imported, success=true). Transcode the payload to UTF-8 when it is not already valid UTF-8, before staging.
2 A zero-effect import reported success. When N rows parsed but nothing was added or updated, the function returned success=true. Critical — masks encoding/mapping failures. Return success=false with a diagnostic message when rows>0 but added==0 && updated==0.
3 Dead $naechste_spielernr block referenced an undefined variable in the insert branch. Runtime notice; incoming rows already carry their Passnummer. Removed the block.
4 Unconditional mass-deactivation. A partial CSV deactivated every member not listed. High — a broken/partial export could wipe an org's roster. Per organisation, skip the deactivation sweep when the incoming count is below sync_deactivation_min_ratio (default 0.5) of the org's currently-active members; adds/updates still proceed and a warning is returned.
5 Split clock for staging. session_id came from PHP date() while stale-row cleanup used MySQL NOW(); a PHP/MySQL timezone gap could delete in-flight staging rows. Medium — another silent 0-row path. Derive session_id from the DB clock (NOW()), with date() fallback.
6 No Passnummer format check. The manual import UI enforces ^[0-9]{2}-[0-9]{4,6}$; the sync receiver did not. Medium — inconsistent data quality vs the manual flow. Apply the same regex to spielernr / spielernr_alt (blank/reject on mismatch).

Confirmed by design (not bugs)

  • No contact / personal data (email, phone, address) is ever exported or imported.
  • Existing players keep their lizenznr and geburtsjahr on update; only name / sex / Passnummer-driven fields change.
  • An unknown organisation aborts the whole import with no mutation.
  • The sync path itself (export → cURL push → receive) is UTF-8 end-to-end; the encoding defect (#1) only affected ingesting a legacy latin1 manual file.