import csv import tempfile import unittest from pathlib import Path import analyze_purchases class AnalyzePurchasesTests(unittest.TestCase): def test_analysis_outputs_cover_required_views(self): with tempfile.TemporaryDirectory() as tmpdir: purchases_csv = Path(tmpdir) / "purchases.csv" output_dir = Path(tmpdir) / "analysis" fieldnames = [ "purchase_date", "retailer", "order_id", "catalog_id", "catalog_name", "category", "product_type", "net_line_total", "line_total", "normalized_quantity", "normalized_quantity_unit", "effective_price", "effective_price_unit", "store_name", "store_number", "store_city", "store_state", "is_fee", "is_discount_line", "is_coupon_line", ] with purchases_csv.open("w", newline="", encoding="utf-8") as handle: writer = csv.DictWriter(handle, fieldnames=fieldnames) writer.writeheader() writer.writerows( [ { "purchase_date": "2026-03-01", "retailer": "giant", "order_id": "g1", "catalog_id": "cat_banana", "catalog_name": "BANANA", "category": "produce", "product_type": "banana", "net_line_total": "1.29", "line_total": "1.29", "normalized_quantity": "2.19", "normalized_quantity_unit": "lb", "effective_price": "0.589", "effective_price_unit": "lb", "store_name": "Giant", "store_number": "42", "store_city": "Springfield", "store_state": "VA", "is_fee": "false", "is_discount_line": "false", "is_coupon_line": "false", }, { "purchase_date": "2026-03-01", "retailer": "giant", "order_id": "g1", "catalog_id": "cat_ice", "catalog_name": "ICE", "category": "frozen", "product_type": "ice", "net_line_total": "3.50", "line_total": "3.50", "normalized_quantity": "20", "normalized_quantity_unit": "lb", "effective_price": "0.175", "effective_price_unit": "lb", "store_name": "Giant", "store_number": "42", "store_city": "Springfield", "store_state": "VA", "is_fee": "false", "is_discount_line": "false", "is_coupon_line": "false", }, { "purchase_date": "2026-03-02", "retailer": "costco", "order_id": "c1", "catalog_id": "cat_banana", "catalog_name": "BANANA", "category": "produce", "product_type": "banana", "net_line_total": "1.49", "line_total": "2.98", "normalized_quantity": "3", "normalized_quantity_unit": "lb", "effective_price": "0.4967", "effective_price_unit": "lb", "store_name": "MT VERNON", "store_number": "1115", "store_city": "ALEXANDRIA", "store_state": "VA", "is_fee": "false", "is_discount_line": "false", "is_coupon_line": "false", }, ] ) analyze_purchases.main.callback( purchases_csv=str(purchases_csv), output_dir=str(output_dir), ) expected_files = [ "item_price_over_time.csv", "spend_by_visit.csv", "items_per_visit.csv", "category_spend_over_time.csv", "retailer_store_breakdown.csv", ] for name in expected_files: self.assertTrue((output_dir / name).exists(), name) with (output_dir / "spend_by_visit.csv").open(newline="", encoding="utf-8") as handle: spend_rows = list(csv.DictReader(handle)) self.assertEqual("4.79", spend_rows[0]["visit_spend_total"]) with (output_dir / "items_per_visit.csv").open(newline="", encoding="utf-8") as handle: item_rows = list(csv.DictReader(handle)) self.assertEqual("2", item_rows[0]["item_row_count"]) self.assertEqual("2", item_rows[0]["distinct_catalog_count"]) with (output_dir / "category_spend_over_time.csv").open(newline="", encoding="utf-8") as handle: category_rows = list(csv.DictReader(handle)) produce_row = next(row for row in category_rows if row["purchase_date"] == "2026-03-01" and row["category"] == "produce") self.assertEqual("1.29", produce_row["category_spend_total"]) with (output_dir / "retailer_store_breakdown.csv").open(newline="", encoding="utf-8") as handle: store_rows = list(csv.DictReader(handle)) giant_row = next(row for row in store_rows if row["retailer"] == "giant") self.assertEqual("1", giant_row["visit_count"]) self.assertEqual("2", giant_row["item_row_count"]) self.assertEqual("4.79", giant_row["store_spend_total"]) if __name__ == "__main__": unittest.main()