test: Restructure test files
Extract the individual parsing tests (cli, env, rc) and add additional configuration test file (TConf and config builder). To extract the fixtures they have to go into an additional 'conftest.py' file for pytest to recognize and automatically import them, see: https://docs.pytest.org/en/stable/reference/fixtures.html#conftest-py-sharing-fixtures-across-multiple-files and: https://docs.pytest.org/en/stable/how-to/fixtures.html#using-fixtures-from-other-projects
This commit is contained in:
parent
ff0e6cccfb
commit
e50fc9444a
5 changed files with 201 additions and 112 deletions
27
test/conftest.py
Normal file
27
test/conftest.py
Normal file
|
|
@ -0,0 +1,27 @@
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
from topen import OPTIONS
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def fake_id(monkeypatch):
|
||||||
|
monkeypatch.setattr("sys.argv", ["topen", "0"])
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def isolate_env(monkeypatch):
|
||||||
|
# delete all existing env vars that could interfere
|
||||||
|
monkeypatch.delenv("EDITOR", raising=False)
|
||||||
|
monkeypatch.delenv("VISUAL", raising=False)
|
||||||
|
for opt in OPTIONS.values():
|
||||||
|
if opt.env:
|
||||||
|
monkeypatch.delenv(opt.env, raising=False)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def fake_rc(tmp_path: Path, monkeypatch):
|
||||||
|
rc = tmp_path / "test.taskrc"
|
||||||
|
monkeypatch.setattr(OPTIONS["task_rc"], "default", rc)
|
||||||
|
return rc
|
||||||
|
|
@ -1,118 +1,11 @@
|
||||||
|
# pyright: reportUnusedImport=false, reportUnusedParameter=false
|
||||||
|
# ruff: noqa: F401, F811
|
||||||
|
# ^ Turn off for implicit pytest fixture import
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from topen import OPTIONS, TConf, parse_cli, parse_env, parse_rc
|
from topen import TConf, build_config
|
||||||
|
|
||||||
|
|
||||||
class TestCli:
|
|
||||||
def test_cli_minimum_id(self, monkeypatch):
|
|
||||||
monkeypatch.setattr("sys.argv", ["topen", "42"])
|
|
||||||
assert parse_cli() == {"task_id": "42"}
|
|
||||||
|
|
||||||
def test_cli_options(self, monkeypatch):
|
|
||||||
monkeypatch.setattr(
|
|
||||||
"sys.argv",
|
|
||||||
[
|
|
||||||
"topen",
|
|
||||||
"123",
|
|
||||||
"--extension",
|
|
||||||
"txt",
|
|
||||||
"--editor",
|
|
||||||
"vim",
|
|
||||||
"--quiet",
|
|
||||||
"True",
|
|
||||||
],
|
|
||||||
)
|
|
||||||
assert parse_cli() == {
|
|
||||||
"task_id": "123",
|
|
||||||
"notes_ext": "txt",
|
|
||||||
"notes_editor": "vim",
|
|
||||||
"notes_quiet": True,
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
|
||||||
def isolate_env(monkeypatch):
|
|
||||||
# delete all existing env vars that could interfere
|
|
||||||
monkeypatch.delenv("EDITOR", raising=False)
|
|
||||||
monkeypatch.delenv("VISUAL", raising=False)
|
|
||||||
for opt in OPTIONS.values():
|
|
||||||
if opt.env:
|
|
||||||
monkeypatch.delenv(opt.env, raising=False)
|
|
||||||
|
|
||||||
|
|
||||||
class TestEnv:
|
|
||||||
def test_env_notes_ext(self, isolate_env, monkeypatch):
|
|
||||||
monkeypatch.setenv("TOPEN_NOTES_DIR", "/blablubb")
|
|
||||||
monkeypatch.setenv("TOPEN_NOTES_EXT", "rst")
|
|
||||||
monkeypatch.setenv("TOPEN_NOTES_ANNOT", "qmd")
|
|
||||||
monkeypatch.setenv("TOPEN_NOTES_EDITOR", "vim")
|
|
||||||
monkeypatch.setenv("TOPEN_NOTES_QUIET", "true")
|
|
||||||
env = parse_env()
|
|
||||||
assert env["notes_dir"] == Path("/blablubb")
|
|
||||||
assert env["notes_ext"] == "rst"
|
|
||||||
assert env["notes_annot"] == "qmd"
|
|
||||||
assert env["notes_editor"] == "vim"
|
|
||||||
assert env["notes_quiet"] is True
|
|
||||||
|
|
||||||
def test_env_task_rc(self, isolate_env, monkeypatch):
|
|
||||||
monkeypatch.setenv("TASKRC", "/a/dir/that/dont/exist/file")
|
|
||||||
monkeypatch.setenv("TASKDATA", "~/somewhere/tasks")
|
|
||||||
env = parse_env()
|
|
||||||
assert env["task_rc"] == Path("/a/dir/that/dont/exist/file")
|
|
||||||
assert env["task_data"] == Path("~/somewhere/tasks")
|
|
||||||
|
|
||||||
def test_env_parses_boolean_true(self, isolate_env, monkeypatch):
|
|
||||||
monkeypatch.setenv("TOPEN_NOTES_QUIET", "true")
|
|
||||||
env = parse_env()
|
|
||||||
assert env["notes_quiet"] is True
|
|
||||||
|
|
||||||
def test_env_parses_boolean_false(self, isolate_env, monkeypatch):
|
|
||||||
monkeypatch.setenv("TOPEN_NOTES_QUIET", "false")
|
|
||||||
env = parse_env()
|
|
||||||
assert env["notes_quiet"] is False
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
|
||||||
def fake_rc(tmp_path: Path, monkeypatch):
|
|
||||||
rc = tmp_path / "test.taskrc"
|
|
||||||
monkeypatch.setattr(OPTIONS["task_rc"], "default", rc)
|
|
||||||
return rc
|
|
||||||
|
|
||||||
|
|
||||||
class TestRcFile:
|
|
||||||
def test_taskrc_parsing(self, fake_rc):
|
|
||||||
fake_rc.write_text("""
|
|
||||||
data.location=~/.taskies
|
|
||||||
notes.dir=/there
|
|
||||||
notes.ext=yaml
|
|
||||||
notes.annot=Boo!
|
|
||||||
notes.editor=micro
|
|
||||||
notes.quiet=true
|
|
||||||
""")
|
|
||||||
rc_cfg = parse_rc(fake_rc)
|
|
||||||
assert rc_cfg["task_data"] == Path("~/.taskies")
|
|
||||||
assert rc_cfg["notes_dir"] == Path("/there")
|
|
||||||
assert rc_cfg["notes_ext"] == "yaml"
|
|
||||||
assert rc_cfg["notes_annot"] == "Boo!"
|
|
||||||
assert rc_cfg["notes_editor"] == "micro"
|
|
||||||
assert rc_cfg["notes_quiet"] is True
|
|
||||||
|
|
||||||
def test_taskrc_parses_boolean_true(self, fake_rc):
|
|
||||||
fake_rc.write_text("""
|
|
||||||
notes.quiet=true
|
|
||||||
""")
|
|
||||||
rc_cfg = parse_rc(fake_rc)
|
|
||||||
assert rc_cfg["notes_quiet"] is True
|
|
||||||
|
|
||||||
def test_taskrc_parses_boolean_false(self, fake_rc):
|
|
||||||
fake_rc.write_text("""
|
|
||||||
notes.quiet=false
|
|
||||||
""")
|
|
||||||
rc_cfg = parse_rc(fake_rc)
|
|
||||||
assert rc_cfg["notes_quiet"] is False
|
|
||||||
|
|
||||||
|
|
||||||
class TestTConf:
|
class TestTConf:
|
||||||
def test_paths_are_expanded(self):
|
def test_paths_are_expanded(self):
|
||||||
|
|
@ -140,7 +33,68 @@ class TestTConf:
|
||||||
({"VISUAL": "nvim", "EDITOR": "notepad"}, "notepad"),
|
({"VISUAL": "nvim", "EDITOR": "notepad"}, "notepad"),
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
def test_editor_env_resolution(isolate_env, monkeypatch, env, expected):
|
def test_editor_env_resolution(self, isolate_env, monkeypatch, env, expected):
|
||||||
for k, v in env.items():
|
for k, v in env.items():
|
||||||
monkeypatch.setenv(k, v)
|
monkeypatch.setenv(k, v)
|
||||||
assert TConf(0).notes_editor == expected
|
assert TConf(0).notes_editor == expected
|
||||||
|
|
||||||
|
|
||||||
|
class TestBuildConfigPrecedence:
|
||||||
|
"""
|
||||||
|
All tests exercise the same key (notes_ext) to keep the assertions short.
|
||||||
|
Each source sets a different value so we can be sure the right one wins.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def test_defaults_only(self, fake_rc, monkeypatch, isolate_env, fake_id):
|
||||||
|
cfg = build_config()
|
||||||
|
assert cfg.notes_ext == "md"
|
||||||
|
|
||||||
|
def test_taskrc_overrides_defaults(
|
||||||
|
self, fake_rc, monkeypatch, isolate_env, fake_id
|
||||||
|
):
|
||||||
|
fake_rc.write_text("notes.ext=from-rc\n")
|
||||||
|
cfg = build_config()
|
||||||
|
assert cfg.notes_ext == "from-rc"
|
||||||
|
|
||||||
|
def test_env_overrides_taskrc(self, fake_rc, monkeypatch, isolate_env, fake_id):
|
||||||
|
fake_rc.write_text("notes.ext=from-rc\n")
|
||||||
|
monkeypatch.setenv("TOPEN_NOTES_EXT", "from-env")
|
||||||
|
cfg = build_config()
|
||||||
|
assert cfg.notes_ext == "from-env"
|
||||||
|
|
||||||
|
def test_cli_overrides_env(self, fake_rc, monkeypatch, isolate_env):
|
||||||
|
fake_rc.write_text("notes.ext=from-rc\n")
|
||||||
|
monkeypatch.setenv("TOPEN_NOTES_EXT", "from-env")
|
||||||
|
monkeypatch.setattr("sys.argv", ["topen", "0", "--extension", "from-cli"])
|
||||||
|
cfg = build_config()
|
||||||
|
assert cfg.notes_ext == "from-cli"
|
||||||
|
|
||||||
|
def test_cli_overrides_everything(self, fake_rc, monkeypatch, isolate_env):
|
||||||
|
fake_rc.write_text("notes.ext=from-rc\nnotes.dir=/rc-dir\nnotes.editor=joe")
|
||||||
|
monkeypatch.setenv("TOPEN_NOTES_EXT", "from-env")
|
||||||
|
monkeypatch.setenv("TOPEN_NOTES_DIR", "/env-dir")
|
||||||
|
monkeypatch.setenv("EDITOR", "emacs")
|
||||||
|
# CLI wins
|
||||||
|
monkeypatch.setattr(
|
||||||
|
"sys.argv",
|
||||||
|
[
|
||||||
|
"topen",
|
||||||
|
"0",
|
||||||
|
"--extension",
|
||||||
|
"cli-ext",
|
||||||
|
"--notes-dir",
|
||||||
|
"cli-dir",
|
||||||
|
"--editor",
|
||||||
|
"helix",
|
||||||
|
],
|
||||||
|
)
|
||||||
|
cfg = build_config()
|
||||||
|
assert cfg.notes_ext == "cli-ext"
|
||||||
|
assert cfg.notes_dir == Path("cli-dir")
|
||||||
|
assert cfg.notes_editor == "helix"
|
||||||
|
|
||||||
|
# sanity check that the task-id coming from CLI is preserved
|
||||||
|
def test_cli_supplies_task_id(self, fake_rc, monkeypatch, isolate_env):
|
||||||
|
monkeypatch.setattr("sys.argv", ["topen", "42"])
|
||||||
|
cfg = build_config()
|
||||||
|
assert cfg.task_id == "42"
|
||||||
|
|
|
||||||
37
test/test_parse_cli.py
Normal file
37
test/test_parse_cli.py
Normal file
|
|
@ -0,0 +1,37 @@
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
from topen import parse_cli
|
||||||
|
|
||||||
|
|
||||||
|
class TestCli:
|
||||||
|
def test_cli_minimum_id(self, monkeypatch):
|
||||||
|
monkeypatch.setattr("sys.argv", ["topen", "42"])
|
||||||
|
assert parse_cli() == {"task_id": "42"}
|
||||||
|
|
||||||
|
def test_cli_options(self, monkeypatch):
|
||||||
|
monkeypatch.setattr(
|
||||||
|
"sys.argv",
|
||||||
|
[
|
||||||
|
"topen",
|
||||||
|
"123",
|
||||||
|
"--extension",
|
||||||
|
"txt",
|
||||||
|
"--editor",
|
||||||
|
"vim",
|
||||||
|
"--annotation",
|
||||||
|
"HERENOTE",
|
||||||
|
],
|
||||||
|
)
|
||||||
|
assert parse_cli() == {
|
||||||
|
"task_id": "123",
|
||||||
|
"notes_ext": "txt",
|
||||||
|
"notes_editor": "vim",
|
||||||
|
"notes_annot": "HERENOTE",
|
||||||
|
}
|
||||||
|
|
||||||
|
def test_cli_parses_paths(self, monkeypatch):
|
||||||
|
monkeypatch.setattr(
|
||||||
|
"sys.argv",
|
||||||
|
["topen", "123", "--notes-dir", "/somewhere/else"],
|
||||||
|
)
|
||||||
|
assert parse_cli()["notes_dir"] == Path("/somewhere/else")
|
||||||
35
test/test_parse_env.py
Normal file
35
test/test_parse_env.py
Normal file
|
|
@ -0,0 +1,35 @@
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
from topen import parse_env
|
||||||
|
|
||||||
|
|
||||||
|
class TestEnv:
|
||||||
|
def test_env_notes_ext(self, isolate_env, monkeypatch):
|
||||||
|
monkeypatch.setenv("TOPEN_NOTES_DIR", "/blablubb")
|
||||||
|
monkeypatch.setenv("TOPEN_NOTES_EXT", "rst")
|
||||||
|
monkeypatch.setenv("TOPEN_NOTES_ANNOT", "qmd")
|
||||||
|
monkeypatch.setenv("TOPEN_NOTES_EDITOR", "vim")
|
||||||
|
monkeypatch.setenv("TOPEN_NOTES_QUIET", "true")
|
||||||
|
env = parse_env()
|
||||||
|
assert env["notes_dir"] == Path("/blablubb")
|
||||||
|
assert env["notes_ext"] == "rst"
|
||||||
|
assert env["notes_annot"] == "qmd"
|
||||||
|
assert env["notes_editor"] == "vim"
|
||||||
|
assert env["notes_quiet"] is True
|
||||||
|
|
||||||
|
def test_env_task_rc(self, isolate_env, monkeypatch):
|
||||||
|
monkeypatch.setenv("TASKRC", "/a/dir/that/dont/exist/file")
|
||||||
|
monkeypatch.setenv("TASKDATA", "~/somewhere/tasks")
|
||||||
|
env = parse_env()
|
||||||
|
assert env["task_rc"] == Path("/a/dir/that/dont/exist/file")
|
||||||
|
assert env["task_data"] == Path("~/somewhere/tasks")
|
||||||
|
|
||||||
|
def test_env_parses_boolean_true(self, isolate_env, monkeypatch):
|
||||||
|
monkeypatch.setenv("TOPEN_NOTES_QUIET", "true")
|
||||||
|
env = parse_env()
|
||||||
|
assert env["notes_quiet"] is True
|
||||||
|
|
||||||
|
def test_env_parses_boolean_false(self, isolate_env, monkeypatch):
|
||||||
|
monkeypatch.setenv("TOPEN_NOTES_QUIET", "false")
|
||||||
|
env = parse_env()
|
||||||
|
assert env["notes_quiet"] is False
|
||||||
36
test/test_parse_rc.py
Normal file
36
test/test_parse_rc.py
Normal file
|
|
@ -0,0 +1,36 @@
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
from topen import parse_rc
|
||||||
|
|
||||||
|
|
||||||
|
class TestRcFile:
|
||||||
|
def test_taskrc_parsing(self, fake_rc):
|
||||||
|
fake_rc.write_text("""
|
||||||
|
data.location=~/.taskies
|
||||||
|
notes.dir=/there
|
||||||
|
notes.ext=yaml
|
||||||
|
notes.annot=Boo!
|
||||||
|
notes.editor=micro
|
||||||
|
notes.quiet=true
|
||||||
|
""")
|
||||||
|
rc_cfg = parse_rc(fake_rc)
|
||||||
|
assert rc_cfg["task_data"] == Path("~/.taskies")
|
||||||
|
assert rc_cfg["notes_dir"] == Path("/there")
|
||||||
|
assert rc_cfg["notes_ext"] == "yaml"
|
||||||
|
assert rc_cfg["notes_annot"] == "Boo!"
|
||||||
|
assert rc_cfg["notes_editor"] == "micro"
|
||||||
|
assert rc_cfg["notes_quiet"] is True
|
||||||
|
|
||||||
|
def test_taskrc_parses_boolean_true(self, fake_rc):
|
||||||
|
fake_rc.write_text("""
|
||||||
|
notes.quiet=true
|
||||||
|
""")
|
||||||
|
rc_cfg = parse_rc(fake_rc)
|
||||||
|
assert rc_cfg["notes_quiet"] is True
|
||||||
|
|
||||||
|
def test_taskrc_parses_boolean_false(self, fake_rc):
|
||||||
|
fake_rc.write_text("""
|
||||||
|
notes.quiet=false
|
||||||
|
""")
|
||||||
|
rc_cfg = parse_rc(fake_rc)
|
||||||
|
assert rc_cfg["notes_quiet"] is False
|
||||||
Loading…
Add table
Add a link
Reference in a new issue