summaryrefslogtreecommitdiff
path: root/tests/test_config.py
blob: c24064bcbe941ee6b3bda4e33da00285f398e43d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
from __future__ import annotations

from pathlib import Path

import pytest

from mcp_sd15.config import ConfigError, load_config


def make_models(root: Path) -> None:
    for folder, names in {
        "checkpoints": ["base.safetensors"],
        "vae": ["vae.safetensors"],
        "lora": ["a.safetensors", "b.safetensors"],
        "controlnet": ["depth.safetensors", "pose.safetensors", "inpaint.safetensors"],
    }.items():
        destination = root / folder
        destination.mkdir(parents=True)
        for name in names:
            (destination / name).touch()


def valid_toml(root: Path) -> str:
    return f'''[model]
root = "{root.as_posix()}"
checkpoint = "base.safetensors"
vae = "vae.safetensors"
[[model.lora]]
file = "a.safetensors"
weight = 0.8
[[model.lora]]
file = "b.safetensors"
weight = -0.2
[generation]
steps = 20
cfg_scale = 6.5
scheduler = "euler_a"
default_size = "portrait_512"
[controlnet.depth]
enabled = true
model = "depth.safetensors"
preprocessor = "midas"
[controlnet.openpose]
enabled = true
model = "pose.safetensors"
[controlnet.inpaint]
enabled = true
model = "inpaint.safetensors"
[output]
enabled = true
directory = "generated"
'''


def test_loads_and_resolves_all_model_paths(tmp_path: Path) -> None:
    root = tmp_path / "models"
    make_models(root)
    path = tmp_path / "config.toml"
    path.write_text(valid_toml(root), encoding="utf-8")

    config = load_config(path)

    assert config.model.checkpoint_path() == root / "checkpoints" / "base.safetensors"
    assert config.model.vae_path() == root / "vae" / "vae.safetensors"
    assert config.model.lora_paths() == (
        root / "lora" / "a.safetensors",
        root / "lora" / "b.safetensors",
    )
    assert config.generation.default_size == "portrait_512"
    assert config.output.directory == tmp_path / "generated"
    assert config.controlnet_path("openpose_full") == root / "controlnet" / "pose.safetensors"


@pytest.mark.parametrize(
    "line, message",
    [
        ('checkpoint = "../base.safetensors"', "file name"),
        ("steps = 0", "steps"),
        ('scheduler = "dpmpp_2m"', "scheduler"),
        ('scheduler = "euler"', "scheduler"),
        ('scheduler = "unknown"', "scheduler"),
        ('default_size = "1024"', "default_size"),
        ('preprocessor = "zoe"', "preprocessor"),
    ],
)
def test_rejects_invalid_values(tmp_path: Path, line: str, message: str) -> None:
    root = tmp_path / "models"
    make_models(root)
    text = valid_toml(root)
    if line.startswith("checkpoint"):
        text = text.replace('checkpoint = "base.safetensors"', line)
    elif line.startswith("steps"):
        text = text.replace("steps = 20", line)
    elif line.startswith("scheduler"):
        text = text.replace('scheduler = "euler_a"', line)
    elif line.startswith("default_size"):
        text = text.replace('default_size = "portrait_512"', line)
    else:
        text = text.replace('preprocessor = "midas"', line)
    path = tmp_path / "bad.toml"
    path.write_text(text, encoding="utf-8")

    with pytest.raises(ConfigError, match=message):
        load_config(path)


def test_missing_model_fails_startup_validation(tmp_path: Path) -> None:
    root = tmp_path / "models"
    make_models(root)
    (root / "controlnet" / "depth.safetensors").unlink()
    path = tmp_path / "config.toml"
    path.write_text(valid_toml(root), encoding="utf-8")

    with pytest.raises(ConfigError, match=r"depth\.safetensors"):
        load_config(path)


def test_can_skip_path_checks_for_schema_only_use(tmp_path: Path) -> None:
    path = tmp_path / "config.toml"
    path.write_text(
        '[model]\nroot = "missing"\ncheckpoint = "base.safetensors"\n', encoding="utf-8"
    )
    config = load_config(path, validate_paths=False)
    assert config.model.root == (tmp_path / "missing").resolve()
    assert config.generation.scheduler == "dpmpp_2m_karras"