fix(settings): valida csv_delimiter/decimal_separator/auto_logout_minutes (#23)
PUT /api/settings/ non validava nulla: un csv_delimiter vuoto o di piu' caratteri arrivava intatto nel DB e faceva fallire csv.writer con TypeError dentro close_run (500, run mai chiuso). Aggiunta validazione per chiave nota (csv_delimiter 1 carattere fra , ; TAB |, csv_decimal_separator ',' o '.', auto_logout_minutes vuoto o intero 1-480, company_name/company_logo_path non vuoti) -> 422 sui valori fuori dominio. Chiavi non note passano senza validazione (store generico). Difesa in profondita' in production_export_service._csv_format: un csv_delimiter gia' sporco nel DB (scritto prima di questa validazione) non fa piu' fallire close_run, usa ';' e logga un WARNING. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -105,3 +105,80 @@ class TestUpdateSettings:
|
||||
json={"some_key": "some_value"},
|
||||
)
|
||||
assert resp.status_code == 403
|
||||
|
||||
|
||||
class TestUpdateSettingsValidation:
|
||||
"""gitea#23: an invalid csv_delimiter must be rejected, not accepted and
|
||||
left to blow up close_run later with a csv.writer TypeError."""
|
||||
|
||||
async def test_csv_delimiter_two_chars_rejected(
|
||||
self, client: AsyncClient, admin_user: User
|
||||
):
|
||||
resp = await client.put(
|
||||
"/api/settings/",
|
||||
headers=auth_headers(admin_user),
|
||||
json={"csv_delimiter": ";;"},
|
||||
)
|
||||
assert resp.status_code == 422
|
||||
|
||||
async def test_csv_delimiter_empty_rejected(
|
||||
self, client: AsyncClient, admin_user: User
|
||||
):
|
||||
resp = await client.put(
|
||||
"/api/settings/",
|
||||
headers=auth_headers(admin_user),
|
||||
json={"csv_delimiter": ""},
|
||||
)
|
||||
assert resp.status_code == 422
|
||||
|
||||
async def test_csv_delimiter_tab_accepted(
|
||||
self, client: AsyncClient, admin_user: User
|
||||
):
|
||||
resp = await client.put(
|
||||
"/api/settings/",
|
||||
headers=auth_headers(admin_user),
|
||||
json={"csv_delimiter": "\t"},
|
||||
)
|
||||
assert resp.status_code == 200
|
||||
|
||||
async def test_csv_decimal_separator_invalid_rejected(
|
||||
self, client: AsyncClient, admin_user: User
|
||||
):
|
||||
resp = await client.put(
|
||||
"/api/settings/",
|
||||
headers=auth_headers(admin_user),
|
||||
json={"csv_decimal_separator": "x"},
|
||||
)
|
||||
assert resp.status_code == 422
|
||||
|
||||
async def test_auto_logout_minutes_out_of_range_rejected(
|
||||
self, client: AsyncClient, admin_user: User
|
||||
):
|
||||
resp = await client.put(
|
||||
"/api/settings/",
|
||||
headers=auth_headers(admin_user),
|
||||
json={"auto_logout_minutes": "0"},
|
||||
)
|
||||
assert resp.status_code == 422
|
||||
|
||||
async def test_auto_logout_minutes_empty_disables(
|
||||
self, client: AsyncClient, admin_user: User
|
||||
):
|
||||
"""Empty string is what the settings page sends to disable auto-logout."""
|
||||
resp = await client.put(
|
||||
"/api/settings/",
|
||||
headers=auth_headers(admin_user),
|
||||
json={"auto_logout_minutes": ""},
|
||||
)
|
||||
assert resp.status_code == 200
|
||||
|
||||
async def test_unknown_key_not_validated(
|
||||
self, client: AsyncClient, admin_user: User
|
||||
):
|
||||
"""Keys with no known validator pass through (generic settings store)."""
|
||||
resp = await client.put(
|
||||
"/api/settings/",
|
||||
headers=auth_headers(admin_user),
|
||||
json={"some_future_key": "anything"},
|
||||
)
|
||||
assert resp.status_code == 200
|
||||
|
||||
Reference in New Issue
Block a user