hokusai/tests/test_config.py
Konstantin Fickel eef9712924
Some checks failed
Continuous Integration / Build Package (push) Successful in 34s
Continuous Integration / Lint, Check & Test (push) Failing after 48s
test: add integration tests for all modules
- Add pytest-asyncio dev dependency and configure asyncio_mode=auto
- Add filterwarnings to suppress third-party PydanticDeprecatedSince20
- Add conftest.py with shared fixtures (project_dir, write_config, etc.)
- Add test_config.py: YAML loading, target type inference, model resolution
- Add test_graph.py: DAG construction, cycle detection, build ordering
- Add test_state.py: hash functions, state persistence, dirty checking
- Add test_builder.py: full build pipeline with FakeProvider, incremental
  builds, selective builds, error isolation, dependency cascading
- Add test_providers.py: ImageProvider and TextProvider with mocked clients
- Add test_cli.py: build/clean/graph commands via typer CliRunner
- All 94 tests pass with 0 basedpyright warnings
2026-02-14 11:07:36 +01:00

132 lines
4.5 KiB
Python

"""Integration tests for bulkgen.config."""
from __future__ import annotations
from pathlib import Path
import pytest
import yaml
from bulkgen.config import (
Defaults,
TargetConfig,
TargetType,
infer_target_type,
load_config,
resolve_model,
)
class TestLoadConfig:
"""Test loading and validating YAML config files end-to-end."""
def test_minimal_config(self, project_dir: Path) -> None:
config_path = project_dir / "test.bulkgen.yaml"
_ = config_path.write_text(
yaml.dump({"targets": {"out.txt": {"prompt": "hello"}}})
)
config = load_config(config_path)
assert "out.txt" in config.targets
assert config.targets["out.txt"].prompt == "hello"
assert config.defaults.text_model == "mistral-large-latest"
assert config.defaults.image_model == "flux-pro-1.1"
def test_full_config_with_all_fields(self, project_dir: Path) -> None:
raw = {
"defaults": {
"text_model": "custom-text",
"image_model": "custom-image",
},
"targets": {
"banner.png": {
"prompt": "A wide banner",
"model": "flux-dev",
"width": 1920,
"height": 480,
"inputs": ["ref.png"],
"reference_image": "ref.png",
"control_images": ["ctrl.png"],
},
"story.md": {
"prompt": "Write a story",
"inputs": ["banner.png"],
},
},
}
config_path = project_dir / "full.bulkgen.yaml"
_ = config_path.write_text(yaml.dump(raw, default_flow_style=False))
config = load_config(config_path)
assert config.defaults.text_model == "custom-text"
assert config.defaults.image_model == "custom-image"
banner = config.targets["banner.png"]
assert banner.model == "flux-dev"
assert banner.width == 1920
assert banner.height == 480
assert banner.reference_image == "ref.png"
assert banner.control_images == ["ctrl.png"]
story = config.targets["story.md"]
assert story.model is None
assert story.inputs == ["banner.png"]
def test_empty_targets_rejected(self, project_dir: Path) -> None:
config_path = project_dir / "empty.bulkgen.yaml"
_ = config_path.write_text(yaml.dump({"targets": {}}))
with pytest.raises(Exception, match="At least one target"):
_ = load_config(config_path)
def test_missing_prompt_rejected(self, project_dir: Path) -> None:
config_path = project_dir / "bad.bulkgen.yaml"
_ = config_path.write_text(yaml.dump({"targets": {"out.txt": {}}}))
with pytest.raises(Exception):
_ = load_config(config_path)
class TestInferTargetType:
"""Test target type inference from file extensions."""
@pytest.mark.parametrize(
"name", ["photo.png", "photo.jpg", "photo.jpeg", "photo.webp"]
)
def test_image_extensions(self, name: str) -> None:
assert infer_target_type(name) is TargetType.IMAGE
@pytest.mark.parametrize("name", ["PHOTO.PNG", "PHOTO.JPG"])
def test_case_insensitive(self, name: str) -> None:
assert infer_target_type(name) is TargetType.IMAGE
@pytest.mark.parametrize("name", ["doc.md", "doc.txt"])
def test_text_extensions(self, name: str) -> None:
assert infer_target_type(name) is TargetType.TEXT
def test_unsupported_extension_raises(self) -> None:
with pytest.raises(ValueError, match="unsupported extension"):
_ = infer_target_type("data.csv")
def test_no_extension_raises(self) -> None:
with pytest.raises(ValueError, match="unsupported extension"):
_ = infer_target_type("Makefile")
class TestResolveModel:
"""Test model resolution (explicit vs. default)."""
def test_explicit_model_wins(self) -> None:
target = TargetConfig(prompt="x", model="my-model")
assert resolve_model("out.txt", target, Defaults()) == "my-model"
def test_default_text_model(self) -> None:
target = TargetConfig(prompt="x")
defaults = Defaults(text_model="custom-text")
assert resolve_model("out.md", target, defaults) == "custom-text"
def test_default_image_model(self) -> None:
target = TargetConfig(prompt="x")
defaults = Defaults(image_model="custom-image")
assert resolve_model("out.png", target, defaults) == "custom-image"