# Sprint 4b TDD Plan — Editor Power Tools

Companion to `claude-plan.md`. Defines tests to write BEFORE implementing each section.

**Testing Stack**: pytest + pytest-asyncio (auto mode), `AsyncMock` + `patch` for bridge tools, `tmp_path` + `monkeypatch` for filesystem tools.

## 3. Tool 1: `godotiq_scene_tree`

### Python Tests (`tests/test_bridge/test_scene_tree.py`)

```python
# Test: scene_tree with default params calls bridge.request("scene_tree", ...) with depth=3, default include list
# Test: scene_tree clamps depth below 1 to 1
# Test: scene_tree clamps depth above 10 to 10
# Test: scene_tree passes root param to bridge request
# Test: scene_tree passes filter_type param to bridge request
# Test: scene_tree passes custom include list to bridge request
# Test: scene_tree returns bridge response dict as-is
# Test: scene_tree raises on BridgeConnectionError
# Test: scene_tree raises on BridgeTimeoutError
```

## 4. Tool 2: `godotiq_node_ops`

### Python Tests (`tests/test_bridge/test_node_ops.py`)

```python
# Test: node_ops with valid operations calls bridge.request("node_ops", ...) with operations list
# Test: node_ops rejects empty operations list (raises ValueError or returns error)
# Test: node_ops enforces 50-operation limit (rejects list with 51+ operations)
# Test: node_ops returns bridge response dict as-is
# Test: node_ops raises on BridgeConnectionError
# Test: node_ops raises on BridgeTimeoutError
```

## 5. Tool 3: `godotiq_script_ops`

### Python Tests (`tests/test_bridge/test_script_ops.py`)

```python
# --- Path resolution ---
# Test: _resolve_path with res:// prefix resolves relative to GODOTIQ_PROJECT_ROOT
# Test: _resolve_path with relative path resolves relative to GODOTIQ_PROJECT_ROOT
# Test: _resolve_path with path traversal (../../etc/passwd) raises error (containment check)
# Test: _resolve_path with res://../.. traversal raises error

# --- Protected files ---
# Test: _is_protected returns True for project.godot
# Test: _is_protected returns True for paths inside .godot/
# Test: _is_protected returns True for .import files
# Test: _is_protected returns True for paths inside saves/
# Test: _is_protected returns False for regular .gd files

# --- Read mode ---
# Test: read mode returns file content, line count, and size
# Test: read mode raises error for missing file

# --- Write mode ---
# Test: write mode creates/overwrites file with content
# Test: write mode rejects protected files
# Test: write mode with validate_before_write=True runs GDScript validation
# Test: write mode with dry_run=True validates but doesn't write
# Test: write mode path traversal blocked

# --- Patch mode ---
# Test: patch mode applies single find-and-replace
# Test: patch mode applies multiple patches sequentially
# Test: patch mode rejects non-unique search string (appears 0 or 2+ times)
# Test: patch mode returns line numbers calculated BEFORE replacement
# Test: patch mode rejects protected files
# Test: patch mode with empty patches list returns unchanged file

# --- Create mode ---
# Test: create mode creates new file
# Test: create mode fails if file already exists
# Test: create mode rejects protected paths

# --- Validation ---
# Test: _validate_gdscript checks for type hints (basic convention check)
```

## 6. Tool 4: `godotiq_file_ops`

### Python Tests (`tests/test_bridge/test_file_ops.py`)

```python
# --- Path resolution & containment ---
# Test: _resolve with res:// resolves relative to GODOTIQ_PROJECT_ROOT
# Test: _resolve with relative path resolves correctly
# Test: _resolve with path traversal raises error

# --- Protected files ---
# Test: _is_protected matches project.godot, .godot/, *.import, saves/

# --- List operation ---
# Test: list returns directory entries
# Test: list with filter="scripts" returns only .gd files
# Test: list with recursive=True includes subdirectories
# Test: list excludes .godot, .git, __pycache__ directories
# Test: list on missing directory raises error

# --- Read operation ---
# Test: read returns text file content
# Test: read rejects binary files (raises error)
# Test: read on missing file raises error

# --- Write operation ---
# Test: write creates file with content
# Test: write rejects protected files
# Test: write path traversal blocked

# --- Move operation ---
# Test: move renames file successfully
# Test: move rejects protected source file
# Test: move rejects protected destination path
# Test: move path traversal blocked on both source and destination

# --- Delete operation ---
# Test: delete removes file
# Test: delete rejects protected files
# Test: delete path traversal blocked

# --- Search operation ---
# Test: search finds matching text across files
# Test: search returns context lines around matches
# Test: search with filter limits to specific file types
# Test: search excludes .godot, .git directories
# Test: search caps at 50 matches

# --- Tree operation ---
# Test: tree returns directory structure with depth control
# Test: tree with show_sizes=True includes file sizes
# Test: tree excludes hidden/system directories

# --- Type filters ---
# Test: _matches_type_filter correctly matches scene extensions (.tscn, .scn)
# Test: _matches_type_filter correctly matches script extensions (.gd)
# Test: _matches_type_filter with "all" matches everything
```

## 7. Tool Registration Changes

### Registration Tests (in existing bridge test files or `__init__.py` tests)

```python
# Test: scene_tree MCP wrapper catches BridgeConnectionError and returns user-friendly message
# Test: node_ops MCP wrapper catches BridgeConnectionError and returns user-friendly message
# Test: script_ops MCP wrapper catches generic Exception and returns error message
# Test: file_ops MCP wrapper catches generic Exception and returns error message
```

## 8. GDScript Handlers (Manual Testing Only)

GDScript handlers are tested manually via live bridge connection. No automated Python tests for GDScript code. However, the Python bridge tool tests verify correct parameter forwarding and response handling, providing indirect coverage.
