"""Import/export tests — ACs 20-21: CSV import, dry-run preview.""" from __future__ import annotations import pytest from httpx import AsyncClient from tests.conftest import ORIGIN_HEADER, login_client, seed_tenant_and_users CSV_COMPANIES = """name,industry,phone,email,website,description ImportCorp,IT,123456,import@example.com,https://import.example,Imported company TechImport,Finance,654321,tech@example.com,https://tech.example,Tech company """ CSV_COMPANIES_INVALID = """name,industry ,IT ValidCorp,Finance """ CSV_CONTACTS = """first_name,last_name,email,phone,mobile,position,department Alice,Wonderland,alice@example.com,123,456,Manager,Sales Bob,Builder,bob@example.com,789,012,Developer,Tech """ @pytest.mark.asyncio class TestImportCompanies: """AC 20: CSV import for companies.""" async def test_import_companies_csv_returns_200(self, client: AsyncClient, db_session): """AC 20: POST /api/v1/import CSV + entity_type=companies -> 200 + result.""" await seed_tenant_and_users(db_session) await login_client(client, "admin@tenanta.com") files = {"file": ("companies.csv", CSV_COMPANIES.encode(), "text/csv")} data = {"entity_type": "companies"} resp = await client.post( "/api/v1/import", files=files, data=data, headers=ORIGIN_HEADER, ) assert resp.status_code == 200 result = resp.json() assert result["total"] == 2 assert result["valid"] == 2 assert result["invalid"] == 0 assert len(result["created"]) == 2 assert result["dry_run"] is False async def test_import_companies_with_invalid_rows(self, client: AsyncClient, db_session): """Import CSV with some invalid rows — should report errors but import valid ones.""" await seed_tenant_and_users(db_session) await login_client(client, "admin@tenanta.com") files = {"file": ("companies.csv", CSV_COMPANIES_INVALID.encode(), "text/csv")} data = {"entity_type": "companies"} resp = await client.post( "/api/v1/import", files=files, data=data, headers=ORIGIN_HEADER, ) assert resp.status_code == 200 result = resp.json() assert result["total"] == 2 assert result["valid"] == 1 assert result["invalid"] == 1 assert len(result["errors"]) == 1 assert len(result["created"]) == 1 @pytest.mark.asyncio class TestImportPreview: """AC 21: Dry-run preview (no DB changes).""" async def test_import_preview_no_db_changes(self, client: AsyncClient, db_session): """AC 21: POST /api/v1/import/preview CSV -> 200 + dry-run (no DB changes).""" await seed_tenant_and_users(db_session) await login_client(client, "admin@tenanta.com") files = {"file": ("companies.csv", CSV_COMPANIES.encode(), "text/csv")} data = {"entity_type": "companies"} resp = await client.post( "/api/v1/import/preview", files=files, data=data, headers=ORIGIN_HEADER, ) assert resp.status_code == 200 result = resp.json() assert result["total"] == 2 assert result["valid"] == 2 assert result["dry_run"] is True assert len(result["created"]) == 0 # No actual creations # Verify no companies were actually created list_resp = await client.get("/api/v1/companies", headers=ORIGIN_HEADER) names = [item["name"] for item in list_resp.json()["items"]] assert "ImportCorp" not in names assert "TechImport" not in names async def test_import_preview_contacts_no_db_changes(self, client: AsyncClient, db_session): """Preview import for contacts — dry-run.""" await seed_tenant_and_users(db_session) await login_client(client, "admin@tenanta.com") files = {"file": ("contacts.csv", CSV_CONTACTS.encode(), "text/csv")} data = {"entity_type": "contacts"} resp = await client.post( "/api/v1/import/preview", files=files, data=data, headers=ORIGIN_HEADER, ) assert resp.status_code == 200 result = resp.json() assert result["total"] == 2 assert result["valid"] == 2 assert result["dry_run"] is True assert len(result["created"]) == 0 @pytest.mark.asyncio class TestImportContacts: """Import contacts via CSV.""" async def test_import_contacts_csv_returns_200(self, client: AsyncClient, db_session): """Import contacts via CSV.""" await seed_tenant_and_users(db_session) await login_client(client, "admin@tenanta.com") files = {"file": ("contacts.csv", CSV_CONTACTS.encode(), "text/csv")} data = {"entity_type": "contacts"} resp = await client.post( "/api/v1/import", files=files, data=data, headers=ORIGIN_HEADER, ) assert resp.status_code == 200 result = resp.json() assert result["total"] == 2 assert result["valid"] == 2 assert result["invalid"] == 0 assert len(result["created"]) == 2 # Verify contacts appear in list list_resp = await client.get("/api/v1/contacts", headers=ORIGIN_HEADER) assert list_resp.json()["total"] >= 2