Skip to content

Commit 829bffa

Browse files
committed
docs: add testing plugins stub page
- BATS for bash script testing - PHPUnit for PHP testing - PHPStan/ShellCheck for static analysis - CI/CD integration examples - Testable code patterns
1 parent 9a5f0cb commit 829bffa

2 files changed

Lines changed: 254 additions & 0 deletions

File tree

docs/advanced/index.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ This section covers advanced plugin development topics for more complex integrat
1717
## Development & Debugging
1818

1919
- [Debugging Techniques]({% link docs/advanced/debugging-techniques.md %}) - Logging, error handling, dev tools
20+
- [Testing Plugins]({% link docs/advanced/testing.md %}) - Unit testing, static analysis, CI/CD
2021

2122
## Distribution
2223

docs/advanced/testing.md

Lines changed: 253 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,253 @@
1+
---
2+
layout: default
3+
title: Testing Plugins
4+
parent: Advanced Topics
5+
nav_order: 5
6+
---
7+
8+
# Testing Unraid® Plugins
9+
10+
{: .warning }
11+
> This page is a stub. [Help us expand it!](https://github.com/mstrhakr/plugin-docs/blob/main/CONTRIBUTING.md)
12+
13+
{: .note }
14+
> Unraid® is a registered trademark of Lime Technology, Inc. This documentation is not affiliated with Lime Technology, Inc.
15+
16+
## Overview
17+
18+
Testing Unraid® plugins presents unique challenges since plugins depend on Unraid-specific globals, functions, and system state that don't exist in a standard development environment. This guide covers strategies for testing both bash scripts and PHP code.
19+
20+
## The Challenge
21+
22+
Plugins rely on:
23+
- **Unraid globals**: `$var`, `$disks`, `$shares` arrays
24+
- **Helper functions**: `parse_plugin_cfg()`, `autov()`, `csrf_token()`
25+
- **System state**: Array status, Docker daemon, network configuration
26+
- **File paths**: `/usr/local/emhttp/`, `/boot/config/plugins/`
27+
28+
These dependencies make traditional unit testing difficult without a running Unraid system.
29+
30+
## Testing Strategies
31+
32+
### Static Analysis (Recommended Starting Point)
33+
34+
Static analysis catches bugs without executing code. These tools work in any development environment:
35+
36+
| Tool | Purpose | Language |
37+
|------|---------|----------|
38+
| **PHPStan** | Type checking, bug detection | PHP |
39+
| **PHP-CS-Fixer** | Code style/formatting | PHP |
40+
| **ShellCheck** | Bash linting and best practices | Bash |
41+
| **commitlint** | Commit message conventions | Any |
42+
43+
See the [Unraid Plugin Template](https://github.com/dkaser/unraid-plugin-template) for PHPStan/PHP-CS-Fixer configuration examples.
44+
45+
### Bash Script Testing with BATS
46+
47+
[BATS (Bash Automated Testing System)](https://github.com/bats-core/bats-core) enables unit testing for shell scripts.
48+
49+
```bash
50+
#!/usr/bin/env bats
51+
# test_compose.bats
52+
53+
setup() {
54+
# Create mock environment
55+
export MOCK_MODE=1
56+
source ./scripts/compose.sh
57+
}
58+
59+
@test "parse_stack_name extracts name from path" {
60+
result=$(parse_stack_name "/mnt/user/appdata/mystack/docker-compose.yml")
61+
[ "$result" = "mystack" ]
62+
}
63+
64+
@test "validate_compose_file detects missing file" {
65+
run validate_compose_file "/nonexistent/docker-compose.yml"
66+
[ "$status" -eq 1 ]
67+
}
68+
```
69+
70+
**Key techniques:**
71+
- Extract testable functions that don't depend on system state
72+
- Use environment variables to enable "mock mode"
73+
- Stub external commands (`docker`, `logger`, etc.)
74+
75+
### PHP Testing with PHPUnit
76+
77+
[PHPUnit](https://phpunit.de/) is the standard PHP testing framework.
78+
79+
```php
80+
<?php
81+
// tests/UtilTest.php
82+
use PHPUnit\Framework\TestCase;
83+
84+
class UtilTest extends TestCase
85+
{
86+
public function testParseConfig(): void
87+
{
88+
$config = "setting1=\"value1\"\nsetting2=\"value2\"";
89+
$result = parse_config_string($config);
90+
91+
$this->assertEquals('value1', $result['setting1']);
92+
$this->assertEquals('value2', $result['setting2']);
93+
}
94+
}
95+
```
96+
97+
**Mocking Unraid functions:**
98+
99+
```php
100+
<?php
101+
// tests/bootstrap.php - Mock Unraid functions
102+
103+
if (!function_exists('parse_plugin_cfg')) {
104+
function parse_plugin_cfg($plugin) {
105+
// Return mock config for testing
106+
return [
107+
'setting1' => 'test_value',
108+
'debug' => 'yes'
109+
];
110+
}
111+
}
112+
113+
if (!function_exists('autov')) {
114+
function autov($path) {
115+
return $path . '?v=test';
116+
}
117+
}
118+
```
119+
120+
### Integration Testing on Live Unraid
121+
122+
Some tests require a running Unraid system. Options include:
123+
124+
1. **Manual testing** - Install plugin, verify behavior
125+
2. **SSH-based test scripts** - Run validation scripts remotely
126+
3. **VM testing** - Unraid trial in VirtualBox/VMware
127+
4. **Docker-based simulation** - Limited, but useful for some scenarios
128+
129+
```bash
130+
# Example: Remote validation script
131+
ssh root@unraid-server "bash -s" < ./tests/integration/test_event_handlers.sh
132+
```
133+
134+
## CI/CD Integration
135+
136+
### GitHub Actions Example
137+
138+
```yaml
139+
name: Test & Lint
140+
141+
on: [push, pull_request]
142+
143+
jobs:
144+
shellcheck:
145+
runs-on: ubuntu-latest
146+
steps:
147+
- uses: actions/checkout@v4
148+
- name: Run ShellCheck
149+
uses: ludeeus/action-shellcheck@master
150+
with:
151+
scandir: './source'
152+
153+
phpstan:
154+
runs-on: ubuntu-latest
155+
steps:
156+
- uses: actions/checkout@v4
157+
- uses: php-actions/composer@v6
158+
- uses: php-actions/phpstan@v3
159+
160+
bats:
161+
runs-on: ubuntu-latest
162+
steps:
163+
- uses: actions/checkout@v4
164+
- name: Install BATS
165+
run: |
166+
git clone https://github.com/bats-core/bats-core.git
167+
cd bats-core && sudo ./install.sh /usr/local
168+
- name: Run tests
169+
run: bats tests/*.bats
170+
```
171+
172+
## Testable Code Patterns
173+
174+
### Extract Pure Functions
175+
176+
Move logic that doesn't depend on Unraid into separate, testable functions:
177+
178+
```php
179+
<?php
180+
// Before: Hard to test
181+
function get_stack_status($path) {
182+
global $var;
183+
$cfg = parse_plugin_cfg('compose.manager');
184+
// ... complex logic using $var and $cfg
185+
}
186+
187+
// After: Testable
188+
function calculate_status($containers, $expected_count, $config) {
189+
// Pure function - no globals, easy to test
190+
if (count($containers) === 0) return 'stopped';
191+
if (count($containers) < $expected_count) return 'partial';
192+
return 'running';
193+
}
194+
195+
function get_stack_status($path) {
196+
global $var;
197+
$cfg = parse_plugin_cfg('compose.manager');
198+
$containers = get_containers($path);
199+
return calculate_status($containers, $cfg['expected'], $cfg);
200+
}
201+
```
202+
203+
### Dependency Injection
204+
205+
```php
206+
<?php
207+
// Inject dependencies instead of using globals
208+
function process_stack($path, $config = null, $logger = null) {
209+
$config = $config ?? parse_plugin_cfg('compose.manager');
210+
$logger = $logger ?? 'logger';
211+
212+
// Now testable with mock config and logger
213+
}
214+
```
215+
216+
## Project Structure for Testing
217+
218+
```
219+
myplugin/
220+
├── source/
221+
│ └── usr/local/emhttp/plugins/myplugin/
222+
│ ├── *.page
223+
│ ├── php/
224+
│ │ └── util.php
225+
│ └── scripts/
226+
│ └── main.sh
227+
├── tests/
228+
│ ├── bootstrap.php # Mock Unraid functions
229+
│ ├── unit/
230+
│ │ ├── UtilTest.php
231+
│ │ └── main.bats
232+
│ └── integration/
233+
│ └── test_on_server.sh
234+
├── composer.json # PHPUnit, PHPStan
235+
├── phpstan.neon
236+
├── phpunit.xml
237+
└── .github/workflows/
238+
└── test.yml
239+
```
240+
241+
## Resources
242+
243+
- [BATS Core](https://github.com/bats-core/bats-core) - Bash testing framework
244+
- [PHPUnit](https://phpunit.de/) - PHP testing framework
245+
- [PHPStan](https://phpstan.org/) - PHP static analysis
246+
- [ShellCheck](https://www.shellcheck.net/) - Shell script analysis
247+
- [Unraid Plugin Template](https://github.com/dkaser/unraid-plugin-template) - PHPStan/linting setup
248+
249+
## Related Topics
250+
251+
- [Debugging Techniques]({% link docs/advanced/debugging-techniques.md %})
252+
- [Build and Packaging]({% link docs/build-and-packaging.md %})
253+

0 commit comments

Comments
 (0)