Going Further

Testing

You don't need a special test double from this package. The SDK is built on Laravel's own HTTP client, filesystem, and events, so you test an app that uses it with the tools you already know: Http::fake(), Storage::fake(), and the event helpers. There is no Parse::fake() to learn.

The examples below use Pest, Laravel's default test runner, but the assertions are plain framework calls and read the same under PHPUnit.

Faking the submission

Parse::file()->parse() makes one HTTP call to submit the document. Fake it with Http::fake() so no real request leaves your test, and back the file read with Storage::fake():

use Illuminate\Support\Facades\Http;
use Illuminate\Support\Facades\Storage;
use ParseForArtisans\Facades\Parse;
use ParseForArtisans\Models\ParseRequest;

it('submits a document for parsing', function () {
    Storage::fake();
    Storage::disk()->put('contracts/foo.pdf', '%PDF-1.4 fake');

    Http::fake([
        '*/api/v1/parse' => Http::response(['id' => 'parse-123', 'status' => 'pending'], 202),
    ]);

    $parse = Parse::file('contracts/foo.pdf')->parse();

    expect($parse)->toBeInstanceOf(ParseRequest::class)
        ->and($parse->status())->toBe('pending');

    Http::assertSent(fn ($request) => str_contains($request->url(), '/api/v1/parse'));
});

->parse() returns the local ParseRequest handle immediately; the result arrives later through an event (see below). Asserting the row exists and the request was sent is usually all you need for the submit path.


Testing your listeners

The parsed Markdown reaches your app through the ParseCompleted event, and ParseFailed on error. The most direct way to test what you do with a result is to build a ParseRequest, put the Markdown where ->markdown() will read it, and dispatch the event. Your listener then runs exactly as it does in production:

use Illuminate\Support\Facades\Storage;
use ParseForArtisans\Events\ParseCompleted;
use ParseForArtisans\Models\ParseRequest;

it('stores the parsed markdown when a parse completes', function () {
    Storage::fake();

    $parse = ParseRequest::create([
        'disk' => config('filesystems.default'),   // ->markdown() reads from this disk
        'source_path' => 'contracts/foo.pdf',
        'output_path' => 'parsed/contracts/foo.md',
        'status' => 'completed',
        'page_count' => 3,
    ]);

    Storage::disk($parse->disk)->put($parse->output_path, '# Parsed contract');

    ParseCompleted::dispatch($parse);

    // Assert your own side effects: a saved record, a dispatched job, a notification.
    expect($parse->markdown())->toBe('# Parsed contract');
});

Setting disk makes ->markdown() read from the faked disk, which keeps the test free of HTTP. In managed mode disk is null and ->markdown() fetches the body over the API, so fake the */api/v1/parse/*/markdown endpoint with Http::fake() instead.

Test the failure path the same way with ParseFailed:

use ParseForArtisans\Events\ParseFailed;

it('reports a failed parse', function () {
    $parse = ParseRequest::create([
        'source_path' => 'contracts/foo.pdf',
        'output_path' => 'parsed/contracts/foo.md',
        'status' => 'failed',
        'error' => 'unsupported_type',
    ]);

    ParseFailed::dispatch($parse);

    // Assert your handling, e.g. the document was flagged or someone was alerted.
    expect($parse->error)->toBe('unsupported_type');
});

Prefer to assert that an event fired rather than drive a listener? Call Event::fake() before your code runs and Event::assertDispatched(ParseCompleted::class) after. Faking events stops your real listeners from running, so pick one approach per test.


Asserting submission errors

Submission problems, such as a bad key, an unsupported type, or an exceeded quota, are synchronous: the SaaS rejects them and ->parse() throws ParseForArtisans\Exceptions\ParseException. Fake an error response and assert it:

use Illuminate\Support\Facades\Http;
use Illuminate\Support\Facades\Storage;
use ParseForArtisans\Facades\Parse;
use ParseForArtisans\Exceptions\ParseException;

it('throws on an unsupported file type', function () {
    Storage::fake();
    Storage::disk()->put('notes.rtf', 'plain');

    Http::fake([
        '*/api/v1/parse' => Http::response(['error' => ['type' => 'unsupported_type']], 422),
    ]);

    expect(fn () => Parse::file('notes.rtf')->parse())->toThrow(ParseException::class);
});

Parse-time problems are different: they arrive later as a ParseFailed event, not as a thrown exception, so test those with the listener pattern above. See Handling Results for the full split between the two error channels.