from datetime import datetime from types import SimpleNamespace from fastapi.testclient import TestClient from app.auth import require_customer from app.database import get_db from app.main import create_app from app.models import Customer, Lead, WanfaRoute class FakeScalarResult: def __init__(self, items): self.items = items def all(self): return self.items class FakeDb: def __init__(self, *, scalar_results=None, scalar_values=None): self.scalar_results = list(scalar_results or []) self.scalar_values = list(scalar_values or []) self.added = [] self.committed = False def scalars(self, _statement): return FakeScalarResult(self.scalar_results.pop(0) if self.scalar_results else []) def scalar(self, _statement): return self.scalar_values.pop(0) if self.scalar_values else None def add(self, item): self.added.append(item) def flush(self): for item in self.added: if not getattr(item, "id", None): item.id = "browse-history-test" def commit(self): self.committed = True def refresh(self, _item): return None def authenticated_app(fake_db, customer): app = create_app() app.dependency_overrides[get_db] = lambda: fake_db app.dependency_overrides[require_customer] = lambda: customer return app def make_vehicle_lead(**overrides): return Lead( id=overrides.get("id", "lead-1"), leadType="vehicle", contactName=overrides.get("contactName", "Damon"), customerId=overrides.get("customerId", "customer-1"), destination=overrides.get("destination", "黄果树景区"), phone=overrides.get("phone", "13800000000"), travelDate=overrides.get("travelDate", datetime(2026, 8, 25)), peopleCount=overrides.get("peopleCount", 5), note=overrides.get("note", "需要儿童座椅"), vehicleDemand=overrides.get( "vehicleDemand", { "serviceType": "charter", "charterDuration": "fullDay", "travelDate": "2026-08-25", "pickupLocation": "贵阳北站", "dropoffLocation": "黄果树景区", "peopleCount": 5, "luggageCount": 2, "vehicleOptionTitle": "9座精品商务车", "specialRequirements": "需要儿童座椅", }, ), status=overrides.get("status", "new"), createdAt=overrides.get("createdAt", datetime(2026, 8, 20, 10, 0)), updatedAt=overrides.get("updatedAt", datetime(2026, 8, 20, 10, 0)), ) def test_public_vehicle_demands_are_paginated_and_phone_is_masked(): customer = Customer(id="customer-1", phone="13800000000") lead = make_vehicle_lead() db = FakeDb(scalar_results=[[lead]], scalar_values=[1]) app = authenticated_app(db, customer) try: response = TestClient(app).get( "/api/public/customer/vehicle-demands?pageNum=2&pageSize=1" ) finally: app.dependency_overrides.clear() assert response.status_code == 200 assert response.json() == { "code": 200, "msg": "success", "data": { "items": [ { "id": "lead-1", "status": "new", "contactName": "Damon", "phoneMasked": "138****0000", "destination": "黄果树景区", "travelDate": "2026-08-25T00:00:00", "peopleCount": 5, "note": "需要儿童座椅", "vehicleDemand": lead.vehicleDemand, "createdAt": "2026-08-20T10:00:00", "updatedAt": "2026-08-20T10:00:00", } ], "total": 1, "pageNum": 2, "pageSize": 1, }, } def test_public_vehicle_demand_detail_only_returns_the_current_customer_record(): customer = Customer(id="customer-1", phone="13800000000") lead = make_vehicle_lead() db = FakeDb(scalar_values=[lead]) app = authenticated_app(db, customer) try: response = TestClient(app).get("/api/public/customer/vehicle-demands/lead-1") finally: app.dependency_overrides.clear() assert response.status_code == 200 assert response.json()["data"]["id"] == "lead-1" assert response.json()["data"]["phoneMasked"] == "138****0000" assert "phone" not in response.json()["data"] def test_public_vehicle_demand_detail_hides_another_customers_record(): customer = Customer(id="customer-1", phone="13800000000") db = FakeDb(scalar_values=[None]) app = authenticated_app(db, customer) try: response = TestClient(app).get("/api/public/customer/vehicle-demands/lead-other") finally: app.dependency_overrides.clear() assert response.status_code == 404 def test_public_browse_history_is_paginated_for_the_current_customer(): customer = Customer(id="customer-1", phone="13800000000") history = SimpleNamespace( id="history-1", customerId="customer-1", itemType="wanfa-route", itemId="family-water", title="亲子玩水", image="https://example.test/family-water.jpg", visitedAt=datetime(2026, 8, 26, 12, 0), ) db = FakeDb(scalar_results=[[history]], scalar_values=[1]) app = authenticated_app(db, customer) try: response = TestClient(app).get( "/api/public/customer/browse-history?pageNum=1&pageSize=20" ) finally: app.dependency_overrides.clear() assert response.status_code == 200 assert response.json()["data"] == { "items": [ { "id": "history-1", "itemType": "wanfa-route", "itemId": "family-water", "title": "亲子玩水", "image": "https://example.test/family-water.jpg", "visitedAt": "2026-08-26T12:00:00", } ], "total": 1, "pageNum": 1, "pageSize": 20, } def test_public_browse_history_upserts_a_valid_route_without_accepting_a_customer_id(): customer = Customer(id="customer-1", phone="13800000000") route = WanfaRoute( id="family-water", categoryId="family-route", title="亲子玩水", subtitle="贵州·轻松节奏与自然课堂", image="https://example.test/family-water.jpg", routeCount=4, demandKeyword="亲子玩水", sortOrder=0, createdAt=datetime(2026, 1, 1), updatedAt=datetime(2026, 1, 2), ) db = FakeDb(scalar_values=[route, None]) app = authenticated_app(db, customer) try: response = TestClient(app).post( "/api/public/customer/browse-history", json={ "customerId": "customer-other", "itemType": "wanfa-route", "itemId": "family-water", }, ) finally: app.dependency_overrides.clear() assert response.status_code == 201 history = db.added[0] assert history.customerId == "customer-1" assert history.itemType == "wanfa-route" assert history.itemId == "family-water" assert history.title == "亲子玩水" assert history.image == "https://example.test/family-water.jpg" assert response.json()["data"]["itemId"] == "family-water" def test_public_browse_history_updates_existing_item_instead_of_creating_a_duplicate(): customer = Customer(id="customer-1", phone="13800000000") route = WanfaRoute( id="family-water", categoryId="family-route", title="亲子玩水·更新", subtitle="贵州·轻松节奏与自然课堂", image="https://example.test/family-water-new.jpg", routeCount=4, demandKeyword="亲子玩水", sortOrder=0, createdAt=datetime(2026, 1, 1), updatedAt=datetime(2026, 1, 2), ) history = SimpleNamespace( id="history-1", customerId="customer-1", itemType="wanfa-route", itemId="family-water", title="亲子玩水", image="https://example.test/family-water-old.jpg", visitedAt=datetime(2026, 8, 25, 12, 0), ) db = FakeDb(scalar_values=[route, history]) app = authenticated_app(db, customer) try: response = TestClient(app).post( "/api/public/customer/browse-history", json={"itemType": "wanfa-route", "itemId": "family-water"}, ) finally: app.dependency_overrides.clear() assert response.status_code == 201 assert db.added == [] assert history.title == "亲子玩水·更新" assert history.image == "https://example.test/family-water-new.jpg" assert history.visitedAt > datetime(2026, 8, 25, 12, 0)