feat: small refactorings
Test and coverage / build (push) Failing after 56s

This commit is contained in:
2026-06-28 01:18:08 +03:00
parent 17aaa41101
commit 998665a4bf
13 changed files with 634 additions and 140 deletions
@@ -0,0 +1,131 @@
# Hardening CLI And Sync Implementation Plan
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
**Goal:** Make Gitea mirror fail reliably on API/git/config/path errors and expose a proper package CLI entrypoint.
**Architecture:** Keep the existing module layout, but move executable orchestration into an importable `src.cli` module. Preserve `gitea-mirror.py` as a compatibility wrapper while making `process_repo` return success/failure for aggregate exit handling.
**Tech Stack:** Python 3.12, requests, pydantic v2, pytest, ruff, uv.
---
### Task 1: API Errors Are Fatal
**Files:**
- Modify: `src/gitea_api.py`
- Test: `tests/test_gitea_api.py`
- [ ] **Step 1: Write failing tests**
```python
def test_get_repositories_raises_on_http_error(monkeypatch):
api = GiteaApi(endpoint="https://example.com", api_token="token")
session = FakeSession([FakeResponse(status_code=500, data=[])])
monkeypatch.setattr(requests, "session", lambda: session)
with pytest.raises(GiteaApiError):
api.get_repositories()
```
- [ ] **Step 2: Run test to verify it fails**
Run: `uv run pytest tests/test_gitea_api.py -v`
Expected: FAIL because `GiteaApiError` does not exist or is not raised.
- [ ] **Step 3: Implement minimal code**
Add `GiteaApiError`, request timeout, network exception handling, JSON/schema exception handling, and raise instead of returning `[]`.
- [ ] **Step 4: Run test to verify it passes**
Run: `uv run pytest tests/test_gitea_api.py -v`
Expected: PASS.
### Task 2: Git Failures Affect Exit Code
**Files:**
- Create: `src/cli.py`
- Modify: `gitea-mirror.py`
- Test: `tests/test_mirror_process.py`, `tests/test_cli.py`
- [ ] **Step 1: Write failing tests**
```python
def test_process_repo_returns_false_when_clone_fails(monkeypatch, tmp_path):
monkeypatch.setattr(cli, "git_clone", lambda **kwargs: False)
assert not cli.process_repo(config=make_config(tmp_path, FetchConfig(ssh_key="/tmp/key")), repo=make_repo())
```
- [ ] **Step 2: Run test to verify it fails**
Run: `uv run pytest tests/test_mirror_process.py tests/test_cli.py -v`
Expected: FAIL because `process_repo` currently returns `None` and no importable CLI module exists.
- [ ] **Step 3: Implement minimal code**
Move orchestration to `src.cli`, return booleans from `process_repo`, aggregate failures in `run`, and make `main` return non-zero for failures.
- [ ] **Step 4: Run test to verify it passes**
Run: `uv run pytest tests/test_mirror_process.py tests/test_cli.py -v`
Expected: PASS.
### Task 3: Validate Repository Paths And Config
**Files:**
- Modify: `src/models.py`
- Modify: `src/repository_name.py`
- Test: `tests/test_config.py`, `tests/test_repository_name.py`
- [ ] **Step 1: Write failing tests**
```python
def test_output_path_rejects_parent_traversal(tmp_path):
repo = make_repo()
with pytest.raises(ValueError):
get_repository_path(str(tmp_path), "../{name}", repo)
```
- [ ] **Step 2: Run test to verify it fails**
Run: `uv run pytest tests/test_config.py tests/test_repository_name.py -v`
Expected: FAIL because validation is missing.
- [ ] **Step 3: Implement minimal code**
Add repository format validation in `Config`, log level validation in `LoggingConfig`, and a safe output path helper that rejects absolute paths and paths escaping `out_dir`.
- [ ] **Step 4: Run test to verify it passes**
Run: `uv run pytest tests/test_config.py tests/test_repository_name.py -v`
Expected: PASS.
### Task 4: Package CLI Entrypoint
**Files:**
- Modify: `pyproject.toml`
- Modify: `gitea-mirror.py`
- Test: `tests/test_cli.py`
- [ ] **Step 1: Write failing test**
```python
def test_project_defines_console_script():
data = tomllib.loads(Path("pyproject.toml").read_text())
assert data["project"]["scripts"]["gitea-mirror"] == "src.cli:main"
```
- [ ] **Step 2: Run test to verify it fails**
Run: `uv run pytest tests/test_cli.py -v`
Expected: FAIL because `[project.scripts]` is absent.
- [ ] **Step 3: Implement minimal code**
Add `[project.scripts] gitea-mirror = "src.cli:main"` and make `gitea-mirror.py` delegate to `src.cli.main`.
- [ ] **Step 4: Run full verification**
Run: `make check`
Expected: PASS.