"""Unit tests for validation_service.validate_structural(). Structural pre-flight validator — only catches malformed payloads that would crash downstream inserts. Does NOT check SKU existence (handled by validate_skus) or duplicate SKUs (handled by _dedup_items_by_sku). """ import os import sys import pytest sys.path.insert(0, os.path.join(os.path.dirname(__file__), "..")) from app.services.validation_service import validate_structural def _valid_order(**overrides): base = { "order_number": "123456", "order_date": "2026-04-22 10:00:00", "items": [{"sku": "ABC", "quantity": 2, "price": 15.50}], } base.update(overrides) return base @pytest.mark.unit def test_valid_order_passes(): ok, err_type, err_msg = validate_structural(_valid_order()) assert ok is True assert err_type is None assert err_msg is None @pytest.mark.unit def test_missing_order_number(): ok, err_type, _ = validate_structural(_valid_order(order_number="")) assert ok is False assert err_type == "MISSING_FIELD" ok, err_type, _ = validate_structural(_valid_order(order_number=None)) assert ok is False assert err_type == "MISSING_FIELD" @pytest.mark.unit def test_non_dict_order(): ok, err_type, _ = validate_structural("not a dict") assert ok is False assert err_type == "MISSING_FIELD" @pytest.mark.unit def test_invalid_date_unparseable(): ok, err_type, _ = validate_structural(_valid_order(order_date="not-a-date")) assert ok is False assert err_type == "INVALID_DATE" @pytest.mark.unit def test_invalid_date_missing(): ok, err_type, _ = validate_structural(_valid_order(order_date=None)) assert ok is False assert err_type == "INVALID_DATE" @pytest.mark.unit def test_date_iso_format_passes(): ok, _, _ = validate_structural(_valid_order(order_date="2026-04-22T10:00:00")) assert ok is True @pytest.mark.unit def test_empty_items(): ok, err_type, _ = validate_structural(_valid_order(items=[])) assert ok is False assert err_type == "EMPTY_ITEMS" ok, err_type, _ = validate_structural(_valid_order(items=None)) assert ok is False assert err_type == "EMPTY_ITEMS" @pytest.mark.unit def test_items_not_list(): ok, err_type, _ = validate_structural(_valid_order(items="ABC")) assert ok is False assert err_type == "EMPTY_ITEMS" @pytest.mark.unit def test_item_not_dict(): ok, err_type, _ = validate_structural(_valid_order(items=["just-a-string"])) assert ok is False assert err_type == "EMPTY_ITEMS" @pytest.mark.unit def test_invalid_quantity_zero(): ok, err_type, _ = validate_structural( _valid_order(items=[{"sku": "A", "quantity": 0, "price": 1}]) ) assert ok is False assert err_type == "INVALID_QUANTITY" @pytest.mark.unit def test_invalid_quantity_negative(): ok, err_type, _ = validate_structural( _valid_order(items=[{"sku": "A", "quantity": -3, "price": 1}]) ) assert ok is False assert err_type == "INVALID_QUANTITY" @pytest.mark.unit def test_invalid_quantity_non_numeric(): ok, err_type, _ = validate_structural( _valid_order(items=[{"sku": "A", "quantity": "abc", "price": 1}]) ) assert ok is False assert err_type == "INVALID_QUANTITY" @pytest.mark.unit def test_invalid_quantity_missing(): ok, err_type, _ = validate_structural( _valid_order(items=[{"sku": "A", "price": 1}]) ) assert ok is False assert err_type == "INVALID_QUANTITY" @pytest.mark.unit def test_invalid_price_non_numeric(): ok, err_type, _ = validate_structural( _valid_order(items=[{"sku": "A", "quantity": 1, "price": "NaN-text"}]) ) assert ok is False assert err_type == "INVALID_PRICE" @pytest.mark.unit def test_invalid_price_missing(): ok, err_type, _ = validate_structural( _valid_order(items=[{"sku": "A", "quantity": 1}]) ) assert ok is False assert err_type == "INVALID_PRICE" @pytest.mark.unit def test_price_zero_allowed(): """Complex sets can legitimately have price=0 on one leg.""" ok, _, _ = validate_structural( _valid_order(items=[{"sku": "A", "quantity": 1, "price": 0}]) ) assert ok is True @pytest.mark.unit def test_sku_null_passes_structural(): """SKU validation is handled downstream, NOT here.""" ok, _, _ = validate_structural( _valid_order(items=[{"sku": None, "quantity": 1, "price": 1}]) ) assert ok is True