feat: rewrite core layer (errors, template, paths, platform, console, runtime, config)
Complete rewrite of all core modules with proper abstractions:
- FlowError hierarchy with PlanConflict and ExecutionError
- Pure template substitution ($VAR, ${VAR}, {{expr}})
- XDG path constants
- Frozen PlatformInfo dataclass with context detection
- Console with color/quiet/TTY support
- Runtime primitives (CommandRunner, FileSystem, GitClient, SystemRuntime)
- Config loading with target parsing and manifest merging
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
95
tests/test_core_runtime.py
Normal file
95
tests/test_core_runtime.py
Normal file
@@ -0,0 +1,95 @@
|
||||
"""Tests for flow.core.runtime."""
|
||||
|
||||
from pathlib import Path
|
||||
|
||||
from flow.core.runtime import CommandRunner, FileSystem, GitClient, SystemRuntime
|
||||
|
||||
|
||||
class TestFileSystem:
|
||||
def test_ensure_dir_creates_nested(self, tmp_path):
|
||||
fs = FileSystem()
|
||||
target = tmp_path / "a" / "b" / "c"
|
||||
fs.ensure_dir(target)
|
||||
assert target.is_dir()
|
||||
|
||||
def test_write_and_read_text(self, tmp_path):
|
||||
fs = FileSystem()
|
||||
path = tmp_path / "test.txt"
|
||||
fs.write_text(path, "hello")
|
||||
assert fs.read_text(path) == "hello"
|
||||
|
||||
def test_read_text_default(self, tmp_path):
|
||||
fs = FileSystem()
|
||||
path = tmp_path / "missing.txt"
|
||||
assert fs.read_text(path, default="fallback") == "fallback"
|
||||
|
||||
def test_write_and_read_json(self, tmp_path):
|
||||
fs = FileSystem()
|
||||
path = tmp_path / "data.json"
|
||||
fs.write_json(path, {"key": "value"})
|
||||
assert fs.read_json(path) == {"key": "value"}
|
||||
|
||||
def test_create_symlink(self, tmp_path):
|
||||
fs = FileSystem()
|
||||
source = tmp_path / "source"
|
||||
source.write_text("content")
|
||||
target = tmp_path / "link"
|
||||
fs.create_symlink(source, target)
|
||||
assert target.is_symlink()
|
||||
assert target.resolve() == source.resolve()
|
||||
|
||||
def test_same_symlink_true(self, tmp_path):
|
||||
fs = FileSystem()
|
||||
source = tmp_path / "source"
|
||||
source.write_text("content")
|
||||
target = tmp_path / "link"
|
||||
target.symlink_to(source)
|
||||
assert fs.same_symlink(target, source) is True
|
||||
|
||||
def test_same_symlink_false(self, tmp_path):
|
||||
fs = FileSystem()
|
||||
source = tmp_path / "source"
|
||||
source.write_text("content")
|
||||
other = tmp_path / "other"
|
||||
other.write_text("other")
|
||||
target = tmp_path / "link"
|
||||
target.symlink_to(other)
|
||||
assert fs.same_symlink(target, source) is False
|
||||
|
||||
def test_remove_file(self, tmp_path):
|
||||
fs = FileSystem()
|
||||
path = tmp_path / "file"
|
||||
path.write_text("x")
|
||||
fs.remove_file(path)
|
||||
assert not path.exists()
|
||||
|
||||
def test_remove_file_missing_ok(self, tmp_path):
|
||||
fs = FileSystem()
|
||||
fs.remove_file(tmp_path / "missing", missing_ok=True) # no error
|
||||
|
||||
def test_copy_file(self, tmp_path):
|
||||
fs = FileSystem()
|
||||
src = tmp_path / "src"
|
||||
src.write_text("data")
|
||||
dst = tmp_path / "sub" / "dst"
|
||||
fs.copy_file(src, dst)
|
||||
assert dst.read_text() == "data"
|
||||
|
||||
|
||||
class TestCommandRunner:
|
||||
def test_run_echo(self):
|
||||
runner = CommandRunner()
|
||||
result = runner.run(["echo", "hello"], capture_output=True)
|
||||
assert result.stdout.strip() == "hello"
|
||||
|
||||
def test_require_binary_finds_echo(self):
|
||||
runner = CommandRunner()
|
||||
path = runner.require_binary("echo")
|
||||
assert path is not None
|
||||
|
||||
|
||||
class TestSystemRuntime:
|
||||
def test_creates_git_client(self):
|
||||
rt = SystemRuntime()
|
||||
assert isinstance(rt.git, GitClient)
|
||||
assert rt.git.runner is rt.runner
|
||||
Reference in New Issue
Block a user