import sys import os from unittest.mock import MagicMock # --- Environment Setup --- sys.modules['redis'] = MagicMock() sys.modules['sqlalchemy'] = MagicMock() sys.modules['app.config'] = MagicMock() sys.modules['html2text'] = MagicMock() sys.modules['app.log'] = MagicMock() sys.modules['app.lib_general'] = MagicMock() # Mock app.db_sql mock_db_sql = MagicMock() # Mock ID resolution: abc -> 123 mock_db_sql.redis_lookup_id_random.side_effect = lambda record_id_random, table_name: 123 if record_id_random == 'abc' else None sys.modules['app.db_sql'] = mock_db_sql # Add project root to path sys.path.append(os.getcwd()) from app.lib_api_crud_v3 import sanitize_payload from pydantic import BaseModel, Field, ValidationError from typing import Optional, List, ClassVar class MockModel(BaseModel): id: Optional[int] name: str = Field(None, min_length=3) account_id: Optional[int] fields_to_exclude_from_db: ClassVar[List[str]] = ['computed_field'] def test_permissive_update(): print("--- Testing Permissive Update (ignore_extra=True) ---") payload = { "name": "Test", "extra_field": "Should be removed", "computed_field": "Should be removed" } sanitize_payload(payload, MockModel, ignore_extra=True) print(f"Sanitized Payload: {payload}") assert "extra_field" not in payload assert "computed_field" not in payload assert payload["name"] == "Test" print("āœ… Permissive update stripping works.") def test_strict_update(): print("\n--- Testing Strict Update (ignore_extra=False) ---") payload = { "name": "Test", "extra_field": "Should be removed", "computed_field": "Should be removed" } sanitize_payload(payload, MockModel, ignore_extra=False) print(f"Sanitized Payload: {payload}") assert "extra_field" in payload assert "computed_field" not in payload print("āœ… Strict update correctly preserves unknown fields (waiting for DB error) but strips excluded fields.") def test_id_resolution(): print("\n--- Testing ID Resolution ---") payload = { "name": "Test", "account_id_random": "abc" } sanitize_payload(payload, MockModel) print(f"Sanitized Payload: {payload}") assert payload.get("account_id") == 123 assert "account_id_random" not in payload print("āœ… ID resolution (account_id_random -> account_id) works.") def test_structured_validation_errors(): print("\n--- Testing Structured Validation Errors ---") payload = { "name": "a" # Too short } try: MockModel(**payload) except ValidationError as e: structured_errors = {err['loc'][-1]: err['msg'] for err in e.errors()} print(f"Structured Errors: {structured_errors}") assert "name" in structured_errors # Pydantic 1.x error message assert "at least 3 characters" in structured_errors["name"] print("āœ… Structured validation errors work.") if __name__ == "__main__": try: test_permissive_update() test_strict_update() test_id_resolution() test_structured_validation_errors() print("\nšŸŽ‰ All local logic tests passed!") except AssertionError as e: print(f"\nāŒ Test failed: {e}") sys.exit(1) except Exception as e: print(f"\nšŸ’„ An error occurred: {e}") import traceback traceback.print_exc() sys.exit(1)