Add RCON data pipeline validation script
This commit is contained in:
@@ -1,7 +1,7 @@
|
|||||||
---
|
---
|
||||||
id: TASK-131
|
id: TASK-131
|
||||||
title: Add RCON data pipeline validation script
|
title: Add RCON data pipeline validation script
|
||||||
status: pending
|
status: done
|
||||||
type: platform
|
type: platform
|
||||||
team: Backend Senior
|
team: Backend Senior
|
||||||
supporting_teams:
|
supporting_teams:
|
||||||
@@ -74,3 +74,12 @@ The RCON data pipeline now spans parsing, storage, AdminLog ingestion, materiali
|
|||||||
- Stage only intended files.
|
- Stage only intended files.
|
||||||
- Commit the completed implementation.
|
- Commit the completed implementation.
|
||||||
- Push the branch to origin.
|
- Push the branch to origin.
|
||||||
|
|
||||||
|
## Outcome
|
||||||
|
|
||||||
|
- Added `scripts/run-rcon-data-pipeline-tests.ps1`.
|
||||||
|
- The script compiles backend modules, runs RCON parser/storage/materialization/link checks without real RCON credentials, and performs an optional backend health smoke check only when Docker Compose already has `backend` running.
|
||||||
|
- The script prefers `pytest` when available and falls back to deterministic offline checks plus unittest suites when `pytest` is absent.
|
||||||
|
- Validation: `powershell -ExecutionPolicy Bypass -File scripts/run-rcon-data-pipeline-tests.ps1` passed.
|
||||||
|
- Validation: `powershell -ExecutionPolicy Bypass -File scripts/run-integration-tests.ps1` returned exit code 0, but its nested historical UI regression check emitted an existing frontend assertion about the missing recent-match external action label. No frontend files were changed in this platform task.
|
||||||
|
- Real RCON checks were skipped by design because the script must run without RCON credentials.
|
||||||
123
scripts/run-rcon-data-pipeline-tests.ps1
Normal file
123
scripts/run-rcon-data-pipeline-tests.ps1
Normal file
@@ -0,0 +1,123 @@
|
|||||||
|
$ErrorActionPreference = "Stop"
|
||||||
|
|
||||||
|
Write-Host "HLL Vietnam RCON data pipeline validation"
|
||||||
|
|
||||||
|
function Invoke-Step {
|
||||||
|
param(
|
||||||
|
[Parameter(Mandatory = $true)][string]$Name,
|
||||||
|
[Parameter(Mandatory = $true)][scriptblock]$Command
|
||||||
|
)
|
||||||
|
|
||||||
|
Write-Host ""
|
||||||
|
Write-Host "== $Name =="
|
||||||
|
& $Command
|
||||||
|
}
|
||||||
|
|
||||||
|
function Test-PythonModule {
|
||||||
|
param([Parameter(Mandatory = $true)][string]$ModuleName)
|
||||||
|
|
||||||
|
$check = "import importlib.util; raise SystemExit(0 if importlib.util.find_spec('$ModuleName') else 1)"
|
||||||
|
python -c $check *> $null
|
||||||
|
return $LASTEXITCODE -eq 0
|
||||||
|
}
|
||||||
|
|
||||||
|
Invoke-Step "Compile backend application modules" {
|
||||||
|
python -m compileall backend/app
|
||||||
|
}
|
||||||
|
|
||||||
|
$previousPythonPath = $env:PYTHONPATH
|
||||||
|
$env:PYTHONPATH = if ($previousPythonPath) { "backend;$previousPythonPath" } else { "backend" }
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (Test-PythonModule "pytest") {
|
||||||
|
Invoke-Step "Run RCON parser, storage and materialization tests with pytest" {
|
||||||
|
python -m pytest `
|
||||||
|
backend/tests/test_rcon_admin_log_parser.py `
|
||||||
|
backend/tests/test_rcon_admin_log_storage.py `
|
||||||
|
backend/tests/test_rcon_materialization_pipeline.py `
|
||||||
|
backend/tests/test_scoreboard_match_links.py
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
Write-Host ""
|
||||||
|
Write-Host "pytest is not installed; running offline fallback checks for RCON parser/storage and unittest suites."
|
||||||
|
|
||||||
|
Invoke-Step "Run RCON parser and storage fallback checks" {
|
||||||
|
$fallbackChecks = @'
|
||||||
|
from pathlib import Path
|
||||||
|
import tempfile
|
||||||
|
from backend.tests import test_rcon_admin_log_parser as parser_tests
|
||||||
|
from backend.tests import test_rcon_admin_log_storage as storage_tests
|
||||||
|
|
||||||
|
parser_tests.test_parse_match_start()
|
||||||
|
parser_tests.test_parse_match_end()
|
||||||
|
parser_tests.test_parse_kill()
|
||||||
|
parser_tests.test_parse_team_switch()
|
||||||
|
parser_tests.test_parse_connected()
|
||||||
|
parser_tests.test_parse_disconnected()
|
||||||
|
parser_tests.test_parse_chat()
|
||||||
|
parser_tests.test_parse_kick()
|
||||||
|
parser_tests.test_parse_message_profile()
|
||||||
|
parser_tests.test_parse_player_profile_snapshot_spanish_sections()
|
||||||
|
parser_tests.test_non_profile_message_does_not_parse_as_profile_snapshot()
|
||||||
|
|
||||||
|
with tempfile.TemporaryDirectory() as tmp:
|
||||||
|
storage_tests.test_initialize_rcon_admin_log_storage_creates_event_table(Path(tmp))
|
||||||
|
with tempfile.TemporaryDirectory() as tmp:
|
||||||
|
storage_tests.test_persist_rcon_admin_log_entries_inserts_then_reports_duplicates(Path(tmp))
|
||||||
|
with tempfile.TemporaryDirectory() as tmp:
|
||||||
|
storage_tests.test_profile_message_snapshots_are_materialized_and_deduped(Path(tmp))
|
||||||
|
with tempfile.TemporaryDirectory() as tmp:
|
||||||
|
storage_tests.test_non_profile_messages_do_not_create_profile_snapshots(Path(tmp))
|
||||||
|
with tempfile.TemporaryDirectory() as tmp:
|
||||||
|
storage_tests.test_canonical_message_dedupes_changing_relative_prefixes(Path(tmp))
|
||||||
|
with tempfile.TemporaryDirectory() as tmp:
|
||||||
|
storage_tests.test_list_rcon_admin_log_event_counts_groups_by_target_and_event_type(Path(tmp))
|
||||||
|
|
||||||
|
print("RCON parser and storage fallback checks passed.")
|
||||||
|
'@
|
||||||
|
$fallbackChecks | python -
|
||||||
|
}
|
||||||
|
|
||||||
|
Invoke-Step "Run RCON materialization unittest suite" {
|
||||||
|
python -m unittest backend.tests.test_rcon_materialization_pipeline
|
||||||
|
}
|
||||||
|
|
||||||
|
Invoke-Step "Run RCON scoreboard link unittest suite" {
|
||||||
|
python -m unittest backend.tests.test_scoreboard_match_links
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
$env:PYTHONPATH = $previousPythonPath
|
||||||
|
}
|
||||||
|
|
||||||
|
Invoke-Step "Optional Docker backend smoke check" {
|
||||||
|
if (-not (Get-Command docker -ErrorAction SilentlyContinue)) {
|
||||||
|
Write-Host "Skipping Docker smoke check: docker command is not available."
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
docker compose ps --services --filter "status=running" *> $null
|
||||||
|
if ($LASTEXITCODE -ne 0) {
|
||||||
|
Write-Host "Skipping Docker smoke check: docker compose is not available or no compose project is active."
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
$runningServices = docker compose ps --services --filter "status=running"
|
||||||
|
if ($runningServices -notcontains "backend") {
|
||||||
|
Write-Host "Skipping backend endpoint smoke check: backend service is not running."
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
$health = Invoke-WebRequest "http://localhost:8000/health" -UseBasicParsing
|
||||||
|
if ($health.StatusCode -lt 200 -or $health.StatusCode -ge 300) {
|
||||||
|
throw "Backend health smoke check failed with status $($health.StatusCode)."
|
||||||
|
}
|
||||||
|
Write-Host "Backend health smoke check passed."
|
||||||
|
}
|
||||||
|
|
||||||
|
Write-Host ""
|
||||||
|
Write-Host "Skipping real RCON checks: this validation is designed to run without RCON credentials."
|
||||||
|
Write-Host "RCON data pipeline validation passed."
|
||||||
|
exit 0
|
||||||
Reference in New Issue
Block a user