feat: add config write API (POST /api/0/config, POST /api/0/config/rollback)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-05-09 11:35:45 -04:00
parent 2009626fb4
commit 55bdb9593a
2 changed files with 152 additions and 0 deletions
+64
View File
@@ -47,3 +47,67 @@ def test_mask_config_for_api_no_password_in_users_leaves_no_key():
}
result = http._mask_config_for_api(config)
assert "password" not in result["users"]["bob"]
# ---- configio integration for write path ----
def test_write_path_applies_server_section(tmp_path):
cfg = tmp_path / ".hb.yaml"
cfg.write_text("hbd_port: 50004\ninterval: 20\nusers: {}\n")
from hbd.server import configio
data = configio.read_roundtrip(str(cfg))
configio.apply_structured_section(data, "server", {"interval": 60})
configio.write_config(str(cfg), data)
data2 = configio.read_roundtrip(str(cfg))
assert data2["interval"] == 60
assert data2["hbd_port"] == 50004 # unchanged
def test_write_path_applies_yaml_section(tmp_path):
cfg = tmp_path / ".hb.yaml"
cfg.write_text(
"hbd_port: 50004\nnotification_channels:\n old_ch:\n type: email\n"
)
from hbd.server import configio
data = configio.read_roundtrip(str(cfg))
configio.apply_yaml_section(data, "notification_channels", "new_ch:\n type: pushover\n")
configio.write_config(str(cfg), data)
data2 = configio.read_roundtrip(str(cfg))
assert "new_ch" in data2["notification_channels"]
assert "old_ch" not in data2["notification_channels"]
def test_write_path_hashes_plaintext_password(tmp_path):
cfg = tmp_path / ".hb.yaml"
cfg.write_text("hbd_port: 50004\nusers:\n alice:\n full_name: Alice\n admin: true\n password: pbkdf2:sha256:old\n")
from hbd.server import configio
from hbd.server import users as users_mod
data = configio.read_roundtrip(str(cfg))
# Simulate what the POST handler does: hash plaintext password
new_users = {"alice": {"full_name": "Alice", "admin": True, "password": "newplaintext"}}
for username, attrs in new_users.items():
pw = attrs.get("password", "")
if pw and not pw.startswith("pbkdf2:"):
attrs["password"] = users_mod.hash_password(pw)
configio.apply_structured_section(data, "users", new_users)
configio.write_config(str(cfg), data)
data2 = configio.read_roundtrip(str(cfg))
assert data2["users"]["alice"]["password"].startswith("pbkdf2:")
assert data2["users"]["alice"]["password"] != "newplaintext"
def test_rollback_restores_backup(tmp_path):
cfg = tmp_path / ".hb.yaml"
cfg.write_text("hbd_port: 50004\ninterval: 20\n")
from hbd.server import configio
# Make a change to create a backup
data = configio.read_roundtrip(str(cfg))
data["interval"] = 99
configio.write_config(str(cfg), data)
backups = configio.list_backups(str(cfg))
assert len(backups) == 1
# Read the backup and write it back (simulating rollback)
backup_data = configio.read_roundtrip(backups[0])
configio.write_config(str(cfg), backup_data)
restored = configio.read_roundtrip(str(cfg))
assert restored["interval"] == 20