Documentation Index
Fetch the complete documentation index at: https://vu-ddaf4ff3.mintlify.app/llms.txt
Use this file to discover all available pages before exploring further.
Introduction
Prompt Deck is designed to be easily testable. This guide covers strategies for testing prompts, commands, tracking, and AI SDK integration in your Laravel application.Setting up tests
Test base class
If you’re using Orchestra Testbench (recommended for package testing), register the service provider in your test case:<?php
namespace Tests;
use Orchestra\Testbench\TestCase as BaseTestCase;
use Veeqtoh\PromptDeck\Providers\PromptDeckServiceProvider;
abstract class TestCase extends BaseTestCase
{
protected function getPackageProviders($app): array
{
return [
PromptDeckServiceProvider::class,
];
}
protected function getPackageAliases($app): array
{
return [
'PromptDeck' => \Veeqtoh\PromptDeck\Facades\PromptDeck::class,
];
}
}
Disabling cache and tracking
For most tests, disable caching and tracking to avoid side effects:protected function defineEnvironment($app): void
{
$app['config']->set('prompt-deck.cache.enabled', false);
$app['config']->set('prompt-deck.tracking.enabled', false);
$app['config']->set('prompt-deck.path', $this->getFixturePath('prompts'));
}
protected function getFixturePath(string $path = ''): string
{
return __DIR__ . '/fixtures/' . $path;
}
Testing prompts
Creating test prompt files
Create fixture prompt files in your test directory:tests/
└── fixtures/
└── prompts/
└── order-summary/
├── v1/
│ ├── system.md
│ ├── user.md
│ └── metadata.json
└── metadata.json
tests/fixtures/prompts/order-summary/v1/system.md:
You are a {{ $tone }} AI assistant specialized in order summaries.
tests/fixtures/prompts/order-summary/v1/user.md:
Summarise this order: {{ $input }}
Testing prompt loading
use Veeqtoh\PromptDeck\Facades\PromptDeck;
it('loads a prompt by name', function () {
$prompt = PromptDeck::get('order-summary', 1);
expect($prompt->name())->toBe('order-summary');
expect($prompt->version())->toBe(1);
});
it('throws when prompt does not exist', function () {
PromptDeck::get('non-existent');
})->throws(\Veeqtoh\PromptDeck\Exceptions\PromptNotFoundException::class);
it('throws when version does not exist', function () {
PromptDeck::get('order-summary', 999);
})->throws(\Veeqtoh\PromptDeck\Exceptions\InvalidVersionException::class);
Testing variable interpolation
it('interpolates variables in prompt content', function () {
$prompt = PromptDeck::get('order-summary', 1);
$content = $prompt->system(['tone' => 'friendly']);
expect($content)->toContain('friendly');
expect($content)->not->toContain('{{ $tone }}');
});
it('leaves unmatched placeholders intact', function () {
$prompt = PromptDeck::get('order-summary', 1);
$content = $prompt->system([]);
expect($content)->toContain('{{ $tone }}');
});
Testing roles
it('lists available roles', function () {
$prompt = PromptDeck::get('order-summary', 1);
expect($prompt->roles())->toContain('system', 'user');
});
it('checks if a role exists', function () {
$prompt = PromptDeck::get('order-summary', 1);
expect($prompt->has('system'))->toBeTrue();
expect($prompt->has('nonexistent'))->toBeFalse();
});
it('returns empty string for missing roles', function () {
$prompt = PromptDeck::get('order-summary', 1);
expect($prompt->role('nonexistent'))->toBe('');
});
it('gets raw content without interpolation', function () {
$prompt = PromptDeck::get('order-summary', 1);
$raw = $prompt->raw('system');
expect($raw)->toContain('{{ $tone }}');
});
Testing messages output
it('builds messages array for AI APIs', function () {
$prompt = PromptDeck::get('order-summary', 1);
$messages = $prompt->toMessages(['tone' => 'friendly', 'input' => 'Order #123']);
expect($messages)->toBeArray();
expect($messages[0])->toHaveKeys(['role', 'content']);
expect($messages[0]['role'])->toBe('system');
});
it('filters messages to specific roles', function () {
$prompt = PromptDeck::get('order-summary', 1);
$messages = $prompt->toMessages([], ['system']);
expect($messages)->toHaveCount(1);
expect($messages[0]['role'])->toBe('system');
});
Testing with the facade
You can mock the facade in tests where you don’t want filesystem access:use Veeqtoh\PromptDeck\Facades\PromptDeck;
use Veeqtoh\PromptDeck\PromptTemplate;
it('uses a mocked prompt', function () {
PromptDeck::shouldReceive('get')
->with('order-summary', null)
->andReturn(new PromptTemplate(
'order-summary',
1,
['system' => 'You are a helpful assistant.'],
['description' => 'Test prompt']
));
$prompt = PromptDeck::get('order-summary');
expect($prompt->system())->toBe('You are a helpful assistant.');
});
Testing version management
it('lists all versions for a prompt', function () {
$versions = PromptDeck::versions('order-summary');
expect($versions)->toBeArray();
expect($versions[0])->toHaveKey('version');
});
it('activates a specific version', function () {
// With tracking disabled, this writes to metadata.json
$result = PromptDeck::activate('order-summary', 1);
expect($result)->toBeTrue();
});
Testing execution tracking
When testing tracking, enable it and run the migrations in your test setup:protected function defineEnvironment($app): void
{
$app['config']->set('prompt-deck.tracking.enabled', true);
$app['config']->set('database.default', 'testing');
}
protected function defineDatabaseMigrations(): void
{
$this->loadMigrationsFrom(__DIR__ . '/../src/database/migrations');
}
use Veeqtoh\PromptDeck\Models\PromptExecution;
it('tracks prompt executions', function () {
PromptDeck::track('order-summary', 1, [
'input' => ['message' => 'test'],
'output' => 'response',
'tokens' => 100,
]);
expect(PromptExecution::count())->toBe(1);
expect(PromptExecution::first()->prompt_name)->toBe('order-summary');
expect(PromptExecution::first()->tokens)->toBe(100);
});
it('does not track when disabled', function () {
config(['prompt-deck.tracking.enabled' => false]);
PromptDeck::track('order-summary', 1, ['tokens' => 50]);
expect(PromptExecution::count())->toBe(0);
});
Using factories
Use the included factories to seed test data:use Veeqtoh\PromptDeck\Models\PromptVersion;
use Veeqtoh\PromptDeck\Models\PromptExecution;
it('queries execution data', function () {
PromptExecution::factory()
->forPrompt('order-summary', 2)
->count(10)
->create();
$avg = PromptExecution::where('prompt_name', 'order-summary')
->avg('latency_ms');
expect($avg)->toBeGreaterThan(0);
});
it('finds the active version', function () {
PromptVersion::factory()->named('order-summary')->version(1)->create();
PromptVersion::factory()->named('order-summary')->version(2)->active()->create();
$active = PromptVersion::where('name', 'order-summary')
->where('is_active', true)
->first();
expect($active->version)->toBe(2);
});
Testing Artisan commands
use Illuminate\Support\Facades\File;
it('creates a prompt via make:prompt', function () {
$this->artisan('make:prompt', ['name' => 'test-prompt'])
->assertSuccessful();
$promptPath = config('prompt-deck.path') . '/test-prompt/v1';
expect(File::isDirectory($promptPath))->toBeTrue();
expect(File::exists($promptPath . '/system.md'))->toBeTrue();
});
it('lists prompts via prompt:list', function () {
// Create a prompt first
$this->artisan('make:prompt', ['name' => 'list-test']);
$this->artisan('prompt:list')
->assertSuccessful();
});
it('activates a version via prompt:activate', function () {
$this->artisan('make:prompt', ['name' => 'activate-test']);
$this->artisan('prompt:activate', [
'name' => 'activate-test',
'version' => 1,
])->assertSuccessful();
});
it('tests a prompt via prompt:test', function () {
$this->artisan('make:prompt', ['name' => 'render-test']);
$this->artisan('prompt:test', [
'name' => 'render-test',
])->assertSuccessful();
});
it('shows diff between versions', function () {
$this->artisan('make:prompt', ['name' => 'diff-test']);
$this->artisan('prompt:diff', [
'name' => 'diff-test',
'--v1' => 1,
'--v2' => 1, // Compare with self for basic test
])->assertSuccessful();
});
Testing AI SDK integration
Testing HasPromptTemplate
Create a test agent class that uses the trait:use Veeqtoh\PromptDeck\Concerns\HasPromptTemplate;
class TestAgent
{
use HasPromptTemplate;
}
it('derives prompt name from class name', function () {
$agent = new TestAgent;
expect($agent->promptName())->toBe('test-agent');
});
it('returns null for default prompt version', function () {
$agent = new TestAgent;
expect($agent->promptVersion())->toBeNull();
});
it('returns empty array for default variables', function () {
$agent = new TestAgent;
expect($agent->promptVariables())->toBe([]);
});
Clearing cached templates
When testing with different prompt versions:it('clears cached template between tests', function () {
$agent = new TestAgent;
// Load v1
$template1 = $agent->promptTemplate();
// Clear cache and load fresh
$agent->forgetPromptTemplate();
$template2 = $agent->promptTemplate();
// Both are fresh instances
expect($template1)->not->toBe($template2);
});
Always call
forgetPromptTemplate() in tests where you modify prompt files
or change the active version between assertions.