Make AspectRatio the single source for crop math; fill test gaps
Review follow-ups: - AspectRatio::toFloat() now owns the crop ratio math; CropsImageForAspectRatio delegates to it so the enum is the single source of truth for both validation and cropping (no more parallel literal map). - API update controller now reloads postPlatforms before returning, so the update response reflects the persisted platform meta/content_type (was stale). - Tests: AspectRatio enum unit test; API valid-update read-back + 'original' on create; MCP response read-back + valid update.
This commit is contained in:
parent
ce6ee04883
commit
efe49c34de
6 changed files with 111 additions and 6 deletions
|
|
@ -10,4 +10,18 @@ enum AspectRatio: string
|
|||
case Portrait = '4:5';
|
||||
case Landscape = '16:9';
|
||||
case Original = 'original';
|
||||
|
||||
/**
|
||||
* Width-to-height ratio used to center-crop an image. `Original` means "no
|
||||
* crop", so it resolves to a square (1.0) only as a safe fallback — callers
|
||||
* should bypass cropping entirely for `Original`.
|
||||
*/
|
||||
public function toFloat(): float
|
||||
{
|
||||
return match ($this) {
|
||||
self::Portrait => 4 / 5,
|
||||
self::Landscape => 16 / 9,
|
||||
self::Square, self::Original => 1.0,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -75,7 +75,10 @@ public function update(UpdatePostRequest $request, Post $post): PostResource|Jso
|
|||
);
|
||||
}
|
||||
|
||||
return new PostResource(data_get($result, 'post'));
|
||||
$updated = data_get($result, 'post');
|
||||
$updated->load(['postPlatforms.socialAccount']);
|
||||
|
||||
return new PostResource($updated);
|
||||
}
|
||||
|
||||
public function destroy(Request $request, Post $post): JsonResponse
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@
|
|||
|
||||
namespace App\Services\Social\Concerns;
|
||||
|
||||
use App\Enums\PostPlatform\AspectRatio;
|
||||
use App\Exceptions\Social\SocialPublishException;
|
||||
use App\Services\Media\MediaOptimizer;
|
||||
use Illuminate\Support\Facades\Http;
|
||||
|
|
@ -51,11 +52,7 @@ protected function cropImageForAspectRatio(string $imageUrl, ?string $aspectRati
|
|||
|
||||
protected function aspectRatioToFloat(string $ratio): float
|
||||
{
|
||||
return match ($ratio) {
|
||||
'4:5' => 4 / 5,
|
||||
'16:9' => 16 / 9,
|
||||
default => 1.0,
|
||||
};
|
||||
return AspectRatio::tryFrom($ratio)?->toFloat() ?? 1.0;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -624,3 +624,42 @@
|
|||
])
|
||||
->assertJsonValidationErrors(['platforms.0.meta.aspect_ratio']);
|
||||
});
|
||||
|
||||
it('accepts a valid aspect_ratio on update and persists it', function () {
|
||||
$post = Post::factory()->create([
|
||||
'workspace_id' => $this->workspace->id,
|
||||
'user_id' => $this->user->id,
|
||||
'status' => PostStatus::Draft,
|
||||
]);
|
||||
$postPlatform = PostPlatform::factory()->linkedin()->create([
|
||||
'post_id' => $post->id,
|
||||
'social_account_id' => $this->socialAccount->id,
|
||||
'enabled' => true,
|
||||
]);
|
||||
|
||||
$this->withHeaders(['Authorization' => 'Bearer '.$this->plainToken])
|
||||
->putJson(route('api.posts.update', $post), [
|
||||
'status' => 'draft',
|
||||
'platforms' => [
|
||||
['id' => $postPlatform->id, 'content_type' => 'linkedin_post', 'meta' => ['aspect_ratio' => '16:9']],
|
||||
],
|
||||
])
|
||||
->assertOk()
|
||||
->assertJsonPath('platforms.0.meta.aspect_ratio', '16:9');
|
||||
|
||||
expect($postPlatform->fresh()->meta['aspect_ratio'])->toBe('16:9');
|
||||
});
|
||||
|
||||
it('accepts the original aspect_ratio (no crop) on create', function () {
|
||||
$this->withHeaders(['Authorization' => 'Bearer '.$this->plainToken])
|
||||
->postJson(route('api.posts.store'), [
|
||||
'platforms' => [
|
||||
['social_account_id' => $this->socialAccount->id, 'content_type' => 'linkedin_post', 'meta' => ['aspect_ratio' => 'original']],
|
||||
],
|
||||
])
|
||||
->assertCreated();
|
||||
|
||||
$platform = Post::where('workspace_id', $this->workspace->id)->first()
|
||||
->postPlatforms()->where('social_account_id', $this->socialAccount->id)->first();
|
||||
expect($platform->meta['aspect_ratio'])->toBe('original');
|
||||
});
|
||||
|
|
|
|||
|
|
@ -323,3 +323,36 @@
|
|||
|
||||
$response->assertHasErrors();
|
||||
});
|
||||
|
||||
test('create post returns the platform meta in the response (read-back)', function () {
|
||||
TryPostServer::actingAs($this->user)
|
||||
->tool(CreatePostTool::class, [
|
||||
'platforms' => [
|
||||
['social_account_id' => $this->socialAccount->id, 'content_type' => 'linkedin_post', 'meta' => ['aspect_ratio' => '4:5']],
|
||||
],
|
||||
])
|
||||
->assertOk()
|
||||
->assertStructuredContent(fn (AssertableJson $json) => $json->where('platforms.0.meta.aspect_ratio', '4:5')->etc());
|
||||
});
|
||||
|
||||
test('update post accepts a valid aspect_ratio and persists it', function () {
|
||||
$post = Post::factory()->create([
|
||||
'workspace_id' => $this->workspace->id,
|
||||
'user_id' => $this->user->id,
|
||||
]);
|
||||
$platform = PostPlatform::factory()->create([
|
||||
'post_id' => $post->id,
|
||||
'social_account_id' => $this->socialAccount->id,
|
||||
]);
|
||||
|
||||
TryPostServer::actingAs($this->user)
|
||||
->tool(UpdatePostTool::class, [
|
||||
'post_id' => $post->id,
|
||||
'platforms' => [
|
||||
['id' => $platform->id, 'meta' => ['aspect_ratio' => '16:9']],
|
||||
],
|
||||
])
|
||||
->assertOk();
|
||||
|
||||
expect($platform->fresh()->meta['aspect_ratio'])->toBe('16:9');
|
||||
});
|
||||
|
|
|
|||
19
tests/Unit/Enums/AspectRatioTest.php
Normal file
19
tests/Unit/Enums/AspectRatioTest.php
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
use App\Enums\PostPlatform\AspectRatio;
|
||||
|
||||
test('aspect ratio has the expected cases and values', function () {
|
||||
expect(array_column(AspectRatio::cases(), 'value'))
|
||||
->toEqualCanonicalizing(['1:1', '4:5', '16:9', 'original']);
|
||||
});
|
||||
|
||||
test('aspect ratio maps to the correct crop float', function (string $value, float $expected) {
|
||||
expect(AspectRatio::from($value)->toFloat())->toBe($expected);
|
||||
})->with([
|
||||
'1:1' => ['1:1', 1.0],
|
||||
'4:5' => ['4:5', 4 / 5],
|
||||
'16:9' => ['16:9', 16 / 9],
|
||||
'original' => ['original', 1.0],
|
||||
]);
|
||||
Loading…
Reference in a new issue