Some checks failed
continuous-integration/drone/push Build is failing
- harness/settings.py: stdlib tomllib loader, [upgrade].skip_canonicals_for_upgrade (bool, default false), _SCHEMA single-source defaults+validation; graceful on absent/malformed (WARN+defaults), warn-and-ignore unknown keys/tables, TypeError on wrong type. Path $CCCI_SETTINGS / /etc/cc-ci/settings.toml. + tracked settings.toml.example. - resolve_upgrade_base: flag true bypasses the canonical lookup -> no-canonical fallback; canonical-present path (incl. samever step-back) unchanged when false. - _no_canonical_base (always-on, §2.C): newest release tag < head (reuse warm_reconcile.newest_older_version) -> main-tip -> skip; replaces jump-to-main-tip. - unit: full resolution matrix + loader tests; 315 unit pass, ruff clean. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
112 lines
3.9 KiB
Python
112 lines
3.9 KiB
Python
"""Unit tests for `harness.settings` — the minimal, extensible server-level TOML config loader
|
|
(phase settings). Stdlib `tomllib`; defaults baked in; absent/malformed file degrades to defaults
|
|
(never crashes the harness); unknown keys warn-and-ignore; a present known key of the wrong type
|
|
errors clearly.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import os
|
|
import sys
|
|
|
|
import pytest
|
|
|
|
sys.path.insert(0, os.path.join(os.path.dirname(__file__), "..", "..", "runner"))
|
|
from harness import settings # noqa: E402
|
|
|
|
|
|
def _write(tmp_path, text: str) -> str:
|
|
p = tmp_path / "settings.toml"
|
|
p.write_text(text)
|
|
return str(p)
|
|
|
|
|
|
def test_absent_file_yields_defaults(tmp_path):
|
|
# a path that does not exist → all-defaults, no exception
|
|
missing = str(tmp_path / "nope.toml")
|
|
s = settings.load(missing)
|
|
assert s.skip_canonicals_for_upgrade is False
|
|
|
|
|
|
def test_absent_key_yields_default(tmp_path):
|
|
# present file, present table, but the key omitted → default
|
|
p = _write(tmp_path, "[upgrade]\n")
|
|
assert settings.load(p).skip_canonicals_for_upgrade is False
|
|
|
|
|
|
def test_empty_file_yields_defaults(tmp_path):
|
|
p = _write(tmp_path, "")
|
|
assert settings.load(p).skip_canonicals_for_upgrade is False
|
|
|
|
|
|
def test_flag_true_read(tmp_path):
|
|
p = _write(tmp_path, "[upgrade]\nskip_canonicals_for_upgrade = true\n")
|
|
assert settings.load(p).skip_canonicals_for_upgrade is True
|
|
|
|
|
|
def test_flag_false_read(tmp_path):
|
|
p = _write(tmp_path, "[upgrade]\nskip_canonicals_for_upgrade = false\n")
|
|
assert settings.load(p).skip_canonicals_for_upgrade is False
|
|
|
|
|
|
def test_malformed_toml_degrades_to_defaults(tmp_path):
|
|
# syntactically broken TOML must NOT crash the harness — WARN + defaults.
|
|
p = _write(tmp_path, "[upgrade\nskip_canonicals_for_upgrade = tru")
|
|
s = settings.load(p) # must not raise
|
|
assert s.skip_canonicals_for_upgrade is False
|
|
|
|
|
|
def test_wrong_type_errors_clearly(tmp_path):
|
|
# a present key of the wrong type is a loud, actionable error (distinct from a malformed file).
|
|
p = _write(tmp_path, '[upgrade]\nskip_canonicals_for_upgrade = "yes"\n')
|
|
with pytest.raises(TypeError) as e:
|
|
settings.load(p)
|
|
assert "skip_canonicals_for_upgrade" in str(e.value)
|
|
assert "bool" in str(e.value)
|
|
|
|
|
|
def test_int_not_accepted_for_bool(tmp_path):
|
|
# bool is an int subclass — a stray 1/0 must not silently coerce to a flag.
|
|
p = _write(tmp_path, "[upgrade]\nskip_canonicals_for_upgrade = 1\n")
|
|
with pytest.raises(TypeError):
|
|
settings.load(p)
|
|
|
|
|
|
def test_unknown_key_warns_and_ignored(tmp_path, capsys):
|
|
p = _write(
|
|
tmp_path,
|
|
"[upgrade]\nskip_canonicals_for_upgrade = true\nfuture_knob = 7\n",
|
|
)
|
|
s = settings.load(p)
|
|
assert s.skip_canonicals_for_upgrade is True # known key still honored
|
|
err = capsys.readouterr().err
|
|
assert "unknown key" in err and "future_knob" in err
|
|
|
|
|
|
def test_unknown_table_warns_and_ignored(tmp_path, capsys):
|
|
p = _write(tmp_path, "[future_section]\nx = 1\n")
|
|
s = settings.load(p)
|
|
assert s.skip_canonicals_for_upgrade is False
|
|
err = capsys.readouterr().err
|
|
assert "unknown table" in err and "future_section" in err
|
|
|
|
|
|
def test_non_table_section_ignored(tmp_path, capsys):
|
|
# a key named like a table but given a scalar — warn, ignore, fall back to defaults for that table.
|
|
p = _write(tmp_path, "upgrade = 5\n")
|
|
s = settings.load(p)
|
|
assert s.skip_canonicals_for_upgrade is False
|
|
assert "not a table" in capsys.readouterr().err
|
|
|
|
|
|
def test_env_var_path_override(tmp_path, monkeypatch):
|
|
p = _write(tmp_path, "[upgrade]\nskip_canonicals_for_upgrade = true\n")
|
|
monkeypatch.setenv("CCCI_SETTINGS", p)
|
|
assert settings.load().skip_canonicals_for_upgrade is True
|
|
|
|
|
|
def test_default_path_is_absolute_host_path():
|
|
# the live file is an absolute host override co-located with the deployed checkout.
|
|
assert settings.DEFAULT_PATH == "/etc/cc-ci/settings.toml"
|
|
assert os.path.isabs(settings.DEFAULT_PATH)
|