"""Tests du calcul des pièces rares.""" import csv from pathlib import Path from lib.rebrickable.rare_parts import build_rare_parts, write_rare_parts_by_set, write_rare_parts_list def write_csv(path: Path, headers: list[str], rows: list[list[str]]) -> None: """Écrit un CSV simple pour les besoins de tests.""" with path.open("w", newline="") as csv_file: writer = csv.writer(csv_file) writer.writerow(headers) writer.writerows(rows) def test_build_rare_parts_detects_exclusive_variations(tmp_path: Path) -> None: """Identifie les combinaisons pièce+couleur présentes dans un seul set.""" parts_filtered = tmp_path / "parts_filtered.csv" write_csv( parts_filtered, [ "part_num", "color_rgb", "is_translucent", "set_num", "set_id", "year", "quantity_in_set", "is_spare", "is_minifig_part", ], [ ["p1", "AAAAAA", "false", "1000-1", "1000", "2020", "2", "false", "false"], ["p1", "AAAAAA", "false", "2000-1", "2000", "2021", "3", "false", "false"], ["p2", "BBBBBB", "false", "1000-1", "1000", "2020", "1", "false", "true"], ["p3", "CCCCCC", "true", "2000-1", "2000", "2021", "4", "false", "false"], ], ) sets_enriched = tmp_path / "sets_enriched.csv" write_csv( sets_enriched, ["set_num", "set_id", "name", "year", "in_collection"], [ ["1000-1", "1000", "Set A", "2020", "true"], ["2000-1", "2000", "Set B", "2021", "false"], ], ) parts_catalog = tmp_path / "parts.csv" write_csv( parts_catalog, ["part_num", "name", "part_cat_id"], [ ["p1", "Brick 1x1", "1"], ["p2", "Head Custom", "59"], ["p3", "Slope 45", "2"], ], ) colors = tmp_path / "colors.csv" write_csv( colors, ["id", "name", "rgb", "is_trans", "num_parts", "num_sets", "y1", "y2"], [ ["1", "Gray", "AAAAAA", "false", "0", "0", "0", "0"], ["2", "Blue", "BBBBBB", "false", "0", "0", "0", "0"], ["3", "Trans-Clear", "CCCCCC", "true", "0", "0", "0", "0"], ], ) rare_parts, rare_by_set = build_rare_parts(parts_filtered, sets_enriched, parts_catalog, colors) assert rare_parts == [ { "set_num": "1000-1", "set_id": "1000", "set_name": "Set A", "year": "2020", "part_num": "p2", "part_name": "Head Custom", "part_cat_id": "59", "color_rgb": "BBBBBB", "color_name": "Blue", "is_translucent": "false", "is_minifig_part": "true", "quantity_in_set": "1", "in_collection": "true", }, { "set_num": "2000-1", "set_id": "2000", "set_name": "Set B", "year": "2021", "part_num": "p3", "part_name": "Slope 45", "part_cat_id": "2", "color_rgb": "CCCCCC", "color_name": "Trans-Clear", "is_translucent": "true", "is_minifig_part": "false", "quantity_in_set": "4", "in_collection": "false", }, ] assert rare_by_set == [ { "set_num": "2000-1", "set_id": "2000", "name": "Set B", "year": "2021", "in_collection": "false", "rare_parts_distinct": "1", "rare_parts_quantity": "4", "rare_minifig_parts_distinct": "0", "rare_minifig_quantity": "0", }, { "set_num": "1000-1", "set_id": "1000", "name": "Set A", "year": "2020", "in_collection": "true", "rare_parts_distinct": "1", "rare_parts_quantity": "1", "rare_minifig_parts_distinct": "1", "rare_minifig_quantity": "1", }, ] def test_write_rare_parts_outputs_csv(tmp_path: Path) -> None: """Sérialise les pièces rares et l’agrégat par set.""" rare_parts_path = tmp_path / "rare_parts.csv" rare_by_set_path = tmp_path / "rare_parts_by_set.csv" rare_parts_sample = [ { "set_num": "123-1", "set_id": "123", "set_name": "Sample", "year": "2020", "part_num": "p1", "part_name": "Brick", "part_cat_id": "1", "color_rgb": "FFFFFF", "color_name": "White", "is_translucent": "false", "is_minifig_part": "false", "quantity_in_set": "2", "in_collection": "true", } ] rare_by_set_sample = [ { "set_num": "123-1", "set_id": "123", "name": "Sample", "year": "2020", "in_collection": "true", "rare_parts_distinct": "1", "rare_parts_quantity": "2", "rare_minifig_parts_distinct": "0", "rare_minifig_quantity": "0", } ] write_rare_parts_list(rare_parts_path, rare_parts_sample) write_rare_parts_by_set(rare_by_set_path, rare_by_set_sample) assert rare_parts_path.exists() assert rare_by_set_path.exists()