Merge pull request 'test: add PromptVersion model tests' (#45) from test/prompt-version-tests into main
Some checks are pending
CI / PHP 8.3 (push) Waiting to run
CI / PHP 8.4 (push) Waiting to run

Reviewed-on: #45
This commit is contained in:
Snider 2026-02-23 06:08:50 +00:00
commit f97d862f27
2 changed files with 344 additions and 0 deletions

View file

@ -0,0 +1,65 @@
<?php
declare(strict_types=1);
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Create prompts and prompt_versions tables.
*
* Guarded with hasTable() so this migration is idempotent and
* can coexist with the consolidated app-level migration.
*/
public function up(): void
{
Schema::disableForeignKeyConstraints();
if (! Schema::hasTable('prompts')) {
Schema::create('prompts', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->string('category')->nullable();
$table->text('description')->nullable();
$table->text('system_prompt')->nullable();
$table->text('user_template')->nullable();
$table->json('variables')->nullable();
$table->string('model')->nullable();
$table->json('model_config')->nullable();
$table->boolean('is_active')->default(true);
$table->timestamps();
$table->index('category');
$table->index('is_active');
});
}
if (! Schema::hasTable('prompt_versions')) {
Schema::create('prompt_versions', function (Blueprint $table) {
$table->id();
$table->foreignId('prompt_id')->constrained()->cascadeOnDelete();
$table->unsignedInteger('version');
$table->text('system_prompt')->nullable();
$table->text('user_template')->nullable();
$table->json('variables')->nullable();
$table->foreignId('created_by')->nullable()->constrained('users')->nullOnDelete();
$table->timestamps();
$table->index(['prompt_id', 'version']);
});
}
Schema::enableForeignKeyConstraints();
}
public function down(): void
{
Schema::disableForeignKeyConstraints();
Schema::dropIfExists('prompt_versions');
Schema::dropIfExists('prompts');
Schema::enableForeignKeyConstraints();
}
};

View file

