Merge pull request #5 from trypost-it/fix/nullable-content-publishers
Fix nullable content across all social publishers
This commit is contained in:
commit
557cd210a0
15 changed files with 312 additions and 41 deletions
|
|
@ -20,8 +20,6 @@ class ThreadsController extends SocialController
|
|||
protected array $scopes = [
|
||||
'threads_basic',
|
||||
'threads_content_publish',
|
||||
'threads_manage_replies',
|
||||
'threads_read_replies',
|
||||
];
|
||||
|
||||
public function connect(Request $request): Response|RedirectResponse
|
||||
|
|
|
|||
|
|
@ -59,7 +59,7 @@ public function handle(): void
|
|||
|
||||
$this->postPlatform->markAsFailed($e->getMessage());
|
||||
$this->postPlatform->socialAccount->markAsDisconnected($e->getMessage());
|
||||
} catch (\Exception $e) {
|
||||
} catch (\Throwable $e) {
|
||||
Log::error('Failed to publish to social platform', [
|
||||
'post_platform_id' => $this->postPlatform->id,
|
||||
'platform' => $this->postPlatform->platform->value,
|
||||
|
|
|
|||
|
|
@ -48,10 +48,14 @@ public function publish(PostPlatform $postPlatform): array
|
|||
};
|
||||
}
|
||||
|
||||
private function publishPost(string $pageId, string $accessToken, string $content, $media): array
|
||||
private function publishPost(string $pageId, string $accessToken, ?string $content, $media): array
|
||||
{
|
||||
// Text only post
|
||||
if ($media->isEmpty()) {
|
||||
if (empty($content)) {
|
||||
throw new \Exception('Facebook text posts require content. Please add text to your post.');
|
||||
}
|
||||
|
||||
return $this->publishTextPost($pageId, $accessToken, $content);
|
||||
}
|
||||
|
||||
|
|
@ -101,7 +105,7 @@ private function publishTextPost(string $pageId, string $accessToken, string $co
|
|||
];
|
||||
}
|
||||
|
||||
private function publishSingleImagePost(string $pageId, string $accessToken, string $content, $media): array
|
||||
private function publishSingleImagePost(string $pageId, string $accessToken, ?string $content, $media): array
|
||||
{
|
||||
Log::info('Facebook publishing single image post', ['page_id' => $pageId]);
|
||||
|
||||
|
|
@ -128,7 +132,7 @@ private function publishSingleImagePost(string $pageId, string $accessToken, str
|
|||
];
|
||||
}
|
||||
|
||||
private function publishMultiImagePost(string $pageId, string $accessToken, string $content, $mediaCollection): array
|
||||
private function publishMultiImagePost(string $pageId, string $accessToken, ?string $content, $mediaCollection): array
|
||||
{
|
||||
Log::info('Facebook publishing multi-image post', [
|
||||
'page_id' => $pageId,
|
||||
|
|
@ -194,7 +198,7 @@ private function publishMultiImagePost(string $pageId, string $accessToken, stri
|
|||
];
|
||||
}
|
||||
|
||||
private function publishVideoPost(string $pageId, string $accessToken, string $content, $media): array
|
||||
private function publishVideoPost(string $pageId, string $accessToken, ?string $content, $media): array
|
||||
{
|
||||
Log::info('Facebook publishing video post', ['page_id' => $pageId]);
|
||||
|
||||
|
|
@ -222,7 +226,7 @@ private function publishVideoPost(string $pageId, string $accessToken, string $c
|
|||
];
|
||||
}
|
||||
|
||||
private function publishReel(string $pageId, string $accessToken, string $content, $media): array
|
||||
private function publishReel(string $pageId, string $accessToken, ?string $content, $media): array
|
||||
{
|
||||
Log::info('Facebook publishing reel', ['page_id' => $pageId]);
|
||||
|
||||
|
|
|
|||
|
|
@ -56,7 +56,7 @@ public function publish(PostPlatform $postPlatform): array
|
|||
};
|
||||
}
|
||||
|
||||
private function publishSingleImage(string $instagramId, string $accessToken, string $content, $media): array
|
||||
private function publishSingleImage(string $instagramId, string $accessToken, ?string $content, $media): array
|
||||
{
|
||||
Log::info('Instagram publishing single image', ['instagram_id' => $instagramId, 'image_url' => $media->url]);
|
||||
|
||||
|
|
@ -93,7 +93,7 @@ private function publishSingleImage(string $instagramId, string $accessToken, st
|
|||
return $this->publishContainer($instagramId, $accessToken, $containerId);
|
||||
}
|
||||
|
||||
private function publishReel(string $instagramId, string $accessToken, string $content, $media): array
|
||||
private function publishReel(string $instagramId, string $accessToken, ?string $content, $media): array
|
||||
{
|
||||
Log::info('Instagram publishing reel', ['instagram_id' => $instagramId]);
|
||||
|
||||
|
|
@ -167,7 +167,7 @@ private function publishStory(string $instagramId, string $accessToken, $media):
|
|||
return $this->publishContainer($instagramId, $accessToken, $containerId);
|
||||
}
|
||||
|
||||
private function publishCarousel(string $instagramId, string $accessToken, string $content, $mediaCollection): array
|
||||
private function publishCarousel(string $instagramId, string $accessToken, ?string $content, $mediaCollection): array
|
||||
{
|
||||
Log::info('Instagram publishing carousel', [
|
||||
'instagram_id' => $instagramId,
|
||||
|
|
|
|||
|
|
@ -101,11 +101,11 @@ private function retryWithRefresh(PostPlatform $postPlatform, TokenExpiredExcept
|
|||
}
|
||||
}
|
||||
|
||||
private function publishPost(string $organizationUrn, string $content, $media, $account): array
|
||||
private function publishPost(string $organizationUrn, ?string $content, $media, $account): array
|
||||
{
|
||||
$payload = [
|
||||
'author' => $organizationUrn,
|
||||
'commentary' => $content,
|
||||
'commentary' => $content ?? '',
|
||||
'visibility' => 'PUBLIC',
|
||||
'distribution' => [
|
||||
'feedDistribution' => 'MAIN_FEED',
|
||||
|
|
@ -155,7 +155,7 @@ private function publishPost(string $organizationUrn, string $content, $media, $
|
|||
];
|
||||
}
|
||||
|
||||
private function publishCarousel(string $organizationUrn, string $content, $mediaCollection, $account): array
|
||||
private function publishCarousel(string $organizationUrn, ?string $content, $mediaCollection, $account): array
|
||||
{
|
||||
Log::info('LinkedIn Page publishing carousel', [
|
||||
'owner' => $organizationUrn,
|
||||
|
|
@ -186,7 +186,7 @@ private function publishCarousel(string $organizationUrn, string $content, $medi
|
|||
|
||||
$payload = [
|
||||
'author' => $organizationUrn,
|
||||
'commentary' => $content,
|
||||
'commentary' => $content ?? '',
|
||||
'visibility' => 'PUBLIC',
|
||||
'distribution' => [
|
||||
'feedDistribution' => 'MAIN_FEED',
|
||||
|
|
|
|||
|
|
@ -94,11 +94,11 @@ private function retryWithRefresh(PostPlatform $postPlatform, TokenExpiredExcept
|
|||
}
|
||||
}
|
||||
|
||||
private function publishPost(string $personUrn, string $content, $media): array
|
||||
private function publishPost(string $personUrn, ?string $content, $media): array
|
||||
{
|
||||
$payload = [
|
||||
'author' => $personUrn,
|
||||
'commentary' => $content,
|
||||
'commentary' => $content ?? '',
|
||||
'visibility' => 'PUBLIC',
|
||||
'distribution' => [
|
||||
'feedDistribution' => 'MAIN_FEED',
|
||||
|
|
@ -142,7 +142,7 @@ private function publishPost(string $personUrn, string $content, $media): array
|
|||
];
|
||||
}
|
||||
|
||||
private function publishCarousel(string $personUrn, string $content, $mediaCollection): array
|
||||
private function publishCarousel(string $personUrn, ?string $content, $mediaCollection): array
|
||||
{
|
||||
Log::info('LinkedIn publishing carousel', [
|
||||
'owner' => $personUrn,
|
||||
|
|
@ -173,7 +173,7 @@ private function publishCarousel(string $personUrn, string $content, $mediaColle
|
|||
|
||||
$payload = [
|
||||
'author' => $personUrn,
|
||||
'commentary' => $content,
|
||||
'commentary' => $content ?? '',
|
||||
'visibility' => 'PUBLIC',
|
||||
'distribution' => [
|
||||
'feedDistribution' => 'MAIN_FEED',
|
||||
|
|
|
|||
|
|
@ -47,6 +47,10 @@ public function publish(PostPlatform $postPlatform): array
|
|||
|
||||
// Text only post
|
||||
if ($media->isEmpty()) {
|
||||
if (empty($postPlatform->content)) {
|
||||
throw new \Exception('Threads text posts require content. Please add text to your post.');
|
||||
}
|
||||
|
||||
return $this->publishTextPost($userId, $accessToken, $postPlatform->content);
|
||||
}
|
||||
|
||||
|
|
@ -91,7 +95,7 @@ private function publishTextPost(string $userId, string $accessToken, string $co
|
|||
return $this->publishContainer($userId, $accessToken, $containerId);
|
||||
}
|
||||
|
||||
private function publishImagePost(string $userId, string $accessToken, string $content, $media): array
|
||||
private function publishImagePost(string $userId, string $accessToken, ?string $content, $media): array
|
||||
{
|
||||
Log::info('Threads publishing image post', ['user_id' => $userId, 'image_url' => $media->url]);
|
||||
|
||||
|
|
@ -122,7 +126,7 @@ private function publishImagePost(string $userId, string $accessToken, string $c
|
|||
return $this->publishContainer($userId, $accessToken, $containerId);
|
||||
}
|
||||
|
||||
private function publishVideoPost(string $userId, string $accessToken, string $content, $media): array
|
||||
private function publishVideoPost(string $userId, string $accessToken, ?string $content, $media): array
|
||||
{
|
||||
Log::info('Threads publishing video post', ['user_id' => $userId]);
|
||||
|
||||
|
|
@ -151,7 +155,7 @@ private function publishVideoPost(string $userId, string $accessToken, string $c
|
|||
return $this->publishContainer($userId, $accessToken, $containerId);
|
||||
}
|
||||
|
||||
private function publishCarousel(string $userId, string $accessToken, string $content, $mediaCollection): array
|
||||
private function publishCarousel(string $userId, string $accessToken, ?string $content, $mediaCollection): array
|
||||
{
|
||||
Log::info('Threads publishing carousel', [
|
||||
'user_id' => $userId,
|
||||
|
|
|
|||
|
|
@ -85,7 +85,7 @@ private function publishVideo(PostPlatform $postPlatform, $media): array
|
|||
$response = $this->getHttpClient()
|
||||
->post("{$this->baseUrl}/post/publish/video/init/", [
|
||||
'post_info' => [
|
||||
'title' => $postPlatform->content,
|
||||
'title' => $postPlatform->content ?? '',
|
||||
'privacy_level' => 'SELF_ONLY',
|
||||
'disable_duet' => false,
|
||||
'disable_comment' => false,
|
||||
|
|
@ -145,7 +145,7 @@ private function publishPhotos(PostPlatform $postPlatform, $mediaCollection): ar
|
|||
$response = $this->getHttpClient()
|
||||
->post("{$this->baseUrl}/post/publish/content/init/", [
|
||||
'post_info' => [
|
||||
'title' => $postPlatform->content,
|
||||
'title' => $postPlatform->content ?? '',
|
||||
'privacy_level' => 'SELF_ONLY',
|
||||
'disable_comment' => false,
|
||||
],
|
||||
|
|
|
|||
|
|
@ -37,9 +37,11 @@ public function publish(PostPlatform $postPlatform): array
|
|||
|
||||
$this->accessToken = $account->access_token;
|
||||
|
||||
$data = [
|
||||
'text' => $postPlatform->content,
|
||||
];
|
||||
$data = [];
|
||||
|
||||
if (! empty($postPlatform->content)) {
|
||||
$data['text'] = $postPlatform->content;
|
||||
}
|
||||
|
||||
$mediaIds = [];
|
||||
$media = $postPlatform->media;
|
||||
|
|
@ -69,6 +71,10 @@ public function publish(PostPlatform $postPlatform): array
|
|||
];
|
||||
}
|
||||
|
||||
if (empty($data['text']) && empty($mediaIds)) {
|
||||
throw new \Exception('X posts require either text or media. Please add content to your post.');
|
||||
}
|
||||
|
||||
Log::info('Posting tweet', ['data' => $data]);
|
||||
|
||||
$response = $this->getHttpClient()
|
||||
|
|
|
|||
|
|
@ -68,6 +68,10 @@ private function getHttpClient(): PendingRequest
|
|||
|
||||
private function publishShort(PostPlatform $postPlatform, $media): array
|
||||
{
|
||||
if (empty($postPlatform->content)) {
|
||||
throw new \Exception('YouTube Shorts require a title. Please add text to your post.');
|
||||
}
|
||||
|
||||
$title = $this->buildTitle($postPlatform->content);
|
||||
$description = $postPlatform->content;
|
||||
|
||||
|
|
|
|||
|
|
@ -327,3 +327,35 @@
|
|||
expect(fn () => $this->publisher->publish($this->postPlatform))
|
||||
->toThrow(Exception::class, 'Unsupported media type for Facebook');
|
||||
});
|
||||
|
||||
test('facebook publisher throws exception for text post with null content', function () {
|
||||
$this->postPlatform->update(['content' => null]);
|
||||
|
||||
expect(fn () => $this->publisher->publish($this->postPlatform))
|
||||
->toThrow(\Exception::class, 'Facebook text posts require content');
|
||||
});
|
||||
|
||||
test('facebook publisher can publish single image with null content', function () {
|
||||
$this->postPlatform->update(['content' => null]);
|
||||
|
||||
$this->postPlatform->media()->create([
|
||||
'collection' => 'default',
|
||||
'type' => 'image',
|
||||
'path' => 'media/2026-01/test-image.jpg',
|
||||
'original_filename' => 'test.jpg',
|
||||
'mime_type' => 'image/jpeg',
|
||||
'size' => 12345,
|
||||
'order' => 0,
|
||||
]);
|
||||
|
||||
Http::fake([
|
||||
'https://graph.facebook.com/v24.0/page_123/photos' => Http::response([
|
||||
'id' => 'photo-123',
|
||||
'post_id' => 'post-123',
|
||||
], 200),
|
||||
]);
|
||||
|
||||
$result = $this->publisher->publish($this->postPlatform);
|
||||
|
||||
expect($result['id'])->toBe('post-123');
|
||||
});
|
||||
|
|
|
|||
|
|
@ -470,6 +470,145 @@
|
|||
->toThrow(Exception::class, 'Failed to create any carousel items');
|
||||
});
|
||||
|
||||
test('instagram publisher can publish single image with null content', function () {
|
||||
$this->postPlatform->update(['content' => null]);
|
||||
|
||||
$this->postPlatform->media()->create([
|
||||
'collection' => 'default',
|
||||
'type' => 'image',
|
||||
'path' => 'media/2026-01/test-image.jpg',
|
||||
'original_filename' => 'test.jpg',
|
||||
'mime_type' => 'image/jpeg',
|
||||
'size' => 12345,
|
||||
'order' => 0,
|
||||
]);
|
||||
|
||||
Http::fake([
|
||||
'https://graph.instagram.com/v24.0/ig_123456789/media' => Http::response([
|
||||
'id' => 'container-123',
|
||||
], 200),
|
||||
'https://graph.instagram.com/v24.0/container-123*' => Http::response([
|
||||
'status_code' => 'FINISHED',
|
||||
], 200),
|
||||
'https://graph.instagram.com/v24.0/ig_123456789/media_publish' => Http::response([
|
||||
'id' => 'media-null-content',
|
||||
], 200),
|
||||
'https://graph.instagram.com/v24.0/media-null-content*' => Http::response([
|
||||
'permalink' => 'https://www.instagram.com/p/NULL123/',
|
||||
], 200),
|
||||
]);
|
||||
|
||||
$result = $this->publisher->publish($this->postPlatform);
|
||||
|
||||
expect($result['id'])->toBe('media-null-content');
|
||||
expect($result['url'])->toBe('https://www.instagram.com/p/NULL123/');
|
||||
});
|
||||
|
||||
test('instagram publisher can publish reel with null content', function () {
|
||||
$this->postPlatform->update([
|
||||
'content_type' => ContentType::InstagramReel,
|
||||
'content' => null,
|
||||
]);
|
||||
|
||||
$this->postPlatform->media()->create([
|
||||
'collection' => 'default',
|
||||
'type' => 'video',
|
||||
'path' => 'media/2026-01/test-video.mp4',
|
||||
'original_filename' => 'test.mp4',
|
||||
'mime_type' => 'video/mp4',
|
||||
'size' => 1234567,
|
||||
'order' => 0,
|
||||
]);
|
||||
|
||||
Http::fake([
|
||||
'https://graph.instagram.com/v24.0/ig_123456789/media' => Http::response([
|
||||
'id' => 'container-123',
|
||||
], 200),
|
||||
'https://graph.instagram.com/v24.0/container-123*' => Http::response([
|
||||
'status_code' => 'FINISHED',
|
||||
], 200),
|
||||
'https://graph.instagram.com/v24.0/ig_123456789/media_publish' => Http::response([
|
||||
'id' => 'reel-null-content',
|
||||
], 200),
|
||||
'https://graph.instagram.com/v24.0/reel-null-content*' => Http::response([
|
||||
'permalink' => 'https://www.instagram.com/reel/NULL123/',
|
||||
], 200),
|
||||
]);
|
||||
|
||||
$result = $this->publisher->publish($this->postPlatform);
|
||||
|
||||
expect($result['id'])->toBe('reel-null-content');
|
||||
});
|
||||
|
||||
test('instagram publisher can publish carousel with null content', function () {
|
||||
$this->postPlatform->update(['content' => null]);
|
||||
|
||||
for ($i = 0; $i < 2; $i++) {
|
||||
$this->postPlatform->media()->create([
|
||||
'collection' => 'default',
|
||||
'type' => 'image',
|
||||
'path' => "media/2026-01/test-image-{$i}.jpg",
|
||||
'original_filename' => "test-{$i}.jpg",
|
||||
'mime_type' => 'image/jpeg',
|
||||
'size' => 12345,
|
||||
'order' => $i,
|
||||
]);
|
||||
}
|
||||
|
||||
Http::fake([
|
||||
'https://graph.instagram.com/v24.0/ig_123456789/media' => Http::sequence()
|
||||
->push(['id' => 'child-1'], 200)
|
||||
->push(['id' => 'child-2'], 200)
|
||||
->push(['id' => 'carousel-container-123'], 200),
|
||||
'https://graph.instagram.com/v24.0/carousel-container-123*' => Http::response([
|
||||
'status_code' => 'FINISHED',
|
||||
], 200),
|
||||
'https://graph.instagram.com/v24.0/ig_123456789/media_publish' => Http::response([
|
||||
'id' => 'carousel-null-content',
|
||||
], 200),
|
||||
'https://graph.instagram.com/v24.0/carousel-null-content*' => Http::response([
|
||||
'permalink' => 'https://www.instagram.com/p/CAROUSELNULL/',
|
||||
], 200),
|
||||
]);
|
||||
|
||||
$result = $this->publisher->publish($this->postPlatform);
|
||||
|
||||
expect($result['id'])->toBe('carousel-null-content');
|
||||
});
|
||||
|
||||
test('instagram publisher can publish single image with empty string content', function () {
|
||||
$this->postPlatform->update(['content' => '']);
|
||||
|
||||
$this->postPlatform->media()->create([
|
||||
'collection' => 'default',
|
||||
'type' => 'image',
|
||||
'path' => 'media/2026-01/test-image.jpg',
|
||||
'original_filename' => 'test.jpg',
|
||||
'mime_type' => 'image/jpeg',
|
||||
'size' => 12345,
|
||||
'order' => 0,
|
||||
]);
|
||||
|
||||
Http::fake([
|
||||
'https://graph.instagram.com/v24.0/ig_123456789/media' => Http::response([
|
||||
'id' => 'container-123',
|
||||
], 200),
|
||||
'https://graph.instagram.com/v24.0/container-123*' => Http::response([
|
||||
'status_code' => 'FINISHED',
|
||||
], 200),
|
||||
'https://graph.instagram.com/v24.0/ig_123456789/media_publish' => Http::response([
|
||||
'id' => 'media-empty-content',
|
||||
], 200),
|
||||
'https://graph.instagram.com/v24.0/media-empty-content*' => Http::response([
|
||||
'permalink' => 'https://www.instagram.com/p/EMPTY123/',
|
||||
], 200),
|
||||
]);
|
||||
|
||||
$result = $this->publisher->publish($this->postPlatform);
|
||||
|
||||
expect($result['id'])->toBe('media-empty-content');
|
||||
});
|
||||
|
||||
test('instagram publisher handles publish failure', function () {
|
||||
$this->postPlatform->media()->create([
|
||||
'collection' => 'default',
|
||||
|
|
|
|||
|
|
@ -292,3 +292,76 @@
|
|||
expect(fn () => $this->publisher->publish($this->postPlatform))
|
||||
->toThrow(Exception::class, 'Threads media processing failed');
|
||||
});
|
||||
|
||||
test('threads publisher throws exception for text post with null content', function () {
|
||||
$this->postPlatform->update(['content' => null]);
|
||||
|
||||
expect(fn () => $this->publisher->publish($this->postPlatform))
|
||||
->toThrow(\Exception::class, 'Threads text posts require content');
|
||||
});
|
||||
|
||||
test('threads publisher can publish image with null content', function () {
|
||||
$this->postPlatform->update(['content' => null]);
|
||||
|
||||
$this->postPlatform->media()->create([
|
||||
'collection' => 'default',
|
||||
'type' => 'image',
|
||||
'path' => 'media/2026-01/test-image.jpg',
|
||||
'original_filename' => 'test.jpg',
|
||||
'mime_type' => 'image/jpeg',
|
||||
'size' => 12345,
|
||||
'order' => 0,
|
||||
]);
|
||||
|
||||
Http::fake([
|
||||
'https://graph.threads.net/v1.0/123456789/threads' => Http::response([
|
||||
'id' => 'container-123',
|
||||
], 200),
|
||||
'https://graph.threads.net/v1.0/container-123*' => Http::response([
|
||||
'status' => 'FINISHED',
|
||||
], 200),
|
||||
'https://graph.threads.net/v1.0/123456789/threads_publish' => Http::response([
|
||||
'id' => 'media-123',
|
||||
], 200),
|
||||
'https://graph.threads.net/v1.0/media-123*' => Http::response([
|
||||
'permalink' => 'https://threads.net/@testuser/post/123',
|
||||
], 200),
|
||||
]);
|
||||
|
||||
$result = $this->publisher->publish($this->postPlatform);
|
||||
|
||||
expect($result['id'])->toBe('media-123');
|
||||
});
|
||||
|
||||
test('threads publisher can publish video with null content', function () {
|
||||
$this->postPlatform->update(['content' => null]);
|
||||
|
||||
$this->postPlatform->media()->create([
|
||||
'collection' => 'default',
|
||||
'type' => 'video',
|
||||
'path' => 'media/2026-01/test-video.mp4',
|
||||
'original_filename' => 'test.mp4',
|
||||
'mime_type' => 'video/mp4',
|
||||
'size' => 1234567,
|
||||
'order' => 0,
|
||||
]);
|
||||
|
||||
Http::fake([
|
||||
'https://graph.threads.net/v1.0/123456789/threads' => Http::response([
|
||||
'id' => 'container-123',
|
||||
], 200),
|
||||
'https://graph.threads.net/v1.0/container-123*' => Http::response([
|
||||
'status' => 'FINISHED',
|
||||
], 200),
|
||||
'https://graph.threads.net/v1.0/123456789/threads_publish' => Http::response([
|
||||
'id' => 'video-123',
|
||||
], 200),
|
||||
'https://graph.threads.net/v1.0/video-123*' => Http::response([
|
||||
'permalink' => 'https://threads.net/@testuser/post/456',
|
||||
], 200),
|
||||
]);
|
||||
|
||||
$result = $this->publisher->publish($this->postPlatform);
|
||||
|
||||
expect($result['id'])->toBe('video-123');
|
||||
});
|
||||
|
|
|
|||
|
|
@ -156,24 +156,18 @@
|
|||
});
|
||||
});
|
||||
|
||||
test('x publisher handles empty content', function () {
|
||||
test('x publisher throws exception with empty content and no media', function () {
|
||||
$this->postPlatform->update(['content' => '']);
|
||||
|
||||
Http::fake([
|
||||
'https://api.x.com/2/tweets' => Http::response([
|
||||
'data' => [
|
||||
'id' => '1234567890123456789',
|
||||
],
|
||||
], 200),
|
||||
]);
|
||||
expect(fn () => $this->publisher->publish($this->postPlatform))
|
||||
->toThrow(\Exception::class, 'X posts require either text or media');
|
||||
});
|
||||
|
||||
$result = $this->publisher->publish($this->postPlatform);
|
||||
test('x publisher throws exception with null content and no media', function () {
|
||||
$this->postPlatform->update(['content' => null]);
|
||||
|
||||
expect($result['id'])->toBe('1234567890123456789');
|
||||
|
||||
Http::assertSent(function ($request) {
|
||||
return $request['text'] === '';
|
||||
});
|
||||
expect(fn () => $this->publisher->publish($this->postPlatform))
|
||||
->toThrow(\Exception::class, 'X posts require either text or media');
|
||||
});
|
||||
|
||||
test('x publisher throws exception when no refresh token available', function () {
|
||||
|
|
|
|||
|
|
@ -144,3 +144,20 @@
|
|||
// Note: Testing token expiration on auth error would require mocking file_get_contents
|
||||
// which is used to fetch video content. The token refresh test above covers the token
|
||||
// expiration handling. Full integration tests should cover the 401 error scenario.
|
||||
|
||||
test('youtube publisher throws exception with null content', function () {
|
||||
$this->postPlatform->update(['content' => null]);
|
||||
|
||||
$this->postPlatform->media()->create([
|
||||
'collection' => 'default',
|
||||
'type' => 'video',
|
||||
'path' => 'media/2026-01/test-video.mp4',
|
||||
'original_filename' => 'test.mp4',
|
||||
'mime_type' => 'video/mp4',
|
||||
'size' => 1234567,
|
||||
'order' => 0,
|
||||
]);
|
||||
|
||||
expect(fn () => $this->publisher->publish($this->postPlatform))
|
||||
->toThrow(\Exception::class, 'YouTube Shorts require a title');
|
||||
});
|
||||
|
|
|
|||
Loading…
Reference in a new issue