trypost/tests/Feature/Automation/Node/GenerateNodeTest.php
Paulo Castellano b23ab0166e feat(automations): implement automation features and UI enhancements
- Added new automation-related routes and controllers for managing automations.
- Introduced automation nodes in the UI with distinct styles and interactions.
- Updated sidebar to include navigation for automations.
- Enhanced post creation logic to support automation metadata.
- Refactored content type and platform enums into types for better type safety.
- Added localization for automation-related terms in English, Spanish, and Portuguese.
- Improved error handling in various components to accommodate new features.
2026-05-24 09:17:19 -03:00

132 lines
4.8 KiB
PHP

<?php
use App\Actions\Automation\Node\RunGenerateNode;
use App\Ai\Agents\PostContentGenerator;
use App\Ai\Agents\PostContentHumanizer;
use App\Enums\Post\Status as PostStatus;
use App\Models\AutomationRun;
use App\Models\Post;
use App\Models\SocialAccount;
use App\Models\Workspace;
it('creates a draft post and writes generated output to run context', function () {
PostContentGenerator::fake([
['content' => 'Great post about Stripe Radar', 'image_title' => 'Stripe Radar', 'image_body' => 'Fraud prevention', 'image_keywords' => ['stripe', 'fraud']],
]);
PostContentHumanizer::fake([
['content' => 'Great post about Stripe Radar (humanized)', 'image_title' => 'Stripe Radar', 'image_body' => 'Fraud prevention'],
]);
$run = AutomationRun::factory()->create([
'context' => ['trigger' => ['title' => 'Stripe Radar update']],
]);
$result = app(RunGenerateNode::class)($run, [
'accounts' => [],
'prompt_template' => 'Generate a post about: {{ trigger.title }}',
'image_source' => 'none',
]);
expect($result->status->value)->toBe('completed');
expect($result->output)->toHaveKey('generated');
expect($result->output['generated'])->toHaveKey('post_id');
expect($result->output['generated']['content'])->toBe('Great post about Stripe Radar (humanized)');
$run->refresh();
expect($run->generated_post_id)->not->toBeNull();
$post = Post::find($run->generated_post_id);
expect($post)->not->toBeNull();
expect($post->status)->toBe(PostStatus::Draft);
});
it('interpolates the prompt template before passing it to the generator', function () {
PostContentGenerator::fake([
['content' => 'Interpolated content', 'image_title' => 'Title', 'image_body' => 'Body', 'image_keywords' => ['kw']],
]);
PostContentHumanizer::fake([
['content' => 'Interpolated content humanized', 'image_title' => 'Title', 'image_body' => 'Body'],
]);
$run = AutomationRun::factory()->create([
'context' => ['trigger' => ['topic' => 'AI Agents']],
]);
app(RunGenerateNode::class)($run, [
'accounts' => [],
'prompt_template' => 'Write about {{ trigger.topic }}',
]);
PostContentGenerator::assertPrompted(fn ($prompt) => $prompt->contains('Write about AI Agents'));
});
it('still creates a draft post when humanizer fails', function () {
PostContentGenerator::fake([
['content' => 'Raw content', 'image_title' => 'Title', 'image_body' => 'Body', 'image_keywords' => ['kw']],
]);
PostContentHumanizer::fake(fn () => throw new RuntimeException('Humanizer down'));
$run = AutomationRun::factory()->create([
'context' => ['trigger' => ['title' => 'test']],
]);
$result = app(RunGenerateNode::class)($run, [
'accounts' => [],
'prompt_template' => 'Write about {{ trigger.title }}',
]);
expect($result->status->value)->toBe('completed');
expect($result->output['generated']['content'])->toBe('Raw content');
$run->refresh();
expect($run->generated_post_id)->not->toBeNull();
});
it('derives carousel format when a carousel-capable account is configured with target_slide_count > 1', function () {
$workspace = Workspace::factory()->create();
$account = SocialAccount::factory()->for($workspace)->create(['platform' => 'instagram']);
$action = app(RunGenerateNode::class);
$accountsConfig = [
['social_account_id' => (string) $account->id, 'content_type' => 'instagram_carousel', 'meta' => []],
];
['format' => $format, 'slide_count' => $slideCount] = $action->deriveFormat($accountsConfig, ['target_slide_count' => 5]);
expect($format)->toBe('carousel');
expect($slideCount)->toBe(5);
});
it('derives single format when carousel account has target_slide_count of 1', function () {
$workspace = Workspace::factory()->create();
$account = SocialAccount::factory()->for($workspace)->create(['platform' => 'instagram']);
$action = app(RunGenerateNode::class);
$accountsConfig = [
['social_account_id' => (string) $account->id, 'content_type' => 'instagram_carousel', 'meta' => []],
];
['format' => $format, 'slide_count' => $slideCount] = $action->deriveFormat($accountsConfig, ['target_slide_count' => 1]);
expect($format)->toBe('single');
expect($slideCount)->toBe(1);
});
it('derives single format when no carousel-capable account is configured', function () {
$action = app(RunGenerateNode::class);
$accountsConfig = [
['social_account_id' => '1', 'content_type' => 'x_post', 'meta' => []],
['social_account_id' => '2', 'content_type' => 'instagram_feed', 'meta' => []],
];
['format' => $format, 'slide_count' => $slideCount] = $action->deriveFormat($accountsConfig, ['target_slide_count' => 5]);
expect($format)->toBe('single');
expect($slideCount)->toBe(1);
});