Files
benevoles/backend/tests/test_import_parser.py
T
AdamThomasandClaude Sonnet 5 bb919554ff Étape 3 : import CSV/Excel des bénévoles et disponibilités
- Fichier unique, processus en deux temps : POST /editions/{id}/import/analyze
  (colonnes détectées, aperçu, mapping suggéré, demi-journées de l'édition)
  puis POST /editions/{id}/import/execute (mapping validé)
- Parser CSV (délimiteur et encodage devinés, BOM) et XLSX (openpyxl),
  détection XLSX par nombre magique
- Vocabulaire des valeurs de disponibilité (tri-état, accents/casse) et des
  tailles de t-shirt ; compétences créées à la volée dans le référentiel
- Rapport ligne par ligne : codes de traduction + paramètres, jamais de
  phrase. Doublons : upsert sur email, signalement sur (nom, prénom) sans
  email, doublons intra-fichier. Mode simulation (rollback, rien de persisté)
- 40 tests : parser, valeurs, service (tous les cas), endpoints
- Dépendances : python-multipart, openpyxl

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017uUsRg7XzopkCPWimwg9ZM
2026-09-09 22:37:30 +02:00

64 lines
1.8 KiB
Python
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import io
import pytest
from openpyxl import Workbook
from app.importing.parser import ImportParseError, parse_upload
def _xlsx_bytes(rows: list[list[object]]) -> bytes:
wb = Workbook()
ws = wb.active
for row in rows:
ws.append(row)
buffer = io.BytesIO()
wb.save(buffer)
return buffer.getvalue()
def test_csv_semicolon_with_bom():
raw = "Prénom;Nom;Email\nAlex;Martin;a@b.fr\n".encode()
table = parse_upload("benevoles.csv", raw)
assert table.headers == ["Prénom", "Nom", "Email"]
assert table.as_dicts() == [
{"Prénom": "Alex", "Nom": "Martin", "Email": "a@b.fr"}
]
def test_csv_comma_delimiter():
raw = b"first,last\nSam,Nguyen\n"
table = parse_upload("x.csv", raw)
assert table.headers == ["first", "last"]
assert table.rows == [["Sam", "Nguyen"]]
def test_csv_blank_lines_dropped_and_short_rows_padded():
raw = b"a;b;c\n\n1;2\n3;4;5;6\n"
table = parse_upload("x.csv", raw)
assert table.rows == [["1", "2", ""], ["3", "4", "5"]]
def test_xlsx_detected_by_magic_without_extension():
data = _xlsx_bytes([["Prénom", "Nom"], ["Alex", "Martin"], [None, None]])
table = parse_upload("upload.dat", data)
assert table.headers == ["Prénom", "Nom"]
assert table.rows == [["Alex", "Martin"]]
def test_xlsx_numeric_cells_become_plain_strings():
data = _xlsx_bytes([["Nom", "Tel"], ["Martin", 601020304]])
table = parse_upload("x.xlsx", data)
assert table.rows == [["Martin", "601020304"]]
def test_empty_file_raises():
with pytest.raises(ImportParseError) as err:
parse_upload("x.csv", b"")
assert err.value.code == "import.error.empty_file"
def test_unreadable_xlsx_raises():
with pytest.raises(ImportParseError) as err:
parse_upload("x.xlsx", b"not a real workbook")
assert err.value.code == "import.error.unreadable_file"