Skip to content

Commit af8ffc5

Browse files
committed
fix: handle missing config file gracefully
The CLI previously crashed with FileNotFoundError when ~/.config/task-cli/config.yaml did not exist. Changes: - load_config() now creates the config directory and a default config file on first run - Added .gitignore for Python cache files - Added tests for missing config scenarios: - test_load_config_creates_default_when_missing - test_load_config_reads_existing_file - test_load_config_handles_missing_entire_config_dir All existing tests continue to pass.
1 parent 8d6fd68 commit af8ffc5

3 files changed

Lines changed: 74 additions & 3 deletions

File tree

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
__pycache__/
2+
.pytest_cache/
3+
*.pyc

task.py

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,29 @@
1010
from commands.done import mark_done
1111

1212

13+
DEFAULT_CONFIG = """# Default configuration for task CLI
14+
storage:
15+
format: json
16+
max_tasks: 1000
17+
display:
18+
color: true
19+
unicode: true
20+
"""
21+
22+
1323
def load_config():
14-
"""Load configuration from file."""
15-
config_path = Path.home() / ".config" / "task-cli" / "config.yaml"
16-
# NOTE: This will crash if config doesn't exist - known bug for bounty testing
24+
"""Load configuration from file.
25+
26+
Creates a default config if the file doesn't exist so the CLI
27+
never crashes on first run.
28+
"""
29+
config_dir = Path.home() / ".config" / "task-cli"
30+
config_path = config_dir / "config.yaml"
31+
32+
if not config_path.exists():
33+
config_dir.mkdir(parents=True, exist_ok=True)
34+
config_path.write_text(DEFAULT_CONFIG)
35+
1736
with open(config_path) as f:
1837
return f.read()
1938

test_task.py

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,13 @@
11
"""Basic tests for task CLI."""
22

33
import json
4+
import os
5+
import shutil
46
import pytest
57
from pathlib import Path
68
from commands.add import add_task, validate_description
79
from commands.done import validate_task_id
10+
from task import load_config, DEFAULT_CONFIG
811

912

1013
def test_validate_description():
@@ -28,3 +31,49 @@ def test_validate_task_id():
2831

2932
with pytest.raises(ValueError):
3033
validate_task_id(tasks, 99)
34+
35+
36+
def test_load_config_creates_default_when_missing(tmp_path, monkeypatch):
37+
"""Test that load_config creates a default config when file is missing."""
38+
fake_home = tmp_path / "home"
39+
fake_home.mkdir()
40+
monkeypatch.setattr(Path, "home", lambda: fake_home)
41+
42+
config_dir = fake_home / ".config" / "task-cli"
43+
config_path = config_dir / "config.yaml"
44+
45+
# Ensure it doesn't exist before
46+
assert not config_path.exists()
47+
48+
result = load_config()
49+
50+
# Should not crash and should return the default config
51+
assert result == DEFAULT_CONFIG
52+
# Should have created the file
53+
assert config_path.exists()
54+
assert "format: json" in config_path.read_text()
55+
56+
57+
def test_load_config_reads_existing_file(tmp_path, monkeypatch):
58+
"""Test that load_config reads an existing config file."""
59+
fake_home = tmp_path / "home"
60+
config_dir = fake_home / ".config" / "task-cli"
61+
config_dir.mkdir(parents=True)
62+
config_path = config_dir / "config.yaml"
63+
config_path.write_text("custom: true\n")
64+
65+
monkeypatch.setattr(Path, "home", lambda: fake_home)
66+
67+
result = load_config()
68+
assert "custom: true" in result
69+
70+
71+
def test_load_config_handles_missing_entire_config_dir(tmp_path, monkeypatch):
72+
"""Test that load_config works when the entire .config/task-cli dir is missing."""
73+
fake_home = tmp_path / "empty_home"
74+
fake_home.mkdir()
75+
monkeypatch.setattr(Path, "home", lambda: fake_home)
76+
77+
result = load_config()
78+
assert result == DEFAULT_CONFIG
79+
assert (fake_home / ".config" / "task-cli" / "config.yaml").exists()

0 commit comments

Comments
 (0)