Newer
Older
TIASDataHub / tests / conftest.py
"""テスト共通の fixture(ダミーデータのみ.臨床データは使わない)."""

import sqlite3
from collections.abc import Iterator
from dataclasses import dataclass
from pathlib import Path

import pytest
from PIL import Image

from tiasdata import catalog

# 舌色データセットの 9 フォルダ(フォルダ名 → label・severity).
# TIASJudgeModel の TEST_01「フォルダ構成とラベル対応」と同じ.フォルダの直下に <番号>.png を置く
TONGUE_COLOR_FOLDERS: dict[str, tuple[str, str]] = {
    "淡紅(正常)": ("light_red", "normal"),
    "暗赤紅_あり": ("dark_red", "strong"),
    "暗赤紅_ややあり": ("dark_red", "mild"),
    "紫_ややあり": ("purple", "mild"),
    "紅_あり": ("red", "strong"),
    "紅_ややあり": ("red", "mild"),
    "紫_あり": ("purple", "strong"),
    "淡白_あり": ("pale", "strong"),
    "淡白_ややあり": ("pale", "mild"),
}


def write_dummy_png(path: Path, seed: int, size: tuple[int, int] = (8, 6)) -> Path:
    """`seed` ごとに中身の違う小さい PNG を書く(同じ `seed` なら同じバイト列)."""
    path.parent.mkdir(parents=True, exist_ok=True)
    color = (seed * 37 % 256, seed * 59 % 256, seed * 83 % 256)
    image = Image.new("RGB", size, color)
    # 色だけだと seed が 256 周期で重なるため,1 画素に seed を埋めて中身を一意にする
    image.putpixel((0, 0), (seed % 256, seed // 256 % 256, seed // 65536 % 256))
    image.save(path, format="PNG")
    return path


@dataclass(frozen=True)
class DummyImage:
    """ダミーの取り込み元の画像 1 枚."""

    number: int
    folder: str
    label: str
    severity: str
    path: Path


@dataclass(frozen=True)
class DummyTongueColorSource:
    """ダミーの舌色データセット(9 フォルダ × 数枚)."""

    root: Path
    images: list[DummyImage]


@pytest.fixture
def make_png():
    """小さい PNG を書く関数(`make_png(path, seed)`)."""
    return write_dummy_png


@pytest.fixture
def store_dir(tmp_path: Path) -> Path:
    """空のストアのフォルダ(`tiasdata init` 後と同じ状態)."""
    directory = tmp_path / "tiasdata" / "store"
    directory.mkdir(parents=True)
    return directory


@pytest.fixture
def tongue_color_source(tmp_path: Path) -> DummyTongueColorSource:
    """舌色データセットと同じ構成のダミーの取り込み元(各フォルダ 2 枚・番号は 1 から連番).

    対応表(xlsx)は含まない(Step 3 で足す).
    """
    root = tmp_path / "source" / "TIASJudgeModel"
    images: list[DummyImage] = []
    number = 1
    for folder, (label, severity) in TONGUE_COLOR_FOLDERS.items():
        for _ in range(2):
            path = write_dummy_png(root / folder / f"{number}.png", seed=number)
            images.append(DummyImage(number, folder, label, severity, path))
            number += 1
    return DummyTongueColorSource(root=root, images=images)


@pytest.fixture(autouse=True)
def _isolate_config_env(monkeypatch: pytest.MonkeyPatch) -> None:
    # 開発者の環境変数が混ざらないようにする(テストごとに必要なら設定する)
    monkeypatch.delenv("TIASDATA_CONFIG", raising=False)


@pytest.fixture
def catalog_conn(tmp_path: Path) -> Iterator[sqlite3.Connection]:
    """マイグレーション適用済みの目録(tmp_path に作る)."""
    conn = catalog.connect(tmp_path / "catalog.sqlite", create=True)
    catalog.apply_migrations(conn, "catalog")
    try:
        yield conn
    finally:
        conn.close()


@pytest.fixture
def identity_conn(tmp_path: Path) -> Iterator[sqlite3.Connection]:
    """マイグレーション適用済みの対応表(tmp_path に作る)."""
    conn = catalog.connect(tmp_path / "identity.sqlite", create=True)
    catalog.apply_migrations(conn, "identity")
    try:
        yield conn
    finally:
        conn.close()