273 lines
8.8 KiB
Python
273 lines
8.8 KiB
Python
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)
|