oxhq/drove-laravel

Narrow Laravel runtime integration for Drove.

Maintainers

Package info

github.com/oxhq/drove-laravel

Issues

pkg:composer/oxhq/drove-laravel

Transparency log

Statistics

Installs: 2

Dependents: 0

Suggesters: 0

Stars: 0

v0.4.0-alpha.1 2026-07-29 15:51 UTC

This package is auto-updated.

Last update: 2026-07-29 19:32:45 UTC


README

This package is a narrow Linux/macOS Laravel 13 bridge for Drove's prepared-process runtime. It adds:

  • a php artisan drove -- <arguments> subprocess command;
  • one root Laravel or Orchestra Testbench application and TestCase binding;
  • four lifecycle callbacks matching Drove dispatch boundaries;
  • one MySQL transaction adapter;
  • one inherited SQLite :memory: adapter; and
  • one physical file-copy adapter for a single SQLite database.

Install

The alpha depends on Drove's inherited Pest plugin loader, so clean consumers must explicitly trust that Composer plugin before installing:

composer config --no-plugins allow-plugins.pestphp/pest-plugin true
composer require --dev oxhq/drove-laravel:^0.4@alpha
vendor/bin/drove-install-native
vendor/bin/drove --version

Adapter selection is mandatory. Set DROVE_LARAVEL_STATE=transaction or DROVE_LARAVEL_STATE=sqlite-memory or DROVE_LARAVEL_STATE=sqlite-copy; there is no implicit database mode. The selected connection must be Laravel's default connection. The Artisan command launches the child process with APP_ENV=testing. Direct runtime callers must do the same: boot is rejected unless both the process environment and Laravel's booted application environment are testing, including when configuration is cached.

Runtime selection defaults to DROVE_LARAVEL_RUNTIME=auto: a root with bootstrap/app.php boots as an application, while a package root defers to the selected Orchestra Testbench case. Use application or testbench to force the mode. A Testbench package that ships bootstrap/app.php must opt into testbench.

The runtime entrypoint is:

use Drove\Laravel\LaravelRuntime;

$runtime = LaravelRuntime::boot($projectRoot);
$context = $runtime->scopeContext();

Core integration calls:

$runtime->assertPlanSupported($scopeIr);
$runtime->bindTestCase($testCase, $context); // no-op for non-Laravel cases
$runtime->beforeDispatch($context, $tasks);
$runtime->enterDescendant($context, $task);
$runtime->leaveDescendant($context, $task);
$runtime->afterDispatch($context, $tasks);

leaveDescendant() and afterDispatch() belong in finally paths. assertPlanSupported() belongs after Scope IR compilation and before lifecycle execution; unsupported topology is input rejection rather than a failed scope.

Each provider contributes a core ResourcePlan to an EnvironmentPlan. Resource plans identify database, filesystem, cache, queue, or object-storage resources and declare explicit Branchable, LeafIsolated, ScopeIsolated, RollbackIsolated, Resettable, and SharedReadOnly capabilities. The environment also declares whether a multi-resource lifecycle is atomic or best-effort.

The current Laravel environment has one managed database resource and declares best-effort coordination because filesystem, cache, queue, and object-storage are explicitly unmanaged. The SQLite providers are Branchable + ScopeIsolated; the transaction provider is LeafIsolated. Planning and runtime dispatch both reject sibling or mixed scopes when any managed resource lacks scope isolation. The four unmanaged resource kinds have no provider in this alpha.

Deliberate limits

The transaction adapter accepts one resolved MySQL connection in a disposable test database. Every existing table must use InnoDB; other engines are rejected before dispatch. It wraps test descendants in a transaction and rolls them back. Scope hooks are not transaction-isolated, a dispatch containing a scope must contain only that scope, and DDL, explicit or implicit commits, connection purges/reconnects, and raw PDO transactions are unsupported. Those operations can persist writes before Drove detects transaction-depth drift. Test cases using RefreshDatabase, LazilyRefreshDatabase, DatabaseMigrations, DatabaseTransactions, or DatabaseTruncation are rejected recursively before execution.

The SQLite adapter accepts one absolute, file-backed database in DELETE journal mode. It rejects in-memory or URI databases, attached databases, WAL/SHM/journal sidecars, open transactions, and already-resolved secondary connections. Descendant state uses verified PHP copy() output. It makes no reflink claim. RefreshDatabase migrates each private copy unless DROVE_LARAVEL_SQLITE_PREPARED_SCHEMA=true declares that the parent file is already migrated.

The sqlite-memory adapter accepts one literal :memory: database and keeps the prepared PDO open across POSIX forks. RefreshDatabase is accepted only with DROVE_LARAVEL_SQLITE_PREPARED_SCHEMA=true; without that explicit contract Drove rejects the suite instead of skipping migrations. Testbench support is limited to one application profile per selected suite. Testbench class/method attributes and suites mixing Testbench with Laravel application cases are rejected before execution.

Orchestra Testbench is optional for Laravel application consumers. Package suites are tested against orchestra/testbench:^11.1; Drove checks the reflected Testbench application/binding contract before it creates an application.

All configured databases must be disposable. If test code explicitly resolves and writes a secondary connection, Drove detects it only during descendant cleanup, after that external write may already have happened. This late detection applies to both SQLite modes and the transaction adapter.

For the built-in adapters, state passed to bootForSuite() or configured with the documented environment variables is trait-preflighted before Testbench calls createApplication(). Custom adapters and state configured only inside the Testbench application are necessarily validated after application boot; provider bootstrap must therefore be safe to run once in the parent.

Neither adapter manages queue workers, cache, Redis, HTTP clients, sockets, or other external resources. Multi-database applications are outside this slice.