Testing & Simulation

v1.0.0

Complete testing guide for SystemOne workflows in Laravel applications using Pest, PHPUnit, and HTTP mocking.

Testing & Simulation

One of the biggest pain points of AI in production applications is flaky, expensive tests. Traditional LLM tests often hit live endpoints, racking up API bills and introducing non-deterministic race conditions.

Obelaw SystemOne is designed for 100% deterministic, offline testing with zero token costs and sub-millisecond execution times.

flowchart TD
    subgraph TestingStrategies["SystemOne Deterministic Testing Matrix"]
        direction TB
        S1["Strategy 1: Http::fake()<br/>Simulate Cloudflare Workers AI responses<br/><b>Best for:</b> Integration &amp; Feature tests"]
        S2["Strategy 2: In-Memory Mock Driver<br/>SystemOne::extend('test_driver')<br/><b>Best for:</b> Fast unit tests &amp; service layer isolation"]
        S3["Strategy 3: Payload Schema Assertions<br/>Verify $request-&gt;toPayload() serialization<br/><b>Best for:</b> Dynamic primitive generation tests"]
    end

    Outcome["100% Deterministic &bull; Zero API Token Costs &bull; Sub-millisecond Execution"]
    TestingStrategies --> Outcome

Strategy 1: Faking Cloudflare HTTP Responses

Because obelaw/systemone-clef uses Laravel’s native Illuminate\Support\Facades\Http client, you can use Http::fake() to simulate any Cloudflare Workers AI response.

sequenceDiagram
    autonumber
    participant Test as Test Runner (Pest / PHPUnit)
    participant HttpFake as Laravel Http::fake()
    participant Service as Business Service
    participant Engine as SystemOne Clef Driver

    Test->>HttpFake: Define mock response for Workers AI endpoint
    Test->>Service: invoke analyzeOrder()
    Service->>Engine: SystemOne::state(...)->ask(...)->run()
    Engine->>HttpFake: POST to api.cloudflare.com/client/v4/...
    HttpFake-->>Engine: Mocked JSON { answers: { is_fraud: 0.985, ... } }
    Engine-->>Service: DecisionResponse instance
    Service-->>Test: Processed order status
    Test->>Test: expect($response->value('is_fraud'))->toBe(0.985)
    Test->>HttpFake: Http::assertSent(...) verifies request payload

Pest Example

use Illuminate\Support\Facades\Http;
use Obelaw\SystemOne\Facades\SystemOne;
use Obelaw\SystemOne\Primitives\Choice;
use Obelaw\SystemOne\Primitives\Noul;

it('evaluates fraud and routes payment without making live API calls', function () {
    // 1. Fake the Cloudflare Workers AI endpoint
    Http::fake([
        'https://api.cloudflare.com/client/v4/accounts/*/ai/run/*' => Http::response([
            'result' => [
                'answers' => [
                    'is_fraud' => [
                        'value' => 0.985,
                        'confidence' => 0.985,
                    ],
                    'action' => [
                        'value' => 'block',
                        'confidence' => 0.96,
                        'probabilities' => [
                            'allow' => 0.01,
                            'challenge' => 0.03,
                            'block' => 0.96,
                        ],
                    ],
                ],
            ],
            'success' => true,
            'errors' => [],
            'messages' => [],
        ], 200),
    ]);

    // 2. Execute decision request
    $response = SystemOne::state('High velocity transactions from VPN IP')
        ->ask('is_fraud', Noul::make('Is transaction fraudulent?'))
        ->ask('action', Choice::make('Action', ['allow', 'challenge', 'block']))
        ->run();

    // 3. Assert decision outcomes
    expect($response->value('is_fraud'))->toBe(0.985)
        ->and($response->isPositive('is_fraud', 0.90))->toBeTrue()
        ->and($response->value('action'))->toBe('block')
        ->and($response->confidence('action'))->toBe(0.96);

    // 4. Assert that the request sent correct payload structure
    Http::assertSent(function ($request) {
        $data = $request->data();

        return $data['model'] === 'clef'
            && $data['state'] === 'High velocity transactions from VPN IP'
            && isset($data['questions']['is_fraud'])
            && isset($data['questions']['action']);
    });
});

