diff --git a/.claude-plugin/marketplace.json b/.claude-plugin/marketplace.json index ccba5f3..d2912ae 100644 --- a/.claude-plugin/marketplace.json +++ b/.claude-plugin/marketplace.json @@ -8,7 +8,7 @@ "name": "indigo", "source": "./", "description": "Indigo home automation development toolkit \u2014 plugin development, API integration, and control page building", - "version": "1.9.0", + "version": "1.9.1", "repository": "https://github.com/simons-plugins/indigo-claude-plugin", "license": "MIT", "keywords": [ diff --git a/.claude-plugin/plugin.json b/.claude-plugin/plugin.json index 405903b..3a10393 100644 --- a/.claude-plugin/plugin.json +++ b/.claude-plugin/plugin.json @@ -1,6 +1,6 @@ { "name": "indigo", - "version": "1.9.0", + "version": "1.9.1", "description": "Indigo home automation development toolkit \u2014 plugin development, API integration, and control page building", "repository": "https://github.com/simons-plugins/indigo-claude-plugin" } diff --git a/commands/dev.md b/commands/dev.md index ad23a53..5319202 100644 --- a/commands/dev.md +++ b/commands/dev.md @@ -30,6 +30,7 @@ Expert assistant for Indigo home automation plugin development. Provides compreh | `docs/plugin-dev/troubleshooting/common-issues.md` | 11KB | Troubleshooting | | `docs/plugin-dev/patterns/api-patterns.md` | 5KB | Common API patterns | | `docs/plugin-dev/patterns/open-source-contributing.md` | 3KB | Contributing to IndigoDomotics open source | +| `docs/plugin-dev/patterns/testing.md` | 4KB | Testing — pytest mocks (Pattern A) and TestingBase live integration (Pattern B) | ### Modular IOM Reference (Load by Topic) @@ -70,6 +71,7 @@ These are complementary - load based on question type: | Subscriptions | `docs/plugin-dev/api/iom/subscriptions.md` | | Schedule/action group commands | `docs/plugin-dev/api/iom/command-namespaces.md` | | Open source contributing | `docs/plugin-dev/patterns/open-source-contributing.md` | +| Testing, pytest, mocking, TestingBase | `docs/plugin-dev/patterns/testing.md` | ### Specific Routing @@ -99,6 +101,9 @@ These are complementary - load based on question type: **"How do I contribute to open source?" / "Add plugin to IndigoDomotics"** 1. Read `docs/plugin-dev/patterns/open-source-contributing.md` +**"How do I test my plugin?" / "Write tests" / "TestingBase" / "pytest"** +1. Read `docs/plugin-dev/patterns/testing.md` + **"What device properties exist?"** 1. Read `docs/plugin-dev/api/iom/devices.md` @@ -143,7 +148,8 @@ docs/plugin-dev/ │ └── utilities.md # Helpers (4KB) ├── patterns/ │ ├── api-patterns.md # Common patterns (5KB) -│ └── open-source-contributing.md # IndigoDomotics contributing guide (3KB) +│ ├── open-source-contributing.md # IndigoDomotics contributing guide (3KB) +│ └── testing.md # pytest mocks + TestingBase (4KB) ├── examples/ │ └── sdk-examples-guide.md # Example catalog (8KB) └── troubleshooting/ diff --git a/docs/plugin-dev/patterns/README.md b/docs/plugin-dev/patterns/README.md index b407bb8..3fdf560 100644 --- a/docs/plugin-dev/patterns/README.md +++ b/docs/plugin-dev/patterns/README.md @@ -12,6 +12,11 @@ Core patterns for working with the Indigo Object Model: - Variable and action group patterns - Common anti-patterns to avoid +### [Testing](testing.md) +Two complementary testing patterns: +- Pattern A — `unittest.mock`-based unit tests (fast, no Indigo runtime, runs in CI) +- Pattern B — live integration tests with the upstream `TestingBase` git submodule (`APIBase` + `ValidateXmlFile` against a running server) + ## Coming Soon We're building additional implementation patterns including: diff --git a/docs/plugin-dev/patterns/testing.md b/docs/plugin-dev/patterns/testing.md new file mode 100644 index 0000000..7d1825c --- /dev/null +++ b/docs/plugin-dev/patterns/testing.md @@ -0,0 +1,134 @@ +# Testing Patterns + +How to test Indigo plugins. Two complementary patterns: + +- **Pattern A — Unit tests with mocks**: fast, no Indigo runtime required, runs in CI. +- **Pattern B — Live integration with TestingBase**: exercises a real running Indigo server, catches integration bugs and validates plugin XML. + +They are complementary, not alternatives. Run A often, B before a release. + +--- + +## Pattern A — Unit tests with a mocked Indigo runtime + +Mock `indigo` via `unittest.mock` and import your plugin module directly. Tests can then exercise business logic — API parsing, state computation, ConfigUI validation, scheduling — without an Indigo install. + +**When to use**: anything that doesn't depend on Indigo's database — most of your plugin's logic. + +**Reference shape**: + +```python +# tests/conftest.py +import sys +from pathlib import Path +from unittest.mock import Mock +import pytest + +SERVER_PLUGIN_DIR = ( + Path(__file__).parent.parent + / "MyPlugin.indigoPlugin" + / "Contents" + / "Server Plugin" +) +sys.path.insert(0, str(SERVER_PLUGIN_DIR)) + + +@pytest.fixture +def mock_logger(): + # Mock() auto-creates these attributes on first access — the explicit + # assignment is for readability so test failures point at named mocks. + logger = Mock() + for level in ("debug", "info", "warning", "error", "exception"): + setattr(logger, level, Mock()) + return logger +``` + +Tests then import plugin modules and inject `mock_logger` (and other fixtures) where the plugin would normally use `self.logger`. Run with `pytest` — no Indigo running, no credentials, no network. + +--- + +## Pattern B — Live integration with TestingBase + +Indigo ships [`TestingBase`](https://github.com/IndigoDomotics/TestingBase) as a shared git submodule. Tests subclass `APIBase` — an abstract `unittest.TestCase` — and exercise a running Indigo server's HTTP API. The companion `ValidateXmlFile` helper validates `Devices.xml`, `Actions.xml`, `Events.xml`, and `MenuItems.xml` against the Indigo schema. + +**When to use**: pre-release smoke tests; XML schema validation; end-to-end checks against your plugin's HTTP responder. + +**Setup**: + +```bash +# At the top level of your plugin repo +git submodule add https://github.com/IndigoDomotics/TestingBase.git tests/shared +git submodule update --init +``` + +**Layout** (the convention TestingBase expects): + +``` +my-plugin/ +├── tests/ +│ ├── shared/ # submodule — do NOT edit +│ ├── .env # gitignored — Indigo API credentials +│ ├── testing-requirements.txt # references shared/module-requirements.txt +│ ├── venv/ # gitignored — your test venv +│ └── test_my_plugin.py +``` + +`tests/.env` carries credentials — see `tests/shared/ENV_TEMPLATE` for the exact keys. `tests/testing-requirements.txt` should chain to the shared list: + +``` +-r shared/module-requirements.txt +# anything else your tests need +``` + +**Minimal `APIBase` test**: + +```python +# tests/test_my_plugin.py +from shared import APIBase + +class TestMyPlugin(APIBase): + def test_device_reachable(self): + device = self.get_indigo_object() + self.assertTrue(device["enabled"]) +``` + +**XML validation** — `ValidateXmlFile` MUST come first in the MRO. Resolve the path relative to the test file so the test runs on any machine: + +```python +import os +from shared import APIBase, ValidateXmlFile + +class TestActionsXml(ValidateXmlFile, APIBase): + server_plugin_dir_path = os.path.abspath( + os.path.join( + os.path.dirname(__file__), + "../MyPlugin.indigoPlugin/Contents/Server Plugin", + ) + ) + file_name = "Actions.xml" +``` + +**Maintenance**: `tests/shared` is a submodule. Pull updates with +`git submodule update --recursive --remote tests/shared` and never commit +local changes back to it — the upstream README is explicit about that. + +--- + +## Choosing between A and B + +| | Pattern A (mocks) | Pattern B (TestingBase) | +|---|---|---| +| Speed | Seconds | Slower — HTTP round-trips per assertion; helpers like `run_host_script` spawn an IPH3 process per call | +| Indigo install needed | No | Yes — running server + admin API access | +| What it catches | Logic errors | Integration + XML schema errors | +| Best for | CI on every PR | Pre-release smoke | + +Use both for any non-trivial plugin: A on every commit, B before each release. + +--- + +## References + +- TestingBase upstream: https://github.com/IndigoDomotics/TestingBase +- Plugin HTTP API (consumed by Pattern B): see `/indigo:api` +- Plugin lifecycle (what you'd typically test): see `concepts/plugin-lifecycle.md` diff --git a/reference/Python3-Migration-Guide.md b/reference/Python3-Migration-Guide.md index c7104ee..3e3a70b 100644 --- a/reference/Python3-Migration-Guide.md +++ b/reference/Python3-Migration-Guide.md @@ -124,3 +124,56 @@ - `dict.iteritems()` is deprecated, `dict.items()` works just as well in both python 2 & 3 (same for all the iter* functions on dict and list objects). - `pylint -py3k` may give you this error: `round built-in referenced (round-builtin)`. This warning can safely be ignored since it's primarily just a reminder that the [algorithm for calculating how an exact halfway cases](https://docs.python.org/3/whatsnew/3.0.html#builtins) has changed in python 3. + +--- + +## Indigo 2025.2 / Python 3.11 → 3.13 + +Indigo 2025.2 bumps the embedded interpreter from Python **3.11 to 3.13**. The 3.11→3.13 step is small compared to 2→3, and most plugin code needs no changes. Two categories warrant a quick sweep before upgrading. + +### Library-level breakages + +For specific third-party library and stdlib import errors observed under 3.13 (`telnetlib`, `websockets` v14+, `matplotlib`'s `legendHandles` and `plot_date`), see [common-issues.md → Upgrading to Indigo 2025.2 / Python 3.13](../docs/plugin-dev/troubleshooting/common-issues.md#upgrading-to-indigo-20252--python-313). Those are the issues seen in the wild; the rest of this section covers generic 3.13 deprecations to clean up while you're in the area. + +### Deprecations to clean up + +These don't fail under 3.13 yet but are slated for removal: + +- **`datetime.utcnow()`** — deprecated since 3.12, returns naive datetimes. + ```python + # before + ts = datetime.utcnow() + # after + from datetime import datetime, UTC + ts = datetime.now(UTC) + ``` + +- **`asyncio.get_event_loop()`** with no running loop emits a `DeprecationWarning` since 3.12 (cpython gh-100160) and will be removed in 3.14. Inside a coroutine, use `asyncio.get_running_loop()` instead. + ```python + # inside a coroutine + loop = asyncio.get_running_loop() + # creating a new loop explicitly + loop = asyncio.new_event_loop() + ``` + +- **`pkg_resources`** is deprecated. Prefer `importlib.metadata` for distribution introspection. + +### Removed stdlib (PEP 594 — completed in 3.13) + +If any of these appear in your imports, they will now fail at import time: + +`aifc`, `audioop`, `cgi`, `cgitb`, `chunk`, `crypt`, `imghdr`, `mailcap`, `msilib`, `nis`, `nntplib`, `ossaudiodev`, `pipes`, `sndhdr`, `spwd`, `sunau`, `telnetlib`, `uu`, `xdrlib`. + +For `telnetlib` specifically, the `telnetlib-313-and-up` PyPI shim restores the same import path — see the troubleshooting note linked above. + +`imp` (removed in 3.12) and `distutils` (removed in 3.12) are gone too if you've been carrying any pre-3.12 code. Switch to `importlib` and `setuptools`/`packaging`. + +### Vendored packages + +Pre-built packages in `Contents/Packages/` are tagged with the Python version that created them. Pure-Python wheels (`.py` only — no `.so` files) usually transport across versions. Anything with compiled extensions needs rebuilding for 3.13. The safer pattern is `requirements.txt` — Indigo 2025.2 will reinstall under 3.13 automatically and writes a version-keyed `3.13-pip-install-log-success.txt` marker. + +### Testing under 3.13 + +Run your existing test suite (Pattern A from [patterns/testing.md](../docs/plugin-dev/patterns/testing.md)) under 3.13 locally before installing 2025.2 on a live server. If you don't have a test suite, restart plugins one at a time after the upgrade and watch the Indigo Event Log for tracebacks — bump `PluginVersion` and ship a fix for any plugin that fails to start. + +To roll back to 2025.1: quit 2025.2 server and client, relaunch 2025.1. diff --git a/skills/dev/SKILL.md b/skills/dev/SKILL.md index 5c7435c..f620c72 100644 --- a/skills/dev/SKILL.md +++ b/skills/dev/SKILL.md @@ -122,6 +122,7 @@ For detailed guidance on specific topics, read these files relative to `${CLAUDE | Scripting shell & CLI | `docs/plugin-dev/concepts/scripting-shell.md` | | Plugin preferences & PluginConfig.xml | `docs/plugin-dev/concepts/plugin-preferences.md` | | API patterns (state updates, replaceOnServer) | `docs/plugin-dev/patterns/api-patterns.md` | +| Testing patterns (pytest mocks, TestingBase) | `docs/plugin-dev/patterns/testing.md` | | Troubleshooting | `docs/plugin-dev/troubleshooting/common-issues.md` | | SDK examples guide | `docs/plugin-dev/examples/sdk-examples-guide.md` | | Indigo Object Model overview | `docs/plugin-dev/api/indigo-object-model.md` |