From 31de750c71580dfeb91856cc698a8dfb6045428e Mon Sep 17 00:00:00 2001 From: Paulo Castellano Date: Thu, 18 Jun 2026 11:50:10 -0300 Subject: [PATCH] fix(automations): persist the Generate node content style MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The Generate node data rules had no rule for 'style', so validated() dropped it on save — the run always fell back to the default image_card. Add a Rule::in(ContentStyle) rule so the selected style is persisted and unknown values are rejected. --- .../Automations/UpdateAutomationRequest.php | 2 + .../Feature/Automation/AutomationCrudTest.php | 43 +++++++++++++++++++ 2 files changed, 45 insertions(+) diff --git a/app/Http/Requests/App/Automations/UpdateAutomationRequest.php b/app/Http/Requests/App/Automations/UpdateAutomationRequest.php index 0b0a3677..f89de16f 100644 --- a/app/Http/Requests/App/Automations/UpdateAutomationRequest.php +++ b/app/Http/Requests/App/Automations/UpdateAutomationRequest.php @@ -4,6 +4,7 @@ namespace App\Http\Requests\App\Automations; +use App\Enums\Ai\ContentStyle; use App\Enums\Automation\AuthType; use App\Enums\Automation\Condition\Operator as ConditionOperator; use App\Enums\Automation\DelayUnit; @@ -156,6 +157,7 @@ private function dataRulesForNodeType(?string $type, int $i): array 'target_slide_count' => ['nullable', 'integer', 'min:0', 'max:'.GenerateNodeValidator::MAX_GENERATED_IMAGES], 'use_brand_voice' => ['sometimes', 'boolean'], 'use_brand_visuals' => ['sometimes', 'boolean'], + 'style' => ['sometimes', Rule::in(array_column(ContentStyle::cases(), 'value'))], ], NodeType::Delay->value => [ 'duration' => ['required', 'integer', 'min:1'], diff --git a/tests/Feature/Automation/AutomationCrudTest.php b/tests/Feature/Automation/AutomationCrudTest.php index b362778d..fa73c5b1 100644 --- a/tests/Feature/Automation/AutomationCrudTest.php +++ b/tests/Feature/Automation/AutomationCrudTest.php @@ -60,6 +60,49 @@ expect($automation->fresh()->nodes)->toHaveCount(3); }); +it('persists the generate node style through a save round-trip', function () { + $automation = Automation::factory()->for($this->workspace)->create(); + + $payload = [ + 'nodes' => [ + ['id' => 'n1', 'type' => 'trigger', 'position' => ['x' => 0, 'y' => 0], 'data' => ['trigger_type' => 'schedule', 'cron' => '0 9 * * *']], + ['id' => 'n2', 'type' => 'generate', 'position' => ['x' => 200, 'y' => 0], 'data' => [ + 'accounts' => [['social_account_id' => 'acc-1']], + 'prompt_template' => 'hi', + 'style' => 'tweet_card', + ]], + ], + 'connections' => [['id' => 'e1', 'source' => 'n1', 'target' => 'n2']], + ]; + + $this->actingAs($this->user) + ->put(route('app.automations.update', $automation->id), $payload) + ->assertRedirect(); + + $node = collect($automation->fresh()->nodes)->firstWhere('type', 'generate'); + expect($node['data']['style'])->toBe('tweet_card'); +}); + +it('rejects an unknown generate node style', function () { + $automation = Automation::factory()->for($this->workspace)->create(); + + $payload = [ + 'nodes' => [ + ['id' => 'n1', 'type' => 'trigger', 'position' => ['x' => 0, 'y' => 0], 'data' => ['trigger_type' => 'schedule', 'cron' => '0 9 * * *']], + ['id' => 'n2', 'type' => 'generate', 'position' => ['x' => 200, 'y' => 0], 'data' => [ + 'accounts' => [['social_account_id' => 'acc-1']], + 'prompt_template' => 'hi', + 'style' => 'not_a_real_style', + ]], + ], + 'connections' => [['id' => 'e1', 'source' => 'n1', 'target' => 'n2']], + ]; + + $this->actingAs($this->user) + ->put(route('app.automations.update', $automation->id), $payload) + ->assertSessionHasErrors('nodes.1.data.style'); +}); + it('persists every trigger schedule editor field through a save round-trip', function (array $scheduleData) { $automation = Automation::factory()->for($this->workspace)->create();