| """Unit tests for synthetic data generation.""" |
|
|
| import pandas as pd |
| import pytest |
|
|
| from src.data_generation.generate_customers import generate_customers |
| from src.data_generation.generate_orders import generate_orders |
| from src.data_generation.generate_products import generate_products |
|
|
| |
| |
| |
|
|
|
|
| class TestGenerateCustomers: |
| def test_row_count(self): |
| df = generate_customers(n=100, seed=0) |
| assert len(df) == 100 |
|
|
| def test_required_columns(self): |
| df = generate_customers(n=50, seed=0) |
| required = {"customer_id", "registration_date", "country", "segment", "marketing_channel"} |
| assert required.issubset(df.columns) |
|
|
| def test_unique_customer_ids(self): |
| df = generate_customers(n=100, seed=0) |
| assert df["customer_id"].nunique() == 100 |
|
|
| def test_registration_dates_within_range(self): |
| df = generate_customers(n=200, seed=0, start_date="2021-01-01", end_date="2023-12-31") |
| assert df["registration_date"].min() >= pd.Timestamp("2021-01-01") |
| assert df["registration_date"].max() <= pd.Timestamp("2023-12-31") |
|
|
| def test_segment_values(self): |
| df = generate_customers(n=100, seed=0) |
| valid = {"bronze", "silver", "gold", "platinum"} |
| assert set(df["segment"].unique()).issubset(valid) |
|
|
| def test_marketing_channel_values(self): |
| df = generate_customers(n=100, seed=0) |
| valid = {"organic", "email", "social", "paid_search"} |
| assert set(df["marketing_channel"].unique()).issubset(valid) |
|
|
| def test_birth_date_nullable(self): |
| df = generate_customers(n=200, seed=0) |
| |
| null_rate = df["birth_date"].isna().mean() |
| assert 0.02 < null_rate < 0.50 |
|
|
| def test_last_profile_update_nullable(self): |
| df = generate_customers(n=200, seed=0) |
| null_rate = df["last_profile_update"].isna().mean() |
| assert 0.05 < null_rate < 0.70 |
|
|
| def test_reproducibility(self): |
| df1 = generate_customers(n=50, seed=42) |
| df2 = generate_customers(n=50, seed=42) |
| pd.testing.assert_frame_equal(df1, df2) |
|
|
| def test_different_seeds_differ(self): |
| df1 = generate_customers(n=50, seed=1) |
| df2 = generate_customers(n=50, seed=2) |
| assert not df1["registration_date"].equals(df2["registration_date"]) |
|
|
|
|
| |
| |
| |
|
|
|
|
| class TestGenerateProducts: |
| def test_row_count(self): |
| df = generate_products(n=50, seed=0) |
| assert len(df) == 50 |
|
|
| def test_required_columns(self): |
| df = generate_products(n=50, seed=0) |
| assert {"product_id", "product_type", "base_price", "margin_group"}.issubset(df.columns) |
|
|
| def test_prices_positive(self): |
| df = generate_products(n=100, seed=0) |
| assert (df["base_price"] > 0).all() |
|
|
| def test_price_power_law(self): |
| """Most prices should be low, a few should be high (right-skewed).""" |
| df = generate_products(n=1000, seed=0) |
| assert df["base_price"].median() < df["base_price"].mean() |
|
|
| def test_margin_groups_valid(self): |
| df = generate_products(n=100, seed=0) |
| assert set(df["margin_group"].unique()).issubset({"low", "medium", "high"}) |
|
|
|
|
| |
| |
| |
|
|
|
|
| @pytest.fixture(scope="module") |
| def small_dataset(): |
| customers = generate_customers(n=50, seed=7, start_date="2021-01-01", end_date="2023-12-31") |
| products = generate_products(n=20, seed=7) |
| orders = generate_orders( |
| customers=customers, |
| products=products, |
| seed=7, |
| start_date="2021-01-01", |
| end_date="2023-12-31", |
| ) |
| return customers, products, orders |
|
|
|
|
| class TestGenerateOrders: |
| def test_required_columns(self, small_dataset): |
| _, _, orders = small_dataset |
| required = { |
| "order_id", |
| "customer_id", |
| "order_date", |
| "quantity", |
| "price", |
| "total_value", |
| "holiday_flag", |
| "seasonal_flag", |
| } |
| assert required.issubset(orders.columns) |
|
|
| def test_no_future_orders(self, small_dataset): |
| _, _, orders = small_dataset |
| assert orders["order_date"].max() <= pd.Timestamp("2023-12-31") |
|
|
| def test_order_dates_after_registration(self, small_dataset): |
| customers, _, orders = small_dataset |
| merged = orders.merge(customers[["customer_id", "registration_date"]], on="customer_id") |
| assert (merged["order_date"] >= merged["registration_date"]).all() |
|
|
| def test_positive_values(self, small_dataset): |
| _, _, orders = small_dataset |
| assert (orders["total_value"] > 0).all() |
| assert (orders["quantity"] >= 1).all() |
| assert (orders["price"] > 0).all() |
|
|
| def test_product_id_nullable(self, small_dataset): |
| _, _, orders = small_dataset |
| |
| null_rate = orders["product_id"].isna().mean() |
| assert null_rate < 0.30 |
|
|
| def test_valid_customer_ids(self, small_dataset): |
| customers, _, orders = small_dataset |
| valid_ids = set(customers["customer_id"]) |
| assert set(orders["customer_id"]).issubset(valid_ids) |
|
|
| def test_order_ids_unique(self, small_dataset): |
| _, _, orders = small_dataset |
| assert orders["order_id"].nunique() == len(orders) |
|
|
| def test_reproducibility(self): |
| customers = generate_customers(n=20, seed=99) |
| products = generate_products(n=10, seed=99) |
| o1 = generate_orders(customers, products, seed=99) |
| o2 = generate_orders(customers, products, seed=99) |
| pd.testing.assert_frame_equal(o1, o2) |
|
|
| def test_holiday_flag_binary(self, small_dataset): |
| _, _, orders = small_dataset |
| assert orders["holiday_flag"].isin([0, 1]).all() |
|
|
| def test_seasonal_flag_binary(self, small_dataset): |
| _, _, orders = small_dataset |
| assert orders["seasonal_flag"].isin([0, 1]).all() |
|
|