1
etude_lego_jurassic_world/tests/test_inventory_gaps.py

145 lines
4.4 KiB
Python

"""Tests des écarts d'inventaire calculés depuis parts_filtered.csv."""
import csv
from pathlib import Path
from lib.rebrickable.inventory_reconciliation import (
compute_inventory_gaps,
index_sets_by_num,
write_inventory_gaps_csv,
write_inventory_gaps_markdown,
)
def write_csv(path: Path, headers: list[str], rows: list[list[str]]) -> None:
"""Écrit un CSV simple pour les besoins des tests."""
with path.open("w", newline="") as csv_file:
writer = csv.writer(csv_file)
writer.writerow(headers)
writer.writerows(rows)
def test_compute_inventory_gaps_excludes_spares(tmp_path: Path) -> None:
"""Ignore les pièces de rechange et ne conserve que les sets en écart."""
sets_path = tmp_path / "sets_enriched.csv"
parts_path = tmp_path / "parts_filtered.csv"
write_csv(
sets_path,
["set_num", "set_id", "num_parts", "in_collection"],
[
["1000-1", "1000", "4", "true"],
["2000-1", "2000", "3", "false"],
["3000-1", "3000", "1", "true"],
],
)
write_csv(
parts_path,
["part_num", "color_rgb", "is_translucent", "set_num", "set_id", "quantity_in_set", "is_spare"],
[
["A", "AAAAAA", "false", "1000-1", "1000", "2", "false"],
["B", "BBBBBB", "false", "1000-1", "1000", "2", "false"],
["S", "SSSSSS", "false", "1000-1", "1000", "5", "true"],
["C", "CCCCCC", "false", "2000-1", "2000", "2", "false"],
["D", "DDDDDD", "false", "3000-1", "3000", "1", "false"],
],
)
gaps = compute_inventory_gaps(sets_path, parts_path)
assert gaps == [
{
"set_num": "1000-1",
"set_id": "1000",
"expected_parts": 4,
"inventory_parts": 9,
"inventory_parts_non_spare": 4,
"delta": 5,
"delta_non_spare": 0,
"in_collection": "true",
},
{
"set_num": "2000-1",
"set_id": "2000",
"expected_parts": 3,
"inventory_parts": 2,
"inventory_parts_non_spare": 2,
"delta": 1,
"delta_non_spare": 1,
"in_collection": "false",
}
]
def test_write_inventory_gaps_csv(tmp_path: Path) -> None:
"""Sérialise le rapport d'écarts dans un CSV dédié."""
destination_path = tmp_path / "inventory_gaps.csv"
rows = [
{
"set_num": "2000-1",
"set_id": "2000",
"expected_parts": 3,
"inventory_parts": 2,
"inventory_parts_non_spare": 2,
"delta": 1,
"delta_non_spare": 1,
"in_collection": "false",
}
]
write_inventory_gaps_csv(destination_path, rows)
with destination_path.open() as csv_file:
written_rows = list(csv.DictReader(csv_file))
assert written_rows == [
{
"set_num": "2000-1",
"set_id": "2000",
"expected_parts": "3",
"inventory_parts": "2",
"inventory_parts_non_spare": "2",
"delta": "1",
"delta_non_spare": "1",
"in_collection": "false",
}
]
def test_write_inventory_gaps_markdown(tmp_path: Path) -> None:
"""Produit un tableau Markdown listant les sets en écart."""
destination_path = tmp_path / "inventory_gaps.md"
gaps = [
{
"set_num": "2000-1",
"set_id": "2000",
"expected_parts": 3,
"inventory_parts": 2,
"inventory_parts_non_spare": 2,
"delta": 1,
"delta_non_spare": 1,
"in_collection": "false",
}
]
sets = [
{
"set_num": "2000-1",
"set_id": "2000",
"num_parts": "3",
"name": "Test Set",
"year": "2020",
"rebrickable_url": "https://rebrickable.com/sets/2000-1",
"in_collection": "false",
}
]
write_inventory_gaps_markdown(destination_path, gaps, index_sets_by_num(sets))
with destination_path.open() as markdown_file:
content = markdown_file.read().splitlines()
assert content[0].startswith("| set_id | name |")
assert (
"| [2000](https://rebrickable.com/sets/2000-1) | Test Set | 2020 | 1 | 1 | 3 | 2 | 2 | false | [PDF](https://www.lego.com/service/buildinginstructions/2000) |"
in content
)