114 lines
3.7 KiB
Python
114 lines
3.7 KiB
Python
from __future__ import annotations
|
||
|
||
from pathlib import Path
|
||
|
||
import pytest
|
||
import yaml
|
||
|
||
from jinjaturtle.core import (
|
||
parse_config,
|
||
flatten_config,
|
||
generate_ansible_yaml,
|
||
generate_jinja2_template,
|
||
)
|
||
from jinjaturtle.handlers.toml import TomlHandler
|
||
import jinjaturtle.handlers.toml as toml_module
|
||
|
||
SAMPLES_DIR = Path(__file__).parent / "samples"
|
||
|
||
|
||
def test_toml_sample_roundtrip():
|
||
toml_path = SAMPLES_DIR / "tom.toml"
|
||
assert toml_path.is_file(), f"Missing sample TOML file: {toml_path}"
|
||
|
||
fmt, parsed = parse_config(toml_path)
|
||
assert fmt == "toml"
|
||
|
||
flat_items = flatten_config(fmt, parsed)
|
||
assert flat_items
|
||
|
||
ansible_yaml = generate_ansible_yaml("jinjaturtle", flat_items)
|
||
defaults = yaml.safe_load(ansible_yaml)
|
||
|
||
# defaults should be a non-empty dict
|
||
assert isinstance(defaults, dict)
|
||
assert defaults, "Expected non-empty defaults for TOML sample"
|
||
|
||
# all keys should be lowercase, start with prefix, and have no spaces
|
||
for key in defaults:
|
||
assert key.startswith("jinjaturtle_")
|
||
assert key == key.lower()
|
||
assert " " not in key
|
||
|
||
# template generation – **now with original_text**
|
||
original_text = toml_path.read_text(encoding="utf-8")
|
||
template = generate_jinja2_template(
|
||
fmt, parsed, "jinjaturtle", original_text=original_text
|
||
)
|
||
assert isinstance(template, str)
|
||
assert template.strip()
|
||
|
||
# comments from the original file should now be preserved
|
||
assert "# This is a TOML document" in template
|
||
|
||
# each default variable name should appear in the template as a Jinja placeholder
|
||
for var_name in defaults:
|
||
assert (
|
||
var_name in template
|
||
), f"Variable {var_name} not referenced in TOML template"
|
||
|
||
|
||
def test_parse_config_toml_missing_tomllib(monkeypatch):
|
||
"""
|
||
Force tomllib to None to hit the RuntimeError branch when parsing TOML.
|
||
"""
|
||
toml_path = SAMPLES_DIR / "tom.toml"
|
||
|
||
# Simulate an environment without tomllib/tomli
|
||
monkeypatch.setattr(toml_module, "tomllib", None)
|
||
|
||
with pytest.raises(RuntimeError) as exc:
|
||
parse_config(toml_path, fmt="toml")
|
||
assert "tomllib/tomli is required" in str(exc.value)
|
||
|
||
|
||
def test_generate_jinja2_template_fallback_toml():
|
||
"""
|
||
When original_text is not provided, generate_jinja2_template should use the
|
||
structural fallback path for TOML configs.
|
||
"""
|
||
parsed_toml = {
|
||
"title": "Example",
|
||
"server": {"port": 8080, "host": "127.0.0.1"},
|
||
"logging": {
|
||
"file": {"path": "/tmp/app.log"}
|
||
}, # nested table to hit recursive walk
|
||
}
|
||
tmpl_toml = generate_jinja2_template("toml", parsed=parsed_toml, role_prefix="role")
|
||
assert "[server]" in tmpl_toml
|
||
assert "role_server_port" in tmpl_toml
|
||
assert "[logging]" in tmpl_toml or "[logging.file]" in tmpl_toml
|
||
|
||
|
||
def test_generate_toml_template_from_text_edge_cases():
|
||
# Cover CRLF newlines, lines without '=', empty keys, and inline tables
|
||
# that both parse successfully and fail parsing.
|
||
text = (
|
||
"# comment\r\n"
|
||
"[table]\r\n"
|
||
"noequals\r\n"
|
||
" = 42\r\n"
|
||
'inline_good = { name = "abc", value = 1 }\r\n'
|
||
"inline_bad = { invalid = }\r\n"
|
||
)
|
||
handler = TomlHandler()
|
||
tmpl = handler._generate_toml_template_from_text("role", text)
|
||
|
||
# The good inline table should expand into two separate variables.
|
||
assert "role_table_inline_good_name" in tmpl
|
||
assert "role_table_inline_good_value" in tmpl
|
||
# The bad inline table should fall back to scalar handling.
|
||
assert "role_table_inline_bad" in tmpl
|
||
# Ensure the lines without '=' / empty key were handled without exploding.
|
||
assert "[table]" in tmpl
|
||
assert "noequals" in tmpl
|