# GodotIQ v0.1.4 TDD Plan

Testing framework: pytest + pytest-asyncio (`asyncio_mode = "auto"`).
Test location: `tests/` directory. Bridge tools tested with mock bridge responses.
Existing patterns: `conftest.py` fixtures for `session` and `sample_root`.

---

## Section 1: BUILD_SCENE — Grid Offset

### Tests (in `tests/test_tools/test_build_scene.py`)

```python
# Test: validate_build_params accepts grid with valid offset [10, 0, 5]
# Test: validate_build_params accepts grid with no offset (backward compat)
# Test: validate_build_params rejects offset with wrong length [1, 2]
# Test: validate_build_params rejects offset with non-numeric values ["a", 0, 0]
# Test: build_scene passes offset through to bridge request params
# Test: build_scene defaults offset to [0, 0, 0] when not provided
```

---

## Section 2: BUILD_SCENE — Unique Names

### Tests (in `tests/test_tools/test_build_scene.py`)

```python
# Test: scene filename extraction — "res://assets/floor.tscn" → "floor"
# Test: scene filename extraction — "res://models/wall.glb" → "wall"
# Test: scene filename extraction — edge case with no extension
# (GDScript-side naming is tested via integration; Python side only validates params)
```

---

## Section 3: BUILD_SCENE — Parent Path Stripping

### Tests (GDScript-side behavior, tested via mock bridge response)

```python
# Test: build_scene with parent "DungeonCrawler/Room1" when root is "DungeonCrawler" → resolves
# Test: build_scene with parent "Room1" (already correct) → no stripping needed
# Test: build_scene with parent "OtherRoot/Room1" → PARENT_NOT_FOUND with hint message
# Test: error message includes "root node is '{name}'" hint text
```

---

## Section 4: CHECK_ERRORS — False Positive Filtering

### Tests (in `tests/test_tools/test_check_errors.py` — new file)

```python
# Test: error at line 0 gets confidence "low"
# Test: error at line 15 gets confidence "high"
# Test: error with missing line key gets confidence "low"
# Test: mixed errors → note present when any line-0 error exists
# Test: all errors line > 0 → no note added
# Test: empty errors array → no crash, no note
```

---

## Section 5: EXEC — Error Detail Extraction

### Tests (in `tests/test_tools/test_exec_code.py` — new file)

```python
# Test: bridge response with structured errors → error_detail formatted as "line N: message"
# Test: bridge response with old-style error string → no crash, no error_detail
# Test: bridge response with multiple errors → first error used for error_detail
# Test: error_detail added alongside existing error field (not replacing)
```

---

## Section 6: EXEC — Await Warning

### Tests (in `tests/test_tools/test_exec_code.py`)

```python
# Test: TOOL_DESCRIPTION contains "WARNING:" and "await" and "timeout"
# Test: exec_code docstring contains await warning text
```

---

## Section 7: ASSET_REGISTRY — Fuzzy Match Suggestions

### Tests (in `tests/test_tools/test_asset_registry.py`)

```python
# Test: path_filter with 0 matches → suggestions list returned
# Test: suggestions contain only directories with asset files
# Test: suggestions sorted alphabetically
# Test: suggestions capped at 20
# Test: path_filter with matches → no suggestions key
# Test: empty project → empty suggestions list
```

---

## Section 8: SCENE_MAP — Disk Warning

### Tests (in `tests/test_tools/test_scene_map.py` — new file or extend existing)

```python
# Test: scene_map response contains "note" key
# Test: note text mentions "saved .tscn file" and "godotiq_save_scene"
```

---

## Section 9: SAVE_SCENE — Enhanced Feedback

### Tests (in `tests/test_tools/test_save_scene.py` — new file)

```python
# Test: bridge response with file_size_kb and node_count passes through
# Test: file_size_kb is numeric
# Test: node_count is positive integer
# Test: backward compat — response without new fields still works
```

---

## Section 10: SCREENSHOT — Crop Region

### Tests (in `tests/test_tools/test_screenshot.py` — new file)

```python
# Test: screenshot with valid region [0, 0, 100, 100] passes to bridge
# Test: screenshot with no region → backward-compatible, no region in params
# Test: invalid region [0, 0, -1, 100] → validation error
# Test: invalid region [0, 0] (wrong length) → validation error
# Test: region with non-integer values → validation error or coerce
```

---

## Section 11: INPUT — Mouse Motion

### Tests (in `tests/test_tools/test_input_sim.py` — new file)

```python
# Test: command with mouse_motion passes to bridge
# Test: mouse_motion with relative_x and relative_y → valid
# Test: mouse_motion missing relative_x → validation error
# Test: mixed commands [action, mouse_motion, wait] → sequential in bridge params
# Test: TOOL_DESCRIPTION or docstring mentions mouse_motion
```

---

## Section 12: NODE_OPS — Rename and Get Property

### Tests (in `tests/test_tools/test_node_ops.py` — extend existing)

```python
# Test: rename operation in ops list passes to bridge
# Test: rename requires node and new_name
# Test: rename with empty new_name → error
# Test: get_property operation passes to bridge
# Test: get_property requires node and property
# Test: get_property with empty property → error
# Test: node_ops docstring lists rename and get_property
```

---

## Section 13: PACKAGING — Version Sync

### Tests (in `tests/test_version_sync.py` — new file)

```python
# Test: sync_version reads version from pyproject.toml correctly
# Test: sync_version updates plugin.cfg version line
# Test: sync_version updates godotiq_server.gd ADDON_VERSION line
# Test: sync_version with version "0.1.4" → all files contain "0.1.4"
```

---

## Section 14: LICENSE — Dev Key Hashing

### Tests (in `tests/test_license.py`)

```python
# Test: correct GODOTIQ_DEV_KEY → is_pro() returns True
# Test: wrong GODOTIQ_DEV_KEY → is_pro() returns False
# Test: GODOTIQ_LICENSE=pro (old bypass) → is_pro() returns False
# Test: no env vars → is_pro() returns False
# Test: _DEV_KEY_HASH constant is a valid 64-char hex string
```

---

## Section 15: CLI — Version Warning Suppression

### Tests (in `tests/test_cli.py` — new file)

```python
# Test: --version flag → no warning text in stderr
# Test: --version flag → version string in stdout
# Test: normal invocation (no --version) → warnings not suppressed
```
