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
diskmakes->markdown()read from the faked disk, which keeps the test free of HTTP. In managed modediskis null and->markdown()fetches the body over the API, so fake the*/api/v1/parse/*/markdownendpoint withHttp::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 andEvent::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.