Strategy 2: In-Memory Mock Drivers

If you are unit testing application service layers and want to avoid HTTP mocking entirely, inject a test driver via SystemOne::extend():

flowchart LR
    UnitTest["Unit Test Suite"] --> Extend["SystemOne::extend('test_driver')<br/>Register anonymous DriverInterface class"]
    Extend --> InMem["In-Memory Driver Execution<br/>Instantly returns predefined DecisionResponse"]
    InMem --> Service["DiscountRecommendationService<br/>Evaluates business rules"]
    Service --> Assert["expect($cart-&gt;applied_coupon)-&gt;toBe('VIP20')<br/>(0ms, Zero Network, Zero Token Cost)"]
use Obelaw\SystemOne\Contracts\DecisionResponseInterface;
use Obelaw\SystemOne\Contracts\DriverInterface;
use Obelaw\SystemOne\DecisionRequest;
use Obelaw\SystemOne\DecisionResponse;
use Obelaw\SystemOne\Facades\SystemOne;

beforeEach(function () {
    SystemOne::extend('test_driver', function () {
        return new class implements DriverInterface {
            public function decide(DecisionRequest $request): DecisionResponseInterface
            {
                return new DecisionResponse([
                    'is_eligible' => ['value' => 1.0, 'confidence' => 1.0],
                    'discount_tier' => ['value' => 'vip_20', 'confidence' => 0.95],
                ]);
            }

            public function run(DecisionRequest $request): DecisionResponseInterface
            {
                return $this->decide($request);
            }
        };
    });
});

it('applies VIP discount when decision indicates eligibility', function () {
    $cart = createCartWithTotal(500);

    $service = new DiscountRecommendationService();
    $service->evaluate($cart, driver: 'test_driver');

    expect($cart->applied_coupon)->toBe('VIP20');
});

Strategy 3: Unit Testing Primitives Serialization

You can verify that your dynamic schemas are constructed properly before execution:

use Obelaw\SystemOne\Primitives\Choice;
use Obelaw\SystemOne\Primitives\Score;
use Obelaw\SystemOne\DecisionRequest;

it('correctly constructs complex decision payloads', function () {
    $request = (new DecisionRequest())
        ->state(['order_id' => 101])
        ->ask('category', Choice::make('Select category', [
            'books' => 'Physical or digital books',
            'electronics' => 'Hardware and gadgets',
        ]))
        ->ask('rating', Score::range('Quality score', 1, 5));

    $payload = $request->toPayload();

    expect($payload['questions']['category']['criteria'])->toHaveKeys(['books', 'electronics'])
        ->and($payload['questions']['rating']['min'])->toBe(1)
        ->and($payload['questions']['rating']['max'])->toBe(5);
});

Full Workflow Feature Test Example

Testing a complete business service from end to end:

namespace Tests\Feature;

use App\Models\Order;
use App\Services\OrderRiskService;
use Illuminate\Support\Facades\Http;
use Tests\TestCase;

class OrderRiskFeatureTest extends TestCase
{
    public function test_flagged_order_is_marked_for_manual_verification()
    {
        Http::fake([
            'https://api.cloudflare.com/client/v4/accounts/*/ai/run/*' => Http::response([
                'result' => [
                    'answers' => [
                        'flag_review' => ['value' => 0.92, 'confidence' => 0.92],
                    ],
                ],
                'success' => true,
            ]),
        ]);

        $order = Order::factory()->create([
            'total' => 9999.00,
            'is_reviewed' => false,
        ]);

        app(OrderRiskService::class)->analyze($order);

        $this->assertTrue($order->fresh()->requires_manual_review);
        $this->assertEquals(0.92, $order->fresh()->risk_probability);
    }
}

Next Steps

Our Premium Sponsors

Obelaw is proudly open-source. Continued development, bug fixes, and community support are made possible by the generosity of our sponsors.

Sponsor Obelaw