PHPUnit in this project (no Composer)
This legacy project does not use Composer. To run tests, use the PHPUnit PHAR.
Quick start:
- Download phpunit.phar (pick a version compatible with your PHP):
bash
curl -L -o phpunit.phar https://phar.phpunit.de/phpunit-9.6.phar
chmod +x phpunit.phar
Alternatively, place phpunit.phar somewhere in your PATH.
- From the project root, run the tests:
bash
php phpunit.phar
# or
php phpunit.phar --configuration phpunit.xml.dist
Structure added:
- phpunit.xml.dist — PHPUnit configuration using tests/bootstrap.php.
- tests/bootstrap.php — sets up error reporting and an autoloader matching ./inc/<lowername>.inc.php.
- tests/ — test files live here. Initial tests added for IpUtil under tests/Inc/IpUtilTest.php.
Notes:
- The bootstrap avoids including index.php/api.php to prevent side effects; it only loads config.php if present and registers an autoloader for inc/.
- Tests target self-contained helpers first to build coverage without refactoring entangled code.
Testing real classes when stubs exist
Some tests load global stubs (e.g., Database, User) by default via a stub-first autoloader. To test a real class that also has a stub:
- Run the test in a separate PHP process so it starts clean and doesn’t inherit classes from prior tests:
- Add these annotations to the test class (or individual test methods):
@runTestsInSeparateProcesses
@preserveGlobalState disabled
- Before the first reference to the class, tell the stub autoloader to allowlist the real class:
php
$GLOBALS['__TEST_USE_REAL_CLASSES'] = ['User'];
Alternatively, set an environment variable when running PHPUnit:
bash
TEST_USE_REAL_CLASSES=User php phpunit.phar tests/Inc/UserTest.php
- Optionally force-load the class immediately afterwards:
php
require_once __DIR__ . '/../../inc/user.inc.php';
See tests/Inc/UserTest.php for a complete example.
Module testing (modules-available/*/page.inc.php)
This project’s modules define page classes (e.g., Page_SysLog) that normally run under index.php, which defines a global Page class and provides request/rendering infrastructure. For unit tests, a lightweight harness is provided via stubs under tests/Stubs:
Page — minimal base with preprocess() and render() calling the module’s doPreprocess()/doRender(). Also provides Page::getModule() and getIdentifier() as needed by some code.
Request — any(), get(), post() backed by PHP superglobals with simple type casting.
Render — records addTemplate() calls for assertions.
Permission — addGlobalTags() no-op that fills the provided array with the requested tags.
Paginate — captures the SQL and args, returns queued results in tests, and records the template/data passed to render().
- Existing stubs like
Database, User, Session, Message, Dictionary, and Property are reused.
How to write a module test:
- Reset stubs and superglobals in setUp().
- Make sure your test loads the module file directly:
php
require_once __DIR__ . '/../../modules-available/syslog/page.inc.php';
$page = new Page_SysLog('syslog');
$page->render();
- Drive inputs via $_GET, $_POST, and the User/Property stubs.
- Seed DB and pagination data by pushing into stub queues:
php
Database::$simpleQueryResponses[] = [ ['logtypeid' => 'x', 'counter' => 1] ];
Paginate::$queuedResults[] = [ ['dateline' => time(), 'logtypeid' => 'x', ...] ];
- Assert on Paginate::$lastExecArgs, Paginate::$lastRender, and Render::$templates.
See tests/Modules/SyslogPageTest.php for a complete example that covers:
- Permission denial path (no view permission)
- Filter/search handling and type list augmentation
- Location restriction joining via getAllowedLocations()
Database-backed tests without MySQL (SQLite test backend)
Global schema and seed data:
- The SQLite backend now auto-creates a minimal schema on first use and seeds a canonical dataset so tests can share consistent data without duplicating setup.
- Tables created include: location, subnet, machine, user, role, role_x_user, role_x_location, role_x_permission, clientlog, mail_queue, mail_config, audit.
- Seed content highlights:
- Locations: Campus (1) → Building A (2) → Room 101 (4); Building B (3); Offsite (5)
- Subnets: 10.0.0.0/24 @ 1, 10.0.0.128/26 @ 2, 192.168.1.0/24 @ 5
- Users/roles: alice(1), bob(2); roles Admin(1, builtin), Tech(2) with sample permissions
Utility helpers (optional):
- Database::resetSchema() — drop and recreate the in-memory DB with fresh schema and seed.
- Database::reseed() — reinsert the seed data into the existing schema.
- Database::truncateAll() — delete rows from all known tables.
- Database::pdo() — access the underlying PDO if you need custom SQL.
Parameter handling:
- Array parameters in IN (:list) and bulk inserts like VALUES :arg (legacy pattern) are expanded automatically.
There is a very thin translation layer that tries to convert MySQL-specific syntax into SQLite syntax. It might need improvements and extensions as more tests get added.