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
This commit is contained in:
parent
452b3c4eb0
commit
eef9712924
10 changed files with 1662 additions and 0 deletions
79
tests/conftest.py
Normal file
79
tests/conftest.py
Normal file
|
|
@ -0,0 +1,79 @@
|
|||
"""Shared fixtures for bulkgen integration tests."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from collections.abc import Callable
|
||||
from pathlib import Path
|
||||
|
||||
import pytest
|
||||
import yaml
|
||||
|
||||
from bulkgen.config import ProjectConfig, load_config
|
||||
|
||||
WriteConfig = Callable[[dict[str, object]], ProjectConfig]
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def project_dir(tmp_path: Path) -> Path:
|
||||
"""A temporary directory acting as the project root."""
|
||||
return tmp_path
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def write_config(project_dir: Path) -> WriteConfig:
|
||||
"""Write a bulkgen YAML config and return the loaded ProjectConfig.
|
||||
|
||||
Usage::
|
||||
|
||||
config = write_config({"targets": {"out.txt": {"prompt": "hello"}}})
|
||||
"""
|
||||
|
||||
def _write(raw: dict[str, object]) -> ProjectConfig:
|
||||
config_path = project_dir / "project.bulkgen.yaml"
|
||||
_ = config_path.write_text(yaml.dump(raw, default_flow_style=False))
|
||||
return load_config(config_path)
|
||||
|
||||
return _write
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def simple_text_config(write_config: WriteConfig) -> ProjectConfig:
|
||||
"""A minimal config with one text target."""
|
||||
return write_config({"targets": {"output.txt": {"prompt": "Generate something"}}})
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def multi_target_config(project_dir: Path, write_config: WriteConfig) -> ProjectConfig:
|
||||
"""Config with multiple targets forming a dependency chain.
|
||||
|
||||
input.txt (external file) -> summary.md -> final.txt
|
||||
Also has an independent image target: hero.png
|
||||
"""
|
||||
_ = (project_dir / "input.txt").write_text("Some input content")
|
||||
return write_config(
|
||||
{
|
||||
"targets": {
|
||||
"summary.md": {
|
||||
"prompt": "Summarize the input",
|
||||
"inputs": ["input.txt"],
|
||||
},
|
||||
"final.txt": {
|
||||
"prompt": "Finalize the summary",
|
||||
"inputs": ["summary.md"],
|
||||
},
|
||||
"hero.png": {
|
||||
"prompt": "A heroic landscape",
|
||||
"width": 1024,
|
||||
"height": 768,
|
||||
},
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def prompt_file(project_dir: Path) -> Path:
|
||||
"""Create a prompt file on disk and return its path."""
|
||||
p = project_dir / "my_prompt.txt"
|
||||
_ = p.write_text("This prompt comes from a file")
|
||||
return p
|
||||
Loading…
Add table
Add a link
Reference in a new issue