@ -0,0 +1,279 @@
<?php
declare(strict_types=1);
use Core\Mod\Agentic\Models\Prompt;
use Core\Mod\Agentic\Models\PromptVersion;
use Core\Tenant\Models\User;
// =========================================================================
// Version Creation Tests
// =========================================================================
describe('version creation', function () {
it('can be created with required attributes', function () {
$prompt = Prompt::create([
'name' => 'Test Prompt',
'system_prompt' => 'You are a helpful assistant.',
'user_template' => 'Answer this: {{{question}}}',
'variables' => ['question'],
]);
$version = PromptVersion::create([
'prompt_id' => $prompt->id,
'version' => 1,
'system_prompt' => 'You are a helpful assistant.',
'user_template' => 'Answer this: {{{question}}}',
'variables' => ['question'],
]);
expect($version->id)->not->toBeNull()
->and($version->version)->toBe(1)
->and($version->prompt_id)->toBe($prompt->id);
});
it('casts variables as array', function () {
$prompt = Prompt::create(['name' => 'Test Prompt']);
$version = PromptVersion::create([
'prompt_id' => $prompt->id,
'version' => 1,
'variables' => ['topic', 'tone'],
]);
expect($version->variables)
->toBeArray()
->toBe(['topic', 'tone']);
});
it('casts version as integer', function () {
$prompt = Prompt::create(['name' => 'Test Prompt']);
$version = PromptVersion::create([
'prompt_id' => $prompt->id,
'version' => 3,
]);
expect($version->version)->toBeInt()->toBe(3);
});
it('can be created without optional fields', function () {
$prompt = Prompt::create(['name' => 'Minimal Prompt']);
$version = PromptVersion::create([
'prompt_id' => $prompt->id,
'version' => 1,
]);
expect($version->id)->not->toBeNull()
->and($version->system_prompt)->toBeNull()
->and($version->user_template)->toBeNull()
->and($version->created_by)->toBeNull();
});
});
// =========================================================================
// Relationship Tests
// =========================================================================
describe('relationships', function () {
it('belongs to a prompt', function () {
$prompt = Prompt::create([
'name' => 'Parent Prompt',
'system_prompt' => 'System text.',
]);
$version = PromptVersion::create([
'prompt_id' => $prompt->id,
'version' => 1,
]);
expect($version->prompt)->toBeInstanceOf(Prompt::class)
->and($version->prompt->id)->toBe($prompt->id)
->and($version->prompt->name)->toBe('Parent Prompt');
});
it('belongs to a creator user', function () {
$user = User::factory()->create();
$prompt = Prompt::create(['name' => 'Test Prompt']);
$version = PromptVersion::create([
'prompt_id' => $prompt->id,
'version' => 1,
'created_by' => $user->id,
]);
expect($version->creator)->toBeInstanceOf(User::class)
->and($version->creator->id)->toBe($user->id);
});
it('has null creator when created_by is null', function () {
$prompt = Prompt::create(['name' => 'Test Prompt']);
$version = PromptVersion::create([
'prompt_id' => $prompt->id,
'version' => 1,
]);
expect($version->creator)->toBeNull();
});
});
// =========================================================================
// Restore Method Tests
// =========================================================================
describe('restore', function () {
it('restores system_prompt and user_template to the parent prompt', function () {
$prompt = Prompt::create([
'name' => 'Test Prompt',
'system_prompt' => 'Original system prompt.',
'user_template' => 'Original template.',
]);
$version = PromptVersion::create([
'prompt_id' => $prompt->id,
'version' => 1,
'system_prompt' => 'Versioned system prompt.',
'user_template' => 'Versioned template.',
]);
$prompt->update([
'system_prompt' => 'Newer system prompt.',
'user_template' => 'Newer template.',
]);
$version->restore();
$fresh = $prompt->fresh();
expect($fresh->system_prompt)->toBe('Versioned system prompt.')
->and($fresh->user_template)->toBe('Versioned template.');
});
it('restores variables to the parent prompt', function () {
$prompt = Prompt::create([
'name' => 'Test Prompt',
'variables' => ['topic'],
]);
$version = PromptVersion::create([
'prompt_id' => $prompt->id,
'version' => 1,
'variables' => ['topic', 'tone'],
]);
$prompt->update(['variables' => ['topic', 'tone', 'length']]);
$version->restore();
expect($prompt->fresh()->variables)->toBe(['topic', 'tone']);
});
it('returns the parent prompt instance after restore', function () {
$prompt = Prompt::create([
'name' => 'Test Prompt',
'system_prompt' => 'Old.',
]);
$version = PromptVersion::create([
'prompt_id' => $prompt->id,
'version' => 1,
'system_prompt' => 'Versioned.',
]);
$result = $version->restore();
expect($result)->toBeInstanceOf(Prompt::class)
->and($result->id)->toBe($prompt->id);
});
});
// =========================================================================
// Version History Tests
// =========================================================================
describe('version history', function () {
it('prompt tracks multiple versions in descending order', function () {
$prompt = Prompt::create([
'name' => 'Evolving Prompt',
'system_prompt' => 'v1.',
]);
PromptVersion::create(['prompt_id' => $prompt->id, 'version' => 1, 'system_prompt' => 'v1.']);
PromptVersion::create(['prompt_id' => $prompt->id, 'version' => 2, 'system_prompt' => 'v2.']);
PromptVersion::create(['prompt_id' => $prompt->id, 'version' => 3, 'system_prompt' => 'v3.']);
$versions = $prompt->versions()->get();
expect($versions)->toHaveCount(3)
->and($versions->first()->version)->toBe(3)
->and($versions->last()->version)->toBe(1);
});
it('createVersion snapshots current prompt state', function () {
$prompt = Prompt::create([
'name' => 'Test Prompt',
'system_prompt' => 'Original system prompt.',
'user_template' => 'Original template.',
'variables' => ['topic'],
]);
$version = $prompt->createVersion();
expect($version)->toBeInstanceOf(PromptVersion::class)
->and($version->version)->toBe(1)
->and($version->system_prompt)->toBe('Original system prompt.')
->and($version->user_template)->toBe('Original template.')
->and($version->variables)->toBe(['topic']);
});
it('createVersion increments version number', function () {
$prompt = Prompt::create([
'name' => 'Test Prompt',
'system_prompt' => 'v1.',
]);
$v1 = $prompt->createVersion();
$prompt->update(['system_prompt' => 'v2.']);
$v2 = $prompt->createVersion();
expect($v1->version)->toBe(1)
->and($v2->version)->toBe(2);
});
it('createVersion records the creator user id', function () {
$user = User::factory()->create();
$prompt = Prompt::create([
'name' => 'Test Prompt',
'system_prompt' => 'System text.',
]);
$version = $prompt->createVersion($user->id);
expect($version->created_by)->toBe($user->id);
});
it('versions are scoped to their parent prompt', function () {
$promptA = Prompt::create(['name' => 'Prompt A']);
$promptB = Prompt::create(['name' => 'Prompt B']);
PromptVersion::create(['prompt_id' => $promptA->id, 'version' => 1]);
PromptVersion::create(['prompt_id' => $promptA->id, 'version' => 2]);
PromptVersion::create(['prompt_id' => $promptB->id, 'version' => 1]);
expect($promptA->versions()->count())->toBe(2)
->and($promptB->versions()->count())->toBe(1);
});
it('deleting prompt cascades to versions', function () {
$prompt = Prompt::create(['name' => 'Test Prompt']);
PromptVersion::create(['prompt_id' => $prompt->id, 'version' => 1]);
PromptVersion::create(['prompt_id' => $prompt->id, 'version' => 2]);
$promptId = $prompt->id;
$prompt->delete();
expect(PromptVersion::where('prompt_id', $promptId)->count())->toBe(0);
});
});