# Section 4: Tests

## Overview

This section creates `tests/test_tools/test_build_scene.py` containing unit tests for the `validate_build_params` function from section 01, plus integration checks for tool registration and backward compatibility. All 13 validation tests run without a bridge connection since they exercise pure Python logic.

## Dependencies

- **Section 01 (python-tool)** must be complete. `validate_build_params` must exist at `from godotiq.tools.bridge.build_scene import validate_build_params`.
- The `@mcp.tool()` registration in `__init__.py` must be in place for the registration test.
- The `"build_scene": 30.0` timeout in `protocol.py` must be present.

## Testing Strategy

Bridge tools cannot be unit-tested without a running Godot addon. `validate_build_params` was extracted as a pure function for testability. The project uses pytest with `asyncio_mode = "auto"` and class-based grouping. No fixtures from `conftest.py` are needed.

## File to Create

### `tests/test_tools/test_build_scene.py`

Three test classes: `TestValidateBuildParams` (13 tests), `TestBuildSceneRegistration` (2 tests), `TestBuildSceneTimeout` (1 test).

### Class: TestValidateBuildParams

Import: `from godotiq.tools.bridge.build_scene import validate_build_params`

Each test calls `validate_build_params(grid=..., line=..., scatter=..., nodes=...)` and asserts the returned tuple `(params_dict, error_dict_or_None)`.

Convention: Success = `(params_dict, None)`. Failure = `(None, error_dict)` where error_dict has `"error"` key.

**Tests:**

1. **test_mode_exclusivity_none** — All None → error about specifying exactly one mode
2. **test_mode_exclusivity_multiple** — grid + scatter → error about multiple modes
3. **test_node_count_limit_grid** — rows=17, cols=16 (272) → error mentioning 256
4. **test_node_count_limit_scatter** — 257 items → error about 256
5. **test_valid_grid_params** — rows=3, cols=3, spacing=2.0 → ({"grid": ...}, None)
6. **test_valid_scatter_params** — 3 items → ({"scatter": ...}, None)
7. **test_valid_nodes_params** — 4 items → ({"nodes": ...}, None)
8. **test_grid_missing_rows** — no rows key → error
9. **test_grid_zero_cols** — cols=0 → error
10. **test_line_insufficient_points** — 1 point → error (needs >= 2)
11. **test_scatter_missing_items** — no items key → error
12. **test_scatter_empty_items** — empty items → error
13. **test_single_node_grid** — 1x1 → passes (count=1)

### Class: TestBuildSceneRegistration

1. **test_build_scene_tool_registered** — async test. Import `mcp` from `godotiq.server`, call `await mcp.list_tools()`, assert `"godotiq_build_scene"` in tool names.

2. **test_build_scene_signature** — Use `inspect.signature` on the wrapper function to verify expected parameters: `target_scene`, `parent`, `detail`, `grid`, `line`, `scatter`, `nodes`, `ctx`.

### Class: TestBuildSceneTimeout

1. **test_build_scene_timeout_configured** — Import `TOOL_TIMEOUTS` from `godotiq.bridge.protocol`. Assert `"build_scene"` key exists with value `30.0`.

### Test File Skeleton

```python
"""Tests for godotiq_build_scene tool — validation, registration, and config."""

from __future__ import annotations

import inspect

import pytest

from godotiq.tools.bridge.build_scene import validate_build_params


class TestValidateBuildParams:
    """Unit tests for validate_build_params — no bridge connection needed."""

    def test_mode_exclusivity_none(self) -> None:
        """Passing zero modes returns an error."""

    def test_mode_exclusivity_multiple(self) -> None:
        """Passing two modes (grid + scatter) returns an error."""

    def test_node_count_limit_grid(self) -> None:
        """Grid 17x16 = 272 nodes exceeds 256 limit."""

    def test_node_count_limit_scatter(self) -> None:
        """Scatter with 257 items exceeds 256 limit."""

    def test_valid_grid_params(self) -> None:
        """Grid 3x3 with spacing passes validation."""

    def test_valid_scatter_params(self) -> None:
        """Scatter with 3 items passes validation."""

    def test_valid_nodes_params(self) -> None:
        """Nodes list with 4 items passes validation."""

    def test_grid_missing_rows(self) -> None:
        """Grid without rows key returns error."""

    def test_grid_zero_cols(self) -> None:
        """Grid with cols=0 returns error."""

    def test_line_insufficient_points(self) -> None:
        """Line with only 1 point returns error (needs >= 2)."""

    def test_scatter_missing_items(self) -> None:
        """Scatter without items key returns error."""

    def test_scatter_empty_items(self) -> None:
        """Scatter with empty items list returns error."""

    def test_single_node_grid(self) -> None:
        """Grid 1x1 passes validation (count = 1)."""


class TestBuildSceneRegistration:
    """Verify godotiq_build_scene is registered as an MCP tool."""

    @pytest.mark.asyncio
    async def test_build_scene_tool_registered(self) -> None:
        """godotiq_build_scene appears in mcp.list_tools()."""

    def test_build_scene_signature(self) -> None:
        """Wrapper function has expected parameters."""


class TestBuildSceneTimeout:
    """Verify protocol timeout configuration."""

    def test_build_scene_timeout_configured(self) -> None:
        """build_scene has 30s timeout in TOOL_TIMEOUTS."""
```

## Backward Compatibility

Run `pytest tests/ -v`. All existing tests must pass. Only additive changes were made (new import, new tool function, new dict entry).

## Implementation Notes

- `validate_build_params` returns `tuple[dict, dict | None]`. Success: `(params_dict, None)`. Failure: `(None, error_dict)`.
- For error cases, assert error dict has `"error"` key with non-empty string. Don't test exact wording.
- For success cases, assert params dict contains mode key matching input.
- No conftest fixtures needed — all inputs constructed inline.

## Implementation Notes (Post-Build)

**Actual file modified:**
- `tests/test_tools/test_build_scene.py` — Extended (already existed from section-01)

**Deviation from plan:**
- Section-01 already created the test file with 18 validation tests (13 planned + 3 from code review + 2 valid line tests). Section-04 added the 3 integration tests (registration, signature, timeout) to the existing file rather than creating a new file.
- Total test count: 21 (18 from section-01 + 3 from section-04)

**Full test suite: 965 tests, all passing — no backward compatibility issues.**
