diff --git a/AGENTS.md b/AGENTS.md
index 2837d54c1..ec4dbf508 100644
--- a/AGENTS.md
+++ b/AGENTS.md
@@ -116,6 +116,26 @@ All `Events::trigger()` calls in core controllers pass a final `array $pluginDat
- Tests must pass before submitting changes (`composer test`)
- Minimum PHPUnit version: 10.5.16+. Default config: `phpunit.xml.dist`
+### Plugin Tests
+
+Each plugin may ship its own `Tests/` subdirectory. PHPUnit discovers them automatically via the `Plugins` testsuite in `phpunit.xml.dist`.
+
+**Convention:**
+```
+app/Plugins/{PluginName}/
+└── Tests/
+ └── {PluginName}Test.php # namespace App\Plugins\{PluginName}\Tests
+```
+
+**Base class:** Extend `App\Libraries\Plugins\PluginTestCase` (registers plugin namespaces in `setUp`). Pure-PHP tests may extend `CodeIgniter\Test\CIUnitTestCase` directly.
+
+```bash
+vendor/bin/phpunit --testsuite Plugins # plugins only
+vendor/bin/phpunit app/Plugins/CASPOSPlugin/Tests/ # single plugin
+```
+
+No changes to `composer.json` are needed — `App\Plugins\{Name}\Tests\*` resolves via the existing `App\` → `app/` PSR-4 mapping.
+
## Conventions
- Controllers → `app/Controllers/`
diff --git a/app/Plugins/README.md b/app/Plugins/README.md
index 1a323c460..cb7f094b0 100644
--- a/app/Plugins/README.md
+++ b/app/Plugins/README.md
@@ -478,6 +478,8 @@ app/Plugins/
│ └── ExampleModel.php
├── Traits/ # Shared PHP traits for plugin classes
│ └── ExampleTrait.php
+ ├── Tests/ # Plugin unit tests (optional)
+ │ └── ExamplePluginTest.php
├── Views/ # Plugin views
│ ├── config.php
│ └── dashboard.php
@@ -682,6 +684,7 @@ Settings are prefixed with the plugin ID (e.g., `example_api_key`) and stored in
| `app/Plugins/ExamplePlugin/Traits/ExampleTrait.php` | `App\Plugins\ExamplePlugin\Traits\ExampleTrait` |
| `app/Plugins/ExamplePlugin/Migrations/20260627120000_CreateExampleTable.php` | `App\Plugins\ExamplePlugin\Migrations\CreateExampleTable` |
| `app/Plugins/ExamplePlugin/Language/en/ExamplePlugin.php` | *(Language file - returns array, no namespace)* |
+| `app/Plugins/ExamplePlugin/Tests/ExamplePluginTest.php` | `App\Plugins\ExamplePlugin\Tests` |
## Database
@@ -775,6 +778,71 @@ Migration state is stored in `ospos_plugin_migrations`:
Each plugin tracks its version independently. The table is created by the core migration `20260627000000_PluginMigrationsTableCreate`.
+## Unit Tests
+
+Plugins can ship their own test suite inside a `Tests/` subdirectory. PHPUnit discovers them automatically — no separate `phpunit.xml` per plugin is needed or wanted.
+
+### Directory Structure
+
+```text
+app/Plugins/ExamplePlugin/
+└── Tests/
+ └── ExamplePluginTest.php # and any additional *Test.php files
+```
+
+### Namespace
+
+```
+App\Plugins\ExamplePlugin\Tests
+```
+
+This resolves via the existing `App\` → `app/` PSR-4 mapping in `composer.json` — no autoload changes needed.
+
+### Base Class
+
+Extend `App\Libraries\Plugins\PluginTestCase` instead of `CIUnitTestCase` directly. `PluginTestCase` calls `PluginManager::registerAllNamespaces()` in `setUp()` so plugin class namespaces resolve without the `pre_system` hook that normally fires during a real request. Tests that exercise only pure PHP logic (no plugin class loading) may extend `CIUnitTestCase` directly.
+
+### Example
+
+```php
+assertSame('Example', $plugin->getPluginId());
+ }
+
+ public function testGetVersion(): void
+ {
+ $plugin = new ExamplePlugin();
+ $this->assertSame('1.0.0', $plugin->getVersion());
+ }
+}
+```
+
+### Running Plugin Tests
+
+```bash
+# All suites (core + plugins)
+composer test
+
+# Plugins only
+vendor/bin/phpunit --testsuite Plugins
+
+# Single plugin
+vendor/bin/phpunit app/Plugins/ExamplePlugin/Tests/
+```
+
+> **Do not** place plugin tests under the root `tests/` directory — that breaks self-containment. Do not bundle a per-plugin `phpunit.xml` — the root `phpunit.xml.dist` already includes `./app/Plugins` in the `Plugins` testsuite.
+
## Event Flow
1. Application triggers event: `Events::trigger('sale_completed', $saleIdNum, $saleType, $pluginData)`
@@ -1012,6 +1080,7 @@ ExamplePlugin-1.0.0.zip
├── Libraries/
│ └── ApiClient.php
├── Models/
+ ├── Tests/
├── Traits/
├── Views/
├── ExamplePlugin.php
diff --git a/phpunit.xml.dist b/phpunit.xml.dist
index f9a5d9434..6462eb4c4 100644
--- a/phpunit.xml.dist
+++ b/phpunit.xml.dist
@@ -26,6 +26,9 @@
./tests
+
+ ./app/Plugins
+