o2o-negosium-original/negodata/backend/conftest.py
Mina Choi 1f778ab975 [feat] negodata/backend: 상품·협력사·견적 도메인 CRUD + delivery_type 코드화 + 공용 /v1/enums
- item/supplier/quotation/quotation_setting CRUD·service·router 추가
- item protocol delivery_type str→int (ERD/스키마 SMALLINT 일치)
- DeliveryType enum + 한글 라벨, 공용 GET /v1/enums (도메인 코드 메타데이터)
- CompanyBrief → CompanyData 로 *Data 네이밍 통일
- CORS: WebServerConfig.client_url(단일) 도입 (config_models/router)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-17 15:58:38 +09:00

77 lines
2.7 KiB
Python

# 테스트도 APP_ENV=local 로 실행한다 (config.local.toml 사용).
# config.server_configs 가 import 되는 순간 config.<APP_ENV>.toml 을 읽으므로 가장 먼저 설정.
import os
os.environ.setdefault("APP_ENV", "local")
import uuid
import pytest_asyncio
from httpx import ASGITransport, AsyncClient
from sqlalchemy import text
from sqlalchemy.ext.asyncio import create_async_engine
from common.database.model.models import MAIN_BASE
from config.server_configs import main_db_config
def _write_url(cfg) -> str:
pw = f":{cfg.write_pw}" if cfg.write_pw else ""
return f"postgresql+asyncpg://{cfg.write_id}{pw}@{cfg.write_host}:{cfg.write_port}/{cfg.name}"
@pytest_asyncio.fixture
async def db_engine():
"""테스트용 스키마를 보장하고, 매 테스트 시작 시 테이블을 비워 격리한다.
앱(DB_SESSION_MNG)은 자체 엔진으로 같은 DB(config.test.toml)에 접속하므로,
여기서 만든 스키마를 그대로 공유한다.
"""
engine = create_async_engine(_write_url(main_db_config))
async with engine.begin() as conn:
await conn.run_sync(MAIN_BASE.metadata.create_all) # 이미 있으면 skip
# negodata 도메인 테이블 전부 비워 격리 (CASCADE: FK 미설정이라 안전망)
await conn.execute(
text(
"TRUNCATE TABLE tbl_account, users, companies, items, suppliers, "
"quotation_settings, quotations RESTART IDENTITY CASCADE"
)
)
yield engine
await engine.dispose()
@pytest_asyncio.fixture
async def company_id(db_engine) -> str:
"""테스트용 소속사 1개를 시드하고 company_id(uuid str)를 돌려준다.
users 는 company_id 를 요구하므로 계정 생성 테스트의 선행 조건이다.
"""
cid = uuid.uuid4()
async with db_engine.begin() as conn:
await conn.execute(
text("INSERT INTO companies (company_id, name) VALUES (:cid, :name)"),
{"cid": cid, "name": "테스트사"},
)
return str(cid)
@pytest_asyncio.fixture(scope="session", autouse=True)
async def _dispose_app_engines():
"""테스트 세션이 끝날 때 앱 싱글톤 엔진을 정리한다.
(이벤트 루프 종료 후 커넥션이 GC 되며 나오는 'Event loop is closed' 경고 제거)
"""
yield
from common.database.db_session_manager import DB_SESSION_MNG
await DB_SESSION_MNG.dispose_all()
@pytest_asyncio.fixture
async def client(db_engine):
"""앱을 실제 네트워크 없이 호출하는 httpx 클라이언트 (ASGITransport)."""
from router.router import app
transport = ASGITransport(app=app)
async with AsyncClient(transport=transport, base_url="http://test") as ac:
yield ac