| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768 |
- import asyncio
- import json
- import os
- from collections import Counter
- from pathlib import Path
- from uuid import uuid4
- import httpx
- from sqlalchemy import func, select
- from test_after_sales import scenario # noqa: F401
- from zhifu.modules.after_sales.models import Ticket
- async def test_replica_reads_and_idempotent_writes(scenario): # noqa: F811
- phase = os.environ.get("REPLICA_PHASE", "both")
- assert phase in {"both", "single", "recovered"}
- expected = {"api1"} if phase == "single" else {"api1", "api2"}
- async with httpx.AsyncClient(
- base_url="http://127.0.0.1:18080",
- headers=scenario["headers"]["customer"],
- timeout=15,
- trust_env=False,
- ) as client:
- responses = await asyncio.gather(
- *(client.get(f"/openapi/v1/orders?store_id={scenario['store']}") for _ in range(64))
- )
- assert all(response.status_code == 200 for response in responses), [
- {"status": response.status_code, "body": response.text}
- for response in responses
- if response.status_code != 200
- ]
- counts = Counter(response.headers["X-Zhifu-Replica"] for response in responses)
- assert set(counts) == expected, counts
- key = uuid4().hex
- writes = await asyncio.gather(
- *(
- client.post(
- "/openapi/v1/after-sales/proposals",
- json=scenario["proposal"],
- headers={"Idempotency-Key": key},
- )
- for _ in range(16)
- )
- )
- assert all(response.status_code == 201 for response in writes), [r.text for r in writes]
- assert {response.headers["X-Zhifu-Replica"] for response in writes} == expected
- assert len({response.json()["ticket_id"] for response in writes}) == 1
- async with scenario["app"].state.sessions() as session:
- tickets = await session.scalar(
- select(func.count())
- .select_from(Ticket)
- .where(Ticket.tenant_id == scenario["tenant"].bytes)
- )
- assert tickets == 1
- report = {
- "phase": phase,
- "read_statuses": dict(Counter(r.status_code for r in responses)),
- "read_replicas": dict(counts),
- "write_statuses": dict(Counter(r.status_code for r in writes)),
- "write_replicas": dict(Counter(r.headers["X-Zhifu-Replica"] for r in writes)),
- "tickets": tickets,
- }
- await asyncio.to_thread(
- Path(f"docs/verification/replica-{phase}.json").write_text,
- json.dumps(report, indent=2),
- encoding="utf-8",
- )
|