feat(config): add TOML config file support
- Add config.py with load_config(), find_default_config(), get_token(), and get_map_path() - Auto-discover timesheets.toml in cwd; override with --config flag - Priority: CLI flag > config file > env var / cwd default - Add timesheets.example.toml as a committed reference template - Add timesheets.toml to .gitignore to prevent accidental secret leakage - Document config file format in AGENTS.md
This commit is contained in:
parent
267ad5b1b5
commit
29698b1241
6 changed files with 183 additions and 5 deletions
88
tests/test_config.py
Normal file
88
tests/test_config.py
Normal file
|
|
@ -0,0 +1,88 @@
|
|||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
import pytest
|
||||
|
||||
from timesheets.config import (
|
||||
DEFAULT_CONFIG_FILENAME,
|
||||
find_default_config,
|
||||
get_map_path,
|
||||
get_token,
|
||||
load_config,
|
||||
)
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# load_config
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
class TestLoadConfig:
|
||||
def test_none_returns_empty(self):
|
||||
assert load_config(None) == {}
|
||||
|
||||
def test_loads_valid_toml(self, tmp_path):
|
||||
f = tmp_path / "timesheets.toml"
|
||||
f.write_text('[joplin]\ntoken = "abc123"\n')
|
||||
result = load_config(f)
|
||||
assert result == {"joplin": {"token": "abc123"}}
|
||||
|
||||
def test_missing_file_exits(self, tmp_path, capsys):
|
||||
with pytest.raises(SystemExit):
|
||||
load_config(tmp_path / "nonexistent.toml")
|
||||
assert "not found" in capsys.readouterr().err
|
||||
|
||||
def test_invalid_toml_exits(self, tmp_path, capsys):
|
||||
f = tmp_path / "bad.toml"
|
||||
f.write_text("not = valid = toml\n")
|
||||
with pytest.raises(SystemExit):
|
||||
load_config(f)
|
||||
assert "could not parse" in capsys.readouterr().err
|
||||
|
||||
def test_accepts_str_path(self, tmp_path):
|
||||
f = tmp_path / "timesheets.toml"
|
||||
f.write_text('[projects]\nmap = "/some/path.json"\n')
|
||||
result = load_config(str(f))
|
||||
assert result["projects"]["map"] == "/some/path.json"
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# find_default_config
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
class TestFindDefaultConfig:
|
||||
def test_returns_none_when_absent(self, tmp_path, monkeypatch):
|
||||
monkeypatch.chdir(tmp_path)
|
||||
assert find_default_config() is None
|
||||
|
||||
def test_returns_path_when_present(self, tmp_path, monkeypatch):
|
||||
monkeypatch.chdir(tmp_path)
|
||||
cfg = tmp_path / DEFAULT_CONFIG_FILENAME
|
||||
cfg.write_text("")
|
||||
result = find_default_config()
|
||||
assert result == cfg
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# get_token / get_map_path
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
class TestGetters:
|
||||
def test_get_token_present(self):
|
||||
assert get_token({"joplin": {"token": "xyz"}}) == "xyz"
|
||||
|
||||
def test_get_token_missing_section(self):
|
||||
assert get_token({}) is None
|
||||
|
||||
def test_get_token_missing_key(self):
|
||||
assert get_token({"joplin": {}}) is None
|
||||
|
||||
def test_get_map_path_present(self):
|
||||
assert get_map_path({"projects": {"map": "/a/b.json"}}) == "/a/b.json"
|
||||
|
||||
def test_get_map_path_missing_section(self):
|
||||
assert get_map_path({}) is None
|
||||
|
||||
def test_get_map_path_missing_key(self):
|
||||
assert get_map_path({"projects": {}}) is None
|
||||
Loading…
Add table
Add a link
Reference in a new issue