diff --git a/tests/unit/test_config.py b/tests/unit/test_config.py new file mode 100644 index 0000000..c1ae224 --- /dev/null +++ b/tests/unit/test_config.py @@ -0,0 +1,188 @@ +from pathlib import Path +import pytest +from omicron.ssg.config import SiteConfig, ConfigError, DEFAULT_ITEMS_PER_PAGE + + +@pytest.mark.parametrize( + "value, expected_origin, expected_path", + [ + (None, "", Path("/")), + ("http://example.com/blog", "http://example.com", Path("/blog")), + ("http://example.com", "http://example.com", Path("/")), + ("//example.com/blog", "//example.com", Path("/blog")), + ("/blog", "", Path("/blog")), + ], +) +def test_parse_base_url(value, expected_origin, expected_path): + origin, path = SiteConfig.parse_base_url(value) + assert origin == expected_origin + assert path == expected_path + + +@pytest.mark.parametrize( + "value, expected_message", + [ + ("https://[::1", "invalid url format"), + ("relative/path", "relative path"), + ], +) +def test_parse_base_url_error(value, expected_message): + with pytest.raises(ConfigError, match=expected_message): + origin, path = SiteConfig.parse_base_url(value) + + +def test_from_dict_minimal(): + config = SiteConfig.from_dict({"name": "My Site", "template": "plain"}) + + assert config.name == "My Site" + assert config.template == "plain" + assert config.items_per_page == DEFAULT_ITEMS_PER_PAGE + assert config.origin == "" + assert config.base_dir == Path("/") + + +def test_from_dict_full(): + config = SiteConfig.from_dict( + { + "name": "My Site", + "template": "plain", + "base_url": "http://example.com/blog", + "items_per_page": 42, + } + ) + + assert config.name == "My Site" + assert config.template == "plain" + assert config.items_per_page == 42 + assert config.origin == "http://example.com" + assert config.base_dir == Path("/blog") + + +MISSING = object() + + +@pytest.mark.parametrize( + "overrides, expected_message", + [ + ({"name": MISSING}, "must contain a name property"), + ({"template": MISSING}, "must contain a template property"), + ({"name": 123}, "name config property must be a string"), + ({"template": 123}, "template config property must be a string"), + ({"base_url": 123}, "base_url config property must be a string"), + ({"base_url": "relative/path"}, "relative path"), + ({"base_url": "https://[::1"}, "invalid url format"), + ( + {"items_per_page": "10"}, + "items_per_page config property must be a positive integer", + ), + ( + {"items_per_page": 0}, + "items_per_page config property must be a positive integer", + ), + ( + {"items_per_page": -1}, + "items_per_page config property must be a positive integer", + ), + ], +) +def test_from_dict_error(overrides, expected_message): + config = {"name": "My Site", "template": "plain"} + for key, value in overrides.items(): + if value is MISSING: + config.pop(key, None) + else: + config[key] = value + + with pytest.raises(ConfigError, match=expected_message): + SiteConfig.from_dict(config) + + +def test_from_file_minimal(tmp_path): + path = tmp_path / "config.yml" + path.write_text("name: My Site\ntemplate: plain\n") + + config = SiteConfig.from_file(path) + + assert config.name == "My Site" + assert config.template == "plain" + assert config.items_per_page == DEFAULT_ITEMS_PER_PAGE + assert config.origin == "" + assert config.base_dir == Path("/") + + +def test_from_file_full(tmp_path): + path = tmp_path / "config.yml" + path.write_text( + "name: My Site\n" + "template: plain\n" + "base_url: http://example.com/blog\n" + "items_per_page: 42\n" + ) + + config = SiteConfig.from_file(path) + + assert config.name == "My Site" + assert config.template == "plain" + assert config.items_per_page == 42 + assert config.origin == "http://example.com" + assert config.base_dir == Path("/blog") + + +def test_from_file_missing(tmp_path): + path = tmp_path / "config.yml" + + with pytest.raises(ConfigError, match="config file missing"): + SiteConfig.from_file(path) + + +@pytest.mark.parametrize( + "yaml_text, expected_message", + [ + ("- a\n- b\n", "must be a YAML mapping"), + ("just a string\n", "must be a YAML mapping"), + ("123: value\n", "must be a YAML mapping"), + ("name: [1, 2]\n", "must be a YAML mapping"), + ("name: true\n", "must be a YAML mapping"), + (":\n bad: yaml: here\n", "must be a valid YAML file"), + ("template: plain\n", "must contain a name property"), + ("name: My Site\n", "must contain a template property"), + ( + "name: 123\ntemplate: plain\n", + "name config property must be a string", + ), + ( + "name: My Site\ntemplate: 123\n", + "template config property must be a string", + ), + ( + "name: My Site\ntemplate: plain\nbase_url: 123\n", + "base_url config property must be a string", + ), + ( + "name: My Site\ntemplate: plain\nbase_url: relative/path\n", + "relative path", + ), + ( + "name: My Site\ntemplate: plain\nbase_url: https://[::1\n", + "invalid url format", + ), + ( + 'name: My Site\ntemplate: plain\nitems_per_page: "10"\n', + "items_per_page config property must be a positive integer", + ), + ( + "name: My Site\ntemplate: plain\nitems_per_page: 0\n", + "items_per_page config property must be a positive integer", + ), + ( + "name: My Site\ntemplate: plain\nitems_per_page: -1\n", + "items_per_page config property must be a positive integer", + ), + ], +) +def test_from_file_error(tmp_path, yaml_text, expected_message): + path = tmp_path / "config.yml" + path.write_text(yaml_text) + + with pytest.raises(ConfigError, match=expected_message): + SiteConfig.from_file(path) diff --git a/tests/unit/test_site.py b/tests/unit/test_site.py index d57f853..f4a0068 100644 --- a/tests/unit/test_site.py +++ b/tests/unit/test_site.py @@ -3,21 +3,6 @@ import pytest from omicron.ssg.output import ContentError, Directory, OutputError from omicron.ssg.site import ConfigError, Site -# @pytest.mark.parametrize( -# "value, expected_origin, expected_path", -# [ -# (None, "", Path("/")), -# ("http://example.com/blog", "http://example.com", Path("/blog")), -# ("http://example.com", "http://example.com", Path("/")), -# ("//example.com/blog", "//example.com", Path("/blog")), -# ("/blog", "", Path("/blog")), -# ], -# ) -# def test_parse_base_url(value, expected_origin, expected_path): -# origin, path = Site.parse_base_url(value) -# assert origin == expected_origin -# assert path == expected_path - def test_discover_raises_on_duplicate_destination(make_site): site = make_site("duplicate_slug") @@ -58,13 +43,12 @@ def test_read_config_errors(site_dir): Site(path) -def test_build_raises_for_relative_base_url(site_dir): +def test_site_relative_base_url(site_dir): path = site_dir("thin_site") (path / "config.yml").write_text("name: test\ntemplate: plain\nbase_url: blog\n") - site = Site(path) - with pytest.raises(ConfigError, match="absolute path"): - site.build() + with pytest.raises(ConfigError, match="relative path"): + site = Site(path) def test_discover_skips_drafts_by_default(make_site):