Add tests for featured thumbnail generation; apply Pint formatting and related edits

This commit is contained in:
2026-05-06 18:55:40 +02:00
parent 7a8bc8e22a
commit 82f2b1f660
65 changed files with 11325 additions and 49545 deletions

View File

@@ -5,9 +5,12 @@ declare(strict_types=1);
namespace Tests\Feature\Admin;
use App\Http\Middleware\ConditionalValidateCsrfToken;
use App\Models\AcademyAiComparisonResult;
use App\Models\AcademyCategory;
use App\Models\AcademyChallenge;
use App\Models\AcademyChallengeSubmission;
use App\Models\AcademyCategory;
use App\Models\AcademyLesson;
use App\Models\AcademyLessonBlock;
use App\Models\Artwork;
use App\Models\User;
use Illuminate\Foundation\Testing\RefreshDatabase;
@@ -128,4 +131,230 @@ final class AcademyAdminTest extends TestCase
$this->assertNull(Cache::get('academy.home'));
$this->assertNull(Cache::get('academy.categories.lesson'));
}
}
public function test_admin_can_create_a_lesson_with_ai_comparison_block(): void
{
$admin = User::factory()->create(['role' => 'admin']);
$response = $this->actingAs($admin)
->post(route('admin.academy.lessons.store'), [
'title' => 'AI Comparison Lesson',
'slug' => 'ai-comparison-lesson',
'excerpt' => 'Testing comparison block creation.',
'content' => '<p>Lesson body.</p>',
'difficulty' => 'beginner',
'access_level' => 'free',
'lesson_type' => 'article',
'cover_image' => '',
'video_url' => '',
'reading_minutes' => 5,
'featured' => false,
'active' => true,
'published_at' => now()->subMinute()->toDateTimeString(),
'seo_title' => '',
'seo_description' => '',
'blocks' => [
[
'type' => 'ai_comparison',
'title' => 'Same Prompt, Different AI Models',
'payload' => [
'title' => 'Same Prompt, Different AI Models',
'intro' => 'Compare multiple tools.',
'prompt' => 'A peaceful fantasy forest wallpaper.',
'negative_prompt' => 'text, watermark',
'aspect_ratio' => '16:9',
'criteria' => ['Composition', 'Lighting'],
],
'sort_order' => 0,
'active' => true,
'comparison_results' => [],
],
],
]);
$lesson = AcademyLesson::query()->where('slug', 'ai-comparison-lesson')->firstOrFail();
$response->assertRedirect(route('admin.academy.lessons.edit', ['academyLesson' => $lesson]));
$this->assertDatabaseHas('academy_lesson_blocks', [
'lesson_id' => $lesson->id,
'type' => 'ai_comparison',
'active' => true,
]);
}
public function test_admin_can_add_ai_comparison_result_to_existing_lesson(): void
{
$admin = User::factory()->create(['role' => 'admin']);
$lesson = AcademyLesson::query()->create([
'title' => 'Existing Lesson',
'slug' => 'existing-lesson',
'content' => 'Body',
'difficulty' => 'beginner',
'access_level' => 'free',
'lesson_type' => 'article',
'active' => true,
'published_at' => now()->subMinute(),
]);
$this->actingAs($admin)
->patch(route('admin.academy.lessons.update', ['academyLesson' => $lesson]), [
'title' => $lesson->title,
'slug' => $lesson->slug,
'excerpt' => '',
'content' => $lesson->content,
'difficulty' => $lesson->difficulty,
'access_level' => $lesson->access_level,
'lesson_type' => $lesson->lesson_type,
'cover_image' => '',
'video_url' => '',
'reading_minutes' => 5,
'featured' => false,
'active' => true,
'published_at' => now()->subMinute()->toDateTimeString(),
'seo_title' => '',
'seo_description' => '',
'blocks' => [
[
'type' => 'ai_comparison',
'title' => 'Same Prompt, Different AI Models',
'payload' => [
'title' => 'Same Prompt, Different AI Models',
'intro' => 'Compare multiple tools.',
'prompt' => 'A peaceful fantasy forest wallpaper.',
'negative_prompt' => '',
'aspect_ratio' => '16:9',
'criteria' => ['Composition'],
],
'sort_order' => 0,
'active' => true,
'comparison_results' => [
[
'provider' => 'OpenAI',
'model_name' => 'ChatGPT Images',
'image_path' => 'academy/lessons/body/aa/bb/example.webp',
'thumb_path' => 'academy/lessons/body/aa/bb/example-thumb.webp',
'settings' => 'Default quality',
'strengths' => 'Strong composition',
'weaknesses' => 'Slightly over-polished',
'best_for' => 'Wallpaper concepts',
'score' => 9,
'sort_order' => 0,
'active' => true,
],
],
],
],
])
->assertRedirect(route('admin.academy.lessons.edit', ['academyLesson' => $lesson]));
$block = AcademyLessonBlock::query()->where('lesson_id', $lesson->id)->firstOrFail();
$this->assertDatabaseHas('academy_ai_comparison_results', [
'lesson_block_id' => $block->id,
'provider' => 'OpenAI',
'model_name' => 'ChatGPT Images',
'score' => 9,
]);
}
public function test_ai_comparison_score_must_stay_in_range(): void
{
$admin = User::factory()->create(['role' => 'admin']);
$lesson = AcademyLesson::query()->create([
'title' => 'Validation Lesson',
'slug' => 'validation-lesson',
'content' => 'Body',
'difficulty' => 'beginner',
'access_level' => 'free',
'lesson_type' => 'article',
'active' => true,
'published_at' => now()->subMinute(),
]);
$this->from(route('admin.academy.lessons.edit', ['academyLesson' => $lesson]))
->actingAs($admin)
->patch(route('admin.academy.lessons.update', ['academyLesson' => $lesson]), [
'title' => $lesson->title,
'slug' => $lesson->slug,
'excerpt' => '',
'content' => $lesson->content,
'difficulty' => $lesson->difficulty,
'access_level' => $lesson->access_level,
'lesson_type' => $lesson->lesson_type,
'cover_image' => '',
'video_url' => '',
'reading_minutes' => 5,
'featured' => false,
'active' => true,
'published_at' => now()->subMinute()->toDateTimeString(),
'seo_title' => '',
'seo_description' => '',
'blocks' => [
[
'type' => 'ai_comparison',
'title' => 'Invalid score block',
'payload' => [
'title' => 'Invalid score block',
'prompt' => 'Prompt',
'criteria' => ['Composition'],
],
'sort_order' => 0,
'active' => true,
'comparison_results' => [
[
'provider' => 'OpenAI',
'model_name' => 'ChatGPT Images',
'image_path' => 'academy/lessons/body/aa/bb/example.webp',
'score' => 11,
'sort_order' => 0,
'active' => true,
],
],
],
],
])
->assertRedirect(route('admin.academy.lessons.edit', ['academyLesson' => $lesson]))
->assertSessionHasErrors(['blocks.0.comparison_results.0.score']);
}
public function test_lesson_delete_soft_deletes_ai_comparison_children(): void
{
$admin = User::factory()->create(['role' => 'admin']);
$lesson = AcademyLesson::query()->create([
'title' => 'Delete Lesson',
'slug' => 'delete-lesson',
'content' => 'Body',
'difficulty' => 'beginner',
'access_level' => 'free',
'lesson_type' => 'article',
'active' => true,
'published_at' => now()->subMinute(),
]);
$block = AcademyLessonBlock::query()->create([
'lesson_id' => $lesson->id,
'type' => 'ai_comparison',
'title' => 'Delete Block',
'payload' => ['title' => 'Delete Block', 'prompt' => 'Prompt'],
'sort_order' => 0,
'active' => true,
]);
$result = AcademyAiComparisonResult::query()->create([
'lesson_block_id' => $block->id,
'provider' => 'OpenAI',
'model_name' => 'ChatGPT Images',
'image_path' => 'academy/lessons/body/aa/bb/example.webp',
'score' => 8,
'sort_order' => 0,
'active' => true,
]);
$this->actingAs($admin)
->delete(route('admin.academy.lessons.destroy', ['academyLesson' => $lesson]))
->assertRedirect(route('admin.academy.lessons.index'));
$this->assertSoftDeleted('academy_lessons', ['id' => $lesson->id]);
$this->assertSoftDeleted('academy_lesson_blocks', ['id' => $block->id]);
$this->assertSoftDeleted('academy_ai_comparison_results', ['id' => $result->id]);
}